summaryrefslogtreecommitdiff
path: root/plugins/module_utils
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/module_utils')
-rw-r--r--plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py11
-rw-r--r--plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py12
-rw-r--r--plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py12
-rw-r--r--plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py11
-rw-r--r--plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py11
-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/argspec/ospfv3/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py94
-rw-r--r--plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py25
-rw-r--r--plugins/module_utils/network/vyos/config/interfaces/interfaces.py70
-rw-r--r--plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py60
-rw-r--r--plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py72
-rw-r--r--plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py58
-rw-r--r--plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py75
-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/config/ospfv3/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py464
-rw-r--r--plugins/module_utils/network/vyos/config/static_routes/static_routes.py8
-rw-r--r--plugins/module_utils/network/vyos/facts/facts.py8
-rw-r--r--plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py7
-rw-r--r--plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py26
-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/facts/ospfv3/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py213
-rw-r--r--plugins/module_utils/network/vyos/utils/utils.py37
28 files changed, 2822 insertions, 169 deletions
diff --git a/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py b/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py
index 3542cb19..fd8b6123 100644
--- a/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py
+++ b/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py
@@ -61,8 +61,17 @@ class InterfacesArgs(object): # pylint: disable=R0903
},
"type": "list",
},
+ "running_config": {"type": "str"},
"state": {
- "choices": ["merged", "replaced", "overridden", "deleted"],
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "rendered",
+ "parsed",
+ "gathered",
+ ],
"default": "merged",
"type": "str",
},
diff --git a/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py
index 91434e4b..2f1dfe45 100644
--- a/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py
+++ b/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py
@@ -25,7 +25,6 @@
The arg spec for the vyos_l3_interfaces module
"""
-
from __future__ import absolute_import, division, print_function
__metaclass__ = type
@@ -73,8 +72,17 @@ class L3_interfacesArgs(object): # pylint: disable=R0903
},
"type": "list",
},
+ "running_config": {"type": "str"},
"state": {
- "choices": ["merged", "replaced", "overridden", "deleted"],
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "rendered",
+ "gathered",
+ "parsed",
+ ],
"default": "merged",
"type": "str",
},
diff --git a/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py
index 97c5d5a2..6cfdabf1 100644
--- a/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py
+++ b/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py
@@ -19,7 +19,6 @@
# builder template.
#
#############################################
-
"""
The arg spec for the vyos_lag_interfaces module
"""
@@ -72,8 +71,17 @@ class Lag_interfacesArgs(object): # pylint: disable=R0903
},
"type": "list",
},
+ "running_config": {"type": "str"},
"state": {
- "choices": ["merged", "replaced", "overridden", "deleted"],
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "rendered",
+ "gathered",
+ "parsed",
+ ],
"default": "merged",
"type": "str",
},
diff --git a/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py
index 84bbc00c..6205fd77 100644
--- a/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py
+++ b/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py
@@ -19,7 +19,6 @@
# builder template.
#
#############################################
-
"""
The arg spec for the vyos_lldp_global module
"""
@@ -48,8 +47,16 @@ class Lldp_globalArgs(object): # pylint: disable=R0903
},
"type": "dict",
},
+ "running_config": {"type": "str"},
"state": {
- "choices": ["merged", "replaced", "deleted"],
+ "choices": [
+ "merged",
+ "replaced",
+ "deleted",
+ "rendered",
+ "parsed",
+ "gathered",
+ ],
"default": "merged",
"type": "str",
},
diff --git a/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py
index 2976fc09..109ea430 100644
--- a/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py
+++ b/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py
@@ -81,8 +81,17 @@ class Lldp_interfacesArgs(object): # pylint: disable=R0903
},
"type": "list",
},
+ "running_config": {"type": "str"},
"state": {
- "choices": ["merged", "replaced", "overridden", "deleted"],
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "rendered",
+ "gathered",
+ "parsed",
+ ],
"default": "merged",
"type": "str",
},
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 00000000..e69de29b
--- /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 00000000..275aaf36
--- /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/argspec/ospfv3/__init__.py b/plugins/module_utils/network/vyos/argspec/ospfv3/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/ospfv3/__init__.py
diff --git a/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py
new file mode 100644
index 00000000..66aaa8c4
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py
@@ -0,0 +1,94 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the vyos_ospfv3 module
+"""
+
+
+class Ospfv3Args(object): # pylint: disable=R0903
+ """The arg spec for the vyos_ospfv3 module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "config": {
+ "options": {
+ "areas": {
+ "elements": "dict",
+ "options": {
+ "area_id": {"type": "str"},
+ "export_list": {"type": "str"},
+ "import_list": {"type": "str"},
+ "range": {
+ "elements": "dict",
+ "options": {
+ "address": {"type": "str"},
+ "advertise": {"type": "bool"},
+ "not_advertise": {"type": "bool"},
+ },
+ "type": "list",
+ },
+ },
+ "type": "list",
+ },
+ "parameters": {
+ "options": {"router_id": {"type": "str"}},
+ "type": "dict",
+ },
+ "redistribute": {
+ "elements": "dict",
+ "options": {
+ "route_map": {"type": "str"},
+ "route_type": {
+ "choices": [
+ "bgp",
+ "connected",
+ "kernel",
+ "ripng",
+ "static",
+ ],
+ "type": "str",
+ },
+ },
+ "type": "list",
+ },
+ },
+ "type": "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/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py
index e58593f4..5c377410 100644
--- a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py
+++ b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py
@@ -235,28 +235,11 @@ class Firewall_rules(ConfigBase):
have, rs["name"], "r_list"
)
if h:
- w_rules = rs.get("rules") or []
- h_rules = h.get("rules") or []
- if w_rules and h_rules:
- for rule in w_rules:
- if self.search_r_sets_in_have(
- h_rules, rule["number"], "rules"
- ):
- commands.append(
- self._add_r_base_attrib(
- w["afi"],
- rs["name"],
- "number",
- rule,
- opr=False,
- )
- )
- else:
- commands.append(
- self._compute_command(
- w["afi"], h["name"], remove=True
- )
+ commands.append(
+ self._compute_command(
+ w["afi"], h["name"], remove=True
)
+ )
elif have:
for h in have:
if h["afi"] == w["afi"]:
diff --git a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py
index deb504c2..51bf98e3 100644
--- a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py
+++ b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py
@@ -13,7 +13,6 @@ 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,
@@ -49,14 +48,14 @@ class Interfaces(ConfigBase):
def __init__(self, module):
super(Interfaces, self).__init__(module)
- def get_interfaces_facts(self):
+ def get_interfaces_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
+ self.gather_subset, self.gather_network_resources, data=data
)
interfaces_facts = facts["ansible_network_resources"].get("interfaces")
if not interfaces_facts:
@@ -72,25 +71,42 @@ class Interfaces(ConfigBase):
commands = list()
warnings = list()
- existing_interfaces_facts = self.get_interfaces_facts()
- commands.extend(self.set_config(existing_interfaces_facts))
- if commands:
- if self._module.check_mode:
- resp = self._connection.edit_config(commands, commit=False)
- else:
- resp = self._connection.edit_config(commands)
- result["changed"] = True
+ if self.state in self.ACTION_STATES:
+ existing_interfaces_facts = self.get_interfaces_facts()
+ else:
+ existing_interfaces_facts = []
- result["commands"] = commands
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_interfaces_facts))
- if self._module._diff:
- result["diff"] = resp["diff"] if result["changed"] else None
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
- changed_interfaces_facts = self.get_interfaces_facts()
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_interfaces_facts = self.get_interfaces_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_interfaces_facts(data=running_config)
+ else:
+ changed_interfaces_facts = []
- result["before"] = existing_interfaces_facts
- if result["changed"]:
- result["after"] = changed_interfaces_facts
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_interfaces_facts
+ elif self.state == "gathered":
+ result["gathered"] = changed_interfaces_facts
result["warnings"] = warnings
return result
@@ -118,19 +134,21 @@ class Interfaces(ConfigBase):
to the desired configuration
"""
commands = []
- state = self._module.params["state"]
- if state in ("merged", "replaced", "overridden") and not want:
+ if (
+ self.state in ("merged", "replaced", "overridden", "rendered")
+ and not want
+ ):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
- state
+ self.state
)
)
- if state == "overridden":
+ if self.state == "overridden":
commands.extend(self._state_overridden(want=want, have=have))
- elif state == "deleted":
+ elif self.state == "deleted":
if not want:
for intf in have:
commands.extend(
@@ -146,12 +164,12 @@ class Interfaces(ConfigBase):
obj_in_have = search_obj_in_list(name, have)
if not obj_in_have:
- obj_in_have = {"name": item["name"]}
+ obj_in_have = {"name": name}
- elif state == "merged":
+ if self.state in ("merged", "rendered"):
commands.extend(self._state_merged(item, obj_in_have))
- elif state == "replaced":
+ elif self.state == "replaced":
commands.extend(self._state_replaced(item, obj_in_have))
return commands
diff --git a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py
index a23e417f..122cc1eb 100644
--- a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py
+++ b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py
@@ -52,14 +52,14 @@ class L3_interfaces(ConfigBase):
def __init__(self, module):
super(L3_interfaces, self).__init__(module)
- def get_l3_interfaces_facts(self):
+ def get_l3_interfaces_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
+ self.gather_subset, self.gather_network_resources, data=data
)
l3_interfaces_facts = facts["ansible_network_resources"].get(
"l3_interfaces"
@@ -78,25 +78,44 @@ class L3_interfaces(ConfigBase):
warnings = list()
commands = list()
- existing_l3_interfaces_facts = self.get_l3_interfaces_facts()
- commands.extend(self.set_config(existing_l3_interfaces_facts))
- if commands:
- if self._module.check_mode:
- resp = self._connection.edit_config(commands, commit=False)
- else:
- resp = self._connection.edit_config(commands)
- result["changed"] = True
+ if self.state in self.ACTION_STATES:
+ existing_l3_interfaces_facts = self.get_l3_interfaces_facts()
+ else:
+ existing_l3_interfaces_facts = []
- result["commands"] = commands
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_l3_interfaces_facts))
- if self._module._diff:
- result["diff"] = resp["diff"] if result["changed"] else None
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
- changed_l3_interfaces_facts = self.get_l3_interfaces_facts()
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_l3_interfaces_facts = self.get_l3_interfaces_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_l3_interfaces_facts(
+ data=running_config
+ )
+ else:
+ changed_l3_interfaces_facts = []
- result["before"] = existing_l3_interfaces_facts
- if result["changed"]:
- result["after"] = changed_l3_interfaces_facts
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_l3_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_l3_interfaces_facts
+ elif self.state == "gathered":
+ result["gathered"] = changed_l3_interfaces_facts
result["warnings"] = warnings
return result
@@ -126,7 +145,10 @@ class L3_interfaces(ConfigBase):
commands = []
state = self._module.params["state"]
- if state in ("merged", "replaced", "overridden") and not want:
+ if (
+ state in ("merged", "replaced", "overridden", "rendered")
+ and not want
+ ):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
state
@@ -154,7 +176,7 @@ class L3_interfaces(ConfigBase):
if not obj_in_have:
obj_in_have = {"name": item["name"]}
- if state == "merged":
+ if state in ("merged", "rendered"):
commands.extend(self._state_merged(item, obj_in_have))
elif state == "replaced":
diff --git a/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py
index 2a9efd99..452670f1 100644
--- a/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py
+++ b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py
@@ -56,14 +56,14 @@ class Lag_interfaces(ConfigBase):
def __init__(self, module):
super(Lag_interfaces, self).__init__(module)
- def get_lag_interfaces_facts(self):
+ def get_lag_interfaces_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
+ self.gather_subset, self.gather_network_resources, data=data
)
lag_interfaces_facts = facts["ansible_network_resources"].get(
"lag_interfaces"
@@ -79,27 +79,47 @@ class Lag_interfaces(ConfigBase):
:returns: The result from module execution
"""
result = {"changed": False}
- commands = list()
warnings = list()
- existing_lag_interfaces_facts = self.get_lag_interfaces_facts()
- commands.extend(self.set_config(existing_lag_interfaces_facts))
- if commands:
- if self._module.check_mode:
- resp = self._connection.edit_config(commands, commit=False)
- else:
- resp = self._connection.edit_config(commands)
- result["changed"] = True
+ commands = list()
+
+ if self.state in self.ACTION_STATES:
+ existing_lag_interfaces_facts = self.get_lag_interfaces_facts()
+ else:
+ existing_lag_interfaces_facts = []
- result["commands"] = commands
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_lag_interfaces_facts))
- if self._module._diff:
- result["diff"] = resp["diff"] if result["changed"] else None
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
- changed_lag_interfaces_facts = self.get_lag_interfaces_facts()
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_lag_interfaces_facts = self.get_lag_interfaces_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_lag_interfaces_facts(
+ data=running_config
+ )
+ else:
+ changed_lag_interfaces_facts = []
- result["before"] = existing_lag_interfaces_facts
- if result["changed"]:
- result["after"] = changed_lag_interfaces_facts
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_lag_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_lag_interfaces_facts
+ elif self.state == "gathered":
+ result["gathered"] = changed_lag_interfaces_facts
result["warnings"] = warnings
return result
@@ -127,16 +147,18 @@ class Lag_interfaces(ConfigBase):
to the desired configuration
"""
commands = []
- state = self._module.params["state"]
- if state in ("merged", "replaced", "overridden") and not want:
+ if (
+ self.state in ("merged", "replaced", "overridden", "rendered")
+ and not want
+ ):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
- state
+ self.state
)
)
- if state == "overridden":
+ if self.state == "overridden":
commands.extend(self._state_overridden(want, have))
- elif state == "deleted":
+ elif self.state == "deleted":
if want:
for want_item in want:
name = want_item["name"]
@@ -149,9 +171,9 @@ class Lag_interfaces(ConfigBase):
for want_item in want:
name = want_item["name"]
obj_in_have = search_obj_in_list(name, have)
- if state == "merged":
+ if self.state in ("merged", "rendered"):
commands.extend(self._state_merged(want_item, obj_in_have))
- elif state == "replaced":
+ elif self.state == "replaced":
commands.extend(
self._state_replaced(want_item, obj_in_have)
)
diff --git a/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py
index 010e96dd..c70d27ff 100644
--- a/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py
+++ b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py
@@ -47,20 +47,20 @@ class Lldp_global(ConfigBase):
def __init__(self, module):
super(Lldp_global, self).__init__(module)
- def get_lldp_global_facts(self):
+ def get_lldp_global_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
+ self.gather_subset, self.gather_network_resources, data=data
)
lldp_global_facts = facts["ansible_network_resources"].get(
"lldp_global"
)
if not lldp_global_facts:
- return {}
+ return []
return lldp_global_facts
def execute_module(self):
@@ -70,22 +70,45 @@ class Lldp_global(ConfigBase):
:returns: The result from module execution
"""
result = {"changed": False}
- commands = list()
warnings = list()
+ commands = list()
+
+ if self.state in self.ACTION_STATES:
+ existing_lldp_global_facts = self.get_lldp_global_facts()
+ else:
+ existing_lldp_global_facts = []
- existing_lldp_global_facts = self.get_lldp_global_facts()
- commands.extend(self.set_config(existing_lldp_global_facts))
- if commands:
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_lldp_global_facts))
+
+ if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
- result["commands"] = commands
- changed_lldp_global_facts = self.get_lldp_global_facts()
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_lldp_global_facts = self.get_lldp_global_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_lldp_global_facts(data=running_config)
+ else:
+ changed_lldp_global_facts = []
- result["before"] = existing_lldp_global_facts
- if result["changed"]:
- result["after"] = changed_lldp_global_facts
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_lldp_global_facts
+ if result["changed"]:
+ result["after"] = changed_lldp_global_facts
+ elif self.state == "gathered":
+ result["gathered"] = changed_lldp_global_facts
result["warnings"] = warnings
return result
@@ -113,18 +136,17 @@ class Lldp_global(ConfigBase):
to the desired configuration
"""
commands = []
- state = self._module.params["state"]
- if state in ("merged", "replaced") and not want:
+ if self.state in ("merged", "replaced", "rendered") and not want:
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
- state
+ self.state
)
)
- if state == "deleted":
+ if self.state == "deleted":
commands.extend(self._state_deleted(want=None, have=have))
- elif state == "merged":
+ elif self.state in ("merged", "rendered"):
commands.extend(self._state_merged(want=want, have=have))
- elif state == "replaced":
+ elif self.state == "replaced":
commands.extend(self._state_replaced(want=want, have=have))
return commands
diff --git a/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py
index 377fec9a..94e39c35 100644
--- a/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py
+++ b/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py
@@ -15,7 +15,6 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
-
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
@@ -54,14 +53,14 @@ class Lldp_interfaces(ConfigBase):
def __init__(self, module):
super(Lldp_interfaces, self).__init__(module)
- def get_lldp_interfaces_facts(self):
+ def get_lldp_interfaces_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
+ self.gather_subset, self.gather_network_resources, data=data
)
lldp_interfaces_facts = facts["ansible_network_resources"].get(
"lldp_interfaces"
@@ -77,26 +76,47 @@ class Lldp_interfaces(ConfigBase):
:returns: The result from module execution
"""
result = {"changed": False}
- commands = list()
warnings = list()
- existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
- commands.extend(self.set_config(existing_lldp_interfaces_facts))
- if commands:
- if self._module.check_mode:
- resp = self._connection.edit_config(commands, commit=False)
- else:
- resp = self._connection.edit_config(commands)
- result["changed"] = True
+ commands = list()
+
+ if self.state in self.ACTION_STATES:
+ existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
+ else:
+ existing_lldp_interfaces_facts = []
- result["commands"] = commands
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_lldp_interfaces_facts))
- if self._module._diff:
- result["diff"] = resp["diff"] if result["changed"] else None
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
- changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
- result["before"] = existing_lldp_interfaces_facts
- if result["changed"]:
- result["after"] = changed_lldp_interfaces_facts
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_lldp_interfaces_facts = self.get_lldp_interfaces_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_lldp_interfaces_facts(
+ data=running_config
+ )
+ else:
+ changed_lldp_interfaces_facts = []
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_lldp_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_lldp_interfaces_facts
+ elif self.state == "gathered":
+ result["gathered"] = changed_lldp_interfaces_facts
result["warnings"] = warnings
return result
@@ -124,16 +144,18 @@ class Lldp_interfaces(ConfigBase):
to the desired configuration
"""
commands = []
- state = self._module.params["state"]
- if state in ("merged", "replaced", "overridden") and not want:
+ if (
+ self.state in ("merged", "replaced", "overridden", "rendered")
+ and not want
+ ):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
- state
+ self.state
)
)
- if state == "overridden":
+ if self.state == "overridden":
commands.extend(self._state_overridden(want=want, have=have))
- elif state == "deleted":
+ elif self.state == "deleted":
if want:
for item in want:
name = item["name"]
@@ -150,11 +172,11 @@ class Lldp_interfaces(ConfigBase):
for want_item in want:
name = want_item["name"]
have_item = search_obj_in_list(name, have)
- if state == "merged":
+ if self.state in ("merged", "rendered"):
commands.extend(
self._state_merged(want=want_item, have=have_item)
)
- else:
+ if self.state == "replaced":
commands.extend(
self._state_replaced(want=want_item, have=have_item)
)
@@ -243,7 +265,6 @@ class Lldp_interfaces(ConfigBase):
lldp_name = want["name"]
params = Lldp_interfaces.params
- commands.extend(self._add_location(lldp_name, want, have))
for attrib in params:
value = want[attrib]
if value:
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 00000000..e69de29b
--- /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 00000000..fd25c178
--- /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/config/ospfv3/__init__.py b/plugins/module_utils/network/vyos/config/ospfv3/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/ospfv3/__init__.py
diff --git a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py
new file mode 100644
index 00000000..acda3801
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py
@@ -0,0 +1,464 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The vyos_ospfv3 class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from copy import deepcopy
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+ remove_empties,
+ search_obj_in_list,
+)
+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 (
+ _in_target,
+ _is_w_same,
+ _bool_to_str,
+)
+
+
+class Ospfv3(ConfigBase):
+ """
+ The vyos_ospfv3 class
+ """
+
+ gather_subset = [
+ "!all",
+ "!min",
+ ]
+
+ gather_network_resources = [
+ "ospfv3",
+ ]
+
+ def __init__(self, module):
+ super(Ospfv3, self).__init__(module)
+
+ def get_ospfv3_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ ospfv3_facts = facts["ansible_network_resources"].get("ospfv3", {})
+ return ospfv3_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ warnings = list()
+ commands = list()
+
+ if self.state in self.ACTION_STATES:
+ existing_ospfv3_facts = self.get_ospfv3_facts()
+ else:
+ existing_ospfv3_facts = {}
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_ospfv3_facts))
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_ospfv3_facts = self.get_ospfv3_facts()
+ elif self.state == "rendered":
+ result["rendered"] = commands
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.get_ospfv3_facts(data=running_config)
+ else:
+ changed_ospfv3_facts = {}
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_ospfv3_facts
+ if result["changed"]:
+ result["after"] = changed_ospfv3_facts
+ elif self.state == "gathered":
+ result["gathered"] = changed_ospfv3_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_ospfv3_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ want = self._module.params["config"]
+ have = existing_ospfv3_facts
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, w, h):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ if (
+ self.state in ("merged", "replaced", "overridden", "rendered")
+ and not w
+ ):
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ self.state
+ )
+ )
+ if self.state == "deleted":
+ commands.extend(self._state_deleted(w, 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 _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, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if have:
+ commands.append("delete protocols ospfv3")
+ return commands
+
+ def _render_ospf_param(self, want, have, opr=True):
+ """
+ This function forms the set/delete commands for ospf leaf attributes
+ and triggers the process for other child attributes.
+ for firewall_global attributes.
+ :param w: the desired config.
+ :param h: the target config.
+ :param opr: True/False.
+ :return: generated commands list.
+ """
+ commands = []
+ w = deepcopy(remove_empties(want))
+ if w:
+ for key, val in iteritems(w):
+ commands.extend(self._render_child_param(w, have, key, opr))
+ return commands
+
+ def _render_child_param(self, w, h, key, opr=True):
+ """
+ This function invoke the function to extend commands
+ based on the key.
+ :param w: the desired configuration.
+ :param h: the current configuration.
+ :param key: attribute name.
+ :param opr: operation.
+ :return: list of commands.
+ """
+ commands = []
+ if key == "areas":
+ commands.extend(self._render_areas(key, w, h, opr=opr))
+ elif key == "parameters":
+ commands.extend(self._render_dict_param(key, w, h, opr=opr))
+ elif key == "redistribute":
+ commands.extend(self._render_list_dict_param(key, w, h, opr=opr))
+ return commands
+
+ def _render_dict_param(self, attr, want, have, opr=True):
+ """
+ This function generate the commands for dictionary elements.
+ :param attr: attribute name.
+ :param w: the desired configuration.
+ :param h: the target config.
+ :param opr: True/False.
+ :return: generated list of commands.
+ """
+ commands = []
+ h = {}
+ if have:
+ h = have.get(attr) or {}
+ if not opr and not h:
+ commands.append(self._form_attr_cmd(attr=attr, opr=opr))
+ elif want[attr]:
+ leaf_dict = {"parameters": "router_id"}
+ leaf = leaf_dict[attr]
+ for item, value in iteritems(want[attr]):
+ if (
+ opr
+ and item in leaf
+ and not _is_w_same(want[attr], h, item)
+ ):
+ commands.append(
+ self._form_attr_cmd(
+ key=attr, attr=item, val=value, opr=opr
+ )
+ )
+ elif not opr and item in leaf and not _in_target(h, item):
+ commands.append(
+ self._form_attr_cmd(key=attr, attr=item, opr=opr)
+ )
+ return commands
+
+ def _render_list_dict_param(self, attr, want, have, cmd=None, opr=True):
+ """
+ This function forms the set/delete commands based on the 'opr' type
+ for attributes with in desired list of dictionary.
+ :param attr: attribute name.
+ :param w: the desired config.
+ :param h: the target config.
+ :param cmd: commands to be prepend.
+ :param opr: True/False.
+ :return: generated commands list.
+ """
+ commands = []
+ h = []
+ name = {
+ "redistribute": "route_type",
+ "range": "address",
+ }
+ leaf_dict = {
+ "redistribute": ("route_map", "route_type"),
+ "range": ("address", "advertise", "not_advertise"),
+ }
+ leaf = leaf_dict[attr]
+ w = want.get(attr) or []
+ if have:
+ h = have.get(attr) or []
+ if not opr and not h:
+ commands.append(self._compute_command(attr=attr, opr=opr))
+ elif w:
+ for w_item in w:
+ for key, val in iteritems(w_item):
+ if not cmd:
+ cmd = self._compute_command(opr=opr)
+ h_item = search_obj_in_list(
+ w_item[name[attr]], h, name[attr]
+ )
+ if (
+ opr
+ and key in leaf
+ and not _is_w_same(w_item, h_item, key)
+ ):
+ if key == "route_type" or (
+ key == "address"
+ and "advertise" not in w_item
+ and "not-advertise" not in w_item
+ ):
+ if not val:
+ cmd = cmd.replace("set", "delete")
+ commands.append(cmd + attr + " " + str(val))
+ elif key in leaf_dict["range"] and key != "address":
+ commands.append(
+ cmd
+ + attr
+ + " "
+ + w_item[name[attr]]
+ + " "
+ + key.replace("_", "-")
+ )
+ elif key == "route_map":
+ commands.append(
+ cmd
+ + attr
+ + " "
+ + w_item[name[attr]]
+ + " "
+ + key.replace("_", "-")
+ + " "
+ + str(val)
+ )
+ elif (
+ not opr and key in leaf and not _in_target(h_item, key)
+ ):
+ if key in ("route_type", "address"):
+ commands.append(cmd + attr + " " + str(val))
+ else:
+ commands.append(
+ cmd
+ + (attr + " " + w_item[name[attr]] + " " + key)
+ )
+ return commands
+
+ def _render_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", "export_list", "import_list")
+ 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 = search_obj_in_list(
+ w_area["area_id"], h_lst, "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.replace("_", "-")
+ + " "
+ + _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 == "range":
+ commands.extend(
+ self._render_list_dict_param(
+ key, w_area, h_area, cmd, opr
+ )
+ )
+ return commands
+
+ def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True):
+ """
+ This function forms the command for leaf attribute.
+ :param key: parent key.
+ :param attr: attribute name
+ :param value: value
+ :param opr: True/False.
+ :return: generated command.
+ """
+ return self._compute_command(
+ key, attr=self._map_attrib(attr), val=val, opr=opr
+ )
+
+ def _compute_command(
+ self, key=None, attr=None, val=None, remove=False, opr=True
+ ):
+ """
+ This function construct the add/delete command based on passed attributes.
+ :param key: parent key.
+ :param attr: attribute name
+ :param value: value
+ :param opr: True/False.
+ :return: generated command.
+ """
+ if remove or not opr:
+ cmd = "delete protocols ospfv3 "
+ else:
+ cmd = "set protocols ospfv3 "
+ if key:
+ cmd += key.replace("_", "-") + " "
+ if attr:
+ cmd += attr.replace("_", "-")
+ if val and opr:
+ cmd += " '" + str(val) + "'"
+ return cmd
+
+ def _map_attrib(self, attrib):
+ """
+ - This function construct the regex string.
+ - replace the underscore with hyphen.
+ :param attrib: attribute
+ :return: regex string
+ """
+ return "disable" if attrib == "disabled" else attrib.replace("_", "-")
diff --git a/plugins/module_utils/network/vyos/config/static_routes/static_routes.py b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py
index e93d4ee3..b359dbba 100644
--- a/plugins/module_utils/network/vyos/config/static_routes/static_routes.py
+++ b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py
@@ -160,7 +160,7 @@ class Static_routes(ConfigBase):
routes = self._get_routes(want)
for r in routes:
h_item = self.search_route_in_have(have, r["dest"])
- if self.state == "merged" or self.state == "rendered":
+ if self.state in ("merged", "rendered"):
commands.extend(self._state_merged(want=r, have=h_item))
elif self.state == "replaced":
commands.extend(self._state_replaced(want=r, have=h_item))
@@ -253,12 +253,6 @@ class Static_routes(ConfigBase):
afi=item["afi"], remove=True
)
)
- for r in routes:
- h_route = self.search_route_in_have(have, r["dest"])
- if h_route:
- commands.extend(
- self._render_updates(r, h_route, opr=False)
- )
else:
routes = self._get_routes(have)
if self._is_ip_route_exist(routes):
diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py
index ff3d0988..4c7b340d 100644
--- a/plugins/module_utils/network/vyos/facts/facts.py
+++ b/plugins/module_utils/network/vyos/facts/facts.py
@@ -40,6 +40,12 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firew
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_interfaces.firewall_interfaces import (
Firewall_interfacesFacts,
)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv3.ospfv3 import (
+ Ospfv3Facts,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv2.ospfv2 import (
+ Ospfv2Facts,
+)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import (
Default,
Neighbors,
@@ -58,6 +64,8 @@ FACT_RESOURCE_SUBSETS = dict(
firewall_rules=Firewall_rulesFacts,
firewall_global=Firewall_globalFacts,
firewall_interfaces=Firewall_interfacesFacts,
+ ospfv3=Ospfv3Facts,
+ ospfv2=Ospfv2Facts,
)
diff --git a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py
index d1d62c23..3b99d347 100644
--- a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py
+++ b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py
@@ -10,7 +10,12 @@ for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
-from __future__ import absolute_import, division, print_function
+from __future__ import (
+ absolute_import,
+ division,
+ print_function,
+ unicode_literals,
+)
__metaclass__ = type
diff --git a/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py
index 9201e5c6..90562947 100644
--- a/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py
+++ b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py
@@ -12,6 +12,7 @@ based on the configuration.
from __future__ import absolute_import, division, print_function
__metaclass__ = type
+
from re import findall, search, M
from copy import deepcopy
@@ -59,30 +60,21 @@ class Lag_interfacesFacts(object):
lag_regex = r" %s .+$" % lag
cfg = findall(lag_regex, data, M)
obj = self.render_config(cfg)
-
- output = connection.run_commands(
- ["show interfaces bonding " + lag + " slaves"]
- )
- lines = output[0].splitlines()
members = []
member = {}
- if len(lines) > 1:
- for line in lines[2:]:
- splitted_line = line.split()
-
- if len(splitted_line) > 1:
- member["member"] = splitted_line[0]
- members.append(member)
- else:
- members = []
- member = {}
+
+ group_regex = r".*eth.* '%s'" % lag
+ g_cfg = findall(group_regex, data, M)
+ for item in g_cfg:
+ output = search("^set interfaces ethernet (\\S+)", item, M)
+ if output:
+ member["member"] = output.group(1).strip("'")
+ members.append(member)
obj["name"] = lag.strip("'")
if members:
obj["members"] = members
-
if obj:
objs.append(obj)
-
facts = {}
if objs:
facts["lag_interfaces"] = []
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 00000000..e69de29b
--- /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 00000000..d62fa9ab
--- /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/facts/ospfv3/__init__.py b/plugins/module_utils/network/vyos/facts/ospfv3/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/ospfv3/__init__.py
diff --git a/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py
new file mode 100644
index 00000000..457a9636
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py
@@ -0,0 +1,213 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The vyos ospfv3 fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __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.ospfv3.ospfv3 import (
+ Ospfv3Args,
+)
+
+
+class Ospfv3Facts(object):
+ """ The vyos ospfv3 fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Ospfv3Args.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def get_device_data(self, connection):
+ return connection.get_config()
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for ospfv3
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if not data:
+ data = self.get_device_data(connection)
+ # typically data is populated from the current device configuration
+ # data = connection.get('show running-config | section ^interface')
+ # using mock data instead
+ objs = {}
+ ospfv3 = findall(r"^set protocols ospfv3 (.+)", data, M)
+ if ospfv3:
+ objs = self.render_config(ospfv3)
+ facts = {}
+ params = utils.validate_config(self.argument_spec, {"config": objs})
+ facts["ospfv3"] = 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))
+ config = {}
+ config["parameters"] = self.parse_attrib(
+ conf, "parameters", "parameters"
+ )
+ config["areas"] = self.parse_attrib_list(conf, "area", "area_id")
+ config["redistribute"] = self.parse_attrib_list(
+ conf, "redistribute", "route_type"
+ )
+ return config
+
+ def parse_attrib_list(self, conf, attrib, param):
+ """
+ This function forms the regex to fetch the listed attributes
+ from config
+ :param conf: configuration data
+ :param attrib: attribute name
+ :param param: parameter data
+ :return: generated rule list configuration
+ """
+ r_lst = []
+ if attrib == "area":
+ items = findall(r"^" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M)
+ else:
+ items = findall(r"" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M)
+ if items:
+ a_lst = []
+ for item in set(items):
+ i_regex = r" %s .+$" % item
+ cfg = "\n".join(findall(i_regex, conf, M))
+ if attrib == "area":
+ obj = self.parse_area(cfg, item)
+ else:
+ obj = self.parse_attrib(cfg, attrib)
+ obj[param] = item.strip("'")
+ if obj:
+ a_lst.append(obj)
+ r_lst = sorted(a_lst, key=lambda i: i[param])
+ return r_lst
+
+ def parse_area(self, conf, area_id):
+ """
+ This function triggers the parsing of 'area' attributes.
+ :param conf: configuration data
+ :param area_id: area identity
+ :return: generated rule configuration dictionary.
+ """
+
+ rule = self.parse_attrib(conf, "area_id", match=area_id)
+ r_sub = {"range": self.parse_attrib_list(conf, "range", "address")}
+ rule.update(r_sub)
+ return rule
+
+ 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 = {
+ "area_id": ["export_list", "import_list"],
+ "redistribute": ["route_map"],
+ "range": ["advertise", "not_advertise"],
+ "parameters": ["router_id"],
+ }
+ cfg_dict = self.parse_attr(conf, param_lst[param], match)
+ return cfg_dict
+
+ def parse_attr(self, conf, attr_list, match=None):
+ """
+ This function peforms the following:
+ - Form the regex to fetch the required attribute config.
+ - Type cast the output in desired format.
+ :param conf: configuration.
+ :param attr_list: list of attributes.
+ :param match: parent node/attribute name.
+ :return: generated config dictionary.
+ """
+ config = {}
+ for attrib in attr_list:
+ regex = self.map_regex(attrib)
+ if match:
+ regex = match.replace("_", "-") + " " + regex
+ if conf:
+ if self.is_bool(attrib):
+ out = conf.find(attrib.replace("_", "-"))
+ dis = conf.find(attrib.replace("_", "-") + " 'disable'")
+ if match:
+ en = conf.find(match + " 'enable'")
+ if out >= 1:
+ if dis >= 1:
+ config[attrib] = False
+ else:
+ config[attrib] = True
+ elif match and en >= 1:
+ config[attrib] = True
+ else:
+ out = search(r"^.*" + regex + " (.+)", conf, M)
+ if out:
+ val = out.group(1).strip("'")
+ if self.is_num(attrib):
+ val = int(val)
+ config[attrib] = val
+ return config
+
+ def map_regex(self, attrib):
+ """
+ - This function construct the regex string.
+ - replace the underscore with hyphen.
+ :param attrib: attribute
+ :return: regex string
+ """
+ return (
+ "disable"
+ if attrib == "disabled"
+ else "enable"
+ if attrib == "enabled"
+ else attrib.replace("_", "-")
+ )
+
+ def is_bool(self, attrib):
+ """
+ This function looks for the attribute in predefined bool type set.
+ :param attrib: attribute.
+ :return: True/False
+ """
+ bool_set = ("enabled", "advertise", "not_advertise")
+ return True if attrib in bool_set else False
+
+ def is_num(self, attrib):
+ """
+ This function looks for the attribute in predefined integer type set.
+ :param attrib: attribute.
+ :return: True/false.
+ """
+ num_set = "ospf"
+ return True if attrib in num_set else False
diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py
index 4635234c..7e0f3cc3 100644
--- a/plugins/module_utils/network/vyos/utils/utils.py
+++ b/plugins/module_utils/network/vyos/utils/utils.py
@@ -230,3 +230,40 @@ def get_route_type(address):
return "route6"
elif version == 4:
return "route"
+
+
+def _bool_to_str(val):
+ """
+ This function converts the bool value into string.
+ :param val: bool value.
+ :return: enable/disable.
+ """
+ return (
+ "enable"
+ if str(val) == "True"
+ else "disable"
+ if str(val) == "False"
+ else val
+ )
+
+
+def _is_w_same(w, h, key):
+ """
+ This function checks whether the key value is same in desired and
+ target config dictionary.
+ :param w: base config.
+ :param h: target config.
+ :param key:attribute name.
+ :return: True/False.
+ """
+ return True if h and key in h and h[key] == w[key] else False
+
+
+def _in_target(h, key):
+ """
+ This function checks whether the target exist and key present in target config.
+ :param h: target config.
+ :param key: attribute name.
+ :return: True/False.
+ """
+ return True if h and key in h else False