From eb0c29e9b3a5d70adac99bca73770bc685a982b2 Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Mon, 11 Nov 2024 20:36:05 +0530 Subject: [PATCH 01/17] files added using content builder --- plugins/module_utils/common/__init__.py | 0 .../ios/argspec/vrf_interfaces/__init__.py | 0 .../argspec/vrf_interfaces/vrf_interfaces.py | 58 +++++++ .../ios/config/vrf_interfaces/__init__.py | 0 .../config/vrf_interfaces/vrf_interfaces.py | 97 +++++++++++ .../ios/facts/vrf_interfaces/__init__.py | 0 .../facts/vrf_interfaces/vrf_interfaces.py | 67 ++++++++ .../ios/rm_templates/vrf_interfaces.py | 50 ++++++ plugins/modules/ios_vrf_interfaces.py | 154 ++++++++++++++++++ 9 files changed, 426 insertions(+) create mode 100644 plugins/module_utils/common/__init__.py create mode 100644 plugins/module_utils/network/ios/argspec/vrf_interfaces/__init__.py create mode 100644 plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py create mode 100644 plugins/module_utils/network/ios/config/vrf_interfaces/__init__.py create mode 100644 plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py create mode 100644 plugins/module_utils/network/ios/facts/vrf_interfaces/__init__.py create mode 100644 plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py create mode 100644 plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py create mode 100644 plugins/modules/ios_vrf_interfaces.py diff --git a/plugins/module_utils/common/__init__.py b/plugins/module_utils/common/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/argspec/vrf_interfaces/__init__.py b/plugins/module_utils/network/ios/argspec/vrf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py new file mode 100644 index 000000000..e253931e5 --- /dev/null +++ b/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py @@ -0,0 +1,58 @@ +# -*- 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 ios_vrf_interfaces module +""" + + +class Vrf_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_vrf_interfaces module + """ + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "vrf": {"type": "str"}, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/ios/config/vrf_interfaces/__init__.py b/plugins/module_utils/network/ios/config/vrf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py new file mode 100644 index 000000000..2412bd3d1 --- /dev/null +++ b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py @@ -0,0 +1,97 @@ +# +# -*- 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 ios_vrf_interfaces config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from copy import deepcopy + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vrf_interfaces import ( + Vrf_interfacesTemplate, +) + + +class Vrf_interfaces(ResourceModule): + """ + The ios_vrf_interfaces config class + """ + + def __init__(self, module): + super(Vrf_interfaces, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="vrf_interfaces", + tmplt=Vrf_interfacesTemplate(), + ) + self.parsers = [ + ] + + 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 = {entry['name']: entry for entry in self.want} + haved = {entry['name']: entry for entry in 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) + + 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_interfaces network resource. + """ + self.compare(parsers=self.parsers, want=want, have=have) diff --git a/plugins/module_utils/network/ios/facts/vrf_interfaces/__init__.py b/plugins/module_utils/network/ios/facts/vrf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py new file mode 100644 index 000000000..c8b81e8f9 --- /dev/null +++ b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py @@ -0,0 +1,67 @@ +# -*- 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 ios vrf_interfaces 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 copy import deepcopy + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vrf_interfaces import ( + Vrf_interfacesTemplate, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesArgs, +) + +class Vrf_interfacesFacts(object): + """ The ios vrf_interfaces facts class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Vrf_interfacesArgs.argument_spec + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for Vrf_interfaces 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 = connection.get() + + # parse native config using the Vrf_interfaces template + vrf_interfaces_parser = Vrf_interfacesTemplate(lines=data.splitlines(), module=self._module) + objs = list(vrf_interfaces_parser.parse().values()) + + ansible_facts['ansible_network_resources'].pop('vrf_interfaces', None) + + params = utils.remove_empties( + vrf_interfaces_parser.validate_config(self.argument_spec, {"config": objs}, redact=True) + ) + + facts['vrf_interfaces'] = params['config'] + ansible_facts['ansible_network_resources'].update(facts) + + return ansible_facts diff --git a/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py b/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py new file mode 100644 index 000000000..1926139e2 --- /dev/null +++ b/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py @@ -0,0 +1,50 @@ +# -*- 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_interfaces parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + +class Vrf_interfacesTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Vrf_interfacesTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "key_a", + "getval": re.compile( + r""" + ^key_a\s(?P\S+) + $""", re.VERBOSE), + "setval": "", + "result": { + }, + "shared": True + }, + { + "name": "key_b", + "getval": re.compile( + r""" + \s+key_b\s(?P\S+) + $""", re.VERBOSE), + "setval": "", + "result": { + }, + }, + ] + # fmt: on diff --git a/plugins/modules/ios_vrf_interfaces.py b/plugins/modules/ios_vrf_interfaces.py new file mode 100644 index 000000000..fbbf404d5 --- /dev/null +++ b/plugins/modules/ios_vrf_interfaces.py @@ -0,0 +1,154 @@ +#!/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 ios_vrf_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: ios_vrf_interfaces +extends_documentation_fragment: + - cisco.ios.ios +short_description: Manages VRF configuration on interfaces. +description: + - Manages Virtual Routing and Forwarding (VRF) configuration on interfaces of Cisco IOS devices. +version_added: "1.0.0" +author: "AAYUSH ANAND (@username)" +notes: + - Tested against Cisco IOS XE Version X.X + - VRF must exist before assigning to an interface + - When removing VRF from interface, associated IP addresses will be removed +options: + config: + description: A list of interface VRF configurations. + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface to be configured. + - Example - GigabitEthernet0/1, FastEthernet0/0 + type: str + required: true + vrf: + description: + - Name of the VRF to be configured on the interface. + - When configured, applies 'vrf forwarding ' under the interface. + type: str + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by executing + the command B(show running-config | section ^interface). + - The state I(parsed) reads the configuration from C(running_config) option and transforms + it into Ansible structured data as per the resource module's argspec and the value + is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" + +EXAMPLES = """ + +""" + +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: + - sample command 1 + - sample command 2 + - sample command 3 +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - sample command 1 + - sample command 2 + - sample command 3 +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.vrf_interfaces.vrf_interfaces import ( + Vrf_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Vrf_interfacesArgs.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_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() From b2ba3059ef36a7a9d37b5f4e2b589d780f0bcb35 Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Mon, 11 Nov 2024 20:42:27 +0530 Subject: [PATCH 02/17] added facts code --- .../facts/vrf_interfaces/vrf_interfaces.py | 68 +++++++++++++------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py index c8b81e8f9..dde09e27f 100644 --- a/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py @@ -8,39 +8,43 @@ __metaclass__ = type """ -The ios vrf_interfaces fact class -It is in this file the configuration is collected from the device +The cisco.ios vrf_interfaces facts class +It is in this file that the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ -from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesArgs, ) from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vrf_interfaces import ( Vrf_interfacesTemplate, ) -from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import ( - Vrf_interfacesArgs, -) + class Vrf_interfacesFacts(object): - """ The ios vrf_interfaces facts class - """ + """The cisco.ios vrf_interfaces facts class""" - def __init__(self, module, subspec='config', options='options'): + def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Vrf_interfacesArgs.argument_spec + def get_vrf_interfaces_data(self, connection): + """Fetch the configuration data for VRF interfaces. + + :param connection: The device connection object + :returns: The raw configuration data from the device + """ + return connection.get("show running-config | section ^interface") + def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for Vrf_interfaces network resource + """Populate the facts for Vrf_interfaces network resource :param connection: the device connection :param ansible_facts: Facts dictionary - :param data: previously collected conf + :param data: previously collected config (optional) :rtype: dictionary :returns: facts @@ -49,19 +53,39 @@ def populate_facts(self, connection, ansible_facts, data=None): objs = [] if not data: - data = connection.get() + data = self.get_vrf_interfaces_data(connection) + + # Parse native config using the Vrf_interfaces template + vrf_interfaces_parser = Vrf_interfacesTemplate( + lines=data.splitlines(), + module=self._module, + ) + + objs = vrf_interfaces_parser.parse() + final_objs = [] + + for key, value in objs.items(): + # Adjust VRF data if necessary, similar to how address families were handled + if "vrf" in value: + value["vrf"] = value["vrf"] # Could be expanded if more processing is needed - # parse native config using the Vrf_interfaces template - vrf_interfaces_parser = Vrf_interfacesTemplate(lines=data.splitlines(), module=self._module) - objs = list(vrf_interfaces_parser.parse().values()) + if value: + value = utils.remove_empties(value) + final_objs.append(value) - ansible_facts['ansible_network_resources'].pop('vrf_interfaces', None) + # Ensure previous facts are removed to avoid duplication + ansible_facts["ansible_network_resources"].pop("vrf_interfaces", None) params = utils.remove_empties( - vrf_interfaces_parser.validate_config(self.argument_spec, {"config": objs}, redact=True) + vrf_interfaces_parser.validate_config( + self.argument_spec, + {"config": final_objs}, + redact=True, + ), ) - facts['vrf_interfaces'] = params['config'] - ansible_facts['ansible_network_resources'].update(facts) + # Update the ansible_facts dictionary with the VRF interface facts + facts["vrf_interfaces"] = params.get("config", []) + ansible_facts["ansible_network_resources"].update(facts) return ansible_facts From 99bb65450cece163db07ff0ede50ceb871035ee2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:13:48 +0000 Subject: [PATCH 03/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- README.md | 1 + .../argspec/vrf_interfaces/vrf_interfaces.py | 4 +-- .../config/vrf_interfaces/vrf_interfaces.py | 33 +++++++++---------- .../facts/vrf_interfaces/vrf_interfaces.py | 1 + .../ios/rm_templates/vrf_interfaces.py | 11 +++++-- plugins/modules/ios_vrf_interfaces.py | 2 ++ 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index afb9d4302..58187e9ec 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ Name | Description [cisco.ios.ios_vlans](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vlans_module.rst)|Resource module to configure VLANs. [cisco.ios.ios_vrf](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vrf_module.rst)|Module to configure VRF definitions. [cisco.ios.ios_vrf_global](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vrf_global_module.rst)|Resource module to configure global VRF definitions. +[cisco.ios.ios_vrf_interfaces](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vrf_interfaces_module.rst)|Manages VRF configuration on interfaces. [cisco.ios.ios_vxlan_vtep](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vxlan_vtep_module.rst)|Resource module to configure VXLAN VTEP interface. diff --git a/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py index e253931e5..e5aeb26e7 100644 --- a/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function + __metaclass__ = type ############################################# @@ -29,8 +30,7 @@ class Vrf_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the ios_vrf_interfaces module - """ + """The arg spec for the ios_vrf_interfaces module""" argument_spec = { "config": { diff --git a/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py index 2412bd3d1..288c1c061 100644 --- a/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py @@ -7,6 +7,7 @@ from __future__ import absolute_import, division, print_function + __metaclass__ = type """ @@ -20,15 +21,14 @@ from copy import deepcopy from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - dict_merge, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) -from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( - Facts, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, ) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vrf_interfaces import ( Vrf_interfacesTemplate, ) @@ -47,11 +47,10 @@ def __init__(self, module): resource="vrf_interfaces", tmplt=Vrf_interfacesTemplate(), ) - self.parsers = [ - ] + self.parsers = [] def execute_module(self): - """ Execute the module + """Execute the module :rtype: A dictionary :returns: The result from module execution @@ -62,11 +61,11 @@ def execute_module(self): return self.result def generate_commands(self): - """ Generate configuration commands to send based on - want, have and desired state. + """Generate configuration commands to send based on + want, have and desired state. """ - wantd = {entry['name']: entry for entry in self.want} - haved = {entry['name']: entry for entry in self.have} + wantd = {entry["name"]: entry for entry in self.want} + haved = {entry["name"]: entry for entry in self.have} # if state is merged, merge want onto have and then compare if self.state == "merged": @@ -74,9 +73,7 @@ def generate_commands(self): # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": - haved = { - k: v for k, v in iteritems(haved) if k in wantd or not wantd - } + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} wantd = {} # remove superfluous config for overridden and deleted @@ -90,8 +87,8 @@ def generate_commands(self): 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_interfaces network resource. + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Vrf_interfaces network resource. """ self.compare(parsers=self.parsers, want=want, have=have) diff --git a/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py index dde09e27f..771d80591 100644 --- a/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function + __metaclass__ = type """ diff --git a/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py b/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py index 1926139e2..8fe8dd4a8 100644 --- a/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function + __metaclass__ = type """ @@ -15,10 +16,12 @@ """ import re + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) + class Vrf_interfacesTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): super(Vrf_interfacesTemplate, self).__init__(lines=lines, tmplt=self, module=module) @@ -30,18 +33,20 @@ def __init__(self, lines=None, module=None): "getval": re.compile( r""" ^key_a\s(?P\S+) - $""", re.VERBOSE), + $""", re.VERBOSE, + ), "setval": "", "result": { }, - "shared": True + "shared": True, }, { "name": "key_b", "getval": re.compile( r""" \s+key_b\s(?P\S+) - $""", re.VERBOSE), + $""", re.VERBOSE, + ), "setval": "", "result": { }, diff --git a/plugins/modules/ios_vrf_interfaces.py b/plugins/modules/ios_vrf_interfaces.py index fbbf404d5..315998342 100644 --- a/plugins/modules/ios_vrf_interfaces.py +++ b/plugins/modules/ios_vrf_interfaces.py @@ -10,6 +10,7 @@ from __future__ import absolute_import, division, print_function + __metaclass__ = type DOCUMENTATION = """ @@ -119,6 +120,7 @@ """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import ( Vrf_interfacesArgs, ) From 17e45c04ab455b49a59b7d115ccad2d325929b68 Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Mon, 11 Nov 2024 21:57:49 +0530 Subject: [PATCH 04/17] refined the parsers --- .../argspec/vrf_interfaces/vrf_interfaces.py | 2 +- .../config/vrf_interfaces/vrf_interfaces.py | 7 ++++- .../module_utils/network/ios/facts/facts.py | 5 +++- .../facts/vrf_interfaces/vrf_interfaces.py | 15 ++--------- .../ios/rm_templates/vrf_interfaces.py | 26 ++++++++++++------- 5 files changed, 30 insertions(+), 25 deletions(-) diff --git a/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py index e5aeb26e7..7a5a1bbe7 100644 --- a/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py @@ -38,7 +38,7 @@ class Vrf_interfacesArgs(object): # pylint: disable=R0903 "elements": "dict", "options": { "name": {"type": "str", "required": True}, - "vrf": {"type": "str"}, + "vrf_name": {"type": "str"}, }, }, "running_config": {"type": "str"}, diff --git a/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py index 288c1c061..7ff5f2d62 100644 --- a/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py @@ -47,7 +47,9 @@ def __init__(self, module): resource="vrf_interfaces", tmplt=Vrf_interfacesTemplate(), ) - self.parsers = [] + self.parsers = ["interface", + "vrf_name" + ] def execute_module(self): """Execute the module @@ -91,4 +93,7 @@ def _compare(self, want, have): the `want` and `have` data with the `parsers` defined for the Vrf_interfaces network resource. """ + begin = len(self.commands) self.compare(parsers=self.parsers, want=want, have=have) + if len(self.commands) != begin: + self.commands.insert(begin, self._tmplt.render(want or have, "interface", False)) diff --git a/plugins/module_utils/network/ios/facts/facts.py b/plugins/module_utils/network/ios/facts/facts.py index 7e7266b76..74a7a7340 100644 --- a/plugins/module_utils/network/ios/facts/facts.py +++ b/plugins/module_utils/network/ios/facts/facts.py @@ -105,7 +105,9 @@ from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vxlan_vtep.vxlan_vtep import ( Vxlan_vtepFacts, ) - +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_interfaces.vrf_interfaces import( + Vrf_interfacesFacts +) FACT_LEGACY_SUBSETS = dict( default=Default, @@ -143,6 +145,7 @@ evpn_global=Evpn_globalFacts, evpn_evi=Evpn_eviFacts, vrf_global=Vrf_globalFacts, + vrf_interfaces=Vrf_interfacesFacts ) diff --git a/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py index 771d80591..7565fa61e 100644 --- a/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py @@ -62,25 +62,14 @@ def populate_facts(self, connection, ansible_facts, data=None): module=self._module, ) - objs = vrf_interfaces_parser.parse() - final_objs = [] - - for key, value in objs.items(): - # Adjust VRF data if necessary, similar to how address families were handled - if "vrf" in value: - value["vrf"] = value["vrf"] # Could be expanded if more processing is needed - - if value: - value = utils.remove_empties(value) - final_objs.append(value) - + objs = list(vrf_interfaces_parser.parse().values()) # Ensure previous facts are removed to avoid duplication ansible_facts["ansible_network_resources"].pop("vrf_interfaces", None) params = utils.remove_empties( vrf_interfaces_parser.validate_config( self.argument_spec, - {"config": final_objs}, + {"config": objs}, redact=True, ), ) diff --git a/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py b/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py index 8fe8dd4a8..be127c05b 100644 --- a/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py @@ -29,26 +29,34 @@ def __init__(self, lines=None, module=None): # fmt: off PARSERS = [ { - "name": "key_a", + "name": "interface", "getval": re.compile( r""" - ^key_a\s(?P\S+) - $""", re.VERBOSE, + ^interface\s(?P\S+)$""", + re.VERBOSE, ), - "setval": "", - "result": { + "setval": "interface {{ name }}", + 'result': { + '{{ name }}': { + 'name': '{{ name }}', + }, }, "shared": True, + "shared": True, }, { - "name": "key_b", + "name": "vrf_name", "getval": re.compile( r""" - \s+key_b\s(?P\S+) - $""", re.VERBOSE, + \s*vrf\sforwarding\s(?P\S+)$ + """, + re.VERBOSE, ), - "setval": "", + "setval": "vrf forwarding {{ vrf_name }}", "result": { + '{{ name }}': { + 'vrf_name': '{{ vrf_name }}', + }, }, }, ] From 1a6c7d374ab03a1c287b4d007a4fbe3e3ab005d0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:35:26 +0000 Subject: [PATCH 05/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../network/ios/config/vrf_interfaces/vrf_interfaces.py | 5 +++-- plugins/module_utils/network/ios/facts/facts.py | 9 +++++---- .../network/ios/facts/vrf_interfaces/vrf_interfaces.py | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py index 7ff5f2d62..a905a4988 100644 --- a/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py @@ -47,8 +47,9 @@ def __init__(self, module): resource="vrf_interfaces", tmplt=Vrf_interfacesTemplate(), ) - self.parsers = ["interface", - "vrf_name" + self.parsers = [ + "interface", + "vrf_name", ] def execute_module(self): diff --git a/plugins/module_utils/network/ios/facts/facts.py b/plugins/module_utils/network/ios/facts/facts.py index 74a7a7340..35ca7a9aa 100644 --- a/plugins/module_utils/network/ios/facts/facts.py +++ b/plugins/module_utils/network/ios/facts/facts.py @@ -102,12 +102,13 @@ from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_global.vrf_global import ( Vrf_globalFacts, ) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesFacts, +) from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vxlan_vtep.vxlan_vtep import ( Vxlan_vtepFacts, ) -from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_interfaces.vrf_interfaces import( - Vrf_interfacesFacts -) + FACT_LEGACY_SUBSETS = dict( default=Default, @@ -145,7 +146,7 @@ evpn_global=Evpn_globalFacts, evpn_evi=Evpn_eviFacts, vrf_global=Vrf_globalFacts, - vrf_interfaces=Vrf_interfacesFacts + vrf_interfaces=Vrf_interfacesFacts, ) diff --git a/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py index 7565fa61e..61a7f9c1f 100644 --- a/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py @@ -62,7 +62,7 @@ def populate_facts(self, connection, ansible_facts, data=None): module=self._module, ) - objs = list(vrf_interfaces_parser.parse().values()) + objs = list(vrf_interfaces_parser.parse().values()) # Ensure previous facts are removed to avoid duplication ansible_facts["ansible_network_resources"].pop("vrf_interfaces", None) From a29b38c5e6455002ce745c777f3630ff4389846b Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Tue, 12 Nov 2024 02:26:20 +0530 Subject: [PATCH 06/17] added unit test case for vrf_interfaces --- .../network/ios/test_ios_vrf_interfaces.py | 333 ++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 tests/unit/modules/network/ios/test_ios_vrf_interfaces.py diff --git a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py new file mode 100644 index 000000000..f60b133b3 --- /dev/null +++ b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py @@ -0,0 +1,333 @@ +# (c) 2024 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from textwrap import dedent +from unittest.mock import patch + +from ansible_collections.cisco.ios.plugins.modules import ios_vrf_interfaces +from ansible_collections.cisco.ios.tests.unit.modules.utils import set_module_args + +from .ios_module import TestIosModule + + +class TestIosVrfInterfacesModule(TestIosModule): + module = ios_vrf_interfaces + + def setUp(self): + super(TestIosVrfInterfacesModule, self).setUp() + + self.mock_get_resource_connection = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base." + "get_resource_connection", + ) + self.get_resource_connection = self.mock_get_resource_connection.start() + + self.mock_get_config = patch( + "ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_interfaces.vrf_interfaces." + "Vrf_interfacesFacts.get_vrf_interfaces_data", + ) + self.get_config = self.mock_get_config.start() + + def tearDown(self): + super(TestIosVrfInterfacesModule, self).tearDown() + self.get_resource_connection.stop() + self.get_config.stop() + + def test_ios_vrf_interfaces_merged_idempotent(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + no ip address + shutdown + ! + + interface GigabitEthernet4 + no ip address + shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3","vrf_name": "testvrf2"}, + {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, + + ], + state="merged", + ), + ) + commands = [ + "interface GigabitEthernet4", + "vrf forwarding testvrf1", + "interface GigabitEthernet3", + "vrf forwarding testvrf2", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + + def test_ios_vrf_interfaces_merged(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + no ip address + shutdown + ! + + interface GigabitEthernet4 + no ip address + shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3"}, + {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, + + ], + state="merged", + ), + ) + commands = [ + "interface GigabitEthernet4", + "vrf forwarding testvrf1", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + + + def test_ios_vrf_interfaces_replaced(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + no ip address + shutdown + ! + + interface GigabitEthernet4 + vrf forwarding vrf_2 + no shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3"}, + {"name": "GigabitEthernet4", "vrf_name": "testvrf1_replaced"}, + + ], + state="replaced", + ), + ) + commands = [ + "interface GigabitEthernet4", + "vrf forwarding testvrf1_replaced", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + + def test_ios_vrf_interfaces_deleted(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + vrf forwarding vrf_3 + no shutdown + ! + + interface GigabitEthernet4 + vrf forwarding vrf_2 + no shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3"}, + {"name": "GigabitEthernet4"}, + + ], + state="replaced", + ), + ) + commands = [ + "interface GigabitEthernet4", + "no vrf forwarding vrf_2", + "interface GigabitEthernet3", + "no vrf forwarding vrf_3" + + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + + + def test_ios_vrf_interfaces_parsed(self): + set_module_args( + dict( + running_config = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + vrf forwarding vrf_1 + shutdown + ! + + interface GigabitEthernet3 + no shutdown + ! + + interface GigabitEthernet4 + vrf forwarding vrf_2 + no shutdown + ! + """, + ), + state="parsed", + ), + ) + parsed_list = [ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2", "vrf_name": "vrf_1"}, + {"name": "GigabitEthernet3"}, + {"name": "GigabitEthernet4", "vrf_name": "vrf_2"}, + + ] + + result = self.execute_module(changed=False) + + self.assertEqual(result["parsed"], parsed_list) + + + def test_ios_vrf_interfaces_overridden(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + vrf forwarding vrf_1 + shutdown + ! + + interface GigabitEthernet3 + vrf forwarding vrf_2 + shutdown + ! + + interface GigabitEthernet4 + no shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3"}, + {"name": "GigabitEthernet4", "vrf_name": "vrf_5"}, + + ], + state="replaced", + ), + ) + commands = [ + "interface GigabitEthernet2", + "no vrf forwarding vrf_1", + "interface GigabitEthernet3", + "no vrf forwarding vrf_2", + "interface GigabitEthernet4", + "vrf forwarding vrf_5", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) From 58303720ed43b5750b76902525711fcda36df28a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 20:57:05 +0000 Subject: [PATCH 07/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../network/ios/test_ios_vrf_interfaces.py | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py index f60b133b3..a4cbc2c98 100644 --- a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py +++ b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py @@ -83,9 +83,8 @@ def test_ios_vrf_interfaces_merged_idempotent(self): config=[ {"name": "GigabitEthernet1"}, {"name": "GigabitEthernet2"}, - {"name": "GigabitEthernet3","vrf_name": "testvrf2"}, + {"name": "GigabitEthernet3", "vrf_name": "testvrf2"}, {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, - ], state="merged", ), @@ -99,7 +98,6 @@ def test_ios_vrf_interfaces_merged_idempotent(self): result = self.execute_module(changed=True) self.assertEqual(sorted(result["commands"]), sorted(commands)) - def test_ios_vrf_interfaces_merged(self): self.maxDiff = None run_cfg = dedent( @@ -133,7 +131,6 @@ def test_ios_vrf_interfaces_merged(self): {"name": "GigabitEthernet2"}, {"name": "GigabitEthernet3"}, {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, - ], state="merged", ), @@ -145,8 +142,6 @@ def test_ios_vrf_interfaces_merged(self): result = self.execute_module(changed=True) self.assertEqual(sorted(result["commands"]), sorted(commands)) - - def test_ios_vrf_interfaces_replaced(self): self.maxDiff = None run_cfg = dedent( @@ -180,7 +175,6 @@ def test_ios_vrf_interfaces_replaced(self): {"name": "GigabitEthernet2"}, {"name": "GigabitEthernet3"}, {"name": "GigabitEthernet4", "vrf_name": "testvrf1_replaced"}, - ], state="replaced", ), @@ -192,7 +186,6 @@ def test_ios_vrf_interfaces_replaced(self): result = self.execute_module(changed=True) self.assertEqual(sorted(result["commands"]), sorted(commands)) - def test_ios_vrf_interfaces_deleted(self): self.maxDiff = None run_cfg = dedent( @@ -226,7 +219,6 @@ def test_ios_vrf_interfaces_deleted(self): {"name": "GigabitEthernet2"}, {"name": "GigabitEthernet3"}, {"name": "GigabitEthernet4"}, - ], state="replaced", ), @@ -235,18 +227,15 @@ def test_ios_vrf_interfaces_deleted(self): "interface GigabitEthernet4", "no vrf forwarding vrf_2", "interface GigabitEthernet3", - "no vrf forwarding vrf_3" - + "no vrf forwarding vrf_3", ] result = self.execute_module(changed=True) self.assertEqual(sorted(result["commands"]), sorted(commands)) - - def test_ios_vrf_interfaces_parsed(self): set_module_args( - dict( - running_config = dedent( + dict( + running_config=dedent( """\ interface GigabitEthernet1 ip address dhcp @@ -276,14 +265,12 @@ def test_ios_vrf_interfaces_parsed(self): {"name": "GigabitEthernet2", "vrf_name": "vrf_1"}, {"name": "GigabitEthernet3"}, {"name": "GigabitEthernet4", "vrf_name": "vrf_2"}, - ] - + result = self.execute_module(changed=False) self.assertEqual(result["parsed"], parsed_list) - def test_ios_vrf_interfaces_overridden(self): self.maxDiff = None run_cfg = dedent( @@ -316,7 +303,6 @@ def test_ios_vrf_interfaces_overridden(self): {"name": "GigabitEthernet2"}, {"name": "GigabitEthernet3"}, {"name": "GigabitEthernet4", "vrf_name": "vrf_5"}, - ], state="replaced", ), From bb345c63d939772ec24c63b16a7617272bbf4c8e Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Tue, 12 Nov 2024 02:39:32 +0530 Subject: [PATCH 08/17] structured the code according to pep8 --- plugins/modules/ios_vrf_interfaces.py | 2 +- tests/unit/modules/network/ios/test_ios_vrf_interfaces.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/modules/ios_vrf_interfaces.py b/plugins/modules/ios_vrf_interfaces.py index 315998342..85a1ad91d 100644 --- a/plugins/modules/ios_vrf_interfaces.py +++ b/plugins/modules/ios_vrf_interfaces.py @@ -39,7 +39,7 @@ - Example - GigabitEthernet0/1, FastEthernet0/0 type: str required: true - vrf: + vrf_name: description: - Name of the VRF to be configured on the interface. - When configured, applies 'vrf forwarding ' under the interface. diff --git a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py index a4cbc2c98..088def0ca 100644 --- a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py +++ b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py @@ -84,6 +84,7 @@ def test_ios_vrf_interfaces_merged_idempotent(self): {"name": "GigabitEthernet1"}, {"name": "GigabitEthernet2"}, {"name": "GigabitEthernet3", "vrf_name": "testvrf2"}, + {"name": "GigabitEthernet3", "vrf_name": "testvrf2"}, {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, ], state="merged", @@ -266,7 +267,7 @@ def test_ios_vrf_interfaces_parsed(self): {"name": "GigabitEthernet3"}, {"name": "GigabitEthernet4", "vrf_name": "vrf_2"}, ] - + result = self.execute_module(changed=False) self.assertEqual(result["parsed"], parsed_list) From f23cc380a4e2d89f4d5748c7c4bdeeaf1c82b98b Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Tue, 12 Nov 2024 02:45:18 +0530 Subject: [PATCH 09/17] done with pep8 sanity test --- tests/unit/modules/network/ios/test_ios_vrf_interfaces.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py index 088def0ca..5f139af40 100644 --- a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py +++ b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py @@ -267,7 +267,6 @@ def test_ios_vrf_interfaces_parsed(self): {"name": "GigabitEthernet3"}, {"name": "GigabitEthernet4", "vrf_name": "vrf_2"}, ] - result = self.execute_module(changed=False) self.assertEqual(result["parsed"], parsed_list) From 6ccc8bd0cb84d387c7786d491a1555def09ff12e Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Tue, 12 Nov 2024 13:44:23 +0530 Subject: [PATCH 10/17] added integration tests --- .../config/vrf_interfaces/vrf_interfaces.py | 1 - .../ios/rm_templates/vrf_interfaces.py | 1 - plugins/modules/ios_vrf_interfaces.py | 361 ++++++++++++++++-- .../ios_vrf_interfaces/defaults/main.yaml | 3 + .../targets/ios_vrf_interfaces/tasks/cli.yaml | 22 ++ .../ios_vrf_interfaces/tasks/main.yaml | 5 + .../tests/common/_parsed.cfg | 15 + .../tests/common/_populate_config.yaml | 12 + .../tests/common/_remove_config.yaml | 11 + .../tests/common/empty_config.yaml | 50 +++ .../tests/common/gathered.yaml | 38 ++ .../tests/common/merged.yaml | 35 ++ .../tests/common/overridden.yaml | 39 ++ .../tests/common/parsed.yaml | 14 + .../tests/common/rendered.yaml | 21 + .../targets/ios_vrf_interfaces/vars/main.yaml | 57 +++ 16 files changed, 656 insertions(+), 29 deletions(-) create mode 100644 tests/integration/targets/ios_vrf_interfaces/defaults/main.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tasks/cli.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tasks/main.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/_parsed.cfg create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/_populate_config.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/_remove_config.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/parsed.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/rendered.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/vars/main.yaml diff --git a/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py index a905a4988..4d7dd3a9e 100644 --- a/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py @@ -18,7 +18,6 @@ created. """ -from copy import deepcopy from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( diff --git a/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py b/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py index be127c05b..3b11effdf 100644 --- a/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py +++ b/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py @@ -42,7 +42,6 @@ def __init__(self, lines=None, module=None): }, }, "shared": True, - "shared": True, }, { "name": "vrf_name", diff --git a/plugins/modules/ios_vrf_interfaces.py b/plugins/modules/ios_vrf_interfaces.py index 85a1ad91d..b5daa998e 100644 --- a/plugins/modules/ios_vrf_interfaces.py +++ b/plugins/modules/ios_vrf_interfaces.py @@ -10,7 +10,6 @@ from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ @@ -22,9 +21,9 @@ description: - Manages Virtual Routing and Forwarding (VRF) configuration on interfaces of Cisco IOS devices. version_added: "1.0.0" -author: "AAYUSH ANAND (@username)" +author: "AAYUSH ANAND (@AAYUSH2091)" notes: - - Tested against Cisco IOS XE Version X.X + - Tested against Cisco IOS XE Version 17.13.01a - VRF must exist before assigning to an interface - When removing VRF from interface, associated IP addresses will be removed options: @@ -36,7 +35,7 @@ name: description: - Full name of the interface to be configured. - - Example - GigabitEthernet0/1, FastEthernet0/0 + - Example - GigabitEthernet1, Loopback24 type: str required: true vrf_name: @@ -69,64 +68,372 @@ """ EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Merge provided configuration with device configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_D + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: merged + +# Task Output: +# ------------ +# +# before: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# +# commands: +# - interface GigabitEthernet2 +# - vrf forwarding vrf_D +# +# after: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_D" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# vrf forwarding vrf_D +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using overridden + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# vrf forwarding vrf_B +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Override device configuration with provided configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: overridden + +# Task Output: +# ------------ +# +# before: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_B" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# +# commands: +# - interface GigabitEthernet2 +# - no vrf forwarding vrf_B +# +# after: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using gathered + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# vrf forwarding vrf_B +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Gather listed VRF interfaces + cisco.ios.ios_vrf_interfaces: + state: gathered + +# Task Output: +# ------------ +# +# gathered: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_B" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# Using rendered + +- name: Render VRF configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_D + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: rendered + +# Task Output: +# ------------ +# +# rendered: +# - interface GigabitEthernet2 +# - vrf forwarding vrf_D + +# Using parsed + +# File: parsed.cfg +# --------------- +# +# interface GigabitEthernet1 +# vrf vrf_C +# shutdown +# ! +# interface GigabitEthernet2 +# vrf vrf_D +# shutdown +# ! +# interface GigabitEthernet3 +# shutdown +# ! +# interface GigabitEthernet4 +# shutdown +# ! + +- name: Parse configuration from device running config + cisco.ios.ios_vrf_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output: +# ------------ +# +# parsed: +# - name: "GigabitEthernet1" +# vrf_name: "vrf_C" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_D" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" """ 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 + type: list sample: > - This output will always be in the same format as the - module argspec. + [ + { + "name": "Loopback24" + }, + { + "name": "GigabitEthernet1" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_B" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ] + after: description: The resulting configuration after module execution. returned: when changed - type: dict + type: list sample: > - This output will always be in the same format as the - module argspec. + [ + { + "name": "Loopback24" + }, + { + "name": "GigabitEthernet1" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_D" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ] + 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: - - sample command 1 - - sample command 2 - - sample command 3 + - "interface GigabitEthernet2" + - "vrf forwarding vrf_D" + - "no vrf forwarding vrf_B" + rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - - sample command 1 - - sample command 2 - - sample command 3 + - "interface GigabitEthernet1" + - "vrf forwarding vrf_C" + - "interface GigabitEthernet2" + - "vrf forwarding vrf_D" + gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > - This output will always be in the same format as the - module argspec. + [ + { + "name": "Loopback24" + }, + { + "name": "GigabitEthernet1" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_B" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ] + parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > - This output will always be in the same format as the - module argspec. + [ + { + "name": "GigabitEthernet1", + "vrf_name": "vrf_C" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_D" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ] """ from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import ( - Vrf_interfacesArgs, -) -from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.vrf_interfaces.vrf_interfaces import ( - Vrf_interfaces, -) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import Vrf_interfacesArgs +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.vrf_interfaces.vrf_interfaces import Vrf_interfaces def main(): diff --git a/tests/integration/targets/ios_vrf_interfaces/defaults/main.yaml b/tests/integration/targets/ios_vrf_interfaces/defaults/main.yaml new file mode 100644 index 000000000..164afead2 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "[^_].*" +test_items: [] diff --git a/tests/integration/targets/ios_vrf_interfaces/tasks/cli.yaml b/tests/integration/targets/ios_vrf_interfaces/tasks/cli.yaml new file mode 100644 index 000000000..39471d59f --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tasks/cli.yaml @@ -0,0 +1,22 @@ +--- +- 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 }}" + +- name: Run test case (connection=ansible.netcommon.network_cli) + vars: + ansible_connection: ansible.netcommon.network_cli + ansible.builtin.include_tasks: "{{ test_case_to_run }}" + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run + tags: + - network_cli diff --git a/tests/integration/targets/ios_vrf_interfaces/tasks/main.yaml b/tests/integration/targets/ios_vrf_interfaces/tasks/main.yaml new file mode 100644 index 000000000..f75f2f031 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tasks/main.yaml @@ -0,0 +1,5 @@ +--- +- name: Include the CLI tasks + ansible.builtin.include_tasks: cli.yaml + tags: + - cli diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/_parsed.cfg b/tests/integration/targets/ios_vrf_interfaces/tests/common/_parsed.cfg new file mode 100644 index 000000000..4d06b79dd --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/_parsed.cfg @@ -0,0 +1,15 @@ + +interface GigabitEthernet1 + vrf forwarding vrf_C + shutdown +! +interface GigabitEthernet2 + vrf forwarding vrf_D + shutdown +! +interface GigabitEthernet3 + shutdown +! +interface GigabitEthernet4 + shutdown +! diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/_populate_config.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/_populate_config.yaml new file mode 100644 index 000000000..9c676f568 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/_populate_config.yaml @@ -0,0 +1,12 @@ +--- +- name: Populate with simple overridden + cisco.ios.ios_vrf_interfaces: + state: overridden + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_B + - name: GigabitEthernet3 + - name: GigabitEthernet4 + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/_remove_config.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/_remove_config.yaml new file mode 100644 index 000000000..5e0cc0491 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/_remove_config.yaml @@ -0,0 +1,11 @@ +--- +- name: Populate with simple overridden + cisco.ios.ios_vrf_interfaces: + state: overridden + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + - name: GigabitEthernet3 + - name: GigabitEthernet4 + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml new file mode 100644 index 000000000..fd31c161c --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml @@ -0,0 +1,50 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_interfaces empty_config integration tests on connection={{ ansible_connection }} + +- name: Merged with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_interfaces: + 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.ios.ios_vrf_interfaces: + config: + state: replaced + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state replaced' + +- name: Rendered with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_interfaces: + 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.ios.ios_vrf_interfaces: + running_config: + state: parsed + +- ansible.builtin.assert: + that: + - result.msg == 'value of running_config parameter must not be empty for state parsed' + +- ansible.builtin.debug: + msg: END ios_vrf_interfaces empty_config integration tests on connection={{ ansible_connection }} diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml new file mode 100644 index 000000000..0112d0d31 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml @@ -0,0 +1,38 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_interfaces gathered integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Gathered the provided configuration with the existing running configuration + register: result + cisco.ios.ios_vrf_interfaces: + config: + state: gathered + + - name: Debug gathered.config + ansible.builtin.debug: + var: gathered.config + + - name: Debug result.gathered + ansible.builtin.debug: + var: result.gathered + + - name: Run show running-config | section ^interface + cisco.ios.ios_command: + commands: show running-config | section ^interface + register: config + + - name: Assert + ansible.builtin.assert: + that: + - gathered.config | length == result.gathered | length + - result.gathered[1].name == 'GigabitEthernet1' + - result.gathered[2].name == 'GigabitEthernet2' + - result.gathered[2].vrf_name == 'vrf_B' + - result.gathered[3].name == 'GigabitEthernet3' + - result.gathered[4].name == 'GigabitEthernet4' + - result['changed'] == false + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml new file mode 100644 index 000000000..2e524766d --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml @@ -0,0 +1,35 @@ +--- +- ansible.builtin.debug: + msg: Start ios_vrf_interfaces merged integration tests connection={{ ansible_connection}} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Simple merge selective + cisco.ios.ios_vrf_interfaces: + state: merged + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_D + - name: GigabitEthernet3 + - name: GigabitEthernet4 + + register: result + + - name: Run show running-config | section ^interface + cisco.ios.ios_command: + commands: show running-config | section ^interface + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that after dicts were correctly generated + ansible.builtin.assert: + that: + - merged['after'] == result['after'] + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml new file mode 100644 index 000000000..d0e21d8fd --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml @@ -0,0 +1,39 @@ +--- +- ansible.builtin.debug: + msg: Start ios_vrf_interfaces overridden integration tests connection={{ ansible_connection}} + +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Override the route-policy configuration + cisco.ios.ios_vrf_interfaces: + state: overridden + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + - name: GigabitEthernet3 + - name: GigabitEthernet4 + vrf_name: vrf_C + + register: result + + - name: Run show running-config | section ^interface + cisco.ios.ios_command: + commands: show running-config | section ^interface + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ overridden['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - overridden['after'] == result['after'] + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - overridden['before'] == result['before'] + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/parsed.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/parsed.yaml new file mode 100644 index 000000000..a08968c71 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/parsed.yaml @@ -0,0 +1,14 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_interfaces parsed integration tests on connection={{ ansible_connection }} + +- name: Parse externally provided route-policy configuration + register: result + cisco.ios.ios_vrf_interfaces: + running_config: "{{ lookup('file', '_parsed.cfg') }}" + state: parsed + +- name: Assert that configuration was correctly parsed + ansible.builtin.assert: + that: + - parsed['after'] == result['parsed'] diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/rendered.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/rendered.yaml new file mode 100644 index 000000000..84ac4f670 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/rendered.yaml @@ -0,0 +1,21 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_interfaces rendered integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- name: Render route-policy configuration + register: result + cisco.ios.ios_vrf_interfaces: + state: rendered + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_D + - name: GigabitEthernet3 + - name: GigabitEthernet4 + +- name: Assert that correct set of commands were rendered + ansible.builtin.assert: + that: + - merged['commands'] == result['rendered'] diff --git a/tests/integration/targets/ios_vrf_interfaces/vars/main.yaml b/tests/integration/targets/ios_vrf_interfaces/vars/main.yaml new file mode 100644 index 000000000..877959521 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/vars/main.yaml @@ -0,0 +1,57 @@ +--- +gathered: + config: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_B" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +merged: + commands: + - interface GigabitEthernet2 + - vrf forwarding vrf_D + after: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_D" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + before: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +overridden: + commands: + - interface GigabitEthernet2 + - no vrf forwarding vrf_B + - interface GigabitEthernet4 + - vrf forwarding vrf_C + after: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + vrf_name: "vrf_C" + before: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_B" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +parsed: + after: + - name: "GigabitEthernet1" + vrf_name: "vrf_C" + - name: "GigabitEthernet2" + vrf_name: "vrf_D" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" From cce640e16e3284b49985b20d358ae653f3f9d16e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 08:16:41 +0000 Subject: [PATCH 11/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- plugins/modules/ios_vrf_interfaces.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/modules/ios_vrf_interfaces.py b/plugins/modules/ios_vrf_interfaces.py index b5daa998e..ee7689509 100644 --- a/plugins/modules/ios_vrf_interfaces.py +++ b/plugins/modules/ios_vrf_interfaces.py @@ -10,6 +10,7 @@ from __future__ import absolute_import, division, print_function + __metaclass__ = type DOCUMENTATION = """ @@ -432,8 +433,13 @@ """ from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import Vrf_interfacesArgs -from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.vrf_interfaces.vrf_interfaces import Vrf_interfaces + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.vrf_interfaces.vrf_interfaces import ( + Vrf_interfaces, +) def main(): From 504105468fcf3b8e379669b905d81d64ec28d8e2 Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Tue, 12 Nov 2024 13:51:44 +0530 Subject: [PATCH 12/17] added changelogs --- changelogs/fragments/rm_vrf_interfaces.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelogs/fragments/rm_vrf_interfaces.yaml diff --git a/changelogs/fragments/rm_vrf_interfaces.yaml b/changelogs/fragments/rm_vrf_interfaces.yaml new file mode 100644 index 000000000..9ed25308a --- /dev/null +++ b/changelogs/fragments/rm_vrf_interfaces.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Added ios_vrf_interfaces resource module,that helps with configuration of vrfs within interface From 20ae6c09444f3e96292beeec38414c7121205869 Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Fri, 15 Nov 2024 23:33:45 +0530 Subject: [PATCH 13/17] added integration tests for rendered and gathered --- .../network/ios/test_ios_vrf_interfaces.py | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py index 5f139af40..8061169fe 100644 --- a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py +++ b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py @@ -317,3 +317,145 @@ def test_ios_vrf_interfaces_overridden(self): ] result = self.execute_module(changed=True) self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_interfaces_replaced_idempotent(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + vrf forwarding testvrf2 + no ip address + shutdown + ! + + interface GigabitEthernet4 + vrf forwarding testvrf1 + no ip address + shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3", "vrf_name": "testvrf2"}, + {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, + ], + state="replaced", + ), + ) + result = self.execute_module(changed=False) + self.assertEqual(result["commands"], []) + + def test_ios_vrf_interfaces_overridden_idempotent(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + vrf forwarding testvrf2 + no ip address + shutdown + ! + + interface GigabitEthernet4 + vrf forwarding testvrf1 + no ip address + shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3", "vrf_name": "testvrf2"}, + {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, + ], + state="overridden", + ), + ) + result = self.execute_module(changed=False) + self.assertEqual(result["commands"], []) + + def test_ios_vrf_interfaces_gathered(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + vrf forwarding vrf_1 + shutdown + ! + + interface GigabitEthernet3 + vrf forwarding vrf_2 + no ip address + shutdown + ! + + interface GigabitEthernet4 + no shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args(dict(state="gathered")) + gathered_list = [ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2", "vrf_name": "vrf_1"}, + {"name": "GigabitEthernet3", "vrf_name": "vrf_2"}, + {"name": "GigabitEthernet4"}, + ] + result = self.execute_module(changed=False) + self.assertEqual(result["gathered"], gathered_list) + + def test_ios_vrf_interfaces_rendered(self): + self.maxDiff = None + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2", "vrf_name": "vrf_1"}, + {"name": "GigabitEthernet3", "vrf_name": "vrf_2"}, + {"name": "GigabitEthernet4"}, + ], + state="rendered", + ), + ) + commands = [ + "interface GigabitEthernet2", + "vrf forwarding vrf_1", + "interface GigabitEthernet3", + "vrf forwarding vrf_2", + ] + result = self.execute_module(changed=False) + self.assertEqual(sorted(result["rendered"]), sorted(commands)) \ No newline at end of file From 13fabc2b65a36cc74469fb6ac64506b14ffe181d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:04:33 +0000 Subject: [PATCH 14/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/unit/modules/network/ios/test_ios_vrf_interfaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py index 8061169fe..c35f820ce 100644 --- a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py +++ b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py @@ -458,4 +458,4 @@ def test_ios_vrf_interfaces_rendered(self): "vrf forwarding vrf_2", ] result = self.execute_module(changed=False) - self.assertEqual(sorted(result["rendered"]), sorted(commands)) \ No newline at end of file + self.assertEqual(sorted(result["rendered"]), sorted(commands)) From 4c0545ec52310ded8882b4cbcfd4d4b6fe292bac Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Fri, 15 Nov 2024 23:45:59 +0530 Subject: [PATCH 15/17] simplified the integration test of gathered.yaml --- .../tests/common/gathered.yaml | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml index 0112d0d31..616cf616c 100644 --- a/tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml @@ -1,38 +1,22 @@ --- -- ansible.builtin.debug: +- name: START ios_vrf_interfaces gathered integration tests + ansible.builtin.debug: msg: START ios_vrf_interfaces gathered integration tests on connection={{ ansible_connection }} - ansible.builtin.include_tasks: _populate_config.yaml - block: - - name: Gathered the provided configuration with the existing running configuration + - name: Gather the provided configuration register: result cisco.ios.ios_vrf_interfaces: config: state: gathered - - name: Debug gathered.config - ansible.builtin.debug: - var: gathered.config - - - name: Debug result.gathered - ansible.builtin.debug: - var: result.gathered - - - name: Run show running-config | section ^interface - cisco.ios.ios_command: - commands: show running-config | section ^interface - register: config - - name: Assert ansible.builtin.assert: that: - - gathered.config | length == result.gathered | length - - result.gathered[1].name == 'GigabitEthernet1' - - result.gathered[2].name == 'GigabitEthernet2' - - result.gathered[2].vrf_name == 'vrf_B' - - result.gathered[3].name == 'GigabitEthernet3' - - result.gathered[4].name == 'GigabitEthernet4' - - result['changed'] == false + - not result.changed + - "{{ gathered['config'] | symmetric_difference(result['gathered']) |length == 0 }}" + always: - ansible.builtin.include_tasks: _remove_config.yaml From 4a21ac17096da268d5e9fcaf47ee136e281a3a59 Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Tue, 19 Nov 2024 13:07:19 +0530 Subject: [PATCH 16/17] added few more tests --- plugins/modules/ios_vrf_interfaces.py | 152 +++++++++++++++++- .../tests/common/deleted.yaml | 50 ++++++ .../tests/common/empty_config.yaml | 11 ++ .../tests/common/merged.yaml | 33 ++-- .../tests/common/overridden.yaml | 31 ++-- .../tests/common/replaced.yaml | 42 +++++ .../ios_vrf_interfaces/tests/common/rtt.yaml | 55 +++++++ .../targets/ios_vrf_interfaces/vars/main.yaml | 44 +++++ 8 files changed, 391 insertions(+), 27 deletions(-) create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/deleted.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/replaced.yaml create mode 100644 tests/integration/targets/ios_vrf_interfaces/tests/common/rtt.yaml diff --git a/plugins/modules/ios_vrf_interfaces.py b/plugins/modules/ios_vrf_interfaces.py index ee7689509..5a9fdca3c 100644 --- a/plugins/modules/ios_vrf_interfaces.py +++ b/plugins/modules/ios_vrf_interfaces.py @@ -315,12 +315,160 @@ # vrf_name: "vrf_D" # - name: "GigabitEthernet3" # - name: "GigabitEthernet4" + +# Using replaced + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# vrf forwarding vrf_A +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# vrf forwarding vrf_B +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# vrf forwarding vrf_C +# no ip address +# shutdown +# negotiation auto + +- name: Replace device configuration of listed VRF interfaces with provided configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + vrf_name: vrf_D + - name: GigabitEthernet2 + vrf_name: vrf_E + state: replaced + +# Task Output: +# ------------ +# +# before: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# vrf_name: "vrf_A" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_B" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# vrf_name: "vrf_C" +# +# commands: +# - interface GigabitEthernet1 +# - no vrf forwarding vrf_A +# - vrf forwarding vrf_D +# - interface GigabitEthernet2 +# - no vrf forwarding vrf_B +# - vrf forwarding vrf_E +# +# after: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# vrf_name: "vrf_D" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_E" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# vrf_name: "vrf_C" + +# Using deleted + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# vrf forwarding vrf_A +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# vrf forwarding vrf_B +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# vrf forwarding vrf_C +# no ip address +# shutdown +# negotiation auto + +- name: Delete VRF configuration of specified interfaces + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + state: deleted + +# Task Output: +# ------------ +# +# before: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# vrf_name: "vrf_A" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_B" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# vrf_name: "vrf_C" +# +# commands: +# - interface GigabitEthernet1 +# - no vrf forwarding vrf_A +# - interface GigabitEthernet2 +# - no vrf forwarding vrf_B +# +# after: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# vrf_name: "vrf_C" + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# vrf forwarding vrf_C +# no ip address +# shutdown +# negotiation auto """ 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) + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) type: list sample: > [ @@ -368,7 +516,7 @@ 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) + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) type: list sample: - "interface GigabitEthernet2" diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/deleted.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/deleted.yaml new file mode 100644 index 000000000..4f9abef79 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/deleted.yaml @@ -0,0 +1,50 @@ +--- +- ansible.builtin.debug: + msg: Start Deleted integration state for ios_vrf_interfaces ansible_connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Delete provided VRF interfaces + register: result + cisco.ios.ios_vrf_interfaces: &id001 + config: + - name: GigabitEthernet2 + state: deleted + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ deleted['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Delete provided VRF interfaces (idempotent) + register: result + cisco.ios.ios_vrf_interfaces: *id001 + - name: Assert that the previous task was idempotent + ansible.builtin.assert: + that: + - result.changed == false + + - ansible.builtin.include_tasks: _populate_config.yaml + + - name: Delete provided VRF interfaces without any configuration + register: result + cisco.ios.ios_vrf_interfaces: &id002 + state: deleted + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ deleted['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Delete provided VRF interfaces without any configuration (idempotent) + register: result + cisco.ios.ios_vrf_interfaces: *id002 + - name: Assert that the delete task was idempotent + ansible.builtin.assert: + that: + - result.changed == false + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml index fd31c161c..2506c7e50 100644 --- a/tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml @@ -24,6 +24,17 @@ 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.ios.ios_vrf_interfaces: + 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 diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml index 2e524766d..9d169d135 100644 --- a/tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml @@ -1,35 +1,44 @@ --- - ansible.builtin.debug: - msg: Start ios_vrf_interfaces merged integration tests connection={{ ansible_connection}} + msg: START Merged ios_vrf_interfaces state for integration tests on connection={{ ansible_connection }} - ansible.builtin.include_tasks: _remove_config.yaml - block: - - name: Simple merge selective - cisco.ios.ios_vrf_interfaces: - state: merged + - name: Merge provided configuration with device configuration + register: result + cisco.ios.ios_vrf_interfaces: &id001 config: - name: GigabitEthernet1 - name: GigabitEthernet2 vrf_name: vrf_D - name: GigabitEthernet3 - name: GigabitEthernet4 + state: merged - register: result + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ merged['commands'] | symmetric_difference(result['commands']) | length == 0 }}" - - name: Run show running-config | section ^interface - cisco.ios.ios_command: - commands: show running-config | section ^interface + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - "{{ merged['before'] | symmetric_difference(result['before']) | length == 0 }}" - - name: Assert that correct set of commands were generated + - name: Assert that after dict is correctly generated ansible.builtin.assert: that: - - "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + - "{{ merged['after'] | symmetric_difference(result['after']) | length == 0 }}" + + - name: Merge provided configuration with device configuration (idempotent) + register: result + cisco.ios.ios_vrf_interfaces: *id001 - - name: Assert that after dicts were correctly generated + - name: Assert that the previous task was idempotent ansible.builtin.assert: that: - - merged['after'] == result['after'] + - result['changed'] == false always: - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml index d0e21d8fd..5fa53c611 100644 --- a/tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml @@ -1,39 +1,44 @@ --- - ansible.builtin.debug: - msg: Start ios_vrf_interfaces overridden integration tests connection={{ ansible_connection}} + msg: START Overridden ios_vrf_interfaces state for integration tests on connection={{ ansible_connection }} - ansible.builtin.include_tasks: _populate_config.yaml - block: - - name: Override the route-policy configuration - cisco.ios.ios_vrf_interfaces: - state: overridden + - name: Override provided configuration with device configuration + register: result + cisco.ios.ios_vrf_interfaces: &id001 config: - name: GigabitEthernet1 - name: GigabitEthernet2 - name: GigabitEthernet3 - name: GigabitEthernet4 vrf_name: vrf_C - - register: result - - - name: Run show running-config | section ^interface - cisco.ios.ios_command: - commands: show running-config | section ^interface + state: overridden - name: Assert that correct set of commands were generated ansible.builtin.assert: that: - - "{{ overridden['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + - "{{ 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'] == result['after'] - - name: Assert that before dicts are correctly generated + - name: Override provided configuration with device configuration (idempotent) + register: result + cisco.ios.ios_vrf_interfaces: *id001 + + - name: Assert that the previous task was idempotent ansible.builtin.assert: that: - - overridden['before'] == result['before'] + - result['changed'] == false + always: - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/replaced.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/replaced.yaml new file mode 100644 index 000000000..0b8089093 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/replaced.yaml @@ -0,0 +1,42 @@ +--- +- ansible.builtin.debug: + msg: START Replaced ios_vrf_interfaces 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 provided VRF interfaces configuration + register: result + cisco.ios.ios_vrf_interfaces: &id001 + config: + - name: GigabitEthernet2 + vrf_name: vrf_D + 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 dicts are 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: Replace provided VRF interfaces configuration (idempotent) + register: result + cisco.ios.ios_vrf_interfaces: *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/ios_vrf_interfaces/tests/common/rtt.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/rtt.yaml new file mode 100644 index 000000000..7dfed1d48 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/rtt.yaml @@ -0,0 +1,55 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_interfaces round trip integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Apply the provided configuration (base config) + register: base_config + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_B + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: merged + + - name: Gather VRF interfaces facts + cisco.ios.ios_facts: + gather_subset: + - "!all" + - "!min" + gather_network_resources: + - vrf_interfaces + + - name: Apply the provided configuration (config to be reverted) + register: result + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + - name: GigabitEthernet3 + - name: GigabitEthernet4 + vrf_name: vrf_C + state: overridden + + - ansible.builtin.assert: + that: + - result.changed == true + - result.commands|symmetric_difference(overridden.commands) == [] + + - name: Revert back to base configuration using facts round trip + register: revert + cisco.ios.ios_vrf_interfaces: + config: "{{ ansible_facts['network_resources']['vrf_interfaces'] }}" + state: overridden + + - ansible.builtin.assert: + that: + - revert.changed == true + - revert.commands|symmetric_difference(rtt.commands) == [] + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/vars/main.yaml b/tests/integration/targets/ios_vrf_interfaces/vars/main.yaml index 877959521..20d971bc3 100644 --- a/tests/integration/targets/ios_vrf_interfaces/vars/main.yaml +++ b/tests/integration/targets/ios_vrf_interfaces/vars/main.yaml @@ -55,3 +55,47 @@ parsed: vrf_name: "vrf_D" - name: "GigabitEthernet3" - name: "GigabitEthernet4" + +replaced: + commands: + - interface GigabitEthernet2 + - vrf forwarding vrf_D + before: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_B" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + after: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_D" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +deleted: + commands: + - interface GigabitEthernet2 + - no vrf forwarding vrf_B + before: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_B" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + after: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +rtt: + commands: + - interface GigabitEthernet2 + - vrf forwarding vrf_B + - interface GigabitEthernet4 + - no vrf forwarding vrf_C From d40ad98b5fc1a7519bf9400ddad08b1b07a85207 Mon Sep 17 00:00:00 2001 From: AAYUSH ANAND Date: Thu, 5 Dec 2024 17:19:21 +0530 Subject: [PATCH 17/17] added meta cisco.ios.vrf_interfaces --- meta/runtime.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meta/runtime.yml b/meta/runtime.yml index 6713d735c..3219ce3c6 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -63,6 +63,8 @@ plugin_routing: redirect: cisco.ios.ios_vlans vrf: redirect: cisco.ios.ios_vrf + vrf_interfaces: + redirect: cisco.ios.vrf_interfaces vxlan_vtep: redirect: cisco.ios.ios_vxlan_vtep vrf_global: