diff options
| -rw-r--r-- | plugins/module_utils/network/vyos/argspec/ospfv3/__init__.py | 0 | ||||
| -rw-r--r-- | plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py | 106 | ||||
| -rw-r--r-- | plugins/module_utils/network/vyos/config/ospfv3/__init__.py | 0 | ||||
| -rw-r--r-- | plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py | 446 | ||||
| -rw-r--r-- | plugins/module_utils/network/vyos/facts/facts.py | 4 | ||||
| -rw-r--r-- | plugins/module_utils/network/vyos/facts/ospfv3/__init__.py | 0 | ||||
| -rw-r--r-- | plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py | 199 | ||||
| -rw-r--r-- | plugins/module_utils/network/vyos/utils/utils.py | 31 | ||||
| -rw-r--r-- | plugins/modules/vyos_facts.py | 2 | ||||
| -rw-r--r-- | plugins/modules/vyos_ospfv3.py | 181 | 
10 files changed, 967 insertions, 2 deletions
| diff --git a/plugins/module_utils/network/vyos/argspec/ospfv3/__init__.py b/plugins/module_utils/network/vyos/argspec/ospfv3/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/ospfv3/__init__.py diff --git a/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py new file mode 100644 index 00000000..b0166d8d --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py @@ -0,0 +1,106 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +#                WARNING                    # +############################################# +# +# This file is auto generated by the resource +#   module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +#   by the resource module builder. +# +# Changes should be made in the model used to +#   generate this file or in the resource module +#   builder template. +# +############################################# +""" +The arg spec for the vyos_ospfv3 module +""" + + +class Ospfv3Args(object):  # pylint: disable=R0903 +    """The arg spec for the vyos_ospfv3 module +    """ +    def __init__(self, **kwargs): +        pass + +    argument_spec = { +        'config': { +            'elements': 'dict', +            'options': { +                'ospf_area': { +                    'elements': 'dict', +                    'options': { +                        'area': { +                            'type': 'str' +                        }, +                        'export_list': { +                            'type': 'str' +                        }, +                        'import_list': { +                            'type': 'str' +                        }, +                        'range': { +                            'elements': 'dict', +                            'options': { +                                'address': { +                                    'type': 'str' +                                }, +                                'advertise': { +                                    'type': 'bool' +                                }, +                                'not_advertise': { +                                    'type': 'bool' +                                } +                            }, +                            'type': 'list' +                        } +                    }, +                    'type': 'list' +                }, +                'parameters': { +                    'options': { +                        'router_id': { +                            'type': 'str' +                        } +                    }, +                    'type': 'dict' +                }, +                'redistribute': { +                    'elements': 'dict', +                    'options': { +                        'route_map': { +                            'type': 'str' +                        }, +                        'route_type': { +                            'choices': +                            ['bgp', 'connected', 'kernel', 'ripng', 'static'], +                            'type': +                            'str' +                        } +                    }, +                    'type': 'list' +                } +            }, +            'type': 'list' +        }, +        "running_config": {"type": "str"}, +        'state': { +            'choices': [ +                'merged', 'replaced', 'deleted', 'parsed', 'gathered', +                'rendered' +            ], +            'default': +            'merged', +            'type': +            'str' +        } +    }  # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/ospfv3/__init__.py b/plugins/module_utils/network/vyos/config/ospfv3/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/config/ospfv3/__init__.py diff --git a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py new file mode 100644 index 00000000..fa869dad --- /dev/null +++ b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py @@ -0,0 +1,446 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos_ospfv3 class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( +    ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( +    to_list, +    remove_empties, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts +from ansible.module_utils.six import iteritems + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( +    list_diff_want_only, _in_target, _is_w_same, _bool_to_str +) + +class Ospfv3(ConfigBase): +    """ +    The vyos_ospfv3 class +    """ + +    gather_subset = [ +        '!all', +        '!min', +    ] + +    gather_network_resources = [ +        'ospfv3', +    ] + +    def __init__(self, module): +        super(Ospfv3, self).__init__(module) + +    def get_ospfv3_facts(self, data=None): +        """ Get the 'facts' (the current configuration) + +        :rtype: A dictionary +        :returns: The current configuration as a dictionary +        """ +        facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources, data=data) +        ospfv3_facts = facts['ansible_network_resources'].get('ospfv3') +        if not ospfv3_facts: +            return [] +        return ospfv3_facts + +    def execute_module(self): +        """ Execute the module + +        :rtype: A dictionary +        :returns: The result from module execution +        """ +        result = {'changed': False} +        warnings = list() +        commands = list() + +        if self.state in self.ACTION_STATES: +            existing_ospfv3_facts = self.get_ospfv3_facts() +        else: +            existing_ospfv3_facts = [] + +        if self.state in self.ACTION_STATES or self.state == "rendered": +            commands.extend(self.set_config(existing_ospfv3_facts)) + +        if commands and self.state in self.ACTION_STATES: +            if not self._module.check_mode: +                self._connection.edit_config(commands) +            result["changed"] = True + +        if self.state in self.ACTION_STATES: +            result["commands"] = commands + +        if self.state in self.ACTION_STATES or self.state == "gathered": +            changed_ospfv3_facts = self.get_ospfv3_facts() +        elif self.state == "rendered": +            result["rendered"] = commands +        elif self.state == "parsed": +            running_config = self._module.params["running_config"] +            if not running_config: +                self._module.fail_json( +                    msg="value of running_config parameter must not be empty for state parsed" +                ) +            result["parsed"] = self.get_ospfv3_facts( +                data=running_config +            ) +        else: +            changed_ospfv3_facts = [] + +        if self.state in self.ACTION_STATES: +            result["before"] = existing_ospfv3_facts +            if result["changed"]: +                result["after"] = changed_ospfv3_facts +        elif self.state == "gathered": +            result["gathered"] = changed_ospfv3_facts + +        result["warnings"] = warnings +        return result + +    def set_config(self, existing_ospfv3_facts): +        """ Collect the configuration from the args passed to the module, +            collect the current configuration (as a dict from facts) + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        want = self._module.params['config'] +        have = existing_ospfv3_facts +        resp = self.set_state(want, have) +        return to_list(resp) + +    def set_state(self, w, h): +        """ Select the appropriate function based on the state provided + +        :param want: the desired configuration as a dictionary +        :param have: the current configuration as a dictionary +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        if self.state in ("merged", "replaced", "overridden", "rendered") and not w: +            self._module.fail_json( +                msg="value of config parameter must not be empty for state {0}".format( +                    self.state +                ) +            ) +        if self.state == "overridden": +            commands.extend(self._state_overridden(w, h)) +        elif self.state == "deleted": +            commands.extend(self._state_deleted(w, h)) +        elif w: +            if self.state == "merged" or self.state == "rendered": +                for w_item in w: +                    commands.extend(self._state_merged(w_item, h)) +            elif self.state == "replaced": +                for w_item in w: +                    commands.extend(self._state_replaced(w_item, h)) +        return commands + +    def search_obj_in_have(self, have, w_name, key): +        """ +        This function  returns the rule-set/rule if it is present in target config. +        :param have: target config. +        :param w_name: rule-set name. +        :param type: rule_sets/rule/r_list. +        :return: rule-set/rule. +        """ +        if have: +            for item in have: +                if item[key] == w_name[key]: +                    return item +        return None + +    def _state_replaced(self, want, have): +        """ The command generator when state is replaced + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        h_item = {} +        if have: +            h_item = have[0] +            commands.extend(self._render_ospf_param(h_item, want, opr=False)) +        commands.extend(self._render_ospf_param(want, h_item)) +        return commands + +    def _state_merged(self, want, have): +        """ The command generator when state is merged + +        :rtype: A list +        :returns: the commands necessary to merge the provided into +                  the current configuration +        """ +        commands = [] +        h_item = {} +        if have: +            h_item = have[0] +        commands.extend(self._render_ospf_param(want, h_item)) +        return commands + +    def _state_deleted(self, want, have): +        """ The command generator when state is deleted + +        :rtype: A list +        :returns: the commands necessary to remove the current configuration +                  of the provided objects +        """ +        commands = [] +        if want: +            for w in want: +                if have: +                    h = have[0] +                    if h: +                        for key, val in iteritems(w): +                            if key in h: +                                if key == 'ospf_area': +                                    key = 'area' +                                commands.append(self._compute_command(attr=key, opr=False)) +        elif have and have[0]: +            commands.append('delete protocols ospfv3') +        return commands + +    def _render_ospf_param(self, want, have, opr=True): +        """ +        This function forms the set/delete commands for ospf leaf attributes +        and triggers the process for other child attributes. +        for firewall_global attributes. +        :param w: the desired config. +        :param h: the target config. +        :param opr: True/False. +        :return: generated commands list. +        """ +        commands = [] +        w = deepcopy(remove_empties(want)) +        if w: +            for key, val in iteritems(w): +                    commands.extend(self._render_child_param(w, have, key, opr)) +        return commands + +    def _render_child_param(self, w, h, key, opr=True): +        """ +        This function invoke the function to extend commands +        based on the key. +        :param w: the desired configuration. +        :param h: the current configuration. +        :param key: attribute name. +        :param opr: operation. +        :return: list of commands. +        """ +        commands = [] +        if key == 'ospf_area': +            commands.extend(self._render_ospf_area(key, w, h, opr=opr)) +        elif key == 'parameters': +            commands.extend(self._render_dict_param(key, w, h, opr=opr)) +        elif key == 'redistribute': +            commands.extend(self._render_list_dict_param(key, w, h, opr=opr)) +        return commands + +    def _render_dict_param(self, attr, want, have, opr=True): +        """ +        This function generate the commands for dictionary elements. +        :param attr: attribute name. +        :param w: the desired configuration. +        :param h: the target config. +        :param opr: True/False. +        :return: generated list of commands. +        """ +        commands = [] +        h = {} +        if have: +            h = have.get(attr) or {} +        if not opr and not h: +            commands.append(self._form_attr_cmd(attr=attr, opr=opr)) +        elif want[attr]: +            leaf_dict = {'parameters': "router_id"} +            leaf = leaf_dict[attr] +            for item, value in iteritems(want[attr]): +                if opr and item in leaf and not _is_w_same(want[attr], h, item): +                    commands.append(self._form_attr_cmd(key=attr, attr=item, val=value, opr=opr)) +                elif not opr and item in leaf and not _in_target(h, item): +                    commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) +        return commands + +    def _render_list_param(self, attr, want, have, cmd=None, opr=True): +        """ +        This function forms the commands for passed target list attributes'. +        :param attr: attribute name. +        :param w: the desired config. +        :param h: the target config. +        :param cmd: commands to be prepend. +        :param opr: True/False. +        :return: generated list of commands. +        """ +        commands = [] +        h = [] +        if want: +            w = want.get(attr) or [] +        if have: +            h = have.get(attr) or [] +        if not cmd: +            cmd = self._compute_command(opr=opr) +        if w: +            if opr: +                members = list_diff_want_only(w, h) +                for member in members: +                    command = cmd + attr.replace("_","-") + " " +                    if attr == 'network': +                        command += member['address'] +                    else: +                        command += member +                    commands.append(command) +            elif not opr: +                if h: +                    for member in w: +                        if attr == 'network': +                            if not self.search_obj_in_have(h, member, 'address'): +                                commands.append(cmd + attr.replace("_","-") + ' ' + member['address']) +                        elif member not in h: +                            commands.append(cmd + attr.replace("_","-") + ' ' + member) +                else: +                    commands.append(cmd + " " + attr.replace("_","-")) +        return commands + +    def _render_list_dict_param(self, attr, want, have, cmd=None, opr=True): +        """ +        This function forms the set/delete commands based on the 'opr' type +        for attributes with in desired list of dictionary. +        :param attr: attribute name. +        :param w: the desired config. +        :param h: the target config. +        :param cmd: commands to be prepend. +        :param opr: True/False. +        :return: generated commands list. +        """ +        commands = [] +        h = [] +        name = {'redistribute': 'route_type', +                'range': 'address', +                } +        leaf_dict = {'redistribute': ("route_map", "route_type"), +                     'range': ("address", "advertise", "not_advertise") +                     } +        leaf = leaf_dict[attr] +        w = want.get(attr) or [] +        if have: +            h = have.get(attr) or [] +        if not opr and not h: +            commands.append(self._compute_command(attr=attr, opr=opr)) +        elif w: +            for w_item in w: +                for key, val in iteritems(w_item): +                    if not cmd: +                        cmd = self._compute_command(opr=opr) +                    h_item = self.search_obj_in_have(h, w_item, name[attr]) +                    if opr and key in leaf and not _is_w_same(w_item, h_item, key): +                        if key in ('route_type', 'address'): +                            commands.append(cmd + attr + ' ' + str(val)) +                        elif key in leaf_dict['range']: +                            commands.append(cmd + attr + ' ' + w_item[name[attr]] + ' ' + key.replace("_","-")) +                        else: +                            commands.append(cmd + attr + ' ' + w_item[name[attr]] + ' ' + key.replace("_", "-") + ' ' + str(val)) +                    elif not opr and key in leaf and not _in_target(h_item, key): +                        if key in ('route_type', 'address'): +                            commands.append(cmd + attr + ' ' + str(val)) +                        else: +                            commands.append(cmd + (attr + ' ' + w_item[name[attr]] + ' ' + key)) +        return commands + +    def _render_ospf_area(self, attr, want, have, opr=True): +        """ +        This function forms the set/delete commands based on the 'opr' type +        for ospf area attributes. +        :param attr: attribute name. +        :param w: the desired config. +        :param h: the target config. +        :param opr: True/False. +        :return: generated commands list. +        """ +        commands = [] +        h_lst = {} +        w_lst = want.get(attr) or [] +        l_set = ("area") +        if have: +            h_lst = have.get(attr) or [] +        if not opr and not h_lst: +            commands.append(self._form_attr_cmd(attr='area', opr=opr)) +        elif w_lst: +            for w_area in w_lst: +                cmd = self._compute_command(key='area', attr=_bool_to_str(w_area['area']), opr=opr) + ' ' +                h_area = self.search_obj_in_have(h_lst, w_area, 'area') +                if not opr and not h_area: +                    commands.append(self._form_attr_cmd(key='area', attr=w_area['area'], opr=opr)) +                else: +                    for key, val in iteritems(w_area): +                        if opr and key in l_set and not _is_w_same(w_area, h_area, key): +                            if key == 'area': +                                commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) +                            else: +                                commands.append(cmd + key + ' ' + _bool_to_str(val).replace("_","-")) +                        elif not opr and key in l_set: +                            if key == 'area' and not _in_target(h_area, key): +                                commands.append(cmd) +                                continue +                            elif key != 'area' and not _in_target(h_area, key): +                                commands.append(cmd + val + ' ' + key) +                        elif key == 'range': +                            commands.extend(self._render_list_dict_param(key, w_area, h_area, cmd, opr)) +        return commands + +    def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True): +        """ +        This function forms the command for leaf attribute. +        :param key: parent key. +        :param attr: attribute name +        :param value: value +        :param opr: True/False. +        :return: generated command. +        """ +        return self._compute_command(key, attr=self._map_attrib(attr), val=val, opr=opr) + +    def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True): +        """ +        This function construct the add/delete command based on passed attributes. +        :param key: parent key. +        :param attr: attribute name +        :param value: value +        :param opr: True/False. +        :return: generated command. +        """ +        if remove or not opr: +            cmd = "delete protocols ospfv3 " +        else: +            cmd = "set protocols ospfv3 " +        if key: +            cmd += key.replace("_", "-") + " " +        if attr: +            cmd += attr.replace("_", "-") +        if val and opr: +            cmd += " '" + str(val) + "'" +        return cmd + +    def _map_attrib(self, attrib): +        """ +        - This function construct the regex string. +        - replace the underscore with hyphen. +        :param attrib: attribute +        :return: regex string +        """ +        return 'disable' if attrib == 'disabled' else attrib.replace("_","-") diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index ff3d0988..6ead7fe7 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -40,6 +40,9 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firew  from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_interfaces.firewall_interfaces import (      Firewall_interfacesFacts,  ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv3.ospfv3 import ( +   Ospfv3Facts, +)  from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import (      Default,      Neighbors, @@ -58,6 +61,7 @@ FACT_RESOURCE_SUBSETS = dict(      firewall_rules=Firewall_rulesFacts,      firewall_global=Firewall_globalFacts,      firewall_interfaces=Firewall_interfacesFacts, +    ospfv3=Ospfv3Facts  ) diff --git a/plugins/module_utils/network/vyos/facts/ospfv3/__init__.py b/plugins/module_utils/network/vyos/facts/ospfv3/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/ospfv3/__init__.py diff --git a/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py new file mode 100644 index 00000000..c980795a --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py @@ -0,0 +1,199 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos ospfv3 fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from re import findall, search, M +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( +    utils, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv3.ospfv3 import Ospfv3Args + + +class Ospfv3Facts(object): +    """ The vyos ospfv3 fact class +    """ + +    def __init__(self, module, subspec='config', options='options'): +        self._module = module +        self.argument_spec = Ospfv3Args.argument_spec +        spec = deepcopy(self.argument_spec) +        if subspec: +            if options: +                facts_argument_spec = spec[subspec][options] +            else: +                facts_argument_spec = spec[subspec] +        else: +            facts_argument_spec = spec + +        self.generated_spec = utils.generate_dict(facts_argument_spec) + +    def get_device_data(self, connection): +        return connection.get_config() + +    def populate_facts(self, connection, ansible_facts, data=None): +        """ Populate the facts for ospfv3 +        :param connection: the device connection +        :param ansible_facts: Facts dictionary +        :param data: previously collected conf +        :rtype: dictionary +        :returns: facts +        """ +        if not data: +            data = self.get_device_data(connection) +            # typically data is populated from the current device configuration +            # data = connection.get('show running-config | section ^interface') +            # using mock data instead +        objs = [] +        ospfv3 = findall(r"^set protocols ospfv3 (.+)", data, M) +        if ospfv3: +            config = self.render_config(ospfv3) +            if config: +                objs.append(config) +        ansible_facts["ansible_network_resources"].pop("ospfv3", None) +        facts = {} +        if objs: +            facts["ospfv3"] = [] +            params = utils.validate_config(self.argument_spec, {"config": objs}) +            for cfg in params["config"]: +                facts["ospfv3"].append(utils.remove_empties(cfg)) +        ansible_facts["ansible_network_resources"].update(facts) +        return ansible_facts + +    def render_config(self, conf): +        """ +        Render config as dictionary structure + +        :param conf: The configuration +        :returns: The generated config +        """ +        conf = "\n".join(filter(lambda x: x, conf)) +        config = {} +        config["parameters"] = self.parse_attrib(conf, "parameters", "parameters") +        config["ospf_area"] = self.parse_attrib_list(conf, "area", "area") +        config["redistribute"] = self.parse_attrib_list(conf, "redistribute", "route_type") +        return config + +    def parse_attrib_list(self, conf, attrib, param): +        """ +        This function forms the regex to fetch the listed attributes +        from config +        :param conf: configuration data +        :param attrib: attribute name +        :param param: parameter data +        :return: generated rule list configuration +        """ +        r_lst = [] +        if attrib == "area": +            items = findall(r"^" + attrib + " (?:\'*)(\S+)(?:\'*)", conf, M) +        else: +            items = findall(r"" + attrib + " (?:\'*)(\S+)(?:\'*)", conf, M) +        if items: +            a_lst = [] +            for item in set(items): +                i_regex = r" %s .+$" % item +                cfg = "\n".join(findall(i_regex, conf, M)) +                if attrib == 'area': +                    obj = self.parse_area(cfg, item) +                else: +                    obj = self.parse_attrib(cfg, attrib) +                obj[param] = item.strip("'") +                if obj: +                    a_lst.append(obj) +            r_lst = sorted(a_lst, key=lambda i: i[param]) +        return r_lst + +    def parse_area(self, conf, area_id): +        """ +        This function triggers the parsing of 'area' attributes. +        :param conf: configuration data +        :param area_id: area identity +        :return: generated rule configuration dictionary. +        """ +        cfg_dict = {"range": self.parse_attrib_list(conf, "range", "address")} +        return cfg_dict + +    def parse_attrib(self, conf, param, match=None): +        """ +        This function triggers the parsing of 'ospf' attributes +        :param conf: configuration data +        :return: generated configuration dictionary +        """ +        param_lst = { +            'redistribute': ["route_map"], +            'range': ["advertise", "not_advertise"], +            'parameters': ["router_id"] +            } +        cfg_dict = self.parse_attr(conf, param_lst[param], match) +        return cfg_dict + +    def parse_attr(self, conf, attr_list, match=None): +        """ +        This function peforms the following: +        - Form the regex to fetch the required attribute config. +        - Type cast the output in desired format. +        :param conf: configuration. +        :param attr_list: list of attributes. +        :param match: parent node/attribute name. +        :return: generated config dictionary. +        """ +        config = {} +        for attrib in attr_list: +            regex = self.map_regex(attrib) +            if match: +                regex = match.replace("_", "-") + " " + regex +            if conf: +                if self.is_bool(attrib): +                    out = conf.find(attrib.replace("_", "-")) +                    dis = conf.find(attrib.replace("_", "-") + " 'disable'") +                    if match: +                        en = conf.find(match + " 'enable'") +                    if out >= 1: +                        if dis >= 1: +                            config[attrib] = False +                        else: +                            config[attrib] = True +                    elif match and en >= 1: +                        config[attrib] = True +                else: +                    out = search(r"^.*" + regex + " (.+)", conf, M) +                    if out: +                        val = out.group(1).strip("'") +                        if self.is_num(attrib): +                            val = int(val) +                        config[attrib] = val +        return config + +    def map_regex(self, attrib): +        """ +        - This function construct the regex string. +        - replace the underscore with hyphen. +        :param attrib: attribute +        :return: regex string +        """ +        return 'disable' if attrib == "disabled" else 'enable' if attrib == "enabled" else attrib.replace("_","-") + +    def is_bool(self, attrib): +        """ +        This function looks for the attribute in predefined bool type set. +        :param attrib: attribute. +        :return: True/False +        """ +        bool_set = ("enabled", "advertise", "not_advertise") +        return True if attrib in bool_set else False + +    def is_num(self, attrib): +        """ +        This function looks for the attribute in predefined integer type set. +        :param attrib: attribute. +        :return: True/false. +        """ +        num_set = ("ospf") +        return True if attrib in num_set else False diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index 4635234c..2d5b74b7 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -7,6 +7,7 @@  from __future__ import absolute_import, division, print_function  __metaclass__ = type +import q  from ansible.module_utils.six import iteritems  from ansible_collections.ansible.netcommon.plugins.module_utils.compat import (      ipaddress, @@ -113,7 +114,7 @@ def get_lst_same_for_dicts(want, have, lst):  def list_diff_have_only(want_list, have_list): -    """ +    """ -      This function generated the list containing values      that are only in have list.      :param want_list: @@ -230,3 +231,31 @@ def get_route_type(address):          return "route6"      elif version == 4:          return "route" + +def _bool_to_str(val): +    """ +    This function converts the bool value into string. +    :param val: bool value. +    :return: enable/disable. +    """ +    return "enable" if str(val) == "True" else "disable" if str(val) == "False" else val + +def _is_w_same(w, h, key): +    """ +    This function checks whether the key value is same in desired and +    target config dictionary. +    :param w: base config. +    :param h: target config. +    :param key:attribute name. +    :return: True/False. +    """ +    return True if h and key in h and h[key] == w[key] else False + +def _in_target(h, key): +    """ +    This function checks whether the target exist and key present in target config. +    :param h: target config. +    :param key: attribute name. +    :return: True/False. +    """ +    return True if h and key in h else False diff --git a/plugins/modules/vyos_facts.py b/plugins/modules/vyos_facts.py index 4a640663..5849519c 100644 --- a/plugins/modules/vyos_facts.py +++ b/plugins/modules/vyos_facts.py @@ -48,7 +48,7 @@ options:        used with an initial C(M(!)) to specify that a specific subset should not be        collected. Valid subsets are 'all', 'interfaces', 'l3_interfaces', 'lag_interfaces',        'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global', -      'firewall_interfaces'. +      'firewall_interfaces', 'ospfv3'.      required: false  """ diff --git a/plugins/modules/vyos_ospfv3.py b/plugins/modules/vyos_ospfv3.py new file mode 100644 index 00000000..5b4b8467 --- /dev/null +++ b/plugins/modules/vyos_ospfv3.py @@ -0,0 +1,181 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +#                WARNING                    # +############################################# +# +# This file is auto generated by the resource +#   module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +#   by the resource module builder. +# +# Changes should be made in the model used to +#   generate this file or in the resource module +#   builder template. +# +############################################# + +""" +The module file for vyos_ospfv3 +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { +    'metadata_version': '1.1', +    'status': ['preview'], +    'supported_by': 'network' +} + +DOCUMENTATION = """ +--- +module: vyos_ospfv3 +version_added: 2.10 +short_description: Manages attributes of OSPF IPv6 routes on VyOS network devices. +description: This module manages attributes of OSPF IPv6 routes on VyOS network devices. +author: Rohit Thakur (@rohitthakur2590) +options: +  config: +    description: A provided OSPF route configuration. +    type: list +    elements: dict +    suboptions: +      ospf_area: +        description: OSPF area. +        type: list +        elements: dict +        suboptions: +          area: +            description: Area name/identity. +            type: str +          export_list: +            description: Name of export-list. +            type: str +          import_list: +            description: Name of import-list. +            type: str +          range: +            description: Summarize routes matching prefix (border routers only). +            type: list +            elements: dict +            suboptions: +              address: +                description: border router IPv4 address. +                type: str +              advertise: +                description: Advertise this range. +                type: bool +              not_advertise: +                description: Don't advertise this range. +                type: bool +      parameters: +        descriptions: OSPFv3 specific parameters. +        type: dict +        suboptions:  +         router_id:  +           description: Override the default router identifier. +           type: str +      redistribute:   +        description: Redistribute information from another routing protocol. +        type: list +        elements: dict +        suboptions: +          route_type: +            description: Route type to redistribute. +            type: str +            choices: ['bgp', 'connected', 'kernel', 'ripng', 'static']  +          route_map: +            description: Route map references. +            type: str  +  state: +    description: +      - The state the configuration should be left in. +    type: str +    choices: +    - merged +    - replaced +    - deleted +    - parsed +    - gathered +    - rendered +    default: merged +""" +EXAMPLES = """ + + + + + + + + + + + + + + + + + + +""" +RETURN = """ +before: +  description: The configuration prior to the model invocation. +  returned: always +  sample: > +    The configuration returned will always be in the same format +     of the parameters above. +after: +  description: The resulting configuration model invocation. +  returned: when changed +  sample: > +    The configuration returned will always be in the same format +     of the parameters above. +commands: +  description: The set of commands pushed to the remote device. +  returned: always +  type: list +  sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv3.ospfv3 import Ospfv3Args +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv3.ospfv3 import Ospfv3 + + +def main(): +    """ +    Main entry point for module execution + +    :returns: the result form module invocation +    """ +    required_if = [ +        ("state", "merged", ("config",)), +        ("state", "replaced", ("config",)), +        ("state", "parsed", ("running_config",)), +    ] +    mutually_exclusive = [("config", "running_config")] +    module = AnsibleModule( +        argument_spec=Ospfv3Args.argument_spec, +        required_if=required_if, +        supports_check_mode=True, +        mutually_exclusive=mutually_exclusive, +    ) + +    result = Ospfv3(module).execute_module() +    module.exit_json(**result) + + +if __name__ == '__main__': +    main() | 
