diff --git a/README.md b/README.md index 83f287673..f07da3d38 100644 --- a/README.md +++ b/README.md @@ -114,8 +114,9 @@ Name | Description [cisco.nxos.nxos_vlans](https://github.com/ansible-collections/cisco.nxos/blob/main/docs/cisco.nxos.nxos_vlans_module.rst)|VLANs resource module [cisco.nxos.nxos_vpc](https://github.com/ansible-collections/cisco.nxos/blob/main/docs/cisco.nxos.nxos_vpc_module.rst)|Manages global VPC configuration [cisco.nxos.nxos_vpc_interface](https://github.com/ansible-collections/cisco.nxos/blob/main/docs/cisco.nxos.nxos_vpc_interface_module.rst)|Manages interface VPC configuration -[cisco.nxos.nxos_vrf](https://github.com/ansible-collections/cisco.nxos/blob/main/docs/cisco.nxos.nxos_vrf_module.rst)|Manages global VRF configuration. +[cisco.nxos.nxos_vrf](https://github.com/ansible-collections/cisco.nxos/blob/main/docs/cisco.nxos.nxos_vrf_module.rst)|(deprecated, removed after 2026-07-25) Manages global VRF configuration. [cisco.nxos.nxos_vrf_af](https://github.com/ansible-collections/cisco.nxos/blob/main/docs/cisco.nxos.nxos_vrf_af_module.rst)|Manages VRF AF. +[cisco.nxos.nxos_vrf_global](https://github.com/ansible-collections/cisco.nxos/blob/main/docs/cisco.nxos.nxos_vrf_global_module.rst)|Resource module to configure VRF definitions. [cisco.nxos.nxos_vrf_interface](https://github.com/ansible-collections/cisco.nxos/blob/main/docs/cisco.nxos.nxos_vrf_interface_module.rst)|Manages interface specific VRF configuration. [cisco.nxos.nxos_vrrp](https://github.com/ansible-collections/cisco.nxos/blob/main/docs/cisco.nxos.nxos_vrrp_module.rst)|Manages VRRP configuration on NX-OS switches. [cisco.nxos.nxos_vsan](https://github.com/ansible-collections/cisco.nxos/blob/main/docs/cisco.nxos.nxos_vsan_module.rst)|Configuration of vsan for Cisco NXOS MDS Switches. diff --git a/changelogs/fragments/nxos_vrf_global.yml b/changelogs/fragments/nxos_vrf_global.yml new file mode 100644 index 000000000..da9dc650d --- /dev/null +++ b/changelogs/fragments/nxos_vrf_global.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Add nxos_vrf_global resource module in favor of nxos_vrf module (https://github.com/ansible-collections/cisco.nxos/pull/870). diff --git a/docs/cisco.nxos.nxos_vrf_module.rst b/docs/cisco.nxos.nxos_vrf_module.rst index 6765f78b5..440c400eb 100644 --- a/docs/cisco.nxos.nxos_vrf_module.rst +++ b/docs/cisco.nxos.nxos_vrf_module.rst @@ -5,7 +5,7 @@ cisco.nxos.nxos_vrf ******************* -**Manages global VRF configuration.** +**(deprecated, removed after 2026-07-25) Manages global VRF configuration.** Version added: 1.0.0 diff --git a/meta/runtime.yml b/meta/runtime.yml index 381752a06..40dfbf1fc 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -180,6 +180,9 @@ plugin_routing: redirect: cisco.nxos.nxos_vpc_interface vrf: redirect: cisco.nxos.nxos_vrf + deprecation: + removal_date: "2026-07-25" + warning_text: See the plugin documentation for more details vrf_af: redirect: cisco.nxos.nxos_vrf_af vrf_interface: @@ -200,3 +203,5 @@ plugin_routing: redirect: cisco.nxos.nxos_vxlan_vtep_vni zone_zoneset: redirect: cisco.nxos.nxos_zone_zoneset + vrf_global: + redirect: cisco.nxos.nxos_vrf_global diff --git a/plugins/action/vrf_global.py b/plugins/action/vrf_global.py new file mode 120000 index 000000000..a69e27103 --- /dev/null +++ b/plugins/action/vrf_global.py @@ -0,0 +1 @@ +nxos.py \ No newline at end of file diff --git a/plugins/lookup/__init__.py b/plugins/lookup/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/nxos/argspec/vrf_global/__init__.py b/plugins/module_utils/network/nxos/argspec/vrf_global/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/nxos/argspec/vrf_global/vrf_global.py b/plugins/module_utils/network/nxos/argspec/vrf_global/vrf_global.py new file mode 100644 index 000000000..2e1b2f33a --- /dev/null +++ b/plugins/module_utils/network/nxos/argspec/vrf_global/vrf_global.py @@ -0,0 +1,260 @@ +# -*- 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 +# ansible.content_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the documentation in the module file and re-run +# ansible.content_builder commenting out +# the path to external 'docstring' in build.yaml. +# +############################################## + +""" +The arg spec for the nxos_vrf_global module +""" + + +class Vrf_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the nxos_vrf_global module""" + + argument_spec = { + "config": { + "type": "dict", + "options": { + "vrfs": { + "type": "list", + "elements": "dict", + "options": { + "name": {"required": True, "type": "str"}, + "description": {"type": "str"}, + "ip": { + "type": "dict", + "options": { + "auto_discard": {"type": "bool"}, + "domain_list": {"type": "list", "elements": "str"}, + "domain_name": {"type": "str"}, + "icmp_err": { + "type": "dict", + "options": { + "source_interface": { + "type": "dict", + "options": { + "interface": { + "type": "str", + "choices": [ + "loopback", + "ethernet", + "port-channel", + ], + }, + "interface_value": {"type": "str"}, + }, + }, + }, + }, + "igmp": { + "type": "dict", + "options": { + "ssm_translate": { + "type": "list", + "elements": "dict", + "options": { + "group": {"type": "str"}, + "source": {"type": "str"}, + }, + }, + }, + }, + "mroutes": { + "type": "list", + "elements": "dict", + "options": { + "group": {"type": "str"}, + "source": {"type": "str"}, + "preference": {"type": "int"}, + "vrf": {"type": "str"}, + }, + }, + "multicast": { + "type": "dict", + "options": { + "group_range_prefix_list": {"type": "str"}, + "multipath": { + "type": "dict", + "options": { + "resilient": {"type": "bool"}, + "splitting_type": { + "type": "dict", + "mutually_exclusive": [ + [ + "legacy", + "nbm", + "none", + "sg_hash", + "sg_hash_next_hop", + ], + ], + "options": { + "none": {"type": "bool"}, + "legacy": {"type": "bool"}, + "nbm": {"type": "bool"}, + "sg_hash": { + "type": "bool", + }, + "sg_hash_next_hop": { + "type": "bool", + }, + }, + }, + }, + }, + "rpf": { + "type": "list", + "elements": "dict", + "options": { + "vrf_name": {"type": "str"}, + "group_list_range": { + "type": "str", + }, + }, + }, + }, + }, + "name_server": { + "type": "dict", + "options": { + "address_list": { + "type": "list", + "elements": "str", + }, + "use_vrf": { + "type": "dict", + "options": { + "vrf": {"type": "str"}, + "source_address": {"type": "str"}, + }, + }, + }, + }, + "route": { + "type": "list", + "elements": "dict", + "mutually_exclusive": [ + ["tags", "vrf", "track"], + ], + "options": { + "source": {"type": "str"}, + "destination": {"type": "str"}, + "tags": { + "type": "dict", + "options": { + "tag_value": {"type": "int"}, + "route_pref": {"type": "int"}, + }, + }, + "vrf": {"type": "str"}, + "track": {"type": "str"}, + }, + }, + }, + }, + "vni": { + "type": "dict", + "options": { + "vni_number": {"type": "int"}, + "layer_3": {"type": "bool"}, + }, + }, + "multicast": { + "type": "dict", + "options": { + "service_reflect": { + "type": "list", + "elements": "dict", + "options": { + "service_interface": {"type": "str"}, + "map_to": {"type": "str"}, + }, + }, + }, + }, + "ipv6": { + "type": "dict", + "options": { + "mld_ssm_translate": { + "type": "list", + "elements": "dict", + "options": { + "icmp": {"type": "bool"}, + "group": {"type": "str"}, + "source": {"type": "str"}, + }, + }, + "multicast": { + "type": "dict", + "options": { + "group_range_prefix_list": {"type": "str"}, + "multipath": { + "type": "dict", + "options": { + "resilient": {"type": "bool"}, + "splitting_type": { + "type": "dict", + "mutually_exclusive": [ + [ + "none", + "sg_hash", + "sg_hash_next_hop", + ], + ], + "options": { + "none": {"type": "bool"}, + "sg_hash": { + "type": "bool", + }, + "sg_hash_next_hop": { + "type": "bool", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "parsed", + "gathered", + "deleted", + "merged", + "replaced", + "rendered", + "overridden", + "purged", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/nxos/config/vrf_global/__init__.py b/plugins/module_utils/network/nxos/config/vrf_global/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/nxos/config/vrf_global/vrf_global.py b/plugins/module_utils/network/nxos/config/vrf_global/vrf_global.py new file mode 100644 index 000000000..0fa19963c --- /dev/null +++ b/plugins/module_utils/network/nxos/config/vrf_global/vrf_global.py @@ -0,0 +1,233 @@ +# +# -*- 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 + +""" +The nxos_vrf_global 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 ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, + get_from_dict, +) + +from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import Facts +from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.rm_templates.vrf_global import ( + Vrf_globalTemplate, +) + + +class Vrf_global(ResourceModule): + """ + The nxos_vrf_global config class + """ + + def __init__(self, module): + super(Vrf_global, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="vrf_global", + tmplt=Vrf_globalTemplate(), + ) + self.parsers = [ + "description", + "ip.auto_discard", + "ip.domain_name", + "ip.name_server.address_list", + "ip.icmp_err.source_interface", + "ip.multicast.group_range_prefix_list", + "ip.multicast.multipath.resilient", + "ip.multicast.multipath.splitting_type.none", + "ip.multicast.multipath.splitting_type.legacy", + "ip.multicast.multipath.splitting_type.nbm", + "ip.multicast.multipath.splitting_type.sg_hash", + "ip.multicast.multipath.splitting_type.sg_hash_next_hop", + "ip.name_server.use_vrf", + "vni", + "ipv6.multicast.group_range_prefix_list", + "ipv6.multicast.multipath.resilient", + "ipv6.multicast.multipath.splitting_type.none", + "ipv6.multicast.multipath.splitting_type.sg_hash", + "ipv6.multicast.multipath.splitting_type.sg_hash_next_hop", + ] + + self.list_parsers = [ + "ip.domain_list", + "ip.igmp.ssm_translate", + "ip.mroutes", + "ip.multicast.rpf", + "ip.route", + "multicast.service_reflect", + "ipv6.mld_ssm_translate", + ] + + 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 = self._vrf_list_to_dict(self.want) + haved = self._vrf_list_to_dict(self.have) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # 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} + wantd = {} + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self._compare(want={}, have=have) + + if self.state == "purged": + purge_list = wantd or haved + for k, item in iteritems(purge_list): + self.purge(item) + else: + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Vrf_global network resource. + """ + begin = len(self.commands) + self.compare(parsers=self.parsers, want=want, have=have) + self._compare_lists(want=want, have=have) + if len(self.commands) != begin or (not have and want): + self.commands.insert( + begin, + self._tmplt.render( + want or have, + "name", + False, + ), + ) + + def _compare_lists(self, want, have): + """Compares the list parsers for the Vrf_global network resource + and populates the list of commands to be run. + """ + for attrib in self.list_parsers: + wdict = get_from_dict(want, attrib) or {} + hdict = get_from_dict(have, attrib) or {} + + for key, entry in iteritems(wdict): + if entry != hdict.pop(key, {}): + self.addcmd(entry, attrib, False) + # remove remaining items in have for replaced + for entry in hdict.values(): + self.addcmd(entry, attrib, True) + + def purge(self, have): + """Purge the VRF configuration""" + self.commands.append("no vrf context {0}".format(have["name"])) + + def _vrf_list_to_dict(self, vrf_list): + """Converts a list of VRFs to a dictionary with the VRF name as the key. + + :param list vrf_list: A list of VRFs + :rtype: dict + :returns: A dictionary of VRFs with the VRF name as the key + """ + items = dict() + for entry in vrf_list.get("vrfs", []): + items.update({(entry["name"]): entry}) + + for vrf, value in iteritems(items): + domain_list = value.get("ip", {}).get("domain_list", []) + if domain_list: + value["ip"]["domain_list"] = { + entry: {"domain_list": entry} for entry in domain_list + } + ip_ssm = value.get("ip", {}).get("igmp", {}).get("ssm_translate", []) + if ip_ssm: + value["ip"]["igmp"]["ssm_translate"] = { + (entry.get("group"), entry.get("source")): entry for entry in ip_ssm + } + ip_mroutes = value.get("ip", {}).get("mroutes", []) + if ip_mroutes: + value["ip"]["mroutes"] = { + (entry.get("group"), entry.get("source")): entry for entry in ip_mroutes + } + ip_rpf = value.get("ip", {}).get("multicast", {}).get("rpf", []) + if ip_rpf: + value["ip"]["multicast"]["rpf"] = { + (entry.get("vrf_name"), entry.get("group_list_range")): entry + for entry in ip_rpf + } + ip_route = value.get("ip", {}).get("route", []) + if ip_route: + temp_route = {} + for entry in ip_route: + if entry.get("vrf"): + temp_route[ + (entry.get("vrf"), entry.get("source"), entry.get("destination")) + ] = entry + elif entry.get("tags"): + tagv = entry.get("tags") + temp_route[ + ( + f"{tagv.get('tag_value')}_{tagv.get('route_pref', '')}", + entry.get("source"), + entry.get("destination"), + ) + ] = entry + elif entry.get("track"): + temp_route[ + (entry.get("track"), entry.get("source"), entry.get("destination")) + ] = entry + else: + temp_route[(entry.get("source"), entry.get("destination"))] = entry + value["ip"]["route"] = temp_route + + service_reflect = value.get("multicast", {}).get("service_reflect", []) + if service_reflect: + value["multicast"]["service_reflect"] = { + (entry.get("service_interface"), entry.get("map_to")): entry + for entry in service_reflect + } + ipv6_ssm = value.get("ipv6", {}).get("mld_ssm_translate", []) + if ipv6_ssm: + value["ipv6"]["mld_ssm_translate"] = { + (entry.get("group"), entry.get("source")): entry for entry in ipv6_ssm + } + address_list = value.get("ip", {}).get("name_server", {}).get("address_list", []) + if address_list: + value["ip"]["name_server"]["address_list"] = " ".join(address_list) + return items diff --git a/plugins/module_utils/network/nxos/facts/facts.py b/plugins/module_utils/network/nxos/facts/facts.py index a7c2ab350..4d1e38f92 100644 --- a/plugins/module_utils/network/nxos/facts/facts.py +++ b/plugins/module_utils/network/nxos/facts/facts.py @@ -111,6 +111,9 @@ from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.vlans.vlans import ( VlansFacts, ) +from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.vrf_global.vrf_global import ( + Vrf_globalFacts, +) FACT_LEGACY_SUBSETS = dict( @@ -150,6 +153,7 @@ snmp_server=Snmp_serverFacts, hostname=HostnameFacts, bgp_templates=Bgp_templatesFacts, + vrf_global=Vrf_globalFacts, ) MDS_FACT_RESOURCE_SUBSETS = dict( fc_interfaces=Fc_interfacesFacts, diff --git a/plugins/module_utils/network/nxos/facts/vrf_global/__init__.py b/plugins/module_utils/network/nxos/facts/vrf_global/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/nxos/facts/vrf_global/vrf_global.py b/plugins/module_utils/network/nxos/facts/vrf_global/vrf_global.py new file mode 100644 index 000000000..f69c684fc --- /dev/null +++ b/plugins/module_utils/network/nxos/facts/vrf_global/vrf_global.py @@ -0,0 +1,85 @@ +# -*- 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 + +""" +The nxos vrf_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.vrf_global.vrf_global import ( + Vrf_globalArgs, +) +from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.rm_templates.vrf_global import ( + Vrf_globalTemplate, +) + + +class Vrf_globalFacts(object): + """The nxos vrf_global facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Vrf_globalArgs.argument_spec + + def get_config(self, connection): + """Get the configuration from the device""" + + return connection.get("show running-config | section ^vrf") + + def _dict_to_list(self, data): + """Convert a dictionary to a list of dictionaries""" + objs = dict() + objs["vrfs"] = list(data.get("vrfs", {}).values()) if "vrfs" in data else [] + + for item in objs["vrfs"]: + name_server = item.get("ip", {}).get("name_server", {}).get("address_list", []) + if name_server: + item["ip"]["name_server"]["address_list"] = name_server.split() + return objs + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Vrf_global 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) + + # parse native config using the Vrf_global template + vrf_global_parser = Vrf_globalTemplate(lines=data.splitlines(), module=self._module) + objs = vrf_global_parser.parse() + + facts_output = self._dict_to_list(objs) + + ansible_facts["ansible_network_resources"].pop("vrf_global", None) + + params = vrf_global_parser.validate_config( + self.argument_spec, + {"config": facts_output}, + redact=True, + ) + params = utils.remove_empties(params) + facts["vrf_global"] = params.get("config", {}) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/plugins/module_utils/network/nxos/rm_templates/vrf_global.py b/plugins/module_utils/network/nxos/rm_templates/vrf_global.py new file mode 100644 index 000000000..53a4da746 --- /dev/null +++ b/plugins/module_utils/network/nxos/rm_templates/vrf_global.py @@ -0,0 +1,798 @@ +# -*- 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 + +""" +The Vrf_global 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, +) + + +def _tmplt_ip_route(route_item): + command = "ip route {source} {destination}".format(**route_item) + if route_item.get("tags"): + tag_item = route_item.get("tags") + command += " tag {tag_value}".format(**tag_item) + if route_item.get("tags").get("route_pref"): + command += " {route_pref}".format(**tag_item) + if route_item.get("vrf"): + command += " vrf {vrf}".format(**route_item) + if route_item.get("track"): + command += " track {track}".format(**route_item) + return command + + +class Vrf_globalTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Vrf_globalTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "name", + "getval": re.compile( + r""" + ^vrf\scontext\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "vrf context {{ name }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + }, + }, + }, + "shared": True, + }, + { + "name": "description", + "getval": re.compile( + r""" + \s+description\s(?P.+$) + $""", re.VERBOSE, + ), + "setval": "description {{ description }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + 'description': '{{ description }}', + }, + }, + }, + }, + { + "name": "ip.auto_discard", + "getval": re.compile( + r""" + \s+ip\s(?Pauto-discard) + $""", re.VERBOSE, + ), + "setval": "ip auto-discard", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "auto_discard": "{{ True if auto_disc is defined }}", + }, + }, + }, + }, + }, + { + "name": "ip.domain_list", + "getval": re.compile( + r""" + \s+ip\sdomain-list\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "ip domain-list {{ domain_list }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "domain_list": [ + "{{ domain_list }}", + ], + }, + }, + }, + }, + }, + { + "name": "ip.domain_name", + "getval": re.compile( + r""" + \s+ip\sdomain-name\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "ip domain-name {{ ip.domain_name }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "domain_name": "{{ domain_name }}", + }, + }, + }, + }, + }, + { + "name": "ip.icmp_err.source_interface", + "getval": re.compile( + r""" + \s+ip\sicmp-errors + \ssource-interface\s(?Peth|po|lo) + (?P(\d+\S*)) + $""", re.VERBOSE, + ), + "setval": "ip icmp-errors source-interface {{ ip.icmp_err.source_interface.interface }} {{ ip.icmp_err.source_interface.interface_value }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "icmp_err": { + "source_interface": { + "interface": "{{ 'ethernet' if 'eth' in interface }}" + "{{ 'port-channel' if 'po' in interface }}" + "{{ 'loopback' if 'lo' in interface }}", + "interface_value": "{{ interface_val }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "ip.igmp.ssm_translate", + "getval": re.compile( + r""" + \s+ip\sigmp + \sssm-translate + \s(?P\S+) + \s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "ip igmp ssm-translate {{ group }} {{ source }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "igmp": { + "ssm_translate": [ + { + "group": "{{ group_val }}", + "source": "{{ source_val }}", + }, + ], + }, + }, + }, + }, + }, + }, + { + "name": "ip.mroutes", + "getval": re.compile( + r""" + \s+ip\smroute + \s(?P\S+) + \s(?P\S+) + (\s(?P\d+))? + (\svrf\s(?P\S+))? + $""", re.VERBOSE, + ), + "setval": "ip mroute {{ group }} {{ source }}" + "{{ ' ' + preference|string if preference is defined }}" + "{{ ' vrf ' + vrf if vrf is defined }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "mroutes": [ + { + "group": "{{ group_val }}", + "source": "{{ source_val }}", + "preference": "{{ pref_val if pref_val is defined }}", + "vrf": "{{ vrf_val if vrf_val is defined }}", + }, + ], + }, + }, + }, + }, + }, + { + "name": "ip.multicast.group_range_prefix_list", + "getval": re.compile( + r""" + \s+ip\smulticast + \sprefix-list\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "ip multicast group-range prefix-list {{ ip.multicast.group_range_prefix_list.group_range_prefix_list }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "multicast": { + "group_range_prefix_list": "{{ prefix_lst }}", + }, + }, + }, + }, + }, + }, + { + "name": "ip.multicast.multipath.resilient", + "getval": re.compile( + r""" + \s+ip\smulticast + \smultipath\s(?Presilient) + $""", re.VERBOSE, + ), + "setval": "ip multicast multipath resilient", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "multicast": { + "multipath": { + "resilient": "{{ True if res is defined }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "ip.multicast.multipath.splitting_type.none", + "getval": re.compile( + r""" + \s+ip\smulticast + \smultipath\s(?Pnone) + $""", re.VERBOSE, + ), + "setval": "ip multicast multipath none", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "multicast": { + "multipath": { + "splitting_type": { + "none": "{{ True if noneval is defined }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "ip.multicast.multipath.splitting_type.legacy", + "getval": re.compile( + r""" + \s+ip\smulticast + \smultipath\s(?Plegacy) + $""", re.VERBOSE, + ), + "setval": "ip multicast multipath legacy", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "multicast": { + "multipath": { + "splitting_type": { + "legacy": "{{ True if legacy_val is defined }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "ip.multicast.multipath.splitting_type.nbm", + "getval": re.compile( + r""" + \s+ip\smulticast + \smultipath\s(?Pnbm) + $""", re.VERBOSE, + ), + "setval": "ip multicast multipath nbm", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "multicast": { + "multipath": { + "splitting_type": { + "nbm": "{{ True if nbm_val is defined }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "ip.multicast.multipath.splitting_type.sg_hash", + "getval": re.compile( + r""" + \s+ip\smulticast + \smultipath\s(?Ps-g-hash) + $""", re.VERBOSE, + ), + "setval": "ip multicast multipath s-g-hash", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "multicast": { + "multipath": { + "splitting_type": { + "sg_hash": "{{ True if sg_hash_val is defined }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "ip.multicast.multipath.splitting_type.sg_hash_next_hop", + "getval": re.compile( + r""" + \s+ip\smulticast + \smultipath + \s(?Ps-g-hash\snext-hop-based) + $""", re.VERBOSE, + ), + "setval": "ip multicast multipath s-g-hash next-hop-based", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "multicast": { + "multipath": { + "splitting_type": { + "sg_hash_next_hop": "{{ True if sg_hash_nxt_val is defined }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "ip.multicast.rpf", + "getval": re.compile( + r""" + \s+ip\smulticast + \srpf\sselect + \svrf\s(?P\S+) + \sgroup-list\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "ip multicast rpf select vrf {{ vrf_name }} group-list {{ group_list_range }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "multicast": { + "rpf": [ + { + "vrf_name": "{{ vrf_val }}", + "group_list_range": "{{ group_list }}", + }, + ], + }, + }, + }, + }, + }, + }, + { + "name": "ip.name_server.use_vrf", + "getval": re.compile( + r""" + \s+ip\sname-server + \s(?P\S+) + \suse-vrf\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "ip name-server {{ ip.name_server.use_vrf.source_address }} use-vrf {{ ip.name_server.use_vrf.vrf }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "name_server": { + "use_vrf": { + "source_address": "{{ source_addr }}", + "vrf": "{{ vrf_name }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "ip.name_server.address_list", + "getval": re.compile( + r""" + \s+ip\sname-server + \s(?P.+$) + $""", re.VERBOSE, + ), + "setval": "ip name-server {{ ip.name_server.address_list }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "name_server": { + "address_list": "{{ addr_list }}", + }, + }, + }, + }, + }, + }, + { + "name": "ip.route", + "getval": re.compile( + r""" + \s+ip\sroute + \s(?P\S+) + \s(?P\S+) + $""", re.VERBOSE, + ), + "setval": _tmplt_ip_route, + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "route": [ + { + "source": "{{ src_val }}", + "destination": "{{ dest_val }}", + }, + ], + }, + }, + }, + }, + }, + { + "name": "ip.route.tags", + "getval": re.compile( + r""" + \s+ip\sroute + \s(?P\S+) + \s(?P\S+) + \stag\s(?P\d+) + (\s(?P\d+))? + $""", re.VERBOSE, + ), + "setval": _tmplt_ip_route, + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "route": [ + { + "source": "{{ src_val }}", + "destination": "{{ dest_val }}", + "tags": { + "tag_value": "{{ tag_val }}", + "route_pref": "{{ route_pref_val if route_pref_val is defined }}", + }, + + }, + ], + }, + }, + }, + }, + }, + { + "name": "ip.route.vrf", + "getval": re.compile( + r""" + \s+ip\sroute + \s(?P\S+) + \s(?P\S+) + \svrf\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": _tmplt_ip_route, + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "route": [ + { + "source": "{{ src_val }}", + "destination": "{{ dest_val }}", + "vrf": "{{ vrf_val }}", + }, + ], + }, + }, + }, + }, + }, + { + "name": "ip.route.track", + "getval": re.compile( + r""" + \s+ip\sroute + \s(?P\S+) + \s(?P\S+) + \strack\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": _tmplt_ip_route, + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ip": { + "route": [ + { + "source": "{{ src_val }}", + "destination": "{{ dest_val }}", + "track": "{{ track_val }}", + }, + ], + }, + }, + }, + }, + }, + { + "name": "vni", + "getval": re.compile( + r""" + \s+vni\s(?P\d+) + (\s(?Pl3))? + $""", re.VERBOSE, + ), + "setval": "vni {{ vni.vni_number }}{{ ' l3' if vni.layer_3 is defined }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "vni": { + "vni_number": "{{ vni_val }}", + "layer_3": "{{ True if l3_val is defined }}", + }, + }, + }, + }, + }, + { + "name": "multicast.service_reflect", + "getval": re.compile( + r""" + \s+multicast\sservice-reflect + \sinterface\s(?P\S+) + \smap\sinterface\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "multicast service-reflect interface {{ service_interface }} map interface {{ map_to }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "multicast": { + "service_reflect": [ + { + "service_interface": "{{ serv_inter }}", + "map_to": "{{ map_inter }}", + }, + ], + }, + }, + }, + }, + }, + { + "name": "ipv6.mld_ssm_translate", + "getval": re.compile( + r""" + \s+ipv6\smld + (\s(?Picmp))? + \sssm-translate + \s(?P\S+) + \s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "ipv6{{ ' icmp' if icmp is defined }} mld ssm-translate {{ group }} {{ source }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ipv6": { + "mld_ssm_translate": [ + { + "icmp": "{{ True if icmp_val is defined }}", + "group": "{{ group_val }}", + "source": "{{ source_val }}", + }, + ], + }, + }, + }, + }, + }, + { + "name": "ipv6.multicast.group_range_prefix_list", + "getval": re.compile( + r""" + \s+ipv6\smulticast + \sgroup-range\sprefix-list + \s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "ipv6 multicast group-range prefix-list {{ ipv6.multicast.group_range_prefix_list }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ipv6": { + "multicast": { + "group_range_prefix_list": "{{ prefix_lst }}", + }, + }, + }, + }, + }, + }, + { + "name": "ipv6.multicast.multipath.resilient", + "getval": re.compile( + r""" + \s+ipv6\smulticast + \smultipath\s(?Presilient) + $""", re.VERBOSE, + ), + "setval": "ipv6 multicast multipath resilient", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ipv6": { + "multicast": { + "multipath": { + "resilient": "{{ True if res_val is defined }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "ipv6.multicast.multipath.splitting_type.none", + "getval": re.compile( + r""" + \s+ipv6\smulticast + \smultipath\s(?Pnone) + $""", re.VERBOSE, + ), + "setval": "ipv6 multicast multipath none", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ipv6": { + "multicast": { + "multipath": { + "splitting_type": { + "none": "{{ True if none_val is defined }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "ipv6.multicast.multipath.splitting_type.sg_hash", + "getval": re.compile( + r""" + \s+ipv6\smulticast + \smultipath\s(?Ps-g-hash) + $""", re.VERBOSE, + ), + "setval": "ipv6 multicast multipath s-g-hash", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ipv6": { + "multicast": { + "multipath": { + "splitting_type": { + "sg_hash": "{{ True if sg_hash_val is defined }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "ipv6.multicast.multipath.splitting_type.sg_hash_next_hop", + "getval": re.compile( + r""" + \s+ipv6\smulticast + \smultipath\s(?Psg-nexthop-hash) + $""", re.VERBOSE, + ), + "setval": "ipv6 multicast multipath sg-nexthop-hash", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "ipv6": { + "multicast": { + "multipath": { + "splitting_type": { + "sg_hash_next_hop": "{{ True if sg_hash_next_hop_val is defined }}", + }, + }, + }, + }, + }, + }, + }, + }, + ] + # fmt: on diff --git a/plugins/modules/nxos_vrf.py b/plugins/modules/nxos_vrf.py index 6017d3c88..78c2d7e61 100644 --- a/plugins/modules/nxos_vrf.py +++ b/plugins/modules/nxos_vrf.py @@ -25,7 +25,7 @@ module: nxos_vrf extends_documentation_fragment: - cisco.nxos.nxos -short_description: Manages global VRF configuration. +short_description: (deprecated, removed after 2026-07-25) Manages global VRF configuration. description: - This module provides declarative management of VRFs on CISCO NXOS network devices. version_added: 1.0.0 diff --git a/plugins/modules/nxos_vrf_global.py b/plugins/modules/nxos_vrf_global.py new file mode 100644 index 000000000..f726d15ac --- /dev/null +++ b/plugins/modules/nxos_vrf_global.py @@ -0,0 +1,1262 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for nxos_vrf_global +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: nxos_vrf_global +short_description: Resource module to configure VRF definitions. +description: This module provides declarative management of VRF definitions on Cisco NXOS. +version_added: 8.1.0 +author: Vinay Mulugund (@roverflow) +notes: + - Tested against NX-OS 9.3.6. + - This module works with connection C(network_cli) and C(httpapi). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_nxos.html) +options: + config: + description: A list containing device configurations for VRF. + type: dict + suboptions: + vrfs: + description: List of VRF definitions. + type: list + elements: dict + suboptions: + name: + description: Name of the VRF.. + required: true + type: str + description: + description: Description of the VRF. + type: str + ip: + description: Configure IP features for the specified vrf. + type: dict + suboptions: + auto_discard: + description: Auto 0.0.0.0/0 discard route. + type: bool + domain_list: + description: Add list domain names. + type: list + elements: str + domain_name: + description: Specify default domain name. + type: str + icmp_err: + description: Enable ICMP error message. + type: dict + suboptions: + source_interface: + description: Configure source-address for applications. + type: dict + suboptions: + interface: + description: Source interface for ICMP error messages. + type: str + choices: + - loopback + - ethernet + - port-channel + interface_value: + description: Source interface value for ICMP error messages. + type: str + igmp: + description: IGMP global configuration commands + type: dict + suboptions: + ssm_translate: + description: Translate IGMPv1/v2 reports to (S,G) route entries. + type: list + elements: dict + suboptions: + group: + description: Source address. + type: str + source: + description: Group address. + type: str + mroutes: + description: Configure multicast routes. + type: list + elements: dict + suboptions: + group: + description: Multicast group address. + type: str + source: + description: Source address. + type: str + preference: + description: Preference value. + type: int + vrf: + description: VRF name. + type: str + multicast: + description: Configure IP multicast global parameters. + type: dict + suboptions: + group_range_prefix_list: + description: Group range prefix-list policy for multicast boundary. + type: str + multipath: + description: Configure ECMP multicast load splitting. + type: dict + suboptions: + resilient: + description: Configure resilient RPF interface. + type: bool + splitting_type: + description: Configure multicast load splitting type. + type: dict + suboptions: + none: + description: Disable multicast load splitting. + type: bool + legacy: + description: Configure hash based on source and group. + type: bool + nbm: + description: Configure NBM controlled RPF interface. + type: bool + sg_hash: + description: Configure hash based on source and group address. + type: bool + sg_hash_next_hop: + description: Configure hash based on source and group address and next-hop. + type: bool + rpf: + description: Configure RPF check. + type: list + elements: dict + suboptions: + vrf_name: + description: VRF for RPF lookup. + type: str + group_list_range: + description: Group range for RPF select. + type: str + name_server: + description: Specify nameserver address. + type: dict + suboptions: + address_list: + description: Configure multicast name server address. + type: list + elements: str + use_vrf: + description: Display per-VRF information. + type: dict + suboptions: + vrf: + description: VRF name. + type: str + source_address: + description: source address for configuring name server. + type: str + route: + description: Configure static routes. + type: list + elements: dict + suboptions: + source: + description: Destination prefix. + type: str + destination: + description: Next-hop address. + type: str + tags: + description: Route tag. + type: dict + suboptions: + tag_value: + description: Route tag value. + type: int + route_pref: + description: Route preference. + type: int + vrf: + description: add vrf to the route. + type: str + track: + description: Configure track object. + type: str + vni: + description: Virtual Network Identifier. + type: dict + suboptions: + vni_number: + description: VNI number. + type: int + layer_3: + description: Configure Layer 3 VNI. + type: bool + multicast: + description: Configure IP multicast options. + type: dict + suboptions: + service_reflect: + description: Configure service reflect option. + type: list + elements: dict + suboptions: + service_interface: + description: configure service interface. + type: str + map_to: + description: Map to interface. + type: str + ipv6: + description: Configure IPv6 features for the specified vrf. + type: dict + suboptions: + mld_ssm_translate: + description: Translate MLDv1/v2 reports to (S,G) route entries. + type: list + elements: dict + suboptions: + icmp: + description: Configure ICMP parameters with mld. + type: bool + group: + description: Source address. + type: str + source: + description: Group address. + type: str + multicast: + description: Configure IP multicast global parameters for ipv6. + type: dict + suboptions: + group_range_prefix_list: + description: Group range prefix-list policy for multicast boundary. + type: str + multipath: + description: Configure ECMP multicast load splitting. + type: dict + suboptions: + resilient: + description: Configure resilient RPF interface. + type: bool + splitting_type: + description: Configure multicast load splitting type. + type: dict + suboptions: + none: + description: Disable multicast load splitting. + type: bool + sg_hash: + description: Configure hash based on source and group address. + type: bool + sg_hash_next_hop: + description: Configure hash based on source and group address and next-hop. + type: bool + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the NX-OS device + by executing the command B(show running-config | section ^vrf). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + choices: + [ + parsed, + gathered, + deleted, + merged, + replaced, + rendered, + overridden, + purged, + ] + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config | section ^vrf). + connection to remote host is not required. + type: str +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# nxos#show running-config | section ^vrf + +- name: Merge provided VRF configuration + cisco.nxos.vrf_global: + config: + vrfs: + - name: testvrf + description: this is description + ip: + auto_discard: true + domain_list: + - example.net + - example.com + domain_name: test.com + icmp_err: + source_interface: + interface: port-channel + interface_value: '1' + igmp: + ssm_translate: + - group: 232.0.0.0/8 + source: 10.1.1.1 + - group: 239.1.2.3/24 + source: 192.168.1.1 + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + - group: 192.168.1.0/24 + preference: 2 + source: 192.168.1.2 + vrf: temp1 + multicast: + multipath: + resilient: true + splitting_type: + legacy: true + rpf: + - group_list_range: 238.1.0.0/24 + vrf_name: temp1 + - group_list_range: 239.1.0.0/24 + vrf_name: temp1 + name_server: + address_list: + - 192.168.0.1 + - 192.168.0.2 + - 192.168.1.1 + - 192.169.1.3 + use_vrf: + source_address: 192.168.0.1 + vrf: temp1 + route: + - destination: 192.0.2.22 + source: 192.0.0.0/24 + - destination: 192.0.2.22 + source: 192.0.0.0/24 + vrf: temp1 + - destination: 192.0.2.22 + source: 192.0.2.0/24 + tags: + route_pref: 4 + tag_value: 2 + ipv6: + mld_ssm_translate: + - group: 'ff28::/16' + source: '2001:db8:0:abcd::2' + - group: 'ff30::/16' + source: '2001:db8:0:abcd::5' + multicast: + group_range_prefix_list: temp2 + multipath: + resilient: true + splitting_type: + none: true + multicast: + service_reflect: + - map_to: Ethernet2/2 + service_interface: Ethernet1/1 + - map_to: Ethernet4/2 + service_interface: Ethernet2/1 + vni: + vni_number: 5 + state: merged + +# Task Output: +# ------------ + +# before: {} +# commands: +# - vrf context test1 +# - description this is description +# - ip auto-discard +# - ip domain-name test.net +# - ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3 +# - ip icmp-errors source-interface port-channel 1 +# - ip multicast multipath resilient +# - ip multicast multipath legacy +# - ip name-server 192.168.0.1 use-vrf temp1 +# - vni 5 +# - ipv6 multicast group-range prefix-list temp2 +# - ipv6 multicast multipath resilient +# - ipv6 multicast multipath none +# - ip domain-list test.org +# - ip domain-list example.com +# - ip domain-list example.net +# - ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 +# - ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 +# - ip mroute 192.168.1.0/24 192.168.1.1 +# - ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1 +# - ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 +# - ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 +# - ip route 192.0.0.0/24 192.0.2.22 +# - ip route 192.0.0.0/24 192.0.2.22 vrf temp1 +# - ip route 192.0.2.0/24 192.0.2.22 tag 2 4 +# - multicast service-reflect interface Ethernet1/1 map interface Ethernet2/2 +# - multicast service-reflect interface Ethernet2/1 map interface Ethernet4/2 +# - ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2 +# - ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::5 + +# after: +# vrfs: +# - name: testvrf +# description: this is description +# ip: +# auto_discard: true +# domain_list: +# - example.net +# - example.com +# domain_name: test.com +# icmp_err: +# source_interface: +# interface: port-channel +# interface_value: '1' +# igmp: +# ssm_translate: +# - group: 232.0.0.0/8 +# source: 10.1.1.1 +# - group: 239.1.2.3/24 +# source: 192.168.1.1 +# mroutes: +# - group: 192.168.1.0/24 +# source: 192.168.1.1 +# - group: 192.168.1.0/24 +# preference: 2 +# source: 192.168.1.2 +# vrf: temp1 +# multicast: +# multipath: +# resilient: true +# splitting_type: +# legacy: true +# rpf: +# - group_list_range: 238.1.0.0/24 +# vrf_name: temp1 +# - group_list_range: 239.1.0.0/24 +# vrf_name: temp1 +# name_server: +# address_list: +# - 192.168.0.1 +# - 192.168.0.2 +# - 192.168.1.1 +# - 192.169.1.3 +# use_vrf: +# source_address: 192.168.0.1 +# vrf: temp1 +# route: +# - destination: 192.0.2.22 +# source: 192.0.0.0/24 +# - destination: 192.0.2.22 +# source: 192.0.0.0/24 +# vrf: temp1 +# - destination: 192.0.2.22 +# source: 192.0.2.0/24 +# tags: +# route_pref: 4 +# tag_value: 2 +# ipv6: +# mld_ssm_translate: +# - group: ff28::/16 +# source: 2001:db8:0:abcd::2 +# - group: ff30::/16 +# source: 2001:db8:0:abcd::5 +# multicast: +# group_range_prefix_list: temp2 +# multipath: +# resilient: true +# splitting_type: +# none: true +# multicast: +# service_reflect: +# - map_to: Ethernet2/2 +# service_interface: Ethernet1/1 +# - map_to: Ethernet4/2 +# service_interface: Ethernet2/1 +# vni: +# vni_number: 5 + +# After state: +# ------------ +# +# nxos#show running-config | section ^vrf +# vrf context testvrf +# description this is description +# ip auto-discard +# ip domain-name test.net +# ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3 +# ip icmp-errors source-interface port-channel 1 +# ip multicast multipath resilient +# ip multicast multipath legacy +# ip name-server 192.168.0.1 use-vrf temp1 +# vni 5 +# ipv6 multicast group-range prefix-list temp2 +# ipv6 multicast multipath resilient +# ipv6 multicast multipath none +# ip domain-list test.org +# ip domain-list example.com +# ip domain-list example.net +# ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 +# ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 +# ip mroute 192.168.1.0/24 192.168.1.1 +# ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1 +# ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 +# ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 +# ip route 192.0.0.0/24 192.0.2.22 +# ip route 192.0.0.0/24 192.0.2.22 vrf temp1 +# ip route 192.0.2.0/24 192.0.2.22 tag 2 4 +# multicast service-reflect interface Ethernet1/1 map interface Ethernet2/2 +# multicast service-reflect interface Ethernet2/1 map interface Ethernet4/2 +# ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2 +# ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::5 + +# Using deleted + +# Before state: +# ------------- +# +# nxos#show running-config | section ^vrf +# vrf context management +# ip name-server 192.168.255.1 +# ip route 0.0.0.0/0 192.168.255.1 +# vrf context test1 +# description this is description +# ip domain-name test.net +# ip domain-list example.net +# ip domain-list example.com +# ip domain-list test.org +# vni 5 +# ip auto-discard +# ip route 192.0.0.0/24 192.0.2.22 +# ip route 192.0.0.0/24 192.0.2.22 vrf temp1 +# ip route 192.0.2.0/24 192.0.2.22 tag 2 4 +# ip mroute 192.168.1.0/24 192.168.1.1 +# ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1 +# ip icmp-errors source-interface po1 +# ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 +# ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 +# ip multicast multipath legacy +# ip multicast multipath resilient +# ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 +# ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 +# ip multicast group-range prefix-list temp2 + +- name: Delete VRF configuration + cisco.nxos.vrf_global: + config: + vrfs: + - name: test1 + state: deleted + +# Task Output: +# ------------ +# +# before: +# vrfs: +# - name: management +# ip: +# name_server: +# address_list: +# - 192.168.255.1 +# route: +# - source: 0.0.0.0/0 +# destination: 192.168.255.1 +# - name: test1 +# description: this is description +# ip: +# domain_name: test.net +# domain_list: +# - test.org +# - example.net +# - example.com +# auto_discard: true +# route: +# - source: 192.0.0.0/24 +# destination: 192.0.2.22 +# - source: 192.0.0.0/24 +# destination: 192.0.2.22 +# vrf: temp1 +# - source: 192.0.2.0/24 +# destination: 192.0.2.22 +# tags: +# tag_value: 2 +# route_pref: 4 +# mroutes: +# - group: 192.168.1.0/24 +# source: 192.168.1.1 +# - group: 192.168.1.0/24 +# source: 192.168.1.2 +# preference: 2 +# vrf: temp1 +# icmp_err: +# source_interface: +# interface: port-channel +# interface_value: '1' +# igmp: +# ssm_translate: +# - group: 232.0.0.0/8 +# source: 10.1.1.1 +# - group: 239.1.2.3/24 +# source: 192.168.1.1 +# multicast: +# multipath: +# splitting_type: +# legacy: true +# resilient: true +# rpf: +# - vrf_name: temp1 +# group_list_range: 238.1.0.0/24 +# - vrf_name: temp1 +# group_list_range: 239.1.0.0/24 +# vni: +# vni_number: 5 + +# commands: +# - vrf context test1 +# - no description this is description +# - no ip auto-discard +# - no ip domain-name test.net +# - no ip icmp-errors source-interface port-channel 1 +# - no ip multicast multipath resilient +# - no ip multicast multipath legacy +# - no vni 5 +# - no ip domain-list example.net +# - no ip domain-list test.org +# - no ip domain-list example.com +# - no ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 +# - no ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 +# - no ip mroute 192.168.1.0/24 192.168.1.1 +# - no ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1 +# - no ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 +# - no ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 +# - no ip route 192.0.0.0/24 192.0.2.22 +# - no ip route 192.0.0.0/24 192.0.2.22 vrf temp1 +# - no ip route 192.0.2.0/24 192.0.2.22 tag 2 4 +# +# after: +# vrfs: +# - name: management +# ip: +# name_server: +# address_list: +# - 192.168.255.1 +# route: +# - source: 0.0.0.0/0 +# destination: 192.168.255.1 +# - name: test1 + +# Using deleted with empty config + +# Before state: +# ------------- +# +# nxos#show running-config | section ^vrf +# vrf context management +# ip name-server 192.168.255.1 +# ip route 0.0.0.0/0 192.168.255.1 +# vrf context test1 +# description this is description +# ip domain-name test.net +# ip domain-list example.net +# ip domain-list example.com +# ip domain-list test.org +# vni 5 + +- name: Delete VRF configuration + cisco.nxos.vrf_global: + config: + vrfs: + - name: test1 + state: deleted + +# Task Output: +# ------------ +# +# before: +# vrfs: +# - name: management +# ip: +# name_server: +# address_list: +# - 192.168.255.1 +# route: +# - source: 0.0.0.0/0 +# destination: 192.168.255.1 +# - name: test1 +# description: this is description +# ip: +# domain_name: test.net +# domain_list: +# - test.org +# - example.net +# - example.com +# vni: +# vni_number: 5 + +# commands: +# - vrf context management +# - no ip name-server 192.168.255.1 +# - no ip route 0.0.0.0/0 192.168.255.1 +# - vrf context test1 +# - no description this is description +# - no ip domain-name test.net +# - no vni 5 +# - no ip domain-list example.net +# - no ip domain-list test.org +# - no ip domain-list example.com + +# after: +# vrfs: +# - name: management +# - name: test1 + +# Using purged + +# Before state: +# ------------- +# +# nxos#show running-config | section ^vrf +# vrf context management +# ip name-server 192.168.255.1 +# ip route 0.0.0.0/0 192.168.255.1 +# vrf context test1 +# description this is description +# ip domain-name example.com +# ip domain-list example.net +# ip domain-list example.org +# vni 5 +# ip auto-discard +# ip route 192.0.0.0/24 192.0.2.22 +# ip route 192.0.0.0/24 192.0.2.22 vrf temp1 +# ip route 192.0.2.0/24 192.0.2.22 tag 2 4 +# vrf context test2 +# description test description +# ip auto-discard +# ip domain-name test.com + +- name: Override VRF configuration + cisco.nxos.vrf_global: + config: + vrfs: + - name: test1 + - name: test2 + state: purged + +# Task Output: +# ------------ +# +# before: +# vrfs: +# - name: management +# ip: +# name_server: +# address_list: +# - 192.168.255.1 +# route: +# - source: 0.0.0.0/0 +# destination: 192.168.255.1 +# - name: test1 +# description: this is description +# ip: +# domain_name: example.com +# domain_list: +# - example.net +# - example.org +# auto_discard: true +# route: +# - source: 192.0.0.0/24 +# destination: 192.0.2.22 +# - source: 192.0.0.0/24 +# destination: 192.0.2.22 +# vrf: temp1 +# - source: 192.0.2.0/24 +# destination: 192.0.2.22 +# tags: +# tag_value: 2 +# route_pref: 4 +# vni: +# vni_number: 5 +# - name: test2 +# description: test description +# ip: +# auto_discard: true +# domain_name: test.com +# +# commands: +# - no vrf context test1 +# - no vrf context test2 +# +# after: +# vrfs: +# - name: management +# ip: +# name_server: +# address_list: +# - 192.168.255.1 +# route: +# - source: 0.0.0.0/0 +# destination: 192.168.255.1 + +# Using overridden + +# Before state: +# ------------- +# +# nxos#show running-config | section ^vrf +# vrf context management +# ip name-server 192.168.255.1 +# ip route 0.0.0.0/0 192.168.255.1 +# vrf context test1 +# description this is description +# ip domain-name example.com +# ip domain-list example.net +# ip domain-list example.org +# vni 5 +# ip auto-discard +# ip route 192.0.0.0/24 192.0.2.22 +# ip route 192.0.0.0/24 192.0.2.22 vrf temp1 +# ip route 192.0.2.0/24 192.0.2.22 tag 2 4 +# vrf context test2 +# description test description +# ip auto-discard +# ip domain-name test.com + +- name: Override VRF configuration + cisco.nxos.vrf_global: + config: + vrfs: + - name: management + ip: + name_server: + address_list: + - 192.168.255.1 + route: + - source: 0.0.0.0/0 + destination: 192.168.255.1 + - name: test1 + ip: + auto_discard: false + name_server: + address_list: + - 192.168.255.1 + route: + - source: 192.0.0.0/24 + destination: 192.0.2.22 + state: overridden + +# Task Output: +# ------------ +# +# before: +# vrfs: +# - name: management +# ip: +# name_server: +# address_list: +# - 192.168.255.1 +# route: +# - source: 0.0.0.0/0 +# destination: 192.168.255.1 +# - name: test1 +# description: this is description +# ip: +# domain_name: example.com +# domain_list: +# - example.net +# - example.org +# auto_discard: true +# route: +# - source: 192.0.0.0/24 +# destination: 192.0.2.22 +# - source: 192.0.0.0/24 +# destination: 192.0.2.22 +# vrf: temp1 +# - source: 192.0.2.0/24 +# destination: 192.0.2.22 +# tags: +# tag_value: 2 +# route_pref: 4 +# vni: +# vni_number: 5 +# - name: test2 +# description: test description +# ip: +# auto_discard: true +# domain_name: test.com +# +# commands: +# - vrf context test1 +# - no description this is description +# - no ip domain-name example.com +# - no ip domain-list example.net +# - no ip domain-list example.org +# - ip name-server 192.168.255.1 +# - no ip auto-discard +# - no vni 5 +# - no ip route 192.0.0.0/24 192.0.2.22 vrf temp1 +# - no ip route 192.0.2.0/24 192.0.2.22 tag 2 4 +# - vrf context test2 +# - no description test description +# - no ip auto-discard +# - no ip domain-name test.com +# +# after: +# vrfs: +# - name: management +# ip: +# name_server: +# address_list: +# - 192.168.255.1 +# route: +# - source: 0.0.0.0/0 +# destination: 192.168.255.1 +# - name: test1 +# ip: +# auto_discard: false +# name_server: +# address_list: +# - 192.168.255.1 +# route: +# - source: 192.0.0.0/24 +# destination: 192.0.2.22 + +# Using replaced + +# Before state: +# ------------- +# +# nxos# show running-config | section ^vrf +# vrf context management +# ip name-server 192.168.255.1 +# ip route 0.0.0.0/0 192.168.255.1 +# vrf context temp +# ip domain-name test.org +# ip domain-list example.net +# ip domain-list example.com +# ip domain-list test.org +# ip name-server 192.168.0.1 192.169.1.3 +# ip name-server 192.168.0.1 use-vrf temp1 +# multicast service-reflect interface Ethernet1/1 map interface Ethernet2/2 +# multicast service-reflect interface Ethernet2/1 map interface Ethernet4/2 +# description this is descrition +# vni 5 +# ip auto-discard +# ip route 192.0.0.0/24 192.0.2.22 +# ip route 192.0.0.0/24 192.0.2.22 vrf temp1 +# ip route 192.0.2.0/24 192.0.2.22 tag 2 4 +# ip mroute 192.168.1.0/24 192.168.1.1 +# ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1 +# ip icmp-errors source-interface po1 +# ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 +# ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 +# ip multicast multipath legacy +# ip multicast multipath resilient +# ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 +# ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 +# ip multicast group-range prefix-list temp2 +# ipv6 multicast multipath none +# ipv6 multicast multipath resilient +# ipv6 multicast group-range prefix-list temp2 +# ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2 +# ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::1 +# ipv6 mld ssm-translate ff32::/16 2001:db8:0:abcd::2 +# ipv6 mld ssm-translate ff32::/16 2001:db8:0:abcd::3 + +- name: Replaced state for VRF configuration + cisco.nxos.nxos_vrf_global: + config: + vrfs: + - ip: + name_server: + address_list: + - 192.168.255.1 + route: + - destination: 192.168.255.1 + source: 0.0.0.0/0 + name: management + - name: temp + description: Test + ip: + auto_discard: true + domain_list: + - invalid.com + - example.com + domain_name: test.org + state: replaced + +# Task Output: +# ------------ +# +# before: +# vrfs: +# - ip: +# name_server: +# address_list: +# - 192.168.255.1 +# route: +# - destination: 192.168.255.1 +# source: 0.0.0.0/0 +# name: management +# - description: this is descrition +# ip: +# auto_discard: true +# domain_list: +# - example.net +# - test.org +# - example.com +# domain_name: test.org +# icmp_err: +# source_interface: +# interface: port-channel +# interface_value: '1' +# igmp: +# ssm_translate: +# - group: 232.0.0.0/8 +# source: 10.1.1.1 +# - group: 239.1.2.3/24 +# source: 192.168.1.1 +# mroutes: +# - group: 192.168.1.0/24 +# source: 192.168.1.1 +# - group: 192.168.1.0/24 +# preference: 2 +# source: 192.168.1.2 +# vrf: temp1 +# multicast: +# multipath: +# resilient: true +# splitting_type: +# legacy: true +# rpf: +# - group_list_range: 238.1.0.0/24 +# vrf_name: temp1 +# - group_list_range: 239.1.0.0/24 +# vrf_name: temp1 +# name_server: +# address_list: +# - 192.168.0.1 +# - 192.169.1.3 +# use_vrf: +# source_address: 192.168.0.1 +# vrf: temp1 +# route: +# - destination: 192.0.2.22 +# source: 192.0.0.0/24 +# - destination: 192.0.2.22 +# source: 192.0.0.0/24 +# vrf: temp1 +# - destination: 192.0.2.22 +# source: 192.0.2.0/24 +# tags: +# route_pref: 4 +# tag_value: 2 +# ipv6: +# mld_ssm_translate: +# - group: ff28::/16 +# source: 2001:db8:0:abcd::2 +# - group: ff30::/16 +# source: 2001:db8:0:abcd::1 +# - group: ff32::/16 +# source: 2001:db8:0:abcd::2 +# - group: ff32::/16 +# source: 2001:db8:0:abcd::3 +# multicast: +# group_range_prefix_list: temp2 +# multipath: +# resilient: true +# splitting_type: +# none: true +# multicast: +# service_reflect: +# - map_to: Ethernet2/2 +# service_interface: Ethernet1/1 +# - map_to: Ethernet4/2 +# service_interface: Ethernet2/1 +# name: temp +# vni: +# vni_number: 5 +# +# commands: +# - vrf context temp +# - description Test +# - no ip name-server 192.168.0.1 192.169.1.3 +# - no ip icmp-errors source-interface port-channel 1 +# - no ip multicast multipath resilient +# - no ip multicast multipath legacy +# - no ip name-server 192.168.0.1 use-vrf temp1 +# - no vni 5 +# - no ipv6 multicast group-range prefix-list temp2 +# - no ipv6 multicast multipath resilient +# - no ipv6 multicast multipath none +# - ip domain-list invalid.com +# - no ip domain-list example.net +# - no ip domain-list test.org +# - no ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 +# - no ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 +# - no ip mroute 192.168.1.0/24 192.168.1.1 +# - no ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1 +# - no ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 +# - no ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 +# - no ip route 192.0.0.0/24 192.0.2.22 +# - no ip route 192.0.0.0/24 192.0.2.22 vrf temp1 +# - no ip route 192.0.2.0/24 192.0.2.22 tag 2 4 +# - no multicast service-reflect interface Ethernet1/1 map interface Ethernet2/2 +# - no multicast service-reflect interface Ethernet2/1 map interface Ethernet4/2 +# - no ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2 +# - no ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::1 +# - no ipv6 mld ssm-translate ff32::/16 2001:db8:0:abcd::2 +# - no ipv6 mld ssm-translate ff32::/16 2001:db8:0:abcd::3 +# +# after: +# vrfs: +# - ip: +# name_server: +# address_list: +# - 192.168.255.1 +# route: +# - destination: 192.168.255.1 +# source: 0.0.0.0/0 +# name: management +# - description: Test +# ip: +# auto_discard: true +# domain_list: +# - invalid.com +# - example.com +# domain_name: test.org +# multicast: +# rpf: +# - group_list_range: 238.1.0.0/24 +# vrf_name: temp1 +# - group_list_range: 239.1.0.0/24 +# vrf_name: temp1 +# +# After state: +# ------------ +# router-ios#show running-config | section ^vrf +# vrf context management +# ip name-server 192.168.255.1 +# ip route 0.0.0.0/0 192.168.255.1 +# vrf context temp +# ip domain-name test.org +# ip domain-list example.com +# ip domain-list invalid.com +# description Test +# ip auto-discard +# ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 +# ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 +# ip multicast group-range prefix-list temp2 +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - vrf context management + - description this is management vrf + - ip domain-name example.com +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - vrf context test1 + - description This is a test VRF + - ip route 192.0.0.0/24 192.0.2.22 +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.vrf_global.vrf_global import ( + Vrf_globalArgs, +) +from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.vrf_global.vrf_global import ( + Vrf_global, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Vrf_globalArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Vrf_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/plugins/plugin_utils/__init__.py b/plugins/plugin_utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration/targets/nxos_vrf_global/defaults/main.yaml b/tests/integration/targets/nxos_vrf_global/defaults/main.yaml new file mode 100644 index 000000000..871ea460c --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/defaults/main.yaml @@ -0,0 +1,2 @@ +--- +testcase: "[^_].*" diff --git a/tests/integration/targets/nxos_vrf_global/meta/main.yaml b/tests/integration/targets/nxos_vrf_global/meta/main.yaml new file mode 100644 index 000000000..f504a6ab2 --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/meta/main.yaml @@ -0,0 +1,3 @@ +--- +dependencies: + - prepare_nxos_tests diff --git a/tests/integration/targets/nxos_vrf_global/tasks/cli.yaml b/tests/integration/targets/nxos_vrf_global/tasks/cli.yaml new file mode 100644 index 000000000..ed4cb02e2 --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tasks/cli.yaml @@ -0,0 +1,21 @@ +--- +- name: Collect all CLI test cases + ansible.builtin.find: + paths: "{{ role_path }}/tests/common" + patterns: "{{ testcase }}.yaml" + use_regex: true + register: test_cases + delegate_to: localhost + +- name: Set test_items + ansible.builtin.set_fact: + test_items: "{{ test_cases.files | map(attribute='path') | list }}" + delegate_to: localhost + +- name: Run test case (connection=ansible.netcommon.network_cli) + ansible.builtin.include_tasks: "{{ test_case_to_run }}" + vars: + ansible_connection: ansible.netcommon.network_cli + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run diff --git a/tests/integration/targets/nxos_vrf_global/tasks/main.yml b/tests/integration/targets/nxos_vrf_global/tasks/main.yml new file mode 100644 index 000000000..53a64850f --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tasks/main.yml @@ -0,0 +1,22 @@ +--- +- name: Get vrf data + cisco.nxos.nxos_command: + commands: show running | section '^vrf' + register: result + +- name: Set management fact + ansible.builtin.set_fact: + management: + - ip: + name_server: + address_list: + - "{{ result.stdout[0] | regex_search('ip name-server ([0-9.]+)', '\\1') | first }}" + route: + - destination: "{{ result.stdout[0] | regex_search('ip route [0-9.]+/[0-9]+ ([0-9.]+)', '\\1') | first }}" + source: "{{ result.stdout[0] | regex_search('ip route ([0-9.]+/[0-9]+)', '\\1') | first }}" + name: management + +- name: Main task for vrf_global module + ansible.builtin.include_tasks: cli.yaml + tags: + - network_cli diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/_parsed.cfg b/tests/integration/targets/nxos_vrf_global/tests/common/_parsed.cfg new file mode 100644 index 000000000..60a0a2fdf --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/_parsed.cfg @@ -0,0 +1,14 @@ +vrf context management + ip name-server 192.168.255.1 + ip route 0.0.0.0/0 192.168.255.1 +vrf context temp + ip domain-name example.org + ip domain-list example.com + ip domain-list example.net + description Test + ip auto-discard + ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 + ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 + ip multicast group-range prefix-list temp2 +! +end diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/_populate_config.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/_populate_config.yaml new file mode 100644 index 000000000..a8b8ae4cd --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/_populate_config.yaml @@ -0,0 +1,19 @@ +--- +- name: Merge provided vrf configuration with device configuration + cisco.nxos.nxos_config: + lines: + - vrf context testvrf + - description testvrfhere + - ip auto-discard + - ip domain-name example.com + - vni 5 + - ip mroute 192.168.1.0/24 192.168.1.1 + - ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2 + - ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::5 + match: none + +- name: Merge another vrf + cisco.nxos.nxos_config: + lines: + - vrf context temp + match: none diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/_remove_config.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/_remove_config.yaml new file mode 100644 index 000000000..72901535c --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/_remove_config.yaml @@ -0,0 +1,36 @@ +--- +- name: Remove VRF global configurations + cisco.nxos.nxos_config: + lines: + - no vrf context testvrf + ignore_errors: true + register: testvrf + +- name: Wait for 20 seconds if VRF removal was successful + ansible.builtin.wait_for: + timeout: 20 + when: not testvrf.failed + +- name: Remove VRF global configurations + cisco.nxos.nxos_config: + lines: + - no vrf context temp + ignore_errors: true + register: temp + +- name: Wait for 20 seconds if VRF removal was successful + ansible.builtin.wait_for: + timeout: 20 + when: not temp.failed + +- name: Remove VRF global configurations + cisco.nxos.nxos_config: + lines: + - no vrf context VRF7 + ignore_errors: true + register: vrf7 + +- name: Wait for 20 seconds if VRF removal was successful + ansible.builtin.wait_for: + timeout: 20 + when: not vrf7.failed diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/deleted.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/deleted.yaml new file mode 100644 index 000000000..e8bf12318 --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/deleted.yaml @@ -0,0 +1,55 @@ +--- +- ansible.builtin.debug: + msg: Start Deleted integration state for nxos_vrf_global ansible_connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Delete given vrf configuration + register: result + cisco.nxos.nxos_vrf_global: &id001 + config: + vrfs: + - name: testvrf + state: deleted + + - name: Remove management from list of result + set_fact: + res_before: + before: + vrfs: "{{ result['before']['vrfs'] | rejectattr('name', '==', 'management') }}" + res_after: + after: + vrfs: "{{ result['after']['vrfs'] | rejectattr('name', '==', 'management') }}" + + - ansible.builtin.assert: + that: + - result.changed == true + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ deleted['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - "{{ general['after'] | symmetric_difference(res_before['before']) | length == 0 }}" + + - name: Assert that after dicts are correctly generated + ansible.builtin.assert: + that: + - deleted['after'] == res_after['after'] + + - name: Delete provided VRF global (idempotent) + register: result + cisco.nxos.nxos_vrf_global: *id001 + + - name: Assert that the previous task was idempotent + ansible.builtin.assert: + that: + - result.changed == false + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/empty_config.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/empty_config.yaml new file mode 100644 index 000000000..1139e0746 --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/empty_config.yaml @@ -0,0 +1,68 @@ +--- +- ansible.builtin.debug: + msg: START nxos_vrf_global empty_config.yaml integration tests on connection={{ ansible_connection }} + +- name: Merged with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.nxos.nxos_vrf_global: + config: + state: merged + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state merged' + +- name: Replaced with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.nxos.nxos_vrf_global: + config: + state: replaced + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state replaced' + +- name: Overridden with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.nxos.nxos_vrf_global: + config: + state: overridden + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state overridden' + +- name: Rendered with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.nxos.nxos_vrf_global: + config: + state: rendered + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state rendered' + +- name: Parsed with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.nxos.nxos_vrf_global: + running_config: + state: parsed + +- ansible.builtin.assert: + that: + - result.msg == 'value of running_config parameter must not be empty for state parsed' + +- name: Purged with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.nxos.nxos_vrf_global: + config: + state: purged + +- ansible.builtin.debug: + msg: END nxos_vrf_global empty_config integration tests on connection={{ ansible_connection }} diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/gathered.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/gathered.yaml new file mode 100644 index 000000000..6cfa4872d --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/gathered.yaml @@ -0,0 +1,28 @@ +--- +- ansible.builtin.debug: + msg: START nxos_vrf_global gathered integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Gather the provided configuration with the existing running configuration + register: result + cisco.nxos.nxos_vrf_global: + config: + state: gathered + + - name: Remove management from list of result + set_fact: + res_parsed: + gathered: + vrfs: "{{ result['gathered']['vrfs'] | rejectattr('name', '==', 'management') }}" + + - name: Assert + ansible.builtin.assert: + that: + - result.changed == false + - gathered['after'] == res_parsed['gathered'] + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/merged.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/merged.yaml new file mode 100644 index 000000000..62706f137 --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/merged.yaml @@ -0,0 +1,122 @@ +--- +- ansible.builtin.debug: + msg: START Merged nxos_vrf_global state for integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Merge provided configuration with device configuration + register: result + cisco.nxos.nxos_vrf_global: &id001 + config: + vrfs: + - description: this-is-test + ip: + auto_discard: true + domain_list: + - example.net + domain_name: example.org + icmp_err: + source_interface: + interface: port-channel + interface_value: "1" + igmp: + ssm_translate: + - group: 232.0.0.0/8 + source: 10.1.1.1 + - group: 239.1.2.3/24 + source: 192.168.1.1 + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + - group: 192.168.1.0/24 + preference: 2 + source: 192.168.1.2 + vrf: temp + multicast: + multipath: + resilient: true + splitting_type: + legacy: true + rpf: + - group_list_range: 238.1.0.0/24 + vrf_name: temp + - group_list_range: 239.1.0.0/24 + vrf_name: temp + name_server: + address_list: + - 192.168.0.1 + - 192.168.0.2 + - 192.168.1.1 + - 192.169.1.3 + use_vrf: + source_address: 192.168.0.1 + vrf: temp + route: + - destination: 192.0.2.22 + source: 192.0.0.0/24 + - destination: 192.0.2.22 + source: 192.0.0.0/24 + vrf: temp + - destination: 192.0.2.22 + source: 192.0.2.0/24 + tags: + route_pref: 4 + tag_value: 2 + ipv6: + mld_ssm_translate: + - group: ff28::/16 + source: 2001:db8:0:abcd::2 + - group: ff30::/16 + source: 2001:db8:0:abcd::5 + multicast: + group_range_prefix_list: temp + multipath: + resilient: true + splitting_type: + none: true + multicast: + service_reflect: + - map_to: Ethernet2/2 + service_interface: Ethernet1/1 + - map_to: Ethernet4/2 + service_interface: Ethernet2/1 + name: VRF7 + vni: + vni_number: 5 + state: merged + + - name: Remove management from list of result + set_fact: + res_before: + before: + vrfs: "{{ result['before']['vrfs'] | rejectattr('name', '==', 'management') }}" + res_after: + after: + vrfs: "{{ result['after']['vrfs'] | rejectattr('name', '==', 'management') }}" + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ merged['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - res_before['before']['vrfs'] == [] + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - res_after['after']['vrfs'] == merged['after']['vrfs'] + + - name: Merge provided configuration with device configuration (idempotent) + register: result + cisco.nxos.nxos_vrf_global: *id001 + + - name: Assert that the previous task was idempotent + ansible.builtin.assert: + that: + - result['changed'] == false + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/overridden.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/overridden.yaml new file mode 100644 index 000000000..d4bb0b5c7 --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/overridden.yaml @@ -0,0 +1,70 @@ +--- +- ansible.builtin.debug: + msg: START nxos_vrf_global overridden integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Override the provided configuration with the existing running configuration + cisco.nxos.nxos_vrf_global: &overridden + config: + vrfs: + - "{{ management[0] }}" + - name: testvrf + description: Test + ip: + auto_discard: true + domain_list: + - example.com + domain_name: example.net + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + - group: 192.168.1.0/24 + preference: 2 + source: 192.168.1.2 + vrf: temp + name_server: + address_list: + - 192.168.0.1 + - 192.168.0.2 + - 192.168.1.1 + - 192.169.1.3 + use_vrf: + source_address: 192.168.0.1 + vrf: temp + state: overridden + register: result + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ overridden['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - "{{ overridden['before'] == result['before'] }}" + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - "{{ overridden['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Idempotency check + cisco.nxos.nxos_vrf_global: *overridden + register: result + + - name: Assert that no changes were made + ansible.builtin.assert: + that: + - result['changed'] == false + - result.commands|length == 0 + + - name: Assert that before dict is correctly generated + ansible.builtin.assert: + that: + - "{{ overridden['after'] | symmetric_difference(result['before']) |length == 0 }}" + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/parsed.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/parsed.yaml new file mode 100644 index 000000000..e15b608c6 --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/parsed.yaml @@ -0,0 +1,14 @@ +--- +- ansible.builtin.debug: + msg: START nxos_vrf_global parsed integration tests on connection={{ ansible_connection }} + +- name: Parse the commands for provided configuration + register: result + cisco.nxos.nxos_vrf_global: + running_config: "{{ lookup('file', '_parsed.cfg') }}" + state: parsed + +- ansible.builtin.assert: + that: + - result.changed == false + - parsed['after'] == result['parsed'] diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/rendered.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/rendered.yaml new file mode 100644 index 000000000..50cf0e13f --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/rendered.yaml @@ -0,0 +1,41 @@ +--- +- ansible.builtin.debug: + msg: START nxos_vrf_global rendered integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Render provided configuration with device configuration + register: result + cisco.nxos.nxos_vrf_global: + config: + vrfs: + - description: Test + ip: + auto_discard: true + domain_list: + - example.com + domain_name: example.net + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + - group: 192.168.1.0/24 + preference: 2 + source: 192.168.1.2 + vrf: temp + name_server: + address_list: + - 192.168.0.1 + - 192.168.0.2 + - 192.168.1.1 + - 192.169.1.3 + use_vrf: + source_address: 192.168.0.1 + vrf: temp + name: testvrf + state: rendered + + - ansible.builtin.assert: + that: + - result.changed == false + - result.rendered|symmetric_difference(rendered.commands) == [] diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/replaced.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/replaced.yaml new file mode 100644 index 000000000..c57a6f446 --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/replaced.yaml @@ -0,0 +1,69 @@ +--- +- ansible.builtin.debug: + msg: START replaced nxos_vrf_global state for integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Replace the provided configuration with the existing running configuration + register: result + cisco.nxos.nxos_vrf_global: &id001 + config: + vrfs: + - name: temp + description: Test + ip: + auto_discard: true + domain_list: + - example.com + domain_name: example.org + - name: testvrf + description: Test + ip: + auto_discard: true + domain_list: + - example.com + domain_name: example.net + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + - group: 192.168.1.0/24 + preference: 2 + source: 192.168.1.2 + vrf: temp + name_server: + address_list: + - 192.168.0.1 + - 192.168.0.2 + - 192.168.1.1 + - 192.169.1.3 + use_vrf: + source_address: 192.168.0.1 + vrf: temp + state: replaced + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ replaced['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dict is correctly generated + ansible.builtin.assert: + that: + - "{{ replaced['before'] == result['before'] }}" + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - replaced['after'] == result['after'] + + - name: Replaced provided VRF global configuration (idempotent) + register: result + cisco.nxos.nxos_vrf_global: *id001 + - name: Assert that task was idempotent + ansible.builtin.assert: + that: + - result['changed'] == false + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/nxos_vrf_global/tests/common/rtt.yaml b/tests/integration/targets/nxos_vrf_global/tests/common/rtt.yaml new file mode 100644 index 000000000..cff796bdc --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/tests/common/rtt.yaml @@ -0,0 +1,74 @@ +- ansible.builtin.debug: + msg: START nxos_vrf_global round trip integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Merge the provided configuration with the existing running configuration + cisco.nxos.nxos_vrf_global: + config: + vrfs: + - name: VRF7 + description: Test + ip: + auto_discard: true + domain_list: + - example.com + domain_name: example.net + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + name_server: + address_list: + - 192.168.0.1 + - 192.168.0.2 + - 192.168.1.1 + - 192.169.1.3 + state: merged + register: result + + - name: Nxos_vrf_global RTT - gather vrf_global facts + cisco.nxos.nxos_facts: + gather_network_resources: + - vrf_global + + - name: Overridden the provided configuration with the existing running configuration + cisco.nxos.nxos_vrf_global: + config: + vrfs: + - "{{ management[0] }}" + - name: testvrf + description: testing + ip: + auto_discard: true + domain_list: + - example.com + domain_name: example.net + name_server: + address_list: + - 192.168.0.1 + - 192.168.0.2 + - 192.168.1.1 + - 192.169.1.3 + state: overridden + register: result + + - ansible.builtin.assert: + that: + - result.commands|length == 13 + - result.changed == true + - result.commands|symmetric_difference(rtt.commands) == [] + + - name: Revert back to base configuration using facts round trip + register: revert + cisco.nxos.nxos_vrf_global: + config: "{{ ansible_facts['network_resources']['vrf_global'] }}" + state: replaced + + - ansible.builtin.assert: + that: + - revert.changed == true + - revert['commands'] == rtt['revert'] + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/nxos_vrf_global/vars/main.yaml b/tests/integration/targets/nxos_vrf_global/vars/main.yaml new file mode 100644 index 000000000..54705f0a4 --- /dev/null +++ b/tests/integration/targets/nxos_vrf_global/vars/main.yaml @@ -0,0 +1,347 @@ +general: + after: + vrfs: + - name: temp + - description: testvrfhere + ip: + auto_discard: true + domain_name: example.com + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + ipv6: + mld_ssm_translate: + - group: "ff28::/16" + source: "2001:db8:0:abcd::2" + - group: "ff30::/16" + source: "2001:db8:0:abcd::5" + name: testvrf + vni: + vni_number: 5 +merged: + commands: + - vrf context VRF7 + - description this-is-test + - ip auto-discard + - ip domain-name example.org + - ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3 + - ip icmp-errors source-interface port-channel 1 + - ip multicast multipath resilient + - ip multicast multipath legacy + - ip name-server 192.168.0.1 use-vrf temp + - vni 5 + - ipv6 multicast group-range prefix-list temp + - ipv6 multicast multipath resilient + - ipv6 multicast multipath none + - ip domain-list example.net + - ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 + - ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 + - ip mroute 192.168.1.0/24 192.168.1.1 + - ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp + - ip multicast rpf select vrf temp group-list 238.1.0.0/24 + - ip multicast rpf select vrf temp group-list 239.1.0.0/24 + - ip route 192.0.0.0/24 192.0.2.22 + - ip route 192.0.0.0/24 192.0.2.22 vrf temp + - ip route 192.0.2.0/24 192.0.2.22 tag 2 4 + - multicast service-reflect interface Ethernet1/1 map interface Ethernet2/2 + - multicast service-reflect interface Ethernet2/1 map interface Ethernet4/2 + - "ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2" + - "ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::5" + after: + vrfs: + - description: this-is-test + ip: + auto_discard: true + domain_list: + - example.net + domain_name: example.org + icmp_err: + source_interface: + interface: port-channel + interface_value: "1" + igmp: + ssm_translate: + - group: 232.0.0.0/8 + source: 10.1.1.1 + - group: 239.1.2.3/24 + source: 192.168.1.1 + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + - group: 192.168.1.0/24 + preference: 2 + source: 192.168.1.2 + vrf: temp + multicast: + multipath: + resilient: true + splitting_type: + legacy: true + rpf: + - group_list_range: 238.1.0.0/24 + vrf_name: temp + - group_list_range: 239.1.0.0/24 + vrf_name: temp + name_server: + address_list: + - 192.168.0.1 + - 192.168.0.2 + - 192.168.1.1 + - 192.169.1.3 + use_vrf: + source_address: 192.168.0.1 + vrf: temp + route: + - destination: 192.0.2.22 + source: 192.0.0.0/24 + - destination: 192.0.2.22 + source: 192.0.0.0/24 + vrf: temp + - destination: 192.0.2.22 + source: 192.0.2.0/24 + tags: + route_pref: 4 + tag_value: 2 + ipv6: + mld_ssm_translate: + - group: "ff28::/16" + source: "2001:db8:0:abcd::2" + - group: "ff30::/16" + source: "2001:db8:0:abcd::5" + multicast: + group_range_prefix_list: temp + multipath: + resilient: true + splitting_type: + none: true + multicast: + service_reflect: + - map_to: Ethernet2/2 + service_interface: Ethernet1/1 + - map_to: Ethernet4/2 + service_interface: Ethernet2/1 + name: VRF7 + vni: + vni_number: 5 +deleted: + commands: + - vrf context testvrf + - no description testvrfhere + - no ip auto-discard + - no ip domain-name example.com + - no vni 5 + - no ip mroute 192.168.1.0/24 192.168.1.1 + - "no ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2" + - "no ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::5" + after: + vrfs: + - name: temp + - name: testvrf +gathered: + after: + vrfs: + - name: temp + - description: testvrfhere + ip: + auto_discard: true + domain_name: example.com + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + ipv6: + mld_ssm_translate: + - group: "ff28::/16" + source: "2001:db8:0:abcd::2" + - group: "ff30::/16" + source: "2001:db8:0:abcd::5" + name: testvrf + vni: + vni_number: 5 +overridden: + commands: + - vrf context testvrf + - description Test + - ip domain-name example.net + - ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3 + - ip name-server 192.168.0.1 use-vrf temp + - no vni 5 + - ip domain-list example.com + - ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp + - "no ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2" + - "no ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::5" + after: + vrfs: + - "{{ management[0] }}" + - name: temp + - description: Test + ip: + auto_discard: true + domain_list: + - example.com + domain_name: example.net + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + - group: 192.168.1.0/24 + preference: 2 + source: 192.168.1.2 + vrf: temp + name_server: + address_list: + - 192.168.0.1 + - 192.168.0.2 + - 192.168.1.1 + - 192.169.1.3 + use_vrf: + source_address: 192.168.0.1 + vrf: temp + name: testvrf + before: + vrfs: + - "{{ management[0] }}" + - name: temp + - description: testvrfhere + ip: + auto_discard: true + domain_name: example.com + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + ipv6: + mld_ssm_translate: + - group: "ff28::/16" + source: "2001:db8:0:abcd::2" + - group: "ff30::/16" + source: "2001:db8:0:abcd::5" + name: testvrf + vni: + vni_number: 5 +replaced: + after: + vrfs: + - "{{ management[0] }}" + - description: Test + ip: + auto_discard: true + domain_list: + - example.com + domain_name: example.org + name: temp + - description: Test + ip: + auto_discard: true + domain_list: + - example.com + domain_name: example.net + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + - group: 192.168.1.0/24 + preference: 2 + source: 192.168.1.2 + vrf: temp + name_server: + address_list: + - 192.168.0.1 + - 192.168.0.2 + - 192.168.1.1 + - 192.169.1.3 + use_vrf: + source_address: 192.168.0.1 + vrf: temp + name: testvrf + before: + vrfs: + - "{{ management[0] }}" + - name: temp + - description: testvrfhere + ip: + auto_discard: true + domain_name: example.com + mroutes: + - group: 192.168.1.0/24 + source: 192.168.1.1 + ipv6: + mld_ssm_translate: + - group: "ff28::/16" + source: "2001:db8:0:abcd::2" + - group: "ff30::/16" + source: "2001:db8:0:abcd::5" + name: testvrf + vni: + vni_number: 5 + commands: + - vrf context temp + - description Test + - ip auto-discard + - ip domain-name example.org + - ip domain-list example.com + - vrf context testvrf + - description Test + - ip domain-name example.net + - ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3 + - ip name-server 192.168.0.1 use-vrf temp + - no vni 5 + - ip domain-list example.com + - ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp + - "no ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2" + - "no ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::5" +parsed: + after: + vrfs: + - ip: + name_server: + address_list: + - 192.168.255.1 + route: + - destination: 192.168.255.1 + source: 0.0.0.0/0 + name: management + - description: Test + ip: + auto_discard: true + domain_list: + - example.com + - example.net + domain_name: example.org + multicast: + rpf: + - group_list_range: 238.1.0.0/24 + vrf_name: temp1 + - group_list_range: 239.1.0.0/24 + vrf_name: temp1 + name: temp +rendered: + commands: + - vrf context testvrf + - description Test + - ip auto-discard + - ip domain-name example.net + - ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3 + - ip name-server 192.168.0.1 use-vrf temp + - ip domain-list example.com + - ip mroute 192.168.1.0/24 192.168.1.1 + - ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp +rtt: + commands: + - vrf context VRF7 + - no description Test + - no ip auto-discard + - no ip domain-name example.net + - no ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3 + - no ip domain-list example.com + - no ip mroute 192.168.1.0/24 192.168.1.1 + - vrf context testvrf + - description testing + - ip auto-discard + - ip domain-name example.net + - ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3 + - ip domain-list example.com + revert: + - vrf context VRF7 + - description Test + - ip auto-discard + - ip domain-name example.net + - ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3 + - ip domain-list example.com + - ip mroute 192.168.1.0/24 192.168.1.1 diff --git a/tests/unit/modules/network/nxos/test_nxos_vrf_global.py b/tests/unit/modules/network/nxos/test_nxos_vrf_global.py new file mode 100644 index 000000000..26f6f6fa0 --- /dev/null +++ b/tests/unit/modules/network/nxos/test_nxos_vrf_global.py @@ -0,0 +1,917 @@ +# (c) 2024, Ansible by Red Hat, inc +# 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 +from textwrap import dedent +from unittest.mock import patch + +from ansible_collections.cisco.nxos.plugins.modules import nxos_vrf_global + +from .nxos_module import TestNxosModule, set_module_args + + +class TestNxosVrfGlobalModule(TestNxosModule): + """Test the nxos_vrf_global module.""" + + module = nxos_vrf_global + + def setUp(self): + """Set up for nxos_vrf_global module tests.""" + super(TestNxosVrfGlobalModule, self).setUp() + + self.mock_get_resource_connection_facts = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base." + "get_resource_connection", + ) + self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start() + + self.mock_execute_show_command = patch( + "ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.vrf_global.vrf_global." + "Vrf_globalFacts.get_config", + ) + self.execute_show_command = self.mock_execute_show_command.start() + + def tearDown(self): + super(TestNxosVrfGlobalModule, self).tearDown() + self.mock_get_resource_connection_facts.stop() + self.mock_execute_show_command.stop() + + def test_nxos_vrf_global_merged(self): + self.execute_show_command.return_value = dedent( + """\ + vrf context management + ip name-server 192.168.255.1 + ip route 0.0.0.0/0 192.168.255.1 + """, + ) + + set_module_args( + dict( + config={ + "vrfs": [ + { + "description": "this is descrition", + "ip": { + "auto_discard": True, + "domain_list": [ + "anisble.com", + "redhat.com", + "res.com", + ], + "domain_name": "redx.com", + "icmp_err": { + "source_interface": { + "interface": "port-channel", + "interface_value": "1", + }, + }, + "igmp": { + "ssm_translate": [ + { + "group": "232.0.0.0/8", + "source": "10.1.1.1", + }, + { + "group": "239.1.2.3/24", + "source": "192.168.1.1", + }, + ], + }, + "mroutes": [ + { + "group": "192.168.1.0/24", + "source": "192.168.1.1", + }, + { + "group": "192.168.1.0/24", + "preference": 2, + "source": "192.168.1.2", + "vrf": "temp1", + }, + ], + "multicast": { + "multipath": { + "resilient": True, + "splitting_type": { + "legacy": True, + }, + }, + "rpf": [ + { + "group_list_range": "238.1.0.0/24", + "vrf_name": "temp1", + }, + { + "group_list_range": "239.1.0.0/24", + "vrf_name": "temp1", + }, + ], + }, + "name_server": { + "address_list": [ + "192.168.0.1", + "192.168.0.2", + "192.168.1.1", + "192.169.1.3", + ], + "use_vrf": { + "source_address": "192.168.0.1", + "vrf": "temp1", + }, + }, + "route": [ + { + "destination": "192.0.2.22", + "source": "192.0.0.0/24", + }, + { + "destination": "192.0.2.22", + "source": "192.0.0.0/24", + "vrf": "temp1", + }, + { + "destination": "192.0.2.22", + "source": "192.0.2.0/24", + "tags": { + "route_pref": 4, + "tag_value": 2, + }, + }, + ], + }, + "ipv6": { + "mld_ssm_translate": [ + { + "group": "ff28::/16", + "source": "2001:db8:0:abcd::2", + }, + { + "group": "ff30::/16", + "source": "2001:db8:0:abcd::5", + }, + ], + "multicast": { + "group_range_prefix_list": "temp2", + "multipath": { + "resilient": True, + "splitting_type": { + "none": True, + }, + }, + }, + }, + "multicast": { + "service_reflect": [ + { + "map_to": "Ethernet2/2", + "service_interface": "Ethernet1/1", + }, + { + "map_to": "Ethernet4/2", + "service_interface": "Ethernet2/1", + }, + ], + }, + "name": "test1", + "vni": { + "vni_number": 5, + }, + }, + ], + }, + state="merged", + ), + ) + commands = [ + "vrf context test1", + "description this is descrition", + "ip auto-discard", + "ip domain-name redx.com", + "ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3", + "ip icmp-errors source-interface port-channel 1", + "ip multicast multipath resilient", + "ip multicast multipath legacy", + "ip name-server 192.168.0.1 use-vrf temp1", + "vni 5", + "ipv6 multicast group-range prefix-list temp2", + "ipv6 multicast multipath resilient", + "ipv6 multicast multipath none", + "ip domain-list res.com", + "ip domain-list redhat.com", + "ip domain-list anisble.com", + "ip igmp ssm-translate 232.0.0.0/8 10.1.1.1", + "ip igmp ssm-translate 239.1.2.3/24 192.168.1.1", + "ip mroute 192.168.1.0/24 192.168.1.1", + "ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1", + "ip multicast rpf select vrf temp1 group-list 238.1.0.0/24", + "ip multicast rpf select vrf temp1 group-list 239.1.0.0/24", + "ip route 192.0.0.0/24 192.0.2.22", + "ip route 192.0.0.0/24 192.0.2.22 vrf temp1", + "ip route 192.0.2.0/24 192.0.2.22 tag 2 4", + "multicast service-reflect interface Ethernet1/1 map interface Ethernet2/2", + "multicast service-reflect interface Ethernet2/1 map interface Ethernet4/2", + "ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2", + "ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::5", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_nxos_vrf_global_merged_idemp(self): + self.execute_show_command.return_value = dedent( + """\ + vrf context test1 + ip domain-name redx.com + ip domain-list anisble.com + ip domain-list redhat.com + ip domain-list res.com + ip name-server 192.168.0.1 192.168.0.2 192.168.1.1 192.169.1.3 + ip name-server 192.168.0.1 use-vrf temp1 + multicast service-reflect interface Ethernet1/1 map interface Ethernet2/2 + multicast service-reflect interface Ethernet2/1 map interface Ethernet4/2 + description this is descrition + vni 5 + ip auto-discard + ip route 192.0.0.0/24 192.0.2.22 + ip route 192.0.0.0/24 192.0.2.22 vrf temp1 + ip route 192.0.2.0/24 192.0.2.22 tag 2 4 + ip mroute 192.168.1.0/24 192.168.1.1 + ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1 + ip icmp-errors source-interface po1 + ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 + ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 + ip multicast multipath legacy + ip multicast multipath resilient + ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 + ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 + ip multicast group-range prefix-list temp2 + ipv6 multicast multipath none + ipv6 multicast multipath resilient + ipv6 multicast group-range prefix-list temp2 + ipv6 mld ssm-translate ff28::/16 2001:db8:0:abcd::2 + ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::1 + ipv6 mld ssm-translate ff32::/16 2001:db8:0:abcd::2 + ipv6 mld ssm-translate ff32::/16 2001:db8:0:abcd::3 + ipv6 mld ssm-translate ff30::/16 2001:db8:0:abcd::5 + """, + ) + + set_module_args( + dict( + config={ + "vrfs": [ + { + "description": "this is descrition", + "ip": { + "auto_discard": True, + "domain_list": [ + "anisble.com", + "redhat.com", + "res.com", + ], + "domain_name": "redx.com", + "icmp_err": { + "source_interface": { + "interface": "port-channel", + "interface_value": "1", + }, + }, + "igmp": { + "ssm_translate": [ + { + "group": "232.0.0.0/8", + "source": "10.1.1.1", + }, + { + "group": "239.1.2.3/24", + "source": "192.168.1.1", + }, + ], + }, + "mroutes": [ + { + "group": "192.168.1.0/24", + "source": "192.168.1.1", + }, + { + "group": "192.168.1.0/24", + "preference": 2, + "source": "192.168.1.2", + "vrf": "temp1", + }, + ], + "multicast": { + "multipath": { + "resilient": True, + "splitting_type": { + "legacy": True, + }, + }, + "rpf": [ + { + "group_list_range": "238.1.0.0/24", + "vrf_name": "temp1", + }, + { + "group_list_range": "239.1.0.0/24", + "vrf_name": "temp1", + }, + ], + }, + "name_server": { + "address_list": [ + "192.168.0.1", + "192.168.0.2", + "192.168.1.1", + "192.169.1.3", + ], + "use_vrf": { + "source_address": "192.168.0.1", + "vrf": "temp1", + }, + }, + "route": [ + { + "destination": "192.0.2.22", + "source": "192.0.0.0/24", + }, + { + "destination": "192.0.2.22", + "source": "192.0.0.0/24", + "vrf": "temp1", + }, + { + "destination": "192.0.2.22", + "source": "192.0.2.0/24", + "tags": { + "route_pref": 4, + "tag_value": 2, + }, + }, + ], + }, + "ipv6": { + "mld_ssm_translate": [ + { + "group": "ff28::/16", + "source": "2001:db8:0:abcd::2", + }, + { + "group": "ff30::/16", + "source": "2001:db8:0:abcd::5", + }, + ], + "multicast": { + "group_range_prefix_list": "temp2", + "multipath": { + "resilient": True, + "splitting_type": { + "none": True, + }, + }, + }, + }, + "multicast": { + "service_reflect": [ + { + "map_to": "Ethernet2/2", + "service_interface": "Ethernet1/1", + }, + { + "map_to": "Ethernet4/2", + "service_interface": "Ethernet2/1", + }, + ], + }, + "name": "test1", + "vni": { + "vni_number": 5, + }, + }, + ], + }, + state="merged", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_nxos_vrf_global_deleted(self): + self.execute_show_command.return_value = dedent( + """\ + vrf context management + ip name-server 192.168.255.1 + ip route 0.0.0.0/0 192.168.255.1 + vrf context test1 + description this is descrition + ip domain-name redx.com + ip domain-list anisble.com + ip domain-list redhat.com + ip domain-list res.com + vni 5 + ip auto-discard + ip route 192.0.0.0/24 192.0.2.22 + ip route 192.0.0.0/24 192.0.2.22 vrf temp1 + ip route 192.0.2.0/24 192.0.2.22 tag 2 4 + ip mroute 192.168.1.0/24 192.168.1.1 + ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1 + ip icmp-errors source-interface po1 + ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 + ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 + ip multicast multipath legacy + ip multicast multipath resilient + ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 + ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 + ip multicast group-range prefix-list temp2 + """, + ) + + set_module_args( + dict( + config={ + "vrfs": [ + { + "name": "test1", + }, + ], + }, + state="deleted", + ), + ) + + commands = [ + "vrf context test1", + "no description this is descrition", + "no ip auto-discard", + "no ip domain-name redx.com", + "no ip icmp-errors source-interface port-channel 1", + "no ip multicast multipath resilient", + "no ip multicast multipath legacy", + "no vni 5", + "no ip domain-list anisble.com", + "no ip domain-list res.com", + "no ip domain-list redhat.com", + "no ip igmp ssm-translate 232.0.0.0/8 10.1.1.1", + "no ip igmp ssm-translate 239.1.2.3/24 192.168.1.1", + "no ip mroute 192.168.1.0/24 192.168.1.1", + "no ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1", + "no ip multicast rpf select vrf temp1 group-list 238.1.0.0/24", + "no ip multicast rpf select vrf temp1 group-list 239.1.0.0/24", + "no ip route 192.0.0.0/24 192.0.2.22", + "no ip route 192.0.0.0/24 192.0.2.22 vrf temp1", + "no ip route 192.0.2.0/24 192.0.2.22 tag 2 4", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_global_deleted_empty(self): + self.execute_show_command.return_value = dedent( + """\ + vrf context management + ip name-server 192.168.255.1 + ip route 0.0.0.0/0 192.168.255.1 + vrf context test1 + description this is descrition + ip domain-name redx.com + ip domain-list anisble.com + ip domain-list res.com + """, + ) + set_module_args(dict(config=dict(), state="deleted")) + commands = [ + "vrf context management", + "no ip name-server 192.168.255.1", + "no ip route 0.0.0.0/0 192.168.255.1", + "vrf context test1", + "no description this is descrition", + "no ip domain-name redx.com", + "no ip domain-list anisble.com", + "no ip domain-list res.com", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_nxos_vrf_global_overridden(self): + self.execute_show_command.return_value = dedent( + """\ + vrf context management + ip name-server 192.168.255.1 + ip route 0.0.0.0/0 192.168.255.1 + vrf context test1 + description this is descrition + ip domain-name redx.com + ip domain-list anisble.com + ip domain-list res.com + vni 5 + ip auto-discard + ip route 192.0.0.0/24 192.0.2.22 + ip route 192.0.0.0/24 192.0.2.22 vrf temp1 + ip route 192.0.2.0/24 192.0.2.22 tag 2 4 + ip mroute 192.168.1.0/24 192.168.1.1 + ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1 + ip icmp-errors source-interface po1 + ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 + ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 + ip multicast multipath legacy + ip multicast multipath resilient + ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 + ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 + ip multicast group-range prefix-list temp2 + """, + ) + + set_module_args( + dict( + config={ + "vrfs": [ + { + "name": "test1", + "vni": { + "vni_number": 6, + }, + "description": "test description", + "ip": { + "auto_discard": False, + "domain_list": [ + "redhat.com", + "greeblue.com", + ], + "domain_name": "redblue.com", + "icmp_err": { + "source_interface": { + "interface": "port-channel", + "interface_value": "3", + }, + }, + }, + }, + ], + }, + state="overridden", + ), + ) + commands = [ + "vrf context management", + "no ip name-server 192.168.255.1", + "no ip route 0.0.0.0/0 192.168.255.1", + "vrf context test1", + "description test description", + "no ip auto-discard", + "ip domain-name redblue.com", + "ip icmp-errors source-interface port-channel 3", + "no ip multicast multipath resilient", + "no ip multicast multipath legacy", + "vni 6", + "ip domain-list redhat.com", + "ip domain-list greeblue.com", + "no ip domain-list res.com", + "no ip domain-list anisble.com", + "no ip igmp ssm-translate 232.0.0.0/8 10.1.1.1", + "no ip igmp ssm-translate 239.1.2.3/24 192.168.1.1", + "no ip mroute 192.168.1.0/24 192.168.1.1", + "no ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1", + "no ip multicast rpf select vrf temp1 group-list 238.1.0.0/24", + "no ip multicast rpf select vrf temp1 group-list 239.1.0.0/24", + "no ip route 192.0.0.0/24 192.0.2.22", + "no ip route 192.0.0.0/24 192.0.2.22 vrf temp1", + "no ip route 192.0.2.0/24 192.0.2.22 tag 2 4", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_nxos_vrf_replaced(self): + self.execute_show_command.return_value = dedent( + """\ + vrf context management + ip name-server 192.168.255.1 + ip route 0.0.0.0/0 192.168.255.1 + vrf context test1 + description this is descrition + ip domain-name redx.com + ip domain-list anisble.com + ip domain-list res.com + vni 5 + ip auto-discard + ip route 192.0.0.0/24 192.0.2.22 + ip route 192.0.0.0/24 192.0.2.22 vrf temp1 + ip route 192.0.2.0/24 192.0.2.22 tag 2 4 + ip mroute 192.168.1.0/24 192.168.1.1 + ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1 + ip icmp-errors source-interface po1 + ip igmp ssm-translate 232.0.0.0/8 10.1.1.1 + ip igmp ssm-translate 239.1.2.3/24 192.168.1.1 + ip multicast multipath legacy + ip multicast multipath resilient + ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 + ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 + ip multicast group-range prefix-list temp2 + """, + ) + + set_module_args( + dict( + config={ + "vrfs": [ + { + "name": "test1", + "vni": { + "vni_number": 6, + }, + "description": "test description", + "ip": { + "auto_discard": False, + "domain_list": [ + "redhat.com", + "greeblue.com", + ], + "domain_name": "redblue.com", + "icmp_err": { + "source_interface": { + "interface": "port-channel", + "interface_value": "3", + }, + }, + }, + "multicast": { + "service_reflect": [ + { + "map_to": "Ethernet2/2", + "service_interface": "Ethernet1/1", + }, + { + "map_to": "Ethernet4/2", + "service_interface": "Ethernet2/1", + }, + ], + }, + }, + ], + }, + state="replaced", + ), + ) + commands = [ + "vrf context test1", + "description test description", + "no ip auto-discard", + "ip domain-name redblue.com", + "ip icmp-errors source-interface port-channel 3", + "no ip multicast multipath resilient", + "no ip multicast multipath legacy", + "vni 6", + "ip domain-list redhat.com", + "ip domain-list greeblue.com", + "no ip domain-list anisble.com", + "no ip domain-list res.com", + "no ip igmp ssm-translate 232.0.0.0/8 10.1.1.1", + "no ip igmp ssm-translate 239.1.2.3/24 192.168.1.1", + "no ip mroute 192.168.1.0/24 192.168.1.1", + "no ip mroute 192.168.1.0/24 192.168.1.2 2 vrf temp1", + "no ip multicast rpf select vrf temp1 group-list 238.1.0.0/24", + "no ip multicast rpf select vrf temp1 group-list 239.1.0.0/24", + "no ip route 192.0.0.0/24 192.0.2.22", + "no ip route 192.0.0.0/24 192.0.2.22 vrf temp1", + "no ip route 192.0.2.0/24 192.0.2.22 tag 2 4", + "multicast service-reflect interface Ethernet1/1 map interface Ethernet2/2", + "multicast service-reflect interface Ethernet2/1 map interface Ethernet4/2", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_nxos_vrf_global_replaced_idemp(self): + self.execute_show_command.return_value = dedent( + """\ + vrf context management + ip name-server 192.168.255.1 + ip route 0.0.0.0/0 192.168.255.1 + vrf context test1 + description this is descrition + ip domain-name redx.com + ip domain-list redhat.com + ip domain-list res.com + vni 5 + ip auto-discard + ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 + ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 + ip route 192.0.0.0/24 192.0.2.22 + ip route 192.0.0.0/24 192.0.2.22 vrf temp1 + """, + ) + + set_module_args( + dict( + config={ + "vrfs": [ + { + "name": "management", + "ip": { + "name_server": { + "address_list": [ + "192.168.255.1", + ], + }, + "route": [ + { + "destination": "192.168.255.1", + "source": "0.0.0.0/0", + }, + ], + }, + }, + { + "name": "test1", + "description": "this is descrition", + "vni": { + "vni_number": 5, + }, + "ip": { + "auto_discard": True, + "domain_list": [ + "redhat.com", + "res.com", + ], + "domain_name": "redx.com", + "multicast": { + "rpf": [ + { + "group_list_range": "238.1.0.0/24", + "vrf_name": "temp1", + }, + { + "group_list_range": "239.1.0.0/24", + "vrf_name": "temp1", + }, + ], + }, + "route": [ + { + "destination": "192.0.2.22", + "source": "192.0.0.0/24", + }, + { + "destination": "192.0.2.22", + "source": "192.0.0.0/24", + "vrf": "temp1", + }, + ], + }, + }, + ], + }, + state="replaced", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_nxos_vrf_global_purged(self): + self.execute_show_command.return_value = dedent( + """\ + vrf context management + ip name-server 192.168.255.1 + ip route 0.0.0.0/0 192.168.255.1 + vrf context test1 + description this is descrition + ip domain-name redx.com + ip domain-list redhat.com + ip domain-list res.com + vni 5 + ip auto-discard + ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 + ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 + ip route 192.0.0.0/24 192.0.2.22 + ip route 192.0.0.0/24 192.0.2.22 vrf temp1 + """, + ) + + set_module_args( + dict( + config={ + "vrfs": [], + }, + state="purged", + ), + ) + commands = ["no vrf context management", "no vrf context test1"] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_nxos_vrf_global_purged_2(self): + self.execute_show_command.return_value = dedent( + """\ + vrf context management + ip name-server 192.168.255.1 + ip route 0.0.0.0/0 192.168.255.1 + vrf context test1 + description this is descrition + ip domain-name redx.com + ip domain-list redhat.com + ip domain-list res.com + vni 5 + ip auto-discard + ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 + ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 + ip route 192.0.0.0/24 192.0.2.22 + ip route 192.0.0.0/24 192.0.2.22 vrf temp1 + """, + ) + + set_module_args( + dict( + config={ + "vrfs": [ + { + "name": "test1", + }, + ], + }, + state="purged", + ), + ) + commands = ["no vrf context test1"] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_nxos_vrf_global_parsed(self): + set_module_args( + dict( + running_config=dedent( + """\ + vrf context management + ip name-server 192.168.255.1 + ip route 0.0.0.0/0 192.168.255.1 + vrf context test1 + description this is descrition + ip domain-name redx.com + ip domain-list redhat.com + vni 5 + ip auto-discard + ip multicast rpf select vrf temp1 group-list 238.1.0.0/24 + ip multicast rpf select vrf temp1 group-list 239.1.0.0/24 + ip route 192.0.0.0/24 192.0.2.22 + ip route 192.0.0.0/24 192.0.2.22 vrf temp1 + """, + ), + state="parsed", + ), + ) + + parsed_item = { + "vrfs": [ + { + "name": "management", + "ip": { + "name_server": { + "address_list": [ + "192.168.255.1", + ], + }, + "route": [ + { + "destination": "192.168.255.1", + "source": "0.0.0.0/0", + }, + ], + }, + }, + { + "name": "test1", + "description": "this is descrition", + "vni": { + "vni_number": 5, + }, + "ip": { + "auto_discard": True, + "domain_list": [ + "redhat.com", + ], + "domain_name": "redx.com", + "multicast": { + "rpf": [ + { + "group_list_range": "238.1.0.0/24", + "vrf_name": "temp1", + }, + { + "group_list_range": "239.1.0.0/24", + "vrf_name": "temp1", + }, + ], + }, + "route": [ + { + "destination": "192.0.2.22", + "source": "192.0.0.0/24", + }, + { + "destination": "192.0.2.22", + "source": "192.0.0.0/24", + "vrf": "temp1", + }, + ], + }, + }, + ], + } + result = self.execute_module(changed=False) + self.assertEqual(parsed_item, result["parsed"])