summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoransible-zuul[bot] <48994755+ansible-zuul[bot]@users.noreply.github.com>2020-05-12 17:36:03 +0000
committerGitHub <noreply@github.com>2020-05-12 17:36:03 +0000
commiteb43d61c1e54c54888f4cc89fccb814b8f4f6640 (patch)
tree582cbdd0c31461c5c373031ffed0296d55e3b26c
parent62ddab1affb33cb83b7a31ae6076e73c23dd1d0a (diff)
parentdbdefd7223dc2334eb4efcedc3e1481ef9c32576 (diff)
downloadvyos-ansible-collection-eb43d61c1e54c54888f4cc89fccb814b8f4f6640.tar.gz
vyos-ansible-collection-eb43d61c1e54c54888f4cc89fccb814b8f4f6640.zip
Merge pull request #16 from rohitthakur2590/vyos_ospfv2
VyOS: OSPFv2 Resource Module Reviewed-by: https://github.com/apps/ansible-zuul
-rw-r--r--plugins/module_utils/network/vyos/argspec/ospfv2/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py269
-rw-r--r--plugins/module_utils/network/vyos/config/ospfv2/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py949
-rw-r--r--plugins/module_utils/network/vyos/facts/facts.py4
-rw-r--r--plugins/module_utils/network/vyos/facts/ospfv2/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py499
-rw-r--r--plugins/module_utils/network/vyos/utils/utils.py2
-rw-r--r--plugins/modules/vyos_facts.py2
-rw-r--r--plugins/modules/vyos_ospfv2.py1814
-rw-r--r--plugins/modules/vyos_vlan.py4
-rw-r--r--tests/integration/targets/vyos_ospfv2/defaults/main.yaml3
-rw-r--r--tests/integration/targets/vyos_ospfv2/meta/main.yaml3
-rw-r--r--tests/integration/targets/vyos_ospfv2/tasks/cli.yaml19
-rw-r--r--tests/integration/targets/vyos_ospfv2/tasks/main.yaml4
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/_parsed_config.cfg29
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/_populate.yaml35
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/_remove_config.yaml6
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/deleted.yaml48
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/empty_config.yaml49
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml24
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/merged.yaml101
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml70
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml15
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml76
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/replaced.yaml100
-rw-r--r--tests/integration/targets/vyos_ospfv2/tests/cli/rtt.yaml149
-rw-r--r--tests/integration/targets/vyos_ospfv2/vars/main.yaml485
-rw-r--r--tests/unit/modules/network/vyos/fixtures/vyos_ospfv2_config.cfg9
-rw-r--r--tests/unit/modules/network/vyos/test_vyos_ospfv2.py435
30 files changed, 5199 insertions, 4 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..275aaf3
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py
@@ -0,0 +1,269 @@
+#
+# -*- 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": {
+ "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",
+ },
+ "areas": {
+ "elements": "dict",
+ "options": {
+ "area_id": {"type": "str"},
+ "area_type": {
+ "options": {
+ "normal": {"type": "bool"},
+ "nssa": {
+ "options": {
+ "default_cost": {"type": "int"},
+ "no_summary": {"type": "bool"},
+ "set": {"type": "bool"},
+ "translate": {
+ "choices": [
+ "always",
+ "candidate",
+ "never",
+ ],
+ "type": "str",
+ },
+ },
+ "type": "dict",
+ },
+ "stub": {
+ "options": {
+ "default_cost": {"type": "int"},
+ "no_summary": {"type": "bool"},
+ "set": {"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": {
+ "elements": "dict",
+ "options": {
+ "key_id": {"type": "int"},
+ "md5_key": {"type": "str"},
+ },
+ "type": "list",
+ },
+ "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": "dict",
+ },
+ "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..fd25c17
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py
@@ -0,0 +1,949 @@
+#
+# -*- 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", {})
+ 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 == "deleted":
+ commands.extend(self._state_deleted(h))
+ elif self.state in ("merged", "rendered"):
+ commands.extend(self._state_merged(w, h))
+ elif self.state == "replaced":
+ commands.extend(self._state_replaced(w, 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 = []
+ if have:
+ commands.extend(self._render_ospf_param(have, want, opr=False))
+ commands.extend(self._render_ospf_param(want, have))
+ 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 = []
+ commands.extend(self._render_ospf_param(want, have))
+ return commands
+
+ def _state_deleted(self, 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 have:
+ 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 == "areas":
+ commands.extend(self._render_areas(key, w, h, opr=opr))
+ elif key == "timers":
+ commands.extend(self._render_timers(key, w, h, opr=opr))
+ elif key == "distance":
+ commands.extend(self._render_distance(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 = {
+ "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_vlink(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 = {"virtual_link": "address"}
+ leaf_dict = {
+ "virtual_link": (
+ "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(cmd + attr.replace("_", "-"))
+ 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 "address":
+ commands.append(
+ cmd + attr.replace("_", "-") + " " + str(val)
+ )
+ else:
+ commands.append(
+ cmd
+ + attr.replace("_", "-")
+ + " "
+ + 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 "address":
+ commands.append(
+ cmd + attr.replace("_", "-") + " " + str(val)
+ )
+ else:
+ commands.append(
+ cmd
+ + attr.replace("_", "-")
+ + " "
+ + w_item[name[attr]]
+ + " "
+ + key
+ )
+ elif key == "authentication":
+ commands.extend(
+ self._render_vlink_auth(
+ attr,
+ key,
+ w_item,
+ h_item,
+ w_item["address"],
+ cmd,
+ opr,
+ )
+ )
+ return commands
+
+ def _render_vlink_auth(
+ self, attr, key, want, have, address, 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 = []
+
+ w = want.get(key) or {}
+ if have:
+ h = have.get(key) or {}
+ cmd += attr.replace("_", "-") + " " + address + " " + key + " "
+ commands.extend(self._render_list_dict_param("md5", w, h, cmd, opr))
+ 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",
+ "md5": "key_id",
+ "vlink": "address",
+ }
+ leaf_dict = {
+ "md5": "md5_key",
+ "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 in (
+ "route_type",
+ "neighbor_id",
+ "address",
+ "key_id",
+ ):
+ commands.append(cmd + attr + " " + str(val))
+ elif 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 == "md5_key":
+ commands.append(
+ cmd
+ + attr
+ + " "
+ + "key-id"
+ + " "
+ + str(w_item[name[attr]])
+ + " "
+ + key.replace("_", "-")
+ + " "
+ + w_item[key]
+ )
+ 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",
+ "key_id",
+ ):
+ 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": ("administrative", "on_startup", "on_shutdown"),
+ }
+ 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") and val:
+ commands.append(
+ cmd
+ + attr.replace("_", "-")
+ + " "
+ + key.replace("_", "-")
+ + " "
+ + item.replace("_", "-")
+ )
+ elif item not in ("administrative", "always"):
+ 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_areas(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_id", "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_id"]),
+ opr=opr,
+ )
+ + " "
+ )
+ h_area = self.search_obj_in_have(h_lst, w_area, "area_id")
+ if not opr and not h_area:
+ commands.append(
+ self._form_attr_cmd(
+ key="area", attr=w_area["area_id"], 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_id":
+ commands.append(
+ self._form_attr_cmd(
+ attr="area",
+ 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_id" and not _in_target(
+ h_area, key
+ ):
+ commands.append(cmd)
+ continue
+ elif key != "area_id" 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)
+ ):
+ if not w_type[key] and h_type and h_type[key]:
+ commands.append(
+ cmd.replace("set", "delete")
+ + attr.replace("_", "-")
+ + " "
+ + key
+ )
+ elif w_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": ("set", "default_cost", "no_summary", "translate"),
+ "stub": ("set", "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)
+ ):
+ if item == "set" and val:
+ commands.append(
+ cmd + attr.replace("_", "-") + " " + key
+ )
+ elif not val and h_area and h_area[item]:
+ commands.append(
+ cmd.replace("set", "delete")
+ + attr.replace("_", "-")
+ + " "
+ + key
+ )
+ elif item != "set":
+ 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)
+ ):
+ if item == "set":
+ commands.append(
+ cmd + attr.replace("_", "-") + " " + key
+ )
+ else:
+ 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:
+ 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..4c7b340 100644
--- a/plugins/module_utils/network/vyos/facts/facts.py
+++ b/plugins/module_utils/network/vyos/facts/facts.py
@@ -43,6 +43,9 @@ 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,
Neighbors,
@@ -62,6 +65,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..d62fa9a
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py
@@ -0,0 +1,499 @@
+#
+# -*- 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 __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+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:
+ objs = self.render_config(ospfv2)
+ facts = {}
+ params = utils.validate_config(self.argument_spec, {"config": objs})
+ facts["ospfv2"] = utils.remove_empties(params["config"])
+ 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["default_information"] = self.parse_def_info(conf)
+ config["route_map"] = self.parse_leaf_list(conf, "route-map")
+ config["mpls_te"] = self.parse_attrib(conf, "mpls_te", "mpls-te")
+ config["areas"] = self.parse_attrib_list(conf, "area", "area_id")
+ config["parameters"] = self.parse_attrib(
+ conf, "parameters", "parameters"
+ )
+ 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):
+ """
+ This function triggers the parsing of 'timers' attributes
+ :param conf: configuration
+ :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_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("'"))
+ lst.sort()
+ 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):
+ """
+ This function triggers the parsing of 'max_metric' attributes
+ :param conf: configuration
+ :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):
+ """
+ This function triggers the parsing of 'default_information' attributes
+ :param conf: configuration
+ :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_id", 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_key(self, conf, key_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, "key_id", match=key_id)
+ 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", match="nssa")
+ cfg_dict["stub"] = self.parse_attrib(conf, "stub", match="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 'virtual_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_attrib_list(conf, "key-id", "key_id")
+ 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.replace("_", "-") + " (?:'*)(\\S+)(?:'*)",
+ conf,
+ M,
+ )
+ elif attrib == "key-id":
+ items = findall(
+ r"^.*" + attrib.replace("_", "-") + " (?:'*)(\\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)
+ elif attrib == "key-id":
+ obj = self.parse_key(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_attrib(self, conf, param, match=None):
+ """
+ This function triggers the parsing of 'ospf' attributes
+ :param conf: configuration data
+ :return: generated configuration dictionary
+ """
+
+ param_lst = {
+ "key_id": ["md5_key"],
+ "mpls_te": ["enabled", "router_address"],
+ "area_id": ["shortcut", "authentication"],
+ "neighbor": ["priority", "poll_interval"],
+ "stub": ["set", "default_cost", "no_summary"],
+ "range": ["cost", "substitute", "not_advertise"],
+ "ospf": ["external", "inter_area", "intra_area"],
+ "spf": ["delay", "max_holdtime", "initial_holdtime"],
+ "redistribute": ["metric", "metric_type", "route_map"],
+ "nssa": ["set", "translate", "default_cost", "no_summary"],
+ "config_routes": ["default_metric", "log_adjacency_changes"],
+ "originate": ["always", "metric", "metric_type", "route_map"],
+ "router_lsa": ["administrative", "on_shutdown", "on_startup"],
+ "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:
+ if attrib == "set" and conf.find(match) >= 1:
+ config[attrib] = True
+ 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 (
+ "area" if attrib == "area_id" 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 = (
+ "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",
+ "poll_interval",
+ "default_metric",
+ "initial_holdtime",
+ "key_id",
+ )
+ 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..7e0f3cc 100644
--- a/plugins/module_utils/network/vyos/utils/utils.py
+++ b/plugins/module_utils/network/vyos/utils/utils.py
@@ -113,7 +113,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:
diff --git a/plugins/modules/vyos_facts.py b/plugins/modules/vyos_facts.py
index 5849519..72b191c 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', 'ospfv3'.
+ 'firewall_interfaces', 'ospfv3', 'ospfv2'.
required: false
"""
diff --git a/plugins/modules/vyos_ospfv2.py b/plugins/modules/vyos_ospfv2.py
new file mode 100644
index 0000000..9c40d9c
--- /dev/null
+++ b/plugins/modules/vyos_ospfv2.py
@@ -0,0 +1,1814 @@
+#!/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_ospfv2
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+ANSIBLE_METADATA = {"metadata_version": "1.1", "supported_by": "Ansible"}
+
+DOCUMENTATION = """
+---
+module: vyos_ospfv2
+version_added: 2.10
+short_description: OSPFV2 resource module
+description: This resource module configures and manages attributes of OSPFv2 routes on VyOS network devices.
+version_added: "1.0.0"
+notes:
+ - Tested against VyOS 1.1.8 (helium).
+ - This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
+author:
+ - Rohit Thakur (@rohitthakur2590)
+options:
+ config:
+ description: A provided OSPFv2 route configuration.
+ type: dict
+ suboptions:
+ areas:
+ description: OSPFv2 area.
+ type: list
+ elements: dict
+ suboptions:
+ area_id:
+ description: OSPFv2 area identity.
+ type: str
+ area_type:
+ description: Area type.
+ type: dict
+ suboptions:
+ normal:
+ description: Normal OSPFv2 area.
+ type: bool
+ nssa:
+ description: NSSA OSPFv2 area.
+ type: dict
+ suboptions:
+ set:
+ description: Enabling NSSA.
+ type: bool
+ default_cost:
+ description: Summary-default cost of NSSA area.
+ type: int
+ no_summary:
+ description: Do not inject inter-area routes into stub.
+ type: bool
+ translate:
+ description: NSSA-ABR.
+ type: str
+ choices: ['always', 'candidate', 'never']
+ stub:
+ description: Stub OSPFv2 area.
+ type: dict
+ suboptions:
+ set:
+ description: Enabling stub.
+ type: bool
+ default_cost:
+ description: Summary-default cost of stub area.
+ type: int
+ no_summary:
+ description: Do not inject inter-area routes into stub.
+ type: bool
+ authentication:
+ description: OSPFv2 area authentication type.
+ type: str
+ choices: ['plaintext-password', 'md5']
+ network:
+ description: OSPFv2 network.
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ required: True
+ description: OSPFv2 IPv4 network address.
+ type: str
+ range:
+ description: Summarize routes matching prefix (border routers only).
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description: border router IPv4 address.
+ type: str
+ cost:
+ description: Metric for this range.
+ type: int
+ not_advertise:
+ description: Don't advertise this range.
+ type: bool
+ substitute:
+ description: Announce area range (IPv4 address) as another prefix.
+ type: str
+ shortcut:
+ description: Area's shortcut mode.
+ type: str
+ choices: ['default', 'disable', 'enable']
+ virtual_link:
+ description: Virtual link address.
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description: virtual link address.
+ type: str
+ authentication:
+ description: OSPFv2 area authentication type.
+ type: dict
+ suboptions:
+ md5:
+ description: MD5 key id based authentication.
+ type: list
+ elements: dict
+ suboptions:
+ key_id:
+ description: MD5 key id.
+ type: int
+ md5_key:
+ description: MD5 key.
+ type: str
+ plaintext_password:
+ description: Plain text password.
+ type: str
+ dead_interval:
+ description: Interval after which a neighbor is declared dead.
+ type: int
+ hello_interval:
+ description: Interval between hello packets.
+ type: int
+ retransmit_interval:
+ description: Interval between retransmitting lost link state advertisements.
+ type: int
+ transmit_delay:
+ description: Link state transmit delay.
+ type: int
+ log_adjacency_changes:
+ description: Log changes in adjacency state.
+ type: str
+ choices: ['detail']
+ max_metric:
+ description: OSPFv2 maximum/infinite-distance metric.
+ type: dict
+ suboptions:
+ router_lsa:
+ description: Advertise own Router-LSA with infinite distance (stub router).
+ type: dict
+ suboptions:
+ administrative:
+ description: Administratively apply, for an indefinite period.
+ type: bool
+ on_shutdown:
+ description: Time to advertise self as stub-router.
+ type: int
+ on_startup:
+ description: Time to advertise self as stub-router
+ type: int
+ auto_cost:
+ description: Calculate OSPFv2 interface cost according to bandwidth.
+ type: dict
+ suboptions:
+ reference_bandwidth:
+ description: Reference bandwidth cost in Mbits/sec.
+ type: int
+ default_information:
+ description: Control distribution of default information.
+ type: dict
+ suboptions:
+ originate:
+ description: Distribute a default route.
+ type: dict
+ suboptions:
+ always:
+ description: Always advertise default route.
+ type: bool
+ metric:
+ description: OSPFv2 default metric.
+ type: int
+ metric_type:
+ description: OSPFv2 Metric types for default routes.
+ type: int
+ route_map:
+ description: Route map references.
+ type: str
+ default_metric:
+ description: Metric of redistributed routes
+ type: int
+ distance:
+ description: Administrative distance.
+ type: dict
+ suboptions:
+ global:
+ description: Global OSPFv2 administrative distance.
+ type: int
+ ospf:
+ description: OSPFv2 administrative distance.
+ type: dict
+ suboptions:
+ external:
+ description: Distance for external routes.
+ type: int
+ inter_area:
+ description: Distance for inter-area routes.
+ type: int
+ intra_area:
+ description: Distance for intra-area routes.
+ type: int
+ mpls_te:
+ description: MultiProtocol Label Switching-Traffic Engineering (MPLS-TE) parameters.
+ type: dict
+ suboptions:
+ enabled:
+ description: Enable MPLS-TE functionality.
+ type: bool
+ router_address:
+ description: Stable IP address of the advertising router.
+ type: str
+
+ neighbor:
+ description: Neighbor IP address.
+ type: list
+ elements: dict
+ suboptions:
+ neighbor_id:
+ description: Identity (number/IP address) of neighbor.
+ type: str
+ poll_interval:
+ description: Seconds between dead neighbor polling interval.
+ type: int
+ priority:
+ description: Neighbor priority.
+ type: int
+ parameters:
+ descriptions: OSPFv2 specific parameters.
+ type: dict
+ suboptions:
+ abr_type:
+ description: OSPFv2 ABR Type.
+ type: str
+ choices: ['cisco', 'ibm', 'shortcut', 'standard']
+ opaque_lsa:
+ description: Enable the Opaque-LSA capability (rfc2370).
+ type: bool
+ rfc1583_compatibility:
+ description: Enable rfc1583 criteria for handling AS external routes.
+ type: bool
+ router_id:
+ description: Override the default router identifier.
+ type: str
+ passive_interface:
+ description: Suppress routing updates on an interface.
+ type: list
+ passive_interface_exclude:
+ description: Interface to exclude when using passive-interface default.
+ type: list
+ 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', 'rip', 'static']
+ metric:
+ description: Metric for redistribution routes.
+ type: int
+ metric_type:
+ description: OSPFv2 Metric types.
+ type: int
+ route_map:
+ description: Route map references.
+ type: str
+ route_map:
+ description: Filter routes installed in local route map.
+ type: list
+ timers:
+ description: Adjust routing timers.
+ type: dict
+ suboptions:
+ refresh:
+ description: Adjust refresh parameters.
+ type: dict
+ suboptions:
+ timers:
+ description: refresh timer.
+ type: int
+ throttle:
+ description: Throttling adaptive timers.
+ type: dict
+ suboptions:
+ spf:
+ description: OSPFv2 SPF timers.
+ type: dict
+ suboptions:
+ delay:
+ description: Delay (msec) from first change received till SPF calculation.
+ type: int
+ initial_holdtime:
+ description: Initial hold time(msec) between consecutive SPF calculations.
+ type: int
+ max_holdtime:
+ description: maximum hold time (sec).
+ type: int
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the VyOS device by executing
+ the command B(show configuration commands | grep ospf).
+ - The state I(parsed) reads the configuration from C(running_config) option and transforms
+ it into Ansible structured data as per the resource module's argspec and the value is then
+ returned in the I(parsed) key within the result.
+ type: str
+ state:
+ description:
+ - The state the configuration should be left in.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - deleted
+ - parsed
+ - gathered
+ - rendered
+ default: merged
+"""
+EXAMPLES = """
+# Using merged
+#
+# Before state:
+# -------------
+#
+# vyos@vyos# run show configuration commands | grep ospf
+#
+#
+- name: Merge the provided configuration with the existing running configuration
+ vyos.vyos.vyos_ospfv2:
+ config:
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ mpls_te:
+ enabled: true
+ router_address: '192.0.11.11'
+ auto_cost:
+ reference_bandwidth: 2
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ redistribute:
+ - route_type: 'bgp'
+ metric: 10
+ metric_type: 2
+ passive_interface:
+ - 'eth1'
+ - 'eth2'
+ parameters:
+ router_id: '192.0.1.1'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ abr_type: 'cisco'
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ network:
+ - address: '192.0.2.0/24'
+ range:
+ - address: '192.0.3.0/24'
+ cost: 10
+ - address: '192.0.4.0/24'
+ cost: 12
+ state: merged
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+# before": {}
+#
+# "commands": [
+# "set protocols ospf mpls-te enable",
+# "set protocols ospf mpls-te router-address '192.0.11.11'",
+# "set protocols ospf redistribute bgp",
+# "set protocols ospf redistribute bgp metric-type 2",
+# "set protocols ospf redistribute bgp metric 10",
+# "set protocols ospf default-information originate metric-type 2",
+# "set protocols ospf default-information originate always",
+# "set protocols ospf default-information originate metric 10",
+# "set protocols ospf default-information originate route-map ingress",
+# "set protocols ospf auto-cost reference-bandwidth '2'",
+# "set protocols ospf parameters router-id '192.0.1.1'",
+# "set protocols ospf parameters opaque-lsa",
+# "set protocols ospf parameters abr-type 'cisco'",
+# "set protocols ospf parameters rfc1583-compatibility",
+# "set protocols ospf passive-interface eth1",
+# "set protocols ospf passive-interface eth2",
+# "set protocols ospf max-metric router-lsa on-shutdown 10",
+# "set protocols ospf max-metric router-lsa administrative",
+# "set protocols ospf max-metric router-lsa on-startup 10",
+# "set protocols ospf log-adjacency-changes 'detail'",
+# "set protocols ospf neighbor 192.0.11.12 priority 2",
+# "set protocols ospf neighbor 192.0.11.12 poll-interval 10",
+# "set protocols ospf neighbor 192.0.11.12",
+# "set protocols ospf area '2'",
+# "set protocols ospf area 2 authentication plaintext-password",
+# "set protocols ospf area 2 shortcut enable",
+# "set protocols ospf area 2 area-type normal",
+# "set protocols ospf area '3'",
+# "set protocols ospf area 3 area-type nssa",
+# "set protocols ospf area 4 range 192.0.3.0/24 cost 10",
+# "set protocols ospf area 4 range 192.0.3.0/24",
+# "set protocols ospf area 4 range 192.0.4.0/24 cost 12",
+# "set protocols ospf area 4 range 192.0.4.0/24",
+# "set protocols ospf area 4 area-type stub default-cost 20",
+# "set protocols ospf area '4'",
+# "set protocols ospf area 4 network 192.0.2.0/24"
+# ]
+#
+# "after": {
+# "areas": [
+# {
+# "area_id": "2",
+# "area_type": {
+# "normal": true
+# },
+# "authentication": "plaintext-password",
+# "shortcut": "enable"
+# },
+# {
+# "area_id": "3",
+# "area_type": {
+# "nssa": {
+# "set": true
+# }
+# }
+# },
+# {
+# "area_id": "4",
+# "area_type": {
+# "stub": {
+# "default_cost": 20,
+# "set": true
+# }
+# },
+# "network": [
+# {
+# "address": "192.0.2.0/24"
+# }
+# ],
+# "range": [
+# {
+# "address": "192.0.3.0/24",
+# "cost": 10
+# },
+# {
+# "address": "192.0.4.0/24",
+# "cost": 12
+# }
+# ]
+# }
+# ],
+# "auto_cost": {
+# "reference_bandwidth": 2
+# },
+# "default_information": {
+# "originate": {
+# "always": true,
+# "metric": 10,
+# "metric_type": 2,
+# "route_map": "ingress"
+# }
+# },
+# "log_adjacency_changes": "detail",
+# "max_metric": {
+# "router_lsa": {
+# "administrative": true,
+# "on_shutdown": 10,
+# "on_startup": 10
+# }
+# },
+# "mpls_te": {
+# "enabled": true,
+# "router_address": "192.0.11.11"
+# },
+# "neighbor": [
+# {
+# "neighbor_id": "192.0.11.12",
+# "poll_interval": 10,
+# "priority": 2
+# }
+# ],
+# "parameters": {
+# "abr_type": "cisco",
+# "opaque_lsa": true,
+# "rfc1583_compatibility": true,
+# "router_id": "192.0.1.1"
+# },
+# "passive_interface": [
+# "eth2",
+# "eth1"
+# ],
+# "redistribute": [
+# {
+# "metric": 10,
+# "metric_type": 2,
+# "route_type": "bgp"
+# }
+# ]
+# }
+#
+# After state:
+# -------------
+#
+# vyos@192# run show configuration commands | grep ospf
+# set protocols ospf area 2 area-type 'normal'
+# set protocols ospf area 2 authentication 'plaintext-password'
+# set protocols ospf area 2 shortcut 'enable'
+# set protocols ospf area 3 area-type 'nssa'
+# set protocols ospf area 4 area-type stub default-cost '20'
+# set protocols ospf area 4 network '192.0.2.0/24'
+# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
+# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
+# set protocols ospf auto-cost reference-bandwidth '2'
+# set protocols ospf default-information originate 'always'
+# set protocols ospf default-information originate metric '10'
+# set protocols ospf default-information originate metric-type '2'
+# set protocols ospf default-information originate route-map 'ingress'
+# set protocols ospf log-adjacency-changes 'detail'
+# set protocols ospf max-metric router-lsa 'administrative'
+# set protocols ospf max-metric router-lsa on-shutdown '10'
+# set protocols ospf max-metric router-lsa on-startup '10'
+# set protocols ospf mpls-te 'enable'
+# set protocols ospf mpls-te router-address '192.0.11.11'
+# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
+# set protocols ospf neighbor 192.0.11.12 priority '2'
+# set protocols ospf parameters abr-type 'cisco'
+# set protocols ospf parameters 'opaque-lsa'
+# set protocols ospf parameters 'rfc1583-compatibility'
+# set protocols ospf parameters router-id '192.0.1.1'
+# set protocols ospf passive-interface 'eth1'
+# set protocols ospf passive-interface 'eth2'
+# set protocols ospf redistribute bgp metric '10'
+# set protocols ospf redistribute bgp metric-type '2'
+
+
+# Using merged
+#
+# Before state:
+# -------------
+#
+# vyos@vyos# run show configuration commands | grep ospf
+#
+#
+- name: Merge the provided configuration to update existing running configuration
+ vyos.vyos.vyos_ospfv2:
+ config:
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: false
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ network:
+ - address: '192.0.2.0/24'
+ - address: '192.0.22.0/24'
+ - address: '192.0.32.0/24'
+ state: merged
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+# "before": {
+# "areas": [
+# {
+# "area_id": "2",
+# "area_type": {
+# "normal": true
+# },
+# "authentication": "plaintext-password",
+# "shortcut": "enable"
+# },
+# {
+# "area_id": "3",
+# "area_type": {
+# "nssa": {
+# "set": true
+# }
+# }
+# },
+# {
+# "area_id": "4",
+# "area_type": {
+# "stub": {
+# "default_cost": 20,
+# "set": true
+# }
+# },
+# "network": [
+# {
+# "address": "192.0.2.0/24"
+# }
+# ],
+# "range": [
+# {
+# "address": "192.0.3.0/24",
+# "cost": 10
+# },
+# {
+# "address": "192.0.4.0/24",
+# "cost": 12
+# }
+# ]
+# }
+# ],
+# "auto_cost": {
+# "reference_bandwidth": 2
+# },
+# "default_information": {
+# "originate": {
+# "always": true,
+# "metric": 10,
+# "metric_type": 2,
+# "route_map": "ingress"
+# }
+# },
+# "log_adjacency_changes": "detail",
+# "max_metric": {
+# "router_lsa": {
+# "administrative": true,
+# "on_shutdown": 10,
+# "on_startup": 10
+# }
+# },
+# "mpls_te": {
+# "enabled": true,
+# "router_address": "192.0.11.11"
+# },
+# "neighbor": [
+# {
+# "neighbor_id": "192.0.11.12",
+# "poll_interval": 10,
+# "priority": 2
+# }
+# ],
+# "parameters": {
+# "abr_type": "cisco",
+# "opaque_lsa": true,
+# "rfc1583_compatibility": true,
+# "router_id": "192.0.1.1"
+# },
+# "passive_interface": [
+# "eth2",
+# "eth1"
+# ],
+# "redistribute": [
+# {
+# "metric": 10,
+# "metric_type": 2,
+# "route_type": "bgp"
+# }
+# ]
+# }
+#
+# "commands": [
+# "delete protocols ospf area 4 area-type stub",
+# "set protocols ospf area 4 network 192.0.22.0/24"
+# "set protocols ospf area 4 network 192.0.32.0/24"
+# ]
+#
+# "after": {
+# "areas": [
+# {
+# "area_id": "2",
+# "area_type": {
+# "normal": true
+# },
+# "authentication": "plaintext-password",
+# "shortcut": "enable"
+# },
+# {
+# "area_id": "3",
+# "area_type": {
+# "nssa": {
+# "set": true
+# }
+# }
+# },
+# {
+# "area_id": "4",
+# },
+# "network": [
+# {
+# "address": "192.0.2.0/24"
+# },
+# {
+# "address": "192.0.22.0/24"
+# },
+# {
+# "address": "192.0.32.0/24"
+# }
+# ],
+# "range": [
+# {
+# "address": "192.0.3.0/24",
+# "cost": 10
+# },
+# {
+# "address": "192.0.4.0/24",
+# "cost": 12
+# }
+# ]
+# }
+# ],
+# "auto_cost": {
+# "reference_bandwidth": 2
+# },
+# "default_information": {
+# "originate": {
+# "always": true,
+# "metric": 10,
+# "metric_type": 2,
+# "route_map": "ingress"
+# }
+# },
+# "log_adjacency_changes": "detail",
+# "max_metric": {
+# "router_lsa": {
+# "administrative": true,
+# "on_shutdown": 10,
+# "on_startup": 10
+# }
+# },
+# "mpls_te": {
+# "enabled": true,
+# "router_address": "192.0.11.11"
+# },
+# "neighbor": [
+# {
+# "neighbor_id": "192.0.11.12",
+# "poll_interval": 10,
+# "priority": 2
+# }
+# ],
+# "parameters": {
+# "abr_type": "cisco",
+# "opaque_lsa": true,
+# "rfc1583_compatibility": true,
+# "router_id": "192.0.1.1"
+# },
+# "passive_interface": [
+# "eth2",
+# "eth1"
+# ],
+# "redistribute": [
+# {
+# "metric": 10,
+# "metric_type": 2,
+# "route_type": "bgp"
+# }
+# ]
+# }
+#
+# After state:
+# -------------
+#
+# vyos@192# run show configuration commands | grep ospf
+# set protocols ospf area 2 area-type 'normal'
+# set protocols ospf area 2 authentication 'plaintext-password'
+# set protocols ospf area 2 shortcut 'enable'
+# set protocols ospf area 3 area-type 'nssa'
+# set protocols ospf area 4 network '192.0.2.0/24'
+# set protocols ospf area 4 network '192.0.22.0/24'
+# set protocols ospf area 4 network '192.0.32.0/24'
+# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
+# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
+# set protocols ospf auto-cost reference-bandwidth '2'
+# set protocols ospf default-information originate 'always'
+# set protocols ospf default-information originate metric '10'
+# set protocols ospf default-information originate metric-type '2'
+# set protocols ospf default-information originate route-map 'ingress'
+# set protocols ospf log-adjacency-changes 'detail'
+# set protocols ospf max-metric router-lsa 'administrative'
+# set protocols ospf max-metric router-lsa on-shutdown '10'
+# set protocols ospf max-metric router-lsa on-startup '10'
+# set protocols ospf mpls-te 'enable'
+# set protocols ospf mpls-te router-address '192.0.11.11'
+# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
+# set protocols ospf neighbor 192.0.11.12 priority '2'
+# set protocols ospf parameters abr-type 'cisco'
+# set protocols ospf parameters 'opaque-lsa'
+# set protocols ospf parameters 'rfc1583-compatibility'
+# set protocols ospf parameters router-id '192.0.1.1'
+# set protocols ospf passive-interface 'eth1'
+# set protocols ospf passive-interface 'eth2'
+# set protocols ospf redistribute bgp metric '10'
+# set protocols ospf redistribute bgp metric-type '2'
+
+
+# Using replaced
+#
+# Before state:
+# -------------
+#
+# vyos@192# run show configuration commands | grep ospf
+# set protocols ospf area 2 area-type 'normal'
+# set protocols ospf area 2 authentication 'plaintext-password'
+# set protocols ospf area 2 shortcut 'enable'
+# set protocols ospf area 3 area-type 'nssa'
+# set protocols ospf area 4 area-type stub default-cost '20'
+# set protocols ospf area 4 network '192.0.2.0/24'
+# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
+# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
+# set protocols ospf auto-cost reference-bandwidth '2'
+# set protocols ospf default-information originate 'always'
+# set protocols ospf default-information originate metric '10'
+# set protocols ospf default-information originate metric-type '2'
+# set protocols ospf default-information originate route-map 'ingress'
+# set protocols ospf log-adjacency-changes 'detail'
+# set protocols ospf max-metric router-lsa 'administrative'
+# set protocols ospf max-metric router-lsa on-shutdown '10'
+# set protocols ospf max-metric router-lsa on-startup '10'
+# set protocols ospf mpls-te 'enable'
+# set protocols ospf mpls-te router-address '192.0.11.11'
+# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
+# set protocols ospf neighbor 192.0.11.12 priority '2'
+# set protocols ospf parameters abr-type 'cisco'
+# set protocols ospf parameters 'opaque-lsa'
+# set protocols ospf parameters 'rfc1583-compatibility'
+# set protocols ospf parameters router-id '192.0.1.1'
+# set protocols ospf passive-interface 'eth1'
+# set protocols ospf passive-interface 'eth2'
+# set protocols ospf redistribute bgp metric '10'
+# set protocols ospf redistribute bgp metric-type '2'
+#
+- name: Replace ospfv2 routes attributes configuration.
+ vyos.vyos.vyos_ospfv2:
+ config:
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ mpls_te:
+ enabled: true
+ router_address: '192.0.22.22'
+ auto_cost:
+ reference_bandwidth: 2
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ redistribute:
+ - route_type: 'bgp'
+ metric: 10
+ metric_type: 2
+ passive_interface:
+ - 'eth1'
+ parameters:
+ router_id: '192.0.1.1'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ abr_type: 'cisco'
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ network:
+ - address: '192.0.2.0/24'
+ - address: '192.0.12.0/24'
+ - address: '192.0.22.0/24'
+ - address: '192.0.32.0/24'
+ range:
+ - address: '192.0.42.0/24'
+ cost: 10
+ state: replaced
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+# "before": {
+# "areas": [
+# {
+# "area_id": "2",
+# "area_type": {
+# "normal": true
+# },
+# "authentication": "plaintext-password",
+# "shortcut": "enable"
+# },
+# {
+# "area_id": "3",
+# "area_type": {
+# "nssa": {
+# "set": true
+# }
+# }
+# },
+# {
+# "area_id": "4",
+# "area_type": {
+# "stub": {
+# "default_cost": 20,
+# "set": true
+# }
+# },
+# "network": [
+# {
+# "address": "192.0.2.0/24"
+# }
+# ],
+# "range": [
+# {
+# "address": "192.0.3.0/24",
+# "cost": 10
+# },
+# {
+# "address": "192.0.4.0/24",
+# "cost": 12
+# }
+# ]
+# }
+# ],
+# "auto_cost": {
+# "reference_bandwidth": 2
+# },
+# "default_information": {
+# "originate": {
+# "always": true,
+# "metric": 10,
+# "metric_type": 2,
+# "route_map": "ingress"
+# }
+# },
+# "log_adjacency_changes": "detail",
+# "max_metric": {
+# "router_lsa": {
+# "administrative": true,
+# "on_shutdown": 10,
+# "on_startup": 10
+# }
+# },
+# "mpls_te": {
+# "enabled": true,
+# "router_address": "192.0.11.11"
+# },
+# "neighbor": [
+# {
+# "neighbor_id": "192.0.11.12",
+# "poll_interval": 10,
+# "priority": 2
+# }
+# ],
+# "parameters": {
+# "abr_type": "cisco",
+# "opaque_lsa": true,
+# "rfc1583_compatibility": true,
+# "router_id": "192.0.1.1"
+# },
+# "passive_interface": [
+# "eth2",
+# "eth1"
+# ],
+# "redistribute": [
+# {
+# "metric": 10,
+# "metric_type": 2,
+# "route_type": "bgp"
+# }
+# ]
+# }
+#
+# "commands": [
+# "delete protocols ospf passive-interface eth2",
+# "delete protocols ospf area 3",
+# "delete protocols ospf area 4 range 192.0.3.0/24 cost",
+# "delete protocols ospf area 4 range 192.0.3.0/24",
+# "delete protocols ospf area 4 range 192.0.4.0/24 cost",
+# "delete protocols ospf area 4 range 192.0.4.0/24",
+# "set protocols ospf mpls-te router-address '192.0.22.22'",
+# "set protocols ospf area 4 range 192.0.42.0/24 cost 10",
+# "set protocols ospf area 4 range 192.0.42.0/24",
+# "set protocols ospf area 4 network 192.0.12.0/24",
+# "set protocols ospf area 4 network 192.0.22.0/24",
+# "set protocols ospf area 4 network 192.0.32.0/24"
+# ]
+#
+# "after": {
+# "areas": [
+# {
+# "area_id": "2",
+# "area_type": {
+# "normal": true
+# },
+# "authentication": "plaintext-password",
+# "shortcut": "enable"
+# },
+# {
+# "area_id": "4",
+# "area_type": {
+# "stub": {
+# "default_cost": 20,
+# "set": true
+# }
+# },
+# "network": [
+# {
+# "address": "192.0.12.0/24"
+# },
+# {
+# "address": "192.0.2.0/24"
+# },
+# {
+# "address": "192.0.22.0/24"
+# },
+# {
+# "address": "192.0.32.0/24"
+# }
+# ],
+# "range": [
+# {
+# "address": "192.0.42.0/24",
+# "cost": 10
+# }
+# ]
+# }
+# ],
+# "auto_cost": {
+# "reference_bandwidth": 2
+# },
+# "default_information": {
+# "originate": {
+# "always": true,
+# "metric": 10,
+# "metric_type": 2,
+# "route_map": "ingress"
+# }
+# },
+# "log_adjacency_changes": "detail",
+# "max_metric": {
+# "router_lsa": {
+# "administrative": true,
+# "on_shutdown": 10,
+# "on_startup": 10
+# }
+# },
+# "mpls_te": {
+# "enabled": true,
+# "router_address": "192.0.22.22"
+# },
+# "neighbor": [
+# {
+# "neighbor_id": "192.0.11.12",
+# "poll_interval": 10,
+# "priority": 2
+# }
+# ],
+# "parameters": {
+# "abr_type": "cisco",
+# "opaque_lsa": true,
+# "rfc1583_compatibility": true,
+# "router_id": "192.0.1.1"
+# },
+# "passive_interface": [
+# "eth1"
+# ],
+# "redistribute": [
+# {
+# "metric": 10,
+# "metric_type": 2,
+# "route_type": "bgp"
+# }
+# ]
+# }
+#
+# After state:
+# -------------
+#
+# vyos@192# run show configuration commands | grep ospf
+# set protocols ospf area 2 area-type 'normal'
+# set protocols ospf area 2 authentication 'plaintext-password'
+# set protocols ospf area 2 shortcut 'enable'
+# set protocols ospf area 4 area-type stub default-cost '20'
+# set protocols ospf area 4 network '192.0.2.0/24'
+# set protocols ospf area 4 network '192.0.12.0/24'
+# set protocols ospf area 4 network '192.0.22.0/24'
+# set protocols ospf area 4 network '192.0.32.0/24'
+# set protocols ospf area 4 range 192.0.42.0/24 cost '10'
+# set protocols ospf auto-cost reference-bandwidth '2'
+# set protocols ospf default-information originate 'always'
+# set protocols ospf default-information originate metric '10'
+# set protocols ospf default-information originate metric-type '2'
+# set protocols ospf default-information originate route-map 'ingress'
+# set protocols ospf log-adjacency-changes 'detail'
+# set protocols ospf max-metric router-lsa 'administrative'
+# set protocols ospf max-metric router-lsa on-shutdown '10'
+# set protocols ospf max-metric router-lsa on-startup '10'
+# set protocols ospf mpls-te 'enable'
+# set protocols ospf mpls-te router-address '192.0.22.22'
+# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
+# set protocols ospf neighbor 192.0.11.12 priority '2'
+# set protocols ospf parameters abr-type 'cisco'
+# set protocols ospf parameters 'opaque-lsa'
+# set protocols ospf parameters 'rfc1583-compatibility'
+# set protocols ospf parameters router-id '192.0.1.1'
+# set protocols ospf passive-interface 'eth1'
+# set protocols ospf redistribute bgp metric '10'
+# set protocols ospf redistribute bgp metric-type '2'
+
+
+# Using rendered
+#
+#
+- name: Render the commands for provided configuration
+ vyos.vyos.vyos_ospfv2:
+ config:
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ mpls_te:
+ enabled: true
+ router_address: '192.0.11.11'
+ auto_cost:
+ reference_bandwidth: 2
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ redistribute:
+ - route_type: 'bgp'
+ metric: 10
+ metric_type: 2
+ passive_interface:
+ - 'eth1'
+ - 'eth2'
+ parameters:
+ router_id: '192.0.1.1'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ abr_type: 'cisco'
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ network:
+ - address: '192.0.2.0/24'
+ range:
+ - address: '192.0.3.0/24'
+ cost: 10
+ - address: '192.0.4.0/24'
+ cost: 12
+ state: rendered
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+#
+# "rendered": [
+# [
+# "set protocols ospf mpls-te enable",
+# "set protocols ospf mpls-te router-address '192.0.11.11'",
+# "set protocols ospf redistribute bgp",
+# "set protocols ospf redistribute bgp metric-type 2",
+# "set protocols ospf redistribute bgp metric 10",
+# "set protocols ospf default-information originate metric-type 2",
+# "set protocols ospf default-information originate always",
+# "set protocols ospf default-information originate metric 10",
+# "set protocols ospf default-information originate route-map ingress",
+# "set protocols ospf auto-cost reference-bandwidth '2'",
+# "set protocols ospf parameters router-id '192.0.1.1'",
+# "set protocols ospf parameters opaque-lsa",
+# "set protocols ospf parameters abr-type 'cisco'",
+# "set protocols ospf parameters rfc1583-compatibility",
+# "set protocols ospf passive-interface eth1",
+# "set protocols ospf passive-interface eth2",
+# "set protocols ospf max-metric router-lsa on-shutdown 10",
+# "set protocols ospf max-metric router-lsa administrative",
+# "set protocols ospf max-metric router-lsa on-startup 10",
+# "set protocols ospf log-adjacency-changes 'detail'",
+# "set protocols ospf neighbor 192.0.11.12 priority 2",
+# "set protocols ospf neighbor 192.0.11.12 poll-interval 10",
+# "set protocols ospf neighbor 192.0.11.12",
+# "set protocols ospf area '2'",
+# "set protocols ospf area 2 authentication plaintext-password",
+# "set protocols ospf area 2 shortcut enable",
+# "set protocols ospf area 2 area-type normal",
+# "set protocols ospf area '3'",
+# "set protocols ospf area 3 area-type nssa",
+# "set protocols ospf area 4 range 192.0.3.0/24 cost 10",
+# "set protocols ospf area 4 range 192.0.3.0/24",
+# "set protocols ospf area 4 range 192.0.4.0/24 cost 12",
+# "set protocols ospf area 4 range 192.0.4.0/24",
+# "set protocols ospf area 4 area-type stub default-cost 20",
+# "set protocols ospf area '4'",
+# "set protocols ospf area 4 network 192.0.2.0/24"
+# ]
+
+
+# Using parsed
+#
+#
+- name: Parse the commands for provided structured configuration
+ vyos.vyos.vyos_ospfv2:
+ running_config:
+ "set protocols ospf area 2 area-type 'normal'
+ set protocols ospf area 2 authentication 'plaintext-password'
+ set protocols ospf area 2 shortcut 'enable'
+ set protocols ospf area 3 area-type 'nssa'
+ set protocols ospf area 4 area-type stub default-cost '20'
+ set protocols ospf area 4 network '192.0.2.0/24'
+ set protocols ospf area 4 range 192.0.3.0/24 cost '10'
+ set protocols ospf area 4 range 192.0.4.0/24 cost '12'
+ set protocols ospf auto-cost reference-bandwidth '2'
+ set protocols ospf default-information originate 'always'
+ set protocols ospf default-information originate metric '10'
+ set protocols ospf default-information originate metric-type '2'
+ set protocols ospf default-information originate route-map 'ingress'
+ set protocols ospf log-adjacency-changes 'detail'
+ set protocols ospf max-metric router-lsa 'administrative'
+ set protocols ospf max-metric router-lsa on-shutdown '10'
+ set protocols ospf max-metric router-lsa on-startup '10'
+ set protocols ospf mpls-te 'enable'
+ set protocols ospf mpls-te router-address '192.0.11.11'
+ set protocols ospf neighbor 192.0.11.12 poll-interval '10'
+ set protocols ospf neighbor 192.0.11.12 priority '2'
+ set protocols ospf parameters abr-type 'cisco'
+ set protocols ospf parameters 'opaque-lsa'
+ set protocols ospf parameters 'rfc1583-compatibility'
+ set protocols ospf parameters router-id '192.0.1.1'
+ set protocols ospf passive-interface 'eth1'
+ set protocols ospf passive-interface 'eth2'
+ set protocols ospf redistribute bgp metric '10'
+ set protocols ospf redistribute bgp metric-type '2'"
+ state: parsed
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+#
+# "parsed": {
+# "areas": [
+# {
+# "area_id": "2",
+# "area_type": {
+# "normal": true
+# },
+# "authentication": "plaintext-password",
+# "shortcut": "enable"
+# },
+# {
+# "area_id": "3",
+# "area_type": {
+# "nssa": {
+# "set": true
+# }
+# }
+# },
+# {
+# "area_id": "4",
+# "area_type": {
+# "stub": {
+# "default_cost": 20,
+# "set": true
+# }
+# },
+# "network": [
+# {
+# "address": "192.0.2.0/24"
+# }
+# ],
+# "range": [
+# {
+# "address": "192.0.3.0/24",
+# "cost": 10
+# },
+# {
+# "address": "192.0.4.0/24",
+# "cost": 12
+# }
+# ]
+# }
+# ],
+# "auto_cost": {
+# "reference_bandwidth": 2
+# },
+# "default_information": {
+# "originate": {
+# "always": true,
+# "metric": 10,
+# "metric_type": 2,
+# "route_map": "ingress"
+# }
+# },
+# "log_adjacency_changes": "detail",
+# "max_metric": {
+# "router_lsa": {
+# "administrative": true,
+# "on_shutdown": 10,
+# "on_startup": 10
+# }
+# },
+# "mpls_te": {
+# "enabled": true,
+# "router_address": "192.0.11.11"
+# },
+# "neighbor": [
+# {
+# "neighbor_id": "192.0.11.12",
+# "poll_interval": 10,
+# "priority": 2
+# }
+# ],
+# "parameters": {
+# "abr_type": "cisco",
+# "opaque_lsa": true,
+# "rfc1583_compatibility": true,
+# "router_id": "192.0.1.1"
+# },
+# "passive_interface": [
+# "eth2",
+# "eth1"
+# ],
+# "redistribute": [
+# {
+# "metric": 10,
+# "metric_type": 2,
+# "route_type": "bgp"
+# }
+# ]
+# }
+# }
+
+
+# Using gathered
+#
+# Before state:
+# -------------
+#
+# vyos@192# run show configuration commands | grep ospf
+# set protocols ospf area 2 area-type 'normal'
+# set protocols ospf area 2 authentication 'plaintext-password'
+# set protocols ospf area 2 shortcut 'enable'
+# set protocols ospf area 3 area-type 'nssa'
+# set protocols ospf area 4 area-type stub default-cost '20'
+# set protocols ospf area 4 network '192.0.2.0/24'
+# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
+# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
+# set protocols ospf auto-cost reference-bandwidth '2'
+# set protocols ospf default-information originate 'always'
+# set protocols ospf default-information originate metric '10'
+# set protocols ospf default-information originate metric-type '2'
+# set protocols ospf default-information originate route-map 'ingress'
+# set protocols ospf log-adjacency-changes 'detail'
+# set protocols ospf max-metric router-lsa 'administrative'
+# set protocols ospf max-metric router-lsa on-shutdown '10'
+# set protocols ospf max-metric router-lsa on-startup '10'
+# set protocols ospf mpls-te 'enable'
+# set protocols ospf mpls-te router-address '192.0.11.11'
+# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
+# set protocols ospf neighbor 192.0.11.12 priority '2'
+# set protocols ospf parameters abr-type 'cisco'
+# set protocols ospf parameters 'opaque-lsa'
+# set protocols ospf parameters 'rfc1583-compatibility'
+# set protocols ospf parameters router-id '192.0.1.1'
+# set protocols ospf passive-interface 'eth1'
+# set protocols ospf passive-interface 'eth2'
+# set protocols ospf redistribute bgp metric '10'
+# set protocols ospf redistribute bgp metric-type '2'
+#
+- name: Gather ospfv2 routes config with provided configurations
+ vyos.vyos.vyos_ospfv2:
+ config:
+ state: gathered
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+# "gathered": {
+# "areas": [
+# {
+# "area_id": "2",
+# "area_type": {
+# "normal": true
+# },
+# "authentication": "plaintext-password",
+# "shortcut": "enable"
+# },
+# {
+# "area_id": "3",
+# "area_type": {
+# "nssa": {
+# "set": true
+# }
+# }
+# },
+# {
+# "area_id": "4",
+# "area_type": {
+# "stub": {
+# "default_cost": 20,
+# "set": true
+# }
+# },
+# "network": [
+# {
+# "address": "192.0.2.0/24"
+# }
+# ],
+# "range": [
+# {
+# "address": "192.0.3.0/24",
+# "cost": 10
+# },
+# {
+# "address": "192.0.4.0/24",
+# "cost": 12
+# }
+# ]
+# }
+# ],
+# "auto_cost": {
+# "reference_bandwidth": 2
+# },
+# "default_information": {
+# "originate": {
+# "always": true,
+# "metric": 10,
+# "metric_type": 2,
+# "route_map": "ingress"
+# }
+# },
+# "log_adjacency_changes": "detail",
+# "max_metric": {
+# "router_lsa": {
+# "administrative": true,
+# "on_shutdown": 10,
+# "on_startup": 10
+# }
+# },
+# "mpls_te": {
+# "enabled": true,
+# "router_address": "192.0.11.11"
+# },
+# "neighbor": [
+# {
+# "neighbor_id": "192.0.11.12",
+# "poll_interval": 10,
+# "priority": 2
+# }
+# ],
+# "parameters": {
+# "abr_type": "cisco",
+# "opaque_lsa": true,
+# "rfc1583_compatibility": true,
+# "router_id": "192.0.1.1"
+# },
+# "passive_interface": [
+# "eth2",
+# "eth1"
+# ],
+# "redistribute": [
+# {
+# "metric": 10,
+# "metric_type": 2,
+# "route_type": "bgp"
+# }
+# ]
+# }
+#
+# After state:
+# -------------
+#
+# vyos@192# run show configuration commands | grep ospf
+# set protocols ospf area 2 area-type 'normal'
+# set protocols ospf area 2 authentication 'plaintext-password'
+# set protocols ospf area 2 shortcut 'enable'
+# set protocols ospf area 3 area-type 'nssa'
+# set protocols ospf area 4 area-type stub default-cost '20'
+# set protocols ospf area 4 network '192.0.2.0/24'
+# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
+# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
+# set protocols ospf auto-cost reference-bandwidth '2'
+# set protocols ospf default-information originate 'always'
+# set protocols ospf default-information originate metric '10'
+# set protocols ospf default-information originate metric-type '2'
+# set protocols ospf default-information originate route-map 'ingress'
+# set protocols ospf log-adjacency-changes 'detail'
+# set protocols ospf max-metric router-lsa 'administrative'
+# set protocols ospf max-metric router-lsa on-shutdown '10'
+# set protocols ospf max-metric router-lsa on-startup '10'
+# set protocols ospf mpls-te 'enable'
+# set protocols ospf mpls-te router-address '192.0.11.11'
+# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
+# set protocols ospf neighbor 192.0.11.12 priority '2'
+# set protocols ospf parameters abr-type 'cisco'
+# set protocols ospf parameters 'opaque-lsa'
+# set protocols ospf parameters 'rfc1583-compatibility'
+# set protocols ospf parameters router-id '192.0.1.1'
+# set protocols ospf passive-interface 'eth1'
+# set protocols ospf passive-interface 'eth2'
+# set protocols ospf redistribute bgp metric '10'
+# set protocols ospf redistribute bgp metric-type '2'
+
+
+# Using deleted
+#
+# Before state
+# -------------
+#
+# vyos@192# run show configuration commands | grep ospf
+# set protocols ospf area 2 area-type 'normal'
+# set protocols ospf area 2 authentication 'plaintext-password'
+# set protocols ospf area 2 shortcut 'enable'
+# set protocols ospf area 3 area-type 'nssa'
+# set protocols ospf area 4 area-type stub default-cost '20'
+# set protocols ospf area 4 network '192.0.2.0/24'
+# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
+# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
+# set protocols ospf auto-cost reference-bandwidth '2'
+# set protocols ospf default-information originate 'always'
+# set protocols ospf default-information originate metric '10'
+# set protocols ospf default-information originate metric-type '2'
+# set protocols ospf default-information originate route-map 'ingress'
+# set protocols ospf log-adjacency-changes 'detail'
+# set protocols ospf max-metric router-lsa 'administrative'
+# set protocols ospf max-metric router-lsa on-shutdown '10'
+# set protocols ospf max-metric router-lsa on-startup '10'
+# set protocols ospf mpls-te 'enable'
+# set protocols ospf mpls-te router-address '192.0.11.11'
+# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
+# set protocols ospf neighbor 192.0.11.12 priority '2'
+# set protocols ospf parameters abr-type 'cisco'
+# set protocols ospf parameters 'opaque-lsa'
+# set protocols ospf parameters 'rfc1583-compatibility'
+# set protocols ospf parameters router-id '192.0.1.1'
+# set protocols ospf passive-interface 'eth1'
+# set protocols ospf passive-interface 'eth2'
+# set protocols ospf redistribute bgp metric '10'
+# set protocols ospf redistribute bgp metric-type '2'
+#
+- name: Delete attributes of ospfv2 routes.
+ vyos.vyos.vyos_ospfv2:
+ config:
+ state: deleted
+#
+#
+# ------------------------
+# Module Execution Results
+# ------------------------
+#
+# "before": {
+# "areas": [
+# {
+# "area_id": "2",
+# "area_type": {
+# "normal": true
+# },
+# "authentication": "plaintext-password",
+# "shortcut": "enable"
+# },
+# {
+# "area_id": "3",
+# "area_type": {
+# "nssa": {
+# "set": true
+# }
+# }
+# },
+# {
+# "area_id": "4",
+# "area_type": {
+# "stub": {
+# "default_cost": 20,
+# "set": true
+# }
+# },
+# "network": [
+# {
+# "address": "192.0.2.0/24"
+# }
+# ],
+# "range": [
+# {
+# "address": "192.0.3.0/24",
+# "cost": 10
+# },
+# {
+# "address": "192.0.4.0/24",
+# "cost": 12
+# }
+# ]
+# }
+# ],
+# "auto_cost": {
+# "reference_bandwidth": 2
+# },
+# "default_information": {
+# "originate": {
+# "always": true,
+# "metric": 10,
+# "metric_type": 2,
+# "route_map": "ingress"
+# }
+# },
+# "log_adjacency_changes": "detail",
+# "max_metric": {
+# "router_lsa": {
+# "administrative": true,
+# "on_shutdown": 10,
+# "on_startup": 10
+# }
+# },
+# "mpls_te": {
+# "enabled": true,
+# "router_address": "192.0.11.11"
+# },
+# "neighbor": [
+# {
+# "neighbor_id": "192.0.11.12",
+# "poll_interval": 10,
+# "priority": 2
+# }
+# ],
+# "parameters": {
+# "abr_type": "cisco",
+# "opaque_lsa": true,
+# "rfc1583_compatibility": true,
+# "router_id": "192.0.1.1"
+# },
+# "passive_interface": [
+# "eth2",
+# "eth1"
+# ],
+# "redistribute": [
+# {
+# "metric": 10,
+# "metric_type": 2,
+# "route_type": "bgp"
+# }
+# ]
+# }
+# "commands": [
+# "delete protocols ospf"
+# ]
+#
+# "after": {}
+# After state
+# ------------
+# vyos@192# run show configuration commands | grep ospf
+#
+
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: dict
+ 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
+ type: dict
+ 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: ['set protocols ospf parameters router-id 192.0.1.1',
+ 'set protocols ospf passive-interface 'eth1']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import (
+ Ospfv2Args,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv2.ospfv2 import (
+ Ospfv2,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+ module = AnsibleModule(
+ argument_spec=Ospfv2Args.argument_spec,
+ required_if=required_if,
+ supports_check_mode=True,
+ mutually_exclusive=mutually_exclusive,
+ )
+
+ result = Ospfv2(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/vyos_vlan.py b/plugins/modules/vyos_vlan.py
index a0aafb5..04f5856 100644
--- a/plugins/modules/vyos_vlan.py
+++ b/plugins/modules/vyos_vlan.py
@@ -240,8 +240,8 @@ def map_config_to_obj(module):
output = run_commands(module, "show interfaces")
lines = output[0].strip().splitlines()[3:]
- for l in lines:
- splitted_line = re.split(r"\s{2,}", l.strip())
+ for line in lines:
+ splitted_line = re.split(r"\s{2,}", line.strip())
obj = {}
eth = splitted_line[0].strip("'")
diff --git a/tests/integration/targets/vyos_ospfv2/defaults/main.yaml b/tests/integration/targets/vyos_ospfv2/defaults/main.yaml
new file mode 100644
index 0000000..852a6be
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/defaults/main.yaml
@@ -0,0 +1,3 @@
+---
+testcase: '[^_].*'
+test_items: []
diff --git a/tests/integration/targets/vyos_ospfv2/meta/main.yaml b/tests/integration/targets/vyos_ospfv2/meta/main.yaml
new file mode 100644
index 0000000..7413320
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/meta/main.yaml
@@ -0,0 +1,3 @@
+---
+dependencies:
+ - prepare_vyos_tests
diff --git a/tests/integration/targets/vyos_ospfv2/tasks/cli.yaml b/tests/integration/targets/vyos_ospfv2/tasks/cli.yaml
new file mode 100644
index 0000000..93eb2fe
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tasks/cli.yaml
@@ -0,0 +1,19 @@
+---
+- name: Collect all cli test cases
+ find:
+ paths: '{{ role_path }}/tests/cli'
+ patterns: '{{ testcase }}.yaml'
+ use_regex: true
+ register: test_cases
+ delegate_to: localhost
+
+- name: Set test_items
+ set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
+
+- name: Run test case (connection=ansible.netcommon.network_cli)
+ include: '{{ test_case_to_run }}'
+ vars:
+ ansible_connection: ansible.netcommon.network_cli
+ with_items: '{{ test_items }}'
+ loop_control:
+ loop_var: test_case_to_run
diff --git a/tests/integration/targets/vyos_ospfv2/tasks/main.yaml b/tests/integration/targets/vyos_ospfv2/tasks/main.yaml
new file mode 100644
index 0000000..a3db933
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tasks/main.yaml
@@ -0,0 +1,4 @@
+---
+- include: cli.yaml
+ tags:
+ - cli
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_ospfv2/tests/cli/_parsed_config.cfg
new file mode 100644
index 0000000..9cc720b
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/_parsed_config.cfg
@@ -0,0 +1,29 @@
+set protocols ospf area 2 area-type 'normal'
+set protocols ospf area 2 authentication 'plaintext-password'
+set protocols ospf area 2 shortcut 'enable'
+set protocols ospf area 3 area-type 'nssa'
+set protocols ospf area 4 area-type stub default-cost '20'
+set protocols ospf area 4 network '192.0.2.0/24'
+set protocols ospf area 4 range 192.0.3.0/24 cost '10'
+set protocols ospf area 4 range 192.0.4.0/24 cost '12'
+set protocols ospf auto-cost reference-bandwidth '2'
+set protocols ospf default-information originate 'always'
+set protocols ospf default-information originate metric '10'
+set protocols ospf default-information originate metric-type '2'
+set protocols ospf default-information originate route-map 'ingress'
+set protocols ospf log-adjacency-changes 'detail'
+set protocols ospf max-metric router-lsa 'administrative'
+set protocols ospf max-metric router-lsa on-shutdown '10'
+set protocols ospf max-metric router-lsa on-startup '10'
+set protocols ospf mpls-te 'enable'
+set protocols ospf mpls-te router-address '192.0.11.11'
+set protocols ospf neighbor 192.0.11.12 poll-interval '10'
+set protocols ospf neighbor 192.0.11.12 priority '2'
+set protocols ospf parameters abr-type 'cisco'
+set protocols ospf parameters 'opaque-lsa'
+set protocols ospf parameters 'rfc1583-compatibility'
+set protocols ospf parameters router-id '192.0.1.1'
+set protocols ospf passive-interface 'eth1'
+set protocols ospf passive-interface 'eth2'
+set protocols ospf redistribute bgp metric '10'
+set protocols ospf redistribute bgp metric-type '2'
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/_populate.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/_populate.yaml
new file mode 100644
index 0000000..9f358d5
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/_populate.yaml
@@ -0,0 +1,35 @@
+---
+- name: Setup
+ vars:
+ lines: "set protocols ospf mpls-te 'enable' \n
+ set protocols ospf mpls-te router-address '192.0.11.11' \n
+ set protocols ospf redistribute bgp metric-type '2' \n
+ set protocols ospf redistribute bgp metric '10'\n
+ set protocols ospf default-information originate metric-type '2' \n
+ set protocols ospf default-information originate 'always' \n
+ set protocols ospf default-information originate metric '10' \n
+ set protocols ospf default-information originate route-map 'ingress' \n
+ set protocols ospf auto-cost reference-bandwidth '2' \n
+ set protocols ospf parameters router-id '192.0.1.1' \n
+ set protocols ospf parameters 'opaque-lsa' \n
+ set protocols ospf parameters abr-type 'cisco' \n
+ set protocols ospf parameters 'rfc1583-compatibility' \n
+ set protocols ospf passive-interface 'eth1' \n
+ set protocols ospf passive-interface 'eth2' \n
+ set protocols ospf max-metric router-lsa on-shutdown '10' \n
+ set protocols ospf max-metric router-lsa 'administrative' \n
+ set protocols ospf max-metric router-lsa on-startup '10' \n
+ set protocols ospf log-adjacency-changes 'detail' \n
+ set protocols ospf neighbor 192.0.11.12 priority '2' \n
+ set protocols ospf neighbor 192.0.11.12 poll-interval '10' \n
+ set protocols ospf area 2 authentication 'plaintext-password' \n
+ set protocols ospf area 2 shortcut 'enable' \n
+ set protocols ospf area 2 area-type 'normal' \n
+ set protocols ospf area 3 area-type 'nssa' \n
+ set protocols ospf area 4 range 192.0.3.0/24 cost '10' \n
+ set protocols ospf area 4 range 192.0.4.0/24 cost '12' \n
+ set protocols ospf area 4 area-type stub default-cost '20' \n
+ set protocols ospf area 4 network '192.0.2.0/24'"
+
+ ansible.netcommon.cli_config:
+ config: '{{ lines }}'
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/_remove_config.yaml
new file mode 100644
index 0000000..7360870
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/_remove_config.yaml
@@ -0,0 +1,6 @@
+---
+- name: Remove Config
+ vars:
+ lines: "delete protocols ospf\n"
+ ansible.netcommon.cli_config:
+ config: '{{ lines }}'
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/deleted.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/deleted.yaml
new file mode 100644
index 0000000..a61f5a7
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/deleted.yaml
@@ -0,0 +1,48 @@
+---
+- debug:
+ msg: Start vyos_ospfv2 deleted integration tests ansible_connection={{
+ ansible_connection }}
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Delete attributes of ospfv2.
+ register: result
+ vyos.vyos.vyos_ospfv2: &id001
+ config:
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate == result['before'] }}"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted['commands'] | symmetric_difference(result['commands']) |length\
+ \ == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted['after'] == result['after'] }}"
+
+ - name: Delete attributes of given interfaces (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ospfv2: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands|length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted['after'] == result['before'] }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/empty_config.yaml
new file mode 100644
index 0000000..4566bf4
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/empty_config.yaml
@@ -0,0 +1,49 @@
+---
+- debug:
+ msg: START vyos_ospfv2 empty_config integration tests on connection={{
+ ansible_connection }}
+
+- name: Merged with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_ospfv2:
+ config:
+ state: merged
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state merged'
+
+- name: Replaced with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_ospfv2:
+ config:
+ state: replaced
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state replaced'
+
+- name: Parsed with empty running_config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_ospfv2:
+ running_config:
+ state: parsed
+
+- assert:
+ that:
+ - result.msg == 'value of running_config parameter must not be empty for state
+ parsed'
+
+- name: Rendered with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_ospfv2:
+ config:
+ state: rendered
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state rendered'
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml
new file mode 100644
index 0000000..bc5e1e2
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml
@@ -0,0 +1,24 @@
+---
+- debug:
+ msg: START vyos_ospfv2 gathered integration tests on connection={{
+ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Gather the provided configuration with the exisiting running configuration
+ register: result
+ vyos.vyos.vyos_ospfv2:
+ config:
+ state: gathered
+
+ - name: Assert that gathered dicts was correctly generated
+ assert:
+ that:
+ - "{{ populate == result['gathered'] }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/merged.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/merged.yaml
new file mode 100644
index 0000000..6a58bb5
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/merged.yaml
@@ -0,0 +1,101 @@
+---
+- debug:
+ msg: START vyos_ospfv2 merged integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- block:
+
+ - name: Merge the provided configuration with the exisiting running configuration
+ register: result
+ vyos.vyos.vyos_ospfv2: &id001
+ config:
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ mpls_te:
+ enabled: true
+ router_address: '192.0.11.11'
+ auto_cost:
+ reference_bandwidth: 2
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ redistribute:
+ - route_type: 'bgp'
+ metric: 10
+ metric_type: 2
+ passive_interface:
+ - 'eth1'
+ - 'eth2'
+ parameters:
+ router_id: '192.0.1.1'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ abr_type: 'cisco'
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ network:
+ - address: '192.0.2.0/24'
+ range:
+ - address: '192.0.3.0/24'
+ cost: 10
+ - address: '192.0.4.0/24'
+ cost: 12
+ state: merged
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that: "{{ merged['before'] == result['before'] }}"
+
+ - name: Assert that correct set of commands were generated
+ assert:
+ that:
+ - "{{ merged['commands'] | symmetric_difference(result['commands']) |length\
+ \ == 0 }}"
+
+ - name: Assert that after dicts was correctly generated
+ assert:
+ that:
+ - "{{ merged['after'] == result['after'] }}"
+
+ - name: Merge the provided configuration with the existing running configuration
+ (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ospfv2: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that:
+ - "{{ merged['after'] == result['before'] }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml
new file mode 100644
index 0000000..9b6823c
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml
@@ -0,0 +1,70 @@
+---
+- debug:
+ msg: START vyos_ospfv2 merged integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Merge the provided configuration with the exisiting running configuration
+ register: result
+ vyos.vyos.vyos_ospfv2: &id001
+ config:
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ passive_interface:
+ - 'eth1'
+ areas:
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ area_type:
+ stub:
+ set: false
+ network:
+ - address: '192.0.2.0/24'
+ - address: '192.0.22.0/24'
+ - address: '192.0.32.0/24'
+ state: merged
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that: "{{ merged_update['before'] == result['before'] }}"
+
+ - name: Assert that correct set of commands were generated
+ assert:
+ that:
+ - "{{ merged_update['commands'] | symmetric_difference(result['commands']) |length\
+ \ == 0 }}"
+
+ - name: Assert that after dicts was correctly generated
+ assert:
+ that:
+ - "{{ merged_update['after'] == result['after'] }}"
+
+ - name: Merge the provided configuration with the existing running configuration
+ (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ospfv2: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that:
+ - "{{ merged_update['after'] == result['before'] }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml
new file mode 100644
index 0000000..cfa29f9
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml
@@ -0,0 +1,15 @@
+---
+- debug:
+ msg: START vyos_ospfv2 parsed integration tests on connection={{ ansible_connection
+ }}
+
+- name: Parse externally provided ospfv2 config to agnostic model
+ register: result
+ vyos.vyos.vyos_ospfv2:
+ running_config: "{{ lookup('file', '_parsed_config.cfg') }}"
+ state: parsed
+
+- name: Assert that config was correctly parsed
+ assert:
+ that:
+ - "{{ parsed['after'] == result['parsed'] }}"
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml
new file mode 100644
index 0000000..8a805a6
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml
@@ -0,0 +1,76 @@
+---
+- debug:
+ msg: START vyos_ospfv2 rendered integration tests on connection={{
+ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- block:
+
+ - name: Structure provided configuration into device specific commands
+ register: result
+ vyos.vyos.vyos_ospfv2:
+ config:
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ mpls_te:
+ enabled: true
+ router_address: '192.0.11.11'
+ auto_cost:
+ reference_bandwidth: 2
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ redistribute:
+ - route_type: 'bgp'
+ metric: 10
+ metric_type: 2
+ passive_interface:
+ - 'eth1'
+ - 'eth2'
+ parameters:
+ router_id: '192.0.1.1'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ abr_type: 'cisco'
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ network:
+ - address: '192.0.2.0/24'
+ range:
+ - address: '192.0.3.0/24'
+ cost: 10
+ - address: '192.0.4.0/24'
+ cost: 12
+ state: rendered
+
+ - name: Assert that correct set of commands were generated
+ assert:
+ that:
+ - "{{ rendered['commands'] | symmetric_difference(result['rendered'])\
+ \ |length == 0 }}"
+- debug:
+ msg: END vyos_ospfv2 rendered integration tests on connection={{ ansible_connection }}
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/replaced.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/replaced.yaml
new file mode 100644
index 0000000..07606f9
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/replaced.yaml
@@ -0,0 +1,100 @@
+---
+- debug:
+ msg: START vyos_ospfv2 replaced integration tests on connection={{
+ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Replace device configurations of listed ospfv2 routes with provided configurations
+ register: result
+ vyos.vyos.vyos_ospfv2: &id001
+ config:
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ mpls_te:
+ enabled: true
+ router_address: '192.0.22.22'
+ auto_cost:
+ reference_bandwidth: 2
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ redistribute:
+ - route_type: 'bgp'
+ metric: 10
+ metric_type: 2
+ passive_interface:
+ - 'eth1'
+ parameters:
+ router_id: '192.0.1.1'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ abr_type: 'cisco'
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ network:
+ - address: '192.0.2.0/24'
+ - address: '192.0.12.0/24'
+ - address: '192.0.22.0/24'
+ - address: '192.0.32.0/24'
+ range:
+ - address: '1.1.2.0/24'
+ cost: 10
+ state: replaced
+
+ - name: Assert that correct set of commands were generated
+ assert:
+ that:
+ - "{{ replaced['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that before dicts are correctly generated
+ assert:
+ that:
+ - "{{ populate == result['before'] }}"
+
+ - name: Assert that after dict is correctly generated
+ assert:
+ that:
+ - "{{ replaced['after'] == result['after'] }}"
+
+ - name: Replace device configurations of listed ospfv2 routes with provided configurarions
+ (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ospfv2: *id001
+
+ - name: Assert that task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+
+ - name: Assert that before dict is correctly generated
+ assert:
+ that:
+ - "{{ replaced['after'] == result['before'] }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/rtt.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/rtt.yaml
new file mode 100644
index 0000000..7efc2a7
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/tests/cli/rtt.yaml
@@ -0,0 +1,149 @@
+---
+- debug:
+ msg: START vyos_ospfv2 round trip integration tests on connection={{
+ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- block:
+
+ - name: Apply the provided configuration (base config)
+ register: base_config
+ vyos.vyos.vyos_ospfv2:
+ config:
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ mpls_te:
+ enabled: true
+ router_address: '192.0.11.11'
+ auto_cost:
+ reference_bandwidth: 2
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ redistribute:
+ - route_type: 'bgp'
+ metric: 10
+ metric_type: 2
+ passive_interface:
+ - 'eth1'
+ - 'eth2'
+ parameters:
+ router_id: '192.0.1.1'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ abr_type: 'cisco'
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ network:
+ - address: '192.0.2.0/24'
+ range:
+ - address: '192.0.3.0/24'
+ cost: 10
+ - address: '192.0.4.0/24'
+ cost: 12
+ state: merged
+
+ - name: Gather ospfv2 facts
+ vyos.vyos.vyos_facts:
+ gather_subset:
+ - default
+ gather_network_resources:
+ - ospfv2
+
+ - name: Apply the provided configuration (config to be reverted)
+ register: result
+ vyos.vyos.vyos_ospfv2:
+ config:
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ set: true
+ network:
+ - address: '192.0.12.0/24'
+ - address: '192.0.2.0/24'
+ - address: '192.0.22.0/24'
+ - address: '192.0.32.0/24'
+ range:
+ - address: '1.1.2.0/24'
+ cost: 10
+ auto_cost:
+ reference_bandwidth: 2
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ mpls_te:
+ enabled: true
+ router_address: '192.0.22.22'
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ parameters:
+ abr_type: 'cisco'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ router_id: '192.0.1.1'
+ passive_interface:
+ - 'eth1'
+ redistribute:
+ - metric: 10
+ metric_type: 2
+ route_type: 'bgp'
+ state: replaced
+
+ - name: Assert that changes were applied
+ assert:
+ that: "{{ round_trip['after'] == result['after'] }}"
+
+ - name: Revert back to base config using facts round trip
+ register: revert
+ vyos.vyos.vyos_ospfv2:
+ config: "{{ ansible_facts['network_resources']['ospfv2'] }}"
+ state: replaced
+
+ - name: Assert that config was reverted
+ assert:
+ that: "{{ base_config['after'] == revert['after']}}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ospfv2/vars/main.yaml b/tests/integration/targets/vyos_ospfv2/vars/main.yaml
new file mode 100644
index 0000000..e55da20
--- /dev/null
+++ b/tests/integration/targets/vyos_ospfv2/vars/main.yaml
@@ -0,0 +1,485 @@
+---
+merged:
+ before: {}
+ commands:
+ - set protocols ospf mpls-te enable
+ - set protocols ospf mpls-te router-address '192.0.11.11'
+ - set protocols ospf redistribute bgp
+ - set protocols ospf redistribute bgp metric-type 2
+ - set protocols ospf redistribute bgp metric 10
+ - set protocols ospf default-information originate metric-type 2
+ - set protocols ospf default-information originate always
+ - set protocols ospf default-information originate metric 10
+ - set protocols ospf default-information originate route-map ingress
+ - set protocols ospf auto-cost reference-bandwidth '2'
+ - set protocols ospf parameters router-id '192.0.1.1'
+ - set protocols ospf parameters opaque-lsa
+ - set protocols ospf parameters abr-type 'cisco'
+ - set protocols ospf parameters rfc1583-compatibility
+ - set protocols ospf passive-interface eth1
+ - set protocols ospf passive-interface eth2
+ - set protocols ospf max-metric router-lsa on-shutdown 10
+ - set protocols ospf max-metric router-lsa administrative
+ - set protocols ospf max-metric router-lsa on-startup 10
+ - set protocols ospf log-adjacency-changes 'detail'
+ - set protocols ospf neighbor 192.0.11.12 priority 2
+ - set protocols ospf neighbor 192.0.11.12 poll-interval 10
+ - set protocols ospf neighbor 192.0.11.12
+ - set protocols ospf area '2'
+ - set protocols ospf area 2 authentication plaintext-password
+ - set protocols ospf area 2 shortcut enable
+ - set protocols ospf area 2 area-type normal
+ - set protocols ospf area '3'
+ - set protocols ospf area 3 area-type nssa
+ - set protocols ospf area 4 range 192.0.3.0/24 cost 10
+ - set protocols ospf area 4 range 192.0.3.0/24
+ - set protocols ospf area 4 range 192.0.4.0/24 cost 12
+ - set protocols ospf area 4 range 192.0.4.0/24
+ - set protocols ospf area 4 area-type stub default-cost 20
+ - set protocols ospf area '4'
+ - set protocols ospf area 4 network 192.0.2.0/24
+ after:
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ set: true
+ network:
+ - address: '192.0.2.0/24'
+ range:
+ - address: '192.0.3.0/24'
+ cost: 10
+ - address: '192.0.4.0/24'
+ cost: 12
+ auto_cost:
+ reference_bandwidth: 2
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ mpls_te:
+ enabled: true
+ router_address: '192.0.11.11'
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ parameters:
+ abr_type: 'cisco'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ router_id: '192.0.1.1'
+ passive_interface:
+ - 'eth1'
+ - 'eth2'
+ redistribute:
+ - metric: 10
+ metric_type: 2
+ route_type: 'bgp'
+merged_update:
+ before:
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ set: true
+ network:
+ - address: '192.0.2.0/24'
+ range:
+ - address: '192.0.3.0/24'
+ cost: 10
+ - address: '192.0.4.0/24'
+ cost: 12
+ auto_cost:
+ reference_bandwidth: 2
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ mpls_te:
+ enabled: true
+ router_address: '192.0.11.11'
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ parameters:
+ abr_type: 'cisco'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ router_id: '192.0.1.1'
+ passive_interface:
+ - 'eth1'
+ - 'eth2'
+ redistribute:
+ - metric: 10
+ metric_type: 2
+ route_type: 'bgp'
+ after:
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ network:
+ - address: '192.0.2.0/24'
+ - address: '192.0.22.0/24'
+ - address: '192.0.32.0/24'
+ range:
+ - address: '192.0.3.0/24'
+ cost: 10
+ - address: '192.0.4.0/24'
+ cost: 12
+ auto_cost:
+ reference_bandwidth: 2
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ mpls_te:
+ enabled: true
+ router_address: '192.0.11.11'
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ parameters:
+ abr_type: 'cisco'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ router_id: '192.0.1.1'
+ passive_interface:
+ - 'eth1'
+ - 'eth2'
+ redistribute:
+ - metric: 10
+ metric_type: 2
+ route_type: 'bgp'
+ commands:
+ - delete protocols ospf area 4 area-type stub
+ - set protocols ospf area 4 network 192.0.22.0/24
+ - set protocols ospf area 4 network 192.0.32.0/24
+populate:
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ set: true
+ network:
+ - address: '192.0.2.0/24'
+ range:
+ - address: '192.0.3.0/24'
+ cost: 10
+ - address: '192.0.4.0/24'
+ cost: 12
+ auto_cost:
+ reference_bandwidth: 2
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ mpls_te:
+ enabled: true
+ router_address: '192.0.11.11'
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ parameters:
+ abr_type: 'cisco'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ router_id: '192.0.1.1'
+ passive_interface:
+ - 'eth1'
+ - 'eth2'
+ redistribute:
+ - metric: 10
+ metric_type: 2
+ route_type: 'bgp'
+replaced:
+ commands:
+ - delete protocols ospf passive-interface eth2
+ - delete protocols ospf area 3
+ - delete protocols ospf area 4 range 192.0.3.0/24 cost
+ - delete protocols ospf area 4 range 192.0.3.0/24
+ - delete protocols ospf area 4 range 192.0.4.0/24 cost
+ - delete protocols ospf area 4 range 192.0.4.0/24
+ - set protocols ospf mpls-te router-address '192.0.22.22'
+ - set protocols ospf area 4 range 1.1.2.0/24 cost 10
+ - set protocols ospf area 4 range 1.1.2.0/24
+ - set protocols ospf area 4 network 192.0.12.0/24
+ - set protocols ospf area 4 network 192.0.22.0/24
+ - set protocols ospf area 4 network 192.0.32.0/24
+ after:
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ set: true
+ network:
+ - address: '192.0.12.0/24'
+ - address: '192.0.2.0/24'
+ - address: '192.0.22.0/24'
+ - address: '192.0.32.0/24'
+ range:
+ - address: '1.1.2.0/24'
+ cost: 10
+ auto_cost:
+ reference_bandwidth: 2
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ mpls_te:
+ enabled: true
+ router_address: '192.0.22.22'
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ parameters:
+ abr_type: 'cisco'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ router_id: '192.0.1.1'
+ passive_interface:
+ - 'eth1'
+ redistribute:
+ - metric: 10
+ metric_type: 2
+ route_type: 'bgp'
+rendered:
+ commands:
+ - set protocols ospf mpls-te enable
+ - set protocols ospf mpls-te router-address '192.0.11.11'
+ - set protocols ospf redistribute bgp
+ - set protocols ospf redistribute bgp metric-type 2
+ - set protocols ospf redistribute bgp metric 10
+ - set protocols ospf default-information originate metric-type 2
+ - set protocols ospf default-information originate always
+ - set protocols ospf default-information originate metric 10
+ - set protocols ospf default-information originate route-map ingress
+ - set protocols ospf auto-cost reference-bandwidth '2'
+ - set protocols ospf parameters router-id '192.0.1.1'
+ - set protocols ospf parameters opaque-lsa
+ - set protocols ospf parameters abr-type 'cisco'
+ - set protocols ospf parameters rfc1583-compatibility
+ - set protocols ospf passive-interface eth1
+ - set protocols ospf passive-interface eth2
+ - set protocols ospf max-metric router-lsa on-shutdown 10
+ - set protocols ospf max-metric router-lsa administrative
+ - set protocols ospf max-metric router-lsa on-startup 10
+ - set protocols ospf log-adjacency-changes 'detail'
+ - set protocols ospf neighbor 192.0.11.12 priority 2
+ - set protocols ospf neighbor 192.0.11.12 poll-interval 10
+ - set protocols ospf neighbor 192.0.11.12
+ - set protocols ospf area '2'
+ - set protocols ospf area 2 authentication plaintext-password
+ - set protocols ospf area 2 shortcut enable
+ - set protocols ospf area 2 area-type normal
+ - set protocols ospf area '3'
+ - set protocols ospf area 3 area-type nssa
+ - set protocols ospf area 4 range 192.0.3.0/24 cost 10
+ - set protocols ospf area 4 range 192.0.3.0/24
+ - set protocols ospf area 4 range 192.0.4.0/24 cost 12
+ - set protocols ospf area 4 range 192.0.4.0/24
+ - set protocols ospf area 4 area-type stub default-cost 20
+ - set protocols ospf area '4'
+ - set protocols ospf area 4 network 192.0.2.0/24
+parsed:
+ after:
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '3'
+ area_type:
+ nssa:
+ set: true
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ set: true
+ network:
+ - address: '192.0.2.0/24'
+ range:
+ - address: '192.0.3.0/24'
+ cost: 10
+ - address: '192.0.4.0/24'
+ cost: 12
+ auto_cost:
+ reference_bandwidth: 2
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ mpls_te:
+ enabled: true
+ router_address: '192.0.11.11'
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ parameters:
+ abr_type: 'cisco'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ router_id: '192.0.1.1'
+ passive_interface:
+ - 'eth1'
+ - 'eth2'
+ redistribute:
+ - metric: 10
+ metric_type: 2
+ route_type: 'bgp'
+deleted:
+ commands:
+ - 'delete protocols ospf'
+ after: {}
+round_trip:
+ after:
+ areas:
+ - area_id: '2'
+ area_type:
+ normal: true
+ authentication: "plaintext-password"
+ shortcut: 'enable'
+ - area_id: '4'
+ area_type:
+ stub:
+ default_cost: 20
+ set: true
+ network:
+ - address: '192.0.12.0/24'
+ - address: '192.0.2.0/24'
+ - address: '192.0.22.0/24'
+ - address: '192.0.32.0/24'
+ range:
+ - address: '1.1.2.0/24'
+ cost: 10
+ auto_cost:
+ reference_bandwidth: 2
+ default_information:
+ originate:
+ always: true
+ metric: 10
+ metric_type: 2
+ route_map: 'ingress'
+ log_adjacency_changes: 'detail'
+ max_metric:
+ router_lsa:
+ administrative: true
+ on_shutdown: 10
+ on_startup: 10
+ mpls_te:
+ enabled: true
+ router_address: '192.0.22.22'
+ neighbor:
+ - neighbor_id: '192.0.11.12'
+ poll_interval: 10
+ priority: 2
+ parameters:
+ abr_type: 'cisco'
+ opaque_lsa: true
+ rfc1583_compatibility: true
+ router_id: '192.0.1.1'
+ passive_interface:
+ - 'eth1'
+ redistribute:
+ - metric: 10
+ metric_type: 2
+ route_type: 'bgp'
diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_ospfv2_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_ospfv2_config.cfg
new file mode 100644
index 0000000..297671b
--- /dev/null
+++ b/tests/unit/modules/network/vyos/fixtures/vyos_ospfv2_config.cfg
@@ -0,0 +1,9 @@
+set protocols ospf area 12 area-type normal
+set protocols ospf area 12 authentication plaintext-password
+set protocols ospf area 12 shortcut enable
+set protocols ospf area 14 range 192.0.13.0/24 cost 10
+set protocols ospf area 14 range 192.0.13.0/24
+set protocols ospf area 14 range 192.0.14.0/24 cost 12
+set protocols ospf area 14 range 192.0.14.0/24
+set protocols ospf area 14 area-type stub default-cost 20
+set protocols ospf area 14 network 192.0.12.0/24
diff --git a/tests/unit/modules/network/vyos/test_vyos_ospfv2.py b/tests/unit/modules/network/vyos/test_vyos_ospfv2.py
new file mode 100644
index 0000000..b825066
--- /dev/null
+++ b/tests/unit/modules/network/vyos/test_vyos_ospfv2.py
@@ -0,0 +1,435 @@
+# (c) 2016 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch
+from ansible_collections.vyos.vyos.plugins.modules import vyos_ospfv2
+from ansible_collections.vyos.vyos.tests.unit.modules.utils import (
+ set_module_args,
+)
+from .vyos_module import TestVyosModule, load_fixture
+
+
+class TestVyosFirewallRulesModule(TestVyosModule):
+
+ module = vyos_ospfv2
+
+ def setUp(self):
+ super(TestVyosFirewallRulesModule, self).setUp()
+ self.mock_get_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config"
+ )
+ self.get_config = self.mock_get_config.start()
+
+ self.mock_load_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config"
+ )
+ self.load_config = self.mock_load_config.start()
+
+ self.mock_get_resource_connection_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection"
+ )
+ self.get_resource_connection_config = (
+ self.mock_get_resource_connection_config.start()
+ )
+
+ self.mock_get_resource_connection_facts = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection"
+ )
+ self.get_resource_connection_facts = (
+ self.mock_get_resource_connection_facts.start()
+ )
+
+ self.mock_execute_show_command = patch(
+ "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv2.ospfv2.Ospfv2Facts.get_device_data"
+ )
+
+ self.execute_show_command = self.mock_execute_show_command.start()
+
+ def tearDown(self):
+ super(TestVyosFirewallRulesModule, self).tearDown()
+ self.mock_get_resource_connection_config.stop()
+ self.mock_get_resource_connection_facts.stop()
+ self.mock_get_config.stop()
+ self.mock_load_config.stop()
+ self.mock_execute_show_command.stop()
+
+ def load_fixtures(self, commands=None, transport="cli", filename=None):
+ if filename is None:
+ filename = "vyos_ospfv2_config.cfg"
+
+ def load_from_file(*args, **kwargs):
+ output = load_fixture(filename)
+ return output
+
+ self.execute_show_command.side_effect = load_from_file
+
+ def test_vyos_ospfv2_merged_new_config(self):
+ set_module_args(
+ dict(
+ config=dict(
+ log_adjacency_changes="detail",
+ mpls_te=dict(enabled=True, router_address="192.0.11.11"),
+ auto_cost=dict(reference_bandwidth=2),
+ areas=[
+ dict(
+ area_id="2",
+ area_type=dict(normal=True),
+ authentication="plaintext-password",
+ shortcut="enable",
+ ),
+ dict(
+ area_id="4",
+ area_type=dict(stub=dict(default_cost=10)),
+ network=[dict(address="192.0.2.0/24"),],
+ range=[
+ dict(address="192.0.3.0/24", cost=10),
+ dict(address="192.0.4.0/24", cost=12),
+ ],
+ ),
+ ],
+ ),
+ state="merged",
+ )
+ )
+ commands = [
+ "set protocols ospf mpls-te enable",
+ "set protocols ospf mpls-te router-address '192.0.11.11'",
+ "set protocols ospf auto-cost reference-bandwidth '2'",
+ "set protocols ospf log-adjacency-changes 'detail'",
+ "set protocols ospf area '2'",
+ "set protocols ospf area 2 authentication plaintext-password",
+ "set protocols ospf area 2 shortcut enable",
+ "set protocols ospf area 2 area-type normal",
+ "set protocols ospf area 4 range 192.0.3.0/24 cost 10",
+ "set protocols ospf area 4 range 192.0.3.0/24",
+ "set protocols ospf area 4 range 192.0.4.0/24 cost 12",
+ "set protocols ospf area 4 range 192.0.4.0/24",
+ "set protocols ospf area 4 area-type stub default-cost 10",
+ "set protocols ospf area '4'",
+ "set protocols ospf area 4 network 192.0.2.0/24",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_ospfv2_merged_idem(self):
+ set_module_args(
+ dict(
+ config=dict(
+ areas=[
+ dict(
+ area_id="12",
+ area_type=dict(normal=True),
+ authentication="plaintext-password",
+ shortcut="enable",
+ ),
+ dict(
+ area_id="14",
+ area_type=dict(stub=dict(default_cost=20)),
+ network=[dict(address="192.0.12.0/24"),],
+ range=[
+ dict(address="192.0.13.0/24", cost=10),
+ dict(address="192.0.14.0/24", cost=12),
+ ],
+ ),
+ ],
+ ),
+ state="merged",
+ )
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vyos_ospfv2_merged_update_existing(self):
+ set_module_args(
+ dict(
+ config=dict(
+ areas=[
+ dict(
+ area_id="12",
+ area_type=dict(normal=True),
+ authentication="plaintext-password",
+ shortcut="enable",
+ ),
+ dict(
+ area_id="14",
+ area_type=dict(stub=dict(set=False)),
+ network=[
+ dict(address="192.0.12.0/24"),
+ dict(address="192.0.22.0/24"),
+ ],
+ range=[
+ dict(address="192.0.13.0/24", cost=10),
+ dict(address="192.0.14.0/24", cost=12),
+ ],
+ ),
+ ],
+ ),
+ state="merged",
+ )
+ )
+ commands = [
+ "delete protocols ospf area 14 area-type stub",
+ "set protocols ospf area 14 network 192.0.22.0/24",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_ospfv2_replaced(self):
+ set_module_args(
+ dict(
+ config=dict(
+ log_adjacency_changes="detail",
+ mpls_te=dict(enabled=True, router_address="192.0.11.11"),
+ auto_cost=dict(reference_bandwidth=2),
+ areas=[
+ dict(
+ area_id="12",
+ area_type=dict(normal=True),
+ authentication="plaintext-password",
+ shortcut="enable",
+ ),
+ dict(
+ area_id="15",
+ area_type=dict(stub=dict(default_cost=10)),
+ network=[dict(address="192.0.12.0/24"),],
+ range=[
+ dict(address="192.0.13.0/24", cost=10),
+ dict(address="192.0.14.0/24", cost=12),
+ dict(address="192.0.15.0/24", cost=14),
+ ],
+ ),
+ ],
+ ),
+ state="replaced",
+ )
+ )
+ commands = [
+ "set protocols ospf mpls-te enable",
+ "set protocols ospf mpls-te router-address '192.0.11.11'",
+ "set protocols ospf auto-cost reference-bandwidth '2'",
+ "set protocols ospf log-adjacency-changes 'detail'",
+ "delete protocols ospf area 14",
+ "set protocols ospf area 15 range 192.0.13.0/24 cost 10",
+ "set protocols ospf area 15 range 192.0.13.0/24",
+ "set protocols ospf area 15 range 192.0.14.0/24 cost 12",
+ "set protocols ospf area 15 range 192.0.14.0/24",
+ "set protocols ospf area 15 range 192.0.15.0/24 cost 14",
+ "set protocols ospf area 15 range 192.0.15.0/24",
+ "set protocols ospf area 15 area-type stub default-cost 10",
+ "set protocols ospf area '15'",
+ "set protocols ospf area 15 network 192.0.12.0/24",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_ospfv2_replaced_idem(self):
+ set_module_args(
+ dict(
+ config=dict(
+ areas=[
+ dict(
+ area_id="12",
+ area_type=dict(normal=True),
+ authentication="plaintext-password",
+ shortcut="enable",
+ ),
+ dict(
+ area_id="14",
+ area_type=dict(stub=dict(default_cost=20)),
+ network=[dict(address="192.0.12.0/24"),],
+ range=[
+ dict(address="192.0.13.0/24", cost=10),
+ dict(address="192.0.14.0/24", cost=12),
+ ],
+ ),
+ ],
+ ),
+ state="replaced",
+ )
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vyos_ospfv2_deleted_no_config(self):
+ set_module_args(dict(config=None, state="deleted"))
+ commands = ["delete protocols ospf"]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_ospfv2_gathered(self):
+ set_module_args(dict(state="gathered"))
+ result = self.execute_module(
+ changed=False, filename="vyos_ospfv2_config.cfg"
+ )
+ gather_dict = {
+ "areas": [
+ {
+ "area_id": "2",
+ "area_type": {"normal": True},
+ "authentication": "plaintext-password",
+ "shortcut": "enable",
+ },
+ {
+ "area_id": "14",
+ "area_type": {"stub": {"default_cost": 20, "set": True}},
+ "network": [{"address": "192.0.12.0/24"}],
+ "range": [
+ {"address": "192.0.13.0/24", "cost": 10},
+ {"address": "192.0.14.0/24", "cost": 12},
+ ],
+ },
+ ],
+ }
+ self.assertEqual(sorted(gather_dict), sorted(result["gathered"]))
+
+ def test_vyos_ospfv2_parsed(self):
+ parsed_str = """set protocols ospf area 2 area-type 'normal'
+ set protocols ospf area 2 authentication 'plaintext-password'
+ set protocols ospf area 2 shortcut 'enable'
+ set protocols ospf area 3 area-type 'nssa'
+ set protocols ospf area 4 area-type stub default-cost '20'
+ set protocols ospf area 4 network '192.0.2.0/24'
+ set protocols ospf area 4 range 192.0.3.0/24 cost '10'
+ set protocols ospf area 4 range 192.0.4.0/24 cost '12'
+ set protocols ospf default-information originate 'always'
+ set protocols ospf default-information originate metric '10'
+ set protocols ospf default-information originate metric-type '2'
+set protocols ospf auto-cost reference-bandwidth '2'
+set protocols ospf default-information originate route-map 'ingress'
+set protocols ospf log-adjacency-changes 'detail'
+set protocols ospf max-metric router-lsa 'administrative'
+set protocols ospf max-metric router-lsa on-shutdown '10'
+set protocols ospf max-metric router-lsa on-startup '10'
+set protocols ospf mpls-te 'enable'
+set protocols ospf mpls-te router-address '192.0.11.11'
+set protocols ospf neighbor 192.0.11.12 poll-interval '10'
+set protocols ospf neighbor 192.0.11.12 priority '2'
+set protocols ospf parameters abr-type 'cisco'
+set protocols ospf parameters 'opaque-lsa'
+set protocols ospf parameters 'rfc1583-compatibility'
+set protocols ospf parameters router-id '192.0.1.1'
+set protocols ospf passive-interface 'eth1'
+set protocols ospf passive-interface 'eth2'
+set protocols ospf redistribute bgp metric '10'
+set protocols ospf redistribute bgp metric-type '2'"""
+ set_module_args(dict(running_config=parsed_str, state="parsed"))
+ result = self.execute_module(changed=False)
+ parsed_list = {
+ "areas": [
+ {
+ "area_id": "2",
+ "area_type": {"normal": True},
+ "authentication": "plaintext-password",
+ "shortcut": "enable",
+ },
+ {"area_id": "3", "area_type": {"nssa": {"set": True}}},
+ {
+ "area_id": "4",
+ "area_type": {"stub": {"default_cost": 20, "set": True}},
+ "network": [{"address": "192.0.2.0/24"}],
+ "range": [
+ {"address": "192.0.3.0/24", "cost": 10},
+ {"address": "192.0.4.0/24", "cost": 12},
+ ],
+ },
+ ],
+ "auto_cost": {"reference_bandwidth": 2},
+ "default_information": {
+ "originate": {
+ "always": True,
+ "metric": 10,
+ "metric_type": 2,
+ "route_map": "ingress",
+ }
+ },
+ "log_adjacency_changes": "detail",
+ "max_metric": {
+ "router_lsa": {
+ "administrative": True,
+ "on_shutdown": 10,
+ "on_startup": 10,
+ }
+ },
+ "mpls_te": {"enabled": True, "router_address": "192.0.11.11"},
+ "neighbor": [
+ {
+ "neighbor_id": "192.0.11.12",
+ "poll_interval": 10,
+ "priority": 2,
+ }
+ ],
+ "parameters": {
+ "abr_type": "cisco",
+ "opaque_lsa": True,
+ "rfc1583_compatibility": True,
+ "router_id": "192.0.1.1",
+ },
+ "passive_interface": ["eth2", "eth1"],
+ "redistribute": [
+ {"metric": 10, "metric_type": 2, "route_type": "bgp"}
+ ],
+ }
+ self.assertEqual(sorted(parsed_list), sorted(result["parsed"]))
+
+ def test_vyos_ospfv2_rendered(self):
+ set_module_args(
+ dict(
+ config=dict(
+ log_adjacency_changes="detail",
+ mpls_te=dict(enabled=True, router_address="192.0.11.11"),
+ auto_cost=dict(reference_bandwidth=2),
+ areas=[
+ dict(
+ area_id="2",
+ area_type=dict(normal=True),
+ authentication="plaintext-password",
+ shortcut="enable",
+ ),
+ dict(
+ area_id="4",
+ area_type=dict(stub=dict(default_cost=10)),
+ network=[dict(address="192.0.2.0/24"),],
+ range=[
+ dict(address="192.0.3.0/24", cost=10),
+ dict(address="192.0.4.0/24", cost=12),
+ ],
+ ),
+ ],
+ ),
+ state="rendered",
+ )
+ )
+ commands = [
+ "set protocols ospf mpls-te enable",
+ "set protocols ospf mpls-te router-address '192.0.11.11'",
+ "set protocols ospf auto-cost reference-bandwidth '2'",
+ "set protocols ospf log-adjacency-changes 'detail'",
+ "set protocols ospf area '2'",
+ "set protocols ospf area 2 authentication plaintext-password",
+ "set protocols ospf area 2 shortcut enable",
+ "set protocols ospf area 2 area-type normal",
+ "set protocols ospf area 4 range 192.0.3.0/24 cost 10",
+ "set protocols ospf area 4 range 192.0.3.0/24",
+ "set protocols ospf area 4 range 192.0.4.0/24 cost 12",
+ "set protocols ospf area 4 range 192.0.4.0/24",
+ "set protocols ospf area 4 area-type stub default-cost 10",
+ "set protocols ospf area '4'",
+ "set protocols ospf area 4 network 192.0.2.0/24",
+ ]
+ result = self.execute_module(changed=False)
+ self.assertEqual(
+ sorted(result["rendered"]), sorted(commands), result["rendered"]
+ )