diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml new file mode 100644 index 000000000..604d2f366 --- /dev/null +++ b/.github/workflows/integration-test.yml @@ -0,0 +1,39 @@ +--- +name: Integration +on: + pull_request_target: + branches: [main] + types: + - labeled + - opened + - reopened + - synchronize + paths: + - "plugins/**" + - "tests/integration/**" + workflow_dispatch: + +jobs: + safe-to-test: + if: >- + github.event.label.name == 'safe to test' || + github.event.action != 'labeled' + uses: ansible-network/github_actions/.github/workflows/safe-to-test.yml@main + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + integration: + uses: ansible/ansible-content-actions/.github/workflows/network_integration.yaml@main + needs: + - safe-to-test + with: + lab_title: cisco_ios + network_os: cisco.ios.ios + pytest_addopts: "--color=yes -n 2 --log-level WARNING -vvv" + collection_pre_install: >- + git+https://github.com/ansible-collections/ansible.utils.git + git+https://github.com/ansible-collections/ansible.netcommon.git + secrets: + cml_ssh_password: ${{ secrets.CML_SSH_PASSWORD }} + virl_host: ${{ secrets.VIRL_HOST }} + virl_password: ${{ secrets.VIRL_PASSWORD }} diff --git a/README.md b/README.md index 58187e9ec..4c3094111 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ Name | Description [cisco.ios.ios_user](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_user_module.rst)|Module to manage the aggregates of local users. [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_address_family](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vrf_address_family_module.rst)|Resource 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/changelogs/fragments/add_vrf_address_family.yaml b/changelogs/fragments/add_vrf_address_family.yaml new file mode 100644 index 000000000..ccab55287 --- /dev/null +++ b/changelogs/fragments/add_vrf_address_family.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Adds a new module `ios_vrf_address_family` to manage VRFs address families on Cisco IOS devices. diff --git a/changelogs/fragments/fix_facts_gather.yaml b/changelogs/fragments/fix_facts_gather.yaml new file mode 100644 index 000000000..be0bbe562 --- /dev/null +++ b/changelogs/fragments/fix_facts_gather.yaml @@ -0,0 +1,3 @@ +--- +bugfixes: + - Fixes an issue with facts gathering failing when an sub interface is in a deleted state. diff --git a/changelogs/fragments/fix_purge_and_overridden_operations.yaml b/changelogs/fragments/fix_purge_and_overridden_operations.yaml new file mode 100644 index 000000000..3bd498f6f --- /dev/null +++ b/changelogs/fragments/fix_purge_and_overridden_operations.yaml @@ -0,0 +1,6 @@ +--- +bugfixes: + - Fix purged state operation to enable users to completely remove VLAN configurations. + - Fix overridden state operations to ensure excluded VLANs in the provided configuration are removed, thus overriding the VLAN configuration. + - Improve documentation to provide clarity on the "shutdown" variable. + - Improve unit tests to align with the changes made. diff --git a/docs/cisco.ios.ios_vlans_module.rst b/docs/cisco.ios.ios_vlans_module.rst index 78fa5ec97..1ed4c1d23 100644 --- a/docs/cisco.ios.ios_vlans_module.rst +++ b/docs/cisco.ios.ios_vlans_module.rst @@ -229,7 +229,10 @@ Parameters -
Shutdown VLAN switching.
+
Specifies whether VLAN switching should be administratively enabled or disabled.
+
When set to enabled, the VLAN interface is administratively shut down, which prevents it from forwarding traffic.
+
When set to disabled, the VLAN interface is administratively enabled by issuing the internal no shutdown command, allowing it to forward traffic.
+
The operational state of the VLAN depends on both the administrative state (shutdown or no shutdown) and the physical link status.
@@ -448,100 +451,330 @@ Examples # Before state: # ------------- # - # vios_l2#show vlan + # S1#show vlan + # VLAN Name Status Ports # ---- -------------------------------- --------- ------------------------------- - # 1 default active Gi0/1, Gi0/2 - # 10 vlan_10 active - # 20 vlan_20 act/lshut - # 30 vlan_30 sus/lshut + # 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 + # 10 Vlan_10 active + # 20 Vlan_20 active + # 30 Vlan_30 suspended + # 44 Vlan_44 suspended # 1002 fddi-default act/unsup # 1003 token-ring-default act/unsup # 1004 fddinet-default act/unsup # 1005 trnet-default act/unsup - # + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ # 1 enet 100001 1500 - - - - - 0 0 # 10 enet 100010 1500 - - - - - 0 0 # 20 enet 100020 610 - - - - - 0 0 # 30 enet 100030 1500 - - - - - 0 0 + # 44 enet 100044 1500 - - - - - 0 0 # 1002 fddi 101002 1500 - - - - - 0 0 # 1003 tr 101003 1500 - - - - - 0 0 # 1004 fdnet 101004 1500 - - - ieee - 0 0 # 1005 trnet 101005 1500 - - - ibm - 0 0 - # + # Remote SPAN VLANs # ------------------------------------------------------------------------------ - # 10 + + + # Primary Secondary Type Ports + # ------- --------- ----------------- ------------------------------------------ + # S1# + - name: Override device configuration of all VLANs with provided configuration cisco.ios.ios_vlans: config: - - name: Vlan_10 - vlan_id: 10 - mtu: 1000 + - name: Vlan_2020 + state: active + vlan_id: 20 + shutdown: disabled state: overridden + # Task output: + # ------------ + + # after: + # - mtu: 1500 + # name: default + # shutdown: disabled + # state: active + # vlan_id: 1 + # - mtu: 1500 + # name: Vlan_2020 + # shutdown: disabled + # state: active + # vlan_id: 20 + # - mtu: 1500 + # name: fddi-default + # shutdown: enabled + # state: active + # vlan_id: 1002 + # - mtu: 1500 + # name: token-ring-default + # shutdown: enabled + # state: active + # vlan_id: 1003 + # - mtu: 1500 + # name: fddinet-default + # shutdown: enabled + # state: active + # vlan_id: 1004 + # - mtu: 1500 + # name: trnet-default + # shutdown: enabled + # state: active + # vlan_id: 1005 + + # before: + # - mtu: 1500 + # name: default + # shutdown: disabled + # state: active + # vlan_id: 1 + # - mtu: 1500 + # name: Vlan_10 + # shutdown: disabled + # state: active + # vlan_id: 10 + # - mtu: 610 + # name: Vlan_20 + # shutdown: disabled + # state: active + # vlan_id: 20 + # - mtu: 1500 + # name: Vlan_30 + # shutdown: disabled + # state: suspend + # vlan_id: 30 + # - mtu: 1500 + # name: Vlan_44 + # shutdown: disabled + # state: suspend + # vlan_id: 44 + # - mtu: 1500 + # name: fddi-default + # shutdown: enabled + # state: active + # vlan_id: 1002 + # - mtu: 1500 + # name: token-ring-default + # shutdown: enabled + # state: active + # vlan_id: 1003 + # - mtu: 1500 + # name: fddinet-default + # shutdown: enabled + # state: active + # vlan_id: 1004 + # - mtu: 1500 + # name: trnet-default + # shutdown: enabled + # state: active + # vlan_id: 1005 + + # commands: + # - no vlan 1 + # - no vlan 10 + # - no vlan 30 + # - no vlan 44 + # - no vlan 1002 + # - no vlan 1003 + # - no vlan 1004 + # - no vlan 1005 + # - vlan 20 + # - name Vlan_2020 + # - no mtu 610 + # - no vlan configuration 1 + # - no vlan configuration 10 + # - no vlan configuration 30 + # - no vlan configuration 44 + # - no vlan configuration 1002 + # - no vlan configuration 1003 + # - no vlan configuration 1004 + # - no vlan configuration 1005 + # After state: # ------------ # # vios_l2#show vlan # VLAN Name Status Ports # ---- -------------------------------- --------- ------------------------------- - # 1 default active Gi0/1, Gi0/2 - # 10 Vlan_10 active + # 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 + # 20 Vlan_2020 active # 1002 fddi-default act/unsup # 1003 token-ring-default act/unsup # 1004 fddinet-default act/unsup # 1005 trnet-default act/unsup - # + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ # 1 enet 100001 1500 - - - - - 0 0 - # 10 enet 100010 1000 - - - - - 0 0 + # 20 enet 100020 1500 - - - - - 0 0 # 1002 fddi 101002 1500 - - - - - 0 0 # 1003 tr 101003 1500 - - - - - 0 0 # 1004 fdnet 101004 1500 - - - ieee - 0 0 # 1005 trnet 101005 1500 - - - ibm - 0 0 + # Remote SPAN VLANs + # ------------------------------------------------------------------------------ - # Using overridden + + # Primary Secondary Type Ports + # ------- --------- ----------------- ------------------------------------------ + + # Using purged # Before state: # ------------- # - # Leaf-01#show run nve | sec ^vlan configuration - # vlan configuration 101 - # member evpn-instance 101 vni 10101 - # vlan configuration 102 - # member evpn-instance 102 vni 10102 - # vlan configuration 201 - # member evpn-instance 201 vni 10201 - # vlan configuration 901 - # member vni 50901 + # S1#show vlan - - name: Override device configuration of all VLANs with provided configuration + # VLAN Name Status Ports + # ---- -------------------------------- --------- ------------------------------- + # 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 + # 20 Vlan_2020 active + # 1002 fddi-default act/unsup + # 1003 token-ring-default act/unsup + # 1004 fddinet-default act/unsup + # 1005 trnet-default act/unsup + + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 + # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ + # 1 enet 100001 1500 - - - - - 0 0 + # 20 enet 100020 1500 - - - - - 0 0 + # 1002 fddi 101002 1500 - - - - - 0 0 + # 1003 tr 101003 1500 - - - - - 0 0 + # 1004 fdnet 101004 1500 - - - ieee - 0 0 + # 1005 trnet 101005 1500 - - - ibm - 0 0 + + # Remote SPAN VLANs + # ------------------------------------------------------------------------------ + + + # Primary Secondary Type Ports + # ------- --------- ----------------- ------------------------------------------ + + # S1#show running-config | section ^vlan configuration .+ + # vlan configuration 20 + + + - name: Purge all vlans configuration cisco.ios.ios_vlans: config: - - vlan_id: 101 - member: - vni: 10102 - evi: 102 - - vlan_id: 102 - member: - vni: 10101 - evi: 101 - state: overridden + state: purged + + # Task output: + # ------------ + + # after: + # - mtu: 1500 + # name: default + # shutdown: disabled + # state: active + # vlan_id: 1 + # - mtu: 1500 + # name: fddi-default + # shutdown: enabled + # state: active + # vlan_id: 1002 + # - mtu: 1500 + # name: token-ring-default + # shutdown: enabled + # state: active + # vlan_id: 1003 + # - mtu: 1500 + # name: fddinet-default + # shutdown: enabled + # state: active + # vlan_id: 1004 + # - mtu: 1500 + # name: trnet-default + # shutdown: enabled + # state: active + # vlan_id: 1005 + + # before: + # - mtu: 1500 + # name: default + # shutdown: disabled + # state: active + # vlan_id: 1 + # - mtu: 1500 + # name: Vlan_2020 + # shutdown: disabled + # state: active + # vlan_id: 20 + # - mtu: 1500 + # name: fddi-default + # shutdown: enabled + # state: active + # vlan_id: 1002 + # - mtu: 1500 + # name: token-ring-default + # shutdown: enabled + # state: active + # vlan_id: 1003 + # - mtu: 1500 + # name: fddinet-default + # shutdown: enabled + # state: active + # vlan_id: 1004 + # - mtu: 1500 + # name: trnet-default + # shutdown: enabled + # state: active + # vlan_id: 1005 + + # commands: + # - no vlan 1 + # - no vlan 20 + # - no vlan 1002 + # - no vlan 1003 + # - no vlan 1004 + # - no vlan 1005 + # - no vlan configuration 1 + # - no vlan configuration 20 + # - no vlan configuration 1002 + # - no vlan configuration 1003 + # - no vlan configuration 1004 + # - no vlan configuration 1005 # After state: # ------------ # - # Leaf-01#show run nve | sec ^vlan configuration - # vlan configuration 101 - # member evpn-instance 102 vni 10102 - # vlan configuration 102 - # member evpn-instance 101 vni 10101 + # S1#show vlan + + # VLAN Name Status Ports + # ---- -------------------------------- --------- ------------------------------- + # 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 + # 1002 fddi-default act/unsup + # 1003 token-ring-default act/unsup + # 1004 fddinet-default act/unsup + # 1005 trnet-default act/unsup + + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 + # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ + # 1 enet 100001 1500 - - - - - 0 0 + # 1002 fddi 101002 1500 - - - - - 0 0 + # 1003 tr 101003 1500 - - - - - 0 0 + # 1004 fdnet 101004 1500 - - - ieee - 0 0 + # 1005 trnet 101005 1500 - - - ibm - 0 0 + + # Remote SPAN VLANs + # ------------------------------------------------------------------------------ + + + # Primary Secondary Type Ports + # ------- --------- ----------------- ------------------------------------------ + + # S1#show running-config | section ^vlan configuration .+ + # S1# + # Using replaced diff --git a/docs/cisco.ios.ios_vrf_address_family_module.rst b/docs/cisco.ios.ios_vrf_address_family_module.rst new file mode 100644 index 000000000..74f8e6b88 --- /dev/null +++ b/docs/cisco.ios.ios_vrf_address_family_module.rst @@ -0,0 +1,7459 @@ +.. _cisco.ios.ios_vrf_address_family_module: + + +******************************** +cisco.ios.ios_vrf_address_family +******************************** + +**Resource module to configure VRF definitions.** + + +Version added: 7.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This module provides declarative management of VRF definitions on Cisco IOS. + + + + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterChoices/DefaultsComments
+
+ config + +
+ list + / elements=dictionary +
+
+ +
A list of device configurations for VRF.
+
+
+ address_families + +
+ list + / elements=dictionary +
+
+ +
Enable address family and enter its config mode - AFI/SAFI configuration
+
+
+ afi + +
+ string +
+
+
    Choices: +
  • ipv4
  • +
  • ipv6
  • +
+
+
Address Family Identifier (AFI)
+
+
+ bgp + +
+ dictionary +
+
+ +
Commands pertaining to BGP configuration.
+
+
+ next_hop + +
+ dictionary +
+
+ +
Next-hop for the routes of a VRF in the backbone.
+
+
+ loopback + +
+ integer +
+
+ +
Loopback interface for next-hop
+
+
+ export + +
+ dictionary +
+
+ +
VRF export
+
+
+ ipv4 + +
+ dictionary +
+
+ +
Address family based VRF export
+
+
+ multicast + +
+ dictionary +
+
+ +
Export prefixes to IPv4 Multicast table
+
+
+ map + +
+ string +
+
+ +
Route-map name
+
+
+ prefix + +
+ integer +
+
+ +
Upper limit on export prefixes without hogging memory
+
+
+ unicast + +
+ dictionary +
+
+ +
Export prefixes to IPv4 Unicast table
+
+
+ allow_evpn + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Allow EVPN routes into global table
+
+
+ map + +
+ string +
+
+ +
Route-map name
+
+
+ prefix + +
+ integer +
+
+ +
Upper limit on export prefixes without hogging memory
+
+
+ map + +
+ string +
+
+ +
Route-map name
+
+
+ import_config + +
+ dictionary +
+
+ +
VRF import
+
+
+ ipv4 + +
+ dictionary +
+
+ +
Address family based VRF import
+
+
+ multicast + +
+ dictionary +
+
+ +
Import prefixes from IPv4 Multicast table
+
+
+ map + +
+ string +
+
+ +
Route-map name
+
+
+ prefix + +
+ integer +
+
+ +
Upper limit on import prefixes without hogging memory
+
+
+ unicast + +
+ dictionary +
+
+ +
Import prefixes from IPv4 Unicast table
+
+
+ allow_evpn + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
allow Global->VRF routes into EVPN
+
+
+ limit + +
+ integer +
+
+ +
Upper limit on import prefixes without hogging memory
+
+
+ map + +
+ string +
+
+ +
Route-map based VRF import
+
+
+ map + +
+ string +
+
+ +
Route-map name
+
+
+ inter_as_hybrid + +
+ dictionary +
+
+ +
Inter AS hybrid mode
+
+
+ csc + +
+ dictionary +
+
+ +
Carrier Supporting Carrier
+
+
+ next_hop + +
+ string +
+
+ +
Next-hop for the routes of a VRF in the backbone.
+
+
+ next_hop + +
+ string +
+
+ +
Next-hop for the routes of a VRF in the backbone.
+
+
+ mdt + +
+ dictionary +
+
+ +
Backbone Multicast Distribution Tree
+
+
+ auto_discovery + +
+ dictionary +
+
+ +
BGP auto-discovery for MVPN
+
+
+ ingress_replication + +
+ dictionary +
+
+ +
BGP auto-discovery for Ingress-Replication
+
+
+ inter_as + +
+ dictionary +
+
+ +
Enable Inter-AS BGP auto-discovery
+
+
+ mdt_hello_enable + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Enable PIM Hellos over MDT interface
+
+
+ mdt_hello_enable + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Enable PIM Hellos over MDT interface
+
+
+ pim + +
+ dictionary +
+
+ +
BGP auto-discovery for PIM
+
+
+ inter_as + +
+ dictionary +
+
+ +
Enable Inter-AS BGP auto-discovery
+
+
+ mdt_hello_enable + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Enable PIM Hellos over MDT interface
+
+
+ pim_tlv_announce + +
+ dictionary +
+
+ +
Announce PIM TLV for data MDT
+
+
+ mdt_hello_enable + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Enable PIM Hellos over MDT interface
+
+
+ mdt_hello_enable + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Enable PIM Hellos over MDT interface
+
+
+ pim_tlv_announce + +
+ dictionary +
+
+ +
Announce PIM TLV for data MDT
+
+
+ mdt_hello_enable + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Enable PIM Hellos over MDT interface
+
+
+ receiver_site + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
BGP receiver only site for MVPN
+
+
+ data + +
+ dictionary +
+
+ +
MDT data trees
+
+
+ ingress_replication + +
+ dictionary +
+
+ +
Use Ingress-Replication to create the data MDT
+
+
+ immediate_switch + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Switch immediately to Data MDT tree
+
+
+ list + +
+ dictionary +
+
+ +
Access-list
+
+
+ access_list_name + +
+ string +
+
+ +
IP Named Extended Access list
+
+
+ access_list_number + +
+ integer +
+
+ +
Access-list number
+
+
+ number + +
+ integer +
+
+ +
Number of data MDT
+
+
+ list + +
+ dictionary +
+
+ +
Access-list
+
+
+ access_list_name + +
+ string +
+
+ +
IP Named Extended Access list
+
+
+ access_list_number + +
+ integer +
+
+ +
Access-list number
+
+
+ threshold + +
+ integer +
+
+ +
MDT switching threshold
+
+
+ default + +
+ dictionary +
+
+ +
Default MDT configuration
+
+
+ ingress_replication + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Use Ingress-Replication for the default MDT
+
+
+ direct + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Direct MDT's
+
+
+ log_reuse + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Event logging for data MDT reuse
+
+
+ mode + +
+ dictionary +
+
+ +
The type of encapsulation
+
+
+ gre + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
GRE encapsulation
+
+
+ mtu + +
+ integer +
+
+ +
The MTU
+
+
+ overlay + +
+ dictionary +
+
+ +
MDT overlay Protocol
+
+
+ bgp + +
+ dictionary +
+
+ +
BGP Overlay signalling
+
+
+ shared_tree_prune_delay + +
+ integer +
+
+ +
Delay before shared tree is pruned at C-RP PE
+
+
+ source_tree_prune_delay + +
+ integer +
+
+ +
Delay before source tree is pruned at C-S PE
+
+
+ use_bgp + +
+ dictionary +
+
+ +
Use BGP for MDT overlay signaling
+
+
+ spt_only + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Enable SPT-only ASM mode
+
+
+ partitioned + +
+ dictionary +
+
+ +
Partitioned Multicast Distribution Tree
+
+
+ ingress_replication + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Use Ingress-Replication for the partitioned MDT
+
+
+ strict_rpf + +
+ dictionary +
+
+ +
Enable strict RPF check
+
+
+ interface + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Interface based strict RPF check
+
+
+ protection + +
+ dictionary +
+
+ +
Configure local repair
+
+
+ local_prefixes + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Enable protection for local prefixes
+
+
+ route_replicate + +
+ dictionary +
+
+ +
Replicate (import) routes from another topology (and another VRF)
+
+
+ from_config + +
+ dictionary +
+
+ +
Replicate routes from another VRF
+
+
+ multicast + +
+ dictionary +
+
+ +
Multicast SAFI
+
+
+ all + +
+ dictionary +
+
+ +
All routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ bgp + +
+ dictionary +
+
+ +
Border Gateway Protocol (BGP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ eigrp + +
+ dictionary +
+
+ +
Enhanced Interior Gateway Routing Protocol (EIGRP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ isis + +
+ dictionary +
+
+ +
Intermediate System-to-Intermediate System (ISIS)
+
+
+ iso_tag + +
+ string +
+
+ +
ISO routing area tag
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ mobile + +
+ dictionary +
+
+ +
Mobile routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ odr + +
+ dictionary +
+
+ +
On-Demand Stub routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ ospf + +
+ dictionary +
+
+ +
Open Shortest Path First (OSPF)
+
+
+ process_id + +
+ integer +
+
+ +
OSPF process ID
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ rip + +
+ dictionary +
+
+ +
Routing Information Protocol (RIP)
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ static + +
+ dictionary +
+
+ +
Static routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ topology + +
+ dictionary +
+
+ +
Topology name
+
+
+ base + +
+ dictionary +
+
+ +
Base topology
+
+
+ all + +
+ dictionary +
+
+ +
All routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ bgp + +
+ dictionary +
+
+ +
Border Gateway Protocol (BGP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ eigrp + +
+ dictionary +
+
+ +
Enhanced Interior Gateway Routing Protocol (EIGRP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ isis + +
+ dictionary +
+
+ +
Intermediate System-to-Intermediate System (ISIS)
+
+
+ iso_tag + +
+ string +
+
+ +
ISO routing area tag
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ mobile + +
+ dictionary +
+
+ +
Mobile routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ odr + +
+ dictionary +
+
+ +
On-Demand Stub routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ ospf + +
+ dictionary +
+
+ +
Open Shortest Path First (OSPF)
+
+
+ process_id + +
+ integer +
+
+ +
OSPF process ID
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ rip + +
+ dictionary +
+
+ +
Routing Information Protocol (RIP)
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ static + +
+ dictionary +
+
+ +
Static routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ unicast + +
+ dictionary +
+
+ +
Unicast SAFI
+
+
+ all + +
+ dictionary +
+
+ +
All routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ bgp + +
+ dictionary +
+
+ +
Border Gateway Protocol (BGP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ connected + +
+ dictionary +
+
+ +
Connected routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ eigrp + +
+ dictionary +
+
+ +
Enhanced Interior Gateway Routing Protocol (EIGRP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ isis + +
+ dictionary +
+
+ +
Intermediate System-to-Intermediate System (ISIS)
+
+
+ iso_tag + +
+ string +
+
+ +
ISO routing area tag
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ mobile + +
+ dictionary +
+
+ +
Mobile routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ odr + +
+ dictionary +
+
+ +
On-Demand Stub routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ ospf + +
+ dictionary +
+
+ +
Open Shortest Path First (OSPF)
+
+
+ process_id + +
+ integer +
+
+ +
OSPF process ID
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ rip + +
+ dictionary +
+
+ +
Routing Information Protocol (RIP)
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ static + +
+ dictionary +
+
+ +
Static routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ vrf + +
+ dictionary +
+
+ +
Specify a source VRF
+
+
+ global + +
+ dictionary +
+
+ +
global VRF
+
+
+ multicast + +
+ dictionary +
+
+ +
Multicast SAFI
+
+
+ all + +
+ dictionary +
+
+ +
All routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ bgp + +
+ dictionary +
+
+ +
Border Gateway Protocol (BGP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ eigrp + +
+ dictionary +
+
+ +
Enhanced Interior Gateway Routing Protocol (EIGRP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ isis + +
+ dictionary +
+
+ +
Intermediate System-to-Intermediate System (ISIS)
+
+
+ iso_tag + +
+ string +
+
+ +
ISO routing area tag
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ mobile + +
+ dictionary +
+
+ +
Mobile routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ odr + +
+ dictionary +
+
+ +
On-Demand Stub routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ ospf + +
+ dictionary +
+
+ +
Open Shortest Path First (OSPF)
+
+
+ process_id + +
+ integer +
+
+ +
OSPF process ID
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ rip + +
+ dictionary +
+
+ +
Routing Information Protocol (RIP)
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ static + +
+ dictionary +
+
+ +
Static routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ topology + +
+ dictionary +
+
+ +
Topology name
+
+
+ base + +
+ dictionary +
+
+ +
Base topology
+
+
+ all + +
+ dictionary +
+
+ +
All routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ bgp + +
+ dictionary +
+
+ +
Border Gateway Protocol (BGP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ eigrp + +
+ dictionary +
+
+ +
Enhanced Interior Gateway Routing Protocol (EIGRP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ isis + +
+ dictionary +
+
+ +
Intermediate System-to-Intermediate System (ISIS)
+
+
+ iso_tag + +
+ string +
+
+ +
ISO routing area tag
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ mobile + +
+ dictionary +
+
+ +
Mobile routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ odr + +
+ dictionary +
+
+ +
On-Demand Stub routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ ospf + +
+ dictionary +
+
+ +
Open Shortest Path First (OSPF)
+
+
+ process_id + +
+ integer +
+
+ +
OSPF process ID
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ rip + +
+ dictionary +
+
+ +
Routing Information Protocol (RIP)
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ static + +
+ dictionary +
+
+ +
Static routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ unicast + +
+ dictionary +
+
+ +
Unicast SAFI
+
+
+ all + +
+ dictionary +
+
+ +
All routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ bgp + +
+ dictionary +
+
+ +
Border Gateway Protocol (BGP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ connected + +
+ dictionary +
+
+ +
Connected routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ eigrp + +
+ dictionary +
+
+ +
Enhanced Interior Gateway Routing Protocol (EIGRP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ isis + +
+ dictionary +
+
+ +
Intermediate System-to-Intermediate System (ISIS)
+
+
+ iso_tag + +
+ string +
+
+ +
ISO routing area tag
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ mobile + +
+ dictionary +
+
+ +
Mobile routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ odr + +
+ dictionary +
+
+ +
On-Demand Stub routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ ospf + +
+ dictionary +
+
+ +
Open Shortest Path First (OSPF)
+
+
+ process_id + +
+ integer +
+
+ +
OSPF process ID
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ rip + +
+ dictionary +
+
+ +
Routing Information Protocol (RIP)
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ static + +
+ dictionary +
+
+ +
Static routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ multicast + +
+ dictionary +
+
+ +
Multicast SAFI
+
+
+ all + +
+ dictionary +
+
+ +
All routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ bgp + +
+ dictionary +
+
+ +
Border Gateway Protocol (BGP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ eigrp + +
+ dictionary +
+
+ +
Enhanced Interior Gateway Routing Protocol (EIGRP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ isis + +
+ dictionary +
+
+ +
Intermediate System-to-Intermediate System (ISIS)
+
+
+ iso_tag + +
+ string +
+
+ +
ISO routing area tag
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ mobile + +
+ dictionary +
+
+ +
Mobile routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ odr + +
+ dictionary +
+
+ +
On-Demand Stub routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ ospf + +
+ dictionary +
+
+ +
Open Shortest Path First (OSPF)
+
+
+ process_id + +
+ integer +
+
+ +
OSPF process ID
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ rip + +
+ dictionary +
+
+ +
Routing Information Protocol (RIP)
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ static + +
+ dictionary +
+
+ +
Static routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ topology + +
+ dictionary +
+
+ +
Topology name
+
+
+ base + +
+ dictionary +
+
+ +
Base topology
+
+
+ all + +
+ dictionary +
+
+ +
All routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ bgp + +
+ dictionary +
+
+ +
Border Gateway Protocol (BGP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ eigrp + +
+ dictionary +
+
+ +
Enhanced Interior Gateway Routing Protocol (EIGRP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ isis + +
+ dictionary +
+
+ +
Intermediate System-to-Intermediate System (ISIS)
+
+
+ iso_tag + +
+ string +
+
+ +
ISO routing area tag
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ mobile + +
+ dictionary +
+
+ +
Mobile routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ odr + +
+ dictionary +
+
+ +
On-Demand Stub routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ ospf + +
+ dictionary +
+
+ +
Open Shortest Path First (OSPF)
+
+
+ process_id + +
+ integer +
+
+ +
OSPF process ID
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ rip + +
+ dictionary +
+
+ +
Routing Information Protocol (RIP)
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ static + +
+ dictionary +
+
+ +
Static routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ name + +
+ string +
+
+ +
Source VRF name
+
+
+ unicast + +
+ dictionary +
+
+ +
Unicast SAFI
+
+
+ all + +
+ dictionary +
+
+ +
All routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ bgp + +
+ dictionary +
+
+ +
Border Gateway Protocol (BGP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ connected + +
+ dictionary +
+
+ +
Connected routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ eigrp + +
+ dictionary +
+
+ +
Enhanced Interior Gateway Routing Protocol (EIGRP)
+
+
+ as_number + +
+ integer +
+
+ +
Autonomous System Number
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ isis + +
+ dictionary +
+
+ +
Intermediate System-to-Intermediate System (ISIS)
+
+
+ iso_tag + +
+ string +
+
+ +
ISO routing area tag
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ mobile + +
+ dictionary +
+
+ +
Mobile routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ odr + +
+ dictionary +
+
+ +
On-Demand Stub routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ ospf + +
+ dictionary +
+
+ +
Open Shortest Path First (OSPF)
+
+
+ process_id + +
+ integer +
+
+ +
OSPF process ID
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ rip + +
+ dictionary +
+
+ +
Routing Information Protocol (RIP)
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ static + +
+ dictionary +
+
+ +
Static routes
+
+
+ route_map + +
+ string +
+
+ +
Route-map reference
+
+
+ recursion_policy + +
+ dictionary +
+
+ +
Route replication recursion policy
+
+
+ destination + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Recurse in destination topology
+
+
+ route_target + +
+ dictionary +
+
+ +
Specify Target VPN Extended Communities.
+
+
+ both + +
+ string +
+
+ +
Both export and import Target-VPN community
+
+
+ export + +
+ string +
+
+ +
Export Target-VPN community.
+
+
+ import_config + +
+ string +
+
+ +
Export Target-VPN community.
+
+
+ safi + +
+ string +
+
+
    Choices: +
  • multicast
  • +
  • unicast
  • +
+
+
Address Family modifier
+
+
+ name + +
+ string + / required +
+
+ +
Name of the VRF.
+
+
+ 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 show running-config vrf.
+
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: +
  • parsed
  • +
  • gathered
  • +
  • deleted
  • +
  • merged ←
  • +
  • replaced
  • +
  • rendered
  • +
  • overridden
  • +
+
+
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 vrf. connection to remote host is not required.
+
+
+ + +Notes +----- + +.. note:: + - Tested against Cisco IOSXE version 17.3 on CML. + - This module works with connection ``network_cli``. See https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html + + + +Examples +-------- + +.. code-block:: yaml + + # Using merged + # + # Before state: + # ------------- + # + # RP/0/0/CPU0:ios#show running-config | section ^vrf + # vrf defnition test + # + + - name: Merge provided configuration with device configuration + cisco.ios.ios_vrf_address_family: + config: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 40 + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + allow_evpn: true + map: "test-map" + prefix: 67 + map: "testing-map" + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + allow_evpn: true + limit: 12 + map: "ran-map" + map: "import-map" + state: merged + + # Task Output: + # ------------ + # + # before: [] + # + # commands: + # - vrf definition test1 + # - address-family ipv4 unicast + # - bgp next-hop loopback 40 + # - export map "testing-map" + # - export ipv4 multicast 345 map "single" + # - export ipv4 unicast 67 map "test-map" allow-evpn + # - import map "import-map" + # - import ipv4 multicast 89 map "import-map" + # - import ipv4 unicast 12 map "ran-map" allow-evpn + # + # after: + # - name: test1 + # address_families: + # - afi: "ipv4" + # safi: "unicast" + # bgp: + # next_hop: + # loopback: 40 + # export: + # ipv4: + # multicast: + # map: "single" + # prefix: 345 + # unicast: + # allow_evpn: true + # map: "test-map" + # prefix: 67 + # map: "testing-map" + # import_config: + # ipv4: + # multicast: + # map: "import-map" + # prefix: 89 + # unicast: + # allow_evpn: true + # limit: 12 + # map: "ran-map" + # map: "import-map" + # + # After state: + # ------------ + # + # RP/0/0/CPU0:ios#show running-config | section ^vrf + # vrf definition test1 + # address-family ipv4 unicast + # bgp next-hop loopback 40 + # export map "testing-map" + # export ipv4 multicast 345 map "single" + # export ipv4 unicast 67 map "test-map" allow-evpn + # import map "import-map" + # import ipv4 multicast 89 map "import-map" + # import ipv4 unicast 12 map "ran-map" allow-evpn + + # Using replaced + # + # Before state: + # ------------- + # + # RP/0/0/CPU0:ios#show running-config | section ^vrf + # vrf definition test1 + # address-family ipv4 unicast + # bgp next-hop loopback 40 + # export map "testing-map" + # export ipv4 multicast 345 map "single" + # export ipv4 unicast 67 map "test-map" allow-evpn + # import map "import-map" + # import ipv4 multicast 89 map "import-map" + # import ipv4 unicast 12 map "ran-map" allow-evpn + + - name: Replace the provided configuration with the existing running configuration + cisco.ios.ios_vrf_address_family: + config: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 40 + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + allow_evpn: true + map: "test-map" + prefix: 67 + map: "testing-map" + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + allow_evpn: true + limit: 12 + map: "ran-map" + map: "import-map" + state: replaced + + # Task Output: + # ------------ + # + # before: + # - name: test1 + # address_families: + # - afi: "ipv4" + # safi: "unicast" + # bgp: + # next_hop: + # loopback: 40 + # export: + # ipv4: + # multicast: + # map: "single" + # prefix: 345 + # unicast: + # allow_evpn: true + # map: "test-map" + # prefix: 67 + # map: "testing-map" + # import_config: + # ipv4: + # multicast: + # map: "import-map" + # prefix: 89 + # unicast: + # allow_evpn: true + # limit: 12 + # map: "ran-map" + # map: "import-map" + # + # commands: + # - vrf definition test1 + # - address-family ipv4 unicast + # - bgp next-hop loopback 40 + # - export map "testing-map" + # - export ipv4 multicast 345 map "single" + # - export ipv4 unicast 67 map "test-map" allow-evpn + # - import map "import-map" + # - import ipv4 multicast 89 map "import-map" + # - import ipv4 unicast 12 map "ran-map" allow-evpn + # + # after: + # - name: VRF1 + # address_families: + # - afi: "ipv4" + # safi: "unicast" + # bgp: + # next_hop: + # loopback: 23 + # export: + # ipv4: + # multicast: + # map: "single" + # prefix: 33 + # unicast: + # allow_evpn: true + # map: "test-map1" + # prefix: 7 + # map: "testing-map" + # import_config: + # ipv4: + # multicast: + # map: "import-map1" + # prefix: 89 + # unicast: + # allow_evpn: true + # limit: 12 + # map: "ran-map" + # map: "import-map" + # + # After state: + # ------------ + # + # RP/0/RP0/CPU0:ios(config)#show running-config vrf + # vrf definition VRF1 + # address-family ipv4 unicast + # bgp next-hop loopback 23 + # export map "testing-map" + # export ipv4 multicast 345 map "single" + # export ipv4 unicast 67 map "test-map1" allow-evpn + # import map "import-map" + # import ipv4 multicast 89 map "import-map1" + # import ipv4 unicast 12 map "ran-map" allow-evpn + + # Using overridden + # + # Before state: + # ------------- + # + # RP/0/RP0/CPU0:ios(config)#show running-config | section ^vrf + # vrf definition test1 + # address-family ipv4 unicast + # bgp next-hop loopback 40 + # export map "testing-map" + # export ipv4 multicast 345 map "single" + # export ipv4 unicast 67 map "test-map" allow-evpn + # import map "import-map" + # import ipv4 multicast 89 map "import-map" + # import ipv4 unicast 12 map "ran-map" allow-evpn + + - name: Override the provided configuration with the existing running configuration + cisco.ios.ios_vrf_address_family: + state: overridden + config: + - name: VRF7 + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 89 + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + allow_evpn: true + map: "test-map" + prefix: 67 + map: "testing-map" + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + allow_evpn: true + limit: 12 + map: "ran-map" + map: "import-map" + # Task Output: + # ------------ + # + # before: + # - name: test1 + # address_families: + # - afi: "ipv4" + # safi: "unicast" + # bgp: + # next_hop: + # loopback: 40 + # export: + # ipv4: + # multicast: + # map: "single" + # prefix: 345 + # unicast: + # allow_evpn: true + # map: "test-map" + # prefix: 67 + # map: "testing-map" + # import_config: + # ipv4: + # multicast: + # map: "import-map" + # prefix: 89 + # unicast: + # allow_evpn: true + # limit: 12 + # map: "ran-map" + # map: "import-map" + # + # commands: + # - vrf definition VRF7 + # - address-family ipv4 unicast + # - bgp next-hop loopback 89 + # - export map "testing-map" + # - export ipv4 multicast 345 map "single" + # - export ipv4 unicast 67 map "test-map" allow-evpn + # - import map "import-map" + # - import ipv4 multicast 89 map "import-map" + # - import ipv4 unicast 12 map "ran-map" allow-evpn + + # + # After state: + # ------------- + # RP/0/RP0/CPU0:ios(config)#show running-config vrf + # vrf definition VRF4 + # vrf definition VRF7 + # address-family ipv4 unicast + # bgp next-hop loopback 89 + # export map "testing-map" + # export ipv4 multicast 345 map "single" + # export ipv4 unicast 67 map "test-map" allow-evpn + # import map "import-map" + # import ipv4 multicast 89 map "import-map1" + # import ipv4 unicast 12 map "ran-map" allow-evpn + + # Using deleted + # + # Before state: + # ------------- + # + # RP/0/RP0/CPU0:ios(config)#show running-config | section ^vrf + # vrf definition VRF4 + # vrf definition VRF6 + # address-family ipv4 unicast + # bgp next-hop loopback 40 + # import map "import-map" + # import ipv4 multicast 89 map "import-map" + # import ipv4 unicast 12 map "ran-map" allow-evpn + # export map "testing-map" + # export ipv4 multicast 345 map "single" + # export ipv4 unicast 67 map "test-map" allow-evpn + # vrf definition VRF7 + + - name: Delete the provided configuration + cisco.ios.ios_vrf_address_family: + config: + state: deleted + + # Task Output: + # ------------ + # + # before: + # - name: VRF4 + # - name: VRF6 + # address_families: + # - afi: "ipv4" + # safi: "unicast" + # bgp: + # next_hop: + # loopback: 23 + # import_config: + # ipv4: + # multicast: + # map: "import-map" + # prefix: 89 + # unicast: + # map: "ran-map" + # limit: 12 + # allow_evpn: true + # map: "import-map" + # export: + # ipv4: + # multicast: + # map: "single" + # prefix: 345 + # unicast: + # map: "test-map" + # prefix: 67 + # allow_evpn: true + # map: "testing-map" + # - name: VRF7 + + # commands: + # - vrf definition VRF4 + # - vrf definition VRF6 + # - no address-family ipv4 unicast + # - vrf definition VRF7 + # + # after: + # - name: VRF4 + # - name: VRF6 + # - name: VRF7 + # + # After state: + # ------------ + # + # RP/0/RP0/CPU0:ios(config)#show running-config | section ^vrf + # vrf definition VRF4 + # vrf definition VRF6 + # vrf definition VRF7 + + # Using rendered + # + - name: Render provided configuration with device configuration + cisco.ios.ios_vrf_address_family: + config: + - name: test + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 23 + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + map: "ran-map" + limit: 12 + allow_evpn: true + map: "import-map" + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + map: "test-map" + prefix: 67 + allow_evpn: true + map: "testing-map" + state: rendered + + # Task Output: + # ------------ + # + # rendered: + # - vrf definition test + # - address-family ipv4 unicast + # - bgp next-hop loopback 23 + # - import map "import-map" + # - import ipv4 multicast 89 map "import-map" + # - import ipv4 unicast 12 map "ran-map" allow-evpn + # - export map "testing-map" + # - export ipv4 multicast 345 map "single" + # - export ipv4 unicast 67 map "test-map" allow-evpn + + # Using gathered + # + # Before state: + # ------------- + # + # RP/0/RP0/CPU0:ios(config)#show running-config | section ^vrf + # vrf definition test1 + # address-family ipv4 unicast + # bgp next-hop loopback 40 + # import map "import-map" + # import ipv4 multicast 89 map "import-map" + # import ipv4 unicast 12 map "ran-map" allow-evpn + # export map "testing-map" + # export ipv4 multicast 345 map "single" + # export ipv4 unicast 67 map "test-map" allow-evpn + + - name: Gather existing running configuration + cisco.ios.ios_vrf_address_family: + state: gathered + + # Task Output: + # ------------ + # + # gathered: + # - name: test1 + # address_families: + # - afi: "ipv4" + # safi: "unicast" + # bgp: + # next_hop: + # loopback: 40 + # export: + # ipv4: + # multicast: + # map: "single" + # prefix: 345 + # unicast: + # allow_evpn: true + # map: "test-map" + # prefix: 67 + # map: "testing-map" + # import_config: + # ipv4: + # multicast: + # map: "import-map" + # prefix: 89 + # unicast: + # allow_evpn: true + # limit: 12 + # map: "ran-map" + # map: "import-map" + + # Using parsed + # + # File: parsed.cfg + # ---------------- + # + # vrf definition test + # address-family ipv4 unicast + # bgp next-hop loopback 23 + # import map "import-map" + # import ipv4 multicast 89 map "import-map" + # import ipv4 unicast 12 map "ran-map" allow-evpn + # export map "testing-map" + # export ipv4 multicast 345 map "single" + # export ipv4 unicast 67 map "test-map" allow-evpn + + - name: Parse the provided configuration + cisco.ios.ios_vrf_address_family: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + + # Task Output: + # ------------ + # + # parsed: + # - address_families: + # - afi: ipv4 + # bgp: + # next_hop: + # loopback: 40 + # export: + # ipv4: + # multicast: + # map: "single" + # prefix: 345 + # unicast: + # allow_evpn: true + # map: "test-map" + # prefix: 67 + # map: "testing-map" + # import_config: + # ipv4: + # multicast: + # map: "import-map" + # prefix: 89 + # unicast: + # allow_evpn: true + # limit: 12 + # map: "ran-map" + # safi: unicast + # name: test + + + +Return Values +------------- +Common return values are documented `here `_, the following are the fields unique to this module: + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyReturnedDescription
+
+ after + +
+ list +
+
when changed +
The resulting configuration after module execution.
+
+
Sample:
+
This output will always be in the same format as the module argspec.
+
+
+ before + +
+ list +
+
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 +
The set of commands pushed to the remote device.
+
+
Sample:
+
['vrf definition test1', 'address-family ipv4 unicast', 'bgp next-hop loopback 40', 'export map "testing-map"', 'export ipv4 multicast 345 map "testmap"']
+
+
+ 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:
+
['vrf definition test1', 'address-family ipv4 unicast', 'bgp next-hop loopback 40', 'import map "testing-map"', 'export ipv4 multicast 345 map "testmap"']
+
+

+ + +Status +------ + + +Authors +~~~~~~~ + +- Ruchi Pakhle (@Ruchip16) diff --git a/meta/runtime.yml b/meta/runtime.yml index 3219ce3c6..3ccab7553 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -67,6 +67,8 @@ plugin_routing: redirect: cisco.ios.vrf_interfaces vxlan_vtep: redirect: cisco.ios.ios_vxlan_vtep + vrf_address_family: + redirect: cisco.ios.ios_vrf_address_family vrf_global: redirect: cisco.ios.ios_vrf_global requires_ansible: ">=2.15.0" diff --git a/plugins/action/vrf_address_family.py b/plugins/action/vrf_address_family.py new file mode 120000 index 000000000..7747aa9dd --- /dev/null +++ b/plugins/action/vrf_address_family.py @@ -0,0 +1 @@ +ios.py \ No newline at end of file diff --git a/plugins/module_utils/network/ios/argspec/vrf_address_family/__init__.py b/plugins/module_utils/network/ios/argspec/vrf_address_family/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/argspec/vrf_address_family/vrf_address_family.py b/plugins/module_utils/network/ios/argspec/vrf_address_family/vrf_address_family.py new file mode 100644 index 000000000..aed0b63e9 --- /dev/null +++ b/plugins/module_utils/network/ios/argspec/vrf_address_family/vrf_address_family.py @@ -0,0 +1,1153 @@ +# -*- 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_address_family module +""" + + +class Vrf_address_familyArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_vrf_address_family module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "address_families": { + "type": "list", + "elements": "dict", + "options": { + "afi": {"type": "str", "choices": ["ipv4", "ipv6"]}, + "safi": { + "type": "str", + "choices": ["multicast", "unicast"], + }, + "bgp": { + "type": "dict", + "options": { + "next_hop": { + "type": "dict", + "options": {"loopback": {"type": "int"}}, + }, + }, + }, + "export": { + "type": "dict", + "options": { + "map": {"type": "str"}, + "ipv4": { + "type": "dict", + "options": { + "multicast": { + "type": "dict", + "options": { + "prefix": {"type": "int"}, + "map": {"type": "str"}, + }, + }, + "unicast": { + "type": "dict", + "options": { + "prefix": {"type": "int"}, + "map": {"type": "str"}, + "allow_evpn": {"type": "bool"}, + }, + }, + }, + }, + }, + }, + "import_config": { + "type": "dict", + "options": { + "map": {"type": "str"}, + "ipv4": { + "type": "dict", + "options": { + "multicast": { + "type": "dict", + "options": { + "prefix": {"type": "int"}, + "map": {"type": "str"}, + }, + }, + "unicast": { + "type": "dict", + "options": { + "limit": {"type": "int"}, + "map": {"type": "str"}, + "allow_evpn": {"type": "bool"}, + }, + }, + }, + }, + }, + }, + "inter_as_hybrid": { + "type": "dict", + "options": { + "csc": { + "type": "dict", + "options": {"next_hop": {"type": "str"}}, + }, + "next_hop": {"type": "str"}, + }, + }, + "mdt": { + "type": "dict", + "options": { + "auto_discovery": { + "type": "dict", + "options": { + "ingress_replication": { + "type": "dict", + "options": { + "inter_as": { + "type": "dict", + "options": { + "mdt_hello_enable": { + "type": "bool", + }, + }, + }, + "mdt_hello_enable": { + "type": "bool", + }, + }, + }, + "pim": { + "type": "dict", + "options": { + "inter_as": { + "type": "dict", + "options": { + "mdt_hello_enable": { + "type": "bool", + }, + "pim_tlv_announce": { + "type": "dict", + "options": { + "mdt_hello_enable": { + "type": "bool", + }, + }, + }, + }, + }, + "mdt_hello_enable": { + "type": "bool", + }, + "pim_tlv_announce": { + "type": "dict", + "options": { + "mdt_hello_enable": { + "type": "bool", + }, + }, + }, + }, + }, + "receiver_site": {"type": "bool"}, + }, + }, + "data": { + "type": "dict", + "options": { + "ingress_replication": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "immediate_switch": { + "type": "bool", + }, + "list": { + "type": "dict", + "options": { + "access_list_number": { + "type": "int", + }, + "access_list_name": { + "type": "str", + }, + }, + }, + }, + }, + "list": { + "type": "dict", + "options": { + "access_list_number": { + "type": "int", + }, + "access_list_name": { + "type": "str", + }, + }, + }, + "threshold": {"type": "int"}, + }, + }, + "default": { + "type": "dict", + "options": { + "ingress_replication": {"type": "bool"}, + }, + }, + "direct": {"type": "bool"}, + "log_reuse": {"type": "bool"}, + "mode": { + "type": "dict", + "options": {"gre": {"type": "bool"}}, + }, + "mtu": {"type": "int"}, + "overlay": { + "type": "dict", + "options": { + "bgp": { + "type": "dict", + "options": { + "shared_tree_prune_delay": { + "type": "int", + }, + "source_tree_prune_delay": { + "type": "int", + }, + }, + }, + "use_bgp": { + "type": "dict", + "options": { + "spt_only": {"type": "bool"}, + }, + }, + }, + }, + "partitioned": { + "type": "dict", + "options": { + "ingress_replication": {"type": "bool"}, + }, + }, + "strict_rpf": { + "type": "dict", + "options": {"interface": {"type": "bool"}}, + }, + }, + }, + "protection": { + "type": "dict", + "options": {"local_prefixes": {"type": "bool"}}, + }, + "route_replicate": { + "type": "dict", + "options": { + "recursion_policy": { + "type": "dict", + "options": {"destination": {"type": "bool"}}, + }, + "from_config": { + "type": "dict", + "options": { + "multicast": { + "type": "dict", + "options": { + "all": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "isis": { + "type": "dict", + "options": { + "iso_tag": {"type": "str"}, + "route_map": { + "type": "str", + }, + }, + }, + "mobile": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "odr": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "rip": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "static": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "topology": { + "type": "dict", + "options": { + "base": { + "type": "dict", + "options": { + "all": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "isis": { + "type": "dict", + "options": { + "iso_tag": { + "type": "str", + }, + "route_map": { + "type": "str", + }, + }, + }, + "mobile": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "odr": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "rip": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "static": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + }, + }, + }, + }, + }, + }, + "unicast": { + "type": "dict", + "options": { + "all": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "connected": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "isis": { + "type": "dict", + "options": { + "iso_tag": {"type": "str"}, + "route_map": { + "type": "str", + }, + }, + }, + "mobile": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "odr": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "rip": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "static": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + }, + }, + "vrf": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "multicast": { + "type": "dict", + "options": { + "all": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "isis": { + "type": "dict", + "options": { + "iso_tag": { + "type": "str", + }, + "route_map": { + "type": "str", + }, + }, + }, + "mobile": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "odr": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "rip": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "static": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "topology": { + "type": "dict", + "options": { + "base": { + "type": "dict", + "options": { + "all": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "isis": { + "type": "dict", + "options": { + "iso_tag": { + "type": "str", + }, + "route_map": { + "type": "str", + }, + }, + }, + "mobile": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "odr": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "rip": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "static": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + }, + }, + }, + }, + }, + }, + "unicast": { + "type": "dict", + "options": { + "all": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "connected": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "isis": { + "type": "dict", + "options": { + "iso_tag": { + "type": "str", + }, + "route_map": { + "type": "str", + }, + }, + }, + "mobile": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "odr": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "rip": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "static": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + }, + }, + "global": { + "type": "dict", + "options": { + "multicast": { + "type": "dict", + "options": { + "all": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "isis": { + "type": "dict", + "options": { + "iso_tag": { + "type": "str", + }, + "route_map": { + "type": "str", + }, + }, + }, + "mobile": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "odr": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "rip": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "static": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "topology": { + "type": "dict", + "options": { + "base": { + "type": "dict", + "options": { + "all": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "isis": { + "type": "dict", + "options": { + "iso_tag": { + "type": "str", + }, + "route_map": { + "type": "str", + }, + }, + }, + "mobile": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "odr": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "rip": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "static": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + }, + }, + }, + }, + }, + }, + "unicast": { + "type": "dict", + "options": { + "all": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "connected": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "isis": { + "type": "dict", + "options": { + "iso_tag": { + "type": "str", + }, + "route_map": { + "type": "str", + }, + }, + }, + "mobile": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "odr": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": { + "type": "int", + }, + "route_map": { + "type": "str", + }, + }, + }, + "rip": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + "static": { + "type": "dict", + "options": { + "route_map": { + "type": "str", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "route_target": { + "type": "dict", + "options": { + "export": {"type": "str"}, + "import_config": {"type": "str"}, + "both": {"type": "str"}, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "parsed", + "gathered", + "deleted", + "merged", + "replaced", + "rendered", + "overridden", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/ios/config/vlans/vlans.py b/plugins/module_utils/network/ios/config/vlans/vlans.py index d195c317e..a438d3f8e 100644 --- a/plugins/module_utils/network/ios/config/vlans/vlans.py +++ b/plugins/module_utils/network/ios/config/vlans/vlans.py @@ -114,10 +114,12 @@ def generate_commands(self, conf_want, conf_have, resource=None): 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": + # if state is overridden, remove excluded vlans + if self.state == "overridden": + excluded_vlans = {k: v for k, v in iteritems(haved) if k not in wantd or not wantd} haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} - wantd = {} + for k, have in iteritems(excluded_vlans): + self.purge(have, resource) # if state is deleted, empty out wantd and set haved to wantd if self.state in ["deleted", "purged"]: @@ -165,3 +167,5 @@ def purge(self, have, resource): """Handle operation for purged state""" if resource == "vlan_configuration": self.commands.append(self._tmplt.render(have, resource, True)) + elif resource == "vlans": + self.commands.append(self._tmplt.render(have, resource, True)) diff --git a/plugins/module_utils/network/ios/config/vrf_address_family/__init__.py b/plugins/module_utils/network/ios/config/vrf_address_family/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/config/vrf_address_family/vrf_address_family.py b/plugins/module_utils/network/ios/config/vrf_address_family/vrf_address_family.py new file mode 100644 index 000000000..9f061a760 --- /dev/null +++ b/plugins/module_utils/network/ios/config/vrf_address_family/vrf_address_family.py @@ -0,0 +1,217 @@ +# +# -*- 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_address_family 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.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vrf_address_family import ( + Vrf_address_familyTemplate, +) + + +class Vrf_address_family(ResourceModule): + """ + The ios_vrf_address_family config class + """ + + def __init__(self, module): + super(Vrf_address_family, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="vrf_address_family", + tmplt=Vrf_address_familyTemplate(), + ) + self.parsers = [ + "address_family", + "bgp.next_hop.loopback", + "export.map", + "import_config.map", + "export.ipv4.multicast", + "export.ipv4.unicast.allow_evpn", + "import_config.ipv4.multicast", + "import_config.ipv4.unicast", + "inter_as_hybrid.csc.next_hop", + "inter_as_hybrid.next_hop", + "mdt.auto_discovery.ingress_replication.inter_as.mdt_hello_enable", + "mdt.auto_discovery.pim.inter_as.mdt_hello_enable", + "mdt.auto_discovery.pim.inter_as.pim_tlv_announce.mdt_hello_enable", + "mdt.auto_discovery.ingress_replication.mdt_hello_enable", + "mdt.auto_discovery.pim.mdt_hello_enable", + "mdt.auto_discovery.pim.pim_tlv_announce.mdt_hello_enable", + "mdt.auto_discovery.receiver_site", + "mdt.data.ingress_replication.number", + "mdt.data.ingress_replication.immediate_switch", + "mdt.data.ingress_replication.number.immediate_switch", + "mdt.data.list.access_list", + "mdt.data.list.access_list_name", + "mdt.data.threshold", + "mdt.default_ingress_replication", + "mdt.direct", + "mdt.log_reuse", + "mdt.mode.gre", + "mdt.mtu.value", + "mdt.overlay.bgp.shared_tree_prune_delay", + "mdt.overlay.bgp.source_tree_prune_delay", + "mdt.overlay.use_bgp_spt_only", + "mdt.partitioned.ingress_replication", + "mdt.strict_rpf_interface", + "protection.local_prefixes", + "route_replicate.recursion_policy.destination", + "route_replicate.from.unicast.all.route_map", + "route_replicate.from.unicast.bgp.asn.route_map", + "route_replicate.from.unicast.connected.route_map", + "route_replicate.from.unicast.eigrp.asn.route_map", + "route_replicate.from.unicast.isis.route_map", + "route_replicate.from.unicast.mobile.route_map", + "route_replicate.from.unicast.odr.route_map", + "route_replicate.from.unicast.ospf.id.route_map", + "route_replicate.from.unicast.rip.route_map", + "route_replicate.from.unicast.static.route_map", + "route_replicate.from.vrf.vrf_name.unicast.all.route_map", + "route_replicate.from.vrf.vrf_name.unicast.bgp.asn.route_map", + "route_replicate.from.vrf.vrf_name.unicast.connected.route_map", + "route_replicate.from.vrf.vrf_name.unicast.eigrp.asn.route_map", + "route_replicate.from.vrf.vrf_name.unicast.isis.route_map", + "route_replicate.from.vrf.vrf_name.unicast.mobile.route_map", + "route_replicate.from.vrf.vrf_name.unicast.odr.route_map", + "route_replicate.from.vrf.vrf_name.unicast.ospf.id.route_map", + "route_replicate.from.vrf.vrf_name.unicast.rip.route_map", + "route_replicate.from.vrf.vrf_name.unicast.static.route_map", + "route_target.export", + "route_target.import_config", + ] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = self.want + haved = self.have + + wantd = self._vrf_list_to_dict(wantd) + haved = self._vrf_list_to_dict(haved) + + # 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": + for vrfk, vrfv in iteritems(haved): + for afk, afv in iteritems(vrfv.get("address_families", {})): + adrf = wantd.get(vrfk, {}).get("address_families", {}) + if afk in adrf or not adrf: + self.addcmd( + {"name": vrfk}, + "name", + False, + ) + self.addcmd( + {"afi": afv.get("afi"), "safi": afv.get("safi")}, + "address_family", + True, + ) + + if self.state in ["overridden"]: + for vrfk, vrfv in iteritems(haved): + for k, have in iteritems(vrfv.get("address_families", {})): + wantx = wantd.get(vrfk, {}).get("address_families", {}) + if k not in wantx: + self.addcmd( + {"name": vrfk}, + "name", + False, + ) + self.addcmd( + {"afi": have.get("afi"), "safi": have.get("safi")}, + "address_family", + False, + ) + self.compare(parsers=self.parsers, want={}, have=have) + + if self.state != "deleted": + self._compare(want=wantd, have=haved) + + 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 network resource. + """ + for name, entry in iteritems(want): + begin = len(self.commands) + vrf_want = entry + vrf_have = have.pop(name, {}) + self._compare_afs(vrf_want, vrf_have) + if len(self.commands) != begin: + self.commands.insert(begin, "vrf definition {0}".format(name)) + + def _compare_afs(self, want, have): + """Custom handling of afs option + :params want: the want VRF dictionary + :params have: the have VRF dictionary + """ + waafs = want.get("address_families", {}) + haafs = have.get("address_families", {}) + for afk, afv in iteritems(waafs): + begin = len(self.commands) + self._compare_single_af(want=afv, have=haafs.get(afk, {})) + if len(self.commands) != begin: + self.commands.insert(begin, f"address-family {afv.get('afi')} {afv.get('safi')}") + + def _compare_single_af(self, want, have): + """Custom handling of single af option + :params want: the want VRF dictionary + :params have: the have VRF dictionary + """ + self.compare(parsers=self.parsers[1:], want=want, have=have) + + def _vrf_list_to_dict(self, entry): + """Convert list of items to dict of items + for efficient diff calculation. + :params entry: data dictionary + """ + + for vrf in entry: + if "address_families" in vrf: + vrf["address_families"] = { + (x["afi"], x.get("safi")): x for x in vrf["address_families"] + } + + entry = {x["name"]: x for x in entry} + return entry diff --git a/plugins/module_utils/network/ios/facts/facts.py b/plugins/module_utils/network/ios/facts/facts.py index 35ca7a9aa..57c03e639 100644 --- a/plugins/module_utils/network/ios/facts/facts.py +++ b/plugins/module_utils/network/ios/facts/facts.py @@ -99,6 +99,9 @@ from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vlans.vlans import ( VlansFacts, ) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_address_family.vrf_address_family import ( + Vrf_address_familyFacts, +) from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_global.vrf_global import ( Vrf_globalFacts, ) @@ -145,6 +148,7 @@ vxlan_vtep=Vxlan_vtepFacts, evpn_global=Evpn_globalFacts, evpn_evi=Evpn_eviFacts, + vrf_address_family=Vrf_address_familyFacts, vrf_global=Vrf_globalFacts, vrf_interfaces=Vrf_interfacesFacts, ) diff --git a/plugins/module_utils/network/ios/facts/legacy/base.py b/plugins/module_utils/network/ios/facts/legacy/base.py index 5344ca627..6788821a1 100644 --- a/plugins/module_utils/network/ios/facts/legacy/base.py +++ b/plugins/module_utils/network/ios/facts/legacy/base.py @@ -285,7 +285,12 @@ def populate_interfaces(self, interfaces): def populate_ipv4_interfaces(self, data): for key, value in data.items(): - self.facts["interfaces"][key]["ipv4"] = list() + try: + self.facts["interfaces"][key]["ipv4"] = list() + except KeyError: + self.facts["interfaces"][key] = dict() + self.facts["interfaces"][key]["ipv4"] = list() + self.parse_deleted_status(key, value) primary_address = addresses = [] primary_address = re.findall(r"Internet address is (.+)$", value, re.M) addresses = re.findall(r"Secondary address (.+)$", value, re.M) @@ -305,6 +310,7 @@ def populate_ipv6_interfaces(self, data): except KeyError: self.facts["interfaces"][key] = dict() self.facts["interfaces"][key]["ipv6"] = list() + self.parse_deleted_status(key, value) addresses = re.findall(r"\s+(.+), subnet", value, re.M) subnets = re.findall(r", subnet is (.+)$", value, re.M) for addr, subnet in zip(addresses, subnets): @@ -370,6 +376,11 @@ def parse_interfaces(self, data): parsed[key] = line return parsed + def parse_deleted_status(self, interface, value): + status = self.parse_operstatus(value) + if status == "deleted": + self.facts["interfaces"][interface]["operstatus"] = status + def parse_description(self, data): match = re.search(r"Description: (.+)$", data, re.M) if match: diff --git a/plugins/module_utils/network/ios/facts/vrf_address_family/__init__.py b/plugins/module_utils/network/ios/facts/vrf_address_family/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/facts/vrf_address_family/vrf_address_family.py b/plugins/module_utils/network/ios/facts/vrf_address_family/vrf_address_family.py new file mode 100644 index 000000000..ca24fa4be --- /dev/null +++ b/plugins/module_utils/network/ios/facts/vrf_address_family/vrf_address_family.py @@ -0,0 +1,112 @@ +# -*- 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_address_family 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.ios.plugins.module_utils.network.ios.argspec.vrf_address_family.vrf_address_family import ( + Vrf_address_familyArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vrf_address_family import ( + Vrf_address_familyTemplate, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + flatten_config, +) + + +class Vrf_address_familyFacts(object): + """The ios vrf_address_family facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Vrf_address_familyArgs.argument_spec + + def get_config(self, connection): + """Get the configuration from the device""" + + return connection.get("show running-config | section ^vrf") + + def _flatten_config(self, config): + dataLines = config.split("\n") + finalConfig = [] + + for line in dataLines: + if "address-family" in line and "exit-address-family" not in line: + finalConfig.append(line) + + return "\n".join(finalConfig) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Vrf_address_family network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + obj = {} + + if not data: + data = self.get_config(connection) + + address_data = flatten_config(data, "address-family") + data = flatten_config(address_data, "vrf") + finalConfig = self._flatten_config(data) + + # parse native config using the Vrf_address_family template + vrf_address_family_parser = Vrf_address_familyTemplate( + lines=finalConfig.splitlines(), + module=self._module, + ) + obj = vrf_address_family_parser.parse() + objs = list(obj.values()) + + for vrf in objs: + af = vrf.get("address_families", {}) + if af: + self._post_parse(vrf) + else: + vrf["address_families"] = [] + + ansible_facts["ansible_network_resources"].pop("vrf_address_family", None) + + params = utils.remove_empties( + vrf_address_family_parser.validate_config( + self.argument_spec, + {"config": objs}, + redact=True, + ), + ) + + facts["vrf_address_family"] = params.get("config", []) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def _post_parse(self, af_data): + """Converts the intermediate data structure + to valid format as per argspec. + :param obj: dict + """ + af = af_data.get("address_families", {}) + if af: + af_data["address_families"] = list(af.values()) diff --git a/plugins/module_utils/network/ios/rm_templates/vrf_address_family.py b/plugins/module_utils/network/ios/rm_templates/vrf_address_family.py new file mode 100644 index 000000000..1dd3dfe7e --- /dev/null +++ b/plugins/module_utils/network/ios/rm_templates/vrf_address_family.py @@ -0,0 +1,2217 @@ +# -*- 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_address_family 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_address_familyTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Vrf_address_familyTemplate, self).__init__( + lines=lines, + tmplt=self, + module=module, + ) + + # fmt: off + PARSERS = [ + { + "name": "address_family", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + $""", re.VERBOSE, + ), + "setval": "address-family {{ afi }} {{ safi }}", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + }, + }, + }, + }, + "shared": True, + }, + { + "name": "export.map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+export\smap\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "export map {{ export.map }}", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "export": { + "map": "{{ export_map }}", + }, + }, + }, + }, + }, + }, + { + "name": "import_config.map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+import\smap\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "import map {{ import_config.map }}", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "import_config": { + "map": "{{ import_config_map }}", + }, + }, + }, + }, + }, + }, + { + "name": "export.ipv4.multicast", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+export\sipv4\smulticast\s(?P\d+)\smap\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "export ipv4 multicast {{ export.ipv4.multicast.prefix }} " + "map {{ export.ipv4.multicast.prefix.map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "export": { + "ipv4": { + "multicast": { + "prefix": "{{ prefix }}", + "map": "{{ export_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "export.ipv4.unicast.allow_evpn", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+export\sipv4\sunicast\s(?P\d+)\smap\s(?P\S+)\s(?Pallow-evpn) + $""", re.VERBOSE, + ), + "setval": ( + "export ipv4 unicast {{ export.ipv4.unicast.prefix }} map " + "{{ export.ipv4.unicast.map.export_map }} " + "allow-evpn" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "export": { + "ipv4": { + "unicast": { + "prefix": "{{ prefix }}", + "map": "{{ export_map }}", + "allow_evpn": ( + "{{true if allow_evpn is defined}}" + ), + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "import_config.ipv4.multicast", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+import\sipv4\smulticast\s(?P\d+)\smap\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "import ipv4 multicast {{ prefix }} map {{ import_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "import_config": { + "ipv4": { + "multicast": { + "prefix": "{{ prefix }}", + "map": "{{ import_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "import_config.ipv4.unicast", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+import\sipv4\sunicast\s(?P\d+)\smap\s(?P\S+)\s(?Pallow-evpn) + $""", re.VERBOSE, + ), + "setval": ( + "import ipv4 unicast {{ limit }} map {{ import_map }} " + "allow-evpn" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "import_config": { + "ipv4": { + "unicast": { + "limit": "{{ limit }}", + "map": "{{ import_map }}", + "allow_evpn": ( + "{{true if allow_evpn is defined}}" + ), + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "bgp.next_hop.loopback", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+bgp\snext-hop\sLoopback(?P\d+) + $""", re.VERBOSE, + ), + "setval": "bgp next-hop Loopback {{ loopback }}", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "bgp": { + "next_hop": { + "loopback": "{{ loopback }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "inter_as_hybrid.csc.next_hop", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+inter-as-hybrid\scsc\snext-hop\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "inter-as-hybrid csc next-hop " + "{{ inter_as_hybrid.csc.next_hop }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "inter_as_hybrid": { + "csc": { + "next_hop": ( + "{{ inter_as_hybrid_csc_next_hop }}" + ), + }, + }, + }, + }, + }, + }, + }, + { + "name": "inter_as_hybrid.next_hop", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+inter-as-hybrid\snext-hop\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "inter-as-hybrid next-hop {{ inter_as_hybrid.next_hop }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "inter_as_hybrid": { + "next_hop": "{{ inter_as_hybrid_next_hop }}", + }, + }, + }, + }, + }, + }, + { + "name": ( + "mdt.auto_discovery.ingress_replication.inter_as." + "mdt_hello_enable" + ), + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sauto-discovery\singress-replication\sinter-as\smdt-hello-enable + $""", re.VERBOSE, + ), + "setval": ( + "mdt auto-discovery ingress-replication inter-as " + "mdt-hello-enable" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "auto_discovery": { + "ingress_replication": { + "inter_as": { + "mdt_hello_enable": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.auto_discovery.pim.inter_as.mdt_hello_enable", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sauto-discovery\spim\sinter-as\smdt-hello-enable + $""", re.VERBOSE, + ), + "setval": "mdt auto-discovery pim inter-as mdt-hello-enable", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "auto_discovery": { + "pim": { + "inter_as": { + "mdt_hello_enable": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": ( + "mdt.auto_discovery.pim.inter_as.pim_tlv_announce." + "mdt_hello_enable" + ), + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sauto-discovery\spim\sinter-as\spim-tlv-announce\smdt-hello-enable + $""", re.VERBOSE, + ), + "setval": ( + "mdt auto-discovery pim inter-as pim-tlv-announce " + "mdt-hello-enable" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "auto_discovery": { + "pim": { + "inter_as": { + "pim_tlv_announce": { + "mdt_hello_enable": "{{true}}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.auto_discovery.ingress_replication.mdt_hello_enable", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sauto-discovery\singress-replication\smdt-hello-enable + $""", re.VERBOSE, + ), + "setval": ( + "mdt auto-discovery ingress-replication mdt-hello-enable" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "auto_discovery": { + "ingress_replication": { + "mdt_hello_enable": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.auto_discovery.pim.mdt_hello_enable", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sauto-discovery\spim\smdt-hello-enable + $""", re.VERBOSE, + ), + "setval": "mdt auto-discovery pim mdt-hello-enable", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "auto_discovery": { + "pim": { + "mdt_hello_enable": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.auto_discovery.pim.pim_tlv_announce.mdt_hello_enable", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sauto-discovery\spim\spim-tlv-announce\smdt-hello-enable + $""", re.VERBOSE, + ), + "setval": ( + "mdt auto-discovery pim pim-tlv-announce mdt-hello-enable" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "auto_discovery": { + "pim": { + "pim_tlv_announce": { + "mdt_hello_enable": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.auto_discovery.receiver_site", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sauto-discovery\sreceiver-site + $""", re.VERBOSE, + ), + "setval": "mdt auto-discovery receiver-site", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "auto_discovery": { + "receiver_site": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.data.ingress_replication.number", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sdata\singress-replication\s(?P\d+) + $""", re.VERBOSE, + ), + "setval": ( + "mdt data ingress-replication " + "{{ mdt.data.ingress_replication.number }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "data": { + "ingress_replication": { + "number": ( + "{{ mdt_data_ingress_" + "replication_number }}" + ), + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.data.ingress_replication.immediate_switch", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sdata\singress-replication\simmediate-switch + $""", re.VERBOSE, + ), + "setval": "mdt data ingress-replication immediate-switch", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "data": { + "ingress_replication": { + "immediate_switch": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.data.ingress_replication.number.immediate_switch", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sdata\singress-replication\s(?P\d+)\simmediate-switch + $""", re.VERBOSE, + ), + "setval": ( + "mdt data ingress-replication " + "{{ mdt.data.ingress_replication.number }} immediate-switch" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "data": { + "ingress_replication": { + "number": ( + "{{ mdt_data_ingress_" + "replication_number }}" + ), + "immediate_switch": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.data.list.access_list", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sdata\slist\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "mdt data list {{ mdt.data.list.access_list }}", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "data": { + "list": { + "access_list": ( + "{{ mdt_data_list_access_list }}" + ), + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.data.list.access_list_name", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sdata\slist\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "mdt data list {{ mdt.data.list.access_list_name }}", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "data": { + "list": { + "access_list_name": ( + "{{ mdt_data_list_access_list_" + "name }}" + ), + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.data.threshold", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sdata\sthreshold\s(?P\d+) + $""", re.VERBOSE, + ), + "setval": "mdt data threshold {{ mdt.data.threshold }}", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "data": { + "threshold": "{{ mdt_data_threshold }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.default_ingress_replication", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sdefault\singress-replication + $""", re.VERBOSE, + ), + "setval": "mdt default ingress-replication", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "default": { + "ingress_replication": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.direct", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sdirect + $""", re.VERBOSE, + ), + "setval": "mdt direct", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "direct": "{{ true }}", + }, + }, + }, + }, + }, + }, + { + "name": "mdt.log_reuse", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\slog-reuse + $""", re.VERBOSE, + ), + "setval": "mdt log-reuse", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "log_reuse": "{{ true }}", + }, + }, + }, + }, + }, + }, + { + "name": "mdt.mode.gre", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\smode\sgre + $""", re.VERBOSE, + ), + "setval": "mdt mode gre", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "mode": { + "gre": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.mtu.value", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\smtu\s(?P\d+) + $""", re.VERBOSE, + ), + "setval": "mdt mtu {{ mdt.mtu.value }}", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "mtu": "{{ mdt_value }}", + }, + }, + }, + }, + }, + }, + { + "name": "mdt.overlay.bgp.shared_tree_prune_delay", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\soverlay\sbgp\sshared-tree-prune-delay\s(?P\d+) + $""", re.VERBOSE, + ), + "setval": ( + "mdt overlay bgp shared-tree-prune-delay " + "{{ mdt.overlay.bgp.shared_tree_prune_delay }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "overlay": { + "bgp": { + "shared_tree_prune_delay": ( + "{{ shared_tree_prune_delay }}" + ), + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.overlay.bgp.source_tree_prune_delay", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\soverlay\sbgp\ssource-tree-prune-delay\s(?P\d+) + $""", re.VERBOSE, + ), + "setval": ( + "mdt overlay bgp source-tree-prune-delay " + "{{ mdt.overlay.bgp.source_tree_prune_delay }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "overlay": { + "bgp": { + "source_tree_prune_delay": ( + "{{ source_tree_prune_delay }}" + ), + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.overlay.use_bgp_spt_only", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\soverlay\suse-bgp\sspt-only + $""", re.VERBOSE, + ), + "setval": "mdt overlay use-bgp spt-only", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "overlay": { + "use_bgp": { + "spt_only": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.partitioned.ingress_replication", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\spartitioned\singress-replication + $""", re.VERBOSE, + ), + "setval": "mdt partitioned ingress-replication", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "partitioned": { + "ingress_replication": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "mdt.strict_rpf_interface", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+mdt\sstrict-rpf\sinterface + $""", re.VERBOSE, + ), + "setval": "mdt strict-rpf interface", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "mdt": { + "strict_rpf": { + "interface": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "protection.local_prefixes", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + $""", re.VERBOSE, + ), + "setval": "protection local-prefixes", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "protection": { + "local_prefixes": "{{ true }}", + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.recursion_policy.destination", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\srecursion-policy\sdestination + $""", re.VERBOSE, + ), + "setval": "route-replicate recursion-policy destination", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "recursion_policy": { + "destination": "{{ true }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.vrf.vrf_name.unicast.all.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\svrf\s(?P\S+)\sunicast\sall\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from vrf {{ vrf_name }} unicast all " + "route-map {{ route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "vrf": { + "name": "{{ vrf_name }}", + "unicast": { + "all": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": ( + "route_replicate.from.vrf.vrf_name.unicast.bgp.asn.route_map" + ), + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\svrf\s(?P\S+)\sunicast\sbgp\s(?P\d+)\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from vrf {{ vrf_name }} " + "unicast bgp {{ asn }} " + "route-map {{ route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "vrf": { + "name": "{{ vrf_name }}", + "unicast": { + "bgp": { + "as_number": "{{ asn }}", + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": ( + "route_replicate.from.vrf.vrf_name.unicast.connected.route_map" + ), + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\svrf\s(?P\S+)\sunicast\sconnected\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from vrf {{ vrf_name }} unicast connected " + "route-map {{ route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "vrf": { + "name": "{{ vrf_name }}", + "unicast": { + "connected": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": ( + "route_replicate.from.vrf.vrf_name.unicast.eigrp.asn." + "route_map" + ), + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\svrf\s(?P\S+)\sunicast\seigrp\s(?P\d+)\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from vrf {{ vrf_name }} " + "unicast eigrp {{ asn }} " + "route-map {{ route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "vrf": { + "name": "{{ vrf_name }}", + "unicast": { + "eigrp": { + "as_number": "{{ asn }}", + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.vrf.vrf_name.unicast.isis.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\svrf\s(?P\S+)\sunicast\sisis\s(?P\S+)\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from vrf {{ vrf_name }} unicast isis " + "{{ tag }} route-map {{ route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "vrf": { + "name": "{{ vrf_name }}", + "unicast": { + "isis": { + "iso_tag": "{{ tag }}", + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": ( + "route_replicate.from.vrf.vrf_name.unicast.mobile.route_map" + ), + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\svrf\s(?P\S+)\sunicast\smobile\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from vrf {{ vrf_name }} unicast mobile " + "route-map {{ route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "vrf": { + "name": "{{ vrf_name }}", + "unicast": { + "mobile": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.vrf.vrf_name.unicast.odr.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\svrf\s(?P\S+)\sunicast\sodr\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from vrf {{ vrf_name }} " + "unicast odr route-map {{ route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "vrf": { + "name": "{{ vrf_name }}", + "unicast": { + "odr": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": ( + "route_replicate.from.vrf.vrf_name.unicast.ospf.id." + "route_map" + ), + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\svrf\s(?P\S+)\sunicast\sospf\s(?P\d+)\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from vrf {{ vrf_name }} unicast ospf " + "{{ process_id }} route-map " + "{{ route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "vrf": { + "name": "{{ vrf_name }}", + "unicast": { + "ospf": { + "process_id": ( + "{{ process_id }}" + ), + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.vrf.vrf_name.unicast.rip.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\svrf\s(?P\S+)\sunicast\srip\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from vrf {{ vrf_name }} " + "unicast rip route-map {{ route_replicate.from.vrf.vrf_name." + "unicast.rip.route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "vrf": { + "name": "{{ vrf_name }}", + "unicast": { + "rip": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": ( + "route_replicate.from.vrf.vrf_name.unicast.static.route_map" + ), + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\svrf\s(?P\S+)\sunicast\sstatic\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from vrf {{ vrf_name }} " + "unicast static route-map {{ route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "vrf": { + "name": "{{ vrf_name }}", + "unicast": { + "static": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.unicast.all.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\sunicast\sall\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from unicast all route-map " + "{{ route_replicate.from.unicast.all.route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "unicast": { + "all": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.unicast.bgp.asn.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\sunicast\sbgp\s(?P\d+)\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from unicast bgp " + "{{ route_replicate.from.unicast.bgp.asn }} " + "route-map {{ route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "unicast": { + "bgp": { + "as_number": "{{ asn }}", + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.unicast.connected.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\sunicast\sconnected\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from unicast connected route-map " + "{{ route_replicate.from.unicast.connected.route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "unicast": { + "connected": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.unicast.eigrp.asn.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\sunicast\seigrp\s(?P\d+)\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from unicast eigrp " + "{{ route_replicate.from.unicast.eigrp.asn }} " + "route-map {{ route_replicate.from.unicast.eigrp.asn." + "route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "unicast": { + "eigrp": { + "as_number": "{{ asn }}", + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.unicast.isis.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\sunicast\sisis\s(?P\S+)\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from unicast isis " + "{{ route_replicate.from.unicast.isis.tag }} " + "route-map {{ route_replicate.from.unicast.isis.route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "unicast": { + "isis": { + "tag": "{{ tag }}", + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.unicast.mobile.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\sunicast\smobile\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from unicast mobile route-map " + "{{ route_replicate.from.unicast.mobile.route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "unicast": { + "mobile": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.unicast.odr.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\sunicast\sodr\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from unicast odr route-map " + "{{ route_replicate.from.unicast.odr.route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "unicast": { + "odr": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.unicast.ospf.id.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\sunicast\sospf\s(?P\d+)\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from unicast ospf " + "{{ route_replicate.from.unicast.ospf.id }} " + "route-map {{ route_replicate.from.unicast.ospf.id.route_map}}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "unicast": { + "ospf": { + "process_id": "{{ process_id }}", + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.unicast.rip.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\sunicast\srip\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from unicast rip route-map " + "{{ route_replicate.from.unicast.rip.route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "unicast": { + "rip": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_replicate.from.unicast.static.route_map", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-replicate\sfrom\sunicast\sstatic\sroute-map\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": ( + "route-replicate from unicast static route-map " + "{{ route_replicate.from.unicast.static.route_map }}" + ), + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_replicate": { + "from_config": { + "unicast": { + "static": { + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_target.export", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-target\sexport\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "route-target export {{ route_target.export }}", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_target": { + "export": "{{ route_target_export }}", + }, + }, + }, + }, + }, + }, + { + "name": "route_target.import_config", + "getval": re.compile( + r""" + ^vrf\sdefinition\s(?P\S+) + \saddress-family + \s(?P\S+) + (\s(?P\S+))? + \s+route-target\simport\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "route-target import {{ route_target.import_config }}", + "result": { + '{{ name }}': { + 'name': '{{ name }}', + "address_families": { + '{{ "address_families_" + afi + ' + '("_" + safi if safi is defined else "_unicast") }}': { + "afi": "{{ afi }}", + "safi": ( + "{{ safi if safi is defined else " + "'unicast' }}" + ), + "route_target": { + "import_config": ( + "{{ route_target_import_config }}" + ), + }, + }, + }, + }, + }, + }, + ] + # fmt: on diff --git a/plugins/module_utils/network/ios/utils/utils.py b/plugins/module_utils/network/ios/utils/utils.py index e79dd14b7..361c4757e 100644 --- a/plugins/module_utils/network/ios/utils/utils.py +++ b/plugins/module_utils/network/ios/utils/utils.py @@ -214,6 +214,44 @@ def remove_duplicate_interface(commands): return set_cmd +def flatten_dict(x): + result = {} + if not isinstance(x, dict): + return result + + for key, value in iteritems(x): + if isinstance(value, dict): + result.update(flatten_dict(value)) + else: + result[key] = value + + return result + + +def flatten_config(data, context): + """Flatten different contexts in + the running-config for easier parsing. + :param data: dict + :param context: str + :returns: flattened running config + """ + data = data.split("\n") + in_cxt = False + cur = {} + + for index, x in enumerate(data): + cur_indent = len(x) - len(x.lstrip()) + if x.strip().startswith(context): + in_cxt = True + cur["context"] = x + cur["indent"] = cur_indent + elif cur and (cur_indent <= cur["indent"]): + in_cxt = False + elif in_cxt: + data[index] = cur["context"] + " " + x.strip() + return "\n".join(data) + + def validate_ipv4(value, module): if value: address = value.split("/") diff --git a/plugins/modules/ios_vlans.py b/plugins/modules/ios_vlans.py index de9a0212a..f3dd3ecbc 100644 --- a/plugins/modules/ios_vlans.py +++ b/plugins/modules/ios_vlans.py @@ -75,7 +75,11 @@ type: bool shutdown: description: - - Shutdown VLAN switching. + - Specifies whether VLAN switching should be administratively enabled or disabled. + - When set to C(enabled), the VLAN interface is administratively shut down, which prevents it from forwarding traffic. + - When set to C(disabled), the VLAN interface is administratively enabled by issuing the internal C(no shutdown) command, + allowing it to forward traffic. + - The operational state of the VLAN depends on both the administrative state (C(shutdown) or C(no shutdown)) and the physical link status. type: str choices: - enabled @@ -266,100 +270,330 @@ # Before state: # ------------- # -# vios_l2#show vlan +# S1#show vlan + # VLAN Name Status Ports # ---- -------------------------------- --------- ------------------------------- -# 1 default active Gi0/1, Gi0/2 -# 10 vlan_10 active -# 20 vlan_20 act/lshut -# 30 vlan_30 sus/lshut +# 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 +# 10 Vlan_10 active +# 20 Vlan_20 active +# 30 Vlan_30 suspended +# 44 Vlan_44 suspended # 1002 fddi-default act/unsup # 1003 token-ring-default act/unsup # 1004 fddinet-default act/unsup # 1005 trnet-default act/unsup -# + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ # 1 enet 100001 1500 - - - - - 0 0 # 10 enet 100010 1500 - - - - - 0 0 # 20 enet 100020 610 - - - - - 0 0 # 30 enet 100030 1500 - - - - - 0 0 +# 44 enet 100044 1500 - - - - - 0 0 # 1002 fddi 101002 1500 - - - - - 0 0 # 1003 tr 101003 1500 - - - - - 0 0 # 1004 fdnet 101004 1500 - - - ieee - 0 0 # 1005 trnet 101005 1500 - - - ibm - 0 0 -# + # Remote SPAN VLANs # ------------------------------------------------------------------------------ -# 10 + + +# Primary Secondary Type Ports +# ------- --------- ----------------- ------------------------------------------ +# S1# + - name: Override device configuration of all VLANs with provided configuration cisco.ios.ios_vlans: config: - - name: Vlan_10 - vlan_id: 10 - mtu: 1000 + - name: Vlan_2020 + state: active + vlan_id: 20 + shutdown: disabled state: overridden +# Task output: +# ------------ + +# after: +# - mtu: 1500 +# name: default +# shutdown: disabled +# state: active +# vlan_id: 1 +# - mtu: 1500 +# name: Vlan_2020 +# shutdown: disabled +# state: active +# vlan_id: 20 +# - mtu: 1500 +# name: fddi-default +# shutdown: enabled +# state: active +# vlan_id: 1002 +# - mtu: 1500 +# name: token-ring-default +# shutdown: enabled +# state: active +# vlan_id: 1003 +# - mtu: 1500 +# name: fddinet-default +# shutdown: enabled +# state: active +# vlan_id: 1004 +# - mtu: 1500 +# name: trnet-default +# shutdown: enabled +# state: active +# vlan_id: 1005 + +# before: +# - mtu: 1500 +# name: default +# shutdown: disabled +# state: active +# vlan_id: 1 +# - mtu: 1500 +# name: Vlan_10 +# shutdown: disabled +# state: active +# vlan_id: 10 +# - mtu: 610 +# name: Vlan_20 +# shutdown: disabled +# state: active +# vlan_id: 20 +# - mtu: 1500 +# name: Vlan_30 +# shutdown: disabled +# state: suspend +# vlan_id: 30 +# - mtu: 1500 +# name: Vlan_44 +# shutdown: disabled +# state: suspend +# vlan_id: 44 +# - mtu: 1500 +# name: fddi-default +# shutdown: enabled +# state: active +# vlan_id: 1002 +# - mtu: 1500 +# name: token-ring-default +# shutdown: enabled +# state: active +# vlan_id: 1003 +# - mtu: 1500 +# name: fddinet-default +# shutdown: enabled +# state: active +# vlan_id: 1004 +# - mtu: 1500 +# name: trnet-default +# shutdown: enabled +# state: active +# vlan_id: 1005 + +# commands: +# - no vlan 1 +# - no vlan 10 +# - no vlan 30 +# - no vlan 44 +# - no vlan 1002 +# - no vlan 1003 +# - no vlan 1004 +# - no vlan 1005 +# - vlan 20 +# - name Vlan_2020 +# - no mtu 610 +# - no vlan configuration 1 +# - no vlan configuration 10 +# - no vlan configuration 30 +# - no vlan configuration 44 +# - no vlan configuration 1002 +# - no vlan configuration 1003 +# - no vlan configuration 1004 +# - no vlan configuration 1005 + # After state: # ------------ # # vios_l2#show vlan # VLAN Name Status Ports # ---- -------------------------------- --------- ------------------------------- -# 1 default active Gi0/1, Gi0/2 -# 10 Vlan_10 active +# 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 +# 20 Vlan_2020 active # 1002 fddi-default act/unsup # 1003 token-ring-default act/unsup # 1004 fddinet-default act/unsup # 1005 trnet-default act/unsup -# + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ # 1 enet 100001 1500 - - - - - 0 0 -# 10 enet 100010 1000 - - - - - 0 0 +# 20 enet 100020 1500 - - - - - 0 0 # 1002 fddi 101002 1500 - - - - - 0 0 # 1003 tr 101003 1500 - - - - - 0 0 # 1004 fdnet 101004 1500 - - - ieee - 0 0 # 1005 trnet 101005 1500 - - - ibm - 0 0 +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ -# Using overridden + +# Primary Secondary Type Ports +# ------- --------- ----------------- ------------------------------------------ + +# Using purged # Before state: # ------------- # -# Leaf-01#show run nve | sec ^vlan configuration -# vlan configuration 101 -# member evpn-instance 101 vni 10101 -# vlan configuration 102 -# member evpn-instance 102 vni 10102 -# vlan configuration 201 -# member evpn-instance 201 vni 10201 -# vlan configuration 901 -# member vni 50901 +# S1#show vlan -- name: Override device configuration of all VLANs with provided configuration +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 +# 20 Vlan_2020 active +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup + +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 20 enet 100020 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ + + +# Primary Secondary Type Ports +# ------- --------- ----------------- ------------------------------------------ + +# S1#show running-config | section ^vlan configuration .+ +# vlan configuration 20 + + +- name: Purge all vlans configuration cisco.ios.ios_vlans: config: - - vlan_id: 101 - member: - vni: 10102 - evi: 102 - - vlan_id: 102 - member: - vni: 10101 - evi: 101 - state: overridden + state: purged + +# Task output: +# ------------ + +# after: +# - mtu: 1500 +# name: default +# shutdown: disabled +# state: active +# vlan_id: 1 +# - mtu: 1500 +# name: fddi-default +# shutdown: enabled +# state: active +# vlan_id: 1002 +# - mtu: 1500 +# name: token-ring-default +# shutdown: enabled +# state: active +# vlan_id: 1003 +# - mtu: 1500 +# name: fddinet-default +# shutdown: enabled +# state: active +# vlan_id: 1004 +# - mtu: 1500 +# name: trnet-default +# shutdown: enabled +# state: active +# vlan_id: 1005 + +# before: +# - mtu: 1500 +# name: default +# shutdown: disabled +# state: active +# vlan_id: 1 +# - mtu: 1500 +# name: Vlan_2020 +# shutdown: disabled +# state: active +# vlan_id: 20 +# - mtu: 1500 +# name: fddi-default +# shutdown: enabled +# state: active +# vlan_id: 1002 +# - mtu: 1500 +# name: token-ring-default +# shutdown: enabled +# state: active +# vlan_id: 1003 +# - mtu: 1500 +# name: fddinet-default +# shutdown: enabled +# state: active +# vlan_id: 1004 +# - mtu: 1500 +# name: trnet-default +# shutdown: enabled +# state: active +# vlan_id: 1005 + +# commands: +# - no vlan 1 +# - no vlan 20 +# - no vlan 1002 +# - no vlan 1003 +# - no vlan 1004 +# - no vlan 1005 +# - no vlan configuration 1 +# - no vlan configuration 20 +# - no vlan configuration 1002 +# - no vlan configuration 1003 +# - no vlan configuration 1004 +# - no vlan configuration 1005 # After state: # ------------ # -# Leaf-01#show run nve | sec ^vlan configuration -# vlan configuration 101 -# member evpn-instance 102 vni 10102 -# vlan configuration 102 -# member evpn-instance 101 vni 10101 +# S1#show vlan + +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup + +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ + + +# Primary Secondary Type Ports +# ------- --------- ----------------- ------------------------------------------ + +# S1#show running-config | section ^vlan configuration .+ +# S1# + # Using replaced diff --git a/plugins/modules/ios_vrf_address_family.py b/plugins/modules/ios_vrf_address_family.py new file mode 100644 index 000000000..dad3f69ca --- /dev/null +++ b/plugins/modules/ios_vrf_address_family.py @@ -0,0 +1,1157 @@ +#!/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_address_family +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_vrf_address_family +short_description: Resource module to configure VRF definitions. +description: This module provides declarative management of VRF definitions on Cisco IOS. +version_added: 7.0.0 +author: Ruchi Pakhle (@Ruchip16) +notes: + - Tested against Cisco IOSXE version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A list of device configurations for VRF. + type: list + elements: dict + suboptions: + name: + description: Name of the VRF. + type: str + required: true + address_families: + description: Enable address family and enter its config mode - AFI/SAFI configuration + type: list + elements: dict + suboptions: + afi: + description: Address Family Identifier (AFI) + type: str + choices: ['ipv4', 'ipv6'] + safi: + description: Address Family modifier + type: str + choices: ['multicast', 'unicast'] + bgp: &bgp01 + description: Commands pertaining to BGP configuration. + type: dict + suboptions: + next_hop: + description: Next-hop for the routes of a VRF in the backbone. + type: dict + suboptions: + loopback: + description: Loopback interface for next-hop + type: int + export: &export + description: VRF export + type: dict + suboptions: + map: + description: Route-map name + type: str + ipv4: + description: Address family based VRF export + type: dict + suboptions: + multicast: + description: Export prefixes to IPv4 Multicast table + type: dict + suboptions: + prefix: + description: Upper limit on export prefixes without hogging memory + type: int + map: + description: Route-map name + type: str + unicast: + description: Export prefixes to IPv4 Unicast table + type: dict + suboptions: + prefix: + description: Upper limit on export prefixes without hogging memory + type: int + map: + description: Route-map name + type: str + allow_evpn: + description: Allow EVPN routes into global table + type: bool + import_config: &import01 + description: VRF import + type: dict + suboptions: + map: + description: Route-map name + type: str + ipv4: + description: Address family based VRF import + type: dict + suboptions: + multicast: + description: Import prefixes from IPv4 Multicast table + type: dict + suboptions: + prefix: + description: Upper limit on import prefixes without hogging memory + type: int + map: + description: Route-map name + type: str + unicast: + description: Import prefixes from IPv4 Unicast table + type: dict + suboptions: + limit: + description: Upper limit on import prefixes without hogging memory + type: int + map: + description: Route-map based VRF import + type: str + allow_evpn: + description: allow Global->VRF routes into EVPN + type: bool + inter_as_hybrid: &inter_as_hybrid + description: Inter AS hybrid mode + type: dict + suboptions: + csc: + description: Carrier Supporting Carrier + type: dict + suboptions: + next_hop: &next_hop + description: Next-hop for the routes of a VRF in the backbone. + type: str + next_hop: *next_hop + mdt: + description: Backbone Multicast Distribution Tree + type: dict + suboptions: + auto_discovery: + description: BGP auto-discovery for MVPN + type: dict + suboptions: + ingress_replication: + description: BGP auto-discovery for Ingress-Replication + type: dict + suboptions: + inter_as: + description: Enable Inter-AS BGP auto-discovery + type: dict + suboptions: + mdt_hello_enable: &mdt_hello_enable + description: Enable PIM Hellos over MDT interface + type: bool + mdt_hello_enable: *mdt_hello_enable + pim: + description: BGP auto-discovery for PIM + type: dict + suboptions: + inter_as: + description: Enable Inter-AS BGP auto-discovery + type: dict + suboptions: + mdt_hello_enable: *mdt_hello_enable + pim_tlv_announce: &pim_tlv_announce + description: Announce PIM TLV for data MDT + type: dict + suboptions: + mdt_hello_enable: *mdt_hello_enable + mdt_hello_enable: *mdt_hello_enable + pim_tlv_announce: *pim_tlv_announce + receiver_site: + description: BGP receiver only site for MVPN + type: bool + data: + description: MDT data trees + type: dict + suboptions: + ingress_replication: + description: Use Ingress-Replication to create the data MDT + type: dict + suboptions: + number: + description: Number of data MDT + type: int + immediate_switch: + description: Switch immediately to Data MDT tree + type: bool + list: &list + description: Access-list + type: dict + suboptions: + access_list_number: + description: Access-list number + type: int + access_list_name: + description: IP Named Extended Access list + type: str + list: *list + threshold: + description: MDT switching threshold + type: int + default: + description: Default MDT configuration + type: dict + suboptions: + ingress_replication: + description: Use Ingress-Replication for the default MDT + type: bool + direct: + description: Direct MDT's + type: bool + log_reuse: + description: Event logging for data MDT reuse + type: bool + mode: + description: The type of encapsulation + type: dict + suboptions: + gre: + description: GRE encapsulation + type: bool + mtu: + description: The MTU + type: int + overlay: + description: MDT overlay Protocol + type: dict + suboptions: + bgp: + description: BGP Overlay signalling + type: dict + suboptions: + shared_tree_prune_delay: + description: Delay before shared tree is pruned at C-RP PE + type: int + source_tree_prune_delay: + description: Delay before source tree is pruned at C-S PE + type: int + use_bgp: + description: Use BGP for MDT overlay signaling + type: dict + suboptions: + spt_only: + description: Enable SPT-only ASM mode + type: bool + partitioned: + description: Partitioned Multicast Distribution Tree + type: dict + suboptions: + ingress_replication: + description: Use Ingress-Replication for the partitioned MDT + type: bool + strict_rpf: + description: Enable strict RPF check + type: dict + suboptions: + interface: + description: Interface based strict RPF check + type: bool + protection: + description: Configure local repair + type: dict + suboptions: + local_prefixes: + description: Enable protection for local prefixes + type: bool + route_replicate: &route_replicate + description: Replicate (import) routes from another topology (and another VRF) + type: dict + suboptions: + recursion_policy: + description: Route replication recursion policy + type: dict + suboptions: + destination: + description: Recurse in destination topology + type: bool + from_config: + description: Replicate routes from another VRF + type: dict + suboptions: + multicast: + description: Multicast SAFI + type: dict + suboptions: + all: &all + description: All routes + type: dict + suboptions: + route_map: &route_map + description: Route-map reference + type: str + bgp: &bgp + description: Border Gateway Protocol (BGP) + type: dict + suboptions: + as_number: + description: Autonomous System Number + type: int + route_map: *route_map + eigrp: &eigrp + description: Enhanced Interior Gateway Routing Protocol (EIGRP) + type: dict + suboptions: + as_number: + description: Autonomous System Number + type: int + route_map: *route_map + isis: &isis + description: Intermediate System-to-Intermediate System (ISIS) + type: dict + suboptions: + iso_tag: + description: ISO routing area tag + type: str + route_map: *route_map + mobile: &mobile + description: Mobile routes + type: dict + suboptions: + route_map: *route_map + odr: &odr + description: On-Demand Stub routes + type: dict + suboptions: + route_map: *route_map + ospf: &ospf + description: Open Shortest Path First (OSPF) + type: dict + suboptions: + process_id: + description: OSPF process ID + type: int + route_map: *route_map + rip: &rip + description: Routing Information Protocol (RIP) + type: dict + suboptions: + route_map: *route_map + static: &static + description: Static routes + type: dict + suboptions: + route_map: *route_map + topology: &topology + description: Topology name + type: dict + suboptions: + base: + description: Base topology + type: dict + suboptions: + all: *all + bgp: *bgp + eigrp: *eigrp + isis: *isis + mobile: *mobile + odr: *odr + ospf: *ospf + rip: *rip + static: *static + unicast: + description: Unicast SAFI + type: dict + suboptions: + all: *all + bgp: *bgp + connected: + description: Connected routes + type: dict + suboptions: + route_map: *route_map + eigrp: *eigrp + isis: *isis + mobile: *mobile + odr: *odr + ospf: *ospf + rip: *rip + static: *static + vrf: + description: Specify a source VRF + type: dict + suboptions: + name: + description: Source VRF name + type: str + multicast: + description: Multicast SAFI + type: dict + suboptions: + all: *all + bgp: *bgp + eigrp: *eigrp + isis: *isis + mobile: *mobile + odr: *odr + ospf: *ospf + rip: *rip + static: *static + topology: *topology + unicast: + description: Unicast SAFI + type: dict + suboptions: + all: *all + bgp: *bgp + connected: + description: Connected routes + type: dict + suboptions: + route_map: *route_map + eigrp: *eigrp + isis: *isis + mobile: *mobile + odr: *odr + ospf: *ospf + rip: *rip + static: *static + global: + description: global VRF + type: dict + suboptions: + multicast: + description: Multicast SAFI + type: dict + suboptions: + all: *all + bgp: *bgp + eigrp: *eigrp + isis: *isis + mobile: *mobile + odr: *odr + ospf: *ospf + rip: *rip + static: *static + topology: *topology + unicast: + description: Unicast SAFI + type: dict + suboptions: + all: *all + bgp: *bgp + connected: + description: Connected routes + type: dict + suboptions: + route_map: *route_map + eigrp: *eigrp + isis: *isis + mobile: *mobile + odr: *odr + ospf: *ospf + rip: *rip + static: *static + route_target: &route_target + description: Specify Target VPN Extended Communities. + type: dict + suboptions: + export: + description: Export Target-VPN community. + type: str + import_config: + description: Export Target-VPN community. + type: str + both: + description: Both export and import Target-VPN community + 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 vrf). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + choices: [parsed, gathered, deleted, merged, replaced, rendered, overridden] + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config vrf). + connection to remote host is not required. + type: str +""" + +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# RP/0/0/CPU0:ios#show running-config | section ^vrf +# vrf defnition test +# + +- name: Merge provided configuration with device configuration + cisco.ios.ios_vrf_address_family: + config: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 40 + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + allow_evpn: true + map: "test-map" + prefix: 67 + map: "testing-map" + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + allow_evpn: true + limit: 12 + map: "ran-map" + map: "import-map" + state: merged + +# Task Output: +# ------------ +# +# before: [] +# +# commands: +# - vrf definition test1 +# - address-family ipv4 unicast +# - bgp next-hop loopback 40 +# - export map "testing-map" +# - export ipv4 multicast 345 map "single" +# - export ipv4 unicast 67 map "test-map" allow-evpn +# - import map "import-map" +# - import ipv4 multicast 89 map "import-map" +# - import ipv4 unicast 12 map "ran-map" allow-evpn +# +# after: +# - name: test1 +# address_families: +# - afi: "ipv4" +# safi: "unicast" +# bgp: +# next_hop: +# loopback: 40 +# export: +# ipv4: +# multicast: +# map: "single" +# prefix: 345 +# unicast: +# allow_evpn: true +# map: "test-map" +# prefix: 67 +# map: "testing-map" +# import_config: +# ipv4: +# multicast: +# map: "import-map" +# prefix: 89 +# unicast: +# allow_evpn: true +# limit: 12 +# map: "ran-map" +# map: "import-map" +# +# After state: +# ------------ +# +# RP/0/0/CPU0:ios#show running-config | section ^vrf +# vrf definition test1 +# address-family ipv4 unicast +# bgp next-hop loopback 40 +# export map "testing-map" +# export ipv4 multicast 345 map "single" +# export ipv4 unicast 67 map "test-map" allow-evpn +# import map "import-map" +# import ipv4 multicast 89 map "import-map" +# import ipv4 unicast 12 map "ran-map" allow-evpn + +# Using replaced +# +# Before state: +# ------------- +# +# RP/0/0/CPU0:ios#show running-config | section ^vrf +# vrf definition test1 +# address-family ipv4 unicast +# bgp next-hop loopback 40 +# export map "testing-map" +# export ipv4 multicast 345 map "single" +# export ipv4 unicast 67 map "test-map" allow-evpn +# import map "import-map" +# import ipv4 multicast 89 map "import-map" +# import ipv4 unicast 12 map "ran-map" allow-evpn + +- name: Replace the provided configuration with the existing running configuration + cisco.ios.ios_vrf_address_family: + config: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 40 + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + allow_evpn: true + map: "test-map" + prefix: 67 + map: "testing-map" + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + allow_evpn: true + limit: 12 + map: "ran-map" + map: "import-map" + state: replaced + +# Task Output: +# ------------ +# +# before: +# - name: test1 +# address_families: +# - afi: "ipv4" +# safi: "unicast" +# bgp: +# next_hop: +# loopback: 40 +# export: +# ipv4: +# multicast: +# map: "single" +# prefix: 345 +# unicast: +# allow_evpn: true +# map: "test-map" +# prefix: 67 +# map: "testing-map" +# import_config: +# ipv4: +# multicast: +# map: "import-map" +# prefix: 89 +# unicast: +# allow_evpn: true +# limit: 12 +# map: "ran-map" +# map: "import-map" +# +# commands: +# - vrf definition test1 +# - address-family ipv4 unicast +# - bgp next-hop loopback 40 +# - export map "testing-map" +# - export ipv4 multicast 345 map "single" +# - export ipv4 unicast 67 map "test-map" allow-evpn +# - import map "import-map" +# - import ipv4 multicast 89 map "import-map" +# - import ipv4 unicast 12 map "ran-map" allow-evpn +# +# after: +# - name: VRF1 +# address_families: +# - afi: "ipv4" +# safi: "unicast" +# bgp: +# next_hop: +# loopback: 23 +# export: +# ipv4: +# multicast: +# map: "single" +# prefix: 33 +# unicast: +# allow_evpn: true +# map: "test-map1" +# prefix: 7 +# map: "testing-map" +# import_config: +# ipv4: +# multicast: +# map: "import-map1" +# prefix: 89 +# unicast: +# allow_evpn: true +# limit: 12 +# map: "ran-map" +# map: "import-map" +# +# After state: +# ------------ +# +# RP/0/RP0/CPU0:ios(config)#show running-config vrf +# vrf definition VRF1 +# address-family ipv4 unicast +# bgp next-hop loopback 23 +# export map "testing-map" +# export ipv4 multicast 345 map "single" +# export ipv4 unicast 67 map "test-map1" allow-evpn +# import map "import-map" +# import ipv4 multicast 89 map "import-map1" +# import ipv4 unicast 12 map "ran-map" allow-evpn + +# Using overridden +# +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:ios(config)#show running-config | section ^vrf +# vrf definition test1 +# address-family ipv4 unicast +# bgp next-hop loopback 40 +# export map "testing-map" +# export ipv4 multicast 345 map "single" +# export ipv4 unicast 67 map "test-map" allow-evpn +# import map "import-map" +# import ipv4 multicast 89 map "import-map" +# import ipv4 unicast 12 map "ran-map" allow-evpn + +- name: Override the provided configuration with the existing running configuration + cisco.ios.ios_vrf_address_family: + state: overridden + config: + - name: VRF7 + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 89 + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + allow_evpn: true + map: "test-map" + prefix: 67 + map: "testing-map" + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + allow_evpn: true + limit: 12 + map: "ran-map" + map: "import-map" +# Task Output: +# ------------ +# +# before: +# - name: test1 +# address_families: +# - afi: "ipv4" +# safi: "unicast" +# bgp: +# next_hop: +# loopback: 40 +# export: +# ipv4: +# multicast: +# map: "single" +# prefix: 345 +# unicast: +# allow_evpn: true +# map: "test-map" +# prefix: 67 +# map: "testing-map" +# import_config: +# ipv4: +# multicast: +# map: "import-map" +# prefix: 89 +# unicast: +# allow_evpn: true +# limit: 12 +# map: "ran-map" +# map: "import-map" +# +# commands: +# - vrf definition VRF7 +# - address-family ipv4 unicast +# - bgp next-hop loopback 89 +# - export map "testing-map" +# - export ipv4 multicast 345 map "single" +# - export ipv4 unicast 67 map "test-map" allow-evpn +# - import map "import-map" +# - import ipv4 multicast 89 map "import-map" +# - import ipv4 unicast 12 map "ran-map" allow-evpn + +# +# After state: +# ------------- +# RP/0/RP0/CPU0:ios(config)#show running-config vrf +# vrf definition VRF4 +# vrf definition VRF7 +# address-family ipv4 unicast +# bgp next-hop loopback 89 +# export map "testing-map" +# export ipv4 multicast 345 map "single" +# export ipv4 unicast 67 map "test-map" allow-evpn +# import map "import-map" +# import ipv4 multicast 89 map "import-map1" +# import ipv4 unicast 12 map "ran-map" allow-evpn + +# Using deleted +# +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:ios(config)#show running-config | section ^vrf +# vrf definition VRF4 +# vrf definition VRF6 +# address-family ipv4 unicast +# bgp next-hop loopback 40 +# import map "import-map" +# import ipv4 multicast 89 map "import-map" +# import ipv4 unicast 12 map "ran-map" allow-evpn +# export map "testing-map" +# export ipv4 multicast 345 map "single" +# export ipv4 unicast 67 map "test-map" allow-evpn +# vrf definition VRF7 + +- name: Delete the provided configuration + cisco.ios.ios_vrf_address_family: + config: + state: deleted + +# Task Output: +# ------------ +# +# before: +# - name: VRF4 +# - name: VRF6 +# address_families: +# - afi: "ipv4" +# safi: "unicast" +# bgp: +# next_hop: +# loopback: 23 +# import_config: +# ipv4: +# multicast: +# map: "import-map" +# prefix: 89 +# unicast: +# map: "ran-map" +# limit: 12 +# allow_evpn: true +# map: "import-map" +# export: +# ipv4: +# multicast: +# map: "single" +# prefix: 345 +# unicast: +# map: "test-map" +# prefix: 67 +# allow_evpn: true +# map: "testing-map" +# - name: VRF7 + +# commands: +# - vrf definition VRF4 +# - vrf definition VRF6 +# - no address-family ipv4 unicast +# - vrf definition VRF7 +# +# after: +# - name: VRF4 +# - name: VRF6 +# - name: VRF7 +# +# After state: +# ------------ +# +# RP/0/RP0/CPU0:ios(config)#show running-config | section ^vrf +# vrf definition VRF4 +# vrf definition VRF6 +# vrf definition VRF7 + +# Using rendered +# +- name: Render provided configuration with device configuration + cisco.ios.ios_vrf_address_family: + config: + - name: test + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 23 + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + map: "ran-map" + limit: 12 + allow_evpn: true + map: "import-map" + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + map: "test-map" + prefix: 67 + allow_evpn: true + map: "testing-map" + state: rendered + +# Task Output: +# ------------ +# +# rendered: +# - vrf definition test +# - address-family ipv4 unicast +# - bgp next-hop loopback 23 +# - import map "import-map" +# - import ipv4 multicast 89 map "import-map" +# - import ipv4 unicast 12 map "ran-map" allow-evpn +# - export map "testing-map" +# - export ipv4 multicast 345 map "single" +# - export ipv4 unicast 67 map "test-map" allow-evpn + +# Using gathered +# +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:ios(config)#show running-config | section ^vrf +# vrf definition test1 +# address-family ipv4 unicast +# bgp next-hop loopback 40 +# import map "import-map" +# import ipv4 multicast 89 map "import-map" +# import ipv4 unicast 12 map "ran-map" allow-evpn +# export map "testing-map" +# export ipv4 multicast 345 map "single" +# export ipv4 unicast 67 map "test-map" allow-evpn + +- name: Gather existing running configuration + cisco.ios.ios_vrf_address_family: + state: gathered + +# Task Output: +# ------------ +# +# gathered: +# - name: test1 +# address_families: +# - afi: "ipv4" +# safi: "unicast" +# bgp: +# next_hop: +# loopback: 40 +# export: +# ipv4: +# multicast: +# map: "single" +# prefix: 345 +# unicast: +# allow_evpn: true +# map: "test-map" +# prefix: 67 +# map: "testing-map" +# import_config: +# ipv4: +# multicast: +# map: "import-map" +# prefix: 89 +# unicast: +# allow_evpn: true +# limit: 12 +# map: "ran-map" +# map: "import-map" + +# Using parsed +# +# File: parsed.cfg +# ---------------- +# +# vrf definition test +# address-family ipv4 unicast +# bgp next-hop loopback 23 +# import map "import-map" +# import ipv4 multicast 89 map "import-map" +# import ipv4 unicast 12 map "ran-map" allow-evpn +# export map "testing-map" +# export ipv4 multicast 345 map "single" +# export ipv4 unicast 67 map "test-map" allow-evpn + +- name: Parse the provided configuration + cisco.ios.ios_vrf_address_family: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output: +# ------------ +# +# parsed: +# - address_families: +# - afi: ipv4 +# bgp: +# next_hop: +# loopback: 40 +# export: +# ipv4: +# multicast: +# map: "single" +# prefix: 345 +# unicast: +# allow_evpn: true +# map: "test-map" +# prefix: 67 +# map: "testing-map" +# import_config: +# ipv4: +# multicast: +# map: "import-map" +# prefix: 89 +# unicast: +# allow_evpn: true +# limit: 12 +# map: "ran-map" +# safi: unicast +# name: test +""" + +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: list + 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: list + 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) + type: list + sample: + - vrf definition test1 + - address-family ipv4 unicast + - bgp next-hop loopback 40 + - export map "testing-map" + - export ipv4 multicast 345 map "testmap" +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - vrf definition test1 + - address-family ipv4 unicast + - bgp next-hop loopback 40 + - import map "testing-map" + - export ipv4 multicast 345 map "testmap" +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_address_family.vrf_address_family import ( + Vrf_address_familyArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.vrf_address_family.vrf_address_family import ( + Vrf_address_family, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Vrf_address_familyArgs.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_address_family(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration/labs/single.yaml b/tests/integration/labs/single.yaml new file mode 100644 index 000000000..ee3d6065c --- /dev/null +++ b/tests/integration/labs/single.yaml @@ -0,0 +1,210 @@ +--- +lab: + description: "" + notes: "" + title: cisco.ios.ios + version: 0.2.2 +links: + - id: l0 + n1: n0 + n2: n1 + i1: i1 + i2: i0 + conditioning: {} + label: cat8000v-0-GigabitEthernet1<->ext-conn-0-port +nodes: + - boot_disk_size: null + configuration: |2- + service timestamps debug datetime msec + service timestamps log datetime msec + platform qfp utilization monitor load 80 + platform punt-keepalive disable-kernel-core + platform sslvpn use-pd + platform console serial + ! + hostname cisco + ! + boot-start-marker + boot-end-marker + ! + ! + no aaa new-model + ! + ! + ! + ! + ! + ! + ! + ! + ! + ! + ! + ! + ip domain name ansible.com + ! + ! + ! + login on-success log + ! + ! + subscriber templating + ! + pae + ! + ! + crypto pki trustpoint SLA-TrustPoint + enrollment pkcs12 + revocation-check crl + hash sha256 + ! + ! + crypto pki certificate chain SLA-TrustPoint + certificate ca 01 + 30820321 30820209 A0030201 02020101 300D0609 2A864886 F70D0101 0B050030 + 32310E30 0C060355 040A1305 43697363 6F312030 1E060355 04031317 43697363 + 6F204C69 63656E73 696E6720 526F6F74 20434130 1E170D31 33303533 30313934 + 3834375A 170D3338 30353330 31393438 34375A30 32310E30 0C060355 040A1305 + 43697363 6F312030 1E060355 04031317 43697363 6F204C69 63656E73 696E6720 + 526F6F74 20434130 82012230 0D06092A 864886F7 0D010101 05000382 010F0030 + 82010A02 82010100 A6BCBD96 131E05F7 145EA72C 2CD686E6 17222EA1 F1EFF64D + CBB4C798 212AA147 C655D8D7 9471380D 8711441E 1AAF071A 9CAE6388 8A38E520 + 1C394D78 462EF239 C659F715 B98C0A59 5BBB5CBD 0CFEBEA3 700A8BF7 D8F256EE + 4AA4E80D DB6FD1C9 60B1FD18 FFC69C96 6FA68957 A2617DE7 104FDC5F EA2956AC + 7390A3EB 2B5436AD C847A2C5 DAB553EB 69A9A535 58E9F3E3 C0BD23CF 58BD7188 + 68E69491 20F320E7 948E71D7 AE3BCC84 F10684C7 4BC8E00F 539BA42B 42C68BB7 + C7479096 B4CB2D62 EA2F505D C7B062A4 6811D95B E8250FC4 5D5D5FB8 8F27D191 + C55F0D76 61F9A4CD 3D992327 A8BB03BD 4E6D7069 7CBADF8B DF5F4368 95135E44 + DFC7C6CF 04DD7FD1 02030100 01A34230 40300E06 03551D0F 0101FF04 04030201 + 06300F06 03551D13 0101FF04 05300301 01FF301D 0603551D 0E041604 1449DC85 + 4B3D31E5 1B3E6A17 606AF333 3D3B4C73 E8300D06 092A8648 86F70D01 010B0500 + 03820101 00507F24 D3932A66 86025D9F E838AE5C 6D4DF6B0 49631C78 240DA905 + 604EDCDE FF4FED2B 77FC460E CD636FDB DD44681E 3A5673AB 9093D3B1 6C9E3D8B + D98987BF E40CBD9E 1AECA0C2 2189BB5C 8FA85686 CD98B646 5575B146 8DFC66A8 + 467A3DF4 4D565700 6ADF0F0D CF835015 3C04FF7C 21E878AC 11BA9CD2 55A9232C + 7CA7B7E6 C1AF74F6 152E99B7 B1FCF9BB E973DE7F 5BDDEB86 C71E3B49 1765308B + 5FB0DA06 B92AFE7F 494E8A9E 07B85737 F3A58BE1 1A48A229 C37C1E69 39F08678 + 80DDCD16 D6BACECA EEBC7CF9 8428787B 35202CDC 60E4616A B623CDBD 230E3AFB + 418616A9 4093E049 4D10AB75 27E86F73 932E35B5 8862FDAE 0275156F 719BB2F0 + D697DF7F 28 + quit + ! + ! + license udi pid C8000V sn 9EGHOCL6VW2 + memory free low-watermark processor 201711 + diagnostic bootup level minimal + ! + ! + ! + username ansible privilege 15 secret 9 $9$VslpRow9omMy..$5zvxcxrJqLeDd0qEe.5FANSAeLgz9LLAzMiAjieb/nc + ! + redundancy + ! + ! + ! + ! + ! + ! + ! + ! + interface GigabitEthernet1 + ip address dhcp + no shutdown + negotiation auto + ! + interface GigabitEthernet2 + no ip address + shutdown + negotiation auto + ! + interface GigabitEthernet3 + no ip address + shutdown + negotiation auto + ! + interface GigabitEthernet4 + no ip address + shutdown + negotiation auto + ! + ip forward-protocol nd + ! + no ip http server + ip http secure-server + ip ssh bulk-mode 131072 + ! + ! + ! + ! + ! + control-plane + ! + ! + line con 0 + stopbits 1 + line aux 0 + line vty 0 4 + login local + transport input ssh + ! + ! + ! + ! + ! + ! + ! + end + cpu_limit: null + cpus: null + data_volume: null + hide_links: false + id: n0 + image_definition: null + label: cat8000v-0 + node_definition: cat8000v + parameters: {} + ram: null + tags: [] + x: -120 + y: 40 + interfaces: + - id: i0 + label: Loopback0 + type: loopback + - id: i1 + label: GigabitEthernet1 + slot: 0 + type: physical + - id: i2 + label: GigabitEthernet2 + slot: 1 + type: physical + - id: i3 + label: GigabitEthernet3 + slot: 2 + type: physical + - id: i4 + label: GigabitEthernet4 + slot: 3 + type: physical + - boot_disk_size: null + configuration: [] + cpu_limit: null + cpus: null + data_volume: null + hide_links: false + id: n1 + image_definition: null + label: ext-conn-0 + node_definition: external_connector + parameters: {} + ram: null + tags: [] + x: 160 + y: 0 + interfaces: + - id: i0 + label: port + slot: 0 + type: physical +annotations: [] diff --git a/tests/integration/targets/ios_vrf_address_family/defaults/main.yaml b/tests/integration/targets/ios_vrf_address_family/defaults/main.yaml new file mode 100644 index 000000000..164afead2 --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "[^_].*" +test_items: [] diff --git a/tests/integration/targets/ios_vrf_address_family/meta/main.yaml b/tests/integration/targets/ios_vrf_address_family/meta/main.yaml new file mode 100644 index 000000000..23d65c7ef --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/meta/main.yaml @@ -0,0 +1,2 @@ +--- +dependencies: [] diff --git a/tests/integration/targets/ios_vrf_address_family/tasks/cli.yaml b/tests/integration/targets/ios_vrf_address_family/tasks/cli.yaml new file mode 100644 index 000000000..86ca0d9cb --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/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) + ansible.builtin.include_tasks: "{{ test_case_to_run }}" + vars: + ansible_connection: ansible.netcommon.network_cli + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run + tags: + - network_cli diff --git a/tests/integration/targets/ios_vrf_address_family/tasks/main.yaml b/tests/integration/targets/ios_vrf_address_family/tasks/main.yaml new file mode 100644 index 000000000..f75f2f031 --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/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_address_family/tests/common/_parsed.cfg b/tests/integration/targets/ios_vrf_address_family/tests/common/_parsed.cfg new file mode 100644 index 000000000..6156b768e --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/tests/common/_parsed.cfg @@ -0,0 +1,11 @@ +vrf definition test + address-family ipv4 unicast + import ipv4 unicast 56 map import allow-evpn + export map testing-map2 + export ipv4 unicast 37 map test allow-evpn + mdt auto-discovery pim pim-tlv-announce mdt-hello-enable + mdt auto-discovery receiver-site + route-target export 10.12.0.1:20 + route-target import 10.0.0.1:30 + inter-as-hybrid next-hop 1.2.3.4 + bgp next-hop Loopback23 diff --git a/tests/integration/targets/ios_vrf_address_family/tests/common/_populate.yaml b/tests/integration/targets/ios_vrf_address_family/tests/common/_populate.yaml new file mode 100644 index 000000000..bf9888dd6 --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/tests/common/_populate.yaml @@ -0,0 +1,39 @@ +--- +- name: Populate VRF configuration + register: result + cisco.ios.ios_vrf_address_family: + config: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + allow_evpn: true + map: "test-map" + prefix: 67 + map: "testing-map" + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + allow_evpn: true + limit: 12 + map: "ran-map" + map: "import-map" + inter_as_hybrid: + csc: + next_hop: "1.2.3.4" + next_hop: "1.0.0.0" + mdt: + auto_discovery: + pim: + pim_tlv_announce: + mdt_hello_enable: true + state: merged diff --git a/tests/integration/targets/ios_vrf_address_family/tests/common/_remove_config.yaml b/tests/integration/targets/ios_vrf_address_family/tests/common/_remove_config.yaml new file mode 100644 index 000000000..b95d5216d --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/tests/common/_remove_config.yaml @@ -0,0 +1,11 @@ +--- +- name: Remove VRF global configurations + cisco.ios.ios_config: + lines: + - no vrf definition VRF4 + - no vrf definition VRF6 + - no vrf definition VRF1 + - no vrf definition test + - no vrf definition test1 + - no vrf definition test2 + register: result diff --git a/tests/integration/targets/ios_vrf_address_family/tests/common/empty_config.yaml b/tests/integration/targets/ios_vrf_address_family/tests/common/empty_config.yaml new file mode 100644 index 000000000..3833dcc82 --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/tests/common/empty_config.yaml @@ -0,0 +1,57 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_address_family 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_address_family: + 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_address_family: + config: + state: replaced + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state replaced' + +- name: Overridden with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_address_family: + config: + state: overridden + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state overridden' + +- name: Rendered with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_address_family: + 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_address_family: + config: + state: parsed + +- ansible.builtin.debug: + msg: END ios_vrf_address_family empty_config integration tests on connection={{ ansible_connection }} diff --git a/tests/integration/targets/ios_vrf_address_family/tests/common/gathered.yaml b/tests/integration/targets/ios_vrf_address_family/tests/common/gathered.yaml new file mode 100644 index 000000000..471af613a --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/tests/common/gathered.yaml @@ -0,0 +1,23 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_address_family gathered integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- ansible.builtin.include_tasks: _populate.yaml + +- block: + - name: Gather existing running configuration + register: result + cisco.ios.ios_vrf_address_family: + config: + state: gathered + + - name: Assert + ansible.builtin.assert: + that: + - result.changed == false + - gathered['after'] == result['gathered'] + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_address_family/tests/common/merged.yaml b/tests/integration/targets/ios_vrf_address_family/tests/common/merged.yaml new file mode 100644 index 000000000..1134b0974 --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/tests/common/merged.yaml @@ -0,0 +1,57 @@ +--- +- ansible.builtin.debug: + msg: Start ios_vrf_address_family merged integration tests connection={{ ansible_connection}} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Merge provided configuration with device configuration + cisco.ios.ios_vrf_address_family: &merged + config: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + export: + map: "testing-map" + import_config: + map: "import-map" + - name: test2 + address_families: + - afi: "ipv4" + safi: "unicast" + export: + map: "testing-map1" + import_config: + map: "import-map1" + inter_as_hybrid: + csc: + next_hop: "1.2.3.4" + register: result + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ merged['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - merged['before'] == {} + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - merged['after'] == result['after'] + + - name: Merge the provided configuration with the existing running configuration (idempotent) + cisco.ios.ios_vrf_address_family: *merged + register: result + + - name: Assert that the previous task was idempotent + ansible.builtin.assert: + that: + - merged['after'] == result['after'] + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_address_family/tests/common/overridden.yaml b/tests/integration/targets/ios_vrf_address_family/tests/common/overridden.yaml new file mode 100644 index 000000000..5560ec3f2 --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/tests/common/overridden.yaml @@ -0,0 +1,59 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_address_family overridden integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _populate.yaml + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Override the provided configuration with the existing running configuration + cisco.ios.ios_vrf_address_family: &overridden + config: + - name: VRF1 + address_families: + - afi: "ipv4" + safi: "unicast" + inter_as_hybrid: + csc: + next_hop: "2.3.4.5" + mdt: + auto_discovery: + pim: + pim_tlv_announce: + mdt_hello_enable: true + state: overridden + register: result + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ overridden['commands'] == result['commands'] }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - "{{ overridden['before'] == result['before'] }}" + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - "{{ overridden['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Idempotency check + cisco.ios.ios_vrf_address_family: *overridden + register: result + + - name: Assert that no changes were made + ansible.builtin.assert: + that: + - result['changed'] == false + - result.commands|length == 0 + + - name: Assert that before dict is correctly generated + ansible.builtin.assert: + that: + - "{{ overridden['after'] | symmetric_difference(result['before']) |length == 0 }}" + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_address_family/tests/common/parsed.yaml b/tests/integration/targets/ios_vrf_address_family/tests/common/parsed.yaml new file mode 100644 index 000000000..b076d3695 --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/tests/common/parsed.yaml @@ -0,0 +1,13 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_address_family parsed integration tests on connection={{ ansible_connection }} + +- name: Parse externally provided VRF configuration + register: result + cisco.ios.ios_vrf_address_family: + running_config: "{{ lookup('file', './_parsed.cfg') }}" + state: parsed + +- ansible.builtin.assert: + that: + - result.changed == false diff --git a/tests/integration/targets/ios_vrf_address_family/tests/common/rendered.yaml b/tests/integration/targets/ios_vrf_address_family/tests/common/rendered.yaml new file mode 100644 index 000000000..95ca44940 --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/tests/common/rendered.yaml @@ -0,0 +1,43 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_address_family rendered integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- name: Render provided configuration with device configuration + cisco.ios.ios_vrf_address_family: + config: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 40 + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + allow_evpn: true + map: "test-map" + prefix: 67 + map: "testing-map" + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + allow_evpn: true + limit: 12 + map: "ran-map" + map: "import-map" + state: rendered + register: result + +- name: Assert that correct set of commands were rendered + ansible.builtin.assert: + that: + - result.changed == false diff --git a/tests/integration/targets/ios_vrf_address_family/tests/common/replaced.yaml b/tests/integration/targets/ios_vrf_address_family/tests/common/replaced.yaml new file mode 100644 index 000000000..f95733af3 --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/tests/common/replaced.yaml @@ -0,0 +1,54 @@ +--- +- ansible.builtin.debug: + msg: Start ios_vrf_address_family replaced integration tests connection={{ ansible_connection}} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- ansible.builtin.include_tasks: _populate.yaml + +- block: + - name: Replace given vrf configuration with provided configurations + cisco.ios.ios_vrf_address_family: + config: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 40 + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + allow_evpn: true + map: "test-map" + prefix: 67 + map: "testing-map" + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + allow_evpn: true + limit: 12 + map: "ran-map" + map: "import-map" + state: replaced + register: result + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ replaced['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dict is correctly generated + ansible.builtin.assert: + that: + - "{{ replaced['before'] == result['before'] }}" + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_address_family/vars/main.yaml b/tests/integration/targets/ios_vrf_address_family/vars/main.yaml new file mode 100644 index 000000000..a1f45d9a4 --- /dev/null +++ b/tests/integration/targets/ios_vrf_address_family/vars/main.yaml @@ -0,0 +1,157 @@ +--- +merged: + before: {} + commands: + - vrf definition test2 + - address-family ipv4 unicast + - export map testing-map1 + - import map import-map1 + - inter-as-hybrid csc next-hop 1.2.3.4 + - vrf definition test1 + - address-family ipv4 unicast + - export map testing-map + - import map import-map + after: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + - name: test2 + address_families: + - afi: "ipv4" + safi: "unicast" + inter_as_hybrid: + csc: + next_hop: "1.2.3.4" + +replaced: + before: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + inter_as_hybrid: + next_hop: "1.0.0.0" + mdt: + auto_discovery: + pim: + pim_tlv_announce: + mdt_hello_enable: true + + commands: + - vrf definition test1 + - address-family ipv4 unicast + - export map testing-map + - import map import-map + - no inter-as-hybrid next-hop 1.0.0.0 + - no mdt auto-discovery pim pim-tlv-announce mdt-hello-enable + + after: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + +overridden: + before: {} + commands: + - vrf definition VRF1 + - address-family ipv4 unicast + - inter-as-hybrid csc next-hop 2.3.4.5 + - mdt auto-discovery pim pim-tlv-announce mdt-hello-enable + after: + - name: VRF1 + address_families: + - afi: "ipv4" + safi: "unicast" + inter_as_hybrid: + csc: + next_hop: "2.3.4.5" + mdt: + auto_discovery: + pim: + pim_tlv_announce: + mdt_hello_enable: true + +gathered: + after: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + inter_as_hybrid: + next_hop: "1.0.0.0" + mdt: + auto_discovery: + pim: + pim_tlv_announce: + mdt_hello_enable: true + +parsed: + after: + - address_families: + - afi: "ipv4" + bgp: + next_hop: + loopback: 23 + export: + ipv4: + unicast: + allow_evpn: true + map: "test" + prefix: 37 + map: "testing-map2" + import_config: + ipv4: + unicast: + allow_evpn: true + limit: 56 + map: "import" + inter_as_hybrid: + next_hop: "1.2.3.4" + mdt: + auto_discovery: + pim: + pim_tlv_announce: + mdt_hello_enable: true + receiver_site: true + route_target: + export: "10.12.0.1:20" + import_config: "10.0.0.1:30" + safi: "unicast" + - name: test + +deleted: + before: + - name: test1 + address_families: + - afi: "ipv4" + safi: "unicast" + bgp: + next_hop: + loopback: 40 + export: + ipv4: + multicast: + map: "single" + prefix: 345 + unicast: + allow_evpn: true + map: "test-map" + prefix: 67 + map: "testing-map" + import_config: + ipv4: + multicast: + map: "import-map" + prefix: 89 + unicast: + allow_evpn: true + limit: 12 + map: "ran-map" + map: "import-map" + commands: + - vrf definition test1 + - no address-family ipv4 unicast + after: + - name: test1 diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py new file mode 100644 index 000000000..eeda0eed7 --- /dev/null +++ b/tests/integration/test_integration.py @@ -0,0 +1,41 @@ +import subprocess + +import pytest + + +def run(ansible_project, environment): + __tracebackhide__ = True + args = [ + "ansible-navigator", + "run", + str(ansible_project.playbook), + "-i", + str(ansible_project.inventory), + "--ee", + "false", + "--mode", + "stdout", + "--pas", + str(ansible_project.playbook_artifact), + "--ll", + "debug", + "--lf", + str(ansible_project.log_file), + ] + process = subprocess.run( + args=args, + env=environment, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + check=False, + shell=False, + ) + if process.returncode: + print(process.stdout.decode("utf-8")) + print(process.stderr.decode("utf-8")) + + pytest.fail(reason=f"Integration test failed: {ansible_project.role}") + + +def test_integration(ansible_project, environment): + run(ansible_project, environment) diff --git a/tests/unit/modules/network/ios/fixtures/ios_facts_show_ip_interface b/tests/unit/modules/network/ios/fixtures/ios_facts_show_ip_interface index e69de29bb..1cffb658e 100644 --- a/tests/unit/modules/network/ios/fixtures/ios_facts_show_ip_interface +++ b/tests/unit/modules/network/ios/fixtures/ios_facts_show_ip_interface @@ -0,0 +1,6 @@ +Tunnel1110 is up, line protocol is up + Internet address is 10.10.10.2/30 +GigabitEthernet2/5/5 is up, line protocol is up + Internet protocol processing disabled +GigabitEthernet2/5/5.1874 is deleted, line protocol is down + Internet protocol processing disabled diff --git a/tests/unit/modules/network/ios/test_ios_facts.py b/tests/unit/modules/network/ios/test_ios_facts.py index 4878d4ad7..35b396b87 100644 --- a/tests/unit/modules/network/ios/test_ios_facts.py +++ b/tests/unit/modules/network/ios/test_ios_facts.py @@ -199,3 +199,28 @@ def test_ios_facts_neighbors(self): result["ansible_facts"]["ansible_net_neighbors"]["GigabitEthernet3"], [{"host": "Rtest", "port": "Gi1", "ip": "10.3.0.3"}], ) + + def test_ios_facts_interfaces(self): + set_module_args(dict(gather_subset="interfaces")) + result = self.execute_module() + self.assertEqual( + result["ansible_facts"]["ansible_net_interfaces"]["GigabitEthernet2/5/5.1874"], + {"ipv4": [], "operstatus": "deleted"}, + ) + self.assertEqual( + result["ansible_facts"]["ansible_net_interfaces"]["Tunnel1110"], + { + "bandwidth": None, + "description": None, + "duplex": None, + "ipv4": [ + {"address": "10.10.10.2", "subnet": "30"}, + ], + "lineprotocol": "up", + "macaddress": None, + "mediatype": None, + "mtu": None, + "operstatus": "up", + "type": None, + }, + ) diff --git a/tests/unit/modules/network/ios/test_ios_vlans.py b/tests/unit/modules/network/ios/test_ios_vlans.py index 10b30e140..a48a6fd16 100644 --- a/tests/unit/modules/network/ios/test_ios_vlans.py +++ b/tests/unit/modules/network/ios/test_ios_vlans.py @@ -511,42 +511,13 @@ def test_ios_vlans_overridden(self): ) result = self.execute_module(changed=True) commands = [ - "vlan 1", - "no name default", - "no state active", - "no mtu 1500", - "shutdown", - "vlan 150", - "no name VLAN0150", - "no state active", - "no mtu 1500", - "no remote-span", - "shutdown", - "vlan 888", - "no name a_very_long_vlan_name_a_very_long_vlan_name", - "no state active", - "no mtu 1500", - "shutdown", - "vlan 1002", - "no name fddi-default", - "no state active", - "no mtu 1500", - "shutdown", - "vlan 1003", - "no name trcrf-default", - "no state active", - "no mtu 4472", - "shutdown", - "vlan 1004", - "no name fddinet-default", - "no state active", - "no mtu 1500", - "shutdown", - "vlan 1005", - "no name trbrf-default", - "no state active", - "no mtu 4472", - "shutdown", + "no vlan 1", + "no vlan 150", + "no vlan 888", + "no vlan 1002", + "no vlan 1003", + "no vlan 1004", + "no vlan 1005", "vlan 200", "name test_vlan_200", "state active", @@ -558,6 +529,13 @@ def test_ios_vlans_overridden(self): "no mtu 610", "remote-span", "shutdown", + "no vlan configuration 1", + "no vlan configuration 150", + "no vlan configuration 888", + "no vlan configuration 1002", + "no vlan configuration 1003", + "no vlan configuration 1004", + "no vlan configuration 1005", ] self.assertEqual(result["commands"], commands) @@ -1225,14 +1203,14 @@ def test_ios_vlans_config_overridden(self): ) result = self.execute_module(changed=True) commands = [ - "vlan configuration 201", - "no member evpn-instance 201 vni 10201", - "vlan configuration 202", - "no member evpn-instance 202 vni 10202", - "vlan configuration 901", - "no member vni 50901", - "vlan configuration 902", - "no member vni 50902", + "no vlan 201", + "no vlan 202", + "no vlan 901", + "no vlan 902", + "no vlan configuration 201", + "no vlan configuration 202", + "no vlan configuration 901", + "no vlan configuration 902", "vlan configuration 101", "member evpn-instance 102 vni 10102", "vlan configuration 102", @@ -1299,7 +1277,7 @@ def test_ios_purged_vlans_config(self): ), ) result = self.execute_module(changed=True) - commands = ["no vlan configuration 101"] + commands = ["no vlan 101", "no vlan configuration 101"] self.assertEqual(result["commands"], commands) def test_ios_vlans_config_rendered(self): diff --git a/tests/unit/modules/network/ios/test_ios_vrf_address_family.py b/tests/unit/modules/network/ios/test_ios_vrf_address_family.py new file mode 100644 index 000000000..8af6c81eb --- /dev/null +++ b/tests/unit/modules/network/ios/test_ios_vrf_address_family.py @@ -0,0 +1,407 @@ +# (c) 2021 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 . + +# Make coding more python3-ish + +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_address_family +from ansible_collections.cisco.ios.tests.unit.modules.utils import set_module_args + +from .ios_module import TestIosModule + + +class TestIosVrfAddressFamilyModule(TestIosModule): + """Tests the ios_vrf_address_family module.""" + + module = ios_vrf_address_family + + def setUp(self): + """Setup for ios_vrf_address_family module tests.""" + super(TestIosVrfAddressFamilyModule, 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_address_family.vrf_address_family." + "Vrf_address_familyFacts.get_config", + ) + self.get_config = self.mock_get_config.start() + + def tearDown(self): + """Tear down for ios_vrf_address_family module tests.""" + super(TestIosVrfAddressFamilyModule, self).tearDown() + self.get_resource_connection.stop() + self.get_config.stop() + + def test_ios_vrf_address_family_merged_idempotent(self): + """Test the idempotent nature of the ios_vrf_address_family module in merged state.""" + run_cfg = dedent( + """\ + vrf definition test + address-family ipv4 unicast + import map "import-map" + export map "testing-map" + export ipv4 unicast 37 map test allow-evpn + inter-as-hybrid csc next-hop 1.2.3.4 + route-target export 10.12.0.1:20 + route-target import 10.0.0.1:30 + mdt auto-discovery ingress-replication inter-as mdt-hello-enable + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + dict( + name="test", + address_families=[ + dict( + afi="ipv4", + safi="unicast", + bgp=dict(next_hop=dict(loopback=23)), + export=dict( + map="testing-map", + ipv4=dict( + unicast=dict( + prefix=37, + map="test", + allow_evpn=True, + ), + ), + ), + import_config=dict( + map="import-map", + ), + inter_as_hybrid=dict( + csc=dict(next_hop="1.2.3.4"), + ), + route_target=dict( + export="10.12.0.1:20", + import_config="10.0.0.1:30", + ), + mdt=dict( + auto_discovery=dict( + ingress_replication=dict( + inter_as=dict(mdt_hello_enable=True), + ), + ), + ), + ), + ], + ), + ], + state="merged", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_ios_vrf_address_family_merged(self): + """Test the merged state of the ios_vrf_address_family module.""" + set_module_args( + dict( + config=[ + dict( + name="VRF1", + address_families=[ + dict( + afi="ipv4", + safi="unicast", + export=dict( + map="testing-map", + ), + import_config=dict( + map="import-map", + ), + inter_as_hybrid=dict( + csc=dict(next_hop="1.2.3.4"), + ), + route_target=dict( + export="10.12.0.1:20", + import_config="10.0.0.1:10", + ), + ), + ], + ), + ], + state="merged", + ), + ) + commands = [ + "vrf definition VRF1", + "address-family ipv4 unicast", + "export map testing-map", + "import map import-map", + "inter-as-hybrid csc next-hop 1.2.3.4", + "route-target export 10.12.0.1:20", + "route-target import 10.0.0.1:10", + ] + result = self.execute_module(changed=True) + self.assertEqual((result["commands"]), (commands)) + + def test_ios_vrf_address_family_replaced(self): + """Test the replaced state of the ios_vrf_address_family module.""" + run_cfg = dedent( + """\ + vrf definition VRF1 + address-family ipv4 unicast + import map "import-map" + export map "testing-map" + inter-as-hybrid csc next-hop 1.2.3.4 + route-target export 10.12.0.1:20 + route-target import 10.0.0.1:10 + exit-address-family + """, + ) + self.get_config.return_value = run_cfg + + set_module_args( + dict( + config=[ + dict( + name="VRF2", + address_families=[ + dict( + afi="ipv4", + safi="unicast", + export=dict( + map="testing-map", + ), + import_config=dict( + map="import-map", + ), + inter_as_hybrid=dict( + csc=dict(next_hop="1.2.3.4"), + ), + route_target=dict( + export="10.12.0.1:20", + import_config="10.0.0.1:10", + ), + ), + ], + ), + ], + state="replaced", + ), + ) + commands = [ + "vrf definition VRF2", + "address-family ipv4 unicast", + "export map testing-map", + "import map import-map", + "inter-as-hybrid csc next-hop 1.2.3.4", + "route-target export 10.12.0.1:20", + "route-target import 10.0.0.1:10", + ] + result = self.execute_module(changed=True) + self.assertEqual((result["commands"]), (commands)) + + def test_ios_vrf_address_family_replaced_idempotent(self): + """Test the idempotent nature of the ios_vrf_address_family module in replaced state.""" + run_cfg = dedent( + """\ + vrf definition VRF2 + address-family ipv4 unicast + import map "import-map" + export map "testing-map" + inter-as-hybrid csc next-hop 1.2.3.4 + route-target export 10.12.0.1:20 + route-target import 10.0.0.1:10 + """, + ) + self.get_config.return_value = run_cfg + + set_module_args( + dict( + config=[ + dict( + name="VRF2", + address_families=[ + dict( + afi="ipv4", + safi="unicast", + export=dict( + map="testing-map", + ), + import_config=dict( + map="import-map", + ), + inter_as_hybrid=dict( + csc=dict(next_hop="1.2.3.4"), + ), + route_target=dict( + export="10.12.0.1:20", + import_config="10.0.0.1:10", + ), + ), + ], + ), + ], + state="replaced", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_ios_vrf_address_family_overridden_idempotent(self): + """Test the idempotent nature of the ios_vrf_address_family module in overridden state.""" + run_cfg = dedent( + """\ + vrf definition VRF7 + address-family ipv4 unicast + bgp next-hop loopback 40 + import map import-map1 + export map testing-map2 + inter-as-hybrid csc next-hop 1.2.3.4 + """, + ) + self.get_config.return_value = run_cfg + + set_module_args( + dict( + config=[ + dict( + name="VRF7", + address_families=[ + dict( + afi="ipv4", + safi="unicast", + bgp=dict(next_hop=dict(loopback=40)), + export=dict( + map="testing-map2", + ), + import_config=dict( + map="import-map1", + ), + inter_as_hybrid=dict( + csc=dict(next_hop="1.2.3.4"), + ), + ), + ], + ), + ], + state="overridden", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_ios_vrf_address_family_deleted_idempotent(self): + """Test the idempotent nature of the ios_vrf_address_family module in deleted state.""" + run_cfg = dedent( + """\ + """, + ) + self.get_config.return_value = run_cfg + set_module_args(dict(config=[], state="deleted")) + + result = self.execute_module(changed=False) + self.assertEqual(result["commands"], []) + + def test_ios_vrf_address_family_rendered(self): + """Test the rendered state of the ios_vrf_address_family module.""" + set_module_args( + dict( + config=[ + dict( + name="VRF1", + address_families=[ + dict( + afi="ipv4", + safi="unicast", + export=dict( + map="testing-map", + ), + import_config=dict( + map="import-map", + ), + inter_as_hybrid=dict( + csc=dict(next_hop="1.2.3.4"), + ), + ), + ], + ), + ], + state="rendered", + ), + ) + commands = [ + "vrf definition VRF1", + "address-family ipv4 unicast", + "export map testing-map", + "import map import-map", + "inter-as-hybrid csc next-hop 1.2.3.4", + ] + result = self.execute_module(changed=False) + self.assertEqual((result["rendered"]), (commands)) + + def test_ios_vrf_address_family_parsed(self): + """Test the parsed state of the ios_vrf_address_family module.""" + run_cfg = dedent( + """\ + vrf definition test + address-family ipv4 unicast + import map "import-map" + export map "testing-map" + inter-as-hybrid csc next-hop 1.2.3.4 + mdt auto-discovery ingress-replication inter-as mdt-hello-enable + exit-address-family + """, + ) + set_module_args(dict(running_config=run_cfg, state="parsed")) + result = self.execute_module(changed=False) + parsed_list = [ + { + "name": "test", + "address_families": [ + { + "afi": "ipv4", + "safi": "unicast", + "import_config": { + "map": "import-map", + }, + "export": { + "map": "testing-map", + }, + "inter_as_hybrid": { + "csc": { + "next_hop": "1.2.3.4", + }, + }, + "mdt": { + "auto_discovery": { + "ingress_replication": { + "inter_as": { + "mdt_hello_enable": True, + }, + }, + }, + }, + }, + ], + }, + ] + + self.assertEqual(parsed_list, result["parsed"])