summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRohit Thakur <rohitthakur2590@outlook.com>2020-04-02 17:12:08 +0530
committerRohit Thakur <rohitthakur2590@outlook.com>2020-04-02 17:12:08 +0530
commite31d8fce791afe62d13f60056dd3f6aaa4a6f859 (patch)
treefb4fb1f236c22b38b54556bf0b8bdc5d28ea8fdd
parent77e8b041b2983415ac36eb6264f6e385ac87b074 (diff)
downloadvyos.vyos-e31d8fce791afe62d13f60056dd3f6aaa4a6f859.tar.gz
vyos.vyos-e31d8fce791afe62d13f60056dd3f6aaa4a6f859.zip
vyos_ospfv3 resource module
Signed-off-by: Rohit Thakur <rohitthakur2590@outlook.com>
-rw-r--r--plugins/module_utils/network/vyos/argspec/ospfv3/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py106
-rw-r--r--plugins/module_utils/network/vyos/config/ospfv3/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py446
-rw-r--r--plugins/module_utils/network/vyos/facts/facts.py4
-rw-r--r--plugins/module_utils/network/vyos/facts/ospfv3/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py199
-rw-r--r--plugins/module_utils/network/vyos/utils/utils.py31
-rw-r--r--plugins/modules/vyos_facts.py2
-rw-r--r--plugins/modules/vyos_ospfv3.py181
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 0000000..e69de29
--- /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 0000000..b0166d8
--- /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 0000000..e69de29
--- /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 0000000..fa869da
--- /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 ff3d098..6ead7fe 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 0000000..e69de29
--- /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 0000000..c980795
--- /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 4635234..2d5b74b 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 4a64066..5849519 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 0000000..5b4b846
--- /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()