def _get_metadata_from_configuration(
    path, name, config,
    fields, **kwargs
):
    """Recursively get metadata from configuration.

    Args:
       path: used to indicate the path to the root element.
             mainly for trouble shooting.
       name: the key of the metadata section.
       config: the value of the metadata section.
       fields: all fields defined in os fields or package fields dir.
    """
    if not isinstance(config, dict):
        raise exception.InvalidParameter(
            '%s config %s is not dict' % (path, config)
        )
    metadata_self = config.get('_self', {})
    if 'field' in metadata_self:
        field_name = metadata_self['field']
        field = fields[field_name]
    else:
        field = {}
    # mapping to may contain $ like $partition. Here we replace the
    # $partition to the key of the correspendent config. The backend then
    # can use this kind of feature to support multi partitions when we
    # only declare the partition metadata in one place.
    mapping_to_template = metadata_self.get('mapping_to', None)
    if mapping_to_template:
        mapping_to = string.Template(
            mapping_to_template
        ).safe_substitute(
            **kwargs
        )
    else:
        mapping_to = None
    self_metadata = {
        'name': name,
        'display_name': metadata_self.get('display_name', name),
        'field_type': field.get('field_type', dict),
        'display_type': field.get('display_type', None),
        'description': metadata_self.get(
            'description', field.get('description', None)
        ),
        'is_required': metadata_self.get('is_required', False),
        'required_in_whole_config': metadata_self.get(
            'required_in_whole_config', False),
        'mapping_to': mapping_to,
        'validator': metadata_self.get(
            'validator', field.get('validator', None)
        ),
        'js_validator': metadata_self.get(
            'js_validator', field.get('js_validator', None)
        ),
        'default_value': metadata_self.get('default_value', None),
        'default_callback': metadata_self.get('default_callback', None),
        'default_callback_params': metadata_self.get(
            'default_callback_params', {}),
        'options': metadata_self.get('options', None),
        'options_callback': metadata_self.get('options_callback', None),
        'options_callback_params': metadata_self.get(
            'options_callback_params', {}),
        'autofill_callback': metadata_self.get(
            'autofill_callback', None),
        'autofill_callback_params': metadata_self.get(
            'autofill_callback_params', {}),
        'required_in_options': metadata_self.get(
            'required_in_options', False)
    }
    self_metadata.update(kwargs)
    metadata = {'_self': self_metadata}
    # Key extension used to do two things:
    # one is to return the extended metadata that $<something>
    # will be replace to possible extensions.
    # The other is to record the $<something> to extended value
    # and used in future mapping_to subsititution.
    # TODO(grace): select proper name instead of key_extensions if
    # you think it is better.
    # Suppose key_extension is {'$partition': ['/var', '/']} for $partition
    # the metadata for $partition will be mapped to {
    # '/var': ..., '/': ...} and kwargs={'partition': '/var'} and
    # kwargs={'partition': '/'} will be parsed to recursive metadata parsing
    # for sub metadata under '/var' and '/'. Then in the metadata parsing
    # for the sub metadata, this kwargs will be used to substitute mapping_to.
    key_extensions = metadata_self.get('key_extensions', {})
    general_keys = []
    for key, value in config.items():
        if key.startswith('_'):
            continue
        if key in key_extensions:
            if not key.startswith('$'):
                raise exception.InvalidParameter(
                    '%s subkey %s should start with $' % (
                        path, key
                    )
                )
            extended_keys = key_extensions[key]
            for extended_key in extended_keys:
                if extended_key.startswith('$'):
                    raise exception.InvalidParameter(
                        '%s extended key %s should not start with $' % (
                            path, extended_key
                        )
                    )
                sub_kwargs = dict(kwargs)
                sub_kwargs[key[1:]] = extended_key
                metadata[extended_key] = _get_metadata_from_configuration(
                    '%s/%s' % (path, extended_key), extended_key, value,
                    fields, **sub_kwargs
                )
        else:
            if key.startswith('$'):
                general_keys.append(key)
            metadata[key] = _get_metadata_from_configuration(
                '%s/%s' % (path, key), key, value,
                fields, **kwargs
            )
        if len(general_keys) > 1:
            raise exception.InvalidParameter(
                'foud multi general keys in %s: %s' % (
                    path, general_keys
                )
            )
    return metadata
dataset/ETHPy150Open openstack/compass-core/compass/db/api/metadata.py/_get_metadata_from_configuration/original