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"