summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authoromnom62 <75066712+omnom62@users.noreply.github.com>2026-03-04 19:51:16 +1000
committerGitHub <noreply@github.com>2026-03-04 09:51:16 +0000
commita8e5dac21090e9b0ddf876214a9a1522079ecde1 (patch)
tree9865a8b7ad796f8b201b8fea9e93f3c395a5e71f /plugins
parente539028c2f9d062e7684abda5dd32affd92e9f08 (diff)
downloadvyos.vyos-a8e5dac21090e9b0ddf876214a9a1522079ecde1.tar.gz
vyos.vyos-a8e5dac21090e9b0ddf876214a9a1522079ecde1.zip
T6836 VRF module (#408)
* t6836 vfr module init * argspec facts module for VRF * T6834 static_route module to support interface_route (#398) * t6834 init * facts fixes, WIP * T6834 - facts updates * static_routes 1.3- config, facts, args and sanit and unit tests * changelog * changelog updates * static_routes 1.4+ * static_routes v1.4 unit tests * comment removed * T6829 (area interfaces in OSPFv3 1.3-1.5) re-init (#399) * T6829 re-init * re-init T6829 ospfv3 area interface for 1.3- * changelog * changelog fix * Doc is updated * adding 1.4+ support * ospfv3 area interfaces for 1.4+ * fixed ospfv3 facts and added area interface unit test for 1.4 * ospfv3 clean-up * doc updates * Resolve merge conflicts * vrf parsers set_table * more vrf paresers added * vrf facts main code * vrf data structures * simple vrf config push * simple vrf config push * some work on vrf * basic facts done * vrf instance parsers wif * ongoing parser testing * vrf dev work * testing with paresers * all basic settings * merged for vrf * more states for VRF * vrf deleted, overriden * overriden * sanity * rst for vrf * changelog * fix changelog * doc fix * vrf delete idempotent * deleted and overridden for VFR * overriden * overriden vrf idempotency * asdvanced vrf settings * afi for VRF facts * VRF AFI config * VRF AFI tests * vrf dev work * afi dev work * afi and deep_merge fixes for VRF * afi basic provisioning * changelog * vrf sanity fixes * VRF AFI protocols and route-maps * vrf parsed, gathered, rendered fixes * docstrings VRF * docstrings VRF * docstring VRF * module doc update * vrf module examples * VRF doc examples * linter * VRF unit tests init * VRF unit tests init * VRF unit tests updates * fixed VRF overridden and deleted unit test cases * VRF integration tests init * VRF int tests rtt, merged, replaced * VRF int testt gathered, rendered * VRF int tests deleted, rendered, overridden and empty_config * linter * VRF int test misc fixes * VRF global idepmotency * fixing py3.13 runner deps * fixing lint * fixing sanity * reverting previous fixes * VRF protocols load_module and provisioning flow * fixes to bgp VRF protocol methods * VRF protocol facts init * VRF protocol load_module works * reworked VRF load_module routine * vrf protocl static fixed * Fixed static and ospf VRF protocol provisioning * Fixed static and ospf VRF protocol provisioning * VRF doc update * VRF docs update * VRF doc updates * unit tests for VRF protocols * VRF BGP protcol sanity check * VRF protocol fixed sanity checks (DOCUMENTATION) * sanity and doc for VRF fixed * VRF unit tests mock * VRF protocol integration parsed, rtt and merged init * VRF protocols - rendered, overridden * VRF protocols replaced * fix accidental changes to test_vyos_config.py * PR description * fix image * README fix * Fixing linter * Mid-result UAT overriden VRF * Mid-result for VRF UAT * VRF uat * VRF obsolete features removed
Diffstat (limited to 'plugins')
l---------plugins/action/vrf.py1
-rw-r--r--plugins/module_utils/network/vyos/argspec/vrf/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/argspec/vrf/vrf.py131
-rw-r--r--plugins/module_utils/network/vyos/config/vrf/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/config/vrf/vrf.old313
-rw-r--r--plugins/module_utils/network/vyos/config/vrf/vrf.py308
-rw-r--r--plugins/module_utils/network/vyos/facts/facts.py2
-rw-r--r--plugins/module_utils/network/vyos/facts/vrf/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/facts/vrf/vrf.py227
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/vrf.py249
-rw-r--r--plugins/module_utils/network/vyos/utils/utils.py22
-rw-r--r--plugins/modules/vyos_vrf.py1545
12 files changed, 2798 insertions, 0 deletions
diff --git a/plugins/action/vrf.py b/plugins/action/vrf.py
new file mode 120000
index 00000000..331a791f
--- /dev/null
+++ b/plugins/action/vrf.py
@@ -0,0 +1 @@
+vyos.py \ No newline at end of file
diff --git a/plugins/module_utils/network/vyos/argspec/vrf/__init__.py b/plugins/module_utils/network/vyos/argspec/vrf/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/vrf/__init__.py
diff --git a/plugins/module_utils/network/vyos/argspec/vrf/vrf.py b/plugins/module_utils/network/vyos/argspec/vrf/vrf.py
new file mode 100644
index 00000000..e24fd537
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/vrf/vrf.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+# Copyright 2024 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the
+# cli_rm_builder.
+#
+# Manually editing this file is not advised.
+#
+# To update the argspec make the desired changes
+# in the module docstring and re-run
+# cli_rm_builder.
+#
+#############################################
+
+"""
+The arg spec for the vyos_vrf module
+"""
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_global.bgp_global import (
+ Bgp_globalArgs,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import (
+ Ospfv2Args,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv3.ospfv3 import (
+ Ospfv3Args,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.static_routes.static_routes import (
+ Static_routesArgs,
+)
+
+
+class VrfArgs(object): # pylint: disable=R0903
+ """The arg spec for the vyos_vrf module"""
+
+ bgp_argument_spec = Bgp_globalArgs.argument_spec["config"]
+ static_routes_argument_spec = Static_routesArgs.argument_spec["config"]
+ ospfv2_argument_spec = Ospfv2Args.argument_spec["config"]
+ ospfv3_argument_spec = Ospfv3Args.argument_spec["config"]
+
+ argument_spec = {
+ "config": {
+ "type": "dict",
+ "options": {
+ "bind_to_all": {"type": "bool", "default": False},
+ "instances": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "name": {"required": True, "type": "str"},
+ "description": {"type": "str"},
+ "disable": {
+ "aliases": ["disabled"],
+ "default": False,
+ "type": "bool",
+ },
+ "table_id": {"type": "int"},
+ "vni": {"type": "int"},
+ "address_family": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "afi": {
+ "type": "str",
+ "choices": ["ipv4", "ipv6"],
+ },
+ "disable_forwarding": {"type": "bool", "default": False},
+ "nht_no_resolve_via_default": {"type": "bool", "default": False},
+ "route_maps": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "rm_name": {"type": "str", "required": True},
+ "protocol": {
+ "type": "str",
+ "choices": [
+ "any",
+ "babel",
+ "bgp",
+ "eigrp",
+ "isis",
+ "ospf",
+ "rip",
+ "static",
+ ],
+ },
+ },
+ },
+ },
+ },
+ "protocols": {
+ # "type": "list", # sanity
+ # "elements": "dict",
+ "type": "dict",
+ "options": {
+ "bgp": bgp_argument_spec,
+ "ospf": ospfv2_argument_spec,
+ "ospfv3": ospfv3_argument_spec,
+ "static": static_routes_argument_spec,
+ },
+ },
+ },
+ },
+ },
+ },
+ "state": {
+ "type": "str",
+ "choices": [
+ "deleted",
+ "merged",
+ "replaced",
+ "overridden",
+ "gathered",
+ "rendered",
+ "parsed",
+ ],
+ "default": "merged",
+ },
+ "running_config": {"type": "str"},
+ } # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/config/vrf/__init__.py b/plugins/module_utils/network/vyos/config/vrf/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/vrf/__init__.py
diff --git a/plugins/module_utils/network/vyos/config/vrf/vrf.old b/plugins/module_utils/network/vyos/config/vrf/vrf.old
new file mode 100644
index 00000000..b9f56cc6
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/vrf/vrf.old
@@ -0,0 +1,313 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+"""
+The vyos_vrf config file.
+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 its desired end-state is
+created.
+"""
+
+import importlib
+
+from copy import deepcopy
+
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import (
+ ResourceModule,
+)
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_global.bgp_global import (
+ Bgp_global,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.vrf import (
+ VrfTemplate,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import combine
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import (
+ LooseVersion,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
+
+
+# from ansible.plugins.filter.core import combine
+
+
+class Vrf(ResourceModule):
+ """
+ The vyos_vrf config class
+ """
+
+ def __init__(self, module):
+ super(Vrf, self).__init__(
+ empty_fact_val={},
+ facts_module=Facts(module),
+ module=module,
+ resource="vrf",
+ tmplt=VrfTemplate(),
+ )
+ self.parsers = [
+ "bind_to_all",
+ ]
+ self.bgp = Bgp_global(module)
+
+ def _validate_template(self):
+ version = get_os_version(self._module)
+ if LooseVersion(version) >= LooseVersion("1.4"):
+ self._tmplt = VrfTemplate()
+ else:
+ self._module.fail_json(msg="VRF is not supported in this version of VyOS")
+
+ def parse(self):
+ """override parse to check template"""
+ self._validate_template()
+ return super().parse()
+
+ def get_parser(self, name):
+ """get_parsers"""
+ self._validate_template()
+ return super().get_parser(name)
+
+ def execute_module(self):
+ """Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ if self.state not in ["parsed", "gathered"]:
+ self.generate_commands()
+ self.run_commands()
+
+ return self.result
+
+ def generate_commands(self):
+ """Generate configuration commands to send based on
+ want, have and desired state.
+ """
+ wantd = {}
+ haved = {}
+ wantd = deepcopy(self.want)
+ haved = deepcopy(self.have)
+
+ # self._module.fail_json(msg="WanT: " + str(self.want) + "**** H: " + str(self.have))
+
+ # if state is merged, merge want onto have and then compare
+ if self.state in ["merged", "replaced"]:
+ # wantd = dict_merge(wantd, haved)
+ # wantd = haved | combine(wantd, recursive=True)
+ wantd = combine(haved, wantd, recursive=True)
+ # self._module.fail_json(msg="Want: " + str(wantd) + "**** H: " + str(haved))
+
+ # if state is deleted, delete and empty out wantd
+ if self.state == "deleted":
+ w = deepcopy(wantd)
+ if w == {} and haved != {}:
+ self.commands = ["delete vrf"]
+ return
+ for k, want in iteritems(w):
+ if not (k in haved and haved[k]):
+ del wantd[k]
+ else:
+ if isinstance(want, list):
+ for entry in want:
+ wname = entry.get("name")
+ haved["instances"] = [
+ i for i in haved.get("instances", []) if i.get("name") != wname
+ ]
+ self.commands.append("delete vrf name {}".format(wname))
+ else:
+ self.commands.append("delete vrf {}".format(k.replace("_", "-")))
+ del wantd[k]
+
+ if self.state == "overridden":
+ w = deepcopy(wantd)
+ h = deepcopy(haved)
+ for k, want in iteritems(w):
+ if k in haved and haved[k] != want:
+ if isinstance(want, list):
+ for entry in want:
+ wname = entry.get("name")
+ hdict = next(
+ (inst for inst in haved["instances"] if inst["name"] == wname),
+ None,
+ )
+ if entry != hdict:
+ # self._module.fail_json(msg="Want: " + str(entry) + "**** H: " + str(hdict))
+ haved["instances"] = [
+ i for i in haved.get("instances", []) if i.get("name") != wname
+ ]
+ self.commands.append("delete vrf name {}".format(wname))
+ self.commands.append("commit")
+
+ for k, want in iteritems(wantd):
+ if isinstance(want, list):
+ self._compare_instances(want=want, have=haved.pop(k, {}))
+ self.compare(
+ parsers=self.parsers,
+ want={k: want},
+ have={k: haved.pop(k, {})},
+ )
+ self._module.fail_json(msg=self.commands)
+
+ def _compare_instances(self, want, have):
+ """Compare the instances of the VRF"""
+ parsers = [
+ "table_id",
+ "vni",
+ "description",
+ "disable_vrf",
+ ]
+ # self._module.fail_json(msg="want: " + str(want) + "**** have: " + str(have))
+
+ for entry in want:
+ h = {}
+ wname = entry.get("name")
+ # h = next((vrf for vrf in have if vrf["name"] == wname), {})
+ h = {
+ k: v
+ for vrf in have
+ if vrf.get("name") == wname
+ for k, v in vrf.items()
+ if k != "address_family"
+ }
+ self.compare(parsers=parsers, want=entry, have=h)
+
+ if "address_family" in entry:
+ wafi = {"name": wname, "address_family": entry.get("address_family", [])}
+ # hdict = next((item for item in have if item["name"] == wname), None)
+ hdict = next((d for d in have if d.get("name") == wname), None)
+
+ hafi = {
+ "name": (hdict or {"name": wname})["name"],
+ "address_family": hdict.get("address_family", []) if hdict else [],
+ }
+
+ # self._module.fail_json(msg="wafi: " + str(wafi) + "**** hafi: " + str(hafi))
+
+ self._compare_addr_family(wafi, hafi)
+
+ if "protocols" in entry:
+ for protocol_name in entry["protocols"]:
+ protocol_module = self._load_protocol_module(protocol_name)
+ w_p_dict = entry["protocols"][protocol_name]
+ h_p_dict = next(
+ (
+ v.get("protocols", {}).get(protocol_name)
+ for v in have
+ if v.get("name") == wname
+ ),
+ {},
+ )
+ if protocol_name == "bgp":
+ protocol_module._validate_template()
+ protocol_module.want = w_p_dict
+ protocol_module.have = h_p_dict
+ protocol_module.generate_commands()
+ elif protocol_name in [
+ # "ospf",
+ # "ospfv3",
+ "static",
+ ]:
+ self._module.fail_json(msg=str(protocol_module))
+ protocol_module._module.params["config"] = w_p_dict
+ protocol_module.state = self.state
+ self._module.fail_json(msg=str(protocol_module.set_config(h_p_dict)))
+
+ protocol_module.commands = protocol_module.set_config(h_p_dict)
+ self.commands.extend(
+ [
+ cmd.replace("protocols", "vrf name " + wname + " protocols", 1)
+ for cmd in protocol_module.commands
+ ],
+ )
+ protocol_module = None # Clear the module to free resources
+
+ def _compare_addr_family(self, want, have):
+ """Compare the address families of the VRF"""
+ afi_parsers = [
+ # "address_family",
+ "disable_forwarding",
+ "disable_nht",
+ ]
+ # self._module.fail_json(msg="wAfi: " + str(want) + "**** hAfi: " + str(have))
+
+ wafi = self.afi_to_list(want)
+ hafi = self.afi_to_list(have)
+
+ lookup = {(d["name"], d["afi"]): d for d in hafi}
+ pairs = [(d1, lookup.get((d1["name"], d1["afi"]), {})) for d1 in wafi]
+
+ for wafd, hafd in pairs:
+ # self._module.fail_json(msg="wAfd: " + str(wafd) + "**** hAfd: " + str(hafd))
+ if "route_maps" in wafd:
+ self._compare_route_maps(wafd, hafd)
+ self.compare(parsers=afi_parsers, want=wafd, have=hafd)
+ # self.compare(parsers=afi_parsers, want=wafi, have=hafi)
+
+ def afi_to_list(self, data):
+ """Convert address family dict to list"""
+
+ return [
+ {"name": data["name"], **{**af, "afi": "ip" if af["afi"] == "ipv4" else af["afi"]}}
+ for af in data["address_family"]
+ ]
+
+ def _compare_route_maps(self, wafd, hafd):
+ want_rms = wafd.get("route_maps", [])
+ have_rms = hafd.get("route_maps", [])
+
+ for want in want_rms:
+ match = next(
+ (
+ h
+ for h in have_rms
+ if h["rm_name"] == want["rm_name"] and h["protocol"] == want["protocol"]
+ ),
+ {},
+ )
+ base = {"name": wafd["name"], "afi": wafd["afi"]}
+
+ self.compare(
+ parsers="route_maps",
+ want={**base, "route_maps": want},
+ have={**base, "route_maps": match},
+ )
+
+ def _load_protocol_module(self, protocol_name):
+ if protocol_name == "bgp":
+ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_global.bgp_global import (
+ Bgp_global,
+ )
+
+ return Bgp_global(self._module)
+ elif protocol_name == "ospf":
+ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv2.ospfv2 import (
+ Ospfv2,
+ )
+
+ return Ospfv2(self._module)
+ elif protocol_name == "ospfv3":
+ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv3.ospfv3 import (
+ Ospfv3,
+ )
+
+ return Ospfv3(self._module)
+ elif protocol_name == "static":
+ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.static_routes.static_routes import (
+ Static_routes,
+ )
+
+ return Static_routes(self._module)
+ else:
+ self._module.fail_json(msg="The protocol is not supported")
diff --git a/plugins/module_utils/network/vyos/config/vrf/vrf.py b/plugins/module_utils/network/vyos/config/vrf/vrf.py
new file mode 100644
index 00000000..aa4329d9
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/vrf/vrf.py
@@ -0,0 +1,308 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+"""
+The vyos_vrf config file.
+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 its desired end-state is
+created.
+"""
+
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import (
+ ResourceModule,
+)
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_global.bgp_global import (
+ Bgp_global,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv2.ospfv2 import (
+ Ospfv2,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv3.ospfv3 import (
+ Ospfv3,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.static_routes.static_routes import (
+ Static_routes,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.vrf import (
+ VrfTemplate,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import combine
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import (
+ LooseVersion,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
+
+
+class Vrf(ResourceModule):
+ """
+ The vyos_vrf config class
+ """
+
+ def __init__(self, module):
+ super(Vrf, self).__init__(
+ empty_fact_val={},
+ facts_module=Facts(module),
+ module=module,
+ resource="vrf",
+ tmplt=VrfTemplate(),
+ )
+ self.parsers = [
+ "bind_to_all",
+ ]
+
+ def _validate_template(self):
+ version = get_os_version(self._module)
+ if LooseVersion(version) >= LooseVersion("1.4"):
+ self._tmplt = VrfTemplate()
+ else:
+ self._module.fail_json(msg="VRF is not supported in this version of VyOS")
+
+ def parse(self):
+ """override parse to check template"""
+ self._validate_template()
+ return super().parse()
+
+ def get_parser(self, name):
+ """get_parsers"""
+ self._validate_template()
+ return super().get_parser(name)
+
+ def execute_module(self):
+ """Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ if self.state not in ["parsed", "gathered"]:
+ self.generate_commands()
+ self.run_commands()
+
+ return self.result
+
+ def generate_commands(self):
+ """Generate configuration commands to send based on
+ want, have and desired state.
+ """
+ wantd = {}
+ haved = {}
+ wantd = deepcopy(self.want)
+ haved = deepcopy(self.have)
+
+ # if state is merged, merge want onto have and then compare
+ if self.state in ["merged", "replaced"]:
+
+ wantd = combine(haved, wantd, recursive=True)
+
+ # if state is deleted, delete and empty out wantd
+ if self.state == "deleted":
+ w = deepcopy(wantd)
+ if w == {} and haved != {}:
+ self.commands = ["delete vrf"]
+ return
+ for k, want in w.items():
+ if not (k in haved and haved[k]):
+ del wantd[k]
+ else:
+ if isinstance(want, list):
+ for entry in want:
+ wname = entry.get("name")
+ haved["instances"] = [
+ i for i in haved.get("instances", []) if i.get("name") != wname
+ ]
+ self.commands.append("delete vrf name {}".format(wname))
+ else:
+ self.commands.append("delete vrf {}".format(k.replace("_", "-")))
+ del wantd[k]
+
+ if self.state == "overridden":
+ w = deepcopy(wantd)
+ h = deepcopy(haved)
+ for k, want in w.items():
+ if k in haved and haved[k] != want:
+ if isinstance(want, list):
+ for entry in want:
+ wname = entry.get("name")
+ hdict = next(
+ (inst for inst in haved["instances"] if inst["name"] == wname),
+ None,
+ )
+ wantc = self._canonicalise(entry)
+ havec = self._canonicalise(hdict or {})
+
+ if wantc != havec:
+ haved["instances"] = [
+ i for i in haved.get("instances", []) if i.get("name") != wname
+ ]
+ self.commands.append("delete vrf name {}".format(wname))
+ self.commands.append("commit")
+
+ for k, want in wantd.items():
+ if isinstance(want, list):
+ self._compare_instances(want=want, have=haved.pop(k, {}))
+ self.compare(
+ parsers=self.parsers,
+ want={k: want},
+ have={k: haved.pop(k, {})},
+ )
+
+ def _compare_instances(self, want, have):
+ """Compare the instances of the VRF"""
+ parsers = [
+ "table_id",
+ "vni",
+ "description",
+ "disable_vrf",
+ ]
+
+ for entry in want:
+ h = {}
+ wname = entry.get("name")
+ h = {
+ k: v
+ for vrf in have
+ if vrf.get("name") == wname
+ for k, v in vrf.items()
+ if k != "address_family"
+ }
+ self.compare(parsers=parsers, want=entry, have=h)
+
+ if "address_family" in entry:
+ wafi = {"name": wname, "address_family": entry.get("address_family", [])}
+ hdict = next((d for d in have if d.get("name") == wname), None)
+
+ hafi = {
+ "name": (hdict or {"name": wname})["name"],
+ "address_family": hdict.get("address_family", []) if hdict else [],
+ }
+
+ self._compare_addr_family(wafi, hafi)
+
+ if "protocols" in entry:
+ for protocol_name in entry["protocols"]:
+
+ w_p_dict = entry["protocols"][protocol_name]
+
+ h_p_dict = next(
+ (
+ v.get("protocols", {}).get(protocol_name, {})
+ for v in have
+ if v.get("name") == wname
+ ),
+ {},
+ )
+ if protocol_name == "bgp":
+ bgp_module = Bgp_global(self._module)
+ bgp_module._validate_template()
+ bgp_module.want = w_p_dict
+ bgp_module.have = h_p_dict
+ bgp_module.generate_commands()
+ protocol_commands = bgp_module.commands
+ elif protocol_name == "ospf":
+ ospfv2_module = Ospfv2(self._module)
+ ospfv2_module._module.params["config"] = w_p_dict
+ ospfv2_module.state = self.state
+ protocol_commands = ospfv2_module.set_config(h_p_dict)
+ elif protocol_name == "ospfv3":
+ ospfv3_module = Ospfv3(self._module)
+ ospfv3_module._module.params["config"] = w_p_dict
+ ospfv3_module.state = self.state
+ protocol_commands = ospfv3_module.set_config(h_p_dict)
+ elif protocol_name == "static":
+ static_routes_module = Static_routes(self._module)
+ static_routes_module._module.params["config"] = w_p_dict
+ static_routes_module.state = self.state
+ protocol_commands = static_routes_module.set_config(h_p_dict)
+ else:
+ self._module.fail_json(
+ msg="The protocol {} is not supported".format(protocol_name),
+ )
+ self.commands.extend(
+ [
+ cmd.replace("protocols", "vrf name " + wname + " protocols", 1)
+ for cmd in protocol_commands
+ ],
+ )
+
+ def _compare_addr_family(self, want, have):
+ """Compare the address families of the VRF"""
+ afi_parsers = [
+ "disable_forwarding",
+ "disable_nht",
+ ]
+
+ wafi = self.afi_to_list(want)
+ hafi = self.afi_to_list(have)
+
+ lookup = {(d["name"], d["afi"]): d for d in hafi}
+ pairs = [(d1, lookup.get((d1["name"], d1["afi"]), {})) for d1 in wafi]
+
+ for wafd, hafd in pairs:
+ if "route_maps" in wafd:
+ self._compare_route_maps(wafd, hafd)
+ self.compare(parsers=afi_parsers, want=wafd, have=hafd)
+
+ def afi_to_list(self, data):
+ """Convert address family dict to list"""
+
+ return [
+ {"name": data["name"], **{**af, "afi": "ip" if af["afi"] == "ipv4" else af["afi"]}}
+ for af in data["address_family"]
+ ]
+
+ def _compare_route_maps(self, wafd, hafd):
+ want_rms = wafd.get("route_maps", [])
+ have_rms = hafd.get("route_maps", [])
+
+ for want in want_rms:
+ match = next(
+ (
+ h
+ for h in have_rms
+ if h["rm_name"] == want["rm_name"] and h["protocol"] == want["protocol"]
+ ),
+ {},
+ )
+ base = {"name": wafd["name"], "afi": wafd["afi"]}
+
+ self.compare(
+ parsers="route_maps",
+ want={**base, "route_maps": want},
+ have={**base, "route_maps": match},
+ )
+
+ def _canonicalise(self, obj):
+ if isinstance(obj, dict):
+ return {k: self._canonicalise(v) for k, v in obj.items()}
+
+ if isinstance(obj, list):
+ if not obj:
+ return obj
+
+ if not isinstance(obj[0], dict):
+ return sorted(obj)
+
+ canon = [self._canonicalise(i) for i in obj]
+
+ def sort_key(d):
+ for k in ("name", "address", "afi", "area_id", "neighbor_id", "dest"):
+ if k in d:
+ return d[k]
+ return tuple(sorted(d.items()))
+
+ return sorted(canon, key=sort_key)
+
+ return obj
diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py
index 74bbda74..24c444dd 100644
--- a/plugins/module_utils/network/vyos/facts/facts.py
+++ b/plugins/module_utils/network/vyos/facts/facts.py
@@ -80,6 +80,7 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.snmp_
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import (
Static_routesFacts,
)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.vrf.vrf import VrfFacts
FACT_LEGACY_SUBSETS = dict(default=Default, neighbors=Neighbors, config=Config)
@@ -104,6 +105,7 @@ FACT_RESOURCE_SUBSETS = dict(
ntp_global=Ntp_globalFacts,
snmp_server=Snmp_serverFacts,
hostname=HostnameFacts,
+ vrf=VrfFacts,
)
diff --git a/plugins/module_utils/network/vyos/facts/vrf/__init__.py b/plugins/module_utils/network/vyos/facts/vrf/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/vrf/__init__.py
diff --git a/plugins/module_utils/network/vyos/facts/vrf/vrf.py b/plugins/module_utils/network/vyos/facts/vrf/vrf.py
new file mode 100644
index 00000000..6c7b8918
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/vrf/vrf.py
@@ -0,0 +1,227 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+"""
+The vyos vrf 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.
+"""
+
+import re
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.vrf.vrf import VrfArgs
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_global.bgp_global import (
+ Bgp_globalFacts,
+)
+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.ospfv3.ospfv3 import (
+ Ospfv3Facts,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import (
+ Static_routesFacts,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.vrf import (
+ VrfTemplate,
+)
+
+
+class VrfFacts(object):
+ """The vyos vrf facts class"""
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = VrfArgs.argument_spec
+
+ def get_config(self, connection):
+ return connection.get("show configuration commands | match 'set vrf'")
+
+ def get_config_set(self, data, connection):
+ """To classify the configurations beased on vrf"""
+ config_dict = {}
+ for config_line in data.splitlines():
+ vrf_inst = re.search(r"set vrf name (\S+).*", config_line)
+ vrf_bta = re.search(r"set vrf bind-to-all", config_line)
+ if vrf_bta:
+ config_dict["bind_to_all"] = config_dict.get("bind_to_all", "") + config_line + "\n"
+ if vrf_inst:
+ config_dict[vrf_inst.group(1)] = (
+ config_dict.get(vrf_inst.group(1), "") + config_line + "\n"
+ )
+ return list(config_dict.values())
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """Populate the facts for Vrf network resource
+
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+
+ :rtype: dictionary
+ :returns: facts
+ """
+ facts = {}
+ objs = []
+
+ if not data:
+ data = self.get_config(connection)
+
+ vrf_facts = {}
+ instances = []
+ vrf_parser = VrfTemplate(lines=[], module=self._module)
+ resources = self.get_config_set(data, connection)
+
+ for resource in resources:
+ vrf_parser = VrfTemplate(
+ lines=resource.split("\n"),
+ module=self._module,
+ )
+ objs = vrf_parser.parse()
+
+ if objs and "protocols" in resource:
+
+ protocol_lines = []
+ for line in resource.strip().split("\n"):
+ if "protocols" in line:
+ idx = line.index("protocols")
+ protocol_lines.append("set " + line[idx:])
+ objs["protocols"] = self._parse_protocols("\n".join(protocol_lines))
+
+ if objs:
+ if "bind_to_all" in objs:
+ vrf_facts.update(objs)
+ if "name" in objs:
+ instances.append(self._normalise_instance(objs))
+
+ if instances:
+ vrf_facts.update({"instances": instances})
+
+ ansible_facts["ansible_network_resources"].pop("vrf_facts", None)
+ facts = {"vrf": []}
+
+ params = utils.remove_empties(
+ vrf_parser.validate_config(
+ self.argument_spec,
+ {"config": vrf_facts},
+ redact=True,
+ ),
+ )
+
+ if not resources:
+ params["config"].pop("bind_to_all", None)
+
+ if params.get("config"):
+ facts["vrf"] = params["config"]
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def _normalise_instance(self, instance):
+ n_inst = instance.copy()
+ af_map = {}
+
+ for af in instance.get("address_family", []):
+ afi = af.get("afi")
+ if not afi:
+ continue
+
+ if afi not in af_map:
+ af_map[afi] = {"afi": afi}
+
+ for k, v in af.items():
+ if k == "afi":
+ continue
+ elif k == "route_maps":
+ if "route_maps" not in af_map[afi]:
+ af_map[afi]["route_maps"] = []
+ af_map[afi]["route_maps"].extend(v)
+ else:
+ af_map[afi][k] = v
+
+ for afi_data in af_map.values():
+ if "route_maps" in afi_data:
+ seen = []
+ deduped = []
+ for item in afi_data["route_maps"]:
+ if item not in seen:
+ seen.append(item)
+ deduped.append(item)
+ afi_data["route_maps"] = deduped
+
+ n_inst["address_family"] = list(af_map.values())
+ return n_inst
+
+ def _parse_protocols(self, protocols):
+ """Parse protocols and return a dictionary"""
+
+ protocol_chunks = {}
+ parsed_protocols = {}
+
+ for line in protocols.split("\n"):
+ parts = line.split()
+ if len(parts) > 2 and parts[0] == "set" and parts[1] == "protocols":
+ protocol = parts[2]
+ protocol_chunks.setdefault(protocol, []).append(line)
+
+ protocol_strings = {proto: "\n".join(lines) for proto, lines in protocol_chunks.items()}
+
+ for protocol_name, protocol_string in protocol_strings.items():
+ protocol_dict = {}
+
+ if protocol_name == "bgp":
+ bgp_module = Bgp_globalFacts(self._module)
+ protocol_dict = bgp_module.populate_facts(
+ connection=self._module._connection,
+ ansible_facts={"ansible_network_resources": {}},
+ data=protocol_string,
+ )
+ parsed_protocols[protocol_name] = list(
+ protocol_dict.get("ansible_network_resources").values(),
+ )[0]
+
+ elif protocol_name == "ospf":
+ ospf_module = Ospfv2Facts(self._module)
+ protocol_dict = ospf_module.populate_facts(
+ connection=self._module._connection,
+ ansible_facts={"ansible_network_resources": {}},
+ data=protocol_string,
+ )
+ parsed_protocols[protocol_name] = list(
+ protocol_dict.get("ansible_network_resources").values(),
+ )[0]
+
+ elif protocol_name == "ospfv3":
+ ospfv3_module = Ospfv3Facts(self._module)
+ protocol_dict = ospfv3_module.populate_facts(
+ connection=self._module._connection,
+ ansible_facts={"ansible_network_resources": {}},
+ data=protocol_string,
+ )
+ parsed_protocols[protocol_name] = list(
+ protocol_dict.get("ansible_network_resources").values(),
+ )[0]
+
+ elif protocol_name == "static":
+ static_routes_module = Static_routesFacts(self._module)
+ protocol_dict = static_routes_module.populate_facts(
+ connection=self._module._connection,
+ ansible_facts={"ansible_network_resources": {}},
+ data=protocol_string,
+ )
+ parsed_protocols[protocol_name] = list(
+ protocol_dict.get("ansible_network_resources").values(),
+ )[0]
+ else:
+ self._module.fail_json(msg="The protocol is not supported" + protocol_name)
+
+ return parsed_protocols
diff --git a/plugins/module_utils/network/vyos/rm_templates/vrf.py b/plugins/module_utils/network/vyos/rm_templates/vrf.py
new file mode 100644
index 00000000..5171404e
--- /dev/null
+++ b/plugins/module_utils/network/vyos/rm_templates/vrf.py
@@ -0,0 +1,249 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+"""
+The VRF parser templates file. This contains
+a list of parser definitions and associated functions that
+facilitates both facts gathering and native command generation for
+the given network resource.
+"""
+
+import re
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import (
+ NetworkTemplate,
+)
+
+
+class VrfTemplate(NetworkTemplate):
+ def __init__(self, lines=None, module=None):
+ prefix = {"set": "set", "remove": "delete"}
+ super(VrfTemplate, self).__init__(lines=lines, tmplt=self, prefix=prefix, module=module)
+
+ # fmt: off
+ PARSERS = [
+ {
+ "name": "table_id",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+vrf
+ \s+name
+ \s+(?P<name>\S+)
+ \s+table
+ \s+'(?P<tid>\S+)'
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf name {{ name }} table {{ table_id }}",
+ "result": {
+ "name": "{{ name }}",
+ "table_id": "{{ tid }}",
+ },
+ },
+ {
+ "name": "bind_to_all",
+ "getval": re.compile(
+ r"""
+ ^set
+ \svrf
+ \s(?P<bta>bind-to-all)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf bind-to-all",
+ "result": {
+ "bind_to_all": "{{ True if bta is defined }}",
+ },
+ },
+ {
+ "name": "vni",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+vrf
+ \s+name
+ \s+(?P<name>\S+)
+ \s+vni
+ \s'(?P<vni>\S+)'
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf name {{name}} vni {{vni}}",
+ "result": {
+ "name": "{{ name }}",
+ "vni": "{{ vni }}",
+ },
+ },
+ {
+ "name": "description",
+ "getval": re.compile(
+ r"""
+ ^set
+ \svrf
+ \sname
+ \s(?P<name>\S+)
+ \sdescription
+ \s(?P<desc>\S+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf name {{name}} description {{description}}",
+ "result": {
+ "name": "{{ name }}",
+ "description": "{{ desc }}",
+ },
+ },
+ {
+ "name": "disable_vrf",
+ "getval": re.compile(
+ r"""
+ ^set
+ \svrf
+ \sname
+ \s(?P<name>\S+)
+ \s(?P<disable>disable)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf name {{name}} disable",
+ "compval": "disable",
+ "result": {
+ "name": "{{ name }}",
+ "disable": "{{ True if disable is defined }}",
+ },
+ },
+ # {
+ # "name": "address_family",
+ # "getval": re.compile(
+ # r"""
+ # ^set
+ # \svrf
+ # \sname
+ # \s(?P<name>\S+)
+ # \s(?P<af>ip|ipv6)
+ # $""",
+ # re.VERBOSE,
+ # ),
+ # "setval": "vrf name {{name}} {{ af }}",
+ # 'compval': "address_family",
+ # "result": {
+ # "name": "{{ name }}",
+ # "address_family": {
+ # '{{ "ipv4" if af == "ip" else "ipv6" }}': {
+ # "afi": '{{ "ipv4" if af == "ip" else "ipv6" }}',
+ # },
+ # },
+ # },
+ # },
+ # {
+ # "name": "address_family.disable_forwarding",
+ # "getval": re.compile(
+ # r"""
+ # ^set
+ # \svrf
+ # \sname
+ # \s(?P<name>\S+)
+ # \s(?P<af>ip|ipv6)
+ # \s(?P<df>disable-forwarding)
+ # $""",
+ # re.VERBOSE,
+ # ),
+ # "setval": "vrf name {{name}} {{ afi }} disable-forwarding",
+ # # "compval": "address_family.ipv6.disable_forwarding",
+ # "result": {
+ # "name": "{{ name }}",
+ # "address_family": {
+ # '{{ "ipv4" if af == "ip" else "ipv6" }}': {
+ # "afi": '{{ "ipv4" if af == "ip" else "ipv6" }}',
+ # "disable_forwarding": "{{ True if df is defined }}",
+ # },
+ # },
+ # },
+ # },
+ {
+ "name": "disable_forwarding",
+ "getval": re.compile(
+ r"""
+ ^set
+ \svrf
+ \sname
+ \s(?P<name>\S+)
+ \s(?P<af>ip|ipv6)
+ \s(?P<df>disable-forwarding)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf name {{name}} {{ afi }} disable-forwarding",
+ "compval": "disable_forwarding",
+ "result": {
+ "name": "{{ name }}",
+ 'address_family': [{
+ "afi": '{{ "ipv4" if af == "ip" else "ipv6" }}',
+ "disable_forwarding": "{{ True if df is defined }}",
+ }],
+ },
+ },
+ {
+ "name": "disable_nht",
+ "getval": re.compile(
+ r"""
+ ^set
+ \svrf
+ \sname
+ \s(?P<name>\S+)
+ \s(?P<af>ip|ipv6)
+ \snht
+ \s(?P<nht>no-resolve-via-default)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf name {{name}} {{ afi }} nht no-resolve-via-default",
+ "compval": "nht_no_resolve_via_default",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": [{
+ "afi": '{{ "ipv4" if af == "ip" else "ipv6" }}',
+ "nht_no_resolve_via_default": "{{ True if nht is defined }}",
+ }],
+ },
+ },
+ {
+ "name": "route_maps",
+ "getval": re.compile(
+ r"""
+ ^set
+ \svrf
+ \sname
+ \s(?P<name>\S+)
+ \s(?P<af>ip|ipv6)
+ \sprotocol
+ \s(?P<proto>\S+)
+ \sroute-map
+ \s'(?P<rm>\S+)'
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf name {{name}} {{ afi }} protocol {{ route_maps.protocol }} route-map {{ route_maps.rm_name }}",
+ "compval": "route_maps",
+ "remval": "vrf name {{name}} {{ afi }} protocol {{ route_maps.protocol }}",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": [{
+ "afi": '{{ "ipv4" if af == "ip" else "ipv6" }}',
+ "route_maps": [{
+ "rm_name": "{{ rm }}",
+ "protocol": "{{ proto }}",
+ }],
+ }],
+ },
+ },
+ ]
+ # fmt: on
diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py
index 6957f6cb..78445c02 100644
--- a/plugins/module_utils/network/vyos/utils/utils.py
+++ b/plugins/module_utils/network/vyos/utils/utils.py
@@ -285,3 +285,25 @@ def in_target_not_none(h, key):
:return: True/False.
"""
return True if h and key in h and h[key] is not None else False
+
+
+def combine(a, b, recursive=False, list_merge="replace"):
+ """
+ Merge two dictionaries (shallow or deep).
+ :param a: dict
+ :param b: dict
+ :param recursive: bool, deep merge
+ :param list_merge: str, only 'replace' is supported (default Ansible behavior)
+ """
+ if not isinstance(a, dict) or not isinstance(b, dict):
+ raise ValueError("combine expects two dictionaries")
+
+ result = a.copy()
+
+ for k, v in b.items():
+ if recursive and k in result and isinstance(result[k], dict) and isinstance(v, dict):
+ result[k] = combine(result[k], v, recursive=True, list_merge=list_merge)
+ else:
+ result[k] = v
+
+ return result
diff --git a/plugins/modules/vyos_vrf.py b/plugins/modules/vyos_vrf.py
new file mode 100644
index 00000000..395a152b
--- /dev/null
+++ b/plugins/modules/vyos_vrf.py
@@ -0,0 +1,1545 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2024 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+"""
+The module file for vyos_vrf
+"""
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+module: vyos_vrf
+version_added: 1.0.0
+short_description: VRF resource module
+description:
+- This module manages vrf configuration on devices running Vyos
+author:
+- Evgeny Molotkov (@omnom62)
+notes:
+- Tested against vyos 1.4.2 and 1.5-stream-2025-Q1
+- This module works with connection C(network_cli).
+options:
+ config:
+ description: List of vrf configuration.
+ type: dict
+ suboptions:
+ bind_to_all:
+ default: false
+ description: Enable binding services to all VRFs
+ type: bool
+ instances:
+ description: Virtual Routing and Forwarding instance
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description: VRF instance name
+ required: true
+ type: str
+ description:
+ description: Description
+ type: str
+ disable:
+ default: false
+ description: Administratively disable interface
+ type: bool
+ aliases: ['disabled']
+ table_id:
+ description: Routing table associated with this instance
+ type: int
+ vni:
+ description: Virtual Network Identifier
+ type: int
+ address_family:
+ type: list
+ elements: dict
+ description: Address family configuration
+ suboptions:
+ afi:
+ description: Address family identifier
+ type: str
+ choices: ['ipv4', 'ipv6']
+ disable_forwarding:
+ default: False
+ description: Disable forwarding for this address family
+ type: bool
+ nht_no_resolve_via_default:
+ default: False
+ description: Disable next-hop resolution via default route
+ type: bool
+ route_maps:
+ description: List of route maps for this address family
+ type: list
+ elements: dict
+ suboptions:
+ rm_name:
+ description: Route map name
+ type: str
+ required: true
+ protocol:
+ description: Protocol to which the route map applies
+ type: str
+ choices:
+ - any
+ - babel
+ - bgp
+ - eigrp
+ - isis
+ - ospf
+ - rip
+ - static
+ protocols:
+ # type: list # sanity
+ # elements: dict
+ type: dict
+ description: Protocol configuration
+ suboptions:
+ bgp:
+ type: dict
+ description: BGP configuration
+ suboptions:
+ as_number:
+ description:
+ - AS number.
+ type: int
+ #maximum_paths: --> moved to address-family before 1.3
+ neighbor:
+ description: BGP neighbor
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description:
+ - BGP neighbor address (v4/v6).
+ type: str
+ advertisement_interval:
+ description:
+ - Minimum interval for sending routing updates.
+ type: int
+ capability:
+ description:
+ - Advertise capabilities to this neighbor.
+ type: dict
+ suboptions:
+ dynamic:
+ description:
+ - Advertise dynamic capability to this neighbor.
+ type: bool
+ extended_nexthop:
+ description:
+ - Advertise extended nexthop capability to this neighbor.
+ type: bool
+ default_originate:
+ description:
+ - Send default route to this neighbor
+ type: str
+ description:
+ description:
+ - Description of the neighbor
+ type: str
+ disable_capability_negotiation:
+ description:
+ - Disbale capability negotiation with the neighbor
+ type: bool
+ disable_connected_check:
+ description:
+ - Disable check to see if EBGP peer's address is a connected route.
+ type: bool
+ disable_send_community:
+ description:
+ - Disable sending community attributes to this neighbor.
+ type: str
+ choices: ['extended', 'standard']
+ ebgp_multihop:
+ description:
+ - Allow this EBGP neighbor to not be on a directly connected network. Specify
+ the number hops.
+ type: int
+ local_as:
+ description: local as number not to be prepended to updates from EBGP peers
+ type: int
+ override_capability:
+ description: Ignore capability negotiation with specified neighbor.
+ type: bool
+ passive:
+ description: Do not initiate a session with this neighbor
+ type: bool
+ password:
+ description: BGP MD5 password
+ type: str
+ peer_group_name:
+ description: IPv4 peer group for this peer
+ type: str
+ peer_group:
+ description: True if all the configs under this neighbor key is for peer group template.
+ type: bool
+ port:
+ description: Neighbor's BGP port
+ type: int
+ remote_as:
+ description: Neighbor BGP AS number
+ type: int
+ shutdown:
+ description: Administratively shut down neighbor
+ type: bool
+ solo: # <-- added in 1.3
+ description: Do not send back prefixes learned from the neighbor
+ type: bool
+ strict_capability_match:
+ description: Enable strict capability negotiation
+ type: bool
+ timers:
+ description: Neighbor timers
+ type: dict
+ suboptions:
+ connect:
+ description: BGP connect timer for this neighbor.
+ type: int
+ holdtime:
+ description: BGP hold timer for this neighbor
+ type: int
+ keepalive:
+ description: BGP keepalive interval for this neighbor
+ type: int
+ ttl_security:
+ description: Number of the maximum number of hops to the BGP peer
+ type: int
+ update_source:
+ description: Source IP of routing updates
+ type: str
+ timers:
+ description: BGP protocol timers
+ type: dict
+ suboptions:
+ keepalive:
+ description: Keepalive interval
+ type: int
+ holdtime:
+ description: Hold time interval
+ type: int
+ bgp_params:
+ description: BGP parameters
+ type: dict
+ suboptions:
+ always_compare_med:
+ description: Always compare MEDs from different neighbors
+ type: bool
+ bestpath:
+ description: Default bestpath selection mechanism
+ type: dict
+ suboptions:
+ as_path:
+ description: AS-path attribute comparison parameters
+ type: str
+ choices: ['confed', 'ignore']
+ compare_routerid:
+ description: Compare the router-id for identical EBGP paths
+ type: bool
+ med:
+ description: MED attribute comparison parameters
+ type: str
+ choices: ['confed', 'missing-as-worst']
+ cluster_id:
+ description: Route-reflector cluster-id
+ type: str
+ confederation:
+ description: AS confederation parameters
+ type: list
+ elements: dict
+ suboptions:
+ identifier:
+ description: Confederation AS identifier
+ type: int
+ peers:
+ description: Peer ASs in the BGP confederation
+ type: int
+ dampening:
+ description: Enable route-flap dampening
+ type: dict
+ suboptions:
+ half_life:
+ description: Half-life penalty in seconds
+ type: int
+ max_suppress_time:
+ description: Maximum duration to suppress a stable route
+ type: int
+ re_use:
+ description: Time to start reusing a route
+ type: int
+ start_suppress_time:
+ description: When to start suppressing a route
+ type: int
+ default:
+ description: BGP defaults
+ type: dict
+ suboptions:
+ local_pref:
+ description: Default local preference
+ type: int
+ no_ipv4_unicast:
+ description: |
+ Deactivate IPv4 unicast for a peer by default
+ Deprecated: Unavailable after 1.4
+ type: bool
+ deterministic_med:
+ description: Compare MEDs between different peers in the same AS
+ type: bool
+ disable_network_import_check:
+ description: Disable IGP route check for network statements
+ type: bool
+ distance:
+ description: Administrative distances for BGP routes
+ type: list
+ elements: dict
+ suboptions:
+ type:
+ description: Type of route
+ type: str
+ choices: ['external', 'internal', 'local']
+ value:
+ description: distance
+ type: int
+ prefix:
+ description: Administrative distance for a specific BGP prefix
+ type: int
+ enforce_first_as:
+ description: Require first AS in the path to match peer's AS
+ type: bool
+ graceful_restart:
+ description: Maximum time to hold onto restarting peer's stale paths
+ type: int
+ log_neighbor_changes:
+ description: Log neighbor up/down changes and reset reason
+ type: bool
+ no_client_to_client_reflection:
+ description: Disable client to client route reflection
+ type: bool
+ no_fast_external_failover:
+ description: Disable immediate session reset if peer's connected link goes down
+ type: bool
+ router_id:
+ description: BGP router-id
+ type: str
+ scan_time:
+ description: BGP route scanner interval
+ type: int
+ ospf:
+ type: dict
+ description: OSPFv2 configuration
+ suboptions:
+ areas:
+ description: OSPFv2 area.
+ type: list
+ elements: dict
+ suboptions:
+ area_id:
+ description: OSPFv2 area identity.
+ type: str
+ area_type:
+ description: Area type.
+ type: dict
+ suboptions:
+ normal:
+ description: Normal OSPFv2 area.
+ type: bool
+ nssa:
+ description: NSSA OSPFv2 area.
+ type: dict
+ suboptions:
+ set:
+ description: Enabling NSSA.
+ type: bool
+ default_cost:
+ description: Summary-default cost of NSSA area.
+ type: int
+ no_summary:
+ description: Do not inject inter-area routes into stub.
+ type: bool
+ translate:
+ description: NSSA-ABR.
+ type: str
+ choices: [always, candidate, never]
+ stub:
+ description: Stub OSPFv2 area.
+ type: dict
+ suboptions:
+ set:
+ description: Enabling stub.
+ type: bool
+ default_cost:
+ description: Summary-default cost of stub area.
+ type: int
+ no_summary:
+ description: Do not inject inter-area routes into stub.
+ type: bool
+ authentication:
+ description: OSPFv2 area authentication type.
+ type: str
+ choices: [plaintext-password, md5]
+ network:
+ description: OSPFv2 network.
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ required: true
+ description: OSPFv2 IPv4 network address.
+ type: str
+ range:
+ description: Summarize routes matching prefix (border routers only).
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description: border router IPv4 address.
+ type: str
+ cost:
+ description: Metric for this range.
+ type: int
+ not_advertise:
+ description: Don't advertise this range.
+ type: bool
+ substitute:
+ description: Announce area range (IPv4 address) as another prefix.
+ type: str
+ shortcut:
+ description: Area's shortcut mode.
+ type: str
+ choices: [default, disable, enable]
+ virtual_link:
+ description: Virtual link address.
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description: virtual link address.
+ type: str
+ authentication:
+ description: OSPFv2 area authentication type.
+ type: dict
+ suboptions:
+ md5:
+ description: MD5 key id based authentication.
+ type: list
+ elements: dict
+ suboptions:
+ key_id:
+ description: MD5 key id.
+ type: int
+ md5_key:
+ description: MD5 key.
+ type: str
+ plaintext_password:
+ description: Plain text password.
+ type: str
+ dead_interval:
+ description: Interval after which a neighbor is declared dead.
+ type: int
+ hello_interval:
+ description: Interval between hello packets.
+ type: int
+ retransmit_interval:
+ description: Interval between retransmitting lost link state advertisements.
+ type: int
+ transmit_delay:
+ description: Link state transmit delay.
+ type: int
+ log_adjacency_changes:
+ description: Log changes in adjacency state.
+ type: str
+ choices: [detail]
+ max_metric:
+ description: OSPFv2 maximum/infinite-distance metric.
+ type: dict
+ suboptions:
+ router_lsa:
+ description: Advertise own Router-LSA with infinite distance (stub router).
+ type: dict
+ suboptions:
+ administrative:
+ description: Administratively apply, for an indefinite period.
+ type: bool
+ on_shutdown:
+ description: Time to advertise self as stub-router.
+ type: int
+ on_startup:
+ description: Time to advertise self as stub-router
+ type: int
+ auto_cost:
+ description: Calculate OSPFv2 interface cost according to bandwidth.
+ type: dict
+ suboptions:
+ reference_bandwidth:
+ description: Reference bandwidth cost in Mbits/sec.
+ type: int
+ default_information:
+ description: Control distribution of default information.
+ type: dict
+ suboptions:
+ originate:
+ description: Distribute a default route.
+ type: dict
+ suboptions:
+ always:
+ description: Always advertise default route.
+ type: bool
+ metric:
+ description: OSPFv2 default metric.
+ type: int
+ metric_type:
+ description: OSPFv2 Metric types for default routes.
+ type: int
+ route_map:
+ description: Route map references.
+ type: str
+ default_metric:
+ description: Metric of redistributed routes
+ type: int
+ distance:
+ description: Administrative distance.
+ type: dict
+ suboptions:
+ global:
+ description: Global OSPFv2 administrative distance.
+ type: int
+ ospf:
+ description: OSPFv2 administrative distance.
+ type: dict
+ suboptions:
+ external:
+ description: Distance for external routes.
+ type: int
+ inter_area:
+ description: Distance for inter-area routes.
+ type: int
+ intra_area:
+ description: Distance for intra-area routes.
+ type: int
+ mpls_te:
+ description: MultiProtocol Label Switching-Traffic Engineering (MPLS-TE) parameters.
+ type: dict
+ suboptions:
+ enabled:
+ description: Enable MPLS-TE functionality.
+ type: bool
+ router_address:
+ description: Stable IP address of the advertising router.
+ type: str
+ neighbor:
+ description: Neighbor IP address.
+ type: list
+ elements: dict
+ suboptions:
+ neighbor_id:
+ description: Identity (number/IP address) of neighbor.
+ type: str
+ poll_interval:
+ description: Seconds between dead neighbor polling interval.
+ type: int
+ priority:
+ description: Neighbor priority.
+ type: int
+ parameters:
+ description: OSPFv2 specific parameters.
+ type: dict
+ suboptions:
+ abr_type:
+ description: OSPFv2 ABR Type.
+ type: str
+ choices: [cisco, ibm, shortcut, standard]
+ opaque_lsa:
+ description: Enable the Opaque-LSA capability (rfc2370).
+ type: bool
+ rfc1583_compatibility:
+ description: Enable rfc1583 criteria for handling AS external routes.
+ type: bool
+ router_id:
+ description: Override the default router identifier.
+ type: str
+ passive_interface:
+ description: Suppress routing updates on an interface.
+ type: list
+ elements: str
+ passive_interface_exclude:
+ description: Interface to exclude when using passive-interface default.
+ type: list
+ elements: str
+ redistribute:
+ description: Redistribute information from another routing protocol.
+ type: list
+ elements: dict
+ suboptions:
+ route_type:
+ description: Route type to redistribute.
+ type: str
+ choices: [bgp, connected, kernel, rip, static]
+ metric:
+ description: Metric for redistribution routes.
+ type: int
+ metric_type:
+ description: OSPFv2 Metric types.
+ type: int
+ route_map:
+ description: Route map references.
+ type: str
+ route_map:
+ description: Filter routes installed in local route map.
+ type: list
+ elements: str
+ timers:
+ description: Adjust routing timers.
+ type: dict
+ suboptions:
+ refresh:
+ description: Adjust refresh parameters.
+ type: dict
+ suboptions:
+ timers:
+ description: refresh timer.
+ type: int
+ throttle:
+ description: Throttling adaptive timers.
+ type: dict
+ suboptions:
+ spf:
+ description: OSPFv2 SPF timers.
+ type: dict
+ suboptions:
+ delay:
+ description: Delay (msec) from first change received till SPF
+ calculation.
+ type: int
+ initial_holdtime:
+ description: Initial hold time(msec) between consecutive SPF calculations.
+ type: int
+ max_holdtime:
+ description: maximum hold time (sec).
+ type: int
+ ospfv3:
+ type: dict
+ description: OSPFv3 configuration
+ suboptions:
+ areas:
+ description: OSPFv3 area.
+ type: list
+ elements: dict
+ suboptions:
+ area_id:
+ description: OSPFv3 Area name/identity.
+ type: str
+ export_list:
+ description: Name of export-list.
+ type: str
+ import_list:
+ description: Name of import-list.
+ type: str
+ interface:
+ description: Enable OSPVv3 on an interface for this area.
+ aliases: ['interfaces']
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description: Interface name.
+ type: str
+ range:
+ description: Summarize routes matching prefix (border routers only).
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description: border router IPv4 address.
+ type: str
+ advertise:
+ description: Advertise this range.
+ type: bool
+ not_advertise:
+ description: Don't advertise this range.
+ type: bool
+ parameters:
+ description: OSPFv3 specific parameters.
+ type: dict
+ suboptions:
+ router_id:
+ description: Override the default router identifier.
+ type: str
+ redistribute:
+ description: Redistribute information from another routing protocol.
+ type: list
+ elements: dict
+ suboptions:
+ route_type:
+ description: Route type to redistribute.
+ type: str
+ choices:
+ - bgp
+ - connected
+ - kernel
+ - ripng
+ - static
+ route_map:
+ description: Route map references.
+ type: str
+ static:
+ type: list
+ description: Static routes configuration
+ elements: dict
+ suboptions:
+ address_families:
+ description: A dictionary specifying the address family to which the static
+ route(s) belong.
+ type: list
+ elements: dict
+ suboptions:
+ afi:
+ description:
+ - Specifies the type of route.
+ type: str
+ choices:
+ - ipv4
+ - ipv6
+ required: true
+ routes:
+ description: A dictionary that specify the static route configurations.
+ type: list
+ elements: dict
+ suboptions:
+ dest:
+ description:
+ - An IPv4/v6 address in CIDR notation that specifies the destination
+ network for the static route.
+ type: str
+ required: true
+ blackhole_config:
+ description:
+ - Configured to silently discard packets.
+ type: dict
+ suboptions:
+ type:
+ description:
+ - This is to configure only blackhole.
+ type: str
+ distance:
+ description:
+ - Distance for the route.
+ type: int
+ next_hops:
+ description:
+ - Next hops to the specified destination.
+ type: list
+ elements: dict
+ suboptions:
+ forward_router_address:
+ description:
+ - The IP address of the next hop that can be used to reach the
+ destination network.
+ type: str
+ enabled:
+ description:
+ - Disable IPv4/v6 next-hop static route.
+ type: bool
+ admin_distance:
+ description:
+ - Distance value for the route.
+ type: int
+ interface:
+ description:
+ - Name of the outgoing interface.
+ type: str
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the VYOS device by
+ executing the command B(show configuration commands | match "set vrf").
+ - The states I(replaced) and I(overridden) have identical
+ behaviour for this module.
+ - The state I(parsed) reads the configuration from C(show configuration commands | match "set vrf") option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ state:
+ description:
+ - The state the configuration should be left in.
+ type: str
+ choices:
+ - deleted
+ - merged
+ - overridden
+ - replaced
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+"""
+
+EXAMPLES = """
+# # -------------------
+# # 1. Using merged
+# # -------------------
+
+# # Before state:
+# # -------------
+# vyos@vyos:~$ show configuration commands | match 'set vrf'
+# set vrf name vrf-blue description 'blue-vrf'
+# set vrf name vrf-blue disable
+# set vrf name vrf-blue table '100'
+# set vrf name vrf-blue vni '1000'
+# vyos@vyos:~$
+
+# # Task
+# # -------------
+ # - name: Merge provided configuration with device configuration
+ # vyos.vyos.vyos_vrf:
+ # config:
+ # instances:
+ # - name: "vrf-green"
+ # description: "green-vrf"
+ # table_id: 110
+ # vni: 1010
+
+# Task output:
+# -------------
+ # "after": {
+ # "bind_to_all": false,
+ # "instances": [
+ # {
+ # "description": "blue-vrf",
+ # "disable": true,
+ # "name": "vrf-blue",
+ # "table_id": 100,
+ # "vni": 1000
+ # },
+ # {
+ # "description": "green-vrf",
+ # "disable": false,
+ # "name": "vrf-green",
+ # "table_id": 110,
+ # "vni": 1010
+ # }
+ # ]
+ # },
+ # "before": {
+ # "bind_to_all": false,
+ # "instances": [
+ # {
+ # "description": "blue-vrf",
+ # "disable": true,
+ # "name": "vrf-blue",
+ # "table_id": 100,
+ # "vni": 1000
+ # }
+ # ]
+ # },
+ # "changed": true,
+ # "commands": [
+ # "set vrf name vrf-green table 110",
+ # "set vrf name vrf-green vni 1010",
+ # "set vrf name vrf-green description green-vrf"
+ # ]
+
+# After state:
+# # -------------
+# vyos@vyos:~$ show configuration commands | match 'set vrf'
+# set vrf name vrf-blue description 'blue-vrf'
+# set vrf name vrf-blue disable
+# set vrf name vrf-blue table '100'
+# set vrf name vrf-blue vni '1000'
+# set vrf name vrf-green description 'green-vrf'
+# set vrf name vrf-green table '110'
+# set vrf name vrf-green vni '1010'
+# vyos@vyos:~$
+
+# # -------------------
+# # 2. Using replaced
+# # -------------------
+
+# # Before state:
+# # -------------
+ # vyos@vyos:~$ show configuration commands | match 'set vrf'
+ # set vrf bind-to-all
+ # set vrf name vrf-blue description 'blue-vrf'
+ # set vrf name vrf-blue table '100'
+ # set vrf name vrf-blue vni '1000'
+ # set vrf name vrf-red description 'red-vrf'
+ # set vrf name vrf-red disable
+ # set vrf name vrf-red ip disable-forwarding
+ # set vrf name vrf-red ip protocol rip route-map 'rm1'
+ # set vrf name vrf-red table '101'
+ # set vrf name vrf-red vni '1001'
+ # vyos@vyos:~$
+
+
+# # Task
+# # -------------
+ # - name: Merge provided configuration with device configuration
+ # vyos.vyos.vyos_vrf:
+ # config:
+ # bind_to_all: true
+ # instances:
+ # - name: "vrf-blue"
+ # description: "blue-vrf"
+ # disable: false
+ # table_id: 100
+ # vni: 1002
+ # - name: "vrf-red"
+ # description: "red-vrf"
+ # disable: false
+ # table_id: 101
+ # vni: 1001
+ # address_family:
+ # - afi: "ipv4"
+ # disable_forwarding: false
+ # route_maps:
+ # - rm_name: "rm1"
+ # protocol: "ospf"
+ # - afi: "ipv6"
+ # nht_no_resolve_via_default: true
+ # state: replaced
+
+# # Task output:
+# # -------------
+ # "after": {
+ # "bind_to_all": true,
+ # "instances": [
+ # {
+ # "description": "blue-vrf",
+ # "disable": false,
+ # "name": "vrf-blue",
+ # "table_id": 100,
+ # "vni": 1002
+ # },
+ # {
+ # "address_family": [
+ # {
+ # "afi": "ipv4",
+ # "disable_forwarding": false,
+ # "nht_no_resolve_via_default": false,
+ # "route_maps": [
+ # {
+ # "protocol": "ospf",
+ # "rm_name": "rm1"
+ # },
+ # {
+ # "protocol": "rip",
+ # "rm_name": "rm1"
+ # }
+ # ]
+ # },
+ # {
+ # "afi": "ipv6",
+ # "disable_forwarding": false,
+ # "nht_no_resolve_via_default": true
+ # }
+ # ],
+ # "description": "red-vrf",
+ # "disable": false,
+ # "name": "vrf-red",
+ # "table_id": 101,
+ # "vni": 1001
+ # }
+ # ]
+ # },
+ # "before": {
+ # "bind_to_all": true,
+ # "instances": [
+ # {
+ # "description": "blue-vrf",
+ # "disable": false,
+ # "name": "vrf-blue",
+ # "table_id": 100,
+ # "vni": 1000
+ # },
+ # {
+ # "address_family": [
+ # {
+ # "afi": "ipv4",
+ # "disable_forwarding": true,
+ # "nht_no_resolve_via_default": false,
+ # "route_maps": [
+ # {
+ # "protocol": "rip",
+ # "rm_name": "rm1"
+ # }
+ # ]
+ # }
+ # ],
+ # "description": "red-vrf",
+ # "disable": true,
+ # "name": "vrf-red",
+ # "table_id": 101,
+ # "vni": 1001
+ # }
+ # ]
+ # },
+ # "changed": true,
+ # "commands": [
+ # "set vrf name vrf-blue vni 1002",
+ # "delete vrf name vrf-red disable",
+ # "set vrf name vrf-red ip protocol ospf route-map rm1",
+ # "delete vrf name vrf-red ip disable-forwarding",
+ # "set vrf name vrf-red ipv6 nht no-resolve-via-default"
+ # ]
+
+# After state:
+# # -------------
+ # vyos@vyos:~$
+ # set vrf bind-to-all
+ # set vrf name vrf-blue description 'blue-vrf'
+ # set vrf name vrf-blue table '100'
+ # set vrf name vrf-blue vni '1002'
+ # set vrf name vrf-red description 'red-vrf'
+ # set vrf name vrf-red ip protocol ospf route-map 'rm1'
+ # set vrf name vrf-red ip protocol rip route-map 'rm1'
+ # set vrf name vrf-red ipv6 nht no-resolve-via-default
+ # set vrf name vrf-red table '101'
+ # set vrf name vrf-red vni '1001'
+ # vyos@vyos:~$
+
+
+# # -------------------
+# # 3. Using overridden
+# # -------------------
+
+# # Before state:
+# # -------------
+ # vyos@vyos:~$ show configuration commands | match 'set vrf'
+ # set vrf bind-to-all
+ # set vrf name vrf-blue description 'blue-vrf'
+ # set vrf name vrf-blue table '100'
+ # set vrf name vrf-blue vni '1000'
+ # set vrf name vrf-red description 'red-vrf'
+ # set vrf name vrf-red disable
+ # set vrf name vrf-red ip disable-forwarding
+ # set vrf name vrf-red ip protocol rip route-map 'rm1'
+ # set vrf name vrf-red table '101'
+ # set vrf name vrf-red vni '1001'
+ # vyos@vyos:~$
+
+# Task
+# -------------
+ # - name: Overridden provided configuration with device configuration
+ # vyos.vyos.vyos_vrf:
+ # config:
+ # bind_to_all: true
+ # instances:
+ # - name: "vrf-blue"
+ # description: "blue-vrf"
+ # disable: true
+ # table_id: 100
+ # vni: 1000
+ # - name: "vrf-red"
+ # description: "red-vrf"
+ # disable: true
+ # table_id: 101
+ # vni: 1001
+ # address_family:
+ # - afi: "ipv4"
+ # disable_forwarding: false
+ # route_maps:
+ # - rm_name: "rm1"
+ # protocol: "rip"
+ # - afi: "ipv6"
+ # nht_no_resolve_via_default: false
+ # state: overridden
+
+# # Task output:
+# # -------------
+ # "after": {
+ # "bind_to_all": true,
+ # "instances": [
+ # {
+ # "description": "blue-vrf",
+ # "disable": true,
+ # "name": "vrf-blue",
+ # "table_id": 100,
+ # "vni": 1000
+ # },
+ # {
+ # "address_family": [
+ # {
+ # "afi": "ipv4",
+ # "disable_forwarding": false,
+ # "nht_no_resolve_via_default": false,
+ # "route_maps": [
+ # {
+ # "protocol": "rip",
+ # "rm_name": "rm1"
+ # }
+ # ]
+ # }
+ # ],
+ # "description": "red-vrf",
+ # "disable": true,
+ # "name": "vrf-red",
+ # "table_id": 101,
+ # "vni": 1001
+ # }
+ # ]
+ # },
+ # "before": {
+ # "bind_to_all": true,
+ # "instances": [
+ # {
+ # "description": "blue-vrf",
+ # "disable": false,
+ # "name": "vrf-blue",
+ # "table_id": 100,
+ # "vni": 1000
+ # },
+ # {
+ # "address_family": [
+ # {
+ # "afi": "ipv4",
+ # "disable_forwarding": true,
+ # "nht_no_resolve_via_default": false,
+ # "route_maps": [
+ # {
+ # "protocol": "rip",
+ # "rm_name": "rm1"
+ # }
+ # ]
+ # }
+ # ],
+ # "description": "red-vrf",
+ # "disable": true,
+ # "name": "vrf-red",
+ # "table_id": 101,
+ # "vni": 1001
+ # }
+ # ]
+ # },
+ # "changed": true,
+ # "commands": [
+ # "delete vrf name vrf-blue",
+ # "commit",
+ # "delete vrf name vrf-red",
+ # "commit",
+ # "set vrf name vrf-blue table 100",
+ # "set vrf name vrf-blue vni 1000",
+ # "set vrf name vrf-blue description blue-vrf",
+ # "set vrf name vrf-blue disable",
+ # "set vrf name vrf-red table 101",
+ # "set vrf name vrf-red vni 1001",
+ # "set vrf name vrf-red description red-vrf",
+ # "set vrf name vrf-red disable",
+ # "set vrf name vrf-red ip protocol rip route-map rm1"
+ # ]
+
+# After state:
+# # -------------
+ # vyos@vyos:~$ show configuration commands | match 'set vrf'
+ # set vrf bind-to-all
+ # set vrf name vrf-blue description 'blue-vrf'
+ # set vrf name vrf-blue disable
+ # set vrf name vrf-blue table '100'
+ # set vrf name vrf-blue vni '1000'
+ # set vrf name vrf-red description 'red-vrf'
+ # set vrf name vrf-red disable
+ # set vrf name vrf-red ip protocol rip route-map 'rm1'
+ # set vrf name vrf-red table '101'
+ # set vrf name vrf-red vni '1001'
+ # vyos@vyos:~$
+
+# 4. Using gathered
+# -------------------
+
+# # Before state:
+# # -------------
+ # vyos@vyos:~$ show configuration commands | match 'set vrf'
+ # set vrf bind-to-all
+ # set vrf name vrf-blue description 'blue-vrf'
+ # set vrf name vrf-blue table '100'
+ # set vrf name vrf-blue vni '1000'
+ # set vrf name vrf-red description 'red-vrf'
+ # set vrf name vrf-red disable
+ # set vrf name vrf-red ip disable-forwarding
+ # set vrf name vrf-red ip protocol rip route-map 'rm1'
+ # set vrf name vrf-red table '101'
+ # set vrf name vrf-red vni '1001'
+ # vyos@vyos:~$
+
+# Task
+# -------------
+# - name: Gather provided configuration with device configuration
+# vyos.vyos.vyos_vrf:
+# config:
+# state: gathered
+
+# # Task output:
+# # -------------
+ # "gathered": {
+ # "bind_to_all": true,
+ # "instances": [
+ # {
+ # "description": "blue-vrf",
+ # "disable": false,
+ # "name": "vrf-blue",
+ # "table_id": 100,
+ # "vni": 1000
+ # },
+ # {
+ # "address_family": [
+ # {
+ # "afi": "ipv4",
+ # "disable_forwarding": true,
+ # "nht_no_resolve_via_default": false,
+ # "route_maps": [
+ # {
+ # "protocol": "rip",
+ # "rm_name": "rm1"
+ # }
+ # ]
+ # }
+ # ],
+ # "description": "red-vrf",
+ # "disable": true,
+ # "name": "vrf-red",
+ # "table_id": 101,
+ # "vni": 1001
+ # }
+ # ]
+ # }
+
+# After state:
+# # -------------
+ # vyos@vyos:~$ show configuration commands | match 'set vrf'
+ # set vrf bind-to-all
+ # set vrf name vrf-blue description 'blue-vrf'
+ # set vrf name vrf-blue table '100'
+ # set vrf name vrf-blue vni '1000'
+ # set vrf name vrf-red description 'red-vrf'
+ # set vrf name vrf-red disable
+ # set vrf name vrf-red ip disable-forwarding
+ # set vrf name vrf-red ip protocol rip route-map 'rm1'
+ # set vrf name vrf-red table '101'
+ # set vrf name vrf-red vni '1001'
+ # vyos@vyos:~$
+
+
+# # -------------------
+# # 5. Using deleted
+# # -------------------
+
+# # Before state:
+# # -------------
+ # vyos@vyos:~$ show configuration commands | match 'set vrf'
+ # set vrf bind-to-all
+ # set vrf name vrf-blue description 'blue-vrf'
+ # set vrf name vrf-blue table '100'
+ # set vrf name vrf-blue vni '1000'
+ # set vrf name vrf-red description 'red-vrf'
+ # set vrf name vrf-red disable
+ # set vrf name vrf-red ip disable-forwarding
+ # set vrf name vrf-red ip protocol rip route-map 'rm1'
+ # set vrf name vrf-red table '101'
+ # set vrf name vrf-red vni '1001'
+ # vyos@vyos:~$
+
+# # Task
+# # -------------
+# - name: Replace provided configuration with device configuration
+# vyos.vyos.vyos_vrf:
+# config:
+# bind_to_all: false
+# instances:
+# - name: "vrf-blue"
+# state: deleted
+
+
+# # Task output:
+# # -------------
+ # "after": {
+ # "bind_to_all": false,
+ # "instances": [
+ # {
+ # "address_family": [
+ # {
+ # "afi": "ipv4",
+ # "disable_forwarding": true,
+ # "nht_no_resolve_via_default": false,
+ # "route_maps": [
+ # {
+ # "protocol": "rip",
+ # "rm_name": "rm1"
+ # }
+ # ]
+ # }
+ # ],
+ # "description": "red-vrf",
+ # "disable": true,
+ # "name": "vrf-red",
+ # "table_id": 101,
+ # "vni": 1001
+ # }
+ # ]
+ # },
+ # "before": {
+ # "bind_to_all": true,
+ # "instances": [
+ # {
+ # "description": "blue-vrf",
+ # "disable": false,
+ # "name": "vrf-blue",
+ # "table_id": 100,
+ # "vni": 1000
+ # },
+ # {
+ # "address_family": [
+ # {
+ # "afi": "ipv4",
+ # "disable_forwarding": true,
+ # "nht_no_resolve_via_default": false,
+ # "route_maps": [
+ # {
+ # "protocol": "rip",
+ # "rm_name": "rm1"
+ # }
+ # ]
+ # }
+ # ],
+ # "description": "red-vrf",
+ # "disable": true,
+ # "name": "vrf-red",
+ # "table_id": 101,
+ # "vni": 1001
+ # }
+ # ]
+ # },
+ # "changed": true,
+ # "commands": [
+ # "delete vrf bind-to-all",
+ # "delete vrf name vrf-blue"
+ # ]
+
+# After state:
+# # -------------
+ # vyos@vyos:~$ show configuration commands | match 'set vrf'
+ # set vrf name vrf-red description 'red-vrf'
+ # set vrf name vrf-red disable
+ # set vrf name vrf-red ip disable-forwarding
+ # set vrf name vrf-red ip protocol rip route-map 'rm1'
+ # set vrf name vrf-red table '101'
+ # set vrf name vrf-red vni '1001'
+ # vyos@vyos:~$
+
+# # -------------------
+# # 6. Using rendered
+# # -------------------
+
+# # Before state:
+# # -------------
+ # vyos@vyos:~$ show configuration commands | match 'set vrf'
+ # set vrf name vrf-red description 'red-vrf'
+ # set vrf name vrf-red disable
+ # set vrf name vrf-red ip disable-forwarding
+ # set vrf name vrf-red ip protocol rip route-map 'rm1'
+ # set vrf name vrf-red table '101'
+ # set vrf name vrf-red vni '1001'
+ # vyos@vyos:~$
+
+# Task
+# -------------
+ # - name: Render provided configuration with device configuration
+ # vyos.vyos.vyos_vrf:
+ # config:
+ # bind_to_all: true
+ # instances:
+ # - name: "vrf-green"
+ # description: "green-vrf"
+ # disabled: true
+ # table_id: 105
+ # vni: 1000
+ # - name: "vrf-amber"
+ # description: "amber-vrf"
+ # disable: false
+ # table_id: 111
+ # vni: 1001
+ # address_family:
+ # - afi: "ipv4"
+ # disable_forwarding: true
+ # route_maps:
+ # - rm_name: "rm1"
+ # protocol: "ospf"
+ # - afi: "ipv6"
+ # nht_no_resolve_via_default: false
+ # state: rendered
+
+# # Task output:
+# # -------------
+ # "rendered": [
+ # "set vrf bind-to-all",
+ # "set vrf name vrf-green table 105",
+ # "set vrf name vrf-green vni 1000",
+ # "set vrf name vrf-green description green-vrf",
+ # "set vrf name vrf-green disable",
+ # "set vrf name vrf-amber table 111",
+ # "set vrf name vrf-amber vni 1001",
+ # "set vrf name vrf-amber description amber-vrf",
+ # "set vrf name vrf-amber ip protocol ospf route-map rm1",
+ # "set vrf name vrf-amber ip disable-forwarding"
+ # ]
+
+# # -------------------
+# # 7. Using parsed
+# # -------------------
+
+# # vrf_parsed.cfg:
+# # -------------
+# set vrf bind-to-all
+# set vrf name vrf1 description 'red'
+# set vrf name vrf1 disable
+# set vrf name vrf1 table 101
+# set vrf name vrf1 vni 501
+# set vrf name vrf2 description 'blah2'
+# set vrf name vrf2 disable
+# set vrf name vrf2 table 102
+# set vrf name vrf2 vni 102
+# set vrf name vrf1 ip disable-forwarding
+# set vrf name vrf1 ip nht no-resolve-via-default
+# set vrf name vrf-red ip protocol ospf route-map 'rm1'
+# set vrf name vrf-red ipv6 nht no-resolve-via-default
+
+# Task:
+# -------------
+# - name: Parse provided configuration with device configuration
+# vyos.vyos.vyos_vrf:
+# running_config: "{{ lookup('file', './vrf_parsed.cfg') }}"
+# state: parsed
+
+
+# # Task output:
+# # -------------
+# "parsed": {
+# "bind_to_all": true,
+# "instances": [
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "disable_forwarding": true,
+# "nht_no_resolve_via_default": true
+# }
+# ],
+# "description": "red",
+# "disable": true,
+# "name": "vrf1"
+# },
+# {
+# "description": "blah2",
+# "disable": true,
+# "name": "vrf2"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "disable_forwarding": false,
+# "nht_no_resolve_via_default": false,
+# "route_maps": [
+# {
+# "protocol": "ospf",
+# "rm_name": "rm1"
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "disable_forwarding": false,
+# "nht_no_resolve_via_default": true
+# }
+# ],
+# "disable": false,
+# "name": "vrf-red"
+# }
+# ]
+# }
+"""
+
+RETURN = """
+before:
+ description: The configuration prior to the module execution.
+ returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged)
+ type: dict
+ sample: >
+ This output will always be in the same format as the
+ module argspec.
+after:
+ description: The resulting configuration after module execution.
+ returned: when changed
+ type: dict
+ sample: >
+ This output will always be in the same format as the
+ module argspec.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged)
+ type: list
+ sample:
+ - set system ntp server server1 dynamic
+ - set system ntp server server1 prefer
+ - set system ntp server server2 noselect
+ - set system ntp server server2 preempt
+ - set system ntp server server_add preempt
+rendered:
+ description: The provided configuration in the task rendered in device-native format (offline).
+ returned: when I(state) is C(rendered)
+ type: list
+ sample:
+ - set system ntp server server1 dynamic
+ - set system ntp server server1 prefer
+ - set system ntp server server2 noselect
+ - set system ntp server server2 preempt
+ - set system ntp server server_add preempt
+gathered:
+ description: Facts about the network resource gathered from the remote device as structured data.
+ returned: when I(state) is C(gathered)
+ type: list
+ sample: >
+ This output will always be in the same format as the
+ module argspec.
+parsed:
+ description: The device native config provided in I(running_config) option parsed into structured data as per module argspec.
+ returned: when I(state) is C(parsed)
+ type: list
+ sample: >
+ This output will always be in the same format as the
+ module argspec.
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.vrf.vrf import VrfArgs
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.vrf.vrf import Vrf
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=VrfArgs.argument_spec,
+ mutually_exclusive=[["config", "running_config"]],
+ required_if=[
+ ["state", "merged", ["config"]],
+ ["state", "replaced", ["config"]],
+ ["state", "overridden", ["config"]],
+ ["state", "rendered", ["config"]],
+ ["state", "parsed", ["running_config"]],
+ ],
+ supports_check_mode=True,
+ )
+
+ result = Vrf(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()