diff options
Diffstat (limited to 'plugins/module_utils/network')
49 files changed, 3075 insertions, 645 deletions
diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py index 6ae17585..d1cf4fe8 100644 --- a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py @@ -83,6 +83,7 @@ class Firewall_rulesArgs(object): # pylint: disable=R0903 "continue", "return", "jump", + "offload", "queue", "synproxy", ], @@ -210,6 +211,7 @@ class Firewall_rulesArgs(object): # pylint: disable=R0903 "required": True, "type": "int", }, + "offload_target": {"type": "str"}, "outbound_interface": { "options": { "group": {"type": "str"}, diff --git a/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py b/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py index 196db0c7..5b6d404d 100644 --- a/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py +++ b/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py @@ -74,6 +74,8 @@ class Route_mapsArgs(object): # pylint: disable=R0903 }, "extcommunity_rt": {"type": "str"}, "extcommunity_soo": {"type": "str"}, + "extcommunity_bandwidth": {"type": "str"}, + "extcommunity_bandwidth_non_transitive": {"type": "bool"}, "ip_next_hop": {"type": "str"}, "ipv6_next_hop": { "type": "dict", @@ -100,6 +102,7 @@ class Route_mapsArgs(object): # pylint: disable=R0903 "src": {"type": "str"}, "tag": {"type": "str"}, "weight": {"type": "str"}, + "table": {"type": "str"}, }, }, "match": { @@ -178,6 +181,23 @@ class Route_mapsArgs(object): # pylint: disable=R0903 "next_hop": {"type": "str"}, }, }, + "protocol": { + "type": "str", + "choices": [ + "babel", + "bgp", + "connected", + "isis", + "kernel", + "ospf", + "ospfv3", + "rip", + "ripng", + "static", + "table", + "vnc", + ], + }, "large_community_large_community_list": { "type": "str", }, 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/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py index 0e6bec81..2d6481c2 100644 --- a/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py +++ b/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py @@ -20,7 +20,6 @@ created. import re -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) @@ -32,15 +31,14 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family import ( Bgp_address_familyTemplate, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family_14 import ( Bgp_address_familyTemplate14, ) - +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_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import LooseVersion - class Bgp_address_family(ResourceModule): """ @@ -65,7 +63,7 @@ class Bgp_address_family(ResourceModule): self._tmplt = Bgp_address_familyTemplate() def parse(self): - """ override parse to check template """ + """override parse to check template""" self._validate_template() return super().parse() @@ -93,9 +91,11 @@ class Bgp_address_family(ResourceModule): wantd = {} haved = {} - if (self.want.get("as_number") == self.have.get("as_number") or - not self.have or - LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4")): + if ( + self.want.get("as_number") == self.have.get("as_number") + or not self.have + or LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") + ): if self.want: wantd = {self.want["as_number"]: self.want} if self.have: @@ -113,16 +113,16 @@ class Bgp_address_family(ResourceModule): # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": - for k, have in iteritems(haved): + for k, have in haved.items(): self._delete_af(wantd, have) wantd = {} if self.state == "overridden": - for k, have in iteritems(haved): + for k, have in haved.items(): if k not in wantd: self._compare(want={}, have=have) - for k, want in iteritems(wantd): + for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): @@ -149,23 +149,23 @@ class Bgp_address_family(ResourceModule): def _compare_af(self, want, have): waf = want.get("address_family", {}) haf = have.get("address_family", {}) - for name, entry in iteritems(waf): + for name, entry in waf.items(): self._compare_lists( entry, have=haf.get(name, {}), as_number=want["as_number"], afi=name, ) - for name, entry in iteritems(haf): + for name, entry in haf.items(): if name not in waf.keys() and self.state == "replaced": continue self._compare_lists({}, entry, as_number=have["as_number"], afi=name) def _delete_af(self, want, have): - for as_num, entry in iteritems(want): - for afi, af_entry in iteritems(entry.get("address_family", {})): + for as_num, entry in want.items(): + for afi, af_entry in entry.get("address_family", {}).items(): if have.get("address_family"): - for hafi, hentry in iteritems(have["address_family"]): + for hafi, hentry in have["address_family"].items(): if hafi == afi: self.commands.append( self._tmplt.render( @@ -177,9 +177,9 @@ class Bgp_address_family(ResourceModule): True, ), ) - for neigh, neigh_entry in iteritems(entry.get("neighbors", {})): + for neigh, neigh_entry in entry.get("neighbors", {}).items(): if have.get("neighbors"): - for hneigh, hnentry in iteritems(have["neighbors"]): + for hneigh, hnentry in have["neighbors"].items(): if hneigh == neigh: if not neigh_entry.get("address_family"): self.commands.append( @@ -239,9 +239,9 @@ class Bgp_address_family(ResourceModule): ] wneigh = want.get("neighbors", {}) hneigh = have.get("neighbors", {}) - for name, entry in iteritems(wneigh): - for afi, af_entry in iteritems(entry.get("address_family")): - for k, val in iteritems(af_entry): + for name, entry in wneigh.items(): + for afi, af_entry in entry.get("address_family").items(): + for k, val in af_entry.items(): w = { "as_number": want["as_number"], "neighbors": { @@ -268,7 +268,7 @@ class Bgp_address_family(ResourceModule): want=w, have=h, ) - for name, entry in iteritems(hneigh): + for name, entry in hneigh.items(): if name not in wneigh.keys(): # remove surplus config for overridden and replaced if self.state != "replaced": @@ -284,9 +284,9 @@ class Bgp_address_family(ResourceModule): ) continue - for hafi, haf_entry in iteritems(entry.get("address_family")): + for hafi, haf_entry in entry.get("address_family").items(): # remove surplus configs for given neighbor - replace and overridden - for k, val in iteritems(haf_entry): + for k, val in haf_entry.items(): h = { "as_number": have["as_number"], "neighbors": { @@ -317,7 +317,7 @@ class Bgp_address_family(ResourceModule): for attrib in ["redistribute", "networks", "aggregate_address"]: wdict = want.pop(attrib, {}) hdict = have.pop(attrib, {}) - for key, entry in iteritems(wdict): + for key, entry in wdict.items(): if entry != hdict.get(key, {}): self.compare( parsers=parsers, @@ -348,7 +348,7 @@ class Bgp_address_family(ResourceModule): + attrib, ) hdict = {} - for key, entry in iteritems(hdict): + for key, entry in hdict.items(): self.compare( parsers=parsers, want={}, @@ -358,7 +358,7 @@ class Bgp_address_family(ResourceModule): }, ) # de-duplicate child commands if parent command is present - for val in (self.commands): + for val in self.commands: for val2 in self.commands: if val != val2 and val2.startswith(val): self.commands.remove(val2) @@ -366,13 +366,11 @@ class Bgp_address_family(ResourceModule): def _compare_asn(self, want, have): if want.get("as_number") and not have.get("as_number"): self.commands.append( - "set protocols bgp " - + "system-as " - + str(want.get("as_number")), + "set protocols bgp " + "system-as " + str(want.get("as_number")), ) def _bgp_af_list_to_dict(self, entry): - for name, proc in iteritems(entry): + for name, proc in entry.items(): if "address_family" in proc: af_dict = {} for entry in proc.get("address_family"): diff --git a/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py b/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py index 91a5af12..075c8fd2 100644 --- a/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py +++ b/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py @@ -18,7 +18,6 @@ necessary to bring the current configuration to its desired end-state is created. """ -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) @@ -118,10 +117,10 @@ class Bgp_global(ResourceModule): # if state is deleted, empty out wantd and set haved to wantd if self.state == "purged": h_del = {} - for k, v in iteritems(haved): + for k, v in haved.items(): if k in wantd or not wantd: h_del.update({k: v}) - for num, entry in iteritems(h_del): + for num, entry in h_del.items(): self.commands.append(self._tmplt.render({"as_number": num}, "router", True)) wantd = {} @@ -129,7 +128,7 @@ class Bgp_global(ResourceModule): self._compare(want={}, have=self.have) wantd = {} - for k, want in iteritems(wantd): + for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): @@ -144,7 +143,7 @@ class Bgp_global(ResourceModule): parsers = ["maximum_paths", "timers"] self._compare_neighbor(want, have) self._compare_bgp_params(want, have) - for name, entry in iteritems(want): + for name, entry in want.items(): if name != "as_number": self.compare( parsers=parsers, @@ -154,7 +153,7 @@ class Bgp_global(ResourceModule): name: have.pop(name, {}), }, ) - for name, entry in iteritems(have): + for name, entry in have.items(): if name != "as_number": self.compare( parsers=parsers, @@ -217,7 +216,7 @@ class Bgp_global(ResourceModule): hneigh = have.pop("neighbor", {}) self._compare_neigh_lists(wneigh, hneigh) - for name, entry in iteritems(wneigh): + for name, entry in wneigh.items(): for k, v in entry.items(): if k == "address": continue @@ -233,7 +232,7 @@ class Bgp_global(ResourceModule): }, have={"as_number": want["as_number"], "neighbor": h}, ) - for name, entry in iteritems(hneigh): + for name, entry in hneigh.items(): if name not in wneigh.keys(): if self._check_af(name): msg = "Use the _bgp_address_family module to delete the address_family under neighbor {0}, before replacing/deleting the neighbor.".format( @@ -281,7 +280,7 @@ class Bgp_global(ResourceModule): wbgp = want.pop("bgp_params", {}) hbgp = have.pop("bgp_params", {}) - for name, entry in iteritems(wbgp): + for name, entry in wbgp.items(): if name == "confederation": if entry != hbgp.pop(name, {}): self.addcmd( @@ -325,7 +324,7 @@ class Bgp_global(ResourceModule): if not wbgp and hbgp: self.commands.append("delete protocols bgp" + self._asn_mod + " parameters") hbgp = {} - for name, entry in iteritems(hbgp): + for name, entry in hbgp.items(): if name == "confederation": self.commands.append( "delete protocols bgp" + self._asn_mod + " parameters confederation", @@ -362,7 +361,7 @@ class Bgp_global(ResourceModule): ]: wdict = want.pop(attrib, {}) hdict = have.pop(attrib, {}) - for key, entry in iteritems(wdict): + for key, entry in wdict.items(): if entry != hdict.pop(key, {}): self.addcmd(entry, "neighbor.{0}".format(attrib), False) # remove remaining items in have for replaced @@ -370,7 +369,7 @@ class Bgp_global(ResourceModule): self.addcmd(entry, "neighbor.{0}".format(attrib), True) def _bgp_global_list_to_dict(self, entry): - for name, proc in iteritems(entry): + for name, proc in entry.items(): if "neighbor" in proc: neigh_dict = {} for entry in proc.get("neighbor", []): diff --git a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py index e2a25e32..d4e173c1 100644 --- a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py +++ b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py @@ -17,7 +17,6 @@ __metaclass__ = type from copy import deepcopy -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -28,13 +27,16 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.u from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - list_diff_want_only, in_target_not_none, + list_diff_want_only, +) +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, + load_config, ) - -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version - -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import LooseVersion class Firewall_global(ConfigBase): @@ -75,6 +77,13 @@ class Firewall_global(ConfigBase): warnings = list() commands = list() + try: + self._module.params["comment"] + except KeyError: + comment = [] + else: + comment = self._module.params["comment"] + if self.state in self.ACTION_STATES: existing_firewall_global_facts = self.get_firewall_global_facts() else: @@ -83,6 +92,12 @@ class Firewall_global(ConfigBase): if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_firewall_global_facts)) + if commands and self._module._diff: + commit = not self._module.check_mode + diff = load_config(self._module, commands, commit=commit, comment=comment) + if diff: + result["diff"] = {"prepared": str(diff)} + if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) @@ -190,7 +205,7 @@ class Firewall_global(ConfigBase): "twa_hazards_protection", ) if want: - for key, val in iteritems(want): + for key, val in want.items(): if val and key in b_set and not have: commands.append(self._form_attr_cmd(attr=key, opr=False)) elif val and key in b_set and have and key in have and have[key] != val: @@ -200,7 +215,7 @@ class Firewall_global(ConfigBase): elif not want and have: commands.append(self._compute_command(opr=False)) elif have: - for key, val in iteritems(have): + for key, val in have.items(): if val and key in b_set: commands.append(self._form_attr_cmd(attr=key, opr=False)) else: @@ -247,7 +262,7 @@ class Firewall_global(ConfigBase): "twa_hazards_protection", ) if w_fg: - for key, val in iteritems(w_fg): + for key, val in w_fg.items(): if opr and key in l_set and not (h and self._is_w_same(w_fg, h, key)): commands.append( self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr), @@ -258,11 +273,7 @@ class Firewall_global(ConfigBase): self._form_attr_cmd(attr=key, key=self._bool_to_str(val), opr=opr), ) continue - if ( - key in l_set - and not self._in_target(h, key) - and not self._is_del(l_set, h) - ): + if key in l_set and not self._in_target(h, key) and not self._is_del(l_set, h): commands.append( self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr), ) @@ -285,13 +296,13 @@ class Firewall_global(ConfigBase): if h: h_ping = h.get(attr) or {} if self._is_root_del(w[attr], h_ping, attr): - for item, value in iteritems(h[attr]): + for item, value in h[attr].items(): if not opr and item in l_set: commands.append(self._form_attr_cmd(attr=item, opr=opr)) elif w[attr]: if h and attr in h.keys(): h_ping = h.get(attr) or {} - for item, value in iteritems(w[attr]): + for item, value in w[attr].items(): if ( opr and item in l_set @@ -355,7 +366,7 @@ class Firewall_global(ConfigBase): cmd = self._compute_command(key="group", attr="ipv6-" + attr, opr=opr) else: cmd = self._compute_command(key="group", attr=attr, opr=opr) - for key, val in iteritems(want): + for key, val in want.items(): if val: if opr and key in l_set and not (h and self._is_w_same(want, h, key)): if key == "name": @@ -373,9 +384,17 @@ class Firewall_global(ConfigBase): ) elif not opr and key in l_set: if key == "name" and self._is_grp_del(h, want, key): + if len(commands) > 0 and commands[-1] == cmd + " " + want["name"] + " " + self._grp_type( + attr, + ): + commands.pop() commands.append(cmd + " " + want["name"]) continue - if not (h and in_target_not_none(h, key)) and not self._is_grp_del(h, want, "name"): + if not (h and in_target_not_none(h, key)) and not self._is_grp_del( + h, + want, + "name", + ): commands.append(cmd + " " + want["name"] + " " + key) elif key == "members": commands.extend( @@ -435,6 +454,10 @@ class Firewall_global(ConfigBase): + " " + member[self._get_mem_type(type)], ) + elif not opr and not have: + commands.append( + cmd + " " + name + " " + self._grp_type(type), + ) return commands def _get_mem_type(self, group): @@ -470,10 +493,12 @@ class Firewall_global(ConfigBase): if want: for w in want: h = self.search_attrib_in_have(have, w, "connection_type") - for key, val in iteritems(w): + for key, val in w.items(): if val and key != "connection_type": if opr and key in l_set and not (h and self._is_w_same(w, h, key)): - if key == "log" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): + if key == "log" and LooseVersion( + get_os_version(self._module), + ) >= LooseVersion("1.4"): commands.append( self._form_attr_cmd( key=attr + " " + w["connection_type"], @@ -499,7 +524,9 @@ class Firewall_global(ConfigBase): ), ) break # delete the whole thing and move on - if (not self._in_target(h, key) or h[key] is None) and (self._in_target(w, key) and w[key]): + if (not self._in_target(h, key) or h[key] is None) and ( + self._in_target(w, key) and w[key] + ): # delete if not being replaced and value currently exists commands.append( self._form_attr_cmd( @@ -531,23 +558,23 @@ class Firewall_global(ConfigBase): if want: for w in want: h = self.search_attrib_in_have(have, w, "afi") - if 'afi' in w: - afi = w['afi'] + if "afi" in w: + afi = w["afi"] else: - if h and 'afi' in h: - afi = h['afi'] + if h and "afi" in h: + afi = h["afi"] else: afi = None afi = None - for key, val in iteritems(w): - if val and key != "afi": + for key, val in w.items(): + if val is not None and key != "afi": if opr and key in l_set and not (h and self._is_w_same(w, h, key)): commands.append( self._form_attr_cmd( attr=key, val=self._bool_to_str(val), opr=opr, - type=afi + type=afi, ), ) elif not opr and key in l_set: @@ -557,7 +584,7 @@ class Firewall_global(ConfigBase): attr=key, val=self._bool_to_str(val), opr=opr, - type=afi + type=afi, ), ) continue @@ -567,7 +594,7 @@ class Firewall_global(ConfigBase): attr=key, val=self._bool_to_str(val), opr=opr, - type=afi + type=afi, ), ) elif key == "icmp_redirects": @@ -587,20 +614,25 @@ class Firewall_global(ConfigBase): commands = [] h_red = {} l_set = ("send", "receive") - if w and 'afi' in w: - afi = w['afi'] + if w and "afi" in w: + afi = w["afi"] else: - if h and 'afi' in h: - afi = h['afi'] + if h and "afi" in h: + afi = h["afi"] else: afi = None if w[attr]: if h and attr in h.keys(): h_red = h.get(attr) or {} - for item, value in iteritems(w[attr]): + for item, value in w[attr].items(): if opr and item in l_set and not (h_red and self._is_w_same(w[attr], h_red, item)): commands.append( - self._form_attr_cmd(attr=item, val=self._bool_to_str(value), opr=opr, type=afi) + self._form_attr_cmd( + attr=item, + val=self._bool_to_str(value), + opr=opr, + type=afi, + ), ) elif ( not opr @@ -634,7 +666,12 @@ class Firewall_global(ConfigBase): :param type: AF type of attribute. :return: generated command. """ - command = self._compute_command(key=key, attr=self._map_attrib(attr, type=type), val=val, opr=opr) + command = self._compute_command( + key=key, + attr=self._map_attrib(attr, type=type), + val=val, + opr=opr, + ) return command def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True): @@ -651,14 +688,20 @@ class Firewall_global(ConfigBase): cmd = "delete firewall " else: cmd = "set firewall " - if attr and key != "group" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): + if ( + attr + and key != "group" + and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") + ): cmd += "global-options " if key: cmd += key.replace("_", "-") + " " if attr: cmd += attr.replace("_", "-") if val and opr: - if key == "state_policy" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): + if key == "state_policy" and LooseVersion(get_os_version(self._module)) >= LooseVersion( + "1.4", + ): cmd += "" else: cmd += " '" + str(val) + "'" 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 bb6055b7..3c768e32 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 @@ -17,7 +17,6 @@ __metaclass__ = type from copy import deepcopy -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -315,7 +314,7 @@ class Firewall_rules(ConfigBase): h_rs = deepcopy(remove_empties(have)) h_rules = h_rs.pop("rules", None) if w_rs: - for key, val in iteritems(w_rs): + for key, val in w_rs.items(): if opr and key in l_set and not (h_rs and self._is_w_same(w_rs, h_rs, key)): if key == "enable_default_log": if val and (not h_rs or key not in h_rs or not h_rs[key]): @@ -359,6 +358,7 @@ class Firewall_rules(ConfigBase): "disable", "description", "jump_target", + "offload_target", ) if w_rules: for w in w_rules: @@ -366,7 +366,7 @@ class Firewall_rules(ConfigBase): h = self.search_rules_in_have_rs(h_rules, w["number"]) if w != h and self.state == "replaced": h = {} - for key, val in iteritems(w): + for key, val in w.items(): if val: if opr and key in l_set and not (h and self._is_w_same(w, h, key)): if key == "disable": @@ -464,7 +464,7 @@ class Firewall_rules(ConfigBase): if w[attr]: if h and attr in h.keys(): h_state = h.get(attr) or {} - for item, val in iteritems(w[attr]): + for item, val in w[attr].items(): if ( opr and item in l_set @@ -527,7 +527,7 @@ class Firewall_rules(ConfigBase): if w[attr]: if h and attr in h.keys(): h_recent = h.get(attr) or {} - for item, val in iteritems(w[attr]): + for item, val in w[attr].items(): if ( opr and item in l_set @@ -555,7 +555,7 @@ class Firewall_rules(ConfigBase): if w[attr]: if h and attr in h.keys(): h_icmp = h.get(attr) or {} - for item, val in iteritems(w[attr]): + for item, val in w[attr].items(): if ( opr and item in l_set @@ -598,7 +598,7 @@ class Firewall_rules(ConfigBase): if w[attr]: if h and attr in h.keys(): h_if = h.get(attr) or {} - for item, val in iteritems(w[attr]): + for item, val in w[attr].items(): if opr and item in l_set and not (h_if and self._is_w_same(w[attr], h_if, item)): commands.append( cmd @@ -633,7 +633,7 @@ class Firewall_rules(ConfigBase): if w[attr]: if h and attr in h.keys(): h_time = h.get(attr) or {} - for item, val in iteritems(w[attr]): + for item, val in w[attr].items(): if ( opr and item in l_set @@ -862,7 +862,7 @@ class Firewall_rules(ConfigBase): h_group = {} if h and h.get(attr) and key in h[attr].keys(): h_group = h[attr].get(key) - for item, val in iteritems(group): + for item, val in group.items(): if val: if ( opr @@ -1167,11 +1167,12 @@ class Firewall_rules(ConfigBase): return True elif isinstance(w, list) and isinstance(rs, list): try: + def comparison(x): - if 'name' in x: - return x['name'] - if 'number' in x: - return x['number'] + if "name" in x: + return x["name"] + if "number" in x: + return x["number"] return str(x) sorted_list1 = sorted(w, key=comparison) diff --git a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py index a9d9307c..0a55564e 100644 --- a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py +++ b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py @@ -16,7 +16,6 @@ __metaclass__ = type from copy import deepcopy -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -227,7 +226,7 @@ class Interfaces(ConfigBase): updates = dict_diff(have_copy, want_copy) if updates: - for key, value in iteritems(updates): + for key, value in updates.items(): commands.append( self._compute_commands(key=key, value=value, interface=want_copy["name"]), ) @@ -243,7 +242,7 @@ class Interfaces(ConfigBase): vif_updates = dict_diff(have_vif, want_vif) if vif_updates: - for key, value in iteritems(vif_updates): + for key, value in vif_updates.items(): commands.append( self._compute_commands( key=key, 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 6e0c005f..a23494cd 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 @@ -19,7 +19,6 @@ __metaclass__ = type from copy import deepcopy -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -80,7 +79,7 @@ class L3_interfaces(ConfigBase): commands = list() if self.state in self.ACTION_STATES: - existing_l3_interfaces_facts = self.get_l3_interfaces_facts() + existing_l3_interfaces_facts = self.mutate_autoconfig(self.get_l3_interfaces_facts()) else: existing_l3_interfaces_facts = [] @@ -96,16 +95,19 @@ class L3_interfaces(ConfigBase): result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": - changed_l3_interfaces_facts = self.get_l3_interfaces_facts() + changed_l3_interfaces_facts = self.mutate_autoconfig(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) + result["parsed"] = self.mutate_autoconfig( + self.get_l3_interfaces_facts(data=running_config), + ) else: changed_l3_interfaces_facts = [] @@ -129,6 +131,7 @@ class L3_interfaces(ConfigBase): """ want = self._module.params["config"] have = existing_l3_interfaces_facts + resp = self.set_state(want, have) return to_list(resp) @@ -174,6 +177,7 @@ class L3_interfaces(ConfigBase): elif state == "replaced": commands.extend(self._state_replaced(item, obj_in_have)) + commands = [command.replace("auto-config", "autoconf") for command in commands] return commands def _state_replaced(self, want, have): @@ -226,7 +230,7 @@ class L3_interfaces(ConfigBase): have_vifs = have_copy.pop("vifs", []) for update in self._get_updates(want_copy, have_copy): - for key, value in iteritems(update): + for key, value in update.items(): commands.append( self._compute_commands(key=key, value=value, interface=want_copy["name"]), ) @@ -238,7 +242,7 @@ class L3_interfaces(ConfigBase): have_vif = {} for update in self._get_updates(want_vif, have_vif): - for key, value in iteritems(update): + for key, value in update.items(): commands.append( self._compute_commands( key=key, @@ -247,7 +251,6 @@ class L3_interfaces(ConfigBase): vif=want_vif["vlan_id"], ), ) - return commands def _state_deleted(self, want, have): @@ -261,51 +264,73 @@ class L3_interfaces(ConfigBase): want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(have) - want_vifs = want_copy.pop("vifs", []) - have_vifs = have_copy.pop("vifs", []) - - for update in self._get_updates(have_copy, want_copy): - for key, value in iteritems(update): + if have_copy is not None: + if all(v in (None, {}, []) for k, v in want_copy.items() if k != "name"): commands.append( self._compute_commands( - key=key, - value=value, + key=None, + value=None, interface=want_copy["name"], remove=True, ), ) - - if have_vifs: - for have_vif in have_vifs: - want_vif = search_obj_in_list(have_vif["vlan_id"], want_vifs, key="vlan_id") - if not want_vif: - want_vif = {"vlan_id": have_vif["vlan_id"]} - - for update in self._get_updates(have_vif, want_vif): - for key, value in iteritems(update): - commands.append( - self._compute_commands( - key=key, - interface=want_copy["name"], - value=value, - vif=want_vif["vlan_id"], - remove=True, - ), - ) + return commands + + want_vifs = want_copy.pop("vifs", []) + have_vifs = have_copy.pop("vifs", []) + + if have_vifs: + for have_vif in have_vifs: + want_vif = search_obj_in_list(have_vif["vlan_id"], want_vifs, key="vlan_id") + if not want_vif: + want_vif = {"vlan_id": have_vif["vlan_id"]} + + for update in self._get_updates(have_vif, want_vif): + for key, value in update.items(): + commands.append( + self._compute_commands( + key=key, + interface=want_copy["name"], + value=value, + vif=want_vif["vlan_id"], + remove=True, + ), + ) + + for update in self._get_updates(have_copy, want_copy): + for key, value in update.items(): + commands.append( + self._compute_commands( + key=key, + value=value, + interface=want_copy["name"], + remove=True, + ), + ) return commands def _compute_commands(self, interface, key, vif=None, value=None, remove=False): - intf_context = "interfaces {0} {1}".format(get_interface_type(interface), interface) + if value == "auto-config" and vif is None: + intf_context = "interfaces {0} {1} ipv6".format( + get_interface_type(interface), + interface, + ) + else: + intf_context = "interfaces {0} {1}".format(get_interface_type(interface), interface) + set_cmd = "set {0}".format(intf_context) del_cmd = "delete {0}".format(intf_context) if vif: - set_cmd = set_cmd + (" vif {0}".format(vif)) - del_cmd = del_cmd + (" vif {0}".format(vif)) + suffix = " ipv6" if value == "auto-config" else "" + set_cmd += f" vif {vif}{suffix}" + del_cmd += f" vif {vif}{suffix}" - if remove: + if remove and key and value: command = "{0} {1} '{2}'".format(del_cmd, key, value) + elif remove and not (key and value): + command = "{0}".format(del_cmd) else: command = "{0} {1} '{2}'".format(set_cmd, key, value) @@ -318,3 +343,12 @@ class L3_interfaces(ConfigBase): updates.extend(diff_list_of_dicts(want.get("ipv6", []), have.get("ipv6", []))) return updates + + def mutate_autoconfig(self, obj): + if isinstance(obj, dict): + return dict(map(lambda kv: (kv[0], self.mutate_autoconfig(kv[1])), obj.items())) + if isinstance(obj, list): + return list(map(self.mutate_autoconfig, obj)) + if isinstance(obj, str): + return obj.replace("autoconf", "auto-config") + return obj 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 6890fe0c..62270503 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 @@ -12,7 +12,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -from ansible.module_utils.six import iteritems + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -245,7 +245,7 @@ class Lag_interfaces(ConfigBase): commands.extend(self._add_bond_members(want, have)) if updates: - for key, value in iteritems(updates): + for key, value in updates.items(): if value: if key == "arp_monitor": commands.extend(self._add_arp_monitor(updates, key, want, 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 1dfd25e4..39724f1b 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 @@ -12,7 +12,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -from ansible.module_utils.six import iteritems + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -241,7 +241,7 @@ class Lldp_global(ConfigBase): commands.extend(self._add_management_addresses(want, have)) if updates: - for key, value in iteritems(updates): + for key, value in updates.items(): if value is not None: if key == "enable": if value is False: 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 2fd6a548..97fcbce5 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 @@ -16,7 +16,6 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -285,7 +284,7 @@ class Lldp_interfaces(ConfigBase): have_dict = have_location_type.get("coordinate_based") or {} location_type = "coordinate-based" updates = dict_diff(have_dict, want_dict) - for key, value in iteritems(updates): + for key, value in updates.items(): if value: commands.append(self._compute_command(set_cmd + location_type, key, str(value))) @@ -319,7 +318,7 @@ class Lldp_interfaces(ConfigBase): if is_dict_element_present(have_location_type, "coordinate_based"): have_dict = have_location_type.get("coordinate_based") or {} location_type = "coordinate-based" - for key, value in iteritems(have_dict): + for key, value in have_dict.items(): only_in_have = key_value_in_dict(key, value, want_dict) if not only_in_have: commands.append( diff --git a/plugins/module_utils/network/vyos/config/logging_global/logging_global.py b/plugins/module_utils/network/vyos/config/logging_global/logging_global.py index f94c9195..35a2624a 100644 --- a/plugins/module_utils/network/vyos/config/logging_global/logging_global.py +++ b/plugins/module_utils/network/vyos/config/logging_global/logging_global.py @@ -20,7 +20,6 @@ created. 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, ) @@ -92,7 +91,7 @@ class Logging_global(ResourceModule): if self.state in ["overridden", "replaced"]: if wantd != haved: wantx, havex = self.call_op(wantd, haved, "overridden") - for k, have in iteritems(havex): + for k, have in havex.items(): if k not in wantx: self._compare(want={}, have=have) @@ -102,7 +101,7 @@ class Logging_global(ResourceModule): if self.state == "merged": wantd = dict_merge(haved, wantd) - for k, want in iteritems(wantd): + for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): @@ -116,12 +115,12 @@ class Logging_global(ResourceModule): def operation_rep(self, params): op_val = dict() - for k, val in iteritems(params): + for k, val in params.items(): if k in ["console", "global_params"]: mod_val = deepcopy(val) op_val.update(self.flatten_facility({k: mod_val})) elif k in ["files", "hosts", "users"]: - for m, n in iteritems(val): + for m, n in val.items(): mod_n = deepcopy(n) if mod_n.get("archive"): del mod_n["archive"] @@ -160,18 +159,18 @@ class Logging_global(ResourceModule): def flatten_facility(self, param): temp_param = dict() - for element, val in iteritems(param): + for element, val in param.items(): if element in ["console", "global_params", "syslog"]: if element != "syslog" and val.get("facilities"): - for k, v in iteritems(val.get("facilities")): + for k, v in val.get("facilities").items(): temp_param[k + element] = {element: {"facilities": v}} del val["facilities"] if val: temp_param[element] = {element: val} if element in ["files", "hosts", "users"]: - for k, v in iteritems(val): + for k, v in val.items(): if v.get("facilities"): - for pk, dat in iteritems(v.get("facilities")): + for pk, dat in v.get("facilities").items(): temp_param[pk + k] = { element: { "facilities": dat, @@ -197,7 +196,7 @@ class Logging_global(ResourceModule): "hosts": "hostname", "users": "username", } - for element, val in iteritems(param): + for element, val in param.items(): if element == "facilities": # only with recursion call _tem_par = {} for par in val: diff --git a/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py b/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py index 5d294063..06ec1d6b 100644 --- a/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py +++ b/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py @@ -18,7 +18,6 @@ necessary to bring the current configuration to its desired end-state is created. """ -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) @@ -93,16 +92,16 @@ class Ntp_global(ResourceModule): # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": - haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} + haved = {k: v for k, v in haved.items() if k in wantd or not wantd} wantd = {} commandlist = self._commandlist(haved) servernames = self._servernames(haved) # removing the servername and commandlist from the list after deleting it from haved # iterate through the top-level items to delete - for k, have in iteritems(haved): + for k, have in haved.items(): if k not in wantd: - for hk, hval in iteritems(have): + for hk, hval in have.items(): if hk == "allow_clients" and hk in commandlist: self.commands.append( self._tmplt.render({"": hk}, "allow_clients_delete", True), @@ -130,7 +129,7 @@ class Ntp_global(ResourceModule): commandlist = self._commandlist(haved) servernames = self._servernames(haved) - for k, have in iteritems(haved): + for k, have in haved.items(): if k not in wantd: if "server" not in have: self._compareoverride(want={}, have=have) @@ -139,7 +138,7 @@ class Ntp_global(ResourceModule): self._compareoverride(want={}, have=have) servernames.remove(have["server"]) - for k, want in iteritems(wantd): + for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): @@ -155,7 +154,7 @@ class Ntp_global(ResourceModule): def _compareoverride(self, want, have): # do not delete configuration with options level - for i, val in iteritems(have): + for i, val in have.items(): if i == "options": pass else: @@ -163,12 +162,12 @@ class Ntp_global(ResourceModule): def _ntp_list_to_dict(self, entry): servers_dict = {} - for k, data in iteritems(entry): + for k, data in entry.items(): if k == "servers": for value in data: if "options" in value: result = self._serveroptions_list_to_dict(value) - for res, resvalue in iteritems(result): + for res, resvalue in result.items(): servers_dict.update({res: resvalue}) else: servers_dict.update({value["server"]: value}) @@ -179,7 +178,7 @@ class Ntp_global(ResourceModule): def _serveroptions_list_to_dict(self, entry): serveroptions_dict = {} - for Opk, Op in iteritems(entry): + for Opk, Op in entry.items(): if Opk == "options": for val in Op: dict = {} @@ -190,16 +189,16 @@ class Ntp_global(ResourceModule): def _commandlist(self, haved): commandlist = [] - for k, have in iteritems(haved): - for ck, cval in iteritems(have): + for k, have in haved.items(): + for ck, cval in have.items(): if ck != "options" and ck not in commandlist: commandlist.append(ck) return commandlist def _servernames(self, haved): servernames = [] - for k, have in iteritems(haved): - for sk, sval in iteritems(have): + for k, have in haved.items(): + for sk, sval in have.items(): if sk != "options" and sval not in servernames: servernames.append(sval) return servernames diff --git a/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py index 51b47494..837a2ddf 100644 --- a/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py +++ b/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py @@ -17,7 +17,6 @@ necessary to bring the current configuration to its desired end-state is created. """ -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) @@ -27,17 +26,16 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.u 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.ospf_interfaces import ( - Ospf_interfacesTemplate + Ospf_interfacesTemplate, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces_14 import ( - Ospf_interfacesTemplate14 + Ospf_interfacesTemplate14, +) +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_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import LooseVersion - class Ospf_interfaces(ResourceModule): """ @@ -77,7 +75,7 @@ class Ospf_interfaces(ResourceModule): self._tmplt = Ospf_interfacesTemplate() def parse(self): - """ override parse to check template """ + """override parse to check template""" self._validate_template() return super().parse() @@ -119,12 +117,12 @@ class Ospf_interfaces(ResourceModule): # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": h_del = {} - for k, v in iteritems(haved): + for k, v in haved.items(): if k in wantd or not wantd: h_del.update({k: v}) haved = h_del have_int = [] - for k, have in iteritems(haved): + for k, have in haved.items(): if k in wantd: have_int.append(k) self._remove_ospf_int(have) @@ -132,7 +130,7 @@ class Ospf_interfaces(ResourceModule): if self.state == "overridden": have_int = [] - for k, have in iteritems(haved): + for k, have in haved.items(): if k not in wantd: have_int.append(k) self._remove_ospf_int(have) @@ -142,17 +140,17 @@ class Ospf_interfaces(ResourceModule): # removing the interfaces from haved that are already negated for interface in have_int: haved.pop(interface) - for k, have in iteritems(haved): + for k, have in haved.items(): if k not in wantd: self._compare(want={}, have=have) - for k, want in iteritems(wantd): + for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _remove_ospf_int(self, entry): int_name = entry.get("name", {}) int_addr = entry.get("address_family", {}) - for k, addr in iteritems(int_addr): + for k, addr in int_addr.items(): rem_entry = {"name": int_name, "address_family": {"afi": k}} self.addcmd(rem_entry, "ip_ospf", True) @@ -169,8 +167,8 @@ class Ospf_interfaces(ResourceModule): hdict = have.get("address_family", {}) wname = want.get("name") hname = have.get("name") - for name, entry in iteritems(wdict): - for key, param in iteritems(entry): + for name, entry in wdict.items(): + for key, param in entry.items(): w_addr = {"afi": name, key: param} h_addr = {} if hdict.get(name): @@ -178,8 +176,8 @@ class Ospf_interfaces(ResourceModule): w = {"name": wname, "address_family": w_addr} h = {"name": hname, "address_family": h_addr} self.compare(parsers=self.parsers, want=w, have=h) - for name, entry in iteritems(hdict): - for key, param in iteritems(entry): + for name, entry in hdict.items(): + for key, param in entry.items(): h_addr = {"afi": name, key: param} w_addr = {} w = {"name": wname, "address_family": w_addr} @@ -187,7 +185,7 @@ class Ospf_interfaces(ResourceModule): self.compare(parsers=self.parsers, want=w, have=h) def _ospf_int_list_to_dict(self, entry): - for name, family in iteritems(entry): + for name, family in entry.items(): if "address_family" in family: addr_dict = {} for entry in family.get("address_family", []): diff --git a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py index ababc6f1..ed973179 100644 --- a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py @@ -17,7 +17,6 @@ __metaclass__ = type from copy import deepcopy -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -33,10 +32,11 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils _is_w_same, list_diff_want_only, ) +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_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import LooseVersion - class Ospfv2(ConfigBase): """ @@ -222,7 +222,7 @@ class Ospfv2(ConfigBase): w = deepcopy(remove_empties(want)) leaf = ("default_metric", "log_adjacency_changes") if w: - for key, val in iteritems(w): + for key, val in w.items(): 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): @@ -290,7 +290,7 @@ class Ospfv2(ConfigBase): ), } leaf = leaf_dict[attr] - for item, value in iteritems(want[attr]): + for item, value in want[attr].items(): if opr and item in leaf and not _is_w_same(want[attr], h, item): if item == "enabled": item = "enable" @@ -336,10 +336,22 @@ class Ospfv2(ConfigBase): command = cmd + attr.replace("_", "-") + " " if attr == "network": command += member["address"] - elif attr == "passive_interface" and member != "default" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): - command = command.replace("passive-interface", "interface") + member + " passive" - elif attr == "passive_interface_exclude" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): - command = command.replace("passive-interface-exclude", "interface") + member + " passive disable" + elif ( + attr == "passive_interface" + and member != "default" + and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") + ): + command = ( + command.replace("passive-interface", "interface") + member + " passive" + ) + elif attr == "passive_interface_exclude" and LooseVersion( + get_os_version(self._module), + ) >= LooseVersion("1.4"): + command = ( + command.replace("passive-interface-exclude", "interface") + + member + + " passive disable" + ) else: command += member commands.append(command) @@ -352,10 +364,21 @@ class Ospfv2(ConfigBase): cmd + attr.replace("_", "-") + " " + member["address"], ) elif member not in h: - if attr == "passive_interface" and member != "default" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): + if ( + attr == "passive_interface" + and member != "default" + and LooseVersion(get_os_version(self._module)) + >= LooseVersion("1.4") + ): commands.append(cmd + "interface" + " " + member + " passive") - elif attr == "passive_interface_exclude" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): - command = command.replace("passive-interface-exclude", "interface") + member + " passive disable" + elif attr == "passive_interface_exclude" and LooseVersion( + get_os_version(self._module), + ) >= LooseVersion("1.4"): + command = ( + command.replace("passive-interface-exclude", "interface") + + member + + " passive disable" + ) else: commands.append(cmd + attr.replace("_", "-") + " " + member) else: @@ -394,7 +417,7 @@ class Ospfv2(ConfigBase): commands.append(cmd + attr.replace("_", "-")) elif w: for w_item in w: - for key, val in iteritems(w_item): + for key, val in w_item.items(): if not cmd: cmd = self._compute_command(opr=opr) h_item = self.search_obj_in_have(h, w_item, name[attr]) @@ -502,7 +525,7 @@ class Ospfv2(ConfigBase): commands.append(self._compute_command(attr=attr, opr=opr)) elif w: for w_item in w: - for key, val in iteritems(w_item): + for key, val in w_item.items(): if not cmd: cmd = self._compute_command(opr=opr) h_item = self.search_obj_in_have(h, w_item, name[attr]) @@ -599,7 +622,7 @@ class Ospfv2(ConfigBase): leaf = leaf_dict[attr] if h and key in h.keys(): h_attrib = h.get(key) or {} - for item, val in iteritems(w[key]): + for item, val in w[key].items(): 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( @@ -660,7 +683,7 @@ class Ospfv2(ConfigBase): self._form_attr_cmd(key="area", attr=w_area["area_id"], opr=opr), ) else: - for key, val in iteritems(w_area): + for key, val in w_area.items(): if opr and key in l_set and not _is_w_same(w_area, h_area, key): if key == "area_id": commands.append( @@ -733,7 +756,7 @@ class Ospfv2(ConfigBase): 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]): + for item, val in w_type[key].items(): if ( opr and item in a_type[key] diff --git a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py index 2538a6f5..30d30571 100644 --- a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py +++ b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py @@ -17,7 +17,6 @@ __metaclass__ = type from copy import deepcopy -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -204,7 +203,7 @@ class Ospfv3(ConfigBase): commands = [] w = deepcopy(remove_empties(want)) if w: - for key, val in iteritems(w): + for key, val in w.items(): commands.extend(self._render_child_param(w, have, key, opr)) return commands @@ -245,7 +244,7 @@ class Ospfv3(ConfigBase): elif want[attr]: leaf_dict = {"parameters": "router_id"} leaf = leaf_dict[attr] - for item, value in iteritems(want[attr]): + for item, value in want[attr].items(): 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): @@ -283,7 +282,7 @@ class Ospfv3(ConfigBase): commands.append(self._compute_command(attr=attr, opr=opr)) elif w: for w_item in w: - for key, val in iteritems(w_item): + for key, val in w_item.items(): if not cmd: cmd = self._compute_command(opr=opr) h_item = search_obj_in_list(w_item[name[attr]], h, name[attr]) @@ -376,7 +375,7 @@ class Ospfv3(ConfigBase): self._form_attr_cmd(key="area", attr=w_area["area_id"], opr=opr), ) else: - for key, val in iteritems(w_area): + for key, val in w_area.items(): if opr and key in l_set and not _is_w_same(w_area, h_area, key): if key == "area_id": commands.append( diff --git a/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py b/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py index 9da27c15..9b703399 100644 --- a/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py +++ b/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py @@ -19,7 +19,6 @@ created. """ -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) @@ -86,23 +85,23 @@ class Prefix_lists(ResourceModule): # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": - haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} - for key, hvalue in iteritems(haved): + haved = {k: v for k, v in haved.items() if k in wantd or not wantd} + for key, hvalue in haved.items(): wvalue = wantd.pop(key, {}) if wvalue: wplists = wvalue.get("prefix_lists", {}) hplists = hvalue.get("prefix_lists", {}) hvalue["prefix_lists"] = { - k: v for k, v in iteritems(hplists) if k in wplists or not wplists + k: v for k, v in hplists.items() if k in wplists or not wplists } # remove superfluous config for overridden and deleted if self.state in ["overridden", "deleted"]: - for k, have in iteritems(haved): + for k, have in haved.items(): if k not in wantd: self._compare(want={}, have=have) - for k, want in iteritems(wantd): + for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): @@ -127,7 +126,7 @@ class Prefix_lists(ResourceModule): ) def _compare_plists(self, want, have): - for wk, wentry in iteritems(want): + for wk, wentry in want.items(): hentry = have.pop(wk, {}) # parser list for name and descriptions @@ -143,7 +142,7 @@ class Prefix_lists(ResourceModule): self._compare_rules(want=wplrules, have=hplrules) def _compare_rules(self, want, have): - for wr, wrule in iteritems(want): + for wr, wrule in want.items(): hrule = have.pop(wr, {}) # parser list for entries @@ -164,7 +163,7 @@ class Prefix_lists(ResourceModule): ) def _prefix_list_list_to_dict(self, entry): - for afi, value in iteritems(entry): + for afi, value in entry.items(): if "prefix_lists" in value: for pl in value["prefix_lists"]: pl.update({"afi": afi}) diff --git a/plugins/module_utils/network/vyos/config/route_maps/route_maps.py b/plugins/module_utils/network/vyos/config/route_maps/route_maps.py index 9b6c3e9d..dca99d1c 100644 --- a/plugins/module_utils/network/vyos/config/route_maps/route_maps.py +++ b/plugins/module_utils/network/vyos/config/route_maps/route_maps.py @@ -19,7 +19,6 @@ created. """ -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) @@ -31,6 +30,13 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps import ( Route_mapsTemplate, ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps_14 import ( + Route_mapsTemplate14, +) +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 Route_maps(ResourceModule): @@ -59,6 +65,8 @@ class Route_maps(ResourceModule): "set_bgp_extcommunity_rt", "set_extcommunity_rt", "set_extcommunity_soo", + "set_extcommunity_bandwidth", + "set_extcommunity_bandwidth_non_transitive", "set_ip_next_hop", "set_ipv6_next_hop", "set_large_community", @@ -70,6 +78,7 @@ class Route_maps(ResourceModule): "set_src", "set_tag", "set_weight", + "set_table", "set_comm_list", "set_comm_list_delete", "set_community", @@ -89,15 +98,34 @@ class Route_maps(ResourceModule): "on_match_next", "match_ipv6_address", "match_ipv6_nexthop", + "match_protocol", "match_rpki", ] + def _validate_template(self): + version = get_os_version(self._module) + if LooseVersion(version) >= LooseVersion("1.4"): + self._tmplt = Route_mapsTemplate14() + else: + self._tmplt = Route_mapsTemplate() + + 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 """ + self._validate_template() if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() @@ -116,16 +144,16 @@ class Route_maps(ResourceModule): # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": - haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} + haved = {k: v for k, v in haved.items() if k in wantd or not wantd} wantd = {} # remove superfluous config for overridden and deleted if self.state in ["overridden", "deleted"]: - for k, have in iteritems(haved): + for k, have in haved.items(): if k not in wantd: self.commands.append(self._tmplt.render({"route_map": k}, "route_map", True)) - for wk, want in iteritems(wantd): + for wk, want in wantd.items(): self._compare(want=want, have=haved.pop(wk, {})) def _compare(self, want, have): @@ -139,13 +167,13 @@ class Route_maps(ResourceModule): self._compare_entries(want=w_entries, have=h_entries) def _compare_entries(self, want, have): - for wk, wentry in iteritems(want): + for wk, wentry in want.items(): hentry = have.pop(wk, {}) self.compare(parsers=self.parsers, want=wentry, have=hentry) def _route_maps_list_to_dict(self, entry): entry = {x["route_map"]: x for x in entry} - for rmap, data in iteritems(entry): + for rmap, data in entry.items(): if "entries" in data: for x in data["entries"]: x.update({"route_map": rmap}) diff --git a/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py b/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py index 9497d7fa..4bc5ddc4 100644 --- a/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py +++ b/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py @@ -20,7 +20,6 @@ created. import re -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) @@ -107,13 +106,13 @@ class Snmp_server(ResourceModule): """ self._compare_lists(want, have) self._compare_snmp_v3(want, have) - for key, value in iteritems(want): + for key, value in want.items(): self.compare( parsers=self.parsers, want={key: value}, have={key: have.pop(key, "")}, ) - for key, entry in iteritems(have): + for key, entry in have.items(): if entry: self.compare(parsers=self.parsers, want={}, have={key: entry}) @@ -125,10 +124,10 @@ class Snmp_server(ResourceModule): for attrib in parsers: wdict = get_from_dict(want, attrib) or {} hdict = get_from_dict(have, attrib) or {} - for key, entry in iteritems(wdict): + for key, entry in wdict.items(): # self.addcmd(entry, attrib, False) if attrib == "communities": - for k, v in iteritems(entry): + for k, v in entry.items(): if k in ["clients", "networks"]: v.sort() h = {} @@ -153,9 +152,9 @@ class Snmp_server(ResourceModule): ) have.pop(attrib, {}) # remove remaining items in have for replaced - for key, entry in iteritems(hdict): + for key, entry in hdict.items(): if attrib == "communities": - for k, v in iteritems(entry): + for k, v in entry.items(): if k != "name": self.compare( parsers="communities", @@ -188,9 +187,9 @@ class Snmp_server(ResourceModule): for attrib in attribute_dict.keys(): wattrib = get_from_dict(wdict, attrib) or {} hattrib = get_from_dict(hdict, attrib) or {} - for key, entry in iteritems(wattrib): + for key, entry in wattrib.items(): self._compare_snmp_v3_auth_privacy(entry, hattrib.get(key, {}), attrib) - for k, v in iteritems(entry): + for k, v in entry.items(): if k != attribute_dict[attrib]: h = {} if hattrib.get(key): @@ -217,11 +216,11 @@ class Snmp_server(ResourceModule): have=h, ) # remove remaining items in have for replaced - for key, entry in iteritems(hattrib): + for key, entry in hattrib.items(): self._compare_snmp_v3_auth_privacy({}, entry, attrib) self.compare(parsers=parsers, want={}, have={"snmp_v3": {attrib: entry}}) hdict.pop(attrib, {}) - for key, entry in iteritems(wdict): + for key, entry in wdict.items(): # self.addcmd(entry, attrib, False) self.compare( parsers="snmp_v3.engine_id", @@ -229,7 +228,7 @@ class Snmp_server(ResourceModule): have={"snmp_v3": {key: hdict.pop(key, {})}}, ) # remove remaining items in have for replaced - for key, entry in iteritems(hdict): + for key, entry in hdict.items(): self.compare(parsers=parsers, want={}, have={"snmp_v3": {key: entry}}) def _compare_snmp_v3_auth_privacy(self, wattrib, hattrib, attrib): @@ -244,7 +243,7 @@ class Snmp_server(ResourceModule): primary_key = "user" else: primary_key = "address" - for key, entry in iteritems(wattrib): + for key, entry in wattrib.items(): if key != primary_key and key in ["authentication", "privacy"]: self.compare( parsers=parsers, @@ -265,7 +264,7 @@ class Snmp_server(ResourceModule): }, }, ) - for key, entry in iteritems(hattrib): + for key, entry in hattrib.items(): if key != primary_key and key in ["authentication", "privacy"]: self.compare( parsers=parsers, @@ -291,13 +290,13 @@ class Snmp_server(ResourceModule): "views": "view", "trap_targets": "address", } - for k, v in iteritems(param_dict): + for k, v in param_dict.items(): if k in entry: a_dict = {} for el in entry[k]: a_dict.update({el[v]: el}) entry[k] = a_dict - for k, v in iteritems(v3_param_dict): + for k, v in v3_param_dict.items(): if entry.get("snmp_v3") and k in entry.get("snmp_v3"): a_dict = {} for el in entry["snmp_v3"][k]: 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 8451e7da..da8f8e3d 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 @@ -17,7 +17,6 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -182,7 +181,7 @@ class Static_routes(ConfigBase): """ commands = [] if have: - for key, value in iteritems(want): + for key, value in want.items(): if value: if key == "next_hops": commands.extend(self._update_next_hop(want, have)) @@ -257,7 +256,7 @@ class Static_routes(ConfigBase): """ commands = [] have = {} - for key, value in iteritems(want): + for key, value in want.items(): if value: if key == "dest": commands.append(self._compute_command(dest=want["dest"])) @@ -287,7 +286,7 @@ class Static_routes(ConfigBase): updates = dict_delete(want_blackhole, have_blackhole) if updates: - for attrib, value in iteritems(updates): + for attrib, value in updates.items(): if value: if attrib == "distance": commands.append( @@ -394,7 +393,7 @@ class Static_routes(ConfigBase): have_blackhole = have_copy.get(key) or {} updates = dict_delete(have_blackhole, want_blackhole) if updates: - for attrib, value in iteritems(updates): + for attrib, value in updates.items(): if value: if attrib == "distance": commands.append( @@ -508,7 +507,7 @@ class Static_routes(ConfigBase): commands.extend(self._add_next_hop(want, have, opr=opr)) if opr and updates: - for key, value in iteritems(updates): + for key, value in updates.items(): if value: if key == "blackhole_config": commands.extend(self._add_blackhole(key, want, have)) 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.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/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py index 3386bd66..31839c5d 100644 --- a/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py +++ b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py @@ -28,11 +28,11 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_template from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family_14 import ( Bgp_address_familyTemplate14, ) - +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_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import LooseVersion - class Bgp_address_familyFacts(object): """The vyos bgp_address_family facts class""" diff --git a/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py b/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py index dd793681..2883cc2d 100644 --- a/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py +++ b/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py @@ -25,15 +25,14 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global import ( Bgp_globalTemplate, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global_14 import ( Bgp_globalTemplate14, ) - +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_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import LooseVersion - class Bgp_globalFacts(object): """The vyos bgp_global facts class""" 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/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py index a46f8563..3a48889e 100644 --- a/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py +++ b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py @@ -179,7 +179,7 @@ class Firewall_globalFacts(object): if policies: rules_lst = [] for sp in set(policies): - sp_regex = r" %s .+$" % sp + sp_regex = r"^set firewall (?:global-options )?state-policy %s .+$" % sp cfg = "\n".join(findall(sp_regex, conf, M)) obj = self.parse_policies(cfg, sp) obj["connection_type"] = sp @@ -233,7 +233,7 @@ class Firewall_globalFacts(object): if groups: rules_lst = [] for gr in set(groups): - gr_regex = r" %s .+$" % gr + gr_regex = r"^set firewall group " + type + " %s .+$" % gr cfg = "\n".join(findall(gr_regex, conf, M)) if "ipv6" in type: # fmt: off diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py index a6b56345..f8f02e7a 100644 --- a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py @@ -61,12 +61,20 @@ class Firewall_rulesFacts(object): objs = [] # check 1.4+ first new_rules = True - v6_rules = findall(r"^set firewall ipv6 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", data, M) + v6_rules = findall( + r"^set firewall ipv6 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", + data, + M, + ) if not v6_rules: v6_rules = findall(r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M) if v6_rules: new_rules = False - v4_rules = findall(r"^set firewall ipv4 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", data, M) + v4_rules = findall( + r"^set firewall ipv4 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", + data, + M, + ) if not v4_rules: v4_rules = findall(r"^set firewall name (?:\'*)(\S+)(?:\'*)", data, M) if v4_rules: @@ -171,12 +179,18 @@ class Firewall_rulesFacts(object): :returns: The generated config """ conf = "\n".join(filter(lambda x: x, conf)) - a_lst = ["description", "default_action", "default_jump_target", "enable_default_log", "default_log"] + a_lst = [ + "description", + "default_action", + "default_jump_target", + "enable_default_log", + "default_log", + ] config = self.parse_attr(conf, a_lst, match) if not config: config = {} - if 'default_log' in config: - config['enable_default_log'] = config.pop('default_log') + if "default_log" in config: + config["enable_default_log"] = config.pop("default_log") config["rules"] = self.parse_rules_lst(conf) return config @@ -219,6 +233,7 @@ class Firewall_rulesFacts(object): "description", "icmp", "jump_target", + "offload_target", "queue", "queue_options", ] @@ -263,8 +278,8 @@ class Firewall_rulesFacts(object): found_lengths = findall(rule_regex, conf, M) if found_lengths: lengths = [] - for l in set(found_lengths): - obj = {"length": l.strip("'")} + for pplen in set(found_lengths): + obj = {"length": pplen.strip("'")} lengths.append(obj) return lengths @@ -389,21 +404,21 @@ class Firewall_rulesFacts(object): out = search(r"^.*" + regex + " (.+)", conf, M) if out: val = out.group(1).strip("'") - if attrib == 'type-name': - config['type_name'] = val - if attrib == 'code': - config['code'] = int(val) - if attrib == 'type': + if attrib == "type-name": + config["type_name"] = val + if attrib == "code": + config["code"] = int(val) + if attrib == "type": # <1.3 could be # (type), #/# (type/code) or 'type' (type_name) # recent this is only for strings if "/" in val: # type/code - (type_no, code) = val.split(".") - config['type'] = type_no - config['code'] = code + (type_no, code) = val.split("/") + config["type"] = int(type_no) + config["code"] = int(code) elif val.isnumeric(): - config['type'] = type_no + config["type"] = int(val) else: - config['type_name'] = val + config["type_name"] = val return config def parse_icmp(self, conf, attrib=None): @@ -414,7 +429,7 @@ class Firewall_rulesFacts(object): :return: generated config dictionary. """ cfg_dict = self.parse_icmp_attr(conf, "icmp") - if (len(cfg_dict) == 0): + if len(cfg_dict) == 0: cfg_dict = self.parse_icmp_attr(conf, "icmpv6") return cfg_dict @@ -458,7 +473,7 @@ class Firewall_rulesFacts(object): if not out: if attrib == "disable": out = search(r"^.*\d+" + " (disable$)", conf, M) - if attrib == 'log': + if attrib == "log": out = search(r"^.*\d+" + " (log$)", conf, M) if out: @@ -524,6 +539,6 @@ class Firewall_rulesFacts(object): val = out.group(1).strip("'") if "/" in val: # number/unit (number, unit) = val.split("/") - config['number'] = number - config['unit'] = unit + config["number"] = number + config["unit"] = unit return config 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 7d4d1a08..d62ea9b8 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 @@ -20,7 +20,6 @@ import re from copy import deepcopy -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import ( @@ -139,7 +138,7 @@ class L3_interfacesFacts(object): else: config["ipv6"].append({"address": item}) - for key, value in iteritems(config): + for key, value in config.items(): if value == []: config[key] = None diff --git a/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py b/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py index 8b60bef9..da80d967 100644 --- a/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py +++ b/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py @@ -15,7 +15,6 @@ for a given resource, parsed, and the facts tree is populated based on the configuration. """ -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.logging_global.logging_global import ( @@ -38,7 +37,7 @@ class Logging_globalFacts(object): def process_facts(self, objFinal): if objFinal: - for ke, vl in iteritems(objFinal): + for ke, vl in objFinal.items(): if ke == "files": _files = [] for k, v in vl.items(): diff --git a/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py b/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py index 2ad54e63..cfae26e3 100644 --- a/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py +++ b/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py @@ -25,6 +25,13 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.rou from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps import ( Route_mapsTemplate, ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps_14 import ( + Route_mapsTemplate14, +) +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 Route_mapsFacts(object): @@ -59,11 +66,18 @@ class Route_mapsFacts(object): """ facts = {} objs = [] + + if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): + route_maps_class = Route_mapsTemplate14 + else: + route_maps_class = Route_mapsTemplate + if not data: data = self.get_config(connection) # parse native config using the Route_maps template - route_maps_parser = Route_mapsTemplate(lines=data.splitlines()) + route_maps_parser = route_maps_class(lines=data.splitlines()) + if route_maps_parser.parse().get("route_maps"): objs = list(route_maps_parser.parse().get("route_maps").values()) for item in objs: diff --git a/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py b/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py index e70a15f9..d961a0ff 100644 --- a/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py +++ b/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py @@ -17,7 +17,6 @@ based on the configuration. import re -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.snmp_server.snmp_server import ( @@ -63,7 +62,7 @@ class Snmp_serverFacts(object): if objs: if "communities" in objs: for k in objs["communities"].values(): - for param, val in iteritems(k): + for param, val in k.items(): if param in ["clients", "networks"]: if None in val: val.remove(None) diff --git a/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py index 99b3917b..710d91fe 100644 --- a/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py +++ b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py @@ -164,9 +164,9 @@ class Static_routesFacts(object): elif dis >= 1: nh_info["enabled"] = False for element in nh_list: - if element["forward_router_address"] == nh_info["forward_router_address"]: - if "interface" in nh_info.keys(): - element["interface"] = nh_info["interface"] + if element.get("forward_router_address") == nh_info.get( + "forward_router_address", + ): if "admin_distance" in nh_info.keys(): element["admin_distance"] = nh_info["admin_distance"] if "enabled" in nh_info.keys(): @@ -174,4 +174,5 @@ class Static_routesFacts(object): nh_info = None if nh_info is not None: nh_list.append(nh_info) + nh_info = {} return nh_list 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/bgp_address_family.py b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py index f8f86cd2..fccfc9c2 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py @@ -293,8 +293,8 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+aggregate-address \s+(?P<address>\S+) - \s*(?P<as_set>as-set)* - \s*(?P<summary_only>summary-only)* + \s*(?P<as_set>as-set)? + \s*(?P<summary_only>summary-only)? $""", re.VERBOSE, ), @@ -397,8 +397,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+network \s+(?P<address>\S+) \s+path-limit - \s+(?P<limit>\S+) - *$""", + \s+(?P<limit>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, @@ -432,8 +431,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+network \s+(?P<address>\S+) \s+route-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, @@ -499,8 +497,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+redistribute \s+(?P<proto>\S+) \s+metric - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -534,8 +531,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+redistribute \s+(?P<proto>\S+) \s+route-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -568,8 +564,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+redistribute \s+table - \s+(?P<tab>\S+) - *$""", + \s+(?P<tab>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -659,8 +654,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+allowas-in \s+number - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -877,8 +871,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+capability \s+prefix-list - \s+(?P<orf>\S+) - *$""", + \s+(?P<orf>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -915,8 +908,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+default-originate \s+route-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -992,8 +984,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+prefix-list \s+(?P<action>export|import) - \s+(?P<list>\S+) - *$""", + \s+(?P<list>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_prefix_list, @@ -1033,8 +1024,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+filter-list \s+(?P<action>export|import) - \s+(?P<list>\S+) - *$""", + \s+(?P<list>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_filter_list, @@ -1073,8 +1063,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+address-family \s+(?P<afi>\S+)-unicast \s+maximum-prefix - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1176,8 +1165,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+address-family \s+(?P<afi>\S+)-unicast \s+peer-group - \s+(?P<name>\S+) - *$""", + \s+(?P<name>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1246,8 +1234,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+route-map \s+(?P<action>export|import) - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_route_map, @@ -1389,8 +1376,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+address-family \s+(?P<afi>\S+)-unicast \s+unsuppress-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1424,8 +1410,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+address-family \s+(?P<afi>\S+)-unicast \s+weight - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py index fd4c9de9..09cd41a7 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py @@ -309,8 +309,8 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+aggregate-address \s+(?P<address>\S+) - \s*(?P<as_set>as-set)* - \s*(?P<summary_only>summary-only)* + \s*(?P<as_set>as-set)? + \s*(?P<summary_only>summary-only)? $""", re.VERBOSE, ), @@ -410,8 +410,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+network \s+(?P<address>\S+) \s+path-limit - \s+(?P<limit>\S+) - *$""", + \s+(?P<limit>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, @@ -444,8 +443,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+network \s+(?P<address>\S+) \s+route-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, @@ -508,8 +506,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+redistribute \s+(?P<proto>\S+) - \s+metric\s+(?P<val>\S+) - *$""", + \s+metric\s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -542,8 +539,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+redistribute \s+(?P<proto>\S+) \s+route-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -575,8 +571,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+redistribute \s+table - \s+(?P<tab>\S+) - *$""", + \s+(?P<tab>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -663,8 +658,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+allowas-in \s+number - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -875,8 +869,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+capability \s+prefix-list - \s+(?P<orf>\S+) - *$""", + \s+(?P<orf>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -912,8 +905,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+default-originate \s+route-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -987,8 +979,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+prefix-list \s+(?P<action>export|import) - \s+(?P<list>\S+) - *$""", + \s+(?P<list>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_prefix_list, @@ -1027,8 +1018,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+filter-list \s+(?P<action>export|import) - \s+(?P<list>\S+) - *$""", + \s+(?P<list>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_filter_list, @@ -1066,8 +1056,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+address-family \s+(?P<afi>\S+)-unicast \s+maximum-prefix - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1166,8 +1155,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+address-family \s+(?P<afi>\S+)-unicast \s+peer-group - \s+(?P<name>\S+) - *$""", + \s+(?P<name>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1234,8 +1222,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P<afi>\S+)-unicast \s+route-map \s+(?P<action>export|import) - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_route_map, @@ -1373,8 +1360,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+address-family \s+(?P<afi>\S+)-unicast \s+unsuppress-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1407,8 +1393,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+address-family \s+(?P<afi>\S+)-unicast \s+weight - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_global.py b/plugins/module_utils/network/vyos/rm_templates/bgp_global.py index 621f65ea..03f6265b 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_global.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_global.py @@ -17,7 +17,6 @@ the given network resource. import re -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) @@ -26,7 +25,7 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.r def _tmplt_bgp_params_confederation(config_data): command = [] for list_el in config_data["bgp_params"]["confederation"]: - for k, v in iteritems(list_el): + for k, v in list_el.items(): command.append( "protocols bgp {as_number} parameters confederation ".format(**config_data) + k @@ -65,7 +64,7 @@ def _tmplt_bgp_params_default(config_data): def _tmplt_bgp_neighbor_timers(config_data): command = [] - for k, v in iteritems(config_data["neighbor"]["timers"]): + for k, v in config_data["neighbor"]["timers"].items(): command.append( "protocols bgp {as_number} neighbor ".format(**config_data) + config_data["neighbor"]["address"] @@ -80,7 +79,7 @@ def _tmplt_bgp_neighbor_timers(config_data): def _tmplt_bgp_timers(config_data): command = [] - for k, v in iteritems(config_data["timers"]): + for k, v in config_data["timers"].items(): command.append( "protocols bgp {as_number} ".format(**config_data) + "timers " + k + " " + str(v), ) @@ -238,8 +237,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+advertisement-interval - \s+(?P<interval>\S+) - *$""", + \s+(?P<interval>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} advertisement-interval {{ neighbor.advertisement_interval }}", @@ -438,8 +436,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+capability \s+orf \s+prefix-list - \s+(?P<orf>\S+) - *$""", + \s+(?P<orf>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} capability orf prefix-list {{ neighbor.capability.orf }}", @@ -468,8 +465,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P<address>\S+) \s+default-originate \s+route-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} default-originate route-map {{ neighbor.default_originate }}", @@ -495,8 +491,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+description - \s+(?P<desc>\S+) - *$""", + \s+(?P<desc>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} description {{ neighbor.description }}", @@ -574,8 +569,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+disable-send-community - \s+(?P<comm>\S+) - *$""", + \s+(?P<comm>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} disable-send-community {{ neighbor.disable_send_community }}", @@ -634,8 +628,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+ebgp-multihop - \s+(?P<hop>\S+) - *$""", + \s+(?P<hop>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} ebgp-multihop {{ neighbor.ebgp_multihop }}", @@ -662,8 +655,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P<address>\S+) \s+filter-list \s+(?P<action>export|import) - \s+(?P<list>\S+) - *$""", + \s+(?P<list>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_filter_list, @@ -722,8 +714,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+maximum-prefix - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} maximum-prefix {{ neighbor.maximum_prefix }}", @@ -827,8 +818,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+password - \s+(?P<pwd>\S+) - *$""", + \s+(?P<pwd>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} password {{ neighbor.password }}", @@ -854,8 +844,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+peer-group - \s+(?P<name>\S+) - *$""", + \s+(?P<name>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} peer-group {{ neighbor.peer_group_name }}", @@ -881,8 +870,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+port - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} port {{ neighbor.port }}", @@ -909,8 +897,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P<address>\S+) \s+prefix-list \s+(?P<action>export|import) - \s+(?P<list>\S+) - *$""", + \s+(?P<list>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_prefix_list, @@ -941,8 +928,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+remote-as - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} remote-as {{ neighbor.remote_as }}", @@ -995,8 +981,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P<address>\S+) \s+route-map \s+(?P<action>export|import) - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_route_map, @@ -1158,8 +1143,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+unsuppress-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} unsuppress-map {{ neighbor.unsuppress_map }}", @@ -1185,8 +1169,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+update-source - \s+(?P<src>\S+) - *$""", + \s+(?P<src>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} update-source {{ neighbor.update_source }}", @@ -1212,8 +1195,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+weight - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} weight {{ neighbor.weight }}", @@ -1239,8 +1221,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+ttl-security - \s+(?P<ttl>\S+) - *$""", + \s+(?P<ttl>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} ttl-security {{ neighbor.ttl_security }}", @@ -1267,8 +1248,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P<address>\S+) \s+timers \s+(?P<type>connect|holdtime|keepalive) - \s+(?P<sec>\S+) - *$""", + \s+(?P<sec>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_timers, @@ -1296,8 +1276,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P<as_num>\d+) \s+timers \s+(?P<type>\S+) - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_timers, @@ -1419,8 +1398,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P<as_num>\d+) \s+parameters \s+cluster-id - \s+(?P<id>\S+) - *$""", + \s+(?P<id>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters cluster-id {{ bgp_params.cluster_id }}", @@ -1443,8 +1421,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+confederation \s+(?P<type>identifier|peers) - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_confederation, @@ -1472,8 +1449,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+dampening \s+half-life - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening half-life {{ bgp_params.dampening.half_life}}", @@ -1498,8 +1474,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+dampening \s+max-suppress-time - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening max-suppress-time {{ bgp_params.dampening.max_suppress_time}}", @@ -1524,8 +1499,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+dampening \s+re-use - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening re-use {{ bgp_params.dampening.re_use}}", @@ -1550,8 +1524,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+dampening \s+start-suppress-time - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening start-suppress-time {{ bgp_params.dampening.start_suppress_time}}", @@ -1575,9 +1548,8 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P<as_num>\d+) \s+parameters \s+default - \s*(?P<no_ipv4_unicast>no-ipv4-unicast)* - \s*(?P<local_pref>local-pref\s\S+) - *$""", + \s*(?P<no_ipv4_unicast>no-ipv4-unicast)? + \s*(?P<local_pref>local-pref\s\S+)?\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_default, @@ -1649,8 +1621,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+distance\sprefix \s+(?P<prefix>\S+) \s+distance - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters distance prefix {{ bgp_params.distance.prefix }} distance {{ bgp_params.distance.value }}", @@ -1679,8 +1650,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+distance\sglobal \s+(?P<type>\S+) - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_distance, @@ -1730,8 +1700,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P<as_num>\d+) \s+parameters \s+graceful-restart\s+stalepath-time - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters graceful-restart stalepath-time {{ bgp_params.graceful_restart }}", @@ -1819,8 +1788,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P<as_num>\d+) \s+parameters \s+router-id - \s+(?P<id>\S+) - *$""", + \s+(?P<id>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters router-id {{ bgp_params.router_id }}", diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py b/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py index b8beb923..cbe21e3a 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py @@ -17,7 +17,6 @@ the given network resource. import re -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) @@ -26,12 +25,9 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.r def _tmplt_bgp_params_confederation(config_data): command = [] for list_el in config_data["bgp_params"]["confederation"]: - for k, v in iteritems(list_el): + for k, v in list_el.items(): command.append( - "protocols bgp parameters confederation ".format(**config_data) - + k - + " " - + str(v), + "protocols bgp parameters confederation ".format(**config_data) + k + " " + str(v), ) return command @@ -65,7 +61,7 @@ def _tmplt_bgp_params_default(config_data): def _tmplt_bgp_neighbor_timers(config_data): command = [] - for k, v in iteritems(config_data["neighbor"]["timers"]): + for k, v in config_data["neighbor"]["timers"].items(): command.append( "protocols bgp neighbor ".format(**config_data) + config_data["neighbor"]["address"] @@ -80,7 +76,7 @@ def _tmplt_bgp_neighbor_timers(config_data): def _tmplt_bgp_timers(config_data): command = [] - for k, v in iteritems(config_data["timers"]): + for k, v in config_data["timers"].items(): command.append( "protocols bgp ".format(**config_data) + "timers " + k + " " + str(v), ) @@ -238,8 +234,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+advertisement-interval - \s+(?P<interval>\S+) - *$""", + \s+(?P<interval>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} advertisement-interval {{ neighbor.advertisement_interval }}", @@ -431,8 +426,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+capability \s+orf \s+prefix-list - \s+(?P<orf>\S+) - *$""", + \s+(?P<orf>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} capability orf prefix-list {{ neighbor.capability.orf }}", @@ -460,8 +454,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+(?P<address>\S+) \s+default-originate \s+route-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} default-originate route-map {{ neighbor.default_originate }}", @@ -486,8 +479,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+description - \s+(?P<desc>\S+) - *$""", + \s+(?P<desc>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} description {{ neighbor.description }}", @@ -562,8 +554,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+disable-send-community - \s+(?P<comm>\S+) - *$""", + \s+(?P<comm>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} disable-send-community {{ neighbor.disable_send_community }}", @@ -620,8 +611,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+ebgp-multihop - \s+(?P<hop>\S+) - *$""", + \s+(?P<hop>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} ebgp-multihop {{ neighbor.ebgp_multihop }}", @@ -647,8 +637,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+(?P<address>\S+) \s+filter-list \s+(?P<action>export|import) - \s+(?P<list>\S+) - *$""", + \s+(?P<list>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_filter_list, @@ -705,8 +694,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+maximum-prefix - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} maximum-prefix {{ neighbor.maximum_prefix }}", @@ -806,8 +794,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+password - \s+(?P<pwd>\S+) - *$""", + \s+(?P<pwd>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} password {{ neighbor.password }}", @@ -832,8 +819,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+peer-group - \s+(?P<name>\S+) - *$""", + \s+(?P<name>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} peer-group {{ neighbor.peer_group_name }}", @@ -858,8 +844,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+port - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} port {{ neighbor.port }}", @@ -885,8 +870,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+(?P<address>\S+) \s+prefix-list \s+(?P<action>export|import) - \s+(?P<list>\S+) - *$""", + \s+(?P<list>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_prefix_list, @@ -916,8 +900,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+remote-as - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} remote-as {{ neighbor.remote_as }}", @@ -968,8 +951,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+(?P<address>\S+) \s+route-map \s+(?P<action>export|import) - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_route_map, @@ -1125,8 +1107,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+unsuppress-map - \s+(?P<map>\S+) - *$""", + \s+(?P<map>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} unsuppress-map {{ neighbor.unsuppress_map }}", @@ -1151,8 +1132,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+update-source - \s+(?P<src>\S+) - *$""", + \s+(?P<src>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} update-source {{ neighbor.update_source }}", @@ -1177,8 +1157,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+weight - \s+(?P<num>\S+) - *$""", + \s+(?P<num>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} weight {{ neighbor.weight }}", @@ -1203,8 +1182,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P<address>\S+) \s+ttl-security - \s+(?P<ttl>\S+) - *$""", + \s+(?P<ttl>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} ttl-security {{ neighbor.ttl_security }}", @@ -1230,8 +1208,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+(?P<address>\S+) \s+timers \s+(?P<type>connect|holdtime|keepalive) - \s+(?P<sec>\S+) - *$""", + \s+(?P<sec>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_timers, @@ -1258,8 +1235,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+bgp \s+timers \s+(?P<type>\S+) - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_timers, @@ -1376,8 +1352,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+bgp \s+parameters \s+cluster-id - \s+(?P<id>\S+) - *$""", + \s+(?P<id>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters cluster-id {{ bgp_params.cluster_id }}", @@ -1399,8 +1374,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+confederation \s+(?P<type>identifier|peers) - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_confederation, @@ -1427,8 +1401,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+dampening \s+half-life - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening half-life {{ bgp_params.dampening.half_life}}", @@ -1452,8 +1425,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+dampening \s+max-suppress-time - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening max-suppress-time {{ bgp_params.dampening.max_suppress_time}}", @@ -1477,8 +1449,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+dampening \s+re-use - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening re-use {{ bgp_params.dampening.re_use}}", @@ -1502,8 +1473,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+dampening \s+start-suppress-time - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening start-suppress-time {{ bgp_params.dampening.start_suppress_time}}", @@ -1526,9 +1496,8 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+bgp \s+parameters \s+default - \s*(?P<no_ipv4_unicast>no-ipv4-unicast)* - \s*(?P<local_pref>local-pref\s\S+) - *$""", + \s*(?P<no_ipv4_unicast>no-ipv4-unicast)? + \s*(?P<local_pref>local-pref\s\S+)?\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_default, @@ -1597,8 +1566,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+distance\sprefix \s+(?P<prefix>\S+) \s+distance - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters distance prefix {{ bgp_params.distance.prefix }} distance {{ bgp_params.distance.value }}", @@ -1626,8 +1594,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+distance\sglobal \s+(?P<type>\S+) - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_distance, @@ -1675,8 +1642,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+bgp \s+parameters \s+graceful-restart\s+stalepath-time - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters graceful-restart stalepath-time {{ bgp_params.graceful_restart }}", @@ -1760,8 +1726,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+bgp \s+parameters \s+router-id - \s+(?P<id>\S+) - *$""", + \s+(?P<id>\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters router-id {{ bgp_params.router_id }}", diff --git a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py index 134effca..af04da3b 100644 --- a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py +++ b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py @@ -365,8 +365,7 @@ class Ospf_interfacesTemplate(NetworkTemplate): \s+(?P<proto>ospf|ospfv3) \s+authentication \s+plaintext-password - \s+(?P<text>\S+) - *$""", + \s+(?P<text>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_password, @@ -399,8 +398,7 @@ class Ospf_interfacesTemplate(NetworkTemplate): \s+key-id \s+(?P<id>\d+) \s+md5-key - \s+(?P<text>\S+) - *$""", + \s+(?P<text>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_md5, @@ -572,8 +570,7 @@ class Ospf_interfacesTemplate(NetworkTemplate): \s+(?P<afi>ip|ipv6) \s+(?P<proto>ospf|ospfv3) \s+network - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_network, diff --git a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py index 43fae1e9..484d7c44 100644 --- a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py +++ b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function + __metaclass__ = type """ @@ -239,8 +240,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate): \s+protocols \s+(?P<proto>ospf|ospfv3) \s+interface - \s+(?P<name>\S+) - *$""", + \s+(?P<name>\S+)\s*$""", re.VERBOSE, ), "remval": _tmplt_ospf_int_delete, @@ -265,8 +265,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate): \s+(?P<name>\S+) \s+authentication \s+plaintext-password - \s+(?P<text>\S+) - *$""", + \s+(?P<text>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_password, @@ -297,8 +296,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate): \s+key-id \s+(?P<id>\d+) \s+md5-key - \s+(?P<text>\S+) - *$""", + \s+(?P<text>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_md5, @@ -458,8 +456,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate): \s+interface \s+(?P<name>\S+) \s+network - \s+(?P<val>\S+) - *$""", + \s+(?P<val>\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_network, diff --git a/plugins/module_utils/network/vyos/rm_templates/route_maps.py b/plugins/module_utils/network/vyos/rm_templates/route_maps.py index 8f218a6b..51d0ea1c 100644 --- a/plugins/module_utils/network/vyos/rm_templates/route_maps.py +++ b/plugins/module_utils/network/vyos/rm_templates/route_maps.py @@ -33,8 +33,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "route_map", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\s*$""", re.VERBOSE, ), "compval": "route_map", @@ -51,8 +50,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "sequence", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\s*$""", re.VERBOSE, ), "compval": "sequence", @@ -75,8 +73,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "call", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scall\s(?P<call>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scall\s(?P<call>\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} call {{call}}", @@ -99,8 +96,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "description", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sdescription\s(?P<description>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sdescription\s(?P<description>\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} description {{description}}", @@ -123,8 +119,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "action", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\saction\s(?P<action>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\saction\s(?P<action>\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} action {{action}}", @@ -147,8 +142,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "continue_sequence", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scontinue\s(?P<continue>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scontinue\s(?P<continue>\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} continue {{continue_sequence}}", @@ -171,8 +165,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "on_match_next", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\s(?P<next>next) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\s(?P<next>next)\s*$""", re.VERBOSE, ), "compval": "on_match.next", @@ -198,8 +191,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "on_match_goto", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\sgoto\s(?P<goto>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\sgoto\s(?P<goto>\S+)\s*$""", re.VERBOSE, ), "compval": "on_match.goto", @@ -225,8 +217,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_aggregator_ip", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sip\s(?P<ip>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sip\s(?P<ip>\S+)\s*$""", re.VERBOSE, ), "compval": "set.aggregator.ip", @@ -254,8 +245,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_aggregator_as", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sas\s(?P<as>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sas\s(?P<as>\S+)\s*$""", re.VERBOSE, ), "compval": "set.aggregator.as", @@ -283,8 +273,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_as_path_exclude", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path-exclude\s(?P<as>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path-exclude\s(?P<as>\S+)\s*$""", re.VERBOSE, ), "compval": "set.as_path_exclude", @@ -310,12 +299,12 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_as_path_prepend", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path-prepend\s(?P<as>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path-prepend\s(?P<as>.*) + $""", re.VERBOSE, ), "compval": "set.as_path_prepend", - "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path-prepend {{set.as_path_prepend}}", + "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path-prepend '{{set.as_path_prepend}}'", "result": { "route_maps": { "{{ route_map }}": { @@ -337,10 +326,10 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_atomic_aggregate", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\satomic-aggregate(?P<as>) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\s(?P<as>atomic-aggregate)\s*$""", re.VERBOSE, ), + "compval": "set.atomic_aggregate", "setval": "policy route-map {{route_map}} rule {{sequence}} set atomic-aggregate", "result": { "route_maps": { @@ -363,8 +352,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_bgp_extcommunity_rt", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sbgp-extcommunity-rt\s(?P<bgp>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sbgp-extcommunity-rt\s(?P<bgp>\S+)\s*$""", re.VERBOSE, ), "compval": "set.bgp_extcommunity_rt", @@ -391,13 +379,12 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_comm_list", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scomm-list\scomm-list\s(?P<comm_list>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<comm_list>\S+)\s*$""", re.VERBOSE, ), - "compval": "set.comm_list.comm_list", + "compval": "match.community.community_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " - "set comm-list comm-list {{set.comm_list.comm_list}}", + "match community community-list {{set.comm_list.comm_list}}", "result": { "route_maps": { "{{ route_map }}": { @@ -406,8 +393,8 @@ class Route_mapsTemplate(NetworkTemplate): "{{sequence}}": { "sequence": "{{sequence}}", - "set": { - "comm_list": {"comm_list": "{{comm_list}}"}, + "match": { + "community": {"community_list": "{{comm_list}}"}, }, }, }, @@ -419,8 +406,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_comm_list_delete", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scomm-list\sdelete(?P<delete>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scomm-list\s(?P<delete>delete)\s*$""", re.VERBOSE, ), "compval": "set.comm_list.comm_list", @@ -447,8 +433,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_extcommunity_rt", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity-rt\s(?P<extcommunity_rt>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity-rt\s(?P<extcommunity_rt>\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_rt", @@ -475,8 +460,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_extcommunity_soo", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity-soo\s(?P<extcommunity_soo>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity-soo\s(?P<extcommunity_soo>\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_soo", @@ -500,11 +484,65 @@ class Route_mapsTemplate(NetworkTemplate): }, }, { + "name": "set_extcommunity_bandwidth", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\sbandwidth\s(?P<extcommunity_bw>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.extcommunity_bandwidth", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set extcommunity bandwidth {{set.extcommunity_bandwidth}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "extcommunity_bandwidth": "{{extcommunity_bw}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_extcommunity_bandwidth_non_transitive", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+) + \sset\sextcommunity\s(?P<extcommunity_bw_nt>bandwidth-non-transitive)\s*$""", + re.VERBOSE, + ), + "compval": "set.extcommunity_bandwidth_non_transitive", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set extcommunity bandwidth-non-transitive", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "extcommunity_bandwidth_non_transitive": "{{True if extcommunity_bw_nt is defined}}", + }, + }, + }, + }, + }, + }, + }, + { "name": "set_ip_next_hop", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sip-next-hop\s(?P<ip_next_hop>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sip-next-hop\s(?P<ip_next_hop>\S+)\s*$""", re.VERBOSE, ), "compval": "set.ip_next_hop", @@ -533,8 +571,7 @@ class Route_mapsTemplate(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sipv6-next-hop \s(?P<type>global|local) - \s(?P<value>\S+) - *$""", + \s(?P<value>\S+)\s*$""", re.VERBOSE, ), "compval": "set.ipv6_next_hop", @@ -564,8 +601,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_large_community", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slarge-community\s(?P<large_community>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slarge-community\s(?P<large_community>\S+)\s*$""", re.VERBOSE, ), "compval": "set.large_community", @@ -592,8 +628,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_local_preference", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slocal-preference\s(?P<local_preference>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slocal-preference\s(?P<local_preference>\S+)\s*$""", re.VERBOSE, ), "compval": "set.local_preference", @@ -620,8 +655,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_metric", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric\s(?P<metric>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric\s(?P<metric>\S+)\s*$""", re.VERBOSE, ), "compval": "set.metric", @@ -648,8 +682,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_metric_type", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric-type\s(?P<metric_type>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric-type\s(?P<metric_type>\S+)\s*$""", re.VERBOSE, ), "compval": "set.metric_type", @@ -676,8 +709,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_origin", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sorigin\s(?P<origin>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sorigin\s(?P<origin>\S+)\s*$""", re.VERBOSE, ), "compval": "set.origin", @@ -704,8 +736,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_originator_id", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\soriginator-id\s(?P<originator_id>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\soriginator-id\s(?P<originator_id>\S+)\s*$""", re.VERBOSE, ), "compval": "set.originator_id", @@ -732,8 +763,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_src", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\ssrc\s(?P<src>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\ssrc\s(?P<src>\S+)\s*$""", re.VERBOSE, ), "compval": "set.src", @@ -760,8 +790,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_tag", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stag\s(?P<tag>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stag\s(?P<tag>\S+)\s*$""", re.VERBOSE, ), "compval": "set.tag", @@ -788,8 +817,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_weight", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sweight\s(?P<weight>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sweight\s(?P<weight>\S+)\s*$""", re.VERBOSE, ), "compval": "set.weight", @@ -813,11 +841,37 @@ class Route_mapsTemplate(NetworkTemplate): }, }, { + "name": "set_table", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stable\s(?P<table>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.weight", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set table {{set.table}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "table": "{{table}}", + }, + }, + }, + }, + }, + }, + }, + { "name": "set_community", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scommunity\s(?P<value>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scommunity\s(?P<value>\S+)\s*$""", re.VERBOSE, ), "compval": "set.community.value", @@ -846,8 +900,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_as_path", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sas-path\s(?P<as_path>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sas-path\s(?P<as_path>\S+)\s*$""", re.VERBOSE, ), "compval": "match.as_path", @@ -874,8 +927,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_community_community_list", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<community_list>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<community_list>\S+)\s*$""", re.VERBOSE, ), "compval": "match.community.community_list", @@ -902,8 +954,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_community_exact_match", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\sexact-match(?P<exact_match>) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\sexact-match(?P<exact_match>)\s*$""", re.VERBOSE, ), "compval": "match.community.exact_match", @@ -930,8 +981,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_extcommunity", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sextcommunity\s(?P<extcommunity>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sextcommunity\s(?P<extcommunity>\S+)\s*$""", re.VERBOSE, ), "compval": "match.extcommunity", @@ -958,8 +1008,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_interface", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sinterface\s(?P<interface>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sinterface\s(?P<interface>\S+)\s*$""", re.VERBOSE, ), "compval": "match.interface", @@ -986,8 +1035,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_large_community_large_community_list", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\slarge-community\slarge-community-list\s(?P<lc>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\slarge-community\slarge-community-list\s(?P<lc>\S+)\s*$""", re.VERBOSE, ), "compval": "match.large_community_large_community_list", @@ -1014,8 +1062,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_metric", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\smetric\s(?P<metric>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\smetric\s(?P<metric>\S+)\s*$""", re.VERBOSE, ), "compval": "match.metric", @@ -1042,8 +1089,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_origin", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sorigin\s(?P<origin>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sorigin\s(?P<origin>\S+)\s*$""", re.VERBOSE, ), "compval": "match.origin", @@ -1070,8 +1116,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_peer", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\speer\s(?P<peer>\S+) - *$""", + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\speer\s(?P<peer>\S+)\s*$""", re.VERBOSE, ), "compval": "match.peer", @@ -1100,8 +1145,7 @@ class Route_mapsTemplate(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\saddress \s(?P<list_type>access-list|prefix-list) - \s(?P<value>\S+) - *$""", + \s(?P<value>\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.address", @@ -1134,8 +1178,7 @@ class Route_mapsTemplate(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\snexthop \s(?P<list_type>access-list|prefix-list) - \s(?P<value>\S+) - *$""", + \s(?P<value>\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.next_hop", @@ -1168,8 +1211,7 @@ class Route_mapsTemplate(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\sroute-source \s(?P<list_type>access-list|prefix-list) - \s(?P<value>\S+) - *$""", + \s(?P<value>\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.route_source", @@ -1202,8 +1244,7 @@ class Route_mapsTemplate(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sipv6\saddress \s(?P<list_type>access-list|prefix-list) - \s(?P<value>\S+) - *$""", + \s(?P<value>\S+)\s*$""", re.VERBOSE, ), "compval": "match.ipv6.address", @@ -1235,8 +1276,7 @@ class Route_mapsTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sipv6\snexthop - \s(?P<value>\S+) - *$""", + \s(?P<value>\S+)\s*$""", re.VERBOSE, ), "compval": "match.ipv6.next_hop", @@ -1261,12 +1301,37 @@ class Route_mapsTemplate(NetworkTemplate): }, }, { + "name": "match_protocol", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sprotocol\s(?P<value>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.protocol", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match protocol {{match.protocol}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": { + "sequence": "{{sequence}}", + "match": { + "protocol": "{{value}}", + }, + }, + }, + }, + }, + }, + }, + { "name": "match_rpki", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\srpki - \s(?P<value>\S+) - *$""", + \s(?P<value>\S+)\s*$""", re.VERBOSE, ), "compval": "match.rpki", diff --git a/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py b/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py new file mode 100644 index 00000000..ea61a555 --- /dev/null +++ b/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py @@ -0,0 +1,1364 @@ +# -*- 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 Route_maps 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 Route_mapsTemplate14(NetworkTemplate): + def __init__(self, lines=None): + prefix = {"set": "set", "remove": "delete"} + super(Route_mapsTemplate14, self).__init__(lines=lines, tmplt=self, prefix=prefix) + + # fmt: off + PARSERS = [ + { + "name": "route_map", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "route_map", + "setval": "policy route-map {{route_map}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + }, + }, + }, + }, + { + "name": "sequence", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+) + *$""", + re.VERBOSE, + ), + "compval": "sequence", + "setval": "policy route-map {{route_map}} rule {{sequence}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + }, + }, + }, + }, + }, + }, + { + "name": "call", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scall\s(?P<call>\S+)\s*$""", + re.VERBOSE, + ), + "setval": "policy route-map {{route_map}} rule {{sequence}} call {{call}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "call": "{{call}}", + }, + }, + }, + }, + }, + }, + { + "name": "description", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sdescription\s(?P<description>\S+)\s*$""", + re.VERBOSE, + ), + "setval": "policy route-map {{route_map}} rule {{sequence}} description {{description}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "description": "{{description}}", + }, + }, + }, + }, + }, + }, + { + "name": "action", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\saction\s(?P<action>\S+)\s*$""", + re.VERBOSE, + ), + "setval": "policy route-map {{route_map}} rule {{sequence}} action {{action}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "action": "{{action}}", + }, + }, + }, + }, + }, + }, + { + "name": "continue_sequence", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scontinue\s(?P<continue>\S+)\s*$""", + re.VERBOSE, + ), + "setval": "policy route-map {{route_map}} rule {{sequence}} continue {{continue_sequence}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "continue_sequence": "{{continue}}", + }, + }, + }, + }, + }, + }, + { + "name": "on_match_next", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\s(?P<next>next) + *$""", + re.VERBOSE, + ), + "compval": "on_match.next", + "setval": "policy route-map {{route_map}} rule {{sequence}} on-match next", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "on_match": { + "next": "{{True if next is defined}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "on_match_goto", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\sgoto\s(?P<goto>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "on_match.goto", + "setval": "policy route-map {{route_map}} rule {{sequence}} on-match goto {{on_match.goto}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "on_match": { + "goto": "{{goto}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_aggregator_ip", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sip\s(?P<ip>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.aggregator.ip", + "setval": "policy route-map {{route_map}} rule {{sequence}} set aggregator ip {{set.aggregator.ip}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "aggregator": { + "ip": "{{ip}}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_aggregator_as", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sas\s(?P<as>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.aggregator.as", + "setval": "policy route-map {{route_map}} rule {{sequence}} set aggregator as {{set.aggregator.as}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "aggregator": { + "as": "{{as}}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_as_path_exclude", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path\sexclude\s(?P<as>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.as_path_exclude", + "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path exclude {{set.as_path_exclude}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "as_path_exclude": "{{as}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_as_path_prepend", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path\sprepend\s(?P<as>.*) + $""", + re.VERBOSE, + ), + "compval": "set.as_path_prepend", + "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path prepend '{{set.as_path_prepend}}'", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "as_path_prepend": "{{as}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_atomic_aggregate", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\s(?P<as>atomic-aggregate) + *$""", + re.VERBOSE, + ), + "compval": "set.atomic_aggregate", + "setval": "policy route-map {{route_map}} rule {{sequence}} set atomic-aggregate", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "atomic_aggregate": "{{True if as is defined}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_bgp_extcommunity_rt", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sbgp-extcommunity-rt\s(?P<bgp>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.bgp_extcommunity_rt", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set bgp-extcommunity-rt {{set.bgp_extcommunity_rt}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "bgp_extcommunity_rt": "{{bgp}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_comm_list", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<comm_list>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.community.community_list", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match community community-list {{set.comm_list.comm_list}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "match": { + "community": {"community_list": "{{comm_list}}"}, + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_comm_list_delete", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scomm-list\s(?P<delete>delete)\s*$""", + re.VERBOSE, + ), + "compval": "set.comm_list.comm_list", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set comm-list delete", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "comm_list": {"delete": "{{True if delete is defined}}"}, + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_extcommunity_rt", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\srt\s(?P<extcommunity_rt>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.extcommunity_rt", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set extcommunity rt {{set.extcommunity_rt}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "extcommunity_rt": "{{extcommunity_rt}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_extcommunity_soo", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\ssoo\s(?P<extcommunity_soo>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.extcommunity_soo", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set extcommunity soo {{set.extcommunity_soo}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "extcommunity_soo": "{{extcommunity_soo}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_extcommunity_bandwidth", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\sbandwidth\s(?P<extcommunity_bw>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.extcommunity_bandwidth", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set extcommunity bandwidth {{set.extcommunity_bandwidth}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "extcommunity_bandwidth": "{{extcommunity_bw}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_extcommunity_bandwidth_non_transitive", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\s(?P<extcommunity_bw_nt>bandwidth-non-transitive) + *$""", + re.VERBOSE, + ), + "compval": "set.extcommunity_bandwidth_non_transitive", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set extcommunity bandwidth-non-transitive", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "extcommunity_bandwidth_non_transitive": "{{True if extcommunity_bw_nt is defined}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_ip_next_hop", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sip-next-hop\s(?P<ip_next_hop>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.ip_next_hop", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set ip-next-hop {{set.ip_next_hop}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "ip_next_hop": "{{ip_next_hop}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_ipv6_next_hop", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sipv6-next-hop + \s(?P<type>global|local) + \s(?P<value>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.ipv6_next_hop", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set ipv6-next-hop {{set.ipv6_next_hop.ip_type}} {{set.ipv6_next_hop.value}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "ipv6_next_hop": { + "ip_type": "{{type}}", + "value": "{{value}}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_large_community", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slarge-community\s(?P<op>none|replace\s(?P<large_community>\S+)) + $""", + re.VERBOSE, + ), + "compval": "set.large_community", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set large-community {{set.large_community if set.large_community == 'none' else 'replace ' + set.large_community}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "large_community": "{{op if op == 'none' else large_community}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_local_preference", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slocal-preference\s(?P<local_preference>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.local_preference", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set local-preference {{set.local_preference}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "local_preference": "{{local_preference}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_metric", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric\s(?P<metric>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.metric", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set metric {{set.metric}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "metric": "{{metric}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_metric_type", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric-type\s(?P<metric_type>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.metric_type", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set metric-type {{set.metric_type}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "metric_type": "{{metric_type}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_origin", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sorigin\s(?P<origin>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.origin", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set origin {{set.origin}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "origin": "{{origin}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_originator_id", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\soriginator-id\s(?P<originator_id>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.originator_id", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set originator-id {{set.originator_id}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "originator_id": "{{originator_id}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_src", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\ssrc\s(?P<src>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.src", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set src {{set.src}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "src": "{{src}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_tag", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stag\s(?P<tag>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.tag", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set tag {{set.tag}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "tag": "{{tag}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_weight", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sweight\s(?P<weight>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.weight", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set weight {{set.weight}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "weight": "{{weight}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_table", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stable\s(?P<table>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "set.weight", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set table {{set.table}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "table": "{{table}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "set_community", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scommunity\s(?P<op>none|replace\s(?P<value>\S+)) + $""", + re.VERBOSE, + ), + "compval": "set.community.value", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set community {{set.community.value if set.community.value == 'none' else 'replace ' + set.community.value}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "community": { + "value": "{{op if op == 'none' else value}}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_as_path", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sas-path\s(?P<as_path>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.as_path", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match as-path {{match.as_path}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "match": { + "as_path": "{{as_path}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_community_community_list", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<community_list>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.community.community_list", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match community community-list {{match.community.community_list}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "match": { + "community": {"community_list": "{{community_list}}"}, + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_community_exact_match", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\sexact-match(?P<exact_match>) + *$""", + re.VERBOSE, + ), + "compval": "match.community.exact_match", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match community exact-match", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "match": { + "community": {"exact_match": "{{True if exact_match is defined}}"}, + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_extcommunity", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sextcommunity\s(?P<extcommunity>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.extcommunity", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match extcommunity {{match.extcommunity}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "match": { + "extcommunity": "{{extcommunity}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_interface", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sinterface\s(?P<interface>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.interface", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match interface {{match.interface}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "match": { + "interface": "{{interface}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_large_community_large_community_list", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\slarge-community\slarge-community-list\s(?P<lc>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.large_community_large_community_list", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match large-community large-community-list {{match.large_community_large_community_list}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "match": { + "large_community_large_community_list": "{{lc}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_metric", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\smetric\s(?P<metric>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.metric", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match metric {{match.metric}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "match": { + "metric": "{{metric}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_origin", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sorigin\s(?P<origin>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.origin", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match origin {{match.origin}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "match": { + "origin": "{{origin}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_peer", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\speer\s(?P<peer>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.peer", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match peer {{match.peer}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "match": { + "peer": "{{peer}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_ip_address", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\saddress + \s(?P<list_type>access-list|prefix-list) + \s(?P<value>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.ip.address", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match ip address {{match.ip.address.list_type}} {{match.ip.address.value}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": { + "sequence": "{{sequence}}", + "match": { + "ip": { + "address": { + "list_type": "{{list_type}}", + "value": "{{value}}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_ip_next_hop", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\snexthop + \s(?P<list_type>access-list|prefix-list) + \s(?P<value>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.ip.next_hop", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match ip nexthop {{match.ip.next_hop.list_type}} {{match.ip.next_hop.value}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": { + "sequence": "{{sequence}}", + "match": { + "ip": { + "next_hop": { + "list_type": "{{list_type}}", + "value": "{{value}}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_ip_route_source", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\sroute-source + \s(?P<list_type>access-list|prefix-list) + \s(?P<value>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.ip.route_source", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match ip route-source {{match.ip.route_source.list_type}} {{match.ip.route_source.value}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": { + "sequence": "{{sequence}}", + "match": { + "ip": { + "route_source": { + "list_type": "{{list_type}}", + "value": "{{value}}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_ipv6_address", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sipv6\saddress + \s(?P<list_type>access-list|prefix-list) + \s(?P<value>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.ipv6.address", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match ipv6 address {{match.ipv6.address.list_type}} {{match.ipv6.address.value}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": { + "sequence": "{{sequence}}", + "match": { + "ipv6": { + "address": { + "list_type": "{{list_type}}", + "value": "{{value}}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_ipv6_nexthop", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sipv6\snexthop + \s(?P<value>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.ipv6.next_hop", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match ipv6 nexthop {{match.ipv6.next_hop}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": { + "sequence": "{{sequence}}", + "match": { + "ipv6": { + "next_hop": "{{value}}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_protocol", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sprotocol\s(?P<value>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.protocol", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match protocol {{match.protocol}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": { + "sequence": "{{sequence}}", + "match": { + "protocol": "{{value}}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "match_rpki", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\srpki + \s(?P<value>\S+)\s*$""", + re.VERBOSE, + ), + "compval": "match.rpki", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "match rpki {{match.rpki}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": { + "sequence": "{{sequence}}", + "match": { + "rpki": "{{value}}", + }, + }, + }, + }, + }, + }, + }, + + ] + # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/snmp_server.py b/plugins/module_utils/network/vyos/rm_templates/snmp_server.py index 71753083..ff1c6465 100644 --- a/plugins/module_utils/network/vyos/rm_templates/snmp_server.py +++ b/plugins/module_utils/network/vyos/rm_templates/snmp_server.py @@ -140,9 +140,9 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\scommunity \s+(?P<name>\S+) - \s*(?P<auth>authorization\srw|authorization\sro)* - \s*(client\s(?P<client>\S+))* - \s*(network\s(?P<network>\S+))* + \s*(?P<auth>authorization\srw|authorization\sro)? + \s*(client\s(?P<client>\S+))? + \s*(network\s(?P<network>\S+))? $""", re.VERBOSE, ), @@ -164,8 +164,7 @@ class Snmp_serverTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\sservice\ssnmp\scontact - \s+(?P<name>\S+) - *$""", + \s+(?P<name>\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp contact {{ contact }}", @@ -179,8 +178,7 @@ class Snmp_serverTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\sservice\ssnmp\sdescription - \s+(?P<name>\S+) - *$""", + \s+(?P<name>\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp description {{ description }}", @@ -195,8 +193,8 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\slisten-address \s+(?P<addr>\S+) - \s*(port)* - \s*(?P<port>\d+)* + \s*(port)? + \s*(?P<port>\d+)? $""", re.VERBOSE, ), @@ -232,8 +230,7 @@ class Snmp_serverTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\sservice\ssnmp\ssmux-peer - \s+(?P<name>\S+) - *$""", + \s+(?P<name>\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp smux-peer {{ smux_peer }}", @@ -247,8 +244,7 @@ class Snmp_serverTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\sservice\ssnmp\strap-source - \s+(?P<name>\S+) - *$""", + \s+(?P<name>\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp trap-source {{ trap_source }}", @@ -263,9 +259,8 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\strap-target \s+(?P<name>\S+) - \s*(?P<comm>community\s\S+)* - \s*(?P<port>port\s\d+)* - $""", + \s*(?P<comm>community\s\S+)? + \s*(?P<port>port\s\d+)? $""", re.VERBOSE, ), "setval": _tmplt_snmp_server_trap_target, @@ -283,8 +278,7 @@ class Snmp_serverTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\sengineid - \s+(?P<name>\S+) - *$""", + \s+(?P<name>\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp v3 engineid {{ snmp_v3.engine_id }}", @@ -301,9 +295,9 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\sgroup \s+(?P<name>\S+) - \s*(?P<mode>mode\s\S+)* - \s*(?P<sec>seclevel\s\S+)* - \s*(?P<view>view\s\S+)* + \s*(?P<mode>mode\s\S+)? + \s*(?P<sec>seclevel\s\S+)? + \s*(?P<view>view\s\S+)? $""", re.VERBOSE, ), @@ -329,9 +323,9 @@ class Snmp_serverTemplate(NetworkTemplate): ^set\sservice\ssnmp\sv3\strap-target \s+(?P<name>\S+) \s+auth - \s*(?P<enc>encrypted-password\s\S+)* - \s*(?P<plain>plaintext-password\s\S+)* - \s*(?P<type>type\s\S+)* + \s*(?P<enc>encrypted-password\s\S+)? + \s*(?P<plain>plaintext-password\s\S+)? + \s*(?P<type>type\s\S+)? $""", re.VERBOSE, ), @@ -358,8 +352,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P<name>\S+) - \s+(?P<port>port\s\d+)* - $""", + \s+(?P<port>port\s\d+)? $""", re.VERBOSE, ), "setval": "service snmp v3 trap-target port {{ snmp_v3.trap_targets.port }}", @@ -381,7 +374,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P<name>\S+) - \s+(?P<protocol>protocol\s\S+)* + \s+(?P<protocol>protocol\s\S+)? $""", re.VERBOSE, ), @@ -404,7 +397,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P<name>\S+) - \s+(?P<type>type\s\S+)* + \s+(?P<type>type\s\S+)? $""", re.VERBOSE, ), @@ -427,7 +420,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P<name>\S+) - \s+(?P<user>user\s\S+)* + \s+(?P<user>user\s\S+)? $""", re.VERBOSE, ), @@ -451,9 +444,9 @@ class Snmp_serverTemplate(NetworkTemplate): ^set\sservice\ssnmp\sv3\strap-target \s+(?P<name>\S+) \s+privacy - \s*(?P<enc>encrypted-password\s\S+)* - \s*(?P<plain>plaintext-password\s\S+)* - \s*(?P<type>type\s\S+)* + \s*(?P<enc>encrypted-password\s\S+)? + \s*(?P<plain>plaintext-password\s\S+)? + \s*(?P<type>type\s\S+)? $""", re.VERBOSE, ), @@ -481,9 +474,9 @@ class Snmp_serverTemplate(NetworkTemplate): ^set\sservice\ssnmp\sv3\suser \s+(?P<name>\S+) \s+auth - \s*(?P<enc>encrypted-password\s\S+)* - \s*(?P<plain>plaintext-password\s\S+)* - \s*(?P<type>type\s\S+)* + \s*(?P<enc>encrypted-password\s\S+)? + \s*(?P<plain>plaintext-password\s\S+)? + \s*(?P<type>type\s\S+)? $""", re.VERBOSE, ), @@ -511,9 +504,9 @@ class Snmp_serverTemplate(NetworkTemplate): ^set\sservice\ssnmp\sv3\suser \s+(?P<name>\S+) \s+privacy - \s*(?P<enc>encrypted-password\s\S+)* - \s*(?P<plain>plaintext-password\s\S+)* - \s*(?P<type>type\s\S+)* + \s*(?P<enc>encrypted-password\s\S+)? + \s*(?P<plain>plaintext-password\s\S+)? + \s*(?P<type>type\s\S+)? $""", re.VERBOSE, ), @@ -540,8 +533,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\suser \s+(?P<name>\S+) - \s+(?P<group>group\s.+)* - $""", + \s+(?P<group>group\s.+)? $""", re.VERBOSE, ), "setval": "service snmp v3 user {{ snmp_v3.users.user }} group {{ snmp_v3.users.group }}", @@ -563,7 +555,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\suser \s+(?P<name>\S+) - \s+(?P<mode>mode\s\S+)* + \s+(?P<mode>mode\s\S+)? $""", re.VERBOSE, ), @@ -587,8 +579,8 @@ class Snmp_serverTemplate(NetworkTemplate): ^set\sservice\ssnmp\sv3\sview \s+(?P<name>\S+) \s+(?P<oid>oid\s\S+) - \s*(?P<ex>exclude\s\S+)* - \s*(?P<mask>mask\s\S+)* + \s*(?P<ex>exclude\s\S+)? + \s*(?P<mask>mask\s\S+)? $""", re.VERBOSE, ), 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 4c371962..78445c02 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -9,7 +9,6 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type from ansible.module_utils.basic import missing_required_lib -from ansible.module_utils.six import iteritems try: @@ -198,7 +197,7 @@ def key_value_in_dict(have_key, have_value, want_dict): :param want_dict: :return: """ - for key, value in iteritems(want_dict): + for key, value in want_dict.items(): if key == have_key and value == have_value: return True return False @@ -270,7 +269,7 @@ def _is_w_same(w, h, key): def _in_target(h, key): """ - This function checks whether the target exist and key present in target config. + This functi checks whether the target exist and key present in target config. :param h: target config. :param key: attribute name. :return: True/False. @@ -286,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/module_utils/network/vyos/utils/version.py b/plugins/module_utils/network/vyos/utils/version.py index cc3028c3..6d84ef1c 100644 --- a/plugins/module_utils/network/vyos/utils/version.py +++ b/plugins/module_utils/network/vyos/utils/version.py @@ -7,6 +7,8 @@ """Provide version object to compare version numbers.""" from __future__ import absolute_import, division, print_function + + __metaclass__ = type diff --git a/plugins/module_utils/network/vyos/vyos.py b/plugins/module_utils/network/vyos/vyos.py index 1430b1b1..4987d6b0 100644 --- a/plugins/module_utils/network/vyos/vyos.py +++ b/plugins/module_utils/network/vyos/vyos.py @@ -34,6 +34,7 @@ import json from ansible.module_utils._text import to_text from ansible.module_utils.connection import Connection, ConnectionError + _DEVICE_CONFIGS = {} @@ -68,8 +69,13 @@ def get_config(module, flags=None, format=None): flags = [] if flags is None else flags global _DEVICE_CONFIGS - if _DEVICE_CONFIGS != {}: - return _DEVICE_CONFIGS + # If _DEVICE_CONFIGS is non-empty and module.params["match"] is "none", + # return the cached device configurations. This avoids redundant calls + # to the connection when no specific match criteria are provided. + if _DEVICE_CONFIGS != {} and ( + module.params["match"] is not None and module.params["match"] == "none" + ): + return to_text(_DEVICE_CONFIGS) else: connection = get_connection(module) try: @@ -90,11 +96,13 @@ def run_commands(module, commands, check_rc=True): return response -def load_config(module, commands, commit=False, comment=None): +def load_config(module, commands, commit=False, comment=None, confirm=None): connection = get_connection(module) try: - response = connection.edit_config(candidate=commands, commit=commit, comment=comment) + response = connection.edit_config( + candidate=commands, commit=commit, comment=comment, confirm=confirm + ) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) |
