Beispiel #1
0
def metadata_gen(config, meta_config=None, auto_type=False, prop_map=None):
    """Automatically guess the metadata for an application configuration."""
    if prop_map is None:
        prop_map = {}
    metomi.rose.macro.standard_format_config(config)
    if meta_config is None:
        meta_config = metomi.rose.config.ConfigNode()
    for keylist, node in config.walk():
        sect = keylist[0]
        if len(keylist) == 1:
            option = None
        else:
            option = keylist[1]
        if sect in [metomi.rose.CONFIG_SECT_CMD]:
            continue
        if keylist == [
                metomi.rose.CONFIG_SECT_TOP,
                metomi.rose.CONFIG_OPT_META_TYPE,
        ]:
            continue
        meta_sect = metomi.rose.macro.REC_ID_STRIP.sub("", sect)
        modifier_sect = metomi.rose.macro.REC_ID_STRIP_DUPL.sub("", sect)
        if sect and option is None:
            if (modifier_sect != meta_sect and modifier_sect != sect
                    and meta_config.get([modifier_sect]) is None
                    and auto_type):
                meta_config.set(
                    [modifier_sect, metomi.rose.META_PROP_DUPLICATE],
                    metomi.rose.META_PROP_VALUE_TRUE,
                )
            if meta_config.get([meta_sect]) is not None:
                continue
            meta_config.set([meta_sect])
            if meta_sect != sect and auto_type:
                # Add duplicate = true at base and modifier level (if needed).
                meta_config.set(
                    [meta_sect, metomi.rose.META_PROP_DUPLICATE],
                    metomi.rose.META_PROP_VALUE_TRUE,
                )
            for prop_key, prop_value in prop_map.items():
                meta_config.set([meta_sect, prop_key], prop_value)
        if option is None:
            continue
        meta_key = metomi.rose.macro.REC_ID_STRIP_DUPL.sub('', option)
        meta_opt = meta_sect + "=" + meta_key
        if meta_config.get([meta_opt]) is not None:
            continue
        meta_config.set([meta_opt])
        for prop_key, prop_value in prop_map.items():
            meta_config.set([meta_opt, prop_key], prop_value)
        if auto_type:
            opt_type, length = type_gen(node.value)
            if opt_type is not None:
                meta_config.set([meta_opt, metomi.rose.META_PROP_TYPE],
                                opt_type)
            if int(length) > 1:
                meta_config.set([meta_opt, metomi.rose.META_PROP_LENGTH],
                                length)
    return meta_config
Beispiel #2
0
    def transform(self, config, meta_config=None):
        """Apply metadata trigger expressions to variables."""
        self.reports = []
        meta_config = self._load_meta_config(config, meta_config)
        self._setup_triggers(meta_config)
        self.enabled_dict = {}
        self.ignored_dict = {}
        enabled = metomi.rose.config.ConfigNode.STATE_NORMAL
        trig_ignored = metomi.rose.config.ConfigNode.STATE_SYST_IGNORED
        user_ignored = metomi.rose.config.ConfigNode.STATE_USER_IGNORED
        state_map = {
            enabled: 'enabled     ',
            trig_ignored: 'trig-ignored',
            user_ignored: 'user-ignored',
        }
        id_list = []
        prev_ignoreds = {trig_ignored: [], user_ignored: []}
        for keylist, node in config.walk():
            if len(keylist) == 1:
                n_id = keylist[0]
            else:
                n_id = self._get_id_from_section_option(*keylist)
            id_list.append(n_id)
            if node.state in prev_ignoreds:
                prev_ignoreds[node.state].append(n_id)

        ranked_ids = self._get_ranked_trigger_ids()
        for _, var_id in sorted(ranked_ids):
            self.update(var_id, config, meta_config)

        # Report any discrepancies in ignored status.
        for var_id in id_list:
            section, option = self._get_section_option_from_id(var_id)
            node = config.get([section, option])
            old, new = None, None
            if var_id in self.ignored_dict:
                node.state = trig_ignored
                if not any(var_id in v for v in prev_ignoreds.values()):
                    old, new = state_map[enabled], state_map[trig_ignored]
            elif var_id in prev_ignoreds[trig_ignored]:
                node.state = enabled
                old, new = state_map[trig_ignored], state_map[enabled]
            elif (var_id in prev_ignoreds[user_ignored]
                  and var_id in self._trigger_involved_ids):
                node.state = enabled
                old, new = state_map[user_ignored], state_map[enabled]
            if old != new:
                info = self.WARNING_STATE_CHANGED.format(old, new)
                if option is None:
                    value = None
                else:
                    value = node.value
                self.add_report(section, option, value, info)
        return config, self.reports
Beispiel #3
0
    def rename_setting(self, config, keys, new_keys, info=None):
        """Rename a setting in the configuration.

        Args:
            config (metomi.rose.config.ConfigNode): The application
                configuration.
            keys (list): A list defining a hierarchy of node.value 'keys'.
                A section will be a list of one keys, an option will have two.
            new_keys (list): The new hierarchy of node.value 'keys'.
            info (str): A short string containing no new lines,
                describing the addition of the setting.

        Returns:
            None

        """
        section, option = self._get_section_option_from_keys(keys)
        new_section, new_option = self._get_section_option_from_keys(new_keys)
        if option is None:
            if new_option is not None:
                raise TypeError(self.ERROR_RENAME_SECT_TO_OPT.format(
                    section, new_section, new_option))
        elif new_option is None:
            raise TypeError(self.ERROR_RENAME_OPT_TO_SECT.format(
                section, option, new_section))
        node = config.get(keys)
        if node is None:
            return
        if info is None:
            if option is None:
                info = self.INFO_RENAMED_SECT.format(section, new_section)
            else:
                info = self.INFO_RENAMED_VAR.format(section, option,
                                                    new_section, new_option)
        if option is None:
            if config.get([new_section]) is not None:
                self.remove_setting(config, [new_section])
            self.add_setting(config, [new_section], value=None, forced=True,
                             state=node.state, comments=node.comments,
                             info=info)
            for option_keys, opt_node in config.walk([section]):
                renamed_option = option_keys[1]
                self.add_setting(config, [new_section, renamed_option],
                                 value=opt_node.value, forced=True,
                                 state=opt_node.state,
                                 comments=opt_node.comments, info=info)
        else:
            self.add_setting(config, new_keys, value=node.value, forced=True,
                             state=node.state, comments=node.comments,
                             info=info)
        self.remove_setting(config, keys)
Beispiel #4
0
def annotate_config_with_metadata(
    config, meta_config, ignore_regexes=None, metadata_properties=None
):
    """Add metadata to the metomi.rose.config.ConfigNode.comments attribute.

    config -- a metomi.rose.config.ConfigNode instance, containing app or
              suite data.
    meta_config -- a metomi.rose.config.ConfigNode instance, containing
                   metadata for config.
    ignore_regexes -- (default None) a list of uncompiled regular
                      expressions - if a setting contains any of these,
                      don't include it in the annotated output.

    """

    if ignore_regexes is None:
        ignore_regexes = []
    ignore_recs = [re.compile(_) for _ in ignore_regexes]
    unset_keys = []
    for keylist, node in config.walk():
        section = keylist[0]
        option = None
        if len(keylist) > 1:
            option = keylist[1]
        id_ = metomi.rose.macro.get_id_from_section_option(section, option)
        if any(_.search(id_) for _ in ignore_recs):
            unset_keys.append(keylist)
            continue
        metadata = metomi.rose.macro.get_metadata_for_config_id(
            id_, meta_config
        )
        metadata_text = format_metadata_as_text(
            metadata, only_these_options=metadata_properties
        )
        metadata_lines = [" " + line for line in metadata_text.splitlines()]
        node.comments = metadata_lines + node.comments
    for keylist in unset_keys:
        config.unset(keylist)
    return config
Beispiel #5
0
    def remove_setting(self, config, keys, info=None):
        """Remove a setting from the configuration.

        Args:
            config (metomi.rose.config.ConfigNode): The application
                configuration.
            keys (list): A list defining a hierarchy of node.value 'keys'.
                A section will be a list of one keys, an option will have two.
            info (string - optional): A short string containing no new lines,
                describing the addition of the setting.

        Returns:
            None
        """
        section, option = self._get_section_option_from_keys(keys)
        if option is None:
            if config.get([section]) is None:
                return False
            option_node_pairs = config.walk([section])
            for opt_keys, _ in option_node_pairs:
                opt = opt_keys[1]
                self._remove_setting(config, [section, opt], info)
        return self._remove_setting(config, [section, option], info)
Beispiel #6
0
    def add_setting(self, config, keys, value=None, forced=False,
                    state=None, comments=None, info=None):
        """Add a setting to the configuration.

        Args:
            config (metomi.rose.config.ConfigNode): The application
                configuration.
            keys (list): A list defining a hierarchy of node.value 'keys'.
                A section will be a list of one keys, an option will have two.
            value (string - optional): String denoting the new setting value.
                Required for options but not for settings.
            forced (bool - optional)
                If True override value if the setting already exists.
            state (str - optional):
                The state of the new setting - should be one of the
                ``rose.config.ConfigNode`` states e.g.
                ``rose.config.ConfigNode.STATE_USER_IGNORED``. Defaults to
                ``rose.config.ConfigNode.STATE_NORMAL``.
            comments (list - optional): List of comment lines (strings) for
                the new setting or ``None``.
            info (string - optional): A short string containing no new lines,
                describing the addition of the setting.

        Returns:
            None
        """
        section, option = self._get_section_option_from_keys(keys)
        id_ = self._get_id_from_section_option(section, option)
        if option is not None and value is None:
            value = ""
        if info is None:
            if option is None:
                info = self.INFO_ADDED_SECT
            else:
                info = self.INFO_ADDED_VAR.format(repr(value))

        # Search for existing conflicting settings.
        conflict_id = None
        found_setting = False
        if config.get([section, option]) is None:
            for key in config.get_value():
                existing_section = key
                if not existing_section.startswith(section):
                    continue
                existing_base_section = (
                    metomi.rose.macro.REC_ID_STRIP.sub("", existing_section))
                if option is None:
                    # For section 'foo', look for 'foo', 'foo{bar}', 'foo(1)'.
                    found_setting = (existing_section == section or
                                     existing_base_section == section)
                else:
                    # For 'foo=bar', don't allow sections 'foo(1)', 'foo{bar}'.
                    found_setting = (existing_section != section and
                                     existing_base_section == section)
                if found_setting:
                    conflict_id = existing_section
                    break
                if option is not None:
                    for keys, _ in config.walk([existing_section]):
                        existing_option = keys[1]
                        existing_base_option = (
                            metomi.rose.macro.REC_ID_STRIP_DUPL.sub(
                                "", existing_option)
                        )
                        # For option 'foo', look for 'foo', 'foo(1)'.
                        if (existing_section == section and
                                (existing_option == option or
                                 existing_base_option == option)):
                            found_setting = True
                            conflict_id = self._get_id_from_section_option(
                                existing_section, existing_option)
                            break
                    if found_setting:
                        break
        else:
            found_setting = True
            conflict_id = None

        # If already added, quit, unless "forced".
        if found_setting:
            if forced and (conflict_id is None or id_ == conflict_id):
                # If forced, override settings for an identical id.
                return self.change_setting_value(
                    config, keys, value, state, comments, info)
            if conflict_id:
                self.add_report(
                    section, option, value,
                    self.WARNING_ADD_CLASH.format(id_, conflict_id),
                    is_warning=True
                )
            return False

        # Add parent section if missing.
        if option is not None and config.get([section]) is None:
            self.add_setting(config, [section])
        if value is not None and not isinstance(value, str):
            text = "New value {0} for {1} is not a string"
            raise ValueError(text.format(repr(value), id_))

        # Set (add) the section/option.
        config.set([section, option], value=value, state=state,
                   comments=comments)
        self.add_report(section, option, value, info)