From be992851dfe40a10a0a05b802920bb7da82aca74 Mon Sep 17 00:00:00 2001 From: Ruchi Pakhle <72685035+Ruchip16@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:44:03 +0530 Subject: [PATCH 1/2] fix the sanity failures in upstream (#524) * fix the sanity failures in upstream * chore: auto fixes from pre-commit.com hooks * push changes --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- tests/sanity/ignore-2.18.txt | 1 + tests/sanity/ignore-2.19.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/sanity/ignore-2.18.txt b/tests/sanity/ignore-2.18.txt index 687c8ab3..3c6380de 100644 --- a/tests/sanity/ignore-2.18.txt +++ b/tests/sanity/ignore-2.18.txt @@ -1,3 +1,4 @@ plugins/action/iosxr.py action-plugin-docs # base class for deprecated network platform modules using `connection: local` plugins/sub_plugins/grpc/pb/ems_grpc_pb2.py import-3.11!skip plugins/sub_plugins/grpc/pb/ems_grpc_pb2.py import-3.12!skip +plugins/sub_plugins/grpc/pb/ems_grpc_pb2.py import-3.13!skip diff --git a/tests/sanity/ignore-2.19.txt b/tests/sanity/ignore-2.19.txt index 687c8ab3..3c6380de 100644 --- a/tests/sanity/ignore-2.19.txt +++ b/tests/sanity/ignore-2.19.txt @@ -1,3 +1,4 @@ plugins/action/iosxr.py action-plugin-docs # base class for deprecated network platform modules using `connection: local` plugins/sub_plugins/grpc/pb/ems_grpc_pb2.py import-3.11!skip plugins/sub_plugins/grpc/pb/ems_grpc_pb2.py import-3.12!skip +plugins/sub_plugins/grpc/pb/ems_grpc_pb2.py import-3.13!skip From b6ac704b16351c5eb0108be361c432181b02b5f2 Mon Sep 17 00:00:00 2001 From: Sagar Paul Date: Thu, 5 Dec 2024 18:57:51 +0530 Subject: [PATCH 2/2] Add [vrf_interfaces] resource module (#523) * WIP iosxr vrf_interfaces resouce module * chore: auto fixes from pre-commit.com hooks * The formalities for the module * chore: auto fixes from pre-commit.com hooks * add docs - mark complete * Use command module * chore: auto fixes from pre-commit.com hooks * add a runtime.yml entry * chore: auto fixes from pre-commit.com hooks --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ruchi Pakhle <72685035+Ruchip16@users.noreply.github.com> --- README.md | 1 + changelogs/fragments/rm_vrf_interfaces.yaml | 3 + ...isco.iosxr.iosxr_vrf_interfaces_module.rst | 634 ++++++++++++++++++ meta/runtime.yml | 2 + .../iosxr/argspec/vrf_interfaces/__init__.py | 0 .../argspec/vrf_interfaces/vrf_interfaces.py | 58 ++ .../iosxr/config/vrf_interfaces/__init__.py | 0 .../config/vrf_interfaces/vrf_interfaces.py | 98 +++ .../module_utils/network/iosxr/facts/facts.py | 4 + .../iosxr/facts/vrf_interfaces/__init__.py | 0 .../facts/vrf_interfaces/vrf_interfaces.py | 71 ++ .../iosxr/rm_templates/vrf_interfaces.py | 63 ++ plugins/modules/iosxr_vrf_interfaces.py | 524 +++++++++++++++ .../iosxr_vrf_interfaces/defaults/main.yml | 2 + .../iosxr_vrf_interfaces/meta/main.yml | 0 .../iosxr_vrf_interfaces/tasks/cli.yaml | 22 + .../iosxr_vrf_interfaces/tasks/main.yaml | 5 + .../tests/common/_parsed.cfg | 28 + .../tests/common/_populate_config.yaml | 16 + .../tests/common/_remove_config.yaml | 10 + .../tests/common/empty_config.yaml | 50 ++ .../tests/common/gathered.yaml | 24 + .../tests/common/merged.yaml | 34 + .../tests/common/overridden.yaml | 37 + .../tests/common/parsed.yaml | 14 + .../tests/common/rendered.yaml | 21 + .../iosxr_vrf_interfaces/vars/main.yaml | 59 ++ .../iosxr/test_iosxr_vrf_interfaces.py | 392 +++++++++++ 28 files changed, 2172 insertions(+) create mode 100644 changelogs/fragments/rm_vrf_interfaces.yaml create mode 100644 docs/cisco.iosxr.iosxr_vrf_interfaces_module.rst create mode 100644 plugins/module_utils/network/iosxr/argspec/vrf_interfaces/__init__.py create mode 100644 plugins/module_utils/network/iosxr/argspec/vrf_interfaces/vrf_interfaces.py create mode 100644 plugins/module_utils/network/iosxr/config/vrf_interfaces/__init__.py create mode 100644 plugins/module_utils/network/iosxr/config/vrf_interfaces/vrf_interfaces.py create mode 100644 plugins/module_utils/network/iosxr/facts/vrf_interfaces/__init__.py create mode 100644 plugins/module_utils/network/iosxr/facts/vrf_interfaces/vrf_interfaces.py create mode 100644 plugins/module_utils/network/iosxr/rm_templates/vrf_interfaces.py create mode 100644 plugins/modules/iosxr_vrf_interfaces.py create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/defaults/main.yml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/meta/main.yml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tasks/cli.yaml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tasks/main.yaml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tests/common/_parsed.cfg create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tests/common/_populate_config.yaml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tests/common/_remove_config.yaml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tests/common/empty_config.yaml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tests/common/gathered.yaml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tests/common/merged.yaml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tests/common/overridden.yaml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tests/common/parsed.yaml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/tests/common/rendered.yaml create mode 100644 tests/integration/targets/iosxr_vrf_interfaces/vars/main.yaml create mode 100644 tests/unit/modules/network/iosxr/test_iosxr_vrf_interfaces.py diff --git a/README.md b/README.md index 4c11d039..e7cdacb9 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ Name | Description [cisco.iosxr.iosxr_user](https://github.com/ansible-collections/cisco.iosxr/blob/main/docs/cisco.iosxr.iosxr_user_module.rst)|Module to manage the aggregates of local users. [cisco.iosxr.iosxr_vrf_address_family](https://github.com/ansible-collections/cisco.iosxr/blob/main/docs/cisco.iosxr.iosxr_vrf_address_family_module.rst)|Resource module to configure VRF Address family. [cisco.iosxr.iosxr_vrf_global](https://github.com/ansible-collections/cisco.iosxr/blob/main/docs/cisco.iosxr.iosxr_vrf_global_module.rst)|Manages global VRF configuration. +[cisco.iosxr.iosxr_vrf_interfaces](https://github.com/ansible-collections/cisco.iosxr/blob/main/docs/cisco.iosxr.iosxr_vrf_interfaces_module.rst)|Resource module to configure VRF interfaces. diff --git a/changelogs/fragments/rm_vrf_interfaces.yaml b/changelogs/fragments/rm_vrf_interfaces.yaml new file mode 100644 index 00000000..9a3e3584 --- /dev/null +++ b/changelogs/fragments/rm_vrf_interfaces.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Added iosxr_vrf_interfaces resource module, that helps with configuration of vrfs within interface. diff --git a/docs/cisco.iosxr.iosxr_vrf_interfaces_module.rst b/docs/cisco.iosxr.iosxr_vrf_interfaces_module.rst new file mode 100644 index 00000000..1968ade9 --- /dev/null +++ b/docs/cisco.iosxr.iosxr_vrf_interfaces_module.rst @@ -0,0 +1,634 @@ +.. _cisco.iosxr.iosxr_vrf_interfaces_module: + + +******************************** +cisco.iosxr.iosxr_vrf_interfaces +******************************** + +**Resource module to configure VRF interfaces.** + + +Version added: 10.3.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This module configures and manages the VRF configuration in interface on IOS XR platforms. + + + + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterChoices/DefaultsComments
+
+ config + +
+ list + / elements=dictionary +
+
+ +
A list of VRF interfaces options.
+
+
+ name + +
+ string + / required +
+
+ +
Full name of the interface excluding any logical unit number, i.e. GigabitEthernet0/0/0/1.
+
+
+ vrf_name + +
+ string +
+
+ +
Vrf that is to be added to the interface.
+
+
+ running_config + +
+ string +
+
+ +
This option is used only with state parsed.
+
The value of this option should be the output received from the IOS device by executing the command sh running-config interface.
+
The state parsed reads the configuration from 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 parsed key within the result.
+
+
+ state + +
+ string +
+
+
    Choices: +
  • merged ←
  • +
  • replaced
  • +
  • overridden
  • +
  • deleted
  • +
  • gathered
  • +
  • rendered
  • +
  • parsed
  • +
+
+
The state the configuration should be left in
+
The states rendered, gathered and parsed does not perform any change on the device.
+
The state rendered will transform the configuration in config option to platform specific CLI commands which will be returned in the rendered key within the result. For state rendered active connection to remote host is not required.
+
The state gathered will fetch the running configuration from device and transform it into structured data in the format as per the resource module argspec and the value is returned in the gathered key within the result.
+
The state parsed reads the configuration from running_config option and transforms it into JSON format as per the resource module parameters and the value is returned in the parsed key within the result. The value of running_config option should be the same format as the output of command show running-config | include ip route|ipv6 route executed on device. For state parsed active connection to remote host is not required.
+
+
+ + +Notes +----- + +.. note:: + - Tested against Cisco IOS-XR 7.2.2. + - This module works with connection ``network_cli``. + + + +Examples +-------- + +.. code-block:: yaml + + # Using merged + + # Before state: + # ------------- + # + # viosxr#show running-config interfaces + # interface MgmtEth0/RP0/CPU0/0 + # ipv4 address dhcp + # ! + # interface GigabitEthernet0/0/0/0 + # description this is interface0 + # cdp + # ! + # interface GigabitEthernet0/0/0/1 + # shutdown + # ! + # interface GigabitEthernet0/0/0/2 + # shutdown + # ! + + - name: Simple merge selective + cisco.iosxr.iosxr_vrf_interfaces: + state: merged + config: + - name: MgmtEth0/RP0/CPU0/0 + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_C + - name: GigabitEthernet0/0/0/2 + vrf_name: vrf_D + + # Task Output + # ----------- + # + # before: + # - name: MgmtEth0/RP0/CPU0/0 + # - name: GigabitEthernet0/0/0/0 + # - name: GigabitEthernet0/0/0/1 + # - name: GigabitEthernet0/0/0/2 + # commands: + # - interface GigabitEthernet0/0/0/1 + # - vrf vrf_C + # - interface GigabitEthernet0/0/0/2 + # - vrf vrf_D + # after: + # - name: MgmtEth0/RP0/CPU0/0 + # - name: GigabitEthernet0/0/0/0 + # - name: GigabitEthernet0/0/0/1 + # vrf_name: vrf_C + # - name: GigabitEthernet0/0/0/2 + # vrf_name: vrf_D + + # After state: + # ------------- + # + # viosxr#show running-config interfaces + # interface MgmtEth0/RP0/CPU0/0 + # ipv4 address dhcp + # ! + # interface GigabitEthernet0/0/0/0 + # description this is interface0 + # cdp + # ! + # interface GigabitEthernet0/0/0/1 + # vrf vrf_C + # shutdown + # ! + # interface GigabitEthernet0/0/0/2 + # vrf vrf_D + # shutdown + # ! + + # Using replaced + + # Before state: + # ------------- + # + # viosxr#show running-config interfaces + # interface MgmtEth0/RP0/CPU0/0 + # ipv4 address dhcp + # ! + # interface GigabitEthernet0/0/0/0 + # description this is interface0 + # cdp + # ! + # interface GigabitEthernet0/0/0/1 + # vrf vrf_C + # shutdown + # ! + # interface GigabitEthernet0/0/0/2 + # vrf vrf_D + # shutdown + # ! + + - name: Simple replaced selective + cisco.iosxr.iosxr_vrf_interfaces: + state: replaced + config: + - name: MgmtEth0/RP0/CPU0/0 + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_E + - name: GigabitEthernet0/0/0/2 + vrf_name: vrf_D + + # Task Output + # ----------- + # + # before: + # - name: MgmtEth0/RP0/CPU0/0 + # - name: GigabitEthernet0/0/0/0 + # - name: GigabitEthernet0/0/0/1 + # vrf_name: vrf_C + # - name: GigabitEthernet0/0/0/2 + # vrf_name: vrf_D + # commands: + # - interface GigabitEthernet0/0/0/1 + # - vrf vrf_E + # after: + # - name: MgmtEth0/RP0/CPU0/0 + # - name: GigabitEthernet0/0/0/0 + # - name: GigabitEthernet0/0/0/1 + # vrf_name: vrf_E + # - name: GigabitEthernet0/0/0/2 + # vrf_name: vrf_D + + # After state: + # ------------- + # + # viosxr#show running-config interfaces + # interface MgmtEth0/RP0/CPU0/0 + # ipv4 address dhcp + # ! + # interface GigabitEthernet0/0/0/0 + # description this is interface0 + # cdp + # ! + # interface GigabitEthernet0/0/0/1 + # vrf vrf_E + # shutdown + # ! + # interface GigabitEthernet0/0/0/2 + # vrf vrf_D + # shutdown + # ! + + # Using overridden + + # Before state: + # ------------- + # + # viosxr#show running-config interfaces + # interface MgmtEth0/RP0/CPU0/0 + # ipv4 address dhcp + # ! + # interface GigabitEthernet0/0/0/0 + # description this is interface0 + # cdp + # ! + # interface GigabitEthernet0/0/0/1 + # vrf vrf_C + # shutdown + # ! + # interface GigabitEthernet0/0/0/2 + # vrf vrf_D + # shutdown + # ! + + - name: Simple overridden selective + cisco.iosxr.iosxr_vrf_interfaces: + state: overridden + config: + - name: MgmtEth0/RP0/CPU0/0 + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_E + + # Task Output + # ----------- + # + # before: + # - name: MgmtEth0/RP0/CPU0/0 + # - name: GigabitEthernet0/0/0/0 + # - name: GigabitEthernet0/0/0/1 + # vrf_name: vrf_C + # - name: GigabitEthernet0/0/0/2 + # vrf_name: vrf_D + # commands: + # - interface GigabitEthernet0/0/0/1 + # - vrf vrf_E + # - interface GigabitEthernet0/0/0/2 + # - no vrf vrf_E + # after: + # - name: MgmtEth0/RP0/CPU0/0 + # - name: GigabitEthernet0/0/0/0 + # - name: GigabitEthernet0/0/0/1 + # vrf_name: vrf_E + # - name: GigabitEthernet0/0/0/2 + + # After state: + # ------------- + # + # viosxr#show running-config interfaces + # interface MgmtEth0/RP0/CPU0/0 + # ipv4 address dhcp + # ! + # interface GigabitEthernet0/0/0/0 + # description this is interface0 + # cdp + # ! + # interface GigabitEthernet0/0/0/1 + # vrf vrf_E + # shutdown + # ! + # interface GigabitEthernet0/0/0/2 + # shutdown + # ! + + # Using deleted + + # Before state: + # ------------- + # + # viosxr#show running-config interfaces + # interface MgmtEth0/RP0/CPU0/0 + # ipv4 address dhcp + # ! + # interface GigabitEthernet0/0/0/0 + # description this is interface0 + # cdp + # ! + # interface GigabitEthernet0/0/0/1 + # vrf vrf_E + # shutdown + # ! + # interface GigabitEthernet0/0/0/2 + # vrf vrf_D + # shutdown + # ! + + - name: Simple deleted selective + cisco.iosxr.iosxr_vrf_interfaces: + state: deleted + config: + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_E + + # Task Output + # ----------- + # + # before: + # - name: MgmtEth0/RP0/CPU0/0 + # - name: GigabitEthernet0/0/0/0 + # - name: GigabitEthernet0/0/0/1 + # vrf_name: vrf_E + # - name: GigabitEthernet0/0/0/2 + # vrf_name: vrf_D + # commands: + # - interface GigabitEthernet0/0/0/1 + # - no vrf vrf_E + # after: + # - name: MgmtEth0/RP0/CPU0/0 + # - name: GigabitEthernet0/0/0/0 + # - name: GigabitEthernet0/0/0/1 + # - name: GigabitEthernet0/0/0/2 + # vrf_name: vrf_D + + # After state: + # ------------- + # + # viosxr#show running-config interfaces + # interface MgmtEth0/RP0/CPU0/0 + # ipv4 address dhcp + # ! + # interface GigabitEthernet0/0/0/0 + # description this is interface0 + # cdp + # ! + # interface GigabitEthernet0/0/0/1 + # shutdown + # ! + # interface GigabitEthernet0/0/0/2 + # vrf vrf_D + # shutdown + # ! + + # Using gathered + + # Before state: + # ------------- + # + # viosxr#show running-config interfaces + # interface MgmtEth0/RP0/CPU0/0 + # ipv4 address dhcp + # ! + # interface GigabitEthernet0/0/0/0 + # description this is interface0 + # cdp + # ! + # interface GigabitEthernet0/0/0/1 + # vrf vrf_C + # shutdown + # ! + # interface GigabitEthernet0/0/0/2 + # vrf vrf_D + # shutdown + # ! + + - name: Simple gathered selective + cisco.iosxr.iosxr_vrf_interfaces: + state: gathered + + # Task Output + # ----------- + # + # gathered: + # - name: MgmtEth0/RP0/CPU0/0 + # - name: GigabitEthernet0/0/0/0 + # - name: GigabitEthernet0/0/0/1 + # vrf_name: vrf_C + # - name: GigabitEthernet0/0/0/2 + # vrf_name: vrf_D + + # Using rendered + + # Before state: + # ------------- + # + # viosxr#show running-config interfaces + # interface MgmtEth0/RP0/CPU0/0 + # ipv4 address dhcp + # ! + # interface GigabitEthernet0/0/0/0 + # description this is interface0 + # cdp + # ! + # interface GigabitEthernet0/0/0/1 + # vrf vrf_C + # shutdown + # ! + # interface GigabitEthernet0/0/0/2 + # vrf vrf_D + # shutdown + # ! + + - name: Simple rendered selective + cisco.iosxr.iosxr_vrf_interfaces: + state: rendered + + # Task Output + # ----------- + # + # commands: + # - interface GigabitEthernet0/0/0/1 + # - vrf vrf_C + # - interface GigabitEthernet0/0/0/2 + # - vrf vrf_D + + + +Return Values +------------- +Common return values are documented `here `_, the following are the fields unique to this module: + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyReturnedDescription
+
+ after + +
+ dictionary +
+
when changed +
The resulting configuration after module execution.
+
+
Sample:
+
This output will always be in the same format as the module argspec.
+
+
+ before + +
+ dictionary +
+
when state is merged, replaced, overridden, deleted or purged +
The configuration prior to the module execution.
+
+
Sample:
+
This output will always be in the same format as the module argspec.
+
+
+ commands + +
+ list +
+
when state is merged, replaced, overridden, deleted or purged +
The set of commands pushed to the remote device.
+
+
Sample:
+
['interface GigabitEthernet0/0/0/1', 'no vrf test_vrf1', 'vrf test_vrf2']
+
+
+ gathered + +
+ list +
+
when state is gathered +
Facts about the network resource gathered from the remote device as structured data.
+
+
Sample:
+
This output will always be in the same format as the module argspec.
+
+
+ parsed + +
+ list +
+
when state is parsed +
The device native config provided in running_config option parsed into structured data as per module argspec.
+
+
Sample:
+
This output will always be in the same format as the module argspec.
+
+
+ rendered + +
+ list +
+
when state is rendered +
The provided configuration in the task rendered in device-native format (offline).
+
+
Sample:
+
['interface GigabitEthernet0/0/0/1', 'no vrf test_vrf1', 'vrf test_vrf2']
+
+

+ + +Status +------ + + +Authors +~~~~~~~ + +- Sagar Paul (@KB-perByte) diff --git a/meta/runtime.yml b/meta/runtime.yml index e6b8f953..74e5d913 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -64,3 +64,5 @@ plugin_routing: redirect: cisco.iosxr.iosxr_vrf_global vrf_address_family: redirect: cisco.iosxr.iosxr_vrf_address_family + vrf_interfaces: + redirect: cisco.iosxr.iosxr_vrf_interfaces diff --git a/plugins/module_utils/network/iosxr/argspec/vrf_interfaces/__init__.py b/plugins/module_utils/network/iosxr/argspec/vrf_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugins/module_utils/network/iosxr/argspec/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/iosxr/argspec/vrf_interfaces/vrf_interfaces.py new file mode 100644 index 00000000..c7117517 --- /dev/null +++ b/plugins/module_utils/network/iosxr/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 iosxr_vrf_interfaces module +""" + + +class Vrf_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the iosxr_vrf_interfaces module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "vrf_name": {"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/iosxr/config/vrf_interfaces/__init__.py b/plugins/module_utils/network/iosxr/config/vrf_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugins/module_utils/network/iosxr/config/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/iosxr/config/vrf_interfaces/vrf_interfaces.py new file mode 100644 index 00000000..356d406b --- /dev/null +++ b/plugins/module_utils/network/iosxr/config/vrf_interfaces/vrf_interfaces.py @@ -0,0 +1,98 @@ +# +# -*- 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 iosxr_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 ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import Facts +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.vrf_interfaces import ( + Vrf_interfacesTemplate, +) + + +class Vrf_interfaces(ResourceModule): + """ + The iosxr_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 = [ + "interface", + "vrf_name", + ] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = {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. + """ + 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/iosxr/facts/facts.py b/plugins/module_utils/network/iosxr/facts/facts.py index 16da9a2a..9becce25 100644 --- a/plugins/module_utils/network/iosxr/facts/facts.py +++ b/plugins/module_utils/network/iosxr/facts/facts.py @@ -103,6 +103,9 @@ from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.vrf_global.vrf_global import ( Vrf_globalFacts, ) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesFacts, +) FACT_LEGACY_SUBSETS = dict( @@ -138,6 +141,7 @@ vrf_address_family=Vrf_address_familyFacts, vrf_global=Vrf_globalFacts, route_maps=Route_mapsFacts, + vrf_interfaces=Vrf_interfacesFacts, ) diff --git a/plugins/module_utils/network/iosxr/facts/vrf_interfaces/__init__.py b/plugins/module_utils/network/iosxr/facts/vrf_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugins/module_utils/network/iosxr/facts/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/iosxr/facts/vrf_interfaces/vrf_interfaces.py new file mode 100644 index 00000000..87572af1 --- /dev/null +++ b/plugins/module_utils/network/iosxr/facts/vrf_interfaces/vrf_interfaces.py @@ -0,0 +1,71 @@ +# -*- 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 iosxr 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 ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.vrf_interfaces import ( + Vrf_interfacesTemplate, +) + + +class Vrf_interfacesFacts(object): + """The iosxr vrf_interfaces facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Vrf_interfacesArgs.argument_spec + + def get_device_data(self, connection): + return connection.get_config(flags="interface") + + 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 = self.get_device_data(connection) + + # 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/iosxr/rm_templates/vrf_interfaces.py b/plugins/module_utils/network/iosxr/rm_templates/vrf_interfaces.py new file mode 100644 index 00000000..65cb7137 --- /dev/null +++ b/plugins/module_utils/network/iosxr/rm_templates/vrf_interfaces.py @@ -0,0 +1,63 @@ +# -*- 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': 'interface', + 'getval': re.compile( + r''' + ^interface + \s(preconfigure)*\s* + (?P\S+)$''', re.VERBOSE, + ), + 'setval': 'interface {{ name }}', + 'result': { + '{{ name }}': { + 'name': '{{ name }}', + }, + }, + 'shared': True, + }, + { + "name": "vrf_name", + "getval": re.compile( + r""" + \s+vrf\s(?P\S+)$ + """, + re.VERBOSE, + ), + "setval": "vrf {{ vrf_name }}", + "result": { + '{{ name }}': { + 'vrf_name': '{{ vrf_name }}', + }, + }, + }, + ] + # fmt: on diff --git a/plugins/modules/iosxr_vrf_interfaces.py b/plugins/modules/iosxr_vrf_interfaces.py new file mode 100644 index 00000000..2743f79f --- /dev/null +++ b/plugins/modules/iosxr_vrf_interfaces.py @@ -0,0 +1,524 @@ +#!/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 iosxr_vrf_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: iosxr_vrf_interfaces +short_description: Resource module to configure VRF interfaces. +description: This module configures and manages the VRF configuration in interface on IOS XR platforms. +version_added: 10.3.0 +author: Sagar Paul (@KB-perByte) +notes: + - Tested against Cisco IOS-XR 7.2.2. + - This module works with connection C(network_cli). +options: + config: + description: A list of VRF interfaces options. + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit number, + i.e. GigabitEthernet0/0/0/1. + type: str + required: true + vrf_name: + description: + - Vrf that is to be added to 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(sh running-config 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 + - The states I(rendered), I(gathered) and I(parsed) does not perform any + change on the device. + - The state I(rendered) will transform the configuration in C(config) + option to platform specific CLI commands which will be returned in the + I(rendered) key within the result. For state I(rendered) active + connection to remote host is not required. + - The state I(gathered) will fetch the running configuration from device + and transform it into structured data in the format as per the resource + module argspec and the value is returned in the I(gathered) key within + the result. + - The state I(parsed) reads the configuration from C(running_config) + option and transforms it into JSON format as per the resource module + parameters and the value is returned in the I(parsed) key within the + result. The value of C(running_config) option should be the same format + as the output of command I(show running-config | include ip route|ipv6 + route) executed on device. For state I(parsed) active connection to + remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# viosxr#show running-config interfaces +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# description this is interface0 +# cdp +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# shutdown +# ! + +- name: Simple merge selective + cisco.iosxr.iosxr_vrf_interfaces: + state: merged + config: + - name: MgmtEth0/RP0/CPU0/0 + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_C + - name: GigabitEthernet0/0/0/2 + vrf_name: vrf_D + +# Task Output +# ----------- +# +# before: +# - name: MgmtEth0/RP0/CPU0/0 +# - name: GigabitEthernet0/0/0/0 +# - name: GigabitEthernet0/0/0/1 +# - name: GigabitEthernet0/0/0/2 +# commands: +# - interface GigabitEthernet0/0/0/1 +# - vrf vrf_C +# - interface GigabitEthernet0/0/0/2 +# - vrf vrf_D +# after: +# - name: MgmtEth0/RP0/CPU0/0 +# - name: GigabitEthernet0/0/0/0 +# - name: GigabitEthernet0/0/0/1 +# vrf_name: vrf_C +# - name: GigabitEthernet0/0/0/2 +# vrf_name: vrf_D + +# After state: +# ------------- +# +# viosxr#show running-config interfaces +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# description this is interface0 +# cdp +# ! +# interface GigabitEthernet0/0/0/1 +# vrf vrf_C +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# vrf vrf_D +# shutdown +# ! + +# Using replaced + +# Before state: +# ------------- +# +# viosxr#show running-config interfaces +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# description this is interface0 +# cdp +# ! +# interface GigabitEthernet0/0/0/1 +# vrf vrf_C +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# vrf vrf_D +# shutdown +# ! + +- name: Simple replaced selective + cisco.iosxr.iosxr_vrf_interfaces: + state: replaced + config: + - name: MgmtEth0/RP0/CPU0/0 + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_E + - name: GigabitEthernet0/0/0/2 + vrf_name: vrf_D + +# Task Output +# ----------- +# +# before: +# - name: MgmtEth0/RP0/CPU0/0 +# - name: GigabitEthernet0/0/0/0 +# - name: GigabitEthernet0/0/0/1 +# vrf_name: vrf_C +# - name: GigabitEthernet0/0/0/2 +# vrf_name: vrf_D +# commands: +# - interface GigabitEthernet0/0/0/1 +# - vrf vrf_E +# after: +# - name: MgmtEth0/RP0/CPU0/0 +# - name: GigabitEthernet0/0/0/0 +# - name: GigabitEthernet0/0/0/1 +# vrf_name: vrf_E +# - name: GigabitEthernet0/0/0/2 +# vrf_name: vrf_D + +# After state: +# ------------- +# +# viosxr#show running-config interfaces +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# description this is interface0 +# cdp +# ! +# interface GigabitEthernet0/0/0/1 +# vrf vrf_E +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# vrf vrf_D +# shutdown +# ! + +# Using overridden + +# Before state: +# ------------- +# +# viosxr#show running-config interfaces +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# description this is interface0 +# cdp +# ! +# interface GigabitEthernet0/0/0/1 +# vrf vrf_C +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# vrf vrf_D +# shutdown +# ! + +- name: Simple overridden selective + cisco.iosxr.iosxr_vrf_interfaces: + state: overridden + config: + - name: MgmtEth0/RP0/CPU0/0 + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_E + +# Task Output +# ----------- +# +# before: +# - name: MgmtEth0/RP0/CPU0/0 +# - name: GigabitEthernet0/0/0/0 +# - name: GigabitEthernet0/0/0/1 +# vrf_name: vrf_C +# - name: GigabitEthernet0/0/0/2 +# vrf_name: vrf_D +# commands: +# - interface GigabitEthernet0/0/0/1 +# - vrf vrf_E +# - interface GigabitEthernet0/0/0/2 +# - no vrf vrf_E +# after: +# - name: MgmtEth0/RP0/CPU0/0 +# - name: GigabitEthernet0/0/0/0 +# - name: GigabitEthernet0/0/0/1 +# vrf_name: vrf_E +# - name: GigabitEthernet0/0/0/2 + +# After state: +# ------------- +# +# viosxr#show running-config interfaces +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# description this is interface0 +# cdp +# ! +# interface GigabitEthernet0/0/0/1 +# vrf vrf_E +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# shutdown +# ! + +# Using deleted + +# Before state: +# ------------- +# +# viosxr#show running-config interfaces +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# description this is interface0 +# cdp +# ! +# interface GigabitEthernet0/0/0/1 +# vrf vrf_E +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# vrf vrf_D +# shutdown +# ! + +- name: Simple deleted selective + cisco.iosxr.iosxr_vrf_interfaces: + state: deleted + config: + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_E + +# Task Output +# ----------- +# +# before: +# - name: MgmtEth0/RP0/CPU0/0 +# - name: GigabitEthernet0/0/0/0 +# - name: GigabitEthernet0/0/0/1 +# vrf_name: vrf_E +# - name: GigabitEthernet0/0/0/2 +# vrf_name: vrf_D +# commands: +# - interface GigabitEthernet0/0/0/1 +# - no vrf vrf_E +# after: +# - name: MgmtEth0/RP0/CPU0/0 +# - name: GigabitEthernet0/0/0/0 +# - name: GigabitEthernet0/0/0/1 +# - name: GigabitEthernet0/0/0/2 +# vrf_name: vrf_D + +# After state: +# ------------- +# +# viosxr#show running-config interfaces +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# description this is interface0 +# cdp +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# vrf vrf_D +# shutdown +# ! + +# Using gathered + +# Before state: +# ------------- +# +# viosxr#show running-config interfaces +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# description this is interface0 +# cdp +# ! +# interface GigabitEthernet0/0/0/1 +# vrf vrf_C +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# vrf vrf_D +# shutdown +# ! + +- name: Simple gathered selective + cisco.iosxr.iosxr_vrf_interfaces: + state: gathered + +# Task Output +# ----------- +# +# gathered: +# - name: MgmtEth0/RP0/CPU0/0 +# - name: GigabitEthernet0/0/0/0 +# - name: GigabitEthernet0/0/0/1 +# vrf_name: vrf_C +# - name: GigabitEthernet0/0/0/2 +# vrf_name: vrf_D + +# Using rendered + +# Before state: +# ------------- +# +# viosxr#show running-config interfaces +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# description this is interface0 +# cdp +# ! +# interface GigabitEthernet0/0/0/1 +# vrf vrf_C +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# vrf vrf_D +# shutdown +# ! + +- name: Simple rendered selective + cisco.iosxr.iosxr_vrf_interfaces: + state: rendered + +# Task Output +# ----------- +# +# commands: +# - interface GigabitEthernet0/0/0/1 +# - vrf vrf_C +# - interface GigabitEthernet0/0/0/2 +# - vrf vrf_D +""" + +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: + - interface GigabitEthernet0/0/0/1 + - no vrf test_vrf1 + - vrf test_vrf2 +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - interface GigabitEthernet0/0/0/1 + - no vrf test_vrf1 + - vrf test_vrf2 +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.iosxr.plugins.module_utils.network.iosxr.argspec.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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() diff --git a/tests/integration/targets/iosxr_vrf_interfaces/defaults/main.yml b/tests/integration/targets/iosxr_vrf_interfaces/defaults/main.yml new file mode 100644 index 00000000..871ea460 --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/defaults/main.yml @@ -0,0 +1,2 @@ +--- +testcase: "[^_].*" diff --git a/tests/integration/targets/iosxr_vrf_interfaces/meta/main.yml b/tests/integration/targets/iosxr_vrf_interfaces/meta/main.yml new file mode 100644 index 00000000..e69de29b diff --git a/tests/integration/targets/iosxr_vrf_interfaces/tasks/cli.yaml b/tests/integration/targets/iosxr_vrf_interfaces/tasks/cli.yaml new file mode 100644 index 00000000..39471d59 --- /dev/null +++ b/tests/integration/targets/iosxr_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/iosxr_vrf_interfaces/tasks/main.yaml b/tests/integration/targets/iosxr_vrf_interfaces/tasks/main.yaml new file mode 100644 index 00000000..f75f2f03 --- /dev/null +++ b/tests/integration/targets/iosxr_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/iosxr_vrf_interfaces/tests/common/_parsed.cfg b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/_parsed.cfg new file mode 100644 index 00000000..4254a6ea --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/_parsed.cfg @@ -0,0 +1,28 @@ +interface Loopback888 + description test for ansible + shutdown +! +interface Loopback999 +! +interface MgmtEth0/RP0/CPU0/0 + ipv4 address dhcp +! +interface GigabitEthernet0/0/0/0 + description this is interface0 + cdp +! +interface GigabitEthernet0/0/0/1 + vrf vrf_C + shutdown +! +interface GigabitEthernet0/0/0/2 + vrf vrf_D + shutdown +! +interface preconfigure GigabitEthernet0/0/0/7 + description test interface + ipv4 address 172.31.1.1 255.255.255.0 +! +interface preconfigure TenGigE0/0/0/0 + service-policy output mypolicy +! diff --git a/tests/integration/targets/iosxr_vrf_interfaces/tests/common/_populate_config.yaml b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/_populate_config.yaml new file mode 100644 index 00000000..9a00e095 --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/_populate_config.yaml @@ -0,0 +1,16 @@ +--- +- name: Populate interface - 1 + cisco.iosxr.iosxr_config: + lines: + - "vrf vrf_A" + parents: "interface GigabitEthernet0/0/0/1" + vars: + ansible_connection: ansible.netcommon.network_cli + +- name: Populate interface - 2 + cisco.iosxr.iosxr_config: + lines: + - "vrf vrf_B" + parents: "interface GigabitEthernet0/0/0/2" + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/iosxr_vrf_interfaces/tests/common/_remove_config.yaml b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/_remove_config.yaml new file mode 100644 index 00000000..90f7e3c7 --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/_remove_config.yaml @@ -0,0 +1,10 @@ +--- +- name: Populate with simple overridden + cisco.iosxr.iosxr_vrf_interfaces: + state: overridden + config: + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + - name: GigabitEthernet0/0/0/2 + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/iosxr_vrf_interfaces/tests/common/empty_config.yaml b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/empty_config.yaml new file mode 100644 index 00000000..5b5bff5f --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/empty_config.yaml @@ -0,0 +1,50 @@ +--- +- ansible.builtin.debug: + msg: START iosxr_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.iosxr.iosxr_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.iosxr.iosxr_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.iosxr.iosxr_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.iosxr.iosxr_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 iosxr_vrf_interfaces empty_config integration tests on connection={{ ansible_connection }} diff --git a/tests/integration/targets/iosxr_vrf_interfaces/tests/common/gathered.yaml b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/gathered.yaml new file mode 100644 index 00000000..86680812 --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/gathered.yaml @@ -0,0 +1,24 @@ +--- +- ansible.builtin.debug: + msg: START iosxr_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.iosxr.iosxr_vrf_interfaces: + config: + state: gathered + + - name: Run show show running-config interface + cisco.iosxr.iosxr_command: + commands: show running-config interface + + - name: Assert + ansible.builtin.assert: + that: + - gathered.config == result.gathered + - result['changed'] == false + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/iosxr_vrf_interfaces/tests/common/merged.yaml b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/merged.yaml new file mode 100644 index 00000000..e85627a0 --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/merged.yaml @@ -0,0 +1,34 @@ +--- +- ansible.builtin.debug: + msg: Start iosxr_vrf_interfaces merged integration tests connection={{ ansible_connection}} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Simple merge selective + cisco.iosxr.iosxr_vrf_interfaces: + state: merged + config: + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_C + - name: GigabitEthernet0/0/0/2 + vrf_name: vrf_D + register: result + + - name: Run show show running-config interface + cisco.iosxr.iosxr_command: + commands: show running-config 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/iosxr_vrf_interfaces/tests/common/overridden.yaml b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/overridden.yaml new file mode 100644 index 00000000..90df9596 --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/overridden.yaml @@ -0,0 +1,37 @@ +--- +- ansible.builtin.debug: + msg: Start iosxr_vrf_interfaces overridden integration tests connection={{ ansible_connection}} + +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Override the route-policy configuration + cisco.iosxr.iosxr_vrf_interfaces: + state: overridden + config: + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_C + - name: GigabitEthernet0/0/0/2 + register: result + + - name: Run show show running-config interface + cisco.iosxr.iosxr_command: + commands: show running-config 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/iosxr_vrf_interfaces/tests/common/parsed.yaml b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/parsed.yaml new file mode 100644 index 00000000..f0a1461c --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/parsed.yaml @@ -0,0 +1,14 @@ +--- +- ansible.builtin.debug: + msg: START iosxr_vrf_interfaces parsed integration tests on connection={{ ansible_connection }} + +- name: Parse externally provided route-policy configuration + register: result + cisco.iosxr.iosxr_vrf_interfaces: + running_config: "{{ lookup('file', './_parsed.cfg') }}" + state: parsed + +- name: Assert that configuration was correctly parsed + ansible.builtin.assert: + that: + - merged['after'] == result['parsed'] diff --git a/tests/integration/targets/iosxr_vrf_interfaces/tests/common/rendered.yaml b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/rendered.yaml new file mode 100644 index 00000000..1a78008e --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/tests/common/rendered.yaml @@ -0,0 +1,21 @@ +--- +- ansible.builtin.debug: + msg: START iosxr_vrf_interfaces rendered integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- name: Render route-policy configuration + register: result + cisco.iosxr.iosxr_vrf_interfaces: + state: rendered + config: + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_C + - name: GigabitEthernet0/0/0/2 + vrf_name: vrf_D + +- name: Assert that correct set of commands were rendered + ansible.builtin.assert: + that: + - merged['commands'] == result['rendered'] diff --git a/tests/integration/targets/iosxr_vrf_interfaces/vars/main.yaml b/tests/integration/targets/iosxr_vrf_interfaces/vars/main.yaml new file mode 100644 index 00000000..489b8a10 --- /dev/null +++ b/tests/integration/targets/iosxr_vrf_interfaces/vars/main.yaml @@ -0,0 +1,59 @@ +--- +gathered: + config: + - name: Loopback888 + - name: Loopback999 + - name: MgmtEth0/RP0/CPU0/0 + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_A + - name: GigabitEthernet0/0/0/2 + vrf_name: vrf_B + - name: GigabitEthernet0/0/0/7 + - name: TenGigE0/0/0/0 + +merged: + commands: + - interface GigabitEthernet0/0/0/1 + - vrf vrf_C + - interface GigabitEthernet0/0/0/2 + - vrf vrf_D + after: + - name: Loopback888 + - name: Loopback999 + - name: MgmtEth0/RP0/CPU0/0 + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_C + - name: GigabitEthernet0/0/0/2 + vrf_name: vrf_D + - name: GigabitEthernet0/0/0/7 + - name: TenGigE0/0/0/0 + +overridden: + commands: + - interface GigabitEthernet0/0/0/1 + - vrf vrf_C + - interface GigabitEthernet0/0/0/2 + - no vrf vrf_B + after: + - name: Loopback888 + - name: Loopback999 + - name: MgmtEth0/RP0/CPU0/0 + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_C + - name: GigabitEthernet0/0/0/2 + - name: GigabitEthernet0/0/0/7 + - name: TenGigE0/0/0/0 + before: + - name: Loopback888 + - name: Loopback999 + - name: MgmtEth0/RP0/CPU0/0 + - name: GigabitEthernet0/0/0/0 + - name: GigabitEthernet0/0/0/1 + vrf_name: vrf_A + - name: GigabitEthernet0/0/0/2 + vrf_name: vrf_B + - name: GigabitEthernet0/0/0/7 + - name: TenGigE0/0/0/0 diff --git a/tests/unit/modules/network/iosxr/test_iosxr_vrf_interfaces.py b/tests/unit/modules/network/iosxr/test_iosxr_vrf_interfaces.py new file mode 100644 index 00000000..03eb73dc --- /dev/null +++ b/tests/unit/modules/network/iosxr/test_iosxr_vrf_interfaces.py @@ -0,0 +1,392 @@ +# (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.iosxr.plugins.modules import iosxr_vrf_interfaces +from ansible_collections.cisco.iosxr.tests.unit.modules.utils import set_module_args + +from .iosxr_module import TestIosxrModule + + +class TestIosxrVrfInterfacesModule(TestIosxrModule): + module = iosxr_vrf_interfaces + + def setUp(self): + super(TestIosxrVrfInterfacesModule, 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.iosxr.plugins.module_utils.network.iosxr.facts.vrf_interfaces.vrf_interfaces." + "Vrf_interfacesFacts.get_device_data", + ) + self.get_config = self.mock_get_config.start() + + def tearDown(self): + super(TestIosxrVrfInterfacesModule, self).tearDown() + self.get_resource_connection.stop() + self.get_config.stop() + + def test_iosxr_vrf_interfaces_merged_idempotent(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface Loopback888 + description test for ansible + shutdown + ! + interface Loopback999 + ! + interface MgmtEth0/RP0/CPU0/0 + ipv4 address dhcp + ! + interface GigabitEthernet0/0/0/0 + description this is interface0 + cdp + ! + interface GigabitEthernet0/0/0/1 + vrf testvrf1 + shutdown + ! + interface GigabitEthernet0/0/0/2 + vrf testvrf_12 + shutdown + ! + interface preconfigure GigabitEthernet0/0/0/7 + description test interface + ipv4 address 172.31.1.1 255.255.255.0 + ! + interface preconfigure TenGigE0/0/0/0 + service-policy output mypolicy + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "Loopback888"}, + {"name": "Loopback999"}, + {"name": "MgmtEth0/RP0/CPU0/0"}, + {"name": "GigabitEthernet0/0/0/0"}, + {"name": "GigabitEthernet0/0/0/1", "vrf_name": "testvrf1"}, + {"name": "GigabitEthernet0/0/0/2", "vrf_name": "testvrf_12"}, + {"name": "GigabitEthernet0/0/0/7"}, + {"name": "TenGigE0/0/0/0"}, + ], + state="merged", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_iosxr_vrf_interfaces_merged(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface Loopback888 + description test for ansible + shutdown + ! + interface Loopback999 + ! + interface MgmtEth0/RP0/CPU0/0 + ipv4 address dhcp + ! + interface GigabitEthernet0/0/0/0 + description this is interface0 + cdp + ! + interface GigabitEthernet0/0/0/1 + vrf testvrf1 + shutdown + ! + interface GigabitEthernet0/0/0/2 + vrf testvrf_12 + shutdown + ! + interface preconfigure GigabitEthernet0/0/0/7 + description test interface + ipv4 address 172.31.1.1 255.255.255.0 + ! + interface preconfigure TenGigE0/0/0/0 + service-policy output mypolicy + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "Loopback888"}, + {"name": "Loopback999"}, + {"name": "MgmtEth0/RP0/CPU0/0"}, + {"name": "GigabitEthernet0/0/0/0"}, + {"name": "GigabitEthernet0/0/0/1", "vrf_name": "testvrf1"}, + {"name": "GigabitEthernet0/0/0/2", "vrf_name": "testvrf_12"}, + {"name": "GigabitEthernet0/0/0/7"}, + {"name": "TenGigE0/0/0/0", "vrf_name": "testvrf_12"}, + ], + state="merged", + ), + ) + commands = [ + "interface TenGigE0/0/0/0", + "vrf testvrf_12", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_iosxr_vrf_interfaces_replaced(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface Loopback888 + description test for ansible + shutdown + ! + interface Loopback999 + ! + interface MgmtEth0/RP0/CPU0/0 + ipv4 address dhcp + ! + interface GigabitEthernet0/0/0/0 + description this is interface0 + cdp + ! + interface GigabitEthernet0/0/0/1 + vrf testvrf1 + shutdown + ! + interface GigabitEthernet0/0/0/2 + vrf testvrf_12 + shutdown + ! + interface preconfigure GigabitEthernet0/0/0/7 + description test interface + ipv4 address 172.31.1.1 255.255.255.0 + ! + interface preconfigure TenGigE0/0/0/0 + service-policy output mypolicy + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "Loopback888"}, + {"name": "Loopback999"}, + {"name": "MgmtEth0/RP0/CPU0/0"}, + {"name": "GigabitEthernet0/0/0/0"}, + {"name": "GigabitEthernet0/0/0/1", "vrf_name": "testvrf_replaced"}, + {"name": "GigabitEthernet0/0/0/2", "vrf_name": "testvrf_12"}, + {"name": "GigabitEthernet0/0/0/7"}, + {"name": "TenGigE0/0/0/0", "vrf_name": "testvrf_12"}, + ], + state="replaced", + ), + ) + commands = [ + "interface GigabitEthernet0/0/0/1", + "vrf testvrf_replaced", + "interface TenGigE0/0/0/0", + "vrf testvrf_12", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_iosxr_vrf_interfaces_overridden(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface Loopback888 + description test for ansible + shutdown + ! + interface Loopback999 + ! + interface MgmtEth0/RP0/CPU0/0 + ipv4 address dhcp + ! + interface GigabitEthernet0/0/0/0 + description this is interface0 + cdp + ! + interface GigabitEthernet0/0/0/1 + vrf testvrf1 + shutdown + ! + interface GigabitEthernet0/0/0/2 + vrf testvrf_12 + shutdown + ! + interface preconfigure GigabitEthernet0/0/0/7 + description test interface + ipv4 address 172.31.1.1 255.255.255.0 + ! + interface preconfigure TenGigE0/0/0/0 + service-policy output mypolicy + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "Loopback888"}, + {"name": "Loopback999"}, + {"name": "MgmtEth0/RP0/CPU0/0"}, + {"name": "GigabitEthernet0/0/0/0"}, + {"name": "GigabitEthernet0/0/0/1"}, + {"name": "GigabitEthernet0/0/0/2"}, + {"name": "GigabitEthernet0/0/0/7"}, + {"name": "TenGigE0/0/0/0", "vrf_name": "testvrf_12"}, + ], + state="overridden", + ), + ) + commands = [ + "interface GigabitEthernet0/0/0/1", + "no vrf testvrf1", + "interface GigabitEthernet0/0/0/2", + "no vrf testvrf_12", + "interface TenGigE0/0/0/0", + "vrf testvrf_12", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_iosxr_vrf_interfaces_deleted(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface Loopback888 + description test for ansible + shutdown + ! + interface Loopback999 + ! + interface MgmtEth0/RP0/CPU0/0 + ipv4 address dhcp + ! + interface GigabitEthernet0/0/0/0 + description this is interface0 + cdp + ! + interface GigabitEthernet0/0/0/1 + vrf testvrf1 + shutdown + ! + interface GigabitEthernet0/0/0/2 + vrf testvrf_12 + shutdown + ! + interface preconfigure GigabitEthernet0/0/0/7 + description test interface + ipv4 address 172.31.1.1 255.255.255.0 + ! + interface preconfigure TenGigE0/0/0/0 + service-policy output mypolicy + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "Loopback888"}, + {"name": "Loopback999"}, + {"name": "MgmtEth0/RP0/CPU0/0"}, + {"name": "GigabitEthernet0/0/0/0"}, + {"name": "GigabitEthernet0/0/0/1", "vrf_name": "testvrf_replaced"}, + {"name": "GigabitEthernet0/0/0/2", "vrf_name": "testvrf_12"}, + {"name": "GigabitEthernet0/0/0/7"}, + {"name": "TenGigE0/0/0/0", "vrf_name": "testvrf_12"}, + ], + state="deleted", + ), + ) + commands = [ + "interface GigabitEthernet0/0/0/1", + "no vrf testvrf1", + "interface GigabitEthernet0/0/0/2", + "no vrf testvrf_12", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_iosxr_vrf_interfaces_parsed(self): + set_module_args( + dict( + running_config=dedent( + """\ + interface Loopback888 + description test for ansible + shutdown + ! + interface Loopback999 + ! + interface MgmtEth0/RP0/CPU0/0 + ipv4 address dhcp + ! + interface GigabitEthernet0/0/0/0 + description this is interface0 + cdp + ! + interface GigabitEthernet0/0/0/1 + vrf testvrf1 + shutdown + ! + interface GigabitEthernet0/0/0/2 + vrf testvrf_12 + shutdown + ! + interface preconfigure GigabitEthernet0/0/0/7 + description test interface + ipv4 address 172.31.1.1 255.255.255.0 + ! + interface preconfigure TenGigE0/0/0/0 + service-policy output mypolicy + ! + """, + ), + state="parsed", + ), + ) + result = self.execute_module(changed=False) + parsed_list = [ + {"name": "Loopback888"}, + {"name": "Loopback999"}, + {"name": "MgmtEth0/RP0/CPU0/0"}, + {"name": "GigabitEthernet0/0/0/0"}, + {"name": "GigabitEthernet0/0/0/1", "vrf_name": "testvrf1"}, + {"name": "GigabitEthernet0/0/0/2", "vrf_name": "testvrf_12"}, + {"name": "GigabitEthernet0/0/0/7"}, + {"name": "TenGigE0/0/0/0"}, + ] + self.assertEqual(parsed_list, result["parsed"])