summaryrefslogtreecommitdiff
path: root/plugins/module_utils
diff options
context:
space:
mode:
authorRohit Thakur <rohitthakur2590@outlook.com>2020-04-02 00:37:06 +0530
committerRohit Thakur <rohitthakur2590@outlook.com>2020-05-11 19:27:16 +0530
commit37289b45840129f2296fbc9cff9a3eab97bdb2a5 (patch)
treeb3b80384e7e8795911562726c5d76b2789d1cda9 /plugins/module_utils
parent62ddab1affb33cb83b7a31ae6076e73c23dd1d0a (diff)
downloadvyos-ansible-collection-37289b45840129f2296fbc9cff9a3eab97bdb2a5.tar.gz
vyos-ansible-collection-37289b45840129f2296fbc9cff9a3eab97bdb2a5.zip
vyos ospfv2 rm added
Signed-off-by: Rohit Thakur <rohitthakur2590@outlook.com>
Diffstat (limited to 'plugins/module_utils')
-rw-r--r--plugins/module_utils/network/vyos/argspec/ospfv2/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py359
-rw-r--r--plugins/module_utils/network/vyos/config/ospfv2/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py570
-rw-r--r--plugins/module_utils/network/vyos/facts/facts.py3
-rw-r--r--plugins/module_utils/network/vyos/facts/ospfv2/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py395
-rw-r--r--plugins/module_utils/network/vyos/utils/utils.py9
8 files changed, 1329 insertions, 7 deletions
diff --git a/plugins/module_utils/network/vyos/argspec/ospfv2/__init__.py b/plugins/module_utils/network/vyos/argspec/ospfv2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/ospfv2/__init__.py
diff --git a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py
new file mode 100644
index 0000000..1b11d3c
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py
@@ -0,0 +1,359 @@
+#
+# -*- 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_ospfv2 module
+"""
+
+
+class Ospfv2Args(object): # pylint: disable=R0903
+ """The arg spec for the vyos_ospfv2 module
+ """
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'auto_cost': {
+ 'options': {
+ 'reference_bandwidth': {
+ 'type': 'int'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'default_information': {
+ 'options': {
+ 'originate': {
+ 'options': {
+ 'always': {
+ 'type': 'bool'
+ },
+ 'metric': {
+ 'type': 'int'
+ },
+ 'metric_type': {
+ 'type': 'int'
+ },
+ 'route_map': {
+ 'type': 'str'
+ }
+ },
+ 'type': 'dict'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'default_metric': {
+ 'type': 'int'
+ },
+ 'distance': {
+ 'options': {
+ 'global': {
+ 'type': 'int'
+ },
+ 'ospf': {
+ 'options': {
+ 'external': {
+ 'type': 'int'
+ },
+ 'inter_area': {
+ 'type': 'int'
+ },
+ 'intra_area': {
+ 'type': 'int'
+ }
+ },
+ 'type': 'dict'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'log_adjacency_changes': {
+ 'choices': ['detail'],
+ 'type': 'str'
+ },
+ 'max_metric': {
+ 'options': {
+ 'router_lsa': {
+ 'options': {
+ 'administrative': {
+ 'type': 'bool'
+ },
+ 'on_shutdown': {
+ 'type': 'int'
+ },
+ 'on_startup': {
+ 'type': 'int'
+ }
+ },
+ 'type': 'dict'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'mpls_te': {
+ 'options': {
+ 'enabled': {
+ 'type': 'bool'
+ },
+ 'router_address': {
+ 'type': 'str'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'neighbor': {
+ 'elements': 'dict',
+ 'options': {
+ 'neighbor_id': {
+ 'type': 'str'
+ },
+ 'poll_interval': {
+ 'type': 'int'
+ },
+ 'priority': {
+ 'type': 'int'
+ }
+ },
+ 'type': 'list'
+ },
+ 'ospf_area': {
+ 'elements': 'dict',
+ 'options': {
+ 'area': {
+ 'type': 'str'
+ },
+ 'area_type': {
+ 'options': {
+ 'normal': {
+ 'type': 'bool'
+ },
+ 'nssa': {
+ 'options': {
+ 'default_cost': {
+ 'type': 'int'
+ },
+ 'no_summary': {
+ 'type': 'bool'
+ },
+ 'translate': {
+ 'choices':
+ ['always', 'candidate', 'never'],
+ 'type':
+ 'str'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'stub': {
+ 'options': {
+ 'default_cost': {
+ 'type': 'int'
+ },
+ 'no_summary': {
+ 'type': 'bool'
+ }
+ },
+ 'type': 'dict'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'authentication': {
+ 'choices': ['plaintext-password', 'md5'],
+ 'type': 'str'
+ },
+ 'network': {
+ 'elements': 'dict',
+ 'options': {
+ 'address': {
+ 'required': True,
+ 'type': 'str'
+ }
+ },
+ 'type': 'list'
+ },
+ 'range': {
+ 'elements': 'dict',
+ 'options': {
+ 'address': {
+ 'type': 'str'
+ },
+ 'cost': {
+ 'type': 'int'
+ },
+ 'not_advertise': {
+ 'type': 'bool'
+ },
+ 'substitute': {
+ 'type': 'str'
+ }
+ },
+ 'type': 'list'
+ },
+ 'shortcut': {
+ 'choices': ['default', 'disable', 'enable'],
+ 'type': 'str'
+ },
+ 'virtual_link': {
+ 'elements': 'dict',
+ 'options': {
+ 'address': {
+ 'type': 'str'
+ },
+ 'authentication': {
+ 'options': {
+ 'md5': {
+ 'options': {
+ 'key_id': {
+ 'type': 'int'
+ },
+ 'md5_key': {
+ 'type': 'str'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'plaintext_password': {
+ 'type': 'str'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'dead_interval': {
+ 'type': 'int'
+ },
+ 'hello_interval': {
+ 'type': 'int'
+ },
+ 'retransmit_interval': {
+ 'type': 'int'
+ },
+ 'transmit_delay': {
+ 'type': 'int'
+ }
+ },
+ 'type': 'list'
+ }
+ },
+ 'type': 'list'
+ },
+ 'parameters': {
+ 'options': {
+ 'abr_type': {
+ 'choices':
+ ['cisco', 'ibm', 'shortcut', 'standard'],
+ 'type': 'str'
+ },
+ 'opaque_lsa': {
+ 'type': 'bool'
+ },
+ 'rfc1583_compatibility': {
+ 'type': 'bool'
+ },
+ 'router_id': {
+ 'type': 'str'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'passive_interface': {
+ 'type': 'list'
+ },
+ 'passive_interface_exclude': {
+ 'type': 'list'
+ },
+ 'redistribute': {
+ 'elements': 'dict',
+ 'options': {
+ 'metric': {
+ 'type': 'int'
+ },
+ 'metric_type': {
+ 'type': 'int'
+ },
+ 'route_map': {
+ 'type': 'str'
+ },
+ 'route_type': {
+ 'choices':
+ ['bgp', 'connected', 'kernel', 'rip', 'static'],
+ 'type':
+ 'str'
+ }
+ },
+ 'type': 'list'
+ },
+ 'route_map': {
+ 'type': 'list'
+ },
+ 'timers': {
+ 'options': {
+ 'refresh': {
+ 'options': {
+ 'timers': {
+ 'type': 'int'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'throttle': {
+ 'options': {
+ 'spf': {
+ 'options': {
+ 'delay': {
+ 'type': 'int'
+ },
+ 'initial_holdtime': {
+ 'type': 'int'
+ },
+ 'max_holdtime': {
+ 'type': 'int'
+ }
+ },
+ 'type': 'dict'
+ }
+ },
+ 'type': 'dict'
+ }
+ },
+ 'type': 'dict'
+ }
+ },
+ '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/ospfv2/__init__.py b/plugins/module_utils/network/vyos/config/ospfv2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/ospfv2/__init__.py
diff --git a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py
new file mode 100644
index 0000000..0109ca1
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py
@@ -0,0 +1,570 @@
+#
+# -*- 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_ospfv2 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 Ospfv2(ConfigBase):
+ """
+ The vyos_ospfv2 class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'ospfv2',
+ ]
+
+ def __init__(self, module):
+ super(Ospfv2, self).__init__(module)
+
+ def get_ospfv2_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)
+ ospfv2_facts = facts['ansible_network_resources'].get('ospfv2')
+ if not ospfv2_facts:
+ return []
+ return ospfv2_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_ospfv2_facts = self.get_ospfv2_facts()
+ else:
+ existing_ospfv2_facts = []
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_ospfv2_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_ospfv2_facts = self.get_ospfv2_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_ospfv2_facts(
+ data=running_config
+ )
+ else:
+ changed_ospfv2_facts = []
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_ospfv2_facts
+ if result["changed"]:
+ result["after"] = changed_ospfv2_facts
+ elif self.state == "gathered":
+ result["gathered"] = changed_ospfv2_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_ospfv2_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_ospfv2_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 ospf')
+ 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))
+ leaf = (
+ "default_metric",
+ "log_adjacency_changes"
+ )
+ if w:
+ for key, val in iteritems(w):
+ if opr and key in leaf and not _is_w_same(w, have, key):
+ commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr))
+ elif not opr and key in leaf and not _in_target(have, key):
+ commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr))
+ else:
+ 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 in ("neighbor", "redistribute"):
+ commands.extend(self._render_list_dict_param(key, w, h, opr=opr))
+ elif key in ("default_information", 'max_metric'):
+ commands.extend(self._render_nested_dict_param(key, w, h, opr=opr))
+ elif key in ("mpls_te", "auto_cost", "parameters", "auto_cost"):
+ commands.extend(self._render_dict_param(key, w, h, opr=opr))
+ elif key in ("route_map", "passive_interface", "passive_interface_exclude"):
+ commands.extend(self._render_list_param(key, w, h, opr=opr))
+ elif key == "ospf_area":
+ commands.extend(self._render_ospf_area(key, w, h, opr=opr))
+ elif key == "timers":
+ commands.extend(self._render_timers(key, w, h, opr=opr, remove=remove))
+ elif key == "distance":
+ commands.extend(self._render_distance(key, w, h, opr=opr, remove=remove))
+ 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 = {'auto_cost': 'reference_bandwidth',
+ 'mpls_te': ('enabled', 'router_address'),
+ 'parameters': ("router_id", "abr_type", "opaque_lsa", "rfc1583_compatibility")}
+ 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):
+ if item == "enabled":
+ item = 'enable'
+ if item in ("opaque_lsa", "enable", "rfc1583_compatibility"):
+ commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr))
+ else:
+ 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):
+ if item == "enabled":
+ commands.append(self._form_attr_cmd(key=attr, attr='enable', opr=opr))
+ else:
+ 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',
+ 'neighbor': 'neighbor_id',
+ 'range': 'address',
+ 'vlink': 'address'}
+ leaf_dict = {'redistribute': ("metric", "route_map", "route_type", "metric_type"),
+ 'neighbor': ("priority", "poll_interval", "neighbor_id"),
+ 'range': ("cost", "address", "substitute", "not_advertise"),
+ 'vlink': ("address", "dead_interval", "transmit_delay", "hello_interval", "retransmit_interval")
+ }
+ 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 == 'cost':
+ commands.append(cmd + attr + ' ' + w_item[name[attr]] + ' ' + key + ' ' + str(val))
+ elif key == 'not_advertise':
+ commands.append(cmd + attr + ' ' + w_item[name[attr]] + ' ' + key.replace("_","-"))
+ elif key in ('route_type', 'neighbor_id', 'address'):
+ commands.append(cmd + attr + ' ' + str(val))
+ elif key == 'authentication':
+ commands.append(self._render_vlink(key, w_item, h_item, cmd, opr))
+ 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', 'neighbor_id', 'address'):
+ commands.append(cmd + attr + ' ' + str(val))
+ else:
+ commands.append(cmd + (attr + ' ' + w_item[name[attr]] + ' ' + key))
+ return commands
+
+ def _render_nested_dict_param(self, attr, want, have, opr=True):
+ """
+ This function forms the set/delete commands based on the 'opr' type
+ for attributes with in desired nested dicts.
+ :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 = []
+ attr_dict = {'default_information': 'originate',
+ 'max_metric': 'router_lsa',
+ }
+ leaf_dict = {'default_information': ("always", "metric", "metric_type", "route_map"),
+ 'max_metric': ("always", "metric", "metric_type", "route_map"),
+ }
+ h = {}
+ w = want.get(attr) or {}
+ if have:
+ h = have.get(attr) or {}
+ if not opr and not h:
+ commands.append(self._form_attr_cmd(attr=attr, opr=opr))
+ elif w:
+ key = attr_dict[attr]
+ w_attrib = want[attr].get(key) or {}
+ cmd = self._compute_command(opr=opr)
+ h_attrib = {}
+ if w_attrib:
+ leaf = leaf_dict[attr]
+ if h and key in h.keys():
+ h_attrib = h.get(key) or {}
+ for item, val in iteritems(w[key]):
+ if opr and item in leaf and not _is_w_same(w[key], h_attrib, item):
+ if item in ('administrative', 'always'):
+ commands.append(cmd + (attr.replace("_","-") + " " + key.replace("_","-") + " " + item.replace("_","-")))
+ else:
+ commands.append(cmd + (attr.replace("_","-") + " " + key.replace("_","-") + " " + item.replace("_","-") + " " + str(val)))
+
+ elif not opr and item in leaf and not _in_target(h_attrib, item):
+ commands.append(cmd + (attr + " " + item))
+ 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", "shortcut", "authentication")
+ 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 == 'area_type':
+ commands.extend(self._render_area_type(w_area, h_area, key, cmd, opr))
+ elif key == 'network':
+ commands.extend(self._render_list_param(key, w_area, h_area, cmd, opr))
+ elif key == 'range':
+ commands.extend(self._render_list_dict_param(key, w_area, h_area, cmd, opr))
+ elif key == 'virtual_link':
+ commands.extend(self._render_vlink(key, w_area, h_area, cmd, opr))
+ return commands
+
+ def _render_area_type(self, want, have, attr, cmd, opr=True):
+ """
+ This function forms the set/delete commands based on the 'opr' type
+ for area_types attributes.
+ :param attr: attribute name.
+ :param w: the desired config.
+ :param h: the target config.
+ :param cmd: command to prepend.
+ :param opr: True/False.
+ :return: generated commands list.
+ """
+ commands = []
+ h_type = {}
+ w_type = want.get(attr) or []
+ if have:
+ h_type = have.get(attr) or {}
+ if not opr and not h_type:
+ commands.append(cmd + attr.replace("_","-"))
+ elif w_type:
+ key = "normal"
+ if opr and key in w_type.keys() and not _is_w_same(w_type, h_type, key):
+ commands.append(cmd + attr.replace("_","-") + ' ' + key)
+ elif not opr and key in w_type.keys() and not (h_type and key in h_type.keys()):
+ commands.append(cmd + want['area'] + ' ' + attr.replace("_","-"))
+
+ a_type = {'nssa': ("default_cost", "no_summary", "translate"),
+ 'stub': ("default_cost", "no_summary")}
+ for key in a_type:
+ w_area = want[attr].get(key) or {}
+ h_area = {}
+ if w_area:
+ if h_type and key in h_type.keys():
+ h_area = h_type.get(key) or {}
+ for item, val in iteritems(w_type[key]):
+ if opr and item in a_type[key] and not _is_w_same(w_type[key], h_area, item):
+ commands.append(cmd + (attr.replace("_","-") + " " + key + " " + item.replace("_","-") + " " + str(val)))
+ elif not opr and item in a_type[key] and not (h_type and key in h_type):
+ commands.append(cmd + (want['area'] + ' ' + attr.replace("_","-") + " " + key + " " + item.replace("_","-")))
+ 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 ospf "
+ else:
+ cmd = "set protocols ospf "
+ 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 3c87be6..17c7aa3 100644
--- a/plugins/module_utils/network/vyos/facts/facts.py
+++ b/plugins/module_utils/network/vyos/facts/facts.py
@@ -42,6 +42,8 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firew
)
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.ospfv2.ospfv2 import (
+ Ospfv2Facts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import (
Default,
@@ -62,6 +64,7 @@ FACT_RESOURCE_SUBSETS = dict(
firewall_global=Firewall_globalFacts,
firewall_interfaces=Firewall_interfacesFacts,
ospfv3=Ospfv3Facts,
+ ospfv2=Ospfv2Facts,
)
diff --git a/plugins/module_utils/network/vyos/facts/ospfv2/__init__.py b/plugins/module_utils/network/vyos/facts/ospfv2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/ospfv2/__init__.py
diff --git a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py
new file mode 100644
index 0000000..3457fac
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py
@@ -0,0 +1,395 @@
+#
+# -*- 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 ospfv2 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.ospfv2.ospfv2 import Ospfv2Args
+
+
+class Ospfv2Facts(object):
+ """ The vyos ospfv2 fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Ospfv2Args.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 ospfv2
+ :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 = []
+ ospfv2 = findall(r"^set protocols ospf (.+)", data, M)
+ if ospfv2:
+ config = self.render_config(ospfv2)
+ if config:
+ objs.append(config)
+ ansible_facts["ansible_network_resources"].pop("ospfv2", None)
+ facts = {}
+ if objs:
+ facts["ospfv2"] = []
+ params = utils.validate_config(self.argument_spec, {"config": objs})
+ for cfg in params["config"]:
+ facts["ospfv2"].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))
+ a_lst = ["default_metric", "log_adjacency_changes"]
+ config = self.parse_attr(conf, a_lst)
+
+ if not config:
+ config = {}
+ config["timers"] = self.parse_timers(conf)
+ config["auto_cost"] = self.parse_auto_cost(conf)
+ config["distance"] = self.parse_distance(conf)
+ config["max_metric"] = self.parse_max_metric(conf)
+ config["mpls_te"] = self.parse_attrib(conf, "mpls_te", "mpls-te")
+ config["default_information"] = self.parse_def_info(conf)
+ config["parameters"] = self.parse_attrib(conf, "parameters", "parameters")
+ config["route_map"] = self.parse_leaf_list(conf, "route-map")
+ config["ospf_area"] = self.parse_attrib_list(conf, "area", "area")
+ config["neighbor"] = self.parse_attrib_list(conf, "neighbor", "neighbor_id")
+ config["passive_interface"] = self.parse_leaf_list(conf, "passive-interface")
+ config["redistribute"] = self.parse_attrib_list(conf, "redistribute", "route_type")
+ config["passive_interface_exclude"] = self.parse_leaf_list(conf, "passive-interface-exclude")
+ return config
+
+ def parse_timers(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'timers' attributes
+ :param conf: configuration
+ :param attrib: attribute name
+ :return: generated config dictionary
+ """
+ cfg_dict = {}
+ cfg_dict["refresh"] = self.parse_refresh(conf, "refresh")
+ cfg_dict["throttle"] = self.parse_throttle(conf, "spf")
+ return cfg_dict
+
+ def parse_throttle(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'throttle' attributes
+ :param conf: configuration
+ :param attrib: 'spf'
+ :return: generated config dictionary
+ """
+ cfg_dict = {}
+ cfg_dict[attrib] = self.parse_attrib(conf, attrib, match=attrib)
+ return cfg_dict
+
+ def parse_refresh(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'refresh' attributes
+ :param conf: configuration
+ :param attrib: 'refresh'
+ :return: generated config dictionary
+ """
+ cfg_dict = self.parse_attr(conf, ["timers"], match=attrib)
+ return cfg_dict
+
+ 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)
+ elif attrib == 'virtual-link':
+ obj = self.parse_vlink(cfg)
+ 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_leaf_list(self, conf, attrib):
+ """
+ This function forms the regex to fetch the listed attributes
+ from the configuration data
+ :param conf: configuration data
+ :param attrib: attribute name
+ :return: generated rule list configuration
+ """
+ lst = []
+ items = findall(r"^" + attrib + " (?:\'*)(\S+)(?:\'*)", conf, M)
+ if items:
+ for i in set(items):
+ lst.append(i.strip("'"))
+ return lst
+
+ def parse_distance(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'distance' attributes
+ :param conf: configuration
+ :param attrib: attribute name
+ :return: generated config dictionary
+ """
+ cfg_dict = self.parse_attr(conf, ["global"], match=attrib)
+ cfg_dict["ospf"] = self.parse_ospf(conf, "ospf")
+ return cfg_dict
+
+ def parse_ospf(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'distance ospf' attributes
+ :param conf: configuration
+ :param attrib: 'ospf'
+ :return: generated config dictionary
+ """
+ cfg_dict = self.parse_attrib(conf, 'ospf', match=attrib)
+ return cfg_dict
+
+ def parse_max_metric(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'max_metric' attributes
+ :param conf: configuration
+ :param attrib: attribute name
+ :return: generated config dictionary
+ """
+ cfg_dict = {}
+ cfg_dict["router_lsa"] = self.parse_attrib(conf, "router_lsa", match="router-lsa")
+ return cfg_dict
+
+ def parse_auto_cost(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'auto_cost' attributes
+ :param conf: configuration
+ :param attrib: attribute name
+ :return: generated config dictionary
+ """
+ cfg_dict = self.parse_attr(conf, ["reference_bandwidth"], match=attrib)
+ return cfg_dict
+
+ def parse_def_info(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'default_information' attributes
+ :param conf: configuration
+ :param attrib: attribute name
+ :return: generated config dictionary
+ """
+ cfg_dict = {}
+ cfg_dict["originate"] = self.parse_attrib(conf, "originate", "originate")
+ return cfg_dict
+
+ 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.
+ """
+ rule = self.parse_attrib(conf, "area", match=area_id)
+ r_sub = {
+ "area_type": self.parse_area_type(conf, "area-type"),
+ "network": self.parse_network(conf),
+ "range": self.parse_attrib_list(conf, "range", "address"),
+ "virtual_link": self.parse_attrib_list(conf, "virtual-link", "address")
+ }
+ rule.update(r_sub)
+ return rule
+
+ def parse_area_type(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'area_type' attributes
+ :param conf: configuration
+ :param attrib: 'area-type'
+ :return: generated config dictionary
+ """
+ cfg_dict = self.parse_attr(conf, ["normal"], match=attrib)
+ cfg_dict["nssa"] = self.parse_attrib(conf, "nssa")
+ cfg_dict["stub"] = self.parse_attrib(conf, "stub")
+ return cfg_dict
+
+ def parse_network(self, conf):
+ """
+ This function forms the regex to fetch the 'network'
+ :param conf: configuration data
+ :return: generated rule list configuration
+ """
+ a_lst = []
+ applications = findall(r"network (.+)", conf, M)
+ if applications:
+ app_lst = []
+ for r in set(applications):
+ obj = {"address": r.strip("'")}
+ app_lst.append(obj)
+ a_lst = sorted(app_lst, key=lambda i: i["address"])
+ return a_lst
+
+ def parse_vlink(self, conf):
+ """
+ This function triggers the parsing of 'vitual_link' attributes
+ :param conf: configuration data
+ :return: generated rule configuration dictionary
+ """
+ rule = self.parse_attrib(conf, 'vlink')
+ r_sub = {"authentication": self.parse_authentication(conf, "authentication")}
+ rule.update(r_sub)
+ return rule
+
+ def parse_authentication(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'authentication' attributes.
+ :param conf: configuration
+ :param attrib: 'authentication'
+ :return: generated config dictionary
+ """
+ cfg_dict = self.parse_attr(conf, ["plaintext_password"], match=attrib)
+ cfg_dict["md5"] = self.parse_md5(conf, "md5")
+ return cfg_dict
+
+ def parse_md5(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'md5' attributes
+ :param conf: configuration
+ :param attrib: 'md5'
+ :return: generated config dictionary
+ """
+ cfg_dict = self.parse_attr(conf, ["key_id"], match=attrib)
+ 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 = {
+ 'stub': ["default_cost", "no_summary"],
+ 'area': ["shortcut", "authentication"],
+ 'mpls_te': ["enabled", "router_address"],
+ 'neighbor': ["priority", "poll_interval"],
+ 'ospf': ["external", "inter_area", "intra_area"],
+ 'nssa': ["translate", "default_cost", "no_summary"],
+ 'redistribute': ["metric", "metric_type", "route_map"],
+ 'spf': ["delay", "max_holdtime", "initial_holdtime"],
+ 'range': ["cost", "substitute", "not_advertise"],
+ 'originate': ["always", "metric", "metric_type", "route_map"],
+ 'router_lsa': ["administrative", "on_shutdown", "on_startup"],
+ 'config_routes': ["default_metric", "log_adjacency_changes"],
+ 'parameters': ["abr_type", "opaque_lsa", "router_id", "rfc1583_compatibility"],
+ 'vlink': ["dead_interval", "hello_interval", "transmit_delay", "retransmit_interval"]
+ }
+ 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 = ("always", "normal", "enabled", "opaque_lsa", "not_advertise", "administrative", "rfc1583_compatibility")
+ 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", "delay", "metric", "inter_area", "intra_area", "on_startup", "metric_type", "on_shutdown",
+ "max_holdtime", "default_metric", "initial_holdtime")
+ 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 96feddd..c7dc575 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,
@@ -238,13 +239,7 @@ def _bool_to_str(val):
:param val: bool value.
:return: enable/disable.
"""
- return (
- "enable"
- if str(val) == "True"
- else "disable"
- if str(val) == "False"
- else val
- )
+ return "enable" if str(val) == "True" else "disable" if str(val) == "False" else val
def _is_w_same(w, h, key):