From 44ca7210b93259d000716c98bfc99ba0a96110ac Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 1 Sep 2022 21:12:32 +0000 Subject: [PATCH 01/26] Fix dcnm_network query tests --- .../dcnm_network/tests/dcnm/query.yaml | 91 ++++++++++--------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml index f00bf750a..e156075c2 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml @@ -25,9 +25,9 @@ fabric: "{{ ansible_it_fabric }}" state: deleted -- name: QUERY - sleep for 40 seconds for DCNM to completely update the state +- name: QUERY - sleep for 20 seconds for DCNM to completely update the state wait_for: - timeout: 40 + timeout: 20 - name: QUERY - Create, Attach and Deploy Multiple Network with Single Switch Attach cisco.dcnm.dcnm_network: @@ -43,7 +43,7 @@ gw_ip_subnet: '192.168.30.1/24' attach: - ip_address: "{{ ansible_switch1 }}" - ports: [Ethernet1/1, Ethernet1/2] + ports: [Ethernet1/2, Ethernet1/3] deploy: true - net_name: ansible-net12 vrf_name: Tenant-2 @@ -54,7 +54,7 @@ gw_ip_subnet: '192.168.40.1/24' attach: - ip_address: "{{ ansible_switch2 }}" - ports: [Ethernet1/11, Ethernet1/12] + ports: [Ethernet1/12, Ethernet1/13] deploy: true deploy: false register: result @@ -72,12 +72,12 @@ - '"{{ ansible_switch1 }}" in result.diff[0].attach[0].ip_address' - 'result.diff[0].net_name == "ansible-net13"' - 'result.diff[0].net_id == 7005' - - 'result.diff[0].vrf_name == "Tenant-1"' + - 'result.diff[0].vrf_name | regex_search("Tenant-[1|2]", ignorecase=True)' - 'result.diff[1].attach[0].deploy == true' - '"{{ ansible_switch2 }}" in result.diff[1].attach[0].ip_address' - 'result.diff[1].net_name == "ansible-net12"' - 'result.diff[1].net_id == 7002' - - 'result.diff[1].vrf_name == "Tenant-2"' + - 'result.diff[1].vrf_name | regex_search("Tenant-[1|2]", ignorecase=True)' - name: QUERY - sleep for 20 seconds for DCNM to completely update the state wait_for: @@ -112,9 +112,9 @@ that: - 'result.changed == false' - 'result.response[0].parent.networkName == "ansible-net13"' - - 'result.response[0].parent.networkId == 7005' + - 'result.response[0].parent.networkId | regex_search("700[2|5]", ignorecase=True)' - 'result.response[0].parent.networkTemplate == "Default_Network_Universal"' - - 'result.response[0].parent.vrf == "Tenant-1"' + - 'result.response[0].parent.vrf | regex_search("Tenant-[1|2]", ignorecase=True)' - 'result.response[0].attach[0].isLanAttached== true' - 'result.response[0].attach[0].lanAttachState== "DEPLOYED"' - 'result.response[0].attach[0].networkName== "ansible-net13"' @@ -122,9 +122,9 @@ - 'result.response[0].attach[1].lanAttachState== "NA"' - 'result.response[0].attach[1].networkName== "ansible-net13"' - 'result.response[1].parent.networkName == "ansible-net12"' - - 'result.response[1].parent.networkId == 7002' + - 'result.response[1].parent.networkId | regex_search("700[2|5]", ignorecase=True)' - 'result.response[1].parent.networkTemplate == "Default_Network_Universal"' - - 'result.response[1].parent.vrf == "Tenant-2"' + - 'result.response[1].parent.vrf | regex_search("Tenant-[1|2]", ignorecase=True)' - 'result.response[1].attach[0].isLanAttached== true' - 'result.response[1].attach[0].lanAttachState== "DEPLOYED"' - 'result.response[1].attach[0].networkName== "ansible-net12"' @@ -145,26 +145,27 @@ - assert: that: - 'result.changed == false' - - 'result.response[0].parent.networkName == "ansible-net13"' - - 'result.response[0].parent.networkId == 7005' + - 'result.response[0].parent.networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' + - 'result.response[0].parent.networkId | regex_search("700[2|5]", ignorecase=True)' - 'result.response[0].parent.networkTemplate == "Default_Network_Universal"' - - 'result.response[0].parent.vrf == "Tenant-1"' + - 'result.response[0].parent.vrf | regex_search("Tenant-[1|2]", ignorecase=True)' - 'result.response[0].attach[0].isLanAttached== true' - 'result.response[0].attach[0].lanAttachState== "DEPLOYED"' - - 'result.response[0].attach[0].networkName== "ansible-net13"' + - 'result.response[0].attach[0].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' - 'result.response[0].attach[1].isLanAttached== false' - 'result.response[0].attach[1].lanAttachState== "NA"' - - 'result.response[0].attach[1].networkName== "ansible-net13"' - - 'result.response[1].parent.networkName == "ansible-net12"' - - 'result.response[1].parent.networkId == 7002' + - 'result.response[0].attach[1].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' + - 'result.response[1].parent.networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' + - 'result.response[1].parent.networkId | regex_search("700[2|5]", ignorecase=True)' - 'result.response[1].parent.networkTemplate == "Default_Network_Universal"' - - 'result.response[1].parent.vrf == "Tenant-2"' + - 'result.response[1].parent.vrf | regex_search("Tenant-[1|2]", ignorecase=True)' - 'result.response[1].attach[0].isLanAttached== true' - 'result.response[1].attach[0].lanAttachState== "DEPLOYED"' - - 'result.response[1].attach[0].networkName== "ansible-net12"' + - 'result.response[1].attach[0].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' - 'result.response[1].attach[1].isLanAttached== false' - 'result.response[1].attach[1].lanAttachState== "NA"' - - 'result.response[1].attach[1].networkName== "ansible-net12"' + - 'result.response[1].attach[1].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' + - name: QUERY - Delete all the networks cisco.dcnm.dcnm_network: @@ -193,9 +194,9 @@ - '"ansible-net13" or "ansible-net12" in result.diff[1].net_name' - '"ansible-net13" or "ansible-net12" in result.diff[0].net_name' -- name: QUERY - sleep for 40 seconds for DCNM to completely update the state +- name: QUERY - sleep for 20 seconds for DCNM to completely update the state wait_for: - timeout: 40 + timeout: 20 - name: QUERY - Query the non available Network cisco.dcnm.dcnm_network: @@ -298,9 +299,9 @@ - 'result.diff[0].dhcp_srvr3_ip == "3.3.3.3"' - 'result.diff[0].dhcp_srvr3_vrf == "three"' - '"{{ ansible_switch2 }}" in result.diff[1].attach[0].ip_address' - - 'result.diff[1].net_name == "ansible-net12"' + - 'result.diff[1].net_name | regex_search("ansible-net1[2|3]", ignorecase=True)' - 'result.diff[1].net_id == 7010' - - 'result.diff[1].vrf_name == "Tenant-2"' + - 'result.diff[1].vrf_name | regex_search("Tenant-[1|2]", ignorecase=True)' - 'result.diff[1].int_desc == "test interface 1"' - 'result.diff[1].mtu_l3intf == 7600' - 'result.diff[1].vlan_name == "testvlan1"' @@ -311,9 +312,9 @@ - 'result.diff[1].dhcp_srvr3_ip == "3.3.3.3"' - 'result.diff[1].dhcp_srvr3_vrf == "three"' -- name: QUERY - sleep for 40 seconds for DCNM to completely update the state +- name: QUERY - sleep for 20 seconds for DCNM to completely update the state wait_for: - timeout: 40 + timeout: 20 - name: QUERY - Query the L2 and L3 Network cisco.dcnm.dcnm_network: @@ -358,8 +359,8 @@ - assert: that: - 'result.changed == false' - - 'result.response[0].parent.networkName == "ansible-net13"' - - 'result.response[0].parent.networkId == 7009' + - 'result.response[0].parent.networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' + - 'result.response[0].parent.networkId | regex_search("70[0|1][0|9]", ignorecase=True)' - 'result.response[0].parent.networkTemplate == "Default_Network_Universal"' - 'result.response[0].parent.vrf == "NA"' - 'result.response[0].parent.networkTemplateConfig.suppressArp == "true"' @@ -375,14 +376,14 @@ - 'result.response[0].parent.networkTemplateConfig.vrfName == "NA"' - 'result.response[0].attach[0].isLanAttached== true' - 'result.response[0].attach[0].lanAttachState== "DEPLOYED"' - - 'result.response[0].attach[0].networkName== "ansible-net13"' + - 'result.response[0].attach[0].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' - 'result.response[0].attach[1].isLanAttached== false' - 'result.response[0].attach[1].lanAttachState== "NA"' - - 'result.response[0].attach[1].networkName== "ansible-net13"' - - 'result.response[1].parent.networkName == "ansible-net12"' - - 'result.response[1].parent.networkId == 7010' + - 'result.response[0].attach[1].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' + - 'result.response[1].parent.networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' + - 'result.response[1].parent.networkId | regex_search("70[0|1][0|9]", ignorecase=True)' - 'result.response[1].parent.networkTemplate == "Default_Network_Universal"' - - 'result.response[1].parent.vrf == "Tenant-2"' + - 'result.response[1].parent.vrf | regex_search("Tenant-[1|2]", ignorecase=True)' - 'result.response[1].parent.networkTemplateConfig.suppressArp == "false"' - 'result.response[1].parent.networkTemplateConfig.isLayer2Only == "false"' - 'result.response[1].parent.networkTemplateConfig.intfDescription == "test interface 1"' @@ -396,10 +397,10 @@ - 'result.response[1].parent.networkTemplateConfig.vrfName == "Tenant-2"' - 'result.response[1].attach[0].isLanAttached== true' - 'result.response[1].attach[0].lanAttachState== "DEPLOYED"' - - 'result.response[1].attach[0].networkName== "ansible-net12"' + - 'result.response[1].attach[0].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' - 'result.response[1].attach[1].isLanAttached== false' - 'result.response[1].attach[1].lanAttachState== "NA"' - - 'result.response[1].attach[1].networkName== "ansible-net12"' + - 'result.response[1].attach[1].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' - name: QUERY - Query L2 and L3 the Network without the config element cisco.dcnm.dcnm_network: @@ -410,10 +411,10 @@ - assert: that: - 'result.changed == false' - - 'result.response[1].parent.networkName == "ansible-net13"' - - 'result.response[1].parent.networkId == 7009' + - 'result.response[1].parent.networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' + - 'result.response[1].parent.networkId | regex_search("70[0|1][0|9]", ignorecase=True)' - 'result.response[1].parent.networkTemplate == "Default_Network_Universal"' - - 'result.response[1].parent.vrf == "NA"' + - 'result.response[1].parent.vrf | regex_search("NA|Tenant-[1|2]", ignorecase=True)' - 'result.response[1].parent.networkTemplateConfig.suppressArp == "true"' - 'result.response[1].parent.networkTemplateConfig.isLayer2Only == "true"' - 'result.response[1].parent.networkTemplateConfig.intfDescription == "test interface"' @@ -427,14 +428,14 @@ - 'result.response[1].parent.networkTemplateConfig.vrfName == "NA"' - 'result.response[1].attach[0].isLanAttached== true' - 'result.response[1].attach[0].lanAttachState== "DEPLOYED"' - - 'result.response[1].attach[0].networkName== "ansible-net13"' + - 'result.response[1].attach[0].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' - 'result.response[1].attach[1].isLanAttached== false' - 'result.response[1].attach[1].lanAttachState== "NA"' - - 'result.response[1].attach[1].networkName== "ansible-net13"' - - 'result.response[0].parent.networkName == "ansible-net12"' - - 'result.response[0].parent.networkId == 7010' + - 'result.response[1].attach[1].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' + - 'result.response[0].parent.networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' + - 'result.response[0].parent.networkId | regex_search("70[0|1][0|9]", ignorecase=True)' - 'result.response[0].parent.networkTemplate == "Default_Network_Universal"' - - 'result.response[0].parent.vrf == "Tenant-2"' + - 'result.response[0].parent.vrf | regex_search("Tenant-[1|2]", ignorecase=True)' - 'result.response[0].parent.networkTemplateConfig.suppressArp == "false"' - 'result.response[0].parent.networkTemplateConfig.isLayer2Only == "false"' - 'result.response[0].parent.networkTemplateConfig.intfDescription == "test interface 1"' @@ -448,10 +449,10 @@ - 'result.response[0].parent.networkTemplateConfig.vrfName == "Tenant-2"' - 'result.response[0].attach[0].isLanAttached== true' - 'result.response[0].attach[0].lanAttachState== "DEPLOYED"' - - 'result.response[0].attach[0].networkName== "ansible-net12"' + - 'result.response[0].attach[0].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' - 'result.response[0].attach[1].isLanAttached== false' - 'result.response[0].attach[1].lanAttachState== "NA"' - - 'result.response[0].attach[1].networkName== "ansible-net12"' + - 'result.response[0].attach[1].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' ############################################### ### CLEAN-UP ## From d425a62788e78a834655f60df65dcb3e9ffa2a7a Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 1 Sep 2022 23:32:16 +0000 Subject: [PATCH 02/26] Fix dcnm_network query test timeout --- tests/integration/targets/dcnm_network/tests/dcnm/query.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml index e156075c2..7367c4dec 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml @@ -312,9 +312,9 @@ - 'result.diff[1].dhcp_srvr3_ip == "3.3.3.3"' - 'result.diff[1].dhcp_srvr3_vrf == "three"' -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state +- name: QUERY - sleep for 40 seconds for DCNM to completely update the state wait_for: - timeout: 20 + timeout: 40 - name: QUERY - Query the L2 and L3 Network cisco.dcnm.dcnm_network: From 67a0b00044df8cff4201c3a7840ae3555aa3a040 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Tue, 6 Sep 2022 17:00:41 +0000 Subject: [PATCH 03/26] Increase timeouts --- .../targets/dcnm_network/tests/dcnm/query.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml index 7367c4dec..469a25bf1 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml @@ -79,9 +79,9 @@ - 'result.diff[1].net_id == 7002' - 'result.diff[1].vrf_name | regex_search("Tenant-[1|2]", ignorecase=True)' -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state +- name: QUERY - sleep for 40 seconds for DCNM to completely update the state wait_for: - timeout: 20 + timeout: 40 ############################################### ### QUERY ## @@ -132,9 +132,9 @@ - 'result.response[1].attach[1].lanAttachState== "NA"' - 'result.response[1].attach[1].networkName== "ansible-net12"' -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state +- name: QUERY - sleep for 40 seconds for DCNM to completely update the state wait_for: - timeout: 20 + timeout: 40 - name: QUERY - Query the Network without the config element cisco.dcnm.dcnm_network: @@ -194,9 +194,9 @@ - '"ansible-net13" or "ansible-net12" in result.diff[1].net_name' - '"ansible-net13" or "ansible-net12" in result.diff[0].net_name' -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state +- name: QUERY - sleep for 40 seconds for DCNM to completely update the state wait_for: - timeout: 20 + timeout: 40 - name: QUERY - Query the non available Network cisco.dcnm.dcnm_network: From 6cd12acccb65f82d1d48c90ea8391149eb866ee1 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Tue, 6 Sep 2022 17:22:34 +0000 Subject: [PATCH 04/26] Add saftey check for isAttached key --- plugins/modules/dcnm_network.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/modules/dcnm_network.py b/plugins/modules/dcnm_network.py index 05ef1fa29..c7ec0c69f 100644 --- a/plugins/modules/dcnm_network.py +++ b/plugins/modules/dcnm_network.py @@ -1654,8 +1654,10 @@ def get_diff_merge(self, replace=False): if not found and want_a.get("lanAttachList"): atch_list = [] for attach in want_a["lanAttachList"]: - del attach["isAttached"] - atch_list.append(attach) + # Saftey check + if attach.get("isAttached"): + del attach["isAttached"] + atch_list.append(attach) if atch_list: base = want_a.copy() del base["lanAttachList"] From 4871ac86ee2eb509f0b8f8207322d5862e857365 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Tue, 6 Sep 2022 18:06:45 +0000 Subject: [PATCH 05/26] Speed up tests --- .../dcnm_network/tests/dcnm/query.yaml | 66 ++++++++++++------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml index 469a25bf1..a2bca9dc1 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml @@ -10,7 +10,7 @@ rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ ansible_it_fabric }}" when: controller_version >= "12" -- name: QUERY - Verify if fabric - Fabric1 is deployed. +- name: Verify if fabric - Fabric1 is deployed. cisco.dcnm.dcnm_rest: method: GET path: "{{ rest_path }}" @@ -20,16 +20,17 @@ that: - 'result.response.DATA != None' -- name: QUERY - setup - Clean up any existing networks +- name: Setup - Clean up any existing networks cisco.dcnm.dcnm_network: fabric: "{{ ansible_it_fabric }}" state: deleted + register: result + until: + - "result.response | length == 0" + retries: 30 + delay: 2 -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - -- name: QUERY - Create, Attach and Deploy Multiple Network with Single Switch Attach +- name: Create, Attach and Deploy Multiple Network with Single Switch Attach cisco.dcnm.dcnm_network: fabric: "{{ ansible_it_fabric }}" state: merged @@ -59,6 +60,17 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ ansible_it_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -79,10 +91,6 @@ - 'result.diff[1].net_id == 7002' - 'result.diff[1].vrf_name | regex_search("Tenant-[1|2]", ignorecase=True)' -- name: QUERY - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - ############################################### ### QUERY ## ############################################### @@ -132,10 +140,6 @@ - 'result.response[1].attach[1].lanAttachState== "NA"' - 'result.response[1].attach[1].networkName== "ansible-net12"' -- name: QUERY - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: QUERY - Query the Network without the config element cisco.dcnm.dcnm_network: fabric: "{{ ansible_it_fabric }}" @@ -167,12 +171,22 @@ - 'result.response[1].attach[1].networkName | regex_search("ansible-net1[2|3]", ignorecase=True)' -- name: QUERY - Delete all the networks +- name: Delete all the networks cisco.dcnm.dcnm_network: fabric: "{{ ansible_it_fabric }}" state: deleted register: result +- name: Query fabric state until all networks are deleted + cisco.dcnm.dcnm_network: + fabric: "{{ ansible_it_fabric }}" + state: query + register: query_result + until: + - "query_result.response | length == 0" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -194,9 +208,6 @@ - '"ansible-net13" or "ansible-net12" in result.diff[1].net_name' - '"ansible-net13" or "ansible-net12" in result.diff[0].net_name' -- name: QUERY - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - name: QUERY - Query the non available Network cisco.dcnm.dcnm_network: @@ -224,7 +235,7 @@ - 'result.changed == false' - 'result.response|length == 0' -- name: QUERY - Create a L2 only and L3 networks along with all dhcp, arp options +- name: Create a L2 only and L3 networks along with all dhcp, arp options cisco.dcnm.dcnm_network: &conf3 fabric: "{{ ansible_it_fabric }}" state: merged @@ -273,6 +284,17 @@ deploy: True register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ ansible_it_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -312,10 +334,6 @@ - 'result.diff[1].dhcp_srvr3_ip == "3.3.3.3"' - 'result.diff[1].dhcp_srvr3_vrf == "three"' -- name: QUERY - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: QUERY - Query the L2 and L3 Network cisco.dcnm.dcnm_network: fabric: "{{ ansible_it_fabric }}" From 26593c49016c63312875744bc19c76c3abd49cec Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Sat, 10 Sep 2022 22:22:17 +0530 Subject: [PATCH 06/26] POAP support for inventory module --- docs/cisco.dcnm.dcnm_inventory_module.rst | 253 ++++- plugins/modules/dcnm_inventory.py | 421 +++++++- .../dcnm_inventory/tests/dcnm/merged.yaml | 129 ++- .../dcnm_inventory/tests/dcnm/query.yaml | 74 +- .../modules/dcnm/fixtures/dcnm_inventory.json | 911 ++++++++++++++++++ .../unit/modules/dcnm/test_dcnm_inventory.py | 237 +++++ 6 files changed, 1960 insertions(+), 65 deletions(-) diff --git a/docs/cisco.dcnm.dcnm_inventory_module.rst b/docs/cisco.dcnm.dcnm_inventory_module.rst index 6a1f8b019..5f7de51bf 100644 --- a/docs/cisco.dcnm.dcnm_inventory_module.rst +++ b/docs/cisco.dcnm.dcnm_inventory_module.rst @@ -29,12 +29,12 @@ Parameters - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + +
ParameterParameter Choices/Defaults Comments
+
config @@ -51,18 +51,17 @@ Parameters
+
auth_proto
string - / required
    Choices: -
  • MD5
  • +
  • MD5 ←
  • SHA
  • MD5_DES
  • MD5_AES
  • @@ -71,21 +70,21 @@ Parameters
-
Name of the authentication protocol to be used
+
Name of the authentication protocol to be used. For POAP configurations authentication protocol should be 'MD5'.
+
max_hops
string - / required
+ Default:
0
Maximum Hops to reach the switch
@@ -93,7 +92,7 @@ Parameters
+
password @@ -110,9 +109,45 @@ Parameters
+
+ poap + +
+ list + / elements=dictionary +
+
+ +
Configurations of switch to Bootstrap/Pre-provision.
+
- preserve_configs + config_data + +
+ dictionary + / required +
+
+ +
Basic config data of switch to Bootstrap/Pre-provision. 'modulesModel' and 'gateway' parameters are mandatory. 'modulesModel' is list of model of modules in switch to Bootstrap/Pre-provision. 'gateway' is the gateway IP with mask for the switch to Bootstrap/Pre-provision. For other supported config data please refer to NDFC/DCNM configuration guide.
+
+
+ hostname
string @@ -122,20 +157,94 @@ Parameters
-
Set this to false for greenfield deployment and true for brownfield deployment
+
Hostname of switch to Bootstrap/Pre-provision.
- role + model + +
+ string + / required +
+
+ +
Model of switch to Bootstrap/Pre-provision.
+
+
+ serial_number
string / required
+ +
Serial number of switch to Bootstrap/Pre-provision.
+
+
+ version + +
+ string + / required +
+
+ +
Software version of switch to Bootstrap/Pre-provision.
+
+
+ preserve_config + +
+ boolean +
+
+
    Choices: +
  • no ←
  • +
  • yes
  • +
+
+
Set this to false for greenfield deployment and true for brownfield deployment
+
+
+ role + +
+ string +
+
    Choices:
  • leaf ←
  • @@ -155,7 +264,7 @@ Parameters
+
seed_ip @@ -172,7 +281,7 @@ Parameters
+
user_name @@ -184,12 +293,12 @@ Parameters
-
Login username to the switch
+
Login username to the switch. For POAP configurations username should be 'admin'
+
fabric @@ -205,7 +314,26 @@ Parameters
+ +
+ query_poap + +
+ boolean +
+
+
    Choices: +
  • no ←
  • +
  • yes
  • +
+
+
Query for Bootstrap(POAP) capable swicthes available.
+
state @@ -222,7 +350,7 @@ Parameters
-
The state of DCNM after module completion.
+
The state of DCNM after module completion. 'merged' and 'query' are the only states supported for POAP
@@ -321,6 +449,85 @@ Examples role: leaf preserve_config: False # boolean, default is true + # The following switch will be Bootstrapped and merged into the existing fabric + - name: Poap switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: 192.168.0.5 + user_name: switch_username + password: switch_password + role: border_gateway + poap: + - serial_number: 1A2BCDEFJKL + model: 'N9K-C9300v' + version: '9.3(7)' + hostname: 'POAP_SWITCH' + config_data: + modulesModel: [N9K-X9364v, N9K-vSUP] + gateway: 192.168.0.1/24 + + # The following switch will be Pre-provisioned and merged into the existing fabric + - name: Pre-provision switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: 192.168.0.4 + user_name: switch_username + password: switch_password + role: border + poap: + - serial_number: 1A2BCDEFGHI + model: 'N9K-C9300v' + version: '9.3(7)' + hostname: 'PREPRO_SWITCH' + config_data: + modulesModel: [N9K-X9364v, N9K-vSUP] + gateway: 192.168.0.1/24 + preprovision: True + + - name: Poap, Pre-provision and existing switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: merged # Only 2 options supported merged/query for poap config + config: + - seed_ip: 192.168.0.2 + user_name: switch_username + password: switch_password + role: border_gateway + poap: + - serial_number: 1A2BCDEFGHI + model: 'N9K-C9300v' + version: '9.3(7)' + hostname: 'POAP_SWITCH' + config_data: + modulesModel: [N9K-X9364v, N9K-vSUP] + gateway: 192.168.0.1/24 + - seed_ip: 192.168.0.3 + user_name: switch_username + password: switch_password + auth_proto: MD5 + max_hops: 0 + preserve_config: False + role: spine + - seed_ip: 192.168.0.4 + user_name: switch_username + password: switch_password + role: border + poap: + - serial_number: 1A2BCDEFGHI + model: 'N9K-C9300v' + version: '9.3(7)' + hostname: 'PREPRO_SWITCH' + config_data: + modulesModel: [N9K-X9364v, N9K-vSUP] + gateway: 192.168.0.1/24 + preprovision: True + # All the switches will be deleted in the existing fabric - name: Delete all the switches cisco.dcnm.dcnm_inventory: @@ -344,6 +551,14 @@ Examples fabric: vxlan-fabric state: query # merged / deleted / overridden / query + # All the existing switches along with available Bootstrap(POAP) + # will be queried in the existing fabric + - name: Query all the switches in the fabric + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: query # merged / query + query_poap: True + @@ -354,4 +569,4 @@ Status Authors ~~~~~~~ -- Karthik Babu Harichandra Babu(@kharicha) +- Karthik Babu Harichandra Babu(@kharicha), Praveen Ramoorthy(@praveenramoorthy) diff --git a/plugins/modules/dcnm_inventory.py b/plugins/modules/dcnm_inventory.py index 7225be3be..0dd5ca375 100644 --- a/plugins/modules/dcnm_inventory.py +++ b/plugins/modules/dcnm_inventory.py @@ -16,7 +16,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -__author__ = "Karthik Babu Harichandra Babu" +__author__ = "Karthik Babu Harichandra Babu, Praveen Ramoorthy" DOCUMENTATION = """ --- @@ -25,7 +25,7 @@ version_added: "0.9.0" description: - "Add and remove Switches from a DCNM managed VXLAN fabric." -author: Karthik Babu Harichandra Babu(@kharicha) +author: Karthik Babu Harichandra Babu(@kharicha), Praveen Ramoorthy(@praveenramoorthy) options: fabric: description: @@ -35,6 +35,7 @@ state: description: - The state of DCNM after module completion. + 'merged' and 'query' are the only states supported for POAP type: str choices: - merged @@ -56,13 +57,16 @@ required: true auth_proto: description: - - Name of the authentication protocol to be used + - Name of the authentication protocol to be used. + For POAP configurations authentication protocol should be 'MD5'. choices: ['MD5', 'SHA', 'MD5_DES', 'MD5_AES', 'SHA_DES', 'SHA_AES'] type: str - required: true + required: false + default: 'MD5' user_name: description: - - Login username to the switch + - Login username to the switch. + For POAP configurations username should be 'admin' type: str required: true password: @@ -74,20 +78,63 @@ description: - Maximum Hops to reach the switch type: str - required: true + required: false + default: 0 role: description: - Role which needs to be assigned to the switch choices: ['leaf', 'spine', 'border', 'border_spine', 'border_gateway', 'border_gateway_spine', 'super_spine', 'border_super_spine', 'border_gateway_super_spine'] type: str - required: true + required: false default: leaf - preserve_configs: + preserve_config: description: - Set this to false for greenfield deployment and true for brownfield deployment - type: str - required: true + type: bool + required: false + default: false + poap: + description: + - Configurations of switch to Bootstrap/Pre-provision. + type: list + elements: dict + suboptions: + serial_number: + description: + - Serial number of switch to Bootstrap/Pre-provision. + type: str + required: true + model: + description: + - Model of switch to Bootstrap/Pre-provision. + type: str + required: true + version: + description: + - Software version of switch to Bootstrap/Pre-provision. + type: str + required: true + hostname: + description: + - Hostname of switch to Bootstrap/Pre-provision. + type: str + required: true + config_data: + description: + - Basic config data of switch to Bootstrap/Pre-provision. + 'modulesModel' and 'gateway' parameters are mandatory. + 'modulesModel' is list of model of modules in switch to Bootstrap/Pre-provision. + 'gateway' is the gateway IP with mask for the switch to Bootstrap/Pre-provision. + For other supported config data please refer to NDFC/DCNM configuration guide. + type: dict + required: true + query_poap: + description: + - Query for Bootstrap(POAP) capable swicthes available. + type: bool + required: false + default: false """ EXAMPLES = """ @@ -176,6 +223,85 @@ role: leaf preserve_config: False # boolean, default is true +# The following switch will be Bootstrapped and merged into the existing fabric +- name: Poap switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: 192.168.0.5 + user_name: switch_username + password: switch_password + role: border_gateway + poap: + - serial_number: 1A2BCDEFJKL + model: 'N9K-C9300v' + version: '9.3(7)' + hostname: 'POAP_SWITCH' + config_data: + modulesModel: [N9K-X9364v, N9K-vSUP] + gateway: 192.168.0.1/24 + +# The following switch will be Pre-provisioned and merged into the existing fabric +- name: Pre-provision switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: 192.168.0.4 + user_name: switch_username + password: switch_password + role: border + poap: + - serial_number: 1A2BCDEFGHI + model: 'N9K-C9300v' + version: '9.3(7)' + hostname: 'PREPRO_SWITCH' + config_data: + modulesModel: [N9K-X9364v, N9K-vSUP] + gateway: 192.168.0.1/24 + preprovision: True + +- name: Poap, Pre-provision and existing switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: merged # Only 2 options supported merged/query for poap config + config: + - seed_ip: 192.168.0.2 + user_name: switch_username + password: switch_password + role: border_gateway + poap: + - serial_number: 1A2BCDEFGHI + model: 'N9K-C9300v' + version: '9.3(7)' + hostname: 'POAP_SWITCH' + config_data: + modulesModel: [N9K-X9364v, N9K-vSUP] + gateway: 192.168.0.1/24 + - seed_ip: 192.168.0.3 + user_name: switch_username + password: switch_password + auth_proto: MD5 + max_hops: 0 + preserve_config: False + role: spine + - seed_ip: 192.168.0.4 + user_name: switch_username + password: switch_password + role: border + poap: + - serial_number: 1A2BCDEFGHI + model: 'N9K-C9300v' + version: '9.3(7)' + hostname: 'PREPRO_SWITCH' + config_data: + modulesModel: [N9K-X9364v, N9K-vSUP] + gateway: 192.168.0.1/24 + preprovision: True + # All the switches will be deleted in the existing fabric - name: Delete all the switches cisco.dcnm.dcnm_inventory: @@ -198,6 +324,14 @@ cisco.dcnm.dcnm_inventory: fabric: vxlan-fabric state: query # merged / deleted / overridden / query + +# All the existing switches along with available Bootstrap(POAP) +# will be queried in the existing fabric +- name: Query all the switches in the fabric + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: query # merged / query + query_poap: True """ import time @@ -226,12 +360,14 @@ def __init__(self, module): self.validated = [] self.have_create = [] self.want_create = [] + self.want_create_poap = [] self.diff_create = [] self.diff_save = {} self.diff_delete = {} self.query = [] self.node_migration = False self.nd_prefix = "/appcenter/cisco/ndfc/api/v1/lan-fabric" + self.switch_snos = [] self.result = dict(changed=False, diff=[], response=[]) @@ -262,8 +398,72 @@ def update_discover_params(self, inv): else: return 0 + def discover_poap_params(self, poap_upd, poap): + + method = "GET" + path = "/rest/control/fabrics/{0}/inventory/poap".format( + self.fabric + ) + if self.nd: + path = self.nd_prefix + path + response = dcnm_send(self.module, method, path) + self.result["response"].append(response) + fail, self.result["changed"] = self.handle_response(response, "query") + + if fail: + self.module.fail_json(msg=response) + + if "DATA" in response: + for resp in response["DATA"]: + if (resp["serialNumber"] == poap["serial_number"]): + if self.nd: + poap_upd.update({"publicKey": resp["publicKey"]}) + poap_upd.update({"reAdd": resp["reAdd"]}) + poap_upd.update({"fingerprint": resp["fingerprint"]}) + poap_upd.update({"imagePolicy": ""}) + #poap_upd.update({"role": "leaf"}) + return + + msg = ("Specified switch is not listed in bootstrap devices. " + "If you are trying to pre-provision, please set " + "'preprovision' to 'True' in your task") + + self.module.fail_json(msg) + + def update_poap_params(self, inv, poap): + + snos = {} + s_ip = "None" + if inv["seed_ip"]: + s_ip = dcnm_get_ip_addr_info(self.module, inv["seed_ip"], None, None) + + state = self.params["state"] + + if state == "merged": + poap_upd = { + "ipAddress": s_ip, + "discoveryAuthProtocol": "0", + "password": inv["password"], + "hostname": poap["hostname"], + "model": poap["model"], + "serialNumber": poap["serial_number"], + "version": poap["version"], + "data": json.dumps(poap["config_data"]), + "role": inv["role"].replace(" ", "_"), + } + + if poap["preprovision"] is False: + self.discover_poap_params(poap_upd, poap) + + snos = {"serialno": poap["serial_number"], "preprovision": poap["preprovision"]} + self.switch_snos.append(snos) + + + return poap_upd + def update_create_params(self, inv): + snos = {} s_ip = "None" if inv["seed_ip"]: s_ip = dcnm_get_ip_addr_info(self.module, inv["seed_ip"], None, None) @@ -305,6 +505,16 @@ def update_create_params(self, inv): resp = self.update_discover_params(inv_upd) + if (type(resp[0]) is dict): + match = re.search(r"\S+\((\S+)\)", resp[0]["deviceIndex"]) + if match is None: + msg = ("Failed to get the serial number of the specied switch") + self.module.fail_json(msg) + else: + serial_num = match.groups()[0] + snos = {"serialno": serial_num, "preprovision": False } + self.switch_snos.append(snos) + inv_upd["switches"] = resp return inv_upd @@ -362,17 +572,22 @@ def get_have(self): def get_want(self): want_create = [] + want_create_poap = [] if not self.config: return for inv in self.validated: - want_create.append(self.update_create_params(inv)) + if inv.get("poap"): + want_create_poap.append(self.update_poap_params(inv, inv["poap"][0])) + else: + want_create.append(self.update_create_params(inv)) - if not want_create: + if not want_create and not want_create_poap: return self.want_create = want_create + self.want_create_poap = want_create_poap def get_diff_override(self): @@ -447,6 +662,7 @@ def get_diff_delete(self): def get_diff_merge(self): diff_create = [] + switch_snos = [] for want_c in self.want_create: found = False @@ -500,6 +716,13 @@ def validate_input(self): """Parse the playbook values, validate to param specs.""" state = self.params["state"] + query_poap = self.params["query_poap"] + + if query_poap is True and state != "query": + msg = "query_poap: should not be set 'True' for state {0}".format( + state + ) + self.module.fail_json(msg=msg) if state == "merged" or state == "overridden": @@ -529,6 +752,16 @@ def validate_input(self): default="leaf", ), preserve_config=dict(type="bool", default=False), + poap=dict(type="list"), + ) + + poap_spec = dict( + serial_number=dict(required=True, type="str"), + model=dict(required=True, type="str"), + version=dict(required=True, type="str"), + hostname=dict(required=True, type="str"), + config_data=dict(required=True, type="dict"), + preprovision=dict(type="bool", default=False) ) msg = None @@ -540,7 +773,11 @@ def validate_input(self): or "password" not in inv ): msg = "seed ip/user name and password are mandatory under inventory parameters" - + if "poap" in inv: + if state != "merged": + msg = "merged and query are only supported states for POAP" + if inv["user_name"] != "admin": + msg = "For poap configuration, supported user_name is 'admin'" else: if state == "merged": msg = "config: element is mandatory for this state {0}".format( @@ -555,6 +792,13 @@ def validate_input(self): self.config, inv_spec, self.module ) for inv in valid_inv: + if inv.get("poap"): + valid_poap, invalid_poap = validate_list_of_dicts( + inv["poap"], poap_spec, self.module + ) + invalid_params.extend(invalid_poap) + inv["poap"] = valid_poap + self.validated.append(inv) if invalid_params: @@ -572,6 +816,8 @@ def validate_input(self): for inv in self.config: if "seed_ip" not in inv: msg = "seed ip is mandatory under inventory parameters for switch deletion" + if "poap" in inv: + msg = "merged and query are only supported states for POAP" if msg: self.module.fail_json(msg=msg) @@ -686,10 +932,11 @@ def ready_to_continue(inv_data): # even if the GRFIELD_DEBUG_FLAG is enabled so this needs to be # checked first. for switch in inv_data.get("DATA"): - if switch["mode"].lower() == "migration": - # At least one switch is still in migration mode - # so not ready to continue - return False + for snos in self.switch_snos: + if snos["serialno"] == switch["serialNumber"] and switch["mode"].lower() == "migration": + # At least one switch is still in migration mode + # so not ready to continue + return False # Check # 2 # The fabric has a setting to prevent reload for greenfield @@ -704,11 +951,12 @@ def ready_to_continue(inv_data): # moves to unmanagable but we need to wait for this to allow enough time # for the reload to completed. for switch in inv_data.get("DATA"): - if not switch["managable"]: - # We found our first switch that changed state to - # unmanageable because it's reloading. Now we can - # continue - return True + for snos in self.switch_snos: + if snos["serialno"] == switch["serialNumber"] and not snos["preprovision"] and not switch["managable"]: + # We found our first switch that changed state to + # unmanageable because it's reloading. Now we can + # continue + return True # We still have not detected a swich is reloading so return False return False @@ -716,9 +964,10 @@ def ready_to_continue(inv_data): def switches_managable(inv_data): managable = True for switch in inv_data["DATA"]: - if not switch["managable"]: - managable = False - break + for snos in self.switch_snos: + if snos["serialno"] == switch["serialNumber"] and not snos["preprovision"] and not switch["managable"]: + managable = False + break return managable @@ -730,7 +979,7 @@ def switches_managable(inv_data): # we don't need to loop. all_brownfield_switches = True for switch in self.config: - if not switch["preserve_config"]: + if not switch.get("preserve_config", True): all_brownfield_switches = False while attempt < total_attempts and not all_brownfield_switches: @@ -771,7 +1020,9 @@ def switches_managable(inv_data): break for inv in get_inv["DATA"]: - self.rediscover_switch(inv["serialNumber"]) + for snos in self.switch_snos: + if snos["serialno"] == inv["serialNumber"] and not snos["preprovision"]: + self.rediscover_switch(inv["serialNumber"]) def all_switches_ok(self): @@ -790,9 +1041,10 @@ def all_switches_ok(self): self.module.fail_json(msg=msg1 if missing_fabric else msg2) for inv in get_inv["DATA"]: - if inv["status"] != "ok": - all_ok = False - self.rediscover_switch(inv["serialNumber"]) + for snos in self.switch_snos: + if snos["serialno"] == inv["serialNumber"] and not snos["preprovision"] and inv["status"] != "ok": + all_ok = False + self.rediscover_switch(inv["serialNumber"]) return all_ok @@ -811,6 +1063,7 @@ def set_lancred_switch(self, set_lan): def lancred_all_switches(self): + want_list = [] # Get Fabric Inventory Details method = "GET" path = "/fm/fmrest/lanConfig/getLanSwitchCredentials" @@ -850,6 +1103,7 @@ def lancred_all_switches(self): def assign_role(self): + want_list = [] method = "GET" path = "/rest/control/fabrics/{0}/inventory".format(self.fabric) if self.nd: @@ -887,11 +1141,38 @@ def assign_role(self): if fail: self.failure(response) + for create in self.want_create_poap: + for role in get_role["DATA"]: + if not role["switchDbID"]: + msg = "Unable to get SWITCHDBID using getLanSwitchCredentials under fabric: {0}".format( + self.fabric + ) + self.module.fail_json(msg=msg) + if role["ipAddress"] == create["ipAddress"]: + method = "PUT" + path = "/fm/fmrest/topology/role/{0}?newRole={1}".format( + role["switchDbID"], create["role"].replace("_", "%20") + ) + if self.nd: + path = self.nd_prefix + "/" + path[6:] + response = dcnm_send(self.module, method, path) + self.result["response"].append(response) + fail, self.result["changed"] = self.handle_response( + response, "create" + ) + if fail: + self.failure(response) + def config_save(self): success = False no_of_tries = 3 + # NDFC/DCNM return error when we config-save a fabric with single Pre-provisioned switch. + if not self.have_create and not self.want_create: + if len(self.want_create_poap) == 1 and self.switch_snos[0]["preprovision"]: + return + for x in range(0, no_of_tries): # Get Fabric ID method = "GET" @@ -950,17 +1231,26 @@ def config_save(self): def config_deploy(self): # config-deploy + sernos = [] method = "POST" path = "/rest/control/fabrics/{0}".format(self.fabric) if self.nd: path = self.nd_prefix + path - path = path + "/config-deploy" - response = dcnm_send(self.module, method, path) - self.result["response"].append(response) - fail, self.result["changed"] = self.handle_response(response, "create") + path = path + "/config-deploy/" - if fail: - self.failure(response) + for snos in self.switch_snos: + if not snos["preprovision"]: + sernos.append(snos["serialno"]) + + switches = ",".join(sernos) + if switches: + path = path + switches + response = dcnm_send(self.module, method, path) + self.result["response"].append(response) + fail, self.result["changed"] = self.handle_response(response, "create") + + if fail: + self.failure(response) def delete_switch(self): @@ -980,6 +1270,8 @@ def delete_switch(self): def get_diff_query(self): query = [] + query_poap = self.params["query_poap"] + method = "GET" path = "/rest/control/fabrics/{0}/inventory".format(self.fabric) @@ -1002,10 +1294,10 @@ def get_diff_query(self): msg2 = "Unable to find inventories under fabric: {0}".format(self.fabric) self.module.fail_json(msg=msg1 if missing_fabric else msg2) - if not inv_objects["DATA"]: + if not inv_objects["DATA"] and not query_poap: return - if self.config: + if self.config and inv_objects["DATA"]: for want_c in self.want_create: for inv in inv_objects["DATA"]: if want_c["role"] == "None" and want_c["seedIP"] != "None": @@ -1026,8 +1318,51 @@ def get_diff_query(self): for inv in inv_objects["DATA"]: query.append(inv) + if query_poap is True: + method = "GET" + path = "/rest/control/fabrics/{0}/inventory/poap".format(self.fabric) + if self.nd: + path = self.nd_prefix + path + poap_objects = dcnm_send(self.module, method, path) + missing_fabric, not_ok = self.handle_response(poap_objects, "query_dcnm") + + if ( + poap_objects.get("ERROR") == "Not Found" + and poap_objects.get("RETURN_CODE") == 404 + ): + self.module.fail_json( + msg="Fabric {0} not present on DCNM".format(self.fabric) + ) + return + + if missing_fabric or not_ok: + msg1 = "Fabric {0} not present on DCNM".format(self.fabric) + msg2 = "Unable to find inventories under fabric: {0}".format(self.fabric) + self.module.fail_json(msg=msg1 if missing_fabric else msg2) + + if poap_objects["DATA"]: + for poap in poap_objects["DATA"]: + query.append(poap) + self.query = query + def poap_config(self): + + method = "POST" + path = "/rest/control/fabrics/{0}/inventory/poap".format(self.fabric) + if self.nd: + path = self.nd_prefix + path + if self.want_create_poap: + poap_list = copy.deepcopy(self.want_create_poap) + for poap in poap_list: + poap.pop("role") + response = dcnm_send(self.module, method, path, json.dumps(poap_list)) + self.result["response"].append(response) + fail, self.result["changed"] = self.handle_response(response, "create_poap") + + if fail: + self.failure(response) + def handle_response(self, res, op): fail = False @@ -1077,6 +1412,7 @@ def main(): state=dict( default="merged", choices=["merged", "overridden", "deleted", "query"] ), + query_poap=dict(type="bool", default=False) ) module = AnsibleModule(argument_spec=element_spec, supports_check_mode=True) @@ -1106,7 +1442,7 @@ def main(): "response" ] = "The switch provided is not part of the fabric and cannot be deleted" - if not dcnm_inv.diff_create and module.params["state"] == "merged": + if not dcnm_inv.diff_create and module.params["state"] == "merged" and not dcnm_inv.want_create_poap: dcnm_inv.result["changed"] = False dcnm_inv.result[ "response" @@ -1128,7 +1464,7 @@ def main(): "response" ] = "The queried switch is not part of the fabric configured" - if dcnm_inv.diff_create or dcnm_inv.diff_delete: + if dcnm_inv.diff_create or dcnm_inv.diff_delete or dcnm_inv.want_create_poap: dcnm_inv.result["changed"] = True else: module.exit_json(**dcnm_inv.result) @@ -1145,13 +1481,14 @@ def main(): # Discover & Register Switch if not dcnm_inv.node_migration: - - if dcnm_inv.diff_create: + if dcnm_inv.diff_create or dcnm_inv.want_create_poap: # Step 1 # Import all switches dcnm_inv.import_switches() + dcnm_inv.poap_config() + # Step 2 # Rediscover all switches dcnm_inv.rediscover_all_switches() diff --git a/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml b/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml index ac2c2ce6b..8bb69956e 100644 --- a/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml +++ b/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml @@ -2,6 +2,12 @@ ## SETUP ## ############################################## +- set_fact: + poap_enabled: False + +- set_fact: + poap_switch_present: False + - set_fact: rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" when: controller_version == "11" @@ -20,6 +26,10 @@ that: - 'result.response.DATA != None' +- set_fact: + poap_enabled: True + when: result.response.DATA.nvPairs["BOOTSTRAP_ENABLE"] == "true" + - name: MERGED - setup - Clean up any existing devices cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" @@ -183,6 +193,123 @@ fabric: "{{ ansible_it_fabric }}" state: deleted +- name: MERGED - Pre-provision switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: '{{ ansible_it_fabric }}' + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: '{{ ansible_prepro_switch }}' + user_name: '{{ switch_username }}' + password: '{{ switch_password }}' + role: border_gateway + poap: + - serial_number: '{{ ansible_prepro_serial }}' + model: '{{ ansible_prepro_model }}' + version: '{{ ansible_prepro_swversion }}' + hostname: '{{ ansible_prepro_hostname }}' + config_data: + modulesModel: '{{ ansible_modules_model }}' + gateway: '{{ ansible_bstrap_gateway }}' + preprovision: True + when: poap_enabled == True + register: result + +- assert: + that: + - 'result.changed == true' + when: (poap_enabled == True) + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + when: (poap_enabled == True) + loop: '{{ result.response }}' + +- name: MERGED - setup - Clean up any existing devices + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + state: deleted + when: (poap_enabled == True) + +- name: MERGED - Query for POAP enabled swicthes + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + query_poap: True + state: query + when: poap_enabled == True + register: result + +- assert: + that: + - 'result.response != None' + when: poap_enabled == True + +- name: Check poap query for switches + set_fact: + poap_switch_present: True + when: (poap_enabled == True and (item['serialNumber'] == '{{ ansible_poap_serial }}')) + loop: '{{ result.response }}' + ignore_errors: yes + +- name: MERGED - Poap, Pre-provision and discovered switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: '{{ ansible_it_fabric }}' + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: '{{ ansible_poap_switch }}' + user_name: '{{ switch_username }}' + password: '{{ switch_password }}' + role: border_gateway + poap: + - serial_number: '{{ ansible_poap_serial }}' + model: '{{ ansible_poap_model }}' + version: '{{ ansible_poap_swversion }}' + hostname: '{{ ansible_poap_hostname }}' + config_data: + modulesModel: '{{ ansible_modules_model }}' + gateway: '{{ ansible_bstrap_gateway }}' + - seed_ip: '{{ ansible_switch1 }}' + user_name: '{{ switch_username }}' + password: '{{ switch_password }}' + auth_proto: MD5 + max_hops: 0 + preserve_config: False + role: spine + - seed_ip: '{{ ansible_prepro_switch }}' + user_name: '{{ switch_username }}' + password: '{{ switch_password }}' + role: border + poap: + - serial_number: '{{ ansible_prepro_serial }}' + model: '{{ ansible_prepro_model }}' + version: '{{ ansible_prepro_swversion }}' + hostname: '{{ ansible_prepro_hostname }}' + config_data: + modulesModel: '{{ ansible_modules_model }}' + gateway: '{{ ansible_bstrap_gateway }}' + preprovision: True + when: (poap_enabled == True and poap_switch_present == True) + register: result + +- assert: + that: + - 'result.changed == true' + when: (poap_enabled == True and poap_switch_present == True) + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + when: (poap_enabled == True and poap_switch_present == True) + loop: '{{ result.response }}' + +- name: MERGED - setup - Clean up any existing devices + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + state: deleted + when: (poap_enabled == True) + - name: MERGED - Merge a Switch without seed_ip cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" @@ -266,4 +393,4 @@ - name: MERGED - setup - Clean up any existing devices cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" - state: deleted \ No newline at end of file + state: deleted diff --git a/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml b/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml index e32e2ae95..a2fddaf0a 100644 --- a/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml +++ b/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml @@ -2,6 +2,9 @@ ## SETUP ## ############################################## +- set_fact: + poap_enabled: False + - set_fact: rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" when: controller_version == "11" @@ -20,6 +23,10 @@ that: - 'result.response.DATA != None' +- set_fact: + poap_enabled: True + when: result.response.DATA.nvPairs["BOOTSTRAP_ENABLE"] == "true" + - name: QUERY - setup - Clean up any existing devices cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" @@ -183,11 +190,72 @@ - assert: that: - - 'result.response[0].ipAddress == "{{ ansible_switch1 }}"' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.response[0].ipAddress' - 'result.response[0].switchRole == "border gateway"' - - 'result.response[1].ipAddress == "{{ ansible_switch2 }}"' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.response[1].ipAddress' - 'result.response[1].switchRole == "border gateway"' +- name: QUERY - cleanup - Clean up any existing devices + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + state: deleted + +- name: QUERY - Pre-provision switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: '{{ ansible_it_fabric }}' + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: '{{ ansible_prepro_switch }}' + user_name: '{{ switch_username }}' + password: '{{ switch_password }}' + role: border_gateway + poap: + - serial_number: '{{ ansible_prepro_serial }}' + model: '{{ ansible_prepro_model }}' + version: '{{ ansible_prepro_swversion }}' + hostname: '{{ ansible_prepro_hostname }}' + config_data: + modulesModel: '{{ ansible_modules_model }}' + gateway: '{{ ansible_bstrap_gateway }}' + preprovision: True + - seed_ip: "{{ ansible_switch1 }}" + auth_proto: MD5 # choose from [MD5, SHA, MD5_DES, MD5_AES, SHA_DES, SHA_AES] + user_name: "{{ switch_username }}" + password: "{{ switch_password }}" + max_hops: 0 + role: leaf # default is Leaf - choose from [Leaf, Spine, Border, Border Spine, Border Gateway, Border Gateway Spine + # Super Spine, Border Super Spine, Border Gateway Super Spine] + preserve_config: False # boolean, default is true + when: (poap_enabled == True) + register: result + +- assert: + that: + - 'result.changed == true' + when: (poap_enabled == True) + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + when: (poap_enabled == True) + loop: '{{ result.response }}' + +- name: QUERY - Query a Switch without a config element + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + state: query + when: (poap_enabled == True) + register: result + +- assert: + that: + - '"{{ ansible_prepro_switch }}" or "{{ ansible_switch1 }}" in result.response[0].ipAddress' + - '"border gateway" or "leaf" in result.response[0].switchRole' + - '"{{ ansible_prepro_switch }}" or "{{ ansible_switch1 }}" in result.response[1].ipAddress' + - '"border gateway" or "leaf" in result.response[1].switchRole' + when: (poap_enabled == True) + ############################################## ## CLEAN-UP ## ############################################## @@ -195,4 +263,4 @@ - name: QUERY - cleanup - Clean up any existing devices cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" - state: deleted \ No newline at end of file + state: deleted diff --git a/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json b/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json index f49037349..5c101928a 100644 --- a/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json +++ b/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json @@ -179,6 +179,138 @@ } ], + "playbook_poap_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.123.223", + "user_name": "admin", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "hostname": "test_poap_test", + "model": "N9K-C9300v", + "serial_number": "9D2DAUJJFQQ", + "version": "9.3(7)" + } + ] + } + ], + + "playbook_poap_role_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.123.223", + "user_name": "admin", + "role": "border_gateway", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "hostname": "test_poap_test", + "model": "N9K-C9300v", + "serial_number": "9D2DAUJJFQQ", + "version": "9.3(7)" + } + ] + } + ], + + "playbook_preprovision_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.123.224", + "user_name": "admin", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "hostname": "test_poap_prepro", + "model": "N9K-C9300v", + "preprovision": true, + "serial_number": "9D2DAUJJFRR", + "version": "9.3(7)" + } + ] + } + ], + + "playbook_preprovision_role_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.123.224", + "user_name": "admin", + "role": "border_gateway", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "hostname": "test_poap_prepro", + "model": "N9K-C9300v", + "preprovision": true, + "serial_number": "9D2DAUJJFRR", + "version": "9.3(7)" + } + ] + } + ], + + "playbook_poap_wrong_user_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.123.223", + "user_name": "user", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "hostname": "test_poap_test", + "model": "N9K-C9300v", + "serial_number": "9D2DAUJJFQQ", + "version": "9.3(7)" + } + ] + } + ], + + "playbook_merge_multi_type_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.123.223", + "user_name": "admin", + "role": "border_gateway", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "hostname": "test_poap_test", + "model": "N9K-C9300v", + "serial_number": "9D2DAUJJFQQ", + "version": "9.3(7)" + } + ] + }, + { + "auth_proto": "MD5", + "max_hops": 0, + "password": "password", + "preserve_config": false, + "role": "border_gateway", + "seed_ip": "192.168.123.219", + "user_name": "username" + }, + { + "password": "password", + "seed_ip": "192.168.123.224", + "user_name": "admin", + "role": "border_gateway", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "hostname": "test_poap_prepro", + "model": "N9K-C9300v", + "preprovision": true, + "serial_number": "9D2DAUJJFRR", + "version": "9.3(7)" + } + ] + } + ], + "mock_inv_discover_params": { "ERROR": "", "RETURN_CODE": 200, @@ -2821,5 +2953,784 @@ "METHOD": "DELETE", "REQUEST_PATH": "https://192.168.10.8:443/rest/control/fabrics/Fabric1/switches/SAL1812NTBP", "RETURN_CODE": 200 + }, + + "get_inventory_query_poap_switch_success": { + "DATA": [ + { + "activeSupSlot": 0, + "availPorts": 0, + "colDBId": 0, + "connUnitStatus": 0, + "consistencyState": false, + "contact": "", + "cpuUsage": 8, + "displayHdrs": [ + "Name", + "IP Address", + "Fabric", + "WWN", + "FC Ports", + "Vendor", + "Model", + "Release", + "UpTime" + ], + "displayValues": [ + "n9k-110", + "192.168.123.217", + "kharicha-fabric", + "FGE19030RSA", + "52", + "Cisco", + "N9K-C9508", + "9.3(3)", + "00:09:28" + ], + "domain": null, + "domainID": 0, + "fabricName": "kharicha-fabric", + "fcoeEnabled": false, + "fex": false, + "fexMap": {}, + "fid": 3, + "health": -1, + "index": 1, + "ipAddress": "192.168.123.217", + "isLan": false, + "isNonNexus": false, + "isPmCollect": false, + "isVpcConfigured": false, + "keepAliveState": null, + "lastScanTime": 1589560981480, + "licenseDetail": null, + "licenseViolation": false, + "linkName": null, + "location": "", + "logicalName": "n9k-110", + "managable": true, + "mds": false, + "membership": null, + "memoryUsage": 30, + "mgmtAddress": null, + "mode": "Normal", + "model": "N9K-C9508", + "modelType": 0, + "modules": null, + "name": null, + "network": "LAN", + "nonMdsModel": "N9K-C9508", + "npvEnabled": false, + "numberOfPorts": 52, + "peer": null, + "peerSwitchDbId": 0, + "peerlinkState": null, + "ports": 0, + "present": true, + "primaryIP": null, + "primarySwitchDbID": 0, + "principal": null, + "recvIntf": null, + "release": "9.3(3)", + "role": null, + "sanAnalyticsCapable": false, + "scope": null, + "secondaryIP": "", + "secondarySwitchDbID": 0, + "sendIntf": null, + "serialNumber": "FGE19030RSA", + "standbySupState": 0, + "status": "ok", + "swWwn": null, + "swWwnName": "FGE19030RSA", + "switchDbID": 215060, + "switchRole": "leaf", + "uid": 0, + "unmanagableCause": "", + "upTime": 0, + "upTimeNumber": 0, + "upTimeStr": "00:09:28", + "usedPorts": 0, + "username": "WtbYjakLpa8=", + "vdcId": -1, + "vdcMac": null, + "vdcName": "", + "vendor": "Cisco", + "version": null, + "vpcDomain": 0, + "vsanWwn": null, + "vsanWwnName": null, + "wwn": null + } + ], + "MESSAGE": "OK", + "METHOD": "GET", + "REQUEST_PATH": "https://192.168.10.8:443/rest/control/fabrics/kharicha-fabric/inventory", + "RETURN_CODE": 200 + }, + + "get_inventory_query_poap_success": { + "DATA": [ + { + "serialNumber": "9D2DAUJJFQQ", + "model": "N9K-C9300v", + "version": "9.3(7)", + "data": "{\"gateway\": \"192.168.123.1/24\", \"modulesModel\": [\"N9K-X9364v\", \"N9K-vSUP\"]}", + "publicKey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDBuJ+5KalTYzNMr8F6SsgCHdGlF0r1+as+4OrRwpMxFHK69g9OLdaWMHQ1IzSyUJqGi5j13ITrhr+Ooc3qeFjyLAzkawKFCEyeoKniaqgPYjkn7PT+XWTCuIrTsg8s6E7V5IdWdMGB2GWoYdZ3pUR/Bz2VGa3eEnzJd8nMBRLydZ51BzPq5c+TGO1eBAaeSgz0UaAATvYZNOzJxVaQGzZsvgjK5eMAf5dYDHP4BBZzb3HOAazsww+8C6Jx3vlVpzBQeNt08UMCJnY+1XTKO4SMVJ5ztV6a3P5F4IX9SDGQV1Cy35bx2mG7Nc3V2BTQ6ACl424Lajo9DaUJg3yHVK28Y3V9TZC10+q3p34qCLePO9fo0peTMS4jrL0YntBiKo8r5M3PGYozRfKnZxGJJSqt3Z5BKK1ED8QtQuUkEmWDT1JK2OiVr250SMjEfPxwGqArbLVGzbC2XokGoWCOSxwWI4vlebsudFHc166wrYSpEcj3fRQIOP04eIeWt27Zjdk=", + "fingerprint": "MD5:05:97:51:63:09:85:52:59:17:4f:2d:01:c7:36:11:03", + "reAdd": false, + "seedSwitchFlag": false + } + ], + "MESSAGE": "OK", + "METHOD": "GET", + "REQUEST_PATH": "https://192.168.10.8:443/rest/control/fabrics/kharicha-fabric/inventory/poap", + "RETURN_CODE": 200 + }, + + "config_poap_switch_success": { + "DATA": { + "status": "Day0 config generated for the device(s)" + }, + "MESSAGE": "OK", + "METHOD": "POST", + "REQUEST_PATH": "https://192.168.10.8:443/rest/control/fabrics/kharicha-fabric/inventory/poap", + "RETURN_CODE": 200 + }, + + "get_inventory_poap_switch_success": { + "DATA": [ + { + "activeSupSlot": 0, + "availPorts": 0, + "colDBId": 0, + "connUnitStatus": 0, + "consistencyState": false, + "contact": "", + "cpuUsage": 8, + "displayHdrs": [ + "Name", + "IP Address", + "Fabric", + "WWN", + "FC Ports", + "Vendor", + "Model", + "Release", + "UpTime" + ], + "displayValues": [ + "n9k-110", + "192.168.123.223", + "kharicha-fabric", + "9D2DAUJJFQQ", + "52", + "Cisco", + "N9K-C9508", + "9.3(3)", + "00:09:28" + ], + "domain": null, + "domainID": 0, + "fabricName": "kharicha-fabric", + "fcoeEnabled": false, + "fex": false, + "fexMap": {}, + "fid": 3, + "health": -1, + "index": 1, + "ipAddress": "192.168.123.223", + "isLan": false, + "isNonNexus": false, + "isPmCollect": false, + "isVpcConfigured": false, + "keepAliveState": null, + "lastScanTime": 1589560981480, + "licenseDetail": null, + "licenseViolation": false, + "linkName": null, + "location": "", + "logicalName": "n9k-110", + "managable": true, + "mds": false, + "membership": null, + "memoryUsage": 30, + "mgmtAddress": null, + "mode": "Normal", + "model": "N9K-C9508", + "modelType": 0, + "modules": null, + "name": null, + "network": "LAN", + "nonMdsModel": "N9K-C9508", + "npvEnabled": false, + "numberOfPorts": 52, + "peer": null, + "peerSwitchDbId": 0, + "peerlinkState": null, + "ports": 0, + "present": true, + "primaryIP": null, + "primarySwitchDbID": 0, + "principal": null, + "recvIntf": null, + "release": "9.3(3)", + "role": null, + "sanAnalyticsCapable": false, + "scope": null, + "secondaryIP": "", + "secondarySwitchDbID": 0, + "sendIntf": null, + "serialNumber": "9D2DAUJJFQQ", + "standbySupState": 0, + "status": "ok", + "swWwn": null, + "swWwnName": "9D2DAUJJFQQ", + "switchDbID": 215060, + "switchRole": "leaf", + "uid": 0, + "unmanagableCause": "", + "upTime": 0, + "upTimeNumber": 0, + "upTimeStr": "00:09:28", + "usedPorts": 0, + "username": "WtbYjakLpa8=", + "vdcId": -1, + "vdcMac": null, + "vdcName": "", + "vendor": "Cisco", + "version": null, + "vpcDomain": 0, + "vsanWwn": null, + "vsanWwnName": null, + "wwn": null + } + ], + "MESSAGE": "OK", + "METHOD": "GET", + "REQUEST_PATH": "https://192.168.10.8:443/rest/control/fabrics/kharicha-fabric/inventory", + "RETURN_CODE": 200 + }, + + "get_inventory_prepro_switch_success": { + "DATA": [ + { + "activeSupSlot": 0, + "availPorts": 0, + "colDBId": 0, + "connUnitStatus": 0, + "consistencyState": false, + "contact": "", + "cpuUsage": 8, + "displayHdrs": [ + "Name", + "IP Address", + "Fabric", + "WWN", + "FC Ports", + "Vendor", + "Model", + "Release", + "UpTime" + ], + "displayValues": [ + "n9k-110", + "192.168.123.224", + "kharicha-fabric", + "9D2DAUJJFRR", + "52", + "Cisco", + "N9K-C9508", + "9.3(3)", + "00:09:28" + ], + "domain": null, + "domainID": 0, + "fabricName": "kharicha-fabric", + "fcoeEnabled": false, + "fex": false, + "fexMap": {}, + "fid": 3, + "health": -1, + "index": 1, + "ipAddress": "192.168.123.224", + "isLan": false, + "isNonNexus": false, + "isPmCollect": false, + "isVpcConfigured": false, + "keepAliveState": null, + "lastScanTime": 1589560981480, + "licenseDetail": null, + "licenseViolation": false, + "linkName": null, + "location": "", + "logicalName": "n9k-110", + "managable": true, + "mds": false, + "membership": null, + "memoryUsage": 30, + "mgmtAddress": null, + "mode": "Normal", + "model": "N9K-C9508", + "modelType": 0, + "modules": null, + "name": null, + "network": "LAN", + "nonMdsModel": "N9K-C9508", + "npvEnabled": false, + "numberOfPorts": 52, + "peer": null, + "peerSwitchDbId": 0, + "peerlinkState": null, + "ports": 0, + "present": true, + "primaryIP": null, + "primarySwitchDbID": 0, + "principal": null, + "recvIntf": null, + "release": "9.3(3)", + "role": null, + "sanAnalyticsCapable": false, + "scope": null, + "secondaryIP": "", + "secondarySwitchDbID": 0, + "sendIntf": null, + "serialNumber": "9D2DAUJJFRR", + "standbySupState": 0, + "status": "ok", + "swWwn": null, + "swWwnName": "9D2DAUJJFRR", + "switchDbID": 215060, + "switchRole": "leaf", + "uid": 0, + "unmanagableCause": "", + "upTime": 0, + "upTimeNumber": 0, + "upTimeStr": "00:09:28", + "usedPorts": 0, + "username": "WtbYjakLpa8=", + "vdcId": -1, + "vdcMac": null, + "vdcName": "", + "vendor": "Cisco", + "version": null, + "vpcDomain": 0, + "vsanWwn": null, + "vsanWwnName": null, + "wwn": null + } + ], + "MESSAGE": "OK", + "METHOD": "GET", + "REQUEST_PATH": "https://192.168.10.8:443/rest/control/fabrics/kharicha-fabric/inventory", + "RETURN_CODE": 200 + }, + + "poap_inv_discover_params": { + "ERROR": "", + "RETURN_CODE": 200, + "MESSAGE": "OK", + "DATA": [ + { + "auth": true, + "deviceIndex": "poap_test_static(944XJW7L3YJ)", + "hopCount": 0, + "ipaddr": "192.168.123.219", + "known": false, + "lastChange": null, + "platform": "N9K-C9508", + "reachable": true, + "selectable": true, + "statusReason": "manageable", + "sysName": "poap_test_static", + "valid": true, + "version": "9.3(7)" + } + ] + }, + + "get_inventory_merge_multi_type_switch_success": { + "DATA": [ + { + "switchRoleEnum":"Leaf", + "vrf":"management", + "fabricTechnology":"VXLANFabric", + "deviceType":"Switch_Fabric", + "fabricId":7, + "name":"None", + "domainID":0, + "wwn":"None", + "membership":"None", + "ports":0, + "model":"N9K-C9300v", + "version":"None", + "upTime":42021, + "ipAddress":"192.168.123.219", + "mgmtAddress":"None", + "vendor":"Cisco", + "displayHdrs":[ + "Name", + "IP Address", + "Fabric", + "WWN", + "FC Ports", + "Vendor", + "Model", + "Release", + "UpTime" + ], + "displayValues":[ + "poap_test_static", + "192.168.123.219", + "kharicha-fabric", + "944XJW7L3YJ", + "64", + "Cisco", + "N9K-C9300v", + "9.3(7)", + "00:07:00" + ], + "colDBId":0, + "fid":7, + "isLan":false, + "is_smlic_enabled":false, + "present":true, + "licenseViolation":false, + "managable":true, + "mds":false, + "connUnitStatus":0, + "standbySupState":0, + "activeSupSlot":0, + "unmanagableCause":"", + "lastScanTime":1661695665027, + "fabricName":"kharicha-fabric", + "modelType":0, + "logicalName":"poap_test_static", + "switchDbID":507370, + "uid":0, + "release":"9.3(7)", + "location":"", + "contact":"", + "upTimeStr":"00:07:00", + "upTimeNumber":0, + "network":"LAN", + "nonMdsModel":"N9K-C9300v", + "numberOfPorts":64, + "availPorts":0, + "usedPorts":0, + "vsanWwn":"None", + "vsanWwnName":"None", + "swWwn":"None", + "swWwnName":"944XJW7L3YJ", + "serialNumber":"944XJW7L3YJ", + "domain":"None", + "principal":"None", + "status":"ok", + "index":2, + "licenseDetail":"None", + "isPmCollect":false, + "sanAnalyticsCapable":false, + "vdcId":-1, + "vdcName":"", + "vdcMac":"None", + "fcoeEnabled":false, + "cpuUsage":86, + "memoryUsage":53, + "scope":"None", + "fex":false, + "health":-1, + "npvEnabled":false, + "linkName":"None", + "username":"None", + "primaryIP":"None", + "primarySwitchDbID":0, + "secondaryIP":"", + "secondarySwitchDbID":0, + "isEchSupport":false, + "moduleIndexOffset":9999, + "sysDescr":"", + "isTrapDelayed":false, + "switchRole":"leaf", + "mode":"Normal", + "hostName":"poap_test_static", + "ipDomain":"", + "systemMode":"None", + "sourceVrf":"", + "sourceInterface":"", + "modules":"None", + "fexMap":{}, + "isVpcConfigured":false, + "vpcDomain":0, + "role":"None", + "peer":"None", + "peerSerialNumber":"None", + "peerSwitchDbId":0, + "peerlinkState":"None", + "keepAliveState":"None", + "consistencyState":false, + "sendIntf":"None", + "recvIntf":"None", + "interfaces":"None", + "elementType":"None", + "monitorMode":false, + "freezeMode":false, + "isNonNexus":false + }, + { + "switchRoleEnum":"Leaf", + "vrf":"management", + "fabricTechnology":"VXLANFabric", + "deviceType":"Switch_Fabric", + "fabricId":7, + "name":"None", + "domainID":0, + "wwn":"None", + "membership":"None", + "ports":0, + "model":"N9K-C9300v", + "version":"None", + "upTime":166169597441, + "ipAddress":"192.168.123.224", + "mgmtAddress":"None", + "vendor":"None", + "displayHdrs":[ + "Name", + "IP Address", + "Fabric", + "WWN", + "FC Ports", + "Vendor", + "Model", + "Release", + "UpTime" + ], + "displayValues":[ + "test_poap_prepro", + "192.168.123.224", + "kharicha-fabric", + "9D2DAUJJFRR", + "0", + "None", + "N9K-C9300v", + "9.3(7)", + "" + ], + "colDBId":0, + "fid":7, + "isLan":false, + "is_smlic_enabled":false, + "present":true, + "licenseViolation":false, + "managable":false, + "mds":false, + "connUnitStatus":0, + "standbySupState":0, + "activeSupSlot":0, + "unmanagableCause":"Unreachable", + "lastScanTime":0, + "fabricName":"kharicha-fabric", + "modelType":0, + "logicalName":"test_poap_prepro", + "switchDbID":511660, + "uid":0, + "release":"9.3(7)", + "location":"None", + "contact":"None", + "upTimeStr":"", + "upTimeNumber":0, + "network":"LAN", + "nonMdsModel":"N9K-C9300v", + "numberOfPorts":0, + "availPorts":0, + "usedPorts":0, + "vsanWwn":"None", + "vsanWwnName":"None", + "swWwn":"None", + "swWwnName":"9D2DAUJJFRR", + "serialNumber":"9D2DAUJJFRR", + "domain":"None", + "principal":"None", + "status":"Unreachable", + "index":3, + "licenseDetail":"None", + "isPmCollect":false, + "sanAnalyticsCapable":false, + "vdcId":-1, + "vdcName":"", + "vdcMac":"None", + "fcoeEnabled":false, + "cpuUsage":0, + "memoryUsage":0, + "scope":"None", + "fex":false, + "health":-1, + "npvEnabled":false, + "linkName":"None", + "username":"None", + "primaryIP":"None", + "primarySwitchDbID":0, + "secondaryIP":"", + "secondarySwitchDbID":0, + "isEchSupport":false, + "moduleIndexOffset":9999, + "sysDescr":"", + "isTrapDelayed":false, + "switchRole":"leaf", + "mode":"Normal", + "hostName":"None", + "ipDomain":"None", + "systemMode":"None", + "sourceVrf":"", + "sourceInterface":"", + "modules":"None", + "fexMap":{}, + "isVpcConfigured":false, + "vpcDomain":0, + "role":"None", + "peer":"None", + "peerSerialNumber":"None", + "peerSwitchDbId":0, + "peerlinkState":"None", + "keepAliveState":"None", + "consistencyState":false, + "sendIntf":"None", + "recvIntf":"None", + "interfaces":"None", + "elementType":"None", + "monitorMode":false, + "freezeMode":false, + "isNonNexus":false + }, + { + "switchRoleEnum":"Leaf", + "vrf":"management", + "fabricTechnology":"VXLANFabric", + "deviceType":"Switch_Fabric", + "fabricId":7, + "name":"None", + "domainID":0, + "wwn":"None", + "membership":"None", + "ports":0, + "model":"N9K-C9300v", + "version":"None", + "upTime":36548, + "ipAddress":"192.168.123.223", + "mgmtAddress":"None", + "vendor":"Cisco", + "displayHdrs":[ + "Name", + "IP Address", + "Fabric", + "WWN", + "FC Ports", + "Vendor", + "Model", + "Release", + "UpTime" + ], + "displayValues":[ + "test_poap_test", + "192.168.123.223", + "kharicha-fabric", + "9D2DAUJJFQQ", + "64", + "Cisco", + "N9K-C9300v", + "9.3(7)", + "00:06:05" + ], + "colDBId":0, + "fid":7, + "isLan":false, + "is_smlic_enabled":false, + "present":true, + "licenseViolation":false, + "managable":true, + "mds":false, + "connUnitStatus":0, + "standbySupState":0, + "activeSupSlot":0, + "unmanagableCause":"", + "lastScanTime":1661695965026, + "fabricName":"kharicha-fabric", + "modelType":0, + "logicalName":"test_poap_test", + "switchDbID":511620, + "uid":0, + "release":"9.3(7)", + "location":"", + "contact":"", + "upTimeStr":"00:06:05", + "upTimeNumber":0, + "network":"LAN", + "nonMdsModel":"N9K-C9300v", + "numberOfPorts":64, + "availPorts":0, + "usedPorts":0, + "vsanWwn":"None", + "vsanWwnName":"None", + "swWwn":"None", + "swWwnName":"9D2DAUJJFQQ", + "serialNumber":"9D2DAUJJFQQ", + "domain":"None", + "principal":"None", + "status":"ok", + "index":4, + "licenseDetail":"None", + "isPmCollect":false, + "sanAnalyticsCapable":false, + "vdcId":-1, + "vdcName":"", + "vdcMac":"None", + "fcoeEnabled":false, + "cpuUsage":15, + "memoryUsage":43, + "scope":"None", + "fex":false, + "health":-1, + "npvEnabled":false, + "linkName":"None", + "username":"None", + "primaryIP":"None", + "primarySwitchDbID":0, + "secondaryIP":"", + "secondarySwitchDbID":0, + "isEchSupport":false, + "moduleIndexOffset":9999, + "sysDescr":"", + "isTrapDelayed":false, + "switchRole":"leaf", + "mode":"Normal", + "hostName":"test_poap_test", + "ipDomain":"", + "systemMode":"None", + "sourceVrf":"", + "sourceInterface":"", + "modules":"None", + "fexMap":{}, + "isVpcConfigured":false, + "vpcDomain":0, + "role":"None", + "peer":"None", + "peerSerialNumber":"None", + "peerSwitchDbId":0, + "peerlinkState":"None", + "keepAliveState":"None", + "consistencyState":false, + "sendIntf":"None", + "recvIntf":"None", + "interfaces":"None", + "elementType":"None", + "monitorMode":false, + "freezeMode":false, + "isNonNexus":false + } + ], + "MESSAGE": "OK", + "METHOD": "GET", + "REQUEST_PATH": "https://192.168.10.8:443/rest/control/fabrics/kharicha-fabric/inventory", + "RETURN_CODE": 200 } + } diff --git a/tests/unit/modules/dcnm/test_dcnm_inventory.py b/tests/unit/modules/dcnm/test_dcnm_inventory.py index 60c9ea8d6..ee2de5561 100644 --- a/tests/unit/modules/dcnm/test_dcnm_inventory.py +++ b/tests/unit/modules/dcnm/test_dcnm_inventory.py @@ -66,6 +66,12 @@ class TestDcnmInvModule(TestDcnmModule): "playbook_invalid_discover_payload_config" ) playbook_query_switch_config = test_data.get("playbook_query_switch_config") + playbook_poap_switch_config = test_data.get("playbook_poap_switch_config") + playbook_poap_role_switch_config = test_data.get("playbook_poap_role_switch_config") + playbook_preprovision_switch_config = test_data.get("playbook_preprovision_switch_config") + playbook_preprovision_role_switch_config = test_data.get("playbook_preprovision_role_switch_config") + playbook_poap_wrong_user_switch_config = test_data.get("playbook_poap_wrong_user_switch_config") + playbook_merge_multi_type_switch_config = test_data.get("playbook_merge_multi_type_switch_config") # initial merge switch success get_have_initial_success = test_data.get("get_have_initial_success") @@ -124,6 +130,24 @@ class TestDcnmInvModule(TestDcnmModule): get_fabric_id_success = test_data.get("get_fabric_id_success") config_save_switch_success = test_data.get("config_save_switch_success") config_deploy_switch_success = test_data.get("config_deploy_switch_success") + get_inventory_query_poap_switch_success = test_data.get( + "get_inventory_query_poap_switch_success" + ) + get_inventory_query_poap_success = test_data.get( + "get_inventory_query_poap_success" + ) + get_inventory_poap_switch_success = test_data.get( + "get_inventory_poap_switch_success" + ) + config_poap_switch_success = test_data.get("config_poap_switch_success") + get_inventory_prepro_switch_success = test_data.get( + "get_inventory_prepro_switch_success" + ) + config_poap_switch_success = test_data.get("config_poap_switch_success") + poap_inv_discover_params = test_data.get("poap_inv_discover_params") + get_inventory_merge_multi_type_switch_success = test_data.get( + "get_inventory_merge_multi_type_switch_success" + ) # initial delete switch success get_have_one_switch_success = test_data.get("get_have_one_switch_success") @@ -580,6 +604,112 @@ def load_fixtures(self, response=None, device=""): self.init_data() self.run_dcnm_send.side_effect = [self.get_inventory_blank_success] + elif "query_poap_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_have_one_switch_success, + self.get_inventory_query_poap_switch_success, + self.get_inventory_query_poap_success, + ] + + elif "poap_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_inventory_query_poap_success, + self.get_have_initial_success, + self.config_poap_switch_success, + self.get_inventory_poap_switch_success, + self.get_inventory_poap_switch_success, + self.rediscover_switch_success, + self.get_inventory_poap_switch_success, + self.get_inventory_poap_switch_success, + self.get_lan_switch_cred_success, + self.get_inventory_poap_switch_success, + self.set_assign_role_success, + self.get_fabric_id_success, + self.config_save_switch_success, + self.config_deploy_switch_success, + ] + + elif "poap_role_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_inventory_query_poap_success, + self.get_have_initial_success, + self.config_poap_switch_success, + self.get_inventory_poap_switch_success, + self.get_inventory_poap_switch_success, + self.rediscover_switch_success, + self.get_inventory_poap_switch_success, + self.get_inventory_poap_switch_success, + self.get_lan_switch_cred_success, + self.get_inventory_poap_switch_success, + self.set_assign_bg_role_success, + self.get_fabric_id_success, + self.config_save_switch_success, + self.config_deploy_switch_success, + ] + + elif "preprovision_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_have_initial_success, + self.config_poap_switch_success, + self.get_inventory_prepro_switch_success, + self.get_inventory_prepro_switch_success, + self.get_inventory_prepro_switch_success, + self.get_inventory_prepro_switch_success, + self.get_lan_switch_cred_success, + self.get_inventory_prepro_switch_success, + self.set_assign_role_success, + self.get_fabric_id_success, + self.config_save_switch_success, + ] + + elif "preprovision_role_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_have_initial_success, + self.config_poap_switch_success, + self.get_inventory_prepro_switch_success, + self.get_inventory_prepro_switch_success, + self.get_inventory_prepro_switch_success, + self.get_inventory_prepro_switch_success, + self.get_lan_switch_cred_success, + self.get_inventory_prepro_switch_success, + self.set_assign_bg_role_success, + self.get_fabric_id_success, + self.config_save_switch_success, + ] + + elif "poap_wrong_user_switch" in self._testMethodName: + self.init_data() + + elif "merge_multi_type_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_inventory_query_poap_success, + self.poap_inv_discover_params, + self.get_have_initial_success, + self.import_switch_discover_success, + self.config_poap_switch_success, + self.get_inventory_merge_multi_type_switch_success, + self.get_inventory_merge_multi_type_switch_success, + self.get_inventory_merge_multi_type_switch_success, + self.rediscover_switch_success, + self.rediscover_switch_success, + self.get_inventory_merge_multi_type_switch_success, + self.get_inventory_merge_multi_type_switch_success, + self.get_lan_switch_cred_success, + self.get_inventory_merge_multi_type_switch_success, + self.set_assign_bg_role_success, + self.set_assign_bg_role_success, + self.set_assign_bg_role_success, + self.get_fabric_id_success, + self.config_save_switch_success, + self.config_deploy_switch_success, + ] + else: pass @@ -987,3 +1117,110 @@ def test_dcnm_inv_query_no_switch_fabric(self): result["response"], "The queried switch is not part of the fabric configured", ) + + def test_dcnm_inv_query_poap_switch_fabric(self): + set_module_args( + dict( + state="query", + fabric="kharicha-fabric", + query_poap="true", + ) + ) + result = self.execute_module(changed=False, failed=False) + self.assertEqual(result["response"][0]["ipAddress"], "192.168.123.217") + self.assertEqual(result["response"][0]["switchRole"], "leaf") + self.assertEqual(result["response"][1]["model"], "N9K-C9300v") + self.assertEqual(result["response"][1]["serialNumber"], "9D2DAUJJFQQ") + self.assertEqual(result["response"][1]["version"], "9.3(7)") + + def test_dcnm_inv_poap_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_poap_switch_config, + ) + ) + + result = self.execute_module(changed=True, failed=False) + + for resp in result["response"]: + self.assertEqual(resp["RETURN_CODE"], 200) + self.assertEqual(resp["MESSAGE"], "OK") + + def test_dcnm_inv_poap_role_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_poap_role_switch_config, + ) + ) + + result = self.execute_module(changed=True, failed=False) + + self.assertEqual(result["response"][3]["DATA"]["newRole"], "border gateway") + for resp in result["response"]: + self.assertEqual(resp["RETURN_CODE"], 200) + self.assertEqual(resp["MESSAGE"], "OK") + + def test_dcnm_inv_preprovision_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_preprovision_switch_config, + ) + ) + + result = self.execute_module(changed=True, failed=False) + + for resp in result["response"]: + self.assertEqual(resp["RETURN_CODE"], 200) + self.assertEqual(resp["MESSAGE"], "OK") + + def test_dcnm_inv_preprovision_role_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_preprovision_role_switch_config, + ) + ) + + result = self.execute_module(changed=True, failed=False) + + self.assertEqual(result["response"][1]["DATA"]["newRole"], "border gateway") + for resp in result["response"]: + self.assertEqual(resp["RETURN_CODE"], 200) + self.assertEqual(resp["MESSAGE"], "OK") + + def test_dcnm_inv_poap_wrong_user_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_poap_wrong_user_switch_config, + ) + ) + + result = self.execute_module(changed=False, failed=True) + + self.assertEqual( + result.get("msg"), + "For poap configuration, supported user_name is 'admin'", + ) + + def test_dcnm_inv_poap_merge_multi_type_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_merge_multi_type_switch_config, + ) + ) + + result = self.execute_module(changed=True, failed=False) + + for resp in result["response"]: + self.assertEqual(resp["RETURN_CODE"], 200) From 75faa3a21073ffeaee36e29f8032c6768a0e2bb7 Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Sat, 10 Sep 2022 22:48:57 +0530 Subject: [PATCH 07/26] POAP support for inventory module --- plugins/modules/dcnm_inventory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dcnm_inventory.py b/plugins/modules/dcnm_inventory.py index 0dd5ca375..4895c5a16 100644 --- a/plugins/modules/dcnm_inventory.py +++ b/plugins/modules/dcnm_inventory.py @@ -505,7 +505,7 @@ def update_create_params(self, inv): resp = self.update_discover_params(inv_upd) - if (type(resp[0]) is dict): + if (isinstance(resp[0], dict)): match = re.search(r"\S+\((\S+)\)", resp[0]["deviceIndex"]) if match is None: msg = ("Failed to get the serial number of the specied switch") From 8786f487ed86754c3a13f423f9a4a09dbbbae196 Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Sat, 10 Sep 2022 23:03:08 +0530 Subject: [PATCH 08/26] Fixed formatting issues --- plugins/modules/dcnm_inventory.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/plugins/modules/dcnm_inventory.py b/plugins/modules/dcnm_inventory.py index 4895c5a16..c2a8f71b8 100644 --- a/plugins/modules/dcnm_inventory.py +++ b/plugins/modules/dcnm_inventory.py @@ -421,7 +421,7 @@ def discover_poap_params(self, poap_upd, poap): poap_upd.update({"reAdd": resp["reAdd"]}) poap_upd.update({"fingerprint": resp["fingerprint"]}) poap_upd.update({"imagePolicy": ""}) - #poap_upd.update({"role": "leaf"}) + # poap_upd.update({"role": "leaf"}) return msg = ("Specified switch is not listed in bootstrap devices. " @@ -439,7 +439,7 @@ def update_poap_params(self, inv, poap): state = self.params["state"] - if state == "merged": + if state == "merged": poap_upd = { "ipAddress": s_ip, "discoveryAuthProtocol": "0", @@ -458,7 +458,6 @@ def update_poap_params(self, inv, poap): snos = {"serialno": poap["serial_number"], "preprovision": poap["preprovision"]} self.switch_snos.append(snos) - return poap_upd def update_create_params(self, inv): @@ -512,7 +511,7 @@ def update_create_params(self, inv): self.module.fail_json(msg) else: serial_num = match.groups()[0] - snos = {"serialno": serial_num, "preprovision": False } + snos = {"serialno": serial_num, "preprovision": False} self.switch_snos.append(snos) inv_upd["switches"] = resp @@ -720,7 +719,7 @@ def validate_input(self): if query_poap is True and state != "query": msg = "query_poap: should not be set 'True' for state {0}".format( - state + state ) self.module.fail_json(msg=msg) @@ -773,7 +772,7 @@ def validate_input(self): or "password" not in inv ): msg = "seed ip/user name and password are mandatory under inventory parameters" - if "poap" in inv: + if "poap" in inv: if state != "merged": msg = "merged and query are only supported states for POAP" if inv["user_name"] != "admin": @@ -816,7 +815,7 @@ def validate_input(self): for inv in self.config: if "seed_ip" not in inv: msg = "seed ip is mandatory under inventory parameters for switch deletion" - if "poap" in inv: + if "poap" in inv: msg = "merged and query are only supported states for POAP" if msg: @@ -1272,7 +1271,6 @@ def get_diff_query(self): query = [] query_poap = self.params["query_poap"] - method = "GET" path = "/rest/control/fabrics/{0}/inventory".format(self.fabric) if self.nd: From 9c7e42922df95286c43ae503282d6fdaefb17c72 Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Sat, 1 Oct 2022 00:38:03 +0530 Subject: [PATCH 09/26] Idempotence support for POAP inventory module --- docs/cisco.dcnm.dcnm_inventory_module.rst | 76 +++++- plugins/modules/dcnm_inventory.py | 226 ++++++++++++------ .../dcnm_inventory/tests/dcnm/merged.yaml | 37 ++- .../dcnm_inventory/tests/dcnm/query.yaml | 3 +- .../modules/dcnm/fixtures/dcnm_inventory.json | 9 +- .../unit/modules/dcnm/test_dcnm_inventory.py | 42 ++-- 6 files changed, 276 insertions(+), 117 deletions(-) diff --git a/docs/cisco.dcnm.dcnm_inventory_module.rst b/docs/cisco.dcnm.dcnm_inventory_module.rst index 5f7de51bf..1324657de 100644 --- a/docs/cisco.dcnm.dcnm_inventory_module.rst +++ b/docs/cisco.dcnm.dcnm_inventory_module.rst @@ -121,7 +121,7 @@ Parameters -
Configurations of switch to Bootstrap/Pre-provision.
+
Configurations of switch to Bootstrap/Pre-provision. Please note that POAP and DHCP configurations needs to enabled in fabric configuration before adding/preprovisioning switches through POAP. Idempotence checks against inventory is only for 'IP Address' for Preprovision configs. Idempotence checks against inventory is only for 'IP Address' and 'Serial Number' for Bootstrap configs.
@@ -160,6 +160,23 @@ Parameters
Hostname of switch to Bootstrap/Pre-provision.
+ + + + +
+ image_policy + +
+ string +
+ + + + +
Name of the image policy to be applied on switch during Bootstrap/Pre-provision.
+ + @@ -178,6 +195,23 @@ Parameters
Model of switch to Bootstrap/Pre-provision.
+ + + + +
+ preprovision_serial + +
+ string +
+ + + + +
Serial number of switch to Pre-provision. When 'preprovision_serial' is provided along with 'serial_number', then the Preprovisioned switch(with serial number as in 'preprovision_serial') will be swapped with a actual switch(with serial number in 'serial_number') through bootstrap. Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions.
+ + @@ -187,13 +221,12 @@ Parameters
string - / required
-
Serial number of switch to Bootstrap/Pre-provision.
+
Serial number of switch to Bootstrap. When 'preprovision_serial' is provided along with 'serial_number', then the Preprovisioned switch(with serial number as in 'preprovision_serial') will be swapped with a actual switch(with serial number in 'serial_number') through bootstrap. Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions.
@@ -461,10 +494,11 @@ Examples password: switch_password role: border_gateway poap: - - serial_number: 1A2BCDEFJKL + - serial_number: 2A3BCDEFJKL model: 'N9K-C9300v' version: '9.3(7)' hostname: 'POAP_SWITCH' + image_policy: "poap_image_policy" config_data: modulesModel: [N9K-X9364v, N9K-vSUP] gateway: 192.168.0.1/24 @@ -481,14 +515,14 @@ Examples password: switch_password role: border poap: - - serial_number: 1A2BCDEFGHI + - preprovision_serial: 1A2BCDEFGHI model: 'N9K-C9300v' version: '9.3(7)' hostname: 'PREPRO_SWITCH' + image_policy: "prepro_image_policy" config_data: modulesModel: [N9K-X9364v, N9K-vSUP] gateway: 192.168.0.1/24 - preprovision: True - name: Poap, Pre-provision and existing switch Configuration cisco.dcnm.dcnm_inventory: @@ -500,10 +534,11 @@ Examples password: switch_password role: border_gateway poap: - - serial_number: 1A2BCDEFGHI + - serial_number: 2A3BCDEFGHI model: 'N9K-C9300v' version: '9.3(7)' hostname: 'POAP_SWITCH' + image_policy: "poap_image_policy" config_data: modulesModel: [N9K-X9364v, N9K-vSUP] gateway: 192.168.0.1/24 @@ -519,14 +554,37 @@ Examples password: switch_password role: border poap: - - serial_number: 1A2BCDEFGHI + - preprovision_serial: 1A2BCDEFGHI + model: 'N9K-C9300v' + version: '9.3(7)' + hostname: 'PREPRO_SWITCH' + image_policy: "prepro_image_policy" + config_data: + modulesModel: [N9K-X9364v, N9K-vSUP] + gateway: 192.168.0.1/24 + + # The following pre-provisioned switch will be swapped with actual swicth in the existing fabric + # This swap feature is supported only in NDFC and not on DCNM 11.x versions + - name: Pre-provision switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: 192.168.0.4 + user_name: switch_username + password: switch_password + role: border + poap: + - preprovision_serial: 1A2BCDEFGHI + serial_number: 2A3BCDEFGHI model: 'N9K-C9300v' version: '9.3(7)' hostname: 'PREPRO_SWITCH' + image_policy: "poap_image_policy" config_data: modulesModel: [N9K-X9364v, N9K-vSUP] gateway: 192.168.0.1/24 - preprovision: True # All the switches will be deleted in the existing fabric - name: Delete all the switches diff --git a/plugins/modules/dcnm_inventory.py b/plugins/modules/dcnm_inventory.py index c2a8f71b8..08aeb69cd 100644 --- a/plugins/modules/dcnm_inventory.py +++ b/plugins/modules/dcnm_inventory.py @@ -97,14 +97,31 @@ poap: description: - Configurations of switch to Bootstrap/Pre-provision. + Please note that POAP and DHCP configurations needs to enabled in fabric configuration + before adding/preprovisioning switches through POAP. + Idempotence checks against inventory is only for 'IP Address' for Preprovision configs. + Idempotence checks against inventory is only for 'IP Address' and 'Serial Number' for Bootstrap configs. type: list elements: dict suboptions: serial_number: description: - - Serial number of switch to Bootstrap/Pre-provision. + - Serial number of switch to Bootstrap. + When 'preprovision_serial' is provided along with 'serial_number', + then the Preprovisioned switch(with serial number as in 'preprovision_serial') will be swapped + with a actual switch(with serial number in 'serial_number') through bootstrap. + Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions. type: str - required: true + required: false + preprovision_serial: + description: + - Serial number of switch to Pre-provision. + When 'preprovision_serial' is provided along with 'serial_number', + then the Preprovisioned switch(with serial number as in 'preprovision_serial') will be swapped + with a actual switch(with serial number in 'serial_number') through bootstrap. + Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions. + type: str + required: false model: description: - Model of switch to Bootstrap/Pre-provision. @@ -120,6 +137,11 @@ - Hostname of switch to Bootstrap/Pre-provision. type: str required: true + image_policy: + description: + - Name of the image policy to be applied on switch during Bootstrap/Pre-provision. + type: str + required: false config_data: description: - Basic config data of switch to Bootstrap/Pre-provision. @@ -235,10 +257,11 @@ password: switch_password role: border_gateway poap: - - serial_number: 1A2BCDEFJKL + - serial_number: 2A3BCDEFJKL model: 'N9K-C9300v' version: '9.3(7)' hostname: 'POAP_SWITCH' + image_policy: "poap_image_policy" config_data: modulesModel: [N9K-X9364v, N9K-vSUP] gateway: 192.168.0.1/24 @@ -255,14 +278,14 @@ password: switch_password role: border poap: - - serial_number: 1A2BCDEFGHI + - preprovision_serial: 1A2BCDEFGHI model: 'N9K-C9300v' version: '9.3(7)' hostname: 'PREPRO_SWITCH' + image_policy: "prepro_image_policy" config_data: modulesModel: [N9K-X9364v, N9K-vSUP] gateway: 192.168.0.1/24 - preprovision: True - name: Poap, Pre-provision and existing switch Configuration cisco.dcnm.dcnm_inventory: @@ -274,10 +297,11 @@ password: switch_password role: border_gateway poap: - - serial_number: 1A2BCDEFGHI + - serial_number: 2A3BCDEFGHI model: 'N9K-C9300v' version: '9.3(7)' hostname: 'POAP_SWITCH' + image_policy: "poap_image_policy" config_data: modulesModel: [N9K-X9364v, N9K-vSUP] gateway: 192.168.0.1/24 @@ -293,14 +317,37 @@ password: switch_password role: border poap: - - serial_number: 1A2BCDEFGHI + - preprovision_serial: 1A2BCDEFGHI + model: 'N9K-C9300v' + version: '9.3(7)' + hostname: 'PREPRO_SWITCH' + image_policy: "prepro_image_policy" + config_data: + modulesModel: [N9K-X9364v, N9K-vSUP] + gateway: 192.168.0.1/24 + +# The following pre-provisioned switch will be swapped with actual swicth in the existing fabric +# This swap feature is supported only in NDFC and not on DCNM 11.x versions +- name: Pre-provision switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: 192.168.0.4 + user_name: switch_username + password: switch_password + role: border + poap: + - preprovision_serial: 1A2BCDEFGHI + serial_number: 2A3BCDEFGHI model: 'N9K-C9300v' version: '9.3(7)' hostname: 'PREPRO_SWITCH' + image_policy: "poap_image_policy" config_data: modulesModel: [N9K-X9364v, N9K-vSUP] gateway: 192.168.0.1/24 - preprovision: True # All the switches will be deleted in the existing fabric - name: Delete all the switches @@ -376,29 +423,27 @@ def __init__(self, module): self.nd = True if self.controller_version >= 12 else False - def update_discover_params(self, inv): - - # with the inv parameters perform the test-reachability (discover) - method = "POST" - path = "/rest/control/fabrics/{0}/inventory/test-reachability".format( - self.fabric - ) - if self.nd: - path = self.nd_prefix + path - response = dcnm_send(self.module, method, path, json.dumps(inv)) - self.result["response"].append(response) - fail, self.result["changed"] = self.handle_response(response, "create") - - if fail: - self.module.fail_json(msg=response) + def discover_poap_params(self, poap_upd, poap): - if "DATA" in response: - return response["DATA"] + for have_c in self.have_create: + # Idempotence - Bootstrap but already in inventory + if poap_upd["serialNumber"]: + if ( + poap_upd["ipAddress"] == have_c["switches"][0]["ipaddr"] + and poap_upd["serialNumber"] == have_c["switches"][0]["serialNumber"] + ): + return {} - else: - return 0 + # Idempotence - Preprovision but already in inventory + if poap_upd["preprovisionSerial"] and not poap_upd["serialNumber"]: + if ( + poap_upd["ipAddress"] == have_c["switches"][0]["ipaddr"] + ): + return {} - def discover_poap_params(self, poap_upd, poap): + # Preprovision case + if poap_upd["preprovisionSerial"] and not poap_upd["serialNumber"]: + return poap_upd method = "GET" path = "/rest/control/fabrics/{0}/inventory/poap".format( @@ -420,19 +465,22 @@ def discover_poap_params(self, poap_upd, poap): poap_upd.update({"publicKey": resp["publicKey"]}) poap_upd.update({"reAdd": resp["reAdd"]}) poap_upd.update({"fingerprint": resp["fingerprint"]}) - poap_upd.update({"imagePolicy": ""}) + if poap["image_policy"]: + poap_upd.update({"imagePolicy": poap["image_policy"]}) + else: + poap_upd.update({"imagePolicy": ""}) # poap_upd.update({"role": "leaf"}) - return + return poap_upd - msg = ("Specified switch is not listed in bootstrap devices. " - "If you are trying to pre-provision, please set " - "'preprovision' to 'True' in your task") + msg = "Specified switch {0}".format( + poap["serial_number"] + " is not listed in bootstrap devices or inventory. " + + "If you are trying to pre-provision, please set " + + "'preprovision_serial' instead of 'serial_number' in your task") self.module.fail_json(msg) def update_poap_params(self, inv, poap): - snos = {} s_ip = "None" if inv["seed_ip"]: s_ip = dcnm_get_ip_addr_info(self.module, inv["seed_ip"], None, None) @@ -450,19 +498,40 @@ def update_poap_params(self, inv, poap): "version": poap["version"], "data": json.dumps(poap["config_data"]), "role": inv["role"].replace(" ", "_"), + "preprovisionSerial": poap["preprovision_serial"], } - if poap["preprovision"] is False: - self.discover_poap_params(poap_upd, poap) + poap_upd = self.discover_poap_params(poap_upd, poap) - snos = {"serialno": poap["serial_number"], "preprovision": poap["preprovision"]} - self.switch_snos.append(snos) + if poap_upd.get("serialNumber"): + self.switch_snos.append(poap_upd["serialNumber"]) return poap_upd + def update_discover_params(self, inv): + + # with the inv parameters perform the test-reachability (discover) + method = "POST" + path = "/rest/control/fabrics/{0}/inventory/test-reachability".format( + self.fabric + ) + if self.nd: + path = self.nd_prefix + path + response = dcnm_send(self.module, method, path, json.dumps(inv)) + self.result["response"].append(response) + fail, self.result["changed"] = self.handle_response(response, "create") + + if fail: + self.module.fail_json(msg=response) + + if "DATA" in response: + return response["DATA"] + + else: + return 0 + def update_create_params(self, inv): - snos = {} s_ip = "None" if inv["seed_ip"]: s_ip = dcnm_get_ip_addr_info(self.module, inv["seed_ip"], None, None) @@ -504,16 +573,6 @@ def update_create_params(self, inv): resp = self.update_discover_params(inv_upd) - if (isinstance(resp[0], dict)): - match = re.search(r"\S+\((\S+)\)", resp[0]["deviceIndex"]) - if match is None: - msg = ("Failed to get the serial number of the specied switch") - self.module.fail_json(msg) - else: - serial_num = match.groups()[0] - snos = {"serialno": serial_num, "preprovision": False} - self.switch_snos.append(snos) - inv_upd["switches"] = resp return inv_upd @@ -578,7 +637,9 @@ def get_want(self): for inv in self.validated: if inv.get("poap"): - want_create_poap.append(self.update_poap_params(inv, inv["poap"][0])) + create_poap = self.update_poap_params(inv, inv["poap"][0]) + if create_poap: + want_create_poap.append(create_poap) else: want_create.append(self.update_create_params(inv)) @@ -661,15 +722,12 @@ def get_diff_delete(self): def get_diff_merge(self): diff_create = [] - switch_snos = [] for want_c in self.want_create: found = False + match = re.search(r"\S+\((\S+)\)", want_c["switches"][0]["deviceIndex"]) + serial_num = match.groups()[0] for have_c in self.have_create: - match = re.search(r"\S+\((\S+)\)", want_c["switches"][0]["deviceIndex"]) - if match is None: - continue - serial_num = match.groups()[0] if ( want_c["switches"][0]["ipaddr"] == have_c["switches"][0]["ipaddr"] and serial_num == have_c["switches"][0]["serialNumber"] @@ -687,6 +745,7 @@ def get_diff_merge(self): if have_c["switches"][0]["mode"] == "Migration": # Switch is already discovered using DCNM GUI # Perform assign-role/config-save/config-deploy + self.switch_snos.append(serial_num) self.node_migration = True diff_create.append(want_c) self.diff_create = diff_create @@ -707,6 +766,7 @@ def get_diff_merge(self): self.config_deploy() if not found: + self.switch_snos.append(serial_num) diff_create.append(want_c) self.diff_create = diff_create @@ -755,12 +815,13 @@ def validate_input(self): ) poap_spec = dict( - serial_number=dict(required=True, type="str"), + serial_number=dict(type="str", default=""), + preprovision_serial=dict(type="str", default=""), model=dict(required=True, type="str"), version=dict(required=True, type="str"), hostname=dict(required=True, type="str"), config_data=dict(required=True, type="dict"), - preprovision=dict(type="bool", default=False) + image_policy=dict(type="str", default="") ) msg = None @@ -774,9 +835,13 @@ def validate_input(self): msg = "seed ip/user name and password are mandatory under inventory parameters" if "poap" in inv: if state != "merged": - msg = "merged and query are only supported states for POAP" + msg = "'merged' and 'query' are only supported states for POAP" if inv["user_name"] != "admin": msg = "For poap configuration, supported user_name is 'admin'" + if inv["poap"][0].get("serial_number") is None and inv["poap"][0].get("preprovision_serial") is None: + msg = "Please provide 'serial_number' for bootstrap or 'preprovision_serial' for preprovision" + if inv["poap"][0].get("serial_number") and inv["poap"][0].get("preprovision_serial") and not self.nd: + msg = "Serial number swap is not supported in DCNM version 11" else: if state == "merged": msg = "config: element is mandatory for this state {0}".format( @@ -932,7 +997,7 @@ def ready_to_continue(inv_data): # checked first. for switch in inv_data.get("DATA"): for snos in self.switch_snos: - if snos["serialno"] == switch["serialNumber"] and switch["mode"].lower() == "migration": + if snos == switch["serialNumber"] and switch["mode"].lower() == "migration": # At least one switch is still in migration mode # so not ready to continue return False @@ -951,7 +1016,7 @@ def ready_to_continue(inv_data): # for the reload to completed. for switch in inv_data.get("DATA"): for snos in self.switch_snos: - if snos["serialno"] == switch["serialNumber"] and not snos["preprovision"] and not switch["managable"]: + if snos == switch["serialNumber"] and not switch["managable"]: # We found our first switch that changed state to # unmanageable because it's reloading. Now we can # continue @@ -964,7 +1029,7 @@ def switches_managable(inv_data): managable = True for switch in inv_data["DATA"]: for snos in self.switch_snos: - if snos["serialno"] == switch["serialNumber"] and not snos["preprovision"] and not switch["managable"]: + if snos == switch["serialNumber"] and not switch["managable"]: managable = False break @@ -981,7 +1046,7 @@ def switches_managable(inv_data): if not switch.get("preserve_config", True): all_brownfield_switches = False - while attempt < total_attempts and not all_brownfield_switches: + while attempt < total_attempts and not all_brownfield_switches and self.switch_snos: # Don't error out. We might miss the status change so worst case # scenario is that we loop 300 times and then bail out. @@ -1020,7 +1085,7 @@ def switches_managable(inv_data): for inv in get_inv["DATA"]: for snos in self.switch_snos: - if snos["serialno"] == inv["serialNumber"] and not snos["preprovision"]: + if snos == inv["serialNumber"]: self.rediscover_switch(inv["serialNumber"]) def all_switches_ok(self): @@ -1041,7 +1106,7 @@ def all_switches_ok(self): for inv in get_inv["DATA"]: for snos in self.switch_snos: - if snos["serialno"] == inv["serialNumber"] and not snos["preprovision"] and inv["status"] != "ok": + if snos == inv["serialNumber"] and inv["status"] != "ok": all_ok = False self.rediscover_switch(inv["serialNumber"]) @@ -1118,7 +1183,7 @@ def assign_role(self): if not get_role.get("DATA"): return - for create in self.want_create: + for create in self.diff_create: for role in get_role["DATA"]: if not role["switchDbID"]: msg = "Unable to get SWITCHDBID using getLanSwitchCredentials under fabric: {0}".format( @@ -1169,7 +1234,7 @@ def config_save(self): # NDFC/DCNM return error when we config-save a fabric with single Pre-provisioned switch. if not self.have_create and not self.want_create: - if len(self.want_create_poap) == 1 and self.switch_snos[0]["preprovision"]: + if len(self.want_create_poap) == 1 and not self.switch_snos: return for x in range(0, no_of_tries): @@ -1237,11 +1302,7 @@ def config_deploy(self): path = self.nd_prefix + path path = path + "/config-deploy/" - for snos in self.switch_snos: - if not snos["preprovision"]: - sernos.append(snos["serialno"]) - - switches = ",".join(sernos) + switches = ",".join(self.switch_snos) if switches: path = path + switches response = dcnm_send(self.module, method, path) @@ -1344,6 +1405,19 @@ def get_diff_query(self): self.query = query + def swap_serial(self, poap): + + method = "POST" + swap_path = "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{0}/swapSN/{1}/{2}".format( + self.fabric, poap["preprovisionSerial"], poap["serialNumber"] + ) + response = dcnm_send(self.module, method, swap_path) + self.result["response"].append(response) + fail, self.result["changed"] = self.handle_response(response, "create_poap") + + if fail: + self.failure(response) + def poap_config(self): method = "POST" @@ -1352,8 +1426,16 @@ def poap_config(self): path = self.nd_prefix + path if self.want_create_poap: poap_list = copy.deepcopy(self.want_create_poap) + for poap in poap_list: poap.pop("role") + if self.nd and poap["serialNumber"] and poap["preprovisionSerial"]: + self.swap_serial(poap) + poap["reAdd"] = "true" + if not poap["serialNumber"] and poap["preprovisionSerial"]: + poap["serialNumber"] = poap["preprovisionSerial"] + poap.pop("preprovisionSerial") + response = dcnm_send(self.module, method, path, json.dumps(poap_list)) self.result["response"].append(response) fail, self.result["changed"] = self.handle_response(response, "create_poap") @@ -1417,8 +1499,8 @@ def main(): dcnm_inv = DcnmInventory(module) dcnm_inv.validate_input() - dcnm_inv.get_want() dcnm_inv.get_have() + dcnm_inv.get_want() if module.params["state"] == "merged": dcnm_inv.get_diff_merge() diff --git a/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml b/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml index 8bb69956e..bad883176 100644 --- a/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml +++ b/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml @@ -188,13 +188,18 @@ cisco.dcnm.dcnm_inventory: *conf_role register: result +- assert: + that: + - 'result.changed == false' + - 'result.response == "The switch provided is already part of the fabric and cannot be created again"' + - name: MERGED - setup - Clean up any existing devices cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" state: deleted - name: MERGED - Pre-provision switch Configuration - cisco.dcnm.dcnm_inventory: + cisco.dcnm.dcnm_inventory: &conf_prepro fabric: '{{ ansible_it_fabric }}' state: merged # Only 2 options supported merged/query for poap config config: @@ -204,14 +209,13 @@ password: '{{ switch_password }}' role: border_gateway poap: - - serial_number: '{{ ansible_prepro_serial }}' + - preprovision_serial: '{{ ansible_prepro_serial }}' model: '{{ ansible_prepro_model }}' version: '{{ ansible_prepro_swversion }}' hostname: '{{ ansible_prepro_hostname }}' config_data: modulesModel: '{{ ansible_modules_model }}' gateway: '{{ ansible_bstrap_gateway }}' - preprovision: True when: poap_enabled == True register: result @@ -226,6 +230,17 @@ when: (poap_enabled == True) loop: '{{ result.response }}' +- name: MERGED - Pre-provision switch Configuration - Idempotence + cisco.dcnm.dcnm_inventory: *conf_prepro + when: poap_enabled == True + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response == "The switch provided is already part of the fabric and cannot be created again"' + when: (poap_enabled == True) + - name: MERGED - setup - Clean up any existing devices cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" @@ -253,7 +268,7 @@ ignore_errors: yes - name: MERGED - Poap, Pre-provision and discovered switch Configuration - cisco.dcnm.dcnm_inventory: + cisco.dcnm.dcnm_inventory: &conf_poap fabric: '{{ ansible_it_fabric }}' state: merged # Only 2 options supported merged/query for poap config config: @@ -282,14 +297,13 @@ password: '{{ switch_password }}' role: border poap: - - serial_number: '{{ ansible_prepro_serial }}' + - preprovision_serial: '{{ ansible_prepro_serial }}' model: '{{ ansible_prepro_model }}' version: '{{ ansible_prepro_swversion }}' hostname: '{{ ansible_prepro_hostname }}' config_data: modulesModel: '{{ ansible_modules_model }}' gateway: '{{ ansible_bstrap_gateway }}' - preprovision: True when: (poap_enabled == True and poap_switch_present == True) register: result @@ -304,6 +318,17 @@ when: (poap_enabled == True and poap_switch_present == True) loop: '{{ result.response }}' +- name: MERGED - Poap, Pre-provision and discovered switch Configuration - Idempotence + cisco.dcnm.dcnm_inventory: *conf_poap + when: (poap_enabled == True and poap_switch_present == True) + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response == "The switch provided is already part of the fabric and cannot be created again"' + when: (poap_enabled == True and poap_switch_present == True) + - name: MERGED - setup - Clean up any existing devices cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" diff --git a/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml b/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml index a2fddaf0a..6a7e6ccc3 100644 --- a/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml +++ b/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml @@ -211,14 +211,13 @@ password: '{{ switch_password }}' role: border_gateway poap: - - serial_number: '{{ ansible_prepro_serial }}' + - preprovision_serial: '{{ ansible_prepro_serial }}' model: '{{ ansible_prepro_model }}' version: '{{ ansible_prepro_swversion }}' hostname: '{{ ansible_prepro_hostname }}' config_data: modulesModel: '{{ ansible_modules_model }}' gateway: '{{ ansible_bstrap_gateway }}' - preprovision: True - seed_ip: "{{ ansible_switch1 }}" auth_proto: MD5 # choose from [MD5, SHA, MD5_DES, MD5_AES, SHA_DES, SHA_AES] user_name: "{{ switch_username }}" diff --git a/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json b/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json index 5c101928a..d913b28a1 100644 --- a/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json +++ b/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json @@ -224,8 +224,7 @@ "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", "hostname": "test_poap_prepro", "model": "N9K-C9300v", - "preprovision": true, - "serial_number": "9D2DAUJJFRR", + "preprovision_serial": "9D2DAUJJFRR", "version": "9.3(7)" } ] @@ -243,8 +242,7 @@ "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", "hostname": "test_poap_prepro", "model": "N9K-C9300v", - "preprovision": true, - "serial_number": "9D2DAUJJFRR", + "preprovision_serial": "9D2DAUJJFRR", "version": "9.3(7)" } ] @@ -303,8 +301,7 @@ "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", "hostname": "test_poap_prepro", "model": "N9K-C9300v", - "preprovision": true, - "serial_number": "9D2DAUJJFRR", + "preprovision_serial": "9D2DAUJJFRR", "version": "9.3(7)" } ] diff --git a/tests/unit/modules/dcnm/test_dcnm_inventory.py b/tests/unit/modules/dcnm/test_dcnm_inventory.py index ee2de5561..cfde59adc 100644 --- a/tests/unit/modules/dcnm/test_dcnm_inventory.py +++ b/tests/unit/modules/dcnm/test_dcnm_inventory.py @@ -222,19 +222,18 @@ def load_fixtures(self, response=None, device=""): if "get_have_failure" in self._testMethodName: self.run_dcnm_send.side_effect = [ - self.get_have_initial_failure, self.get_have_failure, ] elif "merge_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_inventory_initial_switch_success, self.mock_inv_discover_params, self.get_have_initial_success, self.import_switch_discover_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, - self.get_inventory_initial_switch_success, self.rediscover_switch_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, @@ -250,12 +249,12 @@ def load_fixtures(self, response=None, device=""): elif "merge_role_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_inventory_initial_switch_success, self.mock_inv_discover_params, self.get_have_initial_success, self.import_switch_discover_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, - self.get_inventory_initial_switch_success, self.rediscover_switch_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, @@ -271,11 +270,11 @@ def load_fixtures(self, response=None, device=""): elif "merge_brownfield_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_inventory_initial_switch_success, self.mock_inv_discover_params, self.get_have_initial_success, self.import_switch_discover_success, self.get_inventory_initial_switch_success, - self.get_inventory_initial_switch_success, self.rediscover_switch_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, @@ -291,9 +290,9 @@ def load_fixtures(self, response=None, device=""): elif "merge_multiple_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_have_initial_success, self.mock_inv_discover109_params, self.mock_inv_discover_params, - self.get_have_initial_success, self.import_switch_discover_success, self.import_switch_discover_success, self.get_inventory_multiple_switch_success, @@ -317,9 +316,9 @@ def load_fixtures(self, response=None, device=""): elif "merge_multiple_brownfield_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_have_initial_success, self.mock_inv_discover_params, self.mock_inv_discover107_params, - self.get_have_initial_success, self.import_switch_discover_success, self.import_switch_discover_success, self.get_inventory_multiple_bf_switch_success, @@ -342,9 +341,9 @@ def load_fixtures(self, response=None, device=""): elif "merge_multiple_brown_green_field_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_have_initial_success, self.mock_inv_discover_params, self.mock_inv_discover107_params, - self.get_have_initial_success, self.import_switch_discover_success, self.import_switch_discover_success, self.get_inventory_multiple_bf_gf_switch_success, @@ -403,8 +402,8 @@ def load_fixtures(self, response=None, device=""): elif "override_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ - self.mock_inv_discover_params, self.get_have_override_switch_success, + self.mock_inv_discover_params, self.delete_switch107_success, self.import_switch_discover_success, self.get_inventory_override_switch_success, @@ -425,8 +424,8 @@ def load_fixtures(self, response=None, device=""): elif "migration_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ - self.mock_inv_discover_params, self.get_have_migration_switch_success, + self.mock_inv_discover_params, self.get_inventory_initial_switch_success, self.set_assign_role_success, self.get_inventory_initial_switch_success, @@ -442,48 +441,47 @@ def load_fixtures(self, response=None, device=""): elif "have_initial_failure" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ - self.mock_inv_discover_params, self.get_have_initial_failure, ] elif "import_switch_discover_failure" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ - self.mock_inv_discover_params, self.get_have_initial_success, + self.mock_inv_discover_params, self.import_switch_discover_failure, ] elif "get_inventory_initial_switch_failure" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_inventory_initial_switch_failure, self.mock_inv_discover_params, self.get_have_initial_success, self.import_switch_discover_success, - self.get_inventory_initial_switch_failure, ] elif "rediscover_switch_failure" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_inventory_initial_switch_success, self.mock_inv_discover_params, self.get_have_initial_success, self.import_switch_discover_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, - self.get_inventory_initial_switch_success, self.rediscover_switch_failure, ] elif "get_lan_switch_cred_failure" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_inventory_initial_switch_success, self.mock_inv_discover_params, self.get_have_initial_success, self.import_switch_discover_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, - self.get_inventory_initial_switch_success, self.rediscover_switch_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, @@ -493,12 +491,12 @@ def load_fixtures(self, response=None, device=""): elif "set_lan_switch_cred_failure" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_inventory_initial_switch_success, self.mock_inv_discover_params, self.get_have_initial_success, self.import_switch_discover_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, - self.get_inventory_initial_switch_success, self.rediscover_switch_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, @@ -509,12 +507,12 @@ def load_fixtures(self, response=None, device=""): elif "set_assign_role_failure" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_inventory_initial_switch_success, self.mock_inv_discover_params, self.get_have_initial_success, self.import_switch_discover_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, - self.get_inventory_initial_switch_success, self.rediscover_switch_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, @@ -544,12 +542,12 @@ def load_fixtures(self, response=None, device=""): elif "config_save_switch_failure" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_inventory_initial_switch_success, self.mock_inv_discover_params, self.get_have_initial_success, self.import_switch_discover_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, - self.get_inventory_initial_switch_success, self.rediscover_switch_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, @@ -564,12 +562,12 @@ def load_fixtures(self, response=None, device=""): elif "config_deploy_switch_failure" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_inventory_initial_switch_success, self.mock_inv_discover_params, self.get_have_initial_success, self.import_switch_discover_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, - self.get_inventory_initial_switch_success, self.rediscover_switch_success, self.get_inventory_initial_switch_success, self.get_inventory_initial_switch_success, @@ -596,8 +594,8 @@ def load_fixtures(self, response=None, device=""): elif "already_created_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ - self.mock_inv_discover_params, self.get_have_already_created_switch_success, + self.mock_inv_discover_params, ] elif "already_deleted_switch" in self._testMethodName: @@ -615,8 +613,8 @@ def load_fixtures(self, response=None, device=""): elif "poap_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ - self.get_inventory_query_poap_success, self.get_have_initial_success, + self.get_inventory_query_poap_success, self.config_poap_switch_success, self.get_inventory_poap_switch_success, self.get_inventory_poap_switch_success, @@ -634,8 +632,8 @@ def load_fixtures(self, response=None, device=""): elif "poap_role_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ - self.get_inventory_query_poap_success, self.get_have_initial_success, + self.get_inventory_query_poap_success, self.config_poap_switch_success, self.get_inventory_poap_switch_success, self.get_inventory_poap_switch_success, @@ -688,9 +686,9 @@ def load_fixtures(self, response=None, device=""): elif "merge_multi_type_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ + self.get_have_initial_success, self.get_inventory_query_poap_success, self.poap_inv_discover_params, - self.get_have_initial_success, self.import_switch_discover_success, self.config_poap_switch_success, self.get_inventory_merge_multi_type_switch_success, From bb33007a6468b59eff7dcd3fed742c55648cfb0e Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Fri, 7 Oct 2022 12:33:02 +0530 Subject: [PATCH 10/26] Chages to use preprovision configs in NDFC during swap operation --- docs/cisco.dcnm.dcnm_inventory_module.rst | 13 +--- plugins/modules/dcnm_inventory.py | 76 ++++++++++++++++------- 2 files changed, 55 insertions(+), 34 deletions(-) diff --git a/docs/cisco.dcnm.dcnm_inventory_module.rst b/docs/cisco.dcnm.dcnm_inventory_module.rst index 1324657de..ba687afd1 100644 --- a/docs/cisco.dcnm.dcnm_inventory_module.rst +++ b/docs/cisco.dcnm.dcnm_inventory_module.rst @@ -133,7 +133,6 @@ Parameters
dictionary - / required
@@ -151,7 +150,6 @@ Parameters
string - / required
@@ -186,7 +184,6 @@ Parameters
string - / required
@@ -238,7 +235,6 @@ Parameters
string - / required
@@ -564,6 +560,8 @@ Examples gateway: 192.168.0.1/24 # The following pre-provisioned switch will be swapped with actual swicth in the existing fabric + # No Need to provide any other parameters for swap operation as bootstrap will inherit the preprovision configs + # If other parameters are provided it will be overidden with preprovision switch configs # This swap feature is supported only in NDFC and not on DCNM 11.x versions - name: Pre-provision switch Configuration cisco.dcnm.dcnm_inventory: @@ -578,13 +576,6 @@ Examples poap: - preprovision_serial: 1A2BCDEFGHI serial_number: 2A3BCDEFGHI - model: 'N9K-C9300v' - version: '9.3(7)' - hostname: 'PREPRO_SWITCH' - image_policy: "poap_image_policy" - config_data: - modulesModel: [N9K-X9364v, N9K-vSUP] - gateway: 192.168.0.1/24 # All the switches will be deleted in the existing fabric - name: Delete all the switches diff --git a/plugins/modules/dcnm_inventory.py b/plugins/modules/dcnm_inventory.py index 08aeb69cd..e3ce378a8 100644 --- a/plugins/modules/dcnm_inventory.py +++ b/plugins/modules/dcnm_inventory.py @@ -126,17 +126,17 @@ description: - Model of switch to Bootstrap/Pre-provision. type: str - required: true + required: false version: description: - Software version of switch to Bootstrap/Pre-provision. type: str - required: true + required: false hostname: description: - Hostname of switch to Bootstrap/Pre-provision. type: str - required: true + required: false image_policy: description: - Name of the image policy to be applied on switch during Bootstrap/Pre-provision. @@ -150,7 +150,7 @@ 'gateway' is the gateway IP with mask for the switch to Bootstrap/Pre-provision. For other supported config data please refer to NDFC/DCNM configuration guide. type: dict - required: true + required: false query_poap: description: - Query for Bootstrap(POAP) capable swicthes available. @@ -327,6 +327,8 @@ gateway: 192.168.0.1/24 # The following pre-provisioned switch will be swapped with actual swicth in the existing fabric +# No Need to provide any other parameters for swap operation as bootstrap will inherit the preprovision configs +# If other parameters are provided it will be overidden with preprovision switch configs # This swap feature is supported only in NDFC and not on DCNM 11.x versions - name: Pre-provision switch Configuration cisco.dcnm.dcnm_inventory: @@ -341,13 +343,6 @@ poap: - preprovision_serial: 1A2BCDEFGHI serial_number: 2A3BCDEFGHI - model: 'N9K-C9300v' - version: '9.3(7)' - hostname: 'PREPRO_SWITCH' - image_policy: "poap_image_policy" - config_data: - modulesModel: [N9K-X9364v, N9K-vSUP] - gateway: 192.168.0.1/24 # All the switches will be deleted in the existing fabric - name: Delete all the switches @@ -817,10 +812,10 @@ def validate_input(self): poap_spec = dict( serial_number=dict(type="str", default=""), preprovision_serial=dict(type="str", default=""), - model=dict(required=True, type="str"), - version=dict(required=True, type="str"), - hostname=dict(required=True, type="str"), - config_data=dict(required=True, type="dict"), + model=dict(type="str", default=""), + version=dict(type="str", default=""), + hostname=dict(type="str", default=""), + config_data=dict(type="dict", default={}), image_policy=dict(type="str", default="") ) @@ -840,6 +835,13 @@ def validate_input(self): msg = "For poap configuration, supported user_name is 'admin'" if inv["poap"][0].get("serial_number") is None and inv["poap"][0].get("preprovision_serial") is None: msg = "Please provide 'serial_number' for bootstrap or 'preprovision_serial' for preprovision" + if ((inv["poap"][0].get("serial_number") and inv["poap"][0].get("preprovision_serial") is None) or + (inv["poap"][0].get("preprovision_serial") and inv["poap"][0].get("serial_number") is None)): + if (inv["poap"][0].get("model") is None or inv["poap"][0].get("version") is None or + inv["poap"][0].get("hostname") is None or not inv["poap"][0]["config_data"]): + msg = "model, version, hostname and config_data must be provided for {0}".format( + "Preprovisioning/Bootstraping a switch" + ) if inv["poap"][0].get("serial_number") and inv["poap"][0].get("preprovision_serial") and not self.nd: msg = "Serial number swap is not supported in DCNM version 11" else: @@ -1418,6 +1420,34 @@ def swap_serial(self, poap): if fail: self.failure(response) + method = "GET" + path = "/rest/control/fabrics/{0}/inventory/poap".format( + self.fabric + ) + if self.nd: + path = self.nd_prefix + path + response = dcnm_send(self.module, method, path) + self.result["response"].append(response) + fail, self.result["changed"] = self.handle_response(response, "query") + + if fail: + self.module.fail_json(msg=response) + + if "DATA" in response: + for resp in response["DATA"]: + if (resp["serialNumber"] == poap["serialNumber"]): + resp.update({"password": poap["password"]}) + resp.update({"discoveryAuthProtocol": "0"}) + resp.pop("seedSwitchFlag") + return resp + + msg = "Specified switch {0}".format( + poap["serial_number"] + " is not listed in bootstrap devices or inventory. " + + "If you are trying to pre-provision, please set " + + "'preprovision_serial' instead of 'serial_number' in your task") + + self.module.fail_json(msg=response) + def poap_config(self): method = "POST" @@ -1427,14 +1457,14 @@ def poap_config(self): if self.want_create_poap: poap_list = copy.deepcopy(self.want_create_poap) - for poap in poap_list: - poap.pop("role") - if self.nd and poap["serialNumber"] and poap["preprovisionSerial"]: - self.swap_serial(poap) - poap["reAdd"] = "true" - if not poap["serialNumber"] and poap["preprovisionSerial"]: - poap["serialNumber"] = poap["preprovisionSerial"] - poap.pop("preprovisionSerial") + for i in range(len(poap_list)): + poap_list[i].pop("role") + if self.nd and poap_list[i]["serialNumber"] and poap_list[i]["preprovisionSerial"]: + poap_list[i] = self.swap_serial(poap_list[i]) + if not poap_list[i]["serialNumber"] and poap_list[i]["preprovisionSerial"]: + poap_list[i]["serialNumber"] = poap_list[i]["preprovisionSerial"] + if "preprovisionSerial" in poap_list[i]: + poap_list[i].pop("preprovisionSerial") response = dcnm_send(self.module, method, path, json.dumps(poap_list)) self.result["response"].append(response) From 420d3268be56438f5a91ad7470266f7d06341fed Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 20 Oct 2022 16:06:04 -0400 Subject: [PATCH 11/26] Create Jenkinsfile --- jenkins/Jenkinsfile | 110 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 jenkins/Jenkinsfile diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile new file mode 100644 index 000000000..8a3fd12e4 --- /dev/null +++ b/jenkins/Jenkinsfile @@ -0,0 +1,110 @@ +pipeline { + agent any + + parameters { + string(name: 'NDFC_IP_ADDRESS', defaultValue: 'rtp-ndfc1.cisco.com', description: 'NDFC IP or DNS Name') + string(name: 'NDFC_GITHUB_BRANCH', defaultValue: 'fix_dcnm_network_tests', description: 'NDFC Ansible Collection GitHub Branch') + string(name: 'NDFC_FABRIC_NAME', defaultValue: 'Test-Fabric', description: 'NDFC Fabric Name') + string(name: 'NDFC_CONTROLLER_VERSION', defaultValue: '12', description: 'NDFC Controller Version') + string(name: 'NDFC_SWITCH1_IP', defaultValue: "${env.NDFC_SWITCH1_IP}", description: 'NDFC NXOS SWITCH1_IP') + string(name: 'NDFC_SWITCH2_IP', defaultValue: "${env.NDFC_SWITCH2_IP}", description: 'NDFC NXOS SWITCH2_IP') + string(name: 'NDFC_SWITCH3_IP', defaultValue: "${env.NDFC_SWITCH3_IP}", description: 'NDFC NXOS SWITCH3_IP') + string(name: 'NDFC_TEST_ROLE', defaultValue: 'dcnm_network', description: 'NDFC Integration Test Role') + string(name: 'NDFC_TEST_NAME', defaultValue: 'query', description: 'NDFC Integration Test Name') + + } + + stages { + stage("Select GitHub Branch") { + environment { + HTTP_PROXY = "${env.HTTP_PROXY}" + HTTPS_PROXY = "${env.HTTP_PROXY}" + http_proxy = "${env.HTTP_PROXY}" + https_proxy = "${env.HTTP_PROXY}" + } + steps { + dir("/var/jenkins_home/ndfc/collections/ansible_collections/cisco/dcnm") { + sh """#!/bin/bash + env + echo 'divide' + echo 'divide' + git branch --set-upstream-to=origin/${params.NDFC_GITHUB_BRANCH} ${params.NDFC_GITHUB_BRANCH} + git fetch origin + git checkout ${params.NDFC_GITHUB_BRANCH} + git pull + git log -n 5 + """ + } + } + } + stage("Build Ansible Inventory") { + environment { + NDFC_CREDS = credentials('ndfc_credentials') + NXOS_CREDS = credentials('nxos_credentials') + HTTP_PROXY = "${env.HTTP_PROXY}" + HTTPS_PROXY = "${env.HTTP_PROXY}" + http_proxy = "${env.HTTP_PROXY}" + https_proxy = "${env.HTTP_PROXY}" + } + steps { + withPythonEnv('/var/jenkins_home/.pyenv/versions/ndfc-ansible/bin/python') { + dir("/var/jenkins_home/ndfc/collections/ansible_collections/playbooks") { + sh """#!/bin/bash + pip install jinja2 + python build_inventory.py $NDFC_CREDS_USR \ + $NDFC_CREDS_PSW \ + ${params.NDFC_IP_ADDRESS} \ + ${params.NDFC_SWITCH1_IP} \ + ${params.NDFC_SWITCH2_IP} \ + ${params.NDFC_SWITCH3_IP} \ + $NXOS_CREDS_USR \ + $NXOS_CREDS_PSW + """ + } + } + } + } + stage("Build Ansible Tests File") { + environment { + HTTP_PROXY = "${env.HTTP_PROXY}" + HTTPS_PROXY = "${env.HTTP_PROXY}" + http_proxy = "${env.HTTP_PROXY}" + https_proxy = "${env.HTTP_PROXY}" + } + steps { + withPythonEnv('/var/jenkins_home/.pyenv/versions/ndfc-ansible/bin/python') { + dir("/var/jenkins_home/ndfc/collections/ansible_collections/playbooks") { + sh """#!/bin/bash + python build_tests.py ${params.NDFC_TEST_NAME} \ + ${params.NDFC_FABRIC_NAME} \ + ${params.NDFC_CONTROLLER_VERSION} \ + ${params.NDFC_TEST_ROLE} \ + """ + } + } + } + } + stage("Run NDFC Integration Tests") { + environment { + HTTP_PROXY = "${env.HTTP_PROXY}" + HTTPS_PROXY = "${env.HTTP_PROXY}" + http_proxy = "${env.HTTP_PROXY}" + https_proxy = "${env.HTTP_PROXY}" + } + steps { + withPythonEnv('/var/jenkins_home/.pyenv/versions/ndfc-ansible/bin/python') { + dir("/var/jenkins_home/ndfc/ansible/hacking") { + sh '''#!/bin/bash + pip install requests + pip install -r /var/jenkins_home/ndfc/ansible/requirements.txt + source ndfc_setup + ansible --version + cd /var/jenkins_home/ndfc/collections/ansible_collections/playbooks + ansible-playbook -i ndfc_inventory.yml ndfc_tests.yml -vvvv + ''' + } + } + } + } + } +} From fba174bc0ae076f144a850905c18dbdf6823ba2b Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 20 Oct 2022 16:51:33 -0400 Subject: [PATCH 12/26] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 63a8874c2..e3e5366f3 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ # Cisco DCNM Collection +mwiebe - update + The Ansible Cisco Nexus® Dashboard Fabric Controller (NDFC) (formerly Cisco Data Center Network Manager (DCNM)) collection includes modules to help automate common day 2 operations for VXLAN EVPN fabrics. This collection is intended for use with the following release versions: From 3cb318bb39ffeaff907225ecf3280d4a60f1c9b2 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 20 Oct 2022 17:01:22 -0400 Subject: [PATCH 13/26] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index e3e5366f3..f628d9f56 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ # Cisco DCNM Collection -mwiebe - update The Ansible Cisco Nexus® Dashboard Fabric Controller (NDFC) (formerly Cisco Data Center Network Manager (DCNM)) collection includes modules to help automate common day 2 operations for VXLAN EVPN fabrics. From db8d7bffaf1a0cbfdf40997c49dc45f86dbdc2e9 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Fri, 21 Oct 2022 08:47:32 -0400 Subject: [PATCH 14/26] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f628d9f56..ae949560c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ [![Actions Status](https://github.com/CiscoDevNet/ansible-dcnm/workflows/CI/badge.svg)](https://github.com/CiscoDevNet/ansible-dcnm/actions) +Mike - Update # Cisco DCNM Collection From f0dcd1804cb4b052c241fe16c839aeed16237fb1 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Tue, 1 Nov 2022 11:05:24 -0400 Subject: [PATCH 15/26] Remove Jenkins Changes --- README.md | 2 - jenkins/Jenkinsfile | 110 -------------------------------------------- 2 files changed, 112 deletions(-) delete mode 100644 jenkins/Jenkinsfile diff --git a/README.md b/README.md index ae949560c..63a8874c2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ [![Actions Status](https://github.com/CiscoDevNet/ansible-dcnm/workflows/CI/badge.svg)](https://github.com/CiscoDevNet/ansible-dcnm/actions) -Mike - Update # Cisco DCNM Collection - The Ansible Cisco Nexus® Dashboard Fabric Controller (NDFC) (formerly Cisco Data Center Network Manager (DCNM)) collection includes modules to help automate common day 2 operations for VXLAN EVPN fabrics. This collection is intended for use with the following release versions: diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile deleted file mode 100644 index 8a3fd12e4..000000000 --- a/jenkins/Jenkinsfile +++ /dev/null @@ -1,110 +0,0 @@ -pipeline { - agent any - - parameters { - string(name: 'NDFC_IP_ADDRESS', defaultValue: 'rtp-ndfc1.cisco.com', description: 'NDFC IP or DNS Name') - string(name: 'NDFC_GITHUB_BRANCH', defaultValue: 'fix_dcnm_network_tests', description: 'NDFC Ansible Collection GitHub Branch') - string(name: 'NDFC_FABRIC_NAME', defaultValue: 'Test-Fabric', description: 'NDFC Fabric Name') - string(name: 'NDFC_CONTROLLER_VERSION', defaultValue: '12', description: 'NDFC Controller Version') - string(name: 'NDFC_SWITCH1_IP', defaultValue: "${env.NDFC_SWITCH1_IP}", description: 'NDFC NXOS SWITCH1_IP') - string(name: 'NDFC_SWITCH2_IP', defaultValue: "${env.NDFC_SWITCH2_IP}", description: 'NDFC NXOS SWITCH2_IP') - string(name: 'NDFC_SWITCH3_IP', defaultValue: "${env.NDFC_SWITCH3_IP}", description: 'NDFC NXOS SWITCH3_IP') - string(name: 'NDFC_TEST_ROLE', defaultValue: 'dcnm_network', description: 'NDFC Integration Test Role') - string(name: 'NDFC_TEST_NAME', defaultValue: 'query', description: 'NDFC Integration Test Name') - - } - - stages { - stage("Select GitHub Branch") { - environment { - HTTP_PROXY = "${env.HTTP_PROXY}" - HTTPS_PROXY = "${env.HTTP_PROXY}" - http_proxy = "${env.HTTP_PROXY}" - https_proxy = "${env.HTTP_PROXY}" - } - steps { - dir("/var/jenkins_home/ndfc/collections/ansible_collections/cisco/dcnm") { - sh """#!/bin/bash - env - echo 'divide' - echo 'divide' - git branch --set-upstream-to=origin/${params.NDFC_GITHUB_BRANCH} ${params.NDFC_GITHUB_BRANCH} - git fetch origin - git checkout ${params.NDFC_GITHUB_BRANCH} - git pull - git log -n 5 - """ - } - } - } - stage("Build Ansible Inventory") { - environment { - NDFC_CREDS = credentials('ndfc_credentials') - NXOS_CREDS = credentials('nxos_credentials') - HTTP_PROXY = "${env.HTTP_PROXY}" - HTTPS_PROXY = "${env.HTTP_PROXY}" - http_proxy = "${env.HTTP_PROXY}" - https_proxy = "${env.HTTP_PROXY}" - } - steps { - withPythonEnv('/var/jenkins_home/.pyenv/versions/ndfc-ansible/bin/python') { - dir("/var/jenkins_home/ndfc/collections/ansible_collections/playbooks") { - sh """#!/bin/bash - pip install jinja2 - python build_inventory.py $NDFC_CREDS_USR \ - $NDFC_CREDS_PSW \ - ${params.NDFC_IP_ADDRESS} \ - ${params.NDFC_SWITCH1_IP} \ - ${params.NDFC_SWITCH2_IP} \ - ${params.NDFC_SWITCH3_IP} \ - $NXOS_CREDS_USR \ - $NXOS_CREDS_PSW - """ - } - } - } - } - stage("Build Ansible Tests File") { - environment { - HTTP_PROXY = "${env.HTTP_PROXY}" - HTTPS_PROXY = "${env.HTTP_PROXY}" - http_proxy = "${env.HTTP_PROXY}" - https_proxy = "${env.HTTP_PROXY}" - } - steps { - withPythonEnv('/var/jenkins_home/.pyenv/versions/ndfc-ansible/bin/python') { - dir("/var/jenkins_home/ndfc/collections/ansible_collections/playbooks") { - sh """#!/bin/bash - python build_tests.py ${params.NDFC_TEST_NAME} \ - ${params.NDFC_FABRIC_NAME} \ - ${params.NDFC_CONTROLLER_VERSION} \ - ${params.NDFC_TEST_ROLE} \ - """ - } - } - } - } - stage("Run NDFC Integration Tests") { - environment { - HTTP_PROXY = "${env.HTTP_PROXY}" - HTTPS_PROXY = "${env.HTTP_PROXY}" - http_proxy = "${env.HTTP_PROXY}" - https_proxy = "${env.HTTP_PROXY}" - } - steps { - withPythonEnv('/var/jenkins_home/.pyenv/versions/ndfc-ansible/bin/python') { - dir("/var/jenkins_home/ndfc/ansible/hacking") { - sh '''#!/bin/bash - pip install requests - pip install -r /var/jenkins_home/ndfc/ansible/requirements.txt - source ndfc_setup - ansible --version - cd /var/jenkins_home/ndfc/collections/ansible_collections/playbooks - ansible-playbook -i ndfc_inventory.yml ndfc_tests.yml -vvvv - ''' - } - } - } - } - } -} From ea40753bf3d5ef02f7843f3788ce2e87c96f1792 Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Tue, 1 Nov 2022 20:48:36 +0530 Subject: [PATCH 16/26] Changes based on review comments --- plugins/modules/dcnm_inventory.py | 16 +- .../dcnm_inventory/tests/dcnm/merged.yaml | 147 ------- .../dcnm_inventory/tests/dcnm/poap.yaml | 272 +++++++++++++ .../dcnm_inventory/tests/dcnm/query.yaml | 67 ---- .../modules/dcnm/fixtures/dcnm_inventory.json | 139 +++++-- .../unit/modules/dcnm/test_dcnm_inventory.py | 359 +++++++++++++++++- 6 files changed, 752 insertions(+), 248 deletions(-) create mode 100644 tests/integration/targets/dcnm_inventory/tests/dcnm/poap.yaml diff --git a/plugins/modules/dcnm_inventory.py b/plugins/modules/dcnm_inventory.py index e3ce378a8..7e4530f8a 100644 --- a/plugins/modules/dcnm_inventory.py +++ b/plugins/modules/dcnm_inventory.py @@ -153,7 +153,7 @@ required: false query_poap: description: - - Query for Bootstrap(POAP) capable swicthes available. + - Query for Bootstrap(POAP) capable switches available. type: bool required: false default: false @@ -245,6 +245,15 @@ role: leaf preserve_config: False # boolean, default is true +# The following task will enable Bootstrap and DHCP on an existing fabric. +# Please note that only bootstrap and DHCP configs are present in the below example. +# You have to add other existing fabric configs to the task. +- name: Bootstrap and DHCP Configuration + cisco.dcnm.dcnm_rest: + method: PUT + path: /appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/vxlan-fabric + json_data: '{"fabricId": "FABRIC-7","fabricName": "vxlan-fabric","id": 7,"nvPairs":{...,"BOOTSTRAP_ENABLE": true,"DHCP_ENABLE": true,"DHCP_IPV6_ENABLE": "DHCPv4","DHCP_START": "192.168.1.10", "DHCP_END": "192.168.1.20","MGMT_GW": "192.168.123.1","MGMT_PREFIX": "24",...},"templateName": "Easy_Fabric"}' + # The following switch will be Bootstrapped and merged into the existing fabric - name: Poap switch Configuration cisco.dcnm.dcnm_inventory: @@ -326,7 +335,7 @@ modulesModel: [N9K-X9364v, N9K-vSUP] gateway: 192.168.0.1/24 -# The following pre-provisioned switch will be swapped with actual swicth in the existing fabric +# The following pre-provisioned switch will be swapped with actual switch in the existing fabric # No Need to provide any other parameters for swap operation as bootstrap will inherit the preprovision configs # If other parameters are provided it will be overidden with preprovision switch configs # This swap feature is supported only in NDFC and not on DCNM 11.x versions @@ -828,6 +837,7 @@ def validate_input(self): or "password" not in inv ): msg = "seed ip/user name and password are mandatory under inventory parameters" + break if "poap" in inv: if state != "merged": msg = "'merged' and 'query' are only supported states for POAP" @@ -883,7 +893,7 @@ def validate_input(self): if "seed_ip" not in inv: msg = "seed ip is mandatory under inventory parameters for switch deletion" if "poap" in inv: - msg = "merged and query are only supported states for POAP" + msg = "'merged' and 'query' are only supported states for POAP" if msg: self.module.fail_json(msg=msg) diff --git a/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml b/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml index bad883176..fd1988a27 100644 --- a/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml +++ b/tests/integration/targets/dcnm_inventory/tests/dcnm/merged.yaml @@ -2,12 +2,6 @@ ## SETUP ## ############################################## -- set_fact: - poap_enabled: False - -- set_fact: - poap_switch_present: False - - set_fact: rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" when: controller_version == "11" @@ -26,10 +20,6 @@ that: - 'result.response.DATA != None' -- set_fact: - poap_enabled: True - when: result.response.DATA.nvPairs["BOOTSTRAP_ENABLE"] == "true" - - name: MERGED - setup - Clean up any existing devices cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" @@ -198,143 +188,6 @@ fabric: "{{ ansible_it_fabric }}" state: deleted -- name: MERGED - Pre-provision switch Configuration - cisco.dcnm.dcnm_inventory: &conf_prepro - fabric: '{{ ansible_it_fabric }}' - state: merged # Only 2 options supported merged/query for poap config - config: - # All the values below are mandatory if poap configuration is being done - state is merged - - seed_ip: '{{ ansible_prepro_switch }}' - user_name: '{{ switch_username }}' - password: '{{ switch_password }}' - role: border_gateway - poap: - - preprovision_serial: '{{ ansible_prepro_serial }}' - model: '{{ ansible_prepro_model }}' - version: '{{ ansible_prepro_swversion }}' - hostname: '{{ ansible_prepro_hostname }}' - config_data: - modulesModel: '{{ ansible_modules_model }}' - gateway: '{{ ansible_bstrap_gateway }}' - when: poap_enabled == True - register: result - -- assert: - that: - - 'result.changed == true' - when: (poap_enabled == True) - -- assert: - that: - - 'item["RETURN_CODE"] == 200' - when: (poap_enabled == True) - loop: '{{ result.response }}' - -- name: MERGED - Pre-provision switch Configuration - Idempotence - cisco.dcnm.dcnm_inventory: *conf_prepro - when: poap_enabled == True - register: result - -- assert: - that: - - 'result.changed == false' - - 'result.response == "The switch provided is already part of the fabric and cannot be created again"' - when: (poap_enabled == True) - -- name: MERGED - setup - Clean up any existing devices - cisco.dcnm.dcnm_inventory: - fabric: "{{ ansible_it_fabric }}" - state: deleted - when: (poap_enabled == True) - -- name: MERGED - Query for POAP enabled swicthes - cisco.dcnm.dcnm_inventory: - fabric: "{{ ansible_it_fabric }}" - query_poap: True - state: query - when: poap_enabled == True - register: result - -- assert: - that: - - 'result.response != None' - when: poap_enabled == True - -- name: Check poap query for switches - set_fact: - poap_switch_present: True - when: (poap_enabled == True and (item['serialNumber'] == '{{ ansible_poap_serial }}')) - loop: '{{ result.response }}' - ignore_errors: yes - -- name: MERGED - Poap, Pre-provision and discovered switch Configuration - cisco.dcnm.dcnm_inventory: &conf_poap - fabric: '{{ ansible_it_fabric }}' - state: merged # Only 2 options supported merged/query for poap config - config: - # All the values below are mandatory if poap configuration is being done - state is merged - - seed_ip: '{{ ansible_poap_switch }}' - user_name: '{{ switch_username }}' - password: '{{ switch_password }}' - role: border_gateway - poap: - - serial_number: '{{ ansible_poap_serial }}' - model: '{{ ansible_poap_model }}' - version: '{{ ansible_poap_swversion }}' - hostname: '{{ ansible_poap_hostname }}' - config_data: - modulesModel: '{{ ansible_modules_model }}' - gateway: '{{ ansible_bstrap_gateway }}' - - seed_ip: '{{ ansible_switch1 }}' - user_name: '{{ switch_username }}' - password: '{{ switch_password }}' - auth_proto: MD5 - max_hops: 0 - preserve_config: False - role: spine - - seed_ip: '{{ ansible_prepro_switch }}' - user_name: '{{ switch_username }}' - password: '{{ switch_password }}' - role: border - poap: - - preprovision_serial: '{{ ansible_prepro_serial }}' - model: '{{ ansible_prepro_model }}' - version: '{{ ansible_prepro_swversion }}' - hostname: '{{ ansible_prepro_hostname }}' - config_data: - modulesModel: '{{ ansible_modules_model }}' - gateway: '{{ ansible_bstrap_gateway }}' - when: (poap_enabled == True and poap_switch_present == True) - register: result - -- assert: - that: - - 'result.changed == true' - when: (poap_enabled == True and poap_switch_present == True) - -- assert: - that: - - 'item["RETURN_CODE"] == 200' - when: (poap_enabled == True and poap_switch_present == True) - loop: '{{ result.response }}' - -- name: MERGED - Poap, Pre-provision and discovered switch Configuration - Idempotence - cisco.dcnm.dcnm_inventory: *conf_poap - when: (poap_enabled == True and poap_switch_present == True) - register: result - -- assert: - that: - - 'result.changed == false' - - 'result.response == "The switch provided is already part of the fabric and cannot be created again"' - when: (poap_enabled == True and poap_switch_present == True) - -- name: MERGED - setup - Clean up any existing devices - cisco.dcnm.dcnm_inventory: - fabric: "{{ ansible_it_fabric }}" - state: deleted - when: (poap_enabled == True) - - name: MERGED - Merge a Switch without seed_ip cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" diff --git a/tests/integration/targets/dcnm_inventory/tests/dcnm/poap.yaml b/tests/integration/targets/dcnm_inventory/tests/dcnm/poap.yaml new file mode 100644 index 000000000..fb8c13708 --- /dev/null +++ b/tests/integration/targets/dcnm_inventory/tests/dcnm/poap.yaml @@ -0,0 +1,272 @@ +############################################## +## SETUP ## +############################################## + +- set_fact: + poap_enabled: False + +- set_fact: + poap_switch_present: False + +- set_fact: + rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" + when: controller_version == "11" + +- set_fact: + rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ ansible_it_fabric }}" + when: controller_version >= "12" + +# Below commented tasks are sample tasks to enable Bootstrap and DHCP along with DHCP configs +# Please make sure you provide correct values for required fields +# Fabric config has many NDFC/DCNM auto generated values, so always GET the configs first +# and then set the required values. +# +# - name: POAP MERGED - Get the configs of the fabric deployed. +# cisco.dcnm.dcnm_rest: +# method: GET +# path: "{{ rest_path }}" +# register: result +# +# - set_fact: +# result.response.DATA.nvPairs["BOOTSTRAP_ENABLE"] = true +# result.response.DATA.nvPairs["DHCP_ENABLE"] = true +# result.response.DATA.nvPairs["DHCP_IPV6_ENABLE"] = "DHCPv4" +# result.response.DATA.nvPairs["DHCP_START"] = "192.168.1.10" +# result.response.DATA.nvPairs["DHCP_END"] = "192.168.1.20" +# result.response.DATA.nvPairs["MGMT_GW"] = "192.168.1.1" +# result.response.DATA.nvPairs["MGMT_PREFIX"] = "24" +# +# - name: POAP MERGED - Configure Bootstrap and DHCP on Fabric +# cisco.dcnm.dcnm_rest: +# method: PUT +# path: "{{ rest_path }}" +# json_data: "{{ result.response.DATA }}" +# + +- name: POAP MERGED - Verify if fabric is deployed. + cisco.dcnm.dcnm_rest: + method: GET + path: "{{ rest_path }}" + register: result + +- assert: + that: + - 'result.response.DATA != None' + +- set_fact: + poap_enabled: True + when: result.response.DATA.nvPairs["BOOTSTRAP_ENABLE"] == "true" + +- name: POAP MERGED - setup - Clean up any existing devices + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + state: deleted + +############################################## +## MERGED ## +############################################## + +- name: POAP MERGED - Pre-provision switch Configuration + cisco.dcnm.dcnm_inventory: &conf_prepro + fabric: '{{ ansible_it_fabric }}' + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: '{{ ansible_prepro_switch }}' + user_name: '{{ switch_username }}' + password: '{{ switch_password }}' + role: border_gateway + poap: + - preprovision_serial: '{{ ansible_prepro_serial }}' + model: '{{ ansible_prepro_model }}' + version: '{{ ansible_prepro_swversion }}' + hostname: '{{ ansible_prepro_hostname }}' + config_data: + modulesModel: '{{ ansible_modules_model }}' + gateway: '{{ ansible_bstrap_gateway }}' + when: poap_enabled == True + register: result + +- assert: + that: + - 'result.changed == true' + when: (poap_enabled == True) + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + when: (poap_enabled == True) + loop: '{{ result.response }}' + +- name: POAP MERGED - Pre-provision switch Configuration - Idempotence + cisco.dcnm.dcnm_inventory: *conf_prepro + when: poap_enabled == True + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response == "The switch provided is already part of the fabric and cannot be created again"' + when: (poap_enabled == True) + +- name: POAP MERGED - setup - Clean up any existing devices + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + state: deleted + when: (poap_enabled == True) + +- name: POAP MERGED - Query for POAP enabled swicthes + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + query_poap: True + state: query + when: poap_enabled == True + register: result + +- assert: + that: + - 'result.response != None' + when: poap_enabled == True + +- name: Check poap query for switches + set_fact: + poap_switch_present: True + when: (poap_enabled == True and (item['serialNumber'] == '{{ ansible_poap_serial }}')) + loop: '{{ result.response }}' + ignore_errors: yes + +- name: POAP MERGED - Poap, Pre-provision and discovered switch Configuration + cisco.dcnm.dcnm_inventory: &conf_poap + fabric: '{{ ansible_it_fabric }}' + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: '{{ ansible_poap_switch }}' + user_name: '{{ switch_username }}' + password: '{{ switch_password }}' + role: border_gateway + poap: + - serial_number: '{{ ansible_poap_serial }}' + model: '{{ ansible_poap_model }}' + version: '{{ ansible_poap_swversion }}' + hostname: '{{ ansible_poap_hostname }}' + config_data: + modulesModel: '{{ ansible_modules_model }}' + gateway: '{{ ansible_bstrap_gateway }}' + - seed_ip: '{{ ansible_switch1 }}' + user_name: '{{ switch_username }}' + password: '{{ switch_password }}' + auth_proto: MD5 + max_hops: 0 + preserve_config: False + role: spine + - seed_ip: '{{ ansible_prepro_switch }}' + user_name: '{{ switch_username }}' + password: '{{ switch_password }}' + role: border + poap: + - preprovision_serial: '{{ ansible_prepro_serial }}' + model: '{{ ansible_prepro_model }}' + version: '{{ ansible_prepro_swversion }}' + hostname: '{{ ansible_prepro_hostname }}' + config_data: + modulesModel: '{{ ansible_modules_model }}' + gateway: '{{ ansible_bstrap_gateway }}' + when: (poap_enabled == True and poap_switch_present == True) + register: result + +- assert: + that: + - 'result.changed == true' + when: (poap_enabled == True and poap_switch_present == True) + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + when: (poap_enabled == True and poap_switch_present == True) + loop: '{{ result.response }}' + +- name: POAP MERGED - Poap, Pre-provision and discovered switch Configuration - Idempotence + cisco.dcnm.dcnm_inventory: *conf_poap + when: (poap_enabled == True and poap_switch_present == True) + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response == "The switch provided is already part of the fabric and cannot be created again"' + when: (poap_enabled == True and poap_switch_present == True) + +- name: POAP MERGED - setup - Clean up any existing devices + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + state: deleted + when: (poap_enabled == True) + +############################################# +# QUERY ## +############################################# + +- name: POAP QUERY - Pre-provision switch Configuration + cisco.dcnm.dcnm_inventory: + fabric: '{{ ansible_it_fabric }}' + state: merged # Only 2 options supported merged/query for poap config + config: + # All the values below are mandatory if poap configuration is being done - state is merged + - seed_ip: '{{ ansible_prepro_switch }}' + user_name: '{{ switch_username }}' + password: '{{ switch_password }}' + role: border_gateway + poap: + - preprovision_serial: '{{ ansible_prepro_serial }}' + model: '{{ ansible_prepro_model }}' + version: '{{ ansible_prepro_swversion }}' + hostname: '{{ ansible_prepro_hostname }}' + config_data: + modulesModel: '{{ ansible_modules_model }}' + gateway: '{{ ansible_bstrap_gateway }}' + - seed_ip: "{{ ansible_switch1 }}" + auth_proto: MD5 # choose from [MD5, SHA, MD5_DES, MD5_AES, SHA_DES, SHA_AES] + user_name: "{{ switch_username }}" + password: "{{ switch_password }}" + max_hops: 0 + role: leaf # default is Leaf - choose from [Leaf, Spine, Border, Border Spine, Border Gateway, Border Gateway Spine + # Super Spine, Border Super Spine, Border Gateway Super Spine] + preserve_config: False # boolean, default is true + when: (poap_enabled == True) + register: result + +- assert: + that: + - 'result.changed == true' + when: (poap_enabled == True) + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + when: (poap_enabled == True) + loop: '{{ result.response }}' + +- name: POAP QUERY - Query a Switch without a config element + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + state: query + when: (poap_enabled == True) + register: result + +- assert: + that: + - '"{{ ansible_prepro_switch }}" or "{{ ansible_switch1 }}" in result.response[0].ipAddress' + - '"border gateway" or "leaf" in result.response[0].switchRole' + - '"{{ ansible_prepro_switch }}" or "{{ ansible_switch1 }}" in result.response[1].ipAddress' + - '"border gateway" or "leaf" in result.response[1].switchRole' + when: (poap_enabled == True) + +############################################## +## CLEAN-UP ## +############################################## + +- name: POAP QUERY - cleanup - Clean up any existing devices + cisco.dcnm.dcnm_inventory: + fabric: "{{ ansible_it_fabric }}" + state: deleted diff --git a/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml b/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml index 6a7e6ccc3..728e05b30 100644 --- a/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml +++ b/tests/integration/targets/dcnm_inventory/tests/dcnm/query.yaml @@ -2,9 +2,6 @@ ## SETUP ## ############################################## -- set_fact: - poap_enabled: False - - set_fact: rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" when: controller_version == "11" @@ -23,10 +20,6 @@ that: - 'result.response.DATA != None' -- set_fact: - poap_enabled: True - when: result.response.DATA.nvPairs["BOOTSTRAP_ENABLE"] == "true" - - name: QUERY - setup - Clean up any existing devices cisco.dcnm.dcnm_inventory: fabric: "{{ ansible_it_fabric }}" @@ -195,66 +188,6 @@ - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.response[1].ipAddress' - 'result.response[1].switchRole == "border gateway"' -- name: QUERY - cleanup - Clean up any existing devices - cisco.dcnm.dcnm_inventory: - fabric: "{{ ansible_it_fabric }}" - state: deleted - -- name: QUERY - Pre-provision switch Configuration - cisco.dcnm.dcnm_inventory: - fabric: '{{ ansible_it_fabric }}' - state: merged # Only 2 options supported merged/query for poap config - config: - # All the values below are mandatory if poap configuration is being done - state is merged - - seed_ip: '{{ ansible_prepro_switch }}' - user_name: '{{ switch_username }}' - password: '{{ switch_password }}' - role: border_gateway - poap: - - preprovision_serial: '{{ ansible_prepro_serial }}' - model: '{{ ansible_prepro_model }}' - version: '{{ ansible_prepro_swversion }}' - hostname: '{{ ansible_prepro_hostname }}' - config_data: - modulesModel: '{{ ansible_modules_model }}' - gateway: '{{ ansible_bstrap_gateway }}' - - seed_ip: "{{ ansible_switch1 }}" - auth_proto: MD5 # choose from [MD5, SHA, MD5_DES, MD5_AES, SHA_DES, SHA_AES] - user_name: "{{ switch_username }}" - password: "{{ switch_password }}" - max_hops: 0 - role: leaf # default is Leaf - choose from [Leaf, Spine, Border, Border Spine, Border Gateway, Border Gateway Spine - # Super Spine, Border Super Spine, Border Gateway Super Spine] - preserve_config: False # boolean, default is true - when: (poap_enabled == True) - register: result - -- assert: - that: - - 'result.changed == true' - when: (poap_enabled == True) - -- assert: - that: - - 'item["RETURN_CODE"] == 200' - when: (poap_enabled == True) - loop: '{{ result.response }}' - -- name: QUERY - Query a Switch without a config element - cisco.dcnm.dcnm_inventory: - fabric: "{{ ansible_it_fabric }}" - state: query - when: (poap_enabled == True) - register: result - -- assert: - that: - - '"{{ ansible_prepro_switch }}" or "{{ ansible_switch1 }}" in result.response[0].ipAddress' - - '"border gateway" or "leaf" in result.response[0].switchRole' - - '"{{ ansible_prepro_switch }}" or "{{ ansible_switch1 }}" in result.response[1].ipAddress' - - '"border gateway" or "leaf" in result.response[1].switchRole' - when: (poap_enabled == True) - ############################################## ## CLEAN-UP ## ############################################## diff --git a/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json b/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json index d913b28a1..255cb7213 100644 --- a/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json +++ b/tests/unit/modules/dcnm/fixtures/dcnm_inventory.json @@ -182,11 +182,11 @@ "playbook_poap_switch_config" : [ { "password": "password", - "seed_ip": "192.168.123.223", + "seed_ip": "192.168.1.223", "user_name": "admin", "poap": [ { - "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", "hostname": "test_poap_test", "model": "N9K-C9300v", "serial_number": "9D2DAUJJFQQ", @@ -196,15 +196,33 @@ } ], + "playbook_poap_swap_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.1.223", + "user_name": "admin", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", + "hostname": "test_poap_test", + "model": "N9K-C9300v", + "serial_number": "9D2DAUJJFQQ", + "preprovision_serial": "9D2DAUJJFRR", + "version": "9.3(7)" + } + ] + } + ], + "playbook_poap_role_switch_config" : [ { "password": "password", - "seed_ip": "192.168.123.223", + "seed_ip": "192.168.1.223", "user_name": "admin", "role": "border_gateway", "poap": [ { - "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", "hostname": "test_poap_test", "model": "N9K-C9300v", "serial_number": "9D2DAUJJFQQ", @@ -217,11 +235,11 @@ "playbook_preprovision_switch_config" : [ { "password": "password", - "seed_ip": "192.168.123.224", + "seed_ip": "192.168.1.224", "user_name": "admin", "poap": [ { - "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", "hostname": "test_poap_prepro", "model": "N9K-C9300v", "preprovision_serial": "9D2DAUJJFRR", @@ -234,12 +252,12 @@ "playbook_preprovision_role_switch_config" : [ { "password": "password", - "seed_ip": "192.168.123.224", + "seed_ip": "192.168.1.224", "user_name": "admin", "role": "border_gateway", "poap": [ { - "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", "hostname": "test_poap_prepro", "model": "N9K-C9300v", "preprovision_serial": "9D2DAUJJFRR", @@ -252,11 +270,11 @@ "playbook_poap_wrong_user_switch_config" : [ { "password": "password", - "seed_ip": "192.168.123.223", + "seed_ip": "192.168.1.223", "user_name": "user", "poap": [ { - "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", "hostname": "test_poap_test", "model": "N9K-C9300v", "serial_number": "9D2DAUJJFQQ", @@ -269,12 +287,12 @@ "playbook_merge_multi_type_switch_config" : [ { "password": "password", - "seed_ip": "192.168.123.223", + "seed_ip": "192.168.1.223", "user_name": "admin", "role": "border_gateway", "poap": [ { - "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", "hostname": "test_poap_test", "model": "N9K-C9300v", "serial_number": "9D2DAUJJFQQ", @@ -288,17 +306,17 @@ "password": "password", "preserve_config": false, "role": "border_gateway", - "seed_ip": "192.168.123.219", + "seed_ip": "192.168.1.219", "user_name": "username" }, { "password": "password", - "seed_ip": "192.168.123.224", + "seed_ip": "192.168.1.224", "user_name": "admin", "role": "border_gateway", "poap": [ { - "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.123.1/24\"}", + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", "hostname": "test_poap_prepro", "model": "N9K-C9300v", "preprovision_serial": "9D2DAUJJFRR", @@ -308,6 +326,69 @@ } ], + "playbook_poap_no_pre_ser_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.1.223", + "user_name": "admin", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", + "hostname": "test_poap_test", + "model": "N9K-C9300v", + "version": "9.3(7)" + } + ] + } + ], + + "playbook_poap_no_model_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.1.223", + "user_name": "admin", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", + "hostname": "test_poap_test", + "serial_number": "9D2DAUJJFQQ", + "version": "9.3(7)" + } + ] + } + ], + + "playbook_poap_swap_dcnm11_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.1.224", + "user_name": "admin", + "poap": [ + { + "config_data": "{\"modulesModel\": [\"N9K-C9300v\"], \"gateway\": \"192.168.1.1/24\"}", + "hostname": "test_poap_prepro", + "model": "N9K-C9300v", + "serial_number": "9D2DAUJJFQQ", + "preprovision_serial": "9D2DAUJJFRR", + "version": "9.3(7)" + } + ] + } + ], + + "playbook_no_user_switch_config" : [ + { + "password": "password", + "seed_ip": "192.168.1.224" + } + ], + + "playbook_no_ip_deleted_switch_config" : [ + { + "password": "password", + "user_name": "admin" + } + ], "mock_inv_discover_params": { "ERROR": "", "RETURN_CODE": 200, @@ -2975,7 +3056,7 @@ ], "displayValues": [ "n9k-110", - "192.168.123.217", + "192.168.1.217", "kharicha-fabric", "FGE19030RSA", "52", @@ -2993,7 +3074,7 @@ "fid": 3, "health": -1, "index": 1, - "ipAddress": "192.168.123.217", + "ipAddress": "192.168.1.217", "isLan": false, "isNonNexus": false, "isPmCollect": false, @@ -3072,7 +3153,7 @@ "serialNumber": "9D2DAUJJFQQ", "model": "N9K-C9300v", "version": "9.3(7)", - "data": "{\"gateway\": \"192.168.123.1/24\", \"modulesModel\": [\"N9K-X9364v\", \"N9K-vSUP\"]}", + "data": "{\"gateway\": \"192.168.1.1/24\", \"modulesModel\": [\"N9K-X9364v\", \"N9K-vSUP\"]}", "publicKey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDBuJ+5KalTYzNMr8F6SsgCHdGlF0r1+as+4OrRwpMxFHK69g9OLdaWMHQ1IzSyUJqGi5j13ITrhr+Ooc3qeFjyLAzkawKFCEyeoKniaqgPYjkn7PT+XWTCuIrTsg8s6E7V5IdWdMGB2GWoYdZ3pUR/Bz2VGa3eEnzJd8nMBRLydZ51BzPq5c+TGO1eBAaeSgz0UaAATvYZNOzJxVaQGzZsvgjK5eMAf5dYDHP4BBZzb3HOAazsww+8C6Jx3vlVpzBQeNt08UMCJnY+1XTKO4SMVJ5ztV6a3P5F4IX9SDGQV1Cy35bx2mG7Nc3V2BTQ6ACl424Lajo9DaUJg3yHVK28Y3V9TZC10+q3p34qCLePO9fo0peTMS4jrL0YntBiKo8r5M3PGYozRfKnZxGJJSqt3Z5BKK1ED8QtQuUkEmWDT1JK2OiVr250SMjEfPxwGqArbLVGzbC2XokGoWCOSxwWI4vlebsudFHc166wrYSpEcj3fRQIOP04eIeWt27Zjdk=", "fingerprint": "MD5:05:97:51:63:09:85:52:59:17:4f:2d:01:c7:36:11:03", "reAdd": false, @@ -3118,7 +3199,7 @@ ], "displayValues": [ "n9k-110", - "192.168.123.223", + "192.168.1.223", "kharicha-fabric", "9D2DAUJJFQQ", "52", @@ -3136,7 +3217,7 @@ "fid": 3, "health": -1, "index": 1, - "ipAddress": "192.168.123.223", + "ipAddress": "192.168.1.223", "isLan": false, "isNonNexus": false, "isPmCollect": false, @@ -3232,7 +3313,7 @@ ], "displayValues": [ "n9k-110", - "192.168.123.224", + "192.168.1.224", "kharicha-fabric", "9D2DAUJJFRR", "52", @@ -3250,7 +3331,7 @@ "fid": 3, "health": -1, "index": 1, - "ipAddress": "192.168.123.224", + "ipAddress": "192.168.1.224", "isLan": false, "isNonNexus": false, "isPmCollect": false, @@ -3332,7 +3413,7 @@ "auth": true, "deviceIndex": "poap_test_static(944XJW7L3YJ)", "hopCount": 0, - "ipaddr": "192.168.123.219", + "ipaddr": "192.168.1.219", "known": false, "lastChange": null, "platform": "N9K-C9508", @@ -3362,7 +3443,7 @@ "model":"N9K-C9300v", "version":"None", "upTime":42021, - "ipAddress":"192.168.123.219", + "ipAddress":"192.168.1.219", "mgmtAddress":"None", "vendor":"Cisco", "displayHdrs":[ @@ -3378,7 +3459,7 @@ ], "displayValues":[ "poap_test_static", - "192.168.123.219", + "192.168.1.219", "kharicha-fabric", "944XJW7L3YJ", "64", @@ -3487,7 +3568,7 @@ "model":"N9K-C9300v", "version":"None", "upTime":166169597441, - "ipAddress":"192.168.123.224", + "ipAddress":"192.168.1.224", "mgmtAddress":"None", "vendor":"None", "displayHdrs":[ @@ -3503,7 +3584,7 @@ ], "displayValues":[ "test_poap_prepro", - "192.168.123.224", + "192.168.1.224", "kharicha-fabric", "9D2DAUJJFRR", "0", @@ -3612,7 +3693,7 @@ "model":"N9K-C9300v", "version":"None", "upTime":36548, - "ipAddress":"192.168.123.223", + "ipAddress":"192.168.1.223", "mgmtAddress":"None", "vendor":"Cisco", "displayHdrs":[ @@ -3628,7 +3709,7 @@ ], "displayValues":[ "test_poap_test", - "192.168.123.223", + "192.168.1.223", "kharicha-fabric", "9D2DAUJJFQQ", "64", diff --git a/tests/unit/modules/dcnm/test_dcnm_inventory.py b/tests/unit/modules/dcnm/test_dcnm_inventory.py index cfde59adc..74fda66fa 100644 --- a/tests/unit/modules/dcnm/test_dcnm_inventory.py +++ b/tests/unit/modules/dcnm/test_dcnm_inventory.py @@ -39,6 +39,8 @@ class TestDcnmInvModule(TestDcnmModule): SUCCESS_RETURN_CODE = 200 + version = 11 + playbook_merge_switch_config = test_data.get("playbook_merge_switch_config") playbook_merge_role_switch_config = test_data.get( "playbook_merge_role_switch_config" @@ -72,6 +74,13 @@ class TestDcnmInvModule(TestDcnmModule): playbook_preprovision_role_switch_config = test_data.get("playbook_preprovision_role_switch_config") playbook_poap_wrong_user_switch_config = test_data.get("playbook_poap_wrong_user_switch_config") playbook_merge_multi_type_switch_config = test_data.get("playbook_merge_multi_type_switch_config") + playbook_poap_no_pre_ser_switch_config = test_data.get("playbook_poap_no_pre_ser_switch_config") + playbook_poap_no_model_switch_config = test_data.get("playbook_poap_no_model_switch_config") + playbook_poap_swap_dcnm11_switch_config = test_data.get("playbook_poap_swap_dcnm11_switch_config") + playbook_no_user_switch_config = test_data.get("playbook_no_user_switch_config") + playbook_no_ip_deleted_switch_config = test_data.get("playbook_no_ip_deleted_switch_config") + playbook_poap_no_merged_query_switch_config = test_data.get("playbook_poap_no_merged_query_switch_config") + playbook_poap_swap_switch_config = test_data.get("playbook_poap_swap_switch_config") # initial merge switch success get_have_initial_success = test_data.get("get_have_initial_success") @@ -215,7 +224,11 @@ def tearDown(self): def load_fixtures(self, response=None, device=""): - self.run_dcnm_version_supported.return_value = 11 + if self.version == 12: + self.run_dcnm_version_supported.return_value = 12 + else: + self.run_dcnm_version_supported.return_value = 11 + self.run_dcnm_fabric_details.return_value = { "nvPairs": {"GRFIELD_DEBUG_FLAG": "Enable"} } @@ -338,6 +351,29 @@ def load_fixtures(self, response=None, device=""): self.config_deploy_switch_success, ] + elif "merge_multiple_dcnm12_brown_green_field_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_have_initial_success, + self.mock_inv_discover_params, + self.mock_inv_discover107_params, + self.import_switch_discover_success, + self.import_switch_discover_success, + self.get_inventory_multiple_bf_gf_switch_success, + self.get_inventory_multiple_bf_gf_switch_success, + self.rediscover_switch107_success, + self.rediscover_switch_success, + self.get_inventory_multiple_bf_gf_switch_success, + self.get_inventory_multiple_bf_gf_switch_success, + self.get_lan_multiple_new_bf_switch_cred_success, + self.get_inventory_multiple_bf_gf_switch_success, + self.set_assign_role_success, + self.set_assign_role_success, + self.get_fabric_id_success, + self.config_save_switch_success, + self.config_deploy_switch_success, + ] + elif "merge_multiple_brown_green_field_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ @@ -363,6 +399,13 @@ def load_fixtures(self, response=None, device=""): self.config_deploy_switch_success, ] + elif "delete_dcnm12_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_have_one_switch_success, + self.delete_switch_success, + ] + elif "delete_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ @@ -385,6 +428,13 @@ def load_fixtures(self, response=None, device=""): self.delete_switch_success, ] + elif "query_dcnm12_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_have_one_switch_success, + self.get_inventory_query_switch_success, + ] + elif "query_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ @@ -610,6 +660,25 @@ def load_fixtures(self, response=None, device=""): self.get_inventory_query_poap_success, ] + elif "poap_dcnm12_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_have_initial_success, + self.get_inventory_query_poap_success, + self.config_poap_switch_success, + self.get_inventory_poap_switch_success, + self.get_inventory_poap_switch_success, + self.rediscover_switch_success, + self.get_inventory_poap_switch_success, + self.get_inventory_poap_switch_success, + self.get_lan_switch_cred_success, + self.get_inventory_poap_switch_success, + self.set_assign_role_success, + self.get_fabric_id_success, + self.config_save_switch_success, + self.config_deploy_switch_success, + ] + elif "poap_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ @@ -629,6 +698,27 @@ def load_fixtures(self, response=None, device=""): self.config_deploy_switch_success, ] + elif "poap_swap_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_have_initial_success, + self.get_inventory_query_poap_success, + self.config_poap_switch_success, + self.get_inventory_query_poap_success, + self.config_poap_switch_success, + self.get_inventory_poap_switch_success, + self.get_inventory_poap_switch_success, + self.rediscover_switch_success, + self.get_inventory_poap_switch_success, + self.get_inventory_poap_switch_success, + self.get_lan_switch_cred_success, + self.get_inventory_poap_switch_success, + self.set_assign_role_success, + self.get_fabric_id_success, + self.config_save_switch_success, + self.config_deploy_switch_success, + ] + elif "poap_role_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ @@ -683,6 +773,39 @@ def load_fixtures(self, response=None, device=""): elif "poap_wrong_user_switch" in self._testMethodName: self.init_data() + elif "poap_no_pre_ser_switch" in self._testMethodName: + self.init_data() + + elif "poap_no_model_switch" in self._testMethodName: + self.init_data() + + elif "poap_swap_dcnm11_switch" in self._testMethodName: + self.init_data() + + elif "no_user_switch" in self._testMethodName: + self.init_data() + + elif "no_ip_deleted_switch" in self._testMethodName: + self.init_data() + + elif "poap_no_merged_query_switch" in self._testMethodName: + self.init_data() + + elif "poap_overridden_switch" in self._testMethodName: + self.init_data() + + elif "poap_idempotent_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_inventory_merge_multi_type_switch_success, + ] + + elif "prepro_idempotent_switch" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.get_inventory_merge_multi_type_switch_success, + ] + elif "merge_multi_type_switch" in self._testMethodName: self.init_data() self.run_dcnm_send.side_effect = [ @@ -1125,7 +1248,7 @@ def test_dcnm_inv_query_poap_switch_fabric(self): ) ) result = self.execute_module(changed=False, failed=False) - self.assertEqual(result["response"][0]["ipAddress"], "192.168.123.217") + self.assertEqual(result["response"][0]["ipAddress"], "192.168.1.217") self.assertEqual(result["response"][0]["switchRole"], "leaf") self.assertEqual(result["response"][1]["model"], "N9K-C9300v") self.assertEqual(result["response"][1]["serialNumber"], "9D2DAUJJFQQ") @@ -1222,3 +1345,235 @@ def test_dcnm_inv_poap_merge_multi_type_switch_fabric(self): for resp in result["response"]: self.assertEqual(resp["RETURN_CODE"], 200) + + def test_dcnm_inv_poap_no_pre_ser_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_poap_no_pre_ser_switch_config, + ) + ) + + result = self.execute_module(changed=False, failed=True) + + self.assertEqual( + result.get("msg"), + "Please provide 'serial_number' for bootstrap or 'preprovision_serial' for preprovision", + ) + + def test_dcnm_inv_poap_no_model_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_poap_no_model_switch_config, + ) + ) + + result = self.execute_module(changed=False, failed=True) + + self.assertEqual( + result.get("msg"), + "model, version, hostname and config_data must be provided for Preprovisioning/Bootstraping a switch", + ) + + def test_dcnm_inv_poap_swap_dcnm11_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_poap_swap_dcnm11_switch_config, + ) + ) + + result = self.execute_module(changed=False, failed=True) + + self.assertEqual( + result.get("msg"), + "Serial number swap is not supported in DCNM version 11", + ) + + def test_dcnm_inv_no_user_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_no_user_switch_config, + ) + ) + + result = self.execute_module(changed=False, failed=True) + + self.assertEqual( + result.get("msg"), + "seed ip/user name and password are mandatory under inventory parameters", + ) + + def test_dcnm_inv_poap_no_merged_query_switch_fabric(self): + set_module_args( + dict( + state="deleted", + fabric="kharicha-fabric", + config=self.playbook_poap_switch_config, + ) + ) + + result = self.execute_module(changed=False, failed=True) + + self.assertEqual( + result.get("msg"), + "'merged' and 'query' are only supported states for POAP", + ) + + def test_dcnm_inv_no_ip_deleted_switch_fabric(self): + set_module_args( + dict( + state="deleted", + fabric="kharicha-fabric", + config=self.playbook_no_ip_deleted_switch_config, + ) + ) + + result = self.execute_module(changed=False, failed=True) + + self.assertEqual( + result.get("msg"), + "seed ip is mandatory under inventory parameters for switch deletion", + ) + + def test_dcnm_inv_poap_overridden_switch_fabric(self): + set_module_args( + dict( + state="overridden", + fabric="kharicha-fabric", + config=self.playbook_poap_switch_config, + ) + ) + + result = self.execute_module(changed=False, failed=True) + + self.assertEqual( + result.get("msg"), + "'merged' and 'query' are only supported states for POAP", + ) + + def test_dcnm_inv_poap_idempotent_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_poap_switch_config, + ) + ) + + result = self.execute_module(changed=False, failed=False) + + self.assertEqual( + result.get("response"), + "The switch provided is already part of the fabric and cannot be created again", + ) + + def test_dcnm_inv_prepro_idempotent_switch_fabric(self): + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_preprovision_switch_config, + ) + ) + + result = self.execute_module(changed=False, failed=False) + + self.assertEqual( + result.get("response"), + "The switch provided is already part of the fabric and cannot be created again", + ) + + def test_dcnm_inv_merge_multiple_dcnm12_brown_green_field_switch_fabric(self): + self.version = 12 + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_merge_bf_gf_multiple_switch_config, + ) + ) + + result = self.execute_module(changed=True, failed=False) + + self.version = 11 + + for resp in result["response"]: + self.assertEqual(resp["RETURN_CODE"], 200) + self.assertEqual(resp["MESSAGE"], "OK") + + def test_dcnm_inv_delete_dcnm12_switch_fabric(self): + self.version = 12 + set_module_args( + dict( + state="deleted", + fabric="kharicha-fabric", + config=self.playbook_delete_switch_config, + ) + ) + + result = self.execute_module(changed=True, failed=False) + + self.version = 11 + + for resp in result["response"]: + self.assertEqual(resp["RETURN_CODE"], 200) + self.assertEqual(resp["MESSAGE"], "OK") + + def test_dcnm_inv_query_dcnm12_switch_fabric(self): + self.version = 12 + set_module_args( + dict( + state="query", + fabric="kharicha-fabric", + config=self.playbook_query_switch_config, + ) + ) + result = self.execute_module(changed=False, failed=False) + + self.version = 11 + + self.assertEqual(result["response"][0]["ipAddress"], "192.168.1.110") + self.assertEqual(result["response"][0]["switchRole"], "leaf") + + def test_dcnm_inv_poap_dcnm12_switch_fabric(self): + self.version = 12 + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_poap_switch_config, + ) + ) + + result = self.execute_module(changed=True, failed=False) + + self.version = 11 + + for resp in result["response"]: + self.assertEqual(resp["RETURN_CODE"], 200) + self.assertEqual(resp["MESSAGE"], "OK") + + def test_dcnm_inv_poap_swap_switch_fabric(self): + self.version = 12 + set_module_args( + dict( + state="merged", + fabric="kharicha-fabric", + config=self.playbook_poap_swap_switch_config, + ) + ) + + result = self.execute_module(changed=True, failed=False) + + self.version = 11 + + for resp in result["response"]: + self.assertEqual(resp["RETURN_CODE"], 200) + self.assertEqual(resp["MESSAGE"], "OK") From 20b134963970bec93702eb1608b8a0d9861dfe45 Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Tue, 1 Nov 2022 21:00:49 +0530 Subject: [PATCH 17/26] Changes based on review comments --- plugins/modules/dcnm_inventory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/dcnm_inventory.py b/plugins/modules/dcnm_inventory.py index 7e4530f8a..bf2443aa6 100644 --- a/plugins/modules/dcnm_inventory.py +++ b/plugins/modules/dcnm_inventory.py @@ -252,7 +252,7 @@ cisco.dcnm.dcnm_rest: method: PUT path: /appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/vxlan-fabric - json_data: '{"fabricId": "FABRIC-7","fabricName": "vxlan-fabric","id": 7,"nvPairs":{...,"BOOTSTRAP_ENABLE": true,"DHCP_ENABLE": true,"DHCP_IPV6_ENABLE": "DHCPv4","DHCP_START": "192.168.1.10", "DHCP_END": "192.168.1.20","MGMT_GW": "192.168.123.1","MGMT_PREFIX": "24",...},"templateName": "Easy_Fabric"}' + json_data: '{"fabricId": "FABRIC-7","fabricName": "vxlan-fabric","id": 7,"nvPairs":{...,"BOOTSTRAP_ENABLE": true,"DHCP_ENABLE": true,"DHCP_IPV6_ENABLE": "DHCPv4","DHCP_START": "192.168.1.10", "DHCP_END": "192.168.1.20","MGMT_GW": "192.168.123.1","MGMT_PREFIX": "24",...},"templateName": "Easy_Fabric"}' # noqa # The following switch will be Bootstrapped and merged into the existing fabric - name: Poap switch Configuration From 842805e148f7af446891ac743035f6e730459d39 Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Wed, 2 Nov 2022 12:14:47 +0530 Subject: [PATCH 18/26] Changes based on review comments --- docs/cisco.dcnm.dcnm_inventory_module.rst | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/cisco.dcnm.dcnm_inventory_module.rst b/docs/cisco.dcnm.dcnm_inventory_module.rst index ba687afd1..3a353b93b 100644 --- a/docs/cisco.dcnm.dcnm_inventory_module.rst +++ b/docs/cisco.dcnm.dcnm_inventory_module.rst @@ -358,7 +358,7 @@ Parameters -
Query for Bootstrap(POAP) capable swicthes available.
+
Query for Bootstrap(POAP) capable switches available.
@@ -478,6 +478,15 @@ Examples role: leaf preserve_config: False # boolean, default is true + # The following task will enable Bootstrap and DHCP on an existing fabric. + # Please note that only bootstrap and DHCP configs are present in the below example. + # You have to add other existing fabric configs to the task. + - name: Bootstrap and DHCP Configuration + cisco.dcnm.dcnm_rest: + method: PUT + path: /appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/vxlan-fabric + json_data: '{"fabricId": "FABRIC-7","fabricName": "vxlan-fabric","id": 7,"nvPairs":{...,"BOOTSTRAP_ENABLE": true,"DHCP_ENABLE": true,"DHCP_IPV6_ENABLE": "DHCPv4","DHCP_START": "192.168.1.10", "DHCP_END": "192.168.1.20","MGMT_GW": "192.168.123.1","MGMT_PREFIX": "24",...},"templateName": "Easy_Fabric"}' # noqa + # The following switch will be Bootstrapped and merged into the existing fabric - name: Poap switch Configuration cisco.dcnm.dcnm_inventory: @@ -559,7 +568,7 @@ Examples modulesModel: [N9K-X9364v, N9K-vSUP] gateway: 192.168.0.1/24 - # The following pre-provisioned switch will be swapped with actual swicth in the existing fabric + # The following pre-provisioned switch will be swapped with actual switch in the existing fabric # No Need to provide any other parameters for swap operation as bootstrap will inherit the preprovision configs # If other parameters are provided it will be overidden with preprovision switch configs # This swap feature is supported only in NDFC and not on DCNM 11.x versions From 938e3c9848ee19883f09ecab74c87570a6d187f9 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 3 Nov 2022 13:44:15 -0400 Subject: [PATCH 19/26] More dcnm_network test fixes --- .../targets/dcnm_network/tasks/dcnm.yaml | 2 +- .../targets/dcnm_network/tasks/main.yaml | 6 +- .../dcnm_network/tests/dcnm/deleted.yaml | 129 +++++--- .../dcnm_network/tests/dcnm/merged.yaml | 310 ++++++++++-------- .../dcnm_network/tests/dcnm/overridden.yaml | 82 +++-- .../dcnm_network/tests/dcnm/query.yaml | 30 +- .../dcnm_network/tests/dcnm/replaced.yaml | 120 ++++--- .../dcnm/self-contained-tests/scale.yaml | 12 +- .../self-contained-tests/sm_dhcp_params.yaml | 6 +- .../self-contained-tests/sm_dhcp_update.yaml | 10 +- .../self-contained-tests/sm_mcast_params.yaml | 6 +- .../self-contained-tests/sm_mcast_update.yaml | 10 +- .../self-contained-tests/so_dhcp_update.yaml | 10 +- .../self-contained-tests/so_mcast_update.yaml | 10 +- 14 files changed, 435 insertions(+), 308 deletions(-) diff --git a/tests/integration/targets/dcnm_network/tasks/dcnm.yaml b/tests/integration/targets/dcnm_network/tasks/dcnm.yaml index 7b79644bc..59de4568f 100644 --- a/tests/integration/targets/dcnm_network/tasks/dcnm.yaml +++ b/tests/integration/targets/dcnm_network/tasks/dcnm.yaml @@ -14,7 +14,7 @@ set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - name: run test cases (connection=httpapi) - include: "{{ test_case_to_run }}" + include_tasks: "{{ test_case_to_run }}" with_items: "{{ test_items }}" loop_control: loop_var: test_case_to_run diff --git a/tests/integration/targets/dcnm_network/tasks/main.yaml b/tests/integration/targets/dcnm_network/tasks/main.yaml index 5cf05091a..284fb30ad 100644 --- a/tests/integration/targets/dcnm_network/tasks/main.yaml +++ b/tests/integration/targets/dcnm_network/tasks/main.yaml @@ -34,12 +34,12 @@ - name: Remove all existing networks to start with a clean state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: Create vrfs required for this test and remove all other vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: overridden config: - vrf_name: ansible-vrf-int1 @@ -64,4 +64,4 @@ - ip_address: "{{ ansible_switch2 }}" deploy: true -- { include: dcnm.yaml, tags: ['dcnm'] } +- { include_tasks: dcnm.yaml, tags: ['dcnm'] } diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/deleted.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/deleted.yaml index 9abf2ccbc..80a516fa5 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/deleted.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/deleted.yaml @@ -3,11 +3,11 @@ ############################################## - set_fact: - rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/rest/control/fabrics/{{ test_fabric }}" when: controller_version == "11" - set_fact: - rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ test_fabric }}" when: controller_version >= "12" - name: DELETED - Verify if fabric - Fabric1 is deployed. @@ -22,16 +22,12 @@ - name: DELETED - setup - Clean up any existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: DELETED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: DELETED - Create, Attach and Deploy Single Network with multiple switch Attach cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -51,6 +47,16 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -67,17 +73,13 @@ - 'result.diff[0].net_id == 7005' - 'result.diff[0].vrf_name == "Tenant-1"' -- name: DELETED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - ############################################### ### DELETED ## ############################################### - name: DELETED - Delete Single Network with deleted state cisco.dcnm.dcnm_network: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted config: - net_name: ansible-net13 @@ -116,13 +118,9 @@ - 'result.response|length == 0' - 'result.diff|length == 0' -- name: DELETED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: DELETED - Create, Attach and Deploy Multiple Network with single switch Attach cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -170,13 +168,20 @@ - 'result.diff[1].net_id == 7002' - 'result.diff[1].vrf_name == "Tenant-2"' -- name: DELETED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 - name: DELETED - Delete Single Network with deleted state and verify other network is still there cisco.dcnm.dcnm_network: &conf1 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted config: - net_name: ansible-net12 @@ -214,7 +219,7 @@ - name: DELETED - Delete the other Single Network with deleted state and verify no network is present now cisco.dcnm.dcnm_network: &conf2 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted config: - net_name: ansible-net13 @@ -250,13 +255,9 @@ - 'result.response|length == 0' - 'result.diff|length == 0' -- name: DELETED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: DELETED - Create, Attach and Deploy Multiple Network with single switch Attach cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -284,6 +285,17 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -304,13 +316,9 @@ - 'result.diff[1].net_id == 7002' - 'result.diff[1].vrf_name == "Tenant-2"' -- name: DELETED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: DELETED - Delete all the networks cisco.dcnm.dcnm_network: &conf3 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted register: result @@ -347,7 +355,7 @@ - name: DELETED - Create L2 only networks along with all dhcp, arp options cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -374,6 +382,16 @@ deploy: True register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -398,13 +416,9 @@ - 'result.diff[0].dhcp_srvr3_ip == "3.3.3.3"' - 'result.diff[0].dhcp_srvr3_vrf == "three"' -- name: DELETED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: DELETED - setup - Clean up l2_only existing network cisco.dcnm.dcnm_network: &conf4 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted config: - net_name: ansible-net13 @@ -453,7 +467,7 @@ - name: DELETED - Create a L2 only and L3 networks along with all dhcp, arp options cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -463,7 +477,9 @@ vlan_id: 3504 gw_ip_subnet: '152.168.30.1/24' mtu_l3intf: 7600 - arp_suppress: True + # Leave 'arp_suppress' this false to avoid TCAM issue + # Please configure TCAM region for Ingress ARP-Ether ACL before configuring ARP supression + arp_suppress: False int_desc: 'test interface' is_l2only: True vlan_name: testvlan @@ -485,7 +501,9 @@ vlan_id: 3505 gw_ip_subnet: '162.168.30.1/24' mtu_l3intf: 7600 - arp_suppress: True + # Leave 'arp_suppress' this false to avoid TCAM issue + # Please configure TCAM region for Ingress ARP-Ether ACL before configuring ARP supression + arp_suppress: False int_desc: 'test interface 1' vlan_name: testvlan1 dhcp_srvr1_ip: '1.1.1.1' @@ -501,6 +519,17 @@ deploy: True register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -515,7 +544,7 @@ - 'result.diff[0].net_name == "ansible-net13"' - 'result.diff[0].net_id == 7009' - 'result.diff[0].vrf_name == "NA"' - - 'result.diff[0].arp_suppress == true' + - 'result.diff[0].arp_suppress == false' - 'result.diff[0].int_desc == "test interface"' - 'result.diff[0].is_l2only == true' - 'result.diff[0].mtu_l3intf == 7600' @@ -530,7 +559,7 @@ - 'result.diff[1].net_name == "ansible-net12"' - 'result.diff[1].net_id == 7010' - 'result.diff[1].vrf_name == "Tenant-2"' - - 'result.diff[1].arp_suppress == true' + - 'result.diff[1].arp_suppress == false' - 'result.diff[1].int_desc == "test interface 1"' - 'result.diff[1].mtu_l3intf == 7600' - 'result.diff[1].vlan_name == "testvlan1"' @@ -541,13 +570,9 @@ - 'result.diff[1].dhcp_srvr3_ip == "3.3.3.3"' - 'result.diff[1].dhcp_srvr3_vrf == "three"' -- name: DELETED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: DELETED - Delete all the networks cisco.dcnm.dcnm_network: &conf5 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted register: result @@ -584,7 +609,7 @@ - name: DELETED - Delete Single Network with no network name cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted config: - net_name: @@ -603,7 +628,7 @@ - name: DELETED - Delete Single Network with invalid network name which is not configured cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted config: - net_name: network @@ -626,5 +651,5 @@ - name: DELETED - setup - remove any networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/merged.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/merged.yaml index 3f52fdaa5..c13e3b39f 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/merged.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/merged.yaml @@ -3,11 +3,11 @@ ############################################## - set_fact: - rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/rest/control/fabrics/{{ test_fabric }}" when: controller_version == "11" - set_fact: - rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ test_fabric }}" when: controller_version >= "12" - name: MERGED - Verify if fabric - Fabric1 is deployed. @@ -22,20 +22,16 @@ - name: MERGED - setup - Clean up any existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - ############################################## ## MERGED ## ############################################## - name: MERGED - Create New Network without Deploy cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -48,6 +44,18 @@ deploy: False register: result +- name: Query fabric for creation of Network Object + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.displayName is search('ansible-net13')" + - "query_result.response[0].parent.networkId is search('7005')" + - "query_result.response[0].parent.vrf is search('Tenant-1')" + retries: 5 + delay: 2 + - assert: that: - 'result.changed == true' @@ -57,22 +65,14 @@ - 'result.diff[0].net_id == 7005' - 'result.diff[0].vrf_name == "Tenant-1"' -- name: MERGED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: MERGED - setup - Clean up any existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - Create New Network with Deploy cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -85,6 +85,18 @@ deploy: True register: result +- name: Query fabric for creation of Network Object + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.displayName is search('ansible-net13')" + - "query_result.response[0].parent.networkId is search('7005')" + - "query_result.response[0].parent.vrf is search('Tenant-1')" + retries: 5 + delay: 2 + - assert: that: - 'result.changed == true' @@ -94,22 +106,14 @@ - 'result.diff[0].net_id == 7005' - 'result.diff[0].vrf_name == "Tenant-1"' -- name: MERGED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: MERGED - setup - Clean up any existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - Create New Network with Attach and global deploy cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -125,6 +129,16 @@ deploy: True register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -138,22 +152,14 @@ - 'result.diff[0].net_id == 7005' - 'result.diff[0].vrf_name == "Tenant-1"' -- name: MERGED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: MERGED - setup - Clean up any existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - Create New Network with Attach specific deploy and no global deploy cisco.dcnm.dcnm_network: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -170,6 +176,16 @@ deploy: False register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -183,10 +199,6 @@ - 'result.diff[0].net_id == 7005' - 'result.diff[0].vrf_name == "Tenant-1"' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - conf - Idempotence cisco.dcnm.dcnm_network: *conf register: result @@ -198,16 +210,12 @@ - name: MERGED - setup - Clean up any existing network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - Create, Attach and Deploy Multiple Network with Single Switch Attach cisco.dcnm.dcnm_network: &conf1 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -235,6 +243,17 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -255,10 +274,6 @@ - 'result.diff[1].net_id == 7002' - 'result.diff[1].vrf_name == "Tenant-2"' -- name: MERGED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: MERGED - conf1 - Idempotence cisco.dcnm.dcnm_network: *conf1 register: result @@ -270,16 +285,12 @@ - name: MERGED - setup - Clean up any existing network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: MERGED - Create, Attach and Deploy Single Network with Multiple Switch Attach cisco.dcnm.dcnm_network: &conf2 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -299,6 +310,16 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -315,10 +336,6 @@ - 'result.diff[0].net_id == 7005' - 'result.diff[0].vrf_name == "Tenant-1"' -- name: MERGED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: MERGED - conf2 - Idempotence cisco.dcnm.dcnm_network: *conf2 register: result @@ -330,16 +347,12 @@ - name: MERGED - setup - Clean up any existing network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: MERGED - Create, Attach and Deploy Multiple Network with Multiple Switch Attach - Automatic Vlan Proposed cisco.dcnm.dcnm_network: &conf3 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -372,6 +385,17 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -394,10 +418,6 @@ - 'result.diff[1].net_id == 7002' - 'result.diff[1].vrf_name == "Tenant-2"' -- name: MERGED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: MERGED - conf3 - Idempotence cisco.dcnm.dcnm_network: *conf3 register: result @@ -409,16 +429,12 @@ - name: MERGED - setup - Clean up any existing network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: MERGED - Create, Attach and Deploy Single Network with Multiple Switch Attach - Routing Tag Specified cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -439,6 +455,16 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -455,22 +481,14 @@ - 'result.diff[0].net_id == 7005' - 'result.diff[0].vrf_name == "Tenant-1"' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - setup - Clean up any existing network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - Create L2 only networks along with all dhcp, arp options cisco.dcnm.dcnm_network: &conf4 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -497,6 +515,16 @@ deploy: True register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -521,26 +549,23 @@ - 'result.diff[0].dhcp_srvr3_ip == "3.3.3.3"' - 'result.diff[0].dhcp_srvr3_vrf == "three"' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - conf4 - Idempotence cisco.dcnm.dcnm_network: *conf4 register: result +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + - name: MERGED - setup - Clean up any existing network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - Create L3 networks along with all dhcp, arp options cisco.dcnm.dcnm_network: &conf5 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -551,7 +576,9 @@ vlan_id: 3504 gw_ip_subnet: '152.168.30.1/24' mtu_l3intf: 7600 - arp_suppress: True + # Leave 'arp_suppress' this false to avoid TCAM issue + # Please configure TCAM region for Ingress ARP-Ether ACL before configuring ARP supression + arp_suppress: False int_desc: 'test interface' vlan_name: testvlan dhcp_srvr1_ip: '1.1.1.1' @@ -567,6 +594,16 @@ deploy: True register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -579,7 +616,7 @@ - 'result.diff[0].net_name == "ansible-net13"' - 'result.diff[0].net_id == 7009' - 'result.diff[0].vrf_name == "Tenant-1"' - - 'result.diff[0].arp_suppress == true' + - 'result.diff[0].arp_suppress == false' - 'result.diff[0].int_desc == "test interface"' - 'result.diff[0].is_l2only == false' - 'result.diff[0].mtu_l3intf == 7600' @@ -591,22 +628,23 @@ - 'result.diff[0].dhcp_srvr3_ip == "3.3.3.3"' - 'result.diff[0].dhcp_srvr3_vrf == "three"' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - conf5 - Idempotence cisco.dcnm.dcnm_network: *conf5 register: result +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + - name: MERGED - setup - Clean up any existing network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: MERGED - Create L3 networks along with all dhcp, arp options without attach cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -627,6 +665,16 @@ dhcp_srvr3_vrf: three register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkName is search('ansible-net13')" + retries: 5 + delay: 2 + - assert: that: - 'result.changed == true' @@ -645,13 +693,9 @@ - 'result.diff[0].dhcp_srvr3_ip == "3.3.3.3"' - 'result.diff[0].dhcp_srvr3_vrf == "three"' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - attach networks to already created network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -663,6 +707,16 @@ ports: ["{{ ansible_sw2_int5 }}", "{{ ansible_sw2_int6 }}"] register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -675,13 +729,9 @@ - '(result.response[0].DATA|dict2items)[0].value == "SUCCESS"' - '(result.response[0].DATA|dict2items)[1].value == "SUCCESS"' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - Query the Network to check for configs cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result @@ -716,12 +766,12 @@ - name: MERGED - setup - Clean up any existing network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: MERGED - Create Network with invalid network name cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: @@ -742,7 +792,7 @@ - name: MERGED - Create Network with invalid VRF name cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -763,7 +813,7 @@ - name: MERGED - Create Network with invalid vlan id cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -784,7 +834,7 @@ - name: MERGED - Create Network and deploy in invalid switch cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -809,7 +859,7 @@ - name: MERGED - Create Network and deploy in switch with null interface cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -834,7 +884,7 @@ - name: MERGED - Create Network with out of range routing tag cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -856,7 +906,7 @@ - name: MERGED - Create L2 only Network with a vrf name cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -878,7 +928,7 @@ - name: MERGED - Create L3 Network without a vrf name cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -898,7 +948,7 @@ - name: MERGED - Create L3 Network with DHCP server IP alone cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -918,13 +968,9 @@ - 'result.changed == false' - '"Invalid parameters in playbook: DHCP server IP should be specified along with DHCP server VRF" in result.msg' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - setup - Clean up any existing network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted ############################################## @@ -933,5 +979,5 @@ - name: MERGED - setup - remove any networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/overridden.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/overridden.yaml index 732d7cf32..d4350bab5 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/overridden.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/overridden.yaml @@ -3,11 +3,11 @@ ############################################## - set_fact: - rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/rest/control/fabrics/{{ test_fabric }}" when: controller_version == "11" - set_fact: - rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ test_fabric }}" when: controller_version >= "12" - name: OVERRIDDEN - Verify if fabric - Fabric1 is deployed. @@ -22,16 +22,12 @@ - name: OVERRIDDEN - setup - Clean up any existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: OVERRIDDEN - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: OVERRIDDEN - Create, Attach and Deploy Multiple Network with Single Switch Attach cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -62,6 +58,17 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -82,17 +89,13 @@ - 'result.diff[1].net_id == 7002' - 'result.diff[1].vrf_name == "Tenant-2"' -- name: OVERRIDDEN - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - ############################################## ## OVERRIDDEN ## ############################################## - name: OVERRIDDEN - Create, Attach and Deploy Multiple Network with Single Switch Attach cisco.dcnm.dcnm_network: &conf1 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: overridden config: - net_name: ansible-net13 @@ -112,6 +115,16 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -138,10 +151,6 @@ - '"{{ ansible_switch2 }}" in result.diff[1].attach[0].ip_address' - 'result.diff[1].net_name == "ansible-net12"' -- name: OVERRIDDEN - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: OVERRIDDEN - conf1 - Idempotence cisco.dcnm.dcnm_network: *conf1 register: result @@ -153,12 +162,12 @@ - name: OVERRIDDEN - setup - remove any networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: OVERRIDDEN- Create, Attach and Deploy L2. L3 Network with Switch Attach cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -207,6 +216,17 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -233,13 +253,9 @@ - 'result.diff[1].is_l2only == false' - 'result.diff[1].vlan_name == "testvlan1"' -- name: OVERRIDDEN - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: OVERRIDDEN - Override L2, L3 Networks with a new L2 network cisco.dcnm.dcnm_network: &conf2 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: overridden config: - net_name: ansible-net14 @@ -265,6 +281,16 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -295,10 +321,6 @@ - 'result.diff[1].net_name == "ansible-net12"' - 'result.diff[2].net_name == "ansible-net13"' -- name: OVERRIDDEN - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: OVERRIDDEN - conf2 - Idempotence cisco.dcnm.dcnm_network: *conf2 register: result @@ -310,7 +332,7 @@ - name: OVERRIDDEN - Check for networks in fabric cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result @@ -325,5 +347,5 @@ - name: OVERRIDDEN - setup - remove any networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml index a2bca9dc1..0b04d2d08 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml @@ -3,11 +3,11 @@ ############################################## - set_fact: - rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/rest/control/fabrics/{{ test_fabric }}" when: controller_version == "11" - set_fact: - rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ test_fabric }}" when: controller_version >= "12" - name: Verify if fabric - Fabric1 is deployed. @@ -22,7 +22,7 @@ - name: Setup - Clean up any existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted register: result until: @@ -32,7 +32,7 @@ - name: Create, Attach and Deploy Multiple Network with Single Switch Attach cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -62,7 +62,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: query_result until: @@ -97,7 +97,7 @@ - name: QUERY - Query the Network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query config: - net_name: ansible-net13 @@ -142,7 +142,7 @@ - name: QUERY - Query the Network without the config element cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result @@ -173,13 +173,13 @@ - name: Delete all the networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted register: result - name: Query fabric state until all networks are deleted cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: query_result until: @@ -211,7 +211,7 @@ - name: QUERY - Query the non available Network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query config: - net_name: ansible-net13 @@ -237,7 +237,7 @@ - name: Create a L2 only and L3 networks along with all dhcp, arp options cisco.dcnm.dcnm_network: &conf3 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -286,7 +286,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: query_result until: @@ -336,7 +336,7 @@ - name: QUERY - Query the L2 and L3 Network cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query config: - net_name: ansible-net13 @@ -422,7 +422,7 @@ - name: QUERY - Query L2 and L3 the Network without the config element cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result @@ -478,5 +478,5 @@ - name: QUERY - setup - remove any networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/replaced.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/replaced.yaml index 7115c76d4..9f17ad8ec 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/replaced.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/replaced.yaml @@ -3,11 +3,11 @@ ############################################## - set_fact: - rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/rest/control/fabrics/{{ test_fabric }}" when: controller_version == "11" - set_fact: - rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ test_fabric }}" when: controller_version >= "12" - name: REPLACED - Verify if fabric - Fabric1 is deployed. @@ -22,16 +22,12 @@ - name: REPLACED - setup - Clean up any existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - Create, Attach and Deploy Multiple Network with Single Switch Attach cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -62,6 +58,17 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -82,17 +89,13 @@ - 'result.diff[1].net_id == 7002' - 'result.diff[1].vrf_name == "Tenant-2"' -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - ############################################## ## REPLACED ## ############################################## - name: REPLACED - Update Network using replace - Delete Attachments cisco.dcnm.dcnm_network: &conf1 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: replaced config: - net_name: ansible-net13 @@ -112,6 +115,17 @@ deploy: true register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -129,10 +143,6 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[0].ip_address' - '"ansible-net13" or "ansible-net12" in result.diff[0].net_name' -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - conf1 - Idempotence cisco.dcnm.dcnm_network: *conf1 register: result @@ -144,7 +154,7 @@ - name: REPLACED - Update Network using replace - Create Attachments cisco.dcnm.dcnm_network: &conf2 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: replaced config: - net_name: ansible-net13 @@ -175,6 +185,17 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -192,10 +213,6 @@ - '"{{ ansible_switch2 }}" in result.diff[1].attach[0].ip_address' - 'result.diff[1].net_name == "ansible-net12"' -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - conf2 - Idempotence cisco.dcnm.dcnm_network: *conf2 register: result @@ -207,16 +224,12 @@ - name: REPLACED - setup - remove any networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - Create, Attach and Deploy L2, L3 Network with Switch Attach cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -265,6 +278,17 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -291,13 +315,9 @@ - 'result.diff[1].is_l2only == false' - 'result.diff[1].vlan_name == "testvlan1"' -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - Update L2, L3 Networks using replace - Delete Attachments cisco.dcnm.dcnm_network: &conf3 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: replaced config: - net_name: ansible-net13 @@ -334,6 +354,17 @@ dhcp_srvr3_vrf: three register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -351,10 +382,6 @@ - '"{{ ansible_switch1 }}" or "{{ ansible_switch2 }}" in result.diff[1].attach[1].ip_address' - 'result.diff[0].net_name == "ansible-net12"' -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - conf3 - Idempotence cisco.dcnm.dcnm_network: *conf3 register: result @@ -366,7 +393,7 @@ - name: REPLACED - Update L2, L3 Networks using replace - Create Attachments cisco.dcnm.dcnm_network: &conf4 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: replaced config: - net_name: ansible-net13 @@ -415,6 +442,17 @@ deploy: false register: result +- name: Query fabric state until networkStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -432,10 +470,6 @@ - '"{{ ansible_switch2 }}" in result.diff[1].attach[0].ip_address' - 'result.diff[1].net_name == "ansible-net12"' -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - conf4 - Idempotence cisco.dcnm.dcnm_network: *conf4 register: result @@ -451,5 +485,5 @@ - name: REPLACED - setup - remove any networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/scale.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/scale.yaml index 83be2c3a5..cace9f30f 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/scale.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/scale.yaml @@ -3,11 +3,11 @@ ############################################## - set_fact: - rest_path: "/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/rest/control/fabrics/{{ test_fabric }}" when: controller_version == "11" - set_fact: - rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ ansible_it_fabric }}" + rest_path: "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/fabrics/{{ test_fabric }}" when: controller_version >= "12" - name: SCALE - Verify if fabric is deployed. @@ -22,7 +22,7 @@ - name: SCALE - Clean up any existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: SCALE - sleep for 40 seconds for DCNM to completely update the state @@ -40,14 +40,14 @@ - name: Push all Networks to DCNM cisco.dcnm.dcnm_network: - fabric: '{{ ansible_it_fabric }}' + fabric: '{{ test_fabric }}' state: merged config: '{{ nets_list }}' register: result - name: SCALE - Clean up existing networks cisco.dcnm.dcnm_network: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: SCALE - sleep for 40 seconds for DCNM to completely update the state @@ -70,5 +70,5 @@ - name: SCALE - Clean up any existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted \ No newline at end of file diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_dhcp_params.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_dhcp_params.yaml index 5a7b34389..0c21699b1 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_dhcp_params.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_dhcp_params.yaml @@ -1,11 +1,11 @@ - name: Setup - Remove all existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: Test dhcp parameters for state merged cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -37,7 +37,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result until: diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_dhcp_update.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_dhcp_update.yaml index 0d8b5996f..5bfe0b429 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_dhcp_update.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_dhcp_update.yaml @@ -1,11 +1,11 @@ - name: Setup - Remove all existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: Create network with initial dhcp parameter values cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -33,7 +33,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result until: @@ -53,7 +53,7 @@ - name: Change dhcp parameter values cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -81,7 +81,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result until: diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_params.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_params.yaml index 3ff46d290..237140f39 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_params.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_params.yaml @@ -1,11 +1,11 @@ - name: Setup - Remove all existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: Test mcast parameters for state merged cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -31,7 +31,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result until: diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_update.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_update.yaml index d40f99933..22c14994a 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_update.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_update.yaml @@ -1,11 +1,11 @@ - name: Setup - Remove all existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: Create network with initial DEFAULT mcast parameter value cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -27,7 +27,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result until: @@ -41,7 +41,7 @@ - name: Change mcast parameter values cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -63,7 +63,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result until: diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/so_dhcp_update.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/so_dhcp_update.yaml index d6253b585..bffddbd7c 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/so_dhcp_update.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/so_dhcp_update.yaml @@ -1,11 +1,11 @@ - name: Setup - Remove all existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: Create network with initial dhcp parameter values cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -54,7 +54,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result until: @@ -93,7 +93,7 @@ - name: Override network with a single network and change values within that newtwork cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: overridden config: - net_name: ansible-net14 @@ -121,7 +121,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result until: diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/so_mcast_update.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/so_mcast_update.yaml index 16e2a4a1d..858797750 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/so_mcast_update.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/so_mcast_update.yaml @@ -1,11 +1,11 @@ - name: Setup - Remove all existing networks cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted - name: Create network with initial mcast parameter value cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - net_name: ansible-net13 @@ -42,7 +42,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result until: @@ -69,7 +69,7 @@ - name: Override network with a single network and change values within that newtwork cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: overridden config: - net_name: ansible-net14 @@ -91,7 +91,7 @@ - name: Query fabric state until networkStatus transitions to DEPLOYED state cisco.dcnm.dcnm_network: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result until: From a69ae99eca2a19b2f42afdae6d9e6220355791e7 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Tue, 8 Nov 2022 13:56:03 -0500 Subject: [PATCH 20/26] Fix deleted and mcast issues --- plugins/modules/dcnm_network.py | 21 ++++++++++++---- .../dcnm_network/tests/dcnm/merged.yaml | 24 +++++++++---------- .../self-contained-tests/sm_mcast_update.yaml | 2 +- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/plugins/modules/dcnm_network.py b/plugins/modules/dcnm_network.py index ade061afa..794a35499 100644 --- a/plugins/modules/dcnm_network.py +++ b/plugins/modules/dcnm_network.py @@ -2014,6 +2014,16 @@ def push_to_remote(self, is_rollback=False): resp = dcnm_send( self.module, method, deploy_path, json.dumps(self.diff_undeploy) ) + # Use the self.wait_for_del_ready() function to refresh the state + # of self.diff_delete dict and re-attempt the undeploy action if + # the state of the network is "OUT-OF-SYNC" + self.wait_for_del_ready() + for net, state in self.diff_delete.items(): + if state == "OUT-OF-SYNC": + resp = dcnm_send( + self.module, method, deploy_path, json.dumps(self.diff_undeploy) + ) + self.result["response"].append(resp) fail, self.result["changed"] = self.handle_response(resp, "deploy") if fail: @@ -2040,12 +2050,12 @@ def push_to_remote(self, is_rollback=False): self.failure(resp) if del_failure: - resp = "Deletion of Networkss {0} has failed".format(del_failure[:-1]) + fail_msg = "Deletion of Networks {0} has failed: {1}".format(del_failure[:-1], resp) self.result["response"].append(resp) if is_rollback: self.failed_to_rollback = True return - self.failure(resp) + self.failure(fail_msg) if self.diff_create: for net in self.diff_create: @@ -2174,7 +2184,7 @@ def validate_input(self): dhcp_srvr2_vrf=dict(type="str", length_max=32), dhcp_srvr3_vrf=dict(type="str", length_max=32), dhcp_loopback_id=dict(type="int", range_min=0, range_max=1023), - multicast_group_address=dict(type="ipv4", default=""), + multicast_group_address=dict(type="ipv4", default="239.1.1.0"), ) att_spec = dict( ip_address=dict(required=True, type="str"), @@ -2238,7 +2248,7 @@ def validate_input(self): dhcp_srvr2_vrf=dict(type="str", length_max=32), dhcp_srvr3_vrf=dict(type="str", length_max=32), dhcp_loopback_id=dict(type="int", range_min=0, range_max=1023), - multicast_group_address=dict(type="ipv4", default=""), + multicast_group_address=dict(type="ipv4", default="239.1.1.0"), ) att_spec = dict( ip_address=dict(required=True, type="str"), @@ -2397,6 +2407,9 @@ def failure(self, resp): msg1 = "SUCCESS - Attempted rollback of the task has succeeded" res = copy.deepcopy(resp) + if isinstance(res, str): + self.module.fail_json(msg=res) + res.update({"ROLLBACK_RESULT": msg1}) if not resp.get("DATA"): diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/merged.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/merged.yaml index c13e3b39f..1cdb14032 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/merged.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/merged.yaml @@ -135,7 +135,7 @@ state: query register: query_result until: - - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" retries: 30 delay: 2 @@ -182,7 +182,7 @@ state: query register: query_result until: - - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" retries: 30 delay: 2 @@ -249,8 +249,8 @@ state: query register: query_result until: - - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" - - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED|PENDING')" retries: 30 delay: 2 @@ -316,7 +316,7 @@ state: query register: query_result until: - - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" retries: 30 delay: 2 @@ -391,8 +391,8 @@ state: query register: query_result until: - - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" - - "query_result.response[1].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" + - "query_result.response[1].parent.networkStatus is search('DEPLOYED|PENDING')" retries: 30 delay: 2 @@ -461,7 +461,7 @@ state: query register: query_result until: - - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" retries: 30 delay: 2 @@ -521,7 +521,7 @@ state: query register: query_result until: - - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" retries: 30 delay: 2 @@ -600,7 +600,7 @@ state: query register: query_result until: - - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" retries: 30 delay: 2 @@ -665,7 +665,7 @@ dhcp_srvr3_vrf: three register: result -- name: Query fabric state until networkStatus transitions to DEPLOYED state +- name: Query fabric state until networkName is present cisco.dcnm.dcnm_network: fabric: "{{ test_fabric }}" state: query @@ -713,7 +713,7 @@ state: query register: query_result until: - - "query_result.response[0].parent.networkStatus is search('DEPLOYED')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" retries: 30 delay: 2 diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_update.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_update.yaml index 22c14994a..8f3cc1bae 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_update.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/sm_mcast_update.yaml @@ -37,7 +37,7 @@ - assert: that: - - "result.response[0].parent.networkTemplateConfig.mcastGroup is search('239.1.1.1')" + - "result.response[0].parent.networkTemplateConfig.mcastGroup is search('239.1.1.1|239.1.1.0')" - name: Change mcast parameter values cisco.dcnm.dcnm_network: From 9915ad5404507c7399801131e520e090443ed99a Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Tue, 15 Nov 2022 11:08:28 +0530 Subject: [PATCH 21/26] Update inventory module documentation --- docs/cisco.dcnm.dcnm_inventory_module.rst | 74 +++++++++++--------- plugins/modules/dcnm_inventory.py | 82 +++++++++++------------ 2 files changed, 85 insertions(+), 71 deletions(-) diff --git a/docs/cisco.dcnm.dcnm_inventory_module.rst b/docs/cisco.dcnm.dcnm_inventory_module.rst index 3a353b93b..1ce7dbc02 100644 --- a/docs/cisco.dcnm.dcnm_inventory_module.rst +++ b/docs/cisco.dcnm.dcnm_inventory_module.rst @@ -70,7 +70,8 @@ Parameters -
Name of the authentication protocol to be used. For POAP configurations authentication protocol should be 'MD5'.
+
Name of the authentication protocol to be used.
+
For POAP configurations authentication protocol should be MD5.
@@ -121,7 +122,10 @@ Parameters -
Configurations of switch to Bootstrap/Pre-provision. Please note that POAP and DHCP configurations needs to enabled in fabric configuration before adding/preprovisioning switches through POAP. Idempotence checks against inventory is only for 'IP Address' for Preprovision configs. Idempotence checks against inventory is only for 'IP Address' and 'Serial Number' for Bootstrap configs.
+
Configurations of switch to Bootstrap/Pre-provision.
+
Please note that POAP and DHCP configurations needs to enabled in fabric configuration before adding/preprovisioning switches through POAP.
+
Idempotence checks against inventory is only for IP Address for Preprovision configs.
+
Idempotence checks against inventory is only for IP Address and Serial Number for Bootstrap configs.
@@ -138,7 +142,11 @@ Parameters -
Basic config data of switch to Bootstrap/Pre-provision. 'modulesModel' and 'gateway' parameters are mandatory. 'modulesModel' is list of model of modules in switch to Bootstrap/Pre-provision. 'gateway' is the gateway IP with mask for the switch to Bootstrap/Pre-provision. For other supported config data please refer to NDFC/DCNM configuration guide.
+
Basic config data of switch to Bootstrap/Pre-provision.
+
modulesModel and gateway are mandatory.
+
modulesModel is list of model of modules in switch to Bootstrap/Pre-provision.
+
gateway is the gateway IP with mask for the switch to Bootstrap/Pre-provision.
+
For other supported config data please refer to NDFC/DCNM configuration guide.
@@ -206,7 +214,9 @@ Parameters -
Serial number of switch to Pre-provision. When 'preprovision_serial' is provided along with 'serial_number', then the Preprovisioned switch(with serial number as in 'preprovision_serial') will be swapped with a actual switch(with serial number in 'serial_number') through bootstrap. Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions.
+
Serial number of switch to Pre-provision.
+
When preprovision_serial is provided along with serial_number, then the Preprovisioned switch(with serial number as in preprovision_serial) will be swapped with a actual switch(with serial number in serial_number) through bootstrap.
+
Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions.
@@ -223,7 +233,9 @@ Parameters -
Serial number of switch to Bootstrap. When 'preprovision_serial' is provided along with 'serial_number', then the Preprovisioned switch(with serial number as in 'preprovision_serial') will be swapped with a actual switch(with serial number in 'serial_number') through bootstrap. Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions.
+
Serial number of switch to Bootstrap.
+
When preprovision_serial is provided along with serial_number, then the Preprovisioned switch(with serial number as in preprovision_serial) will be swapped with a actual switch(with serial number in serial_number) through bootstrap.
+
Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions.
@@ -322,7 +334,8 @@ Parameters -
Login username to the switch. For POAP configurations username should be 'admin'
+
Login username to the switch.
+
For POAP configurations username should be admin
@@ -379,7 +392,8 @@ Parameters -
The state of DCNM after module completion. 'merged' and 'query' are the only states supported for POAP
+
The state of DCNM after module completion.
+
merged and query are the only states supported for POAP.
@@ -478,6 +492,29 @@ Examples role: leaf preserve_config: False # boolean, default is true + # All the switches will be deleted in the existing fabric + - name: Delete all the switches + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: deleted # merged / deleted / overridden / query + + # The following two switches information will be queried in the existing fabric + - name: Query switch into fabric + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: query # merged / deleted / overridden / query + config: + - seed_ip: 192.168.0.1 + role: spine + - seed_ip: 192.168.0.2 + role: leaf + + # All the existing switches will be queried in the existing fabric + - name: Query all the switches in the fabric + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: query # merged / deleted / overridden / query + # The following task will enable Bootstrap and DHCP on an existing fabric. # Please note that only bootstrap and DHCP configs are present in the below example. # You have to add other existing fabric configs to the task. @@ -586,29 +623,6 @@ Examples - preprovision_serial: 1A2BCDEFGHI serial_number: 2A3BCDEFGHI - # All the switches will be deleted in the existing fabric - - name: Delete all the switches - cisco.dcnm.dcnm_inventory: - fabric: vxlan-fabric - state: deleted # merged / deleted / overridden / query - - # The following two switches information will be queried in the existing fabric - - name: Query switch into fabric - cisco.dcnm.dcnm_inventory: - fabric: vxlan-fabric - state: query # merged / deleted / overridden / query - config: - - seed_ip: 192.168.0.1 - role: spine - - seed_ip: 192.168.0.2 - role: leaf - - # All the existing switches will be queried in the existing fabric - - name: Query all the switches in the fabric - cisco.dcnm.dcnm_inventory: - fabric: vxlan-fabric - state: query # merged / deleted / overridden / query - # All the existing switches along with available Bootstrap(POAP) # will be queried in the existing fabric - name: Query all the switches in the fabric diff --git a/plugins/modules/dcnm_inventory.py b/plugins/modules/dcnm_inventory.py index bf2443aa6..078335b93 100644 --- a/plugins/modules/dcnm_inventory.py +++ b/plugins/modules/dcnm_inventory.py @@ -35,7 +35,7 @@ state: description: - The state of DCNM after module completion. - 'merged' and 'query' are the only states supported for POAP + - I(merged) and I(query) are the only states supported for POAP. type: str choices: - merged @@ -58,7 +58,7 @@ auth_proto: description: - Name of the authentication protocol to be used. - For POAP configurations authentication protocol should be 'MD5'. + - For POAP configurations authentication protocol should be I(MD5). choices: ['MD5', 'SHA', 'MD5_DES', 'MD5_AES', 'SHA_DES', 'SHA_AES'] type: str required: false @@ -66,7 +66,7 @@ user_name: description: - Login username to the switch. - For POAP configurations username should be 'admin' + - For POAP configurations username should be I(admin) type: str required: true password: @@ -97,29 +97,29 @@ poap: description: - Configurations of switch to Bootstrap/Pre-provision. - Please note that POAP and DHCP configurations needs to enabled in fabric configuration + - Please note that POAP and DHCP configurations needs to enabled in fabric configuration before adding/preprovisioning switches through POAP. - Idempotence checks against inventory is only for 'IP Address' for Preprovision configs. - Idempotence checks against inventory is only for 'IP Address' and 'Serial Number' for Bootstrap configs. + - Idempotence checks against inventory is only for B(IP Address) for Preprovision configs. + - Idempotence checks against inventory is only for B(IP Address) and B(Serial Number) for Bootstrap configs. type: list elements: dict suboptions: serial_number: description: - Serial number of switch to Bootstrap. - When 'preprovision_serial' is provided along with 'serial_number', - then the Preprovisioned switch(with serial number as in 'preprovision_serial') will be swapped - with a actual switch(with serial number in 'serial_number') through bootstrap. - Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions. + - When C(preprovision_serial) is provided along with C(serial_number), + then the Preprovisioned switch(with serial number as in C(preprovision_serial)) will be swapped + with a actual switch(with serial number in C(serial_number)) through bootstrap. + - Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions. type: str required: false preprovision_serial: description: - Serial number of switch to Pre-provision. - When 'preprovision_serial' is provided along with 'serial_number', - then the Preprovisioned switch(with serial number as in 'preprovision_serial') will be swapped - with a actual switch(with serial number in 'serial_number') through bootstrap. - Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions. + - When C(preprovision_serial) is provided along with C(serial_number), + then the Preprovisioned switch(with serial number as in C(preprovision_serial)) will be swapped + with a actual switch(with serial number in C(serial_number)) through bootstrap. + - Swap feature is supported only on NDFC and is not supported on DCNM 11.x versions. type: str required: false model: @@ -145,10 +145,10 @@ config_data: description: - Basic config data of switch to Bootstrap/Pre-provision. - 'modulesModel' and 'gateway' parameters are mandatory. - 'modulesModel' is list of model of modules in switch to Bootstrap/Pre-provision. - 'gateway' is the gateway IP with mask for the switch to Bootstrap/Pre-provision. - For other supported config data please refer to NDFC/DCNM configuration guide. + - C(modulesModel) and C(gateway) are mandatory. + - C(modulesModel) is list of model of modules in switch to Bootstrap/Pre-provision. + - C(gateway) is the gateway IP with mask for the switch to Bootstrap/Pre-provision. + - For other supported config data please refer to NDFC/DCNM configuration guide. type: dict required: false query_poap: @@ -245,6 +245,29 @@ role: leaf preserve_config: False # boolean, default is true +# All the switches will be deleted in the existing fabric +- name: Delete all the switches + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: deleted # merged / deleted / overridden / query + +# The following two switches information will be queried in the existing fabric +- name: Query switch into fabric + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: query # merged / deleted / overridden / query + config: + - seed_ip: 192.168.0.1 + role: spine + - seed_ip: 192.168.0.2 + role: leaf + +# All the existing switches will be queried in the existing fabric +- name: Query all the switches in the fabric + cisco.dcnm.dcnm_inventory: + fabric: vxlan-fabric + state: query # merged / deleted / overridden / query + # The following task will enable Bootstrap and DHCP on an existing fabric. # Please note that only bootstrap and DHCP configs are present in the below example. # You have to add other existing fabric configs to the task. @@ -353,29 +376,6 @@ - preprovision_serial: 1A2BCDEFGHI serial_number: 2A3BCDEFGHI -# All the switches will be deleted in the existing fabric -- name: Delete all the switches - cisco.dcnm.dcnm_inventory: - fabric: vxlan-fabric - state: deleted # merged / deleted / overridden / query - -# The following two switches information will be queried in the existing fabric -- name: Query switch into fabric - cisco.dcnm.dcnm_inventory: - fabric: vxlan-fabric - state: query # merged / deleted / overridden / query - config: - - seed_ip: 192.168.0.1 - role: spine - - seed_ip: 192.168.0.2 - role: leaf - -# All the existing switches will be queried in the existing fabric -- name: Query all the switches in the fabric - cisco.dcnm.dcnm_inventory: - fabric: vxlan-fabric - state: query # merged / deleted / overridden / query - # All the existing switches along with available Bootstrap(POAP) # will be queried in the existing fabric - name: Query all the switches in the fabric From 27c4a6666c777494937bdaa6ee845c286230d223 Mon Sep 17 00:00:00 2001 From: mmudigon <62759545+mmudigon@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:07:30 +0530 Subject: [PATCH 22/26] Dcnm interface svi (#180) * DCNM Interface update for SVI support * Removed .swp file * Fixed an issue in UT and removed unwanted logs --- docs/cisco.dcnm.dcnm_interface_module.rst | 643 +++++++- plugins/modules/dcnm_interface.py | 1167 +++++++++++--- .../targets/dcnm_interface/meta/main.yaml | 3 +- .../targets/dcnm_interface/tasks/dcnm.yaml | 30 + .../tests/dcnm/dcnm_delete_diff_options.yaml | 26 +- .../tests/dcnm/dcnm_eth_delete.yaml | 28 +- .../tests/dcnm/dcnm_eth_merge.yaml | 14 +- .../tests/dcnm/dcnm_eth_override.yaml | 16 +- .../tests/dcnm/dcnm_eth_replace.yaml | 22 +- .../dcnm/dcnm_intf_multi_intf_merge.yaml | 30 +- .../tests/dcnm/dcnm_intf_multi_switches.yaml | 10 +- .../dcnm/dcnm_intf_no_optional_elems.yaml | 58 +- .../tests/dcnm/dcnm_intf_query.yaml | 12 +- .../tests/dcnm/dcnm_lo_delete.yaml | 12 +- .../tests/dcnm/dcnm_lo_merge.yaml | 8 +- .../tests/dcnm/dcnm_lo_override.yaml | 12 +- .../tests/dcnm/dcnm_lo_replace.yaml | 12 +- .../tests/dcnm/dcnm_old_format_pb.yaml | 18 +- .../tests/dcnm/dcnm_pc_delete.yaml | 20 +- .../tests/dcnm/dcnm_pc_merge.yaml | 12 +- .../tests/dcnm/dcnm_pc_override.yaml | 14 +- .../tests/dcnm/dcnm_pc_replace.yaml | 18 +- .../tests/dcnm/dcnm_sub_delete.yaml | 12 +- .../tests/dcnm/dcnm_sub_merge.yaml | 8 +- .../tests/dcnm/dcnm_sub_override.yaml | 10 +- .../tests/dcnm/dcnm_sub_replace.yaml | 12 +- .../tests/dcnm/dcnm_svi_delete.yaml | 154 ++ .../tests/dcnm/dcnm_svi_invalid_params.yaml | 241 +++ .../tests/dcnm/dcnm_svi_merge.yaml | 263 ++++ .../tests/dcnm/dcnm_svi_override.yaml | 151 ++ .../tests/dcnm/dcnm_svi_query.yaml | 122 ++ .../tests/dcnm/dcnm_svi_replace.yaml | 176 +++ .../tests/dcnm/dcnm_vpc_delete.yaml | 12 +- .../tests/dcnm/dcnm_vpc_merge.yaml | 8 +- .../tests/dcnm/dcnm_vpc_override.yaml | 10 +- .../tests/dcnm/dcnm_vpc_replace.yaml | 12 +- .../targets/prepare_dcnm_intf/tasks/main.yaml | 46 + .../fixtures/dcnm_intf_have_all_payloads.json | 24 + .../dcnm/fixtures/dcnm_intf_svi_configs.json | 235 +++ .../dcnm/fixtures/dcnm_intf_svi_payloads.json | 51 + tests/unit/modules/dcnm/test_dcnm_intf.py | 1355 ++++++++++++++--- 41 files changed, 4420 insertions(+), 667 deletions(-) create mode 100644 tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_delete.yaml create mode 100644 tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_invalid_params.yaml create mode 100644 tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_merge.yaml create mode 100644 tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_override.yaml create mode 100644 tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_query.yaml create mode 100644 tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_replace.yaml create mode 100644 tests/integration/targets/prepare_dcnm_intf/tasks/main.yaml create mode 100644 tests/unit/modules/dcnm/fixtures/dcnm_intf_svi_configs.json create mode 100644 tests/unit/modules/dcnm/fixtures/dcnm_intf_svi_payloads.json diff --git a/docs/cisco.dcnm.dcnm_interface_module.rst b/docs/cisco.dcnm.dcnm_interface_module.rst index 4ff779a9c..04d106fea 100644 --- a/docs/cisco.dcnm.dcnm_interface_module.rst +++ b/docs/cisco.dcnm.dcnm_interface_module.rst @@ -1070,6 +1070,520 @@ Parameters + + + +
+ profile_svi + +
+ - +
+ + + + +
Though the key shown here is 'profile_svi' the actual key to be used in playbook is 'profile'. The key 'profile_svi' is used here to logically segregate the interface objects applicable for this profile
+
Object profile which must be included for SVI interface configurations.
+ + + + + + +
+ admin_state + +
+ boolean + / required +
+ + +
    Choices: +
  • no
  • +
  • yes
  • +
+ + +
Administrative state of the interface.
+ + + + + + +
+ adv_subnet_in_underlay + +
+ boolean +
+ + +
    Choices: +
  • no ←
  • +
  • yes
  • +
+ + +
Flag to enable/disable advertisements of subnets into underlay.
+ + + + + + +
+ cmds + +
+ list +
+ + + Default:
[]
+ + +
Commands to be included in the configuration under this interface.
+ + + + + + +
+ description + +
+ string +
+ + + Default:
""
+ + +
Description of the interface.
+ + + + + + +
+ dhcp_server_addr1 + +
+ string +
+ + + Default:
""
+ + +
DHCP relay server address.
+ + + + + + +
+ dhcp_server_addr2 + +
+ string +
+ + + Default:
""
+ + +
DHCP relay server address.
+ + + + + + +
+ dhcp_server_addr3 + +
+ string +
+ + + Default:
""
+ + +
DHCP relay server address.
+ + + + + + +
+ disable_ip_redirects + +
+ boolean +
+ + +
    Choices: +
  • no ←
  • +
  • yes
  • +
+ + +
Flag to enable/disable IP redirects.
+ + + + + + +
+ enable_hsrp + +
+ boolean +
+ + +
    Choices: +
  • no ←
  • +
  • yes
  • +
+ + +
Flag to enable/disable HSRP on the interface.
+ + + + + + +
+ enable_netflow + +
+ boolean +
+ + +
    Choices: +
  • no ←
  • +
  • yes
  • +
+ + +
Flag to enable netflow.
+ + + + + + +
+ hsrp_group + +
+ string +
+ + + Default:
""
+ + +
HSRP group. This parameter is required if "enable_hsrp" is True.
+ + + + + + +
+ hsrp_priority + +
+ string +
+ + + Default:
""
+ + +
HSRP priority.
+ + + + + + +
+ hsrp_version + +
+ integer +
+ + +
    Choices: +
  • 1 ←
  • +
  • 2
  • +
+ + +
HSRP protocol version.
+ + + + + + +
+ hsrp_vip + +
+ string +
+ + + Default:
""
+ + +
Virtual IP address for HSRP. This parameter is required if "enable_hsrp" is True.
+ + + + + + +
+ hsrp_vmac + +
+ string +
+ + + Default:
""
+ + +
HSRP virtual MAC.
+ + + + + + +
+ int_vrf + +
+ string +
+ + + Default:
"default"
+ + +
Interface VRF name.
+ + + + + + +
+ ipv4_addr + +
+ string +
+ + + Default:
""
+ + +
IPV4 address of the interface.
+ + + + + + +
+ ipv4_mask_len + +
+ integer +
+ + +
    Choices: +
  • Min 1
  • +
  • Max 31
  • +
+ + +
IPV4 address mask length. This parameter is required if 'ipv4_addr' is included.
+ + + + + + +
+ mode + +
+ string + / required +
+ + +
    Choices: +
  • vlan
  • +
+ + +
Interface mode.
+ + + + + + +
+ mtu + +
+ integer +
+ + + Default:
9216
+ + +
Interface MTU.
+ + + + + + +
+ netflow_monitor + +
+ string +
+ + + Default:
""
+ + +
Name of netflow monitor. This parameter is required if "enable_netflow" is True.
+ + + + + + +
+ preempt + +
+ boolean +
+ + +
    Choices: +
  • no ←
  • +
  • yes
  • +
+ + +
Flag to enable/disable overthrow of low priority active routers. This parameter is valid only if "enable_hsrp" is True.
+ + + + + + +
+ route_tag + +
+ string +
+ + + Default:
""
+ + +
Route tag associated with the interface IP.
+ + + + + + +
+ vrf_dhcp1 + +
+ string +
+ + + Default:
""
+ + +
VRF to reach DHCP server. This parameter is required if "dhcp_server_addr1" is included.
+ + + + + + +
+ vrf_dhcp2 + +
+ string +
+ + + Default:
""
+ + +
VRF to reach DHCP server. This parameter is required if "dhcp_server_addr2" is included.
+ + + + + + +
+ vrf_dhcp3 + +
+ string +
+ + + Default:
""
+ + +
VRF to reach DHCP server. This parameter is required if "dhcp_server_addr3" is included.
+ + + @@ -1487,10 +2001,11 @@ Parameters
  • sub_int
  • lo
  • eth
  • +
  • svi
  • -
    Interface type. Example, pc, vpc, sub_int, lo, eth
    +
    Interface type. Example, pc, vpc, sub_int, lo, eth, svi
    @@ -2025,6 +2540,120 @@ Examples - no shutdown - no shutdown + # SVI INTERFACES + + - name: Create SVI interfaces including optional parameters + cisco.dcnm.dcnm_interface: &svi_merge2 + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + int_vrf: blue # optional, Interface VRF name, default is "default" + ipv4_addr: 192.168.2.1 # optional, Interfae IP, default is "" + ipv4_mask_len: 24 # optional, IP mask length, default is "" + mtu: 9216 # optional, MTU default is "" + route_tag: 1001 # optional, Routing TAG, default is "" + disable_ip_redirects: true # optional, flag to enable/disable IP redirects, default is "false" + cmds: # Freeform config + - no shutdown + admin_state: true # Flag to enable/disable Vlan interaface + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.168.2.100 # optional, Virtual IP address for HSRP, default is "" + hsrp_group: 10 # optional, HSRP group, default is "" + hsrp_priority: 5 # optional, HSRP priority, default is "" + hsrp_vmac: 0000.0101.ac0a # optional, HSRP virtual MAC, default is "" + dhcp_server_addr1: 192.200.1.1 # optional, DHCP relay server address, default is "" + vrf_dhcp1: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr2: 192.200.1.2 # optional, DHCP relay server address, default is "" + vrf_dhcp2: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr3: 192.200.1.3 # optional, DHCP relay server address, default is "" + vrf_dhcp3: blue # optional, VRF to reach DHCP server. default is "" + adv_subnet_in_underlay: true # optional, flag to enable/disable advertisements of subnets into underlay, default is "false" + enable_netflow: false # optional, flag to enable netflow, default is "false" + netflow_monitor: svi1001 # optional, name of netflow monitor, default is "" + hsrp_version: 1 # optional, HSRP protocol version, default is 1 + preempt: true # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + description: Switched vlan interface 1001 # optional, Interface description, default is "" + + - name: Replace SVI interface + cisco.dcnm.dcnm_interface: &svi_replace + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: replaced # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + int_vrf: red # optional, Interface VRF name, default is "default" + ipv4_addr: 192.169.2.1 # optional, Interfae IP, default is "" + ipv4_mask_len: 20 # optional, IP mask length, default is "" + mtu: 9210 # optional, MTU default is "" + route_tag: 1002 # optional, Routing TAG, default is "" + disable_ip_redirects: false # optional, flag to enable/disable IP redirects, default is "false" + cmds: # Freeform config + - no shutdown + admin_state: false # Flag to enable/disable Vlan interaface + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.169.2.100 # optional, Virtual IP address for HSRP, default is "" + hsrp_group: 11 # optional, HSRP group, default is "" + hsrp_priority: 5 # optional, HSRP priority, default is "" + hsrp_vmac: 0000.0102.ac0a # optional, HSRP virtual MAC, default is "" + dhcp_server_addr1: 193.200.1.1 # optional, DHCP relay server address, default is "" + vrf_dhcp1: green # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr2: 193.200.1.2 # optional, DHCP relay server address, default is "" + vrf_dhcp2: green # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr3: 193.200.1.3 # optional, DHCP relay server address, default is "" + vrf_dhcp3: green # optional, VRF to reach DHCP server. default is "" + adv_subnet_in_underlay: false # optional, flag to enable/disable advertisements of subnets into underlay, default is "false" + enable_netflow: false # optional, flag to enable netflow, default is "false" + netflow_monitor: svi1002 # optional, name of netflow monitor, default is "" + hsrp_version: 2 # optional, HSRP protocol version, default is 1 + preempt: false # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + description: Switched vlan interface 1001 - Rep # optional, Interface description, default is "" + + - name: Delete SVI interfaces + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: deleted # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch where to deploy the config + + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch where to deploy the config + + - name: Override SVI interface + cisco.dcnm.dcnm_interface: &svi_override + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1002 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + + # QUERY - name: Query interface details @@ -2033,22 +2662,22 @@ Examples state: query # only choose from [merged, replaced, deleted, overridden, query] config: - switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" - name: po350 switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" - name: lo450 switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" - name: eth1/1 switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" - name: eth1/15.2 switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" - name: vpc750 switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" diff --git a/plugins/modules/dcnm_interface.py b/plugins/modules/dcnm_interface.py index 70be35372..0e6fa3371 100644 --- a/plugins/modules/dcnm_interface.py +++ b/plugins/modules/dcnm_interface.py @@ -76,10 +76,10 @@ required: true type: description: - - Interface type. Example, pc, vpc, sub_int, lo, eth + - Interface type. Example, pc, vpc, sub_int, lo, eth, svi type: str required: true - choices: ['pc', 'vpc', 'sub_int', 'lo', 'eth'] + choices: ['pc', 'vpc', 'sub_int', 'lo', 'eth', 'svi'] deploy: description: - Flag indicating if the configuration must be pushed to the switch. If not included @@ -475,6 +475,146 @@ - Administrative state of the interface type: bool default: true + profile_svi: + description: + - Though the key shown here is 'profile_svi' the actual key to be used in playbook + is 'profile'. The key 'profile_svi' is used here to logically segregate the interface + objects applicable for this profile + - Object profile which must be included for SVI interface configurations. + suboptions: + mode: + description: + - Interface mode. + choices: ['vlan'] + type: str + required: true + int_vrf: + description: + - Interface VRF name. + type: str + default: "default" + ipv4_addr: + description: + - IPV4 address of the interface. + type: str + default: "" + ipv4_mask_len: + description: + - IPV4 address mask length. This parameter is required if 'ipv4_addr' is included. + type: int + choices : [Min 1, Max 31] + cmds: + description: + - Commands to be included in the configuration under this interface. + type: list + default: [] + description: + description: + - Description of the interface. + type: str + default: "" + admin_state: + description: + - Administrative state of the interface. + type: bool + required: true + route_tag: + description: + - Route tag associated with the interface IP. + type: str + default: "" + mtu: + description: + - Interface MTU. + type: int + default: 9216 + disable_ip_redirects: + description: + - Flag to enable/disable IP redirects. + type: bool + default: false + enable_hsrp: + description: + - Flag to enable/disable HSRP on the interface. + type: bool + default: false + hsrp_vip: + description: + - Virtual IP address for HSRP. This parameter is required if "enable_hsrp" is True. + type: str + default: "" + hsrp_group: + description: + - HSRP group. This parameter is required if "enable_hsrp" is True. + type: str + default: "" + hsrp_priority: + description: + - HSRP priority. + type: str + default: "" + hsrp_vmac: + description: + - HSRP virtual MAC. + type: str + default: "" + dhcp_server_addr1: + description: + - DHCP relay server address. + type: str + default: "" + vrf_dhcp1: + description: + - VRF to reach DHCP server. This parameter is required if "dhcp_server_addr1" is included. + type: str + default: "" + dhcp_server_addr2: + description: + - DHCP relay server address. + type: str + default: "" + vrf_dhcp2: + description: + - VRF to reach DHCP server. This parameter is required if "dhcp_server_addr2" is included. + type: str + default: "" + dhcp_server_addr3: + description: + - DHCP relay server address. + type: str + default: "" + vrf_dhcp3: + description: + - VRF to reach DHCP server. This parameter is required if "dhcp_server_addr3" is included. + type: str + default: "" + adv_subnet_in_underlay: + description: + - Flag to enable/disable advertisements of subnets into underlay. + type: bool + default: false + enable_netflow: + description: + - Flag to enable netflow. + type: bool + default: false + netflow_monitor: + description: + - Name of netflow monitor. This parameter is required if "enable_netflow" is True. + type: str + default: "" + hsrp_version: + description: + - HSRP protocol version. + type: int + default: 1 + choices: [1,2] + preempt: + description: + - Flag to enable/disable overthrow of low priority active routers. This parameter is valid only if "enable_hsrp" is True. + type: bool + default: false + """ EXAMPLES = """ @@ -961,6 +1101,120 @@ - no shutdown - no shutdown +# SVI INTERFACES + +- name: Create SVI interfaces including optional parameters + cisco.dcnm.dcnm_interface: &svi_merge2 + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + int_vrf: blue # optional, Interface VRF name, default is "default" + ipv4_addr: 192.168.2.1 # optional, Interfae IP, default is "" + ipv4_mask_len: 24 # optional, IP mask length, default is "" + mtu: 9216 # optional, MTU default is "" + route_tag: 1001 # optional, Routing TAG, default is "" + disable_ip_redirects: true # optional, flag to enable/disable IP redirects, default is "false" + cmds: # Freeform config + - no shutdown + admin_state: true # Flag to enable/disable Vlan interaface + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.168.2.100 # optional, Virtual IP address for HSRP, default is "" + hsrp_group: 10 # optional, HSRP group, default is "" + hsrp_priority: 5 # optional, HSRP priority, default is "" + hsrp_vmac: 0000.0101.ac0a # optional, HSRP virtual MAC, default is "" + dhcp_server_addr1: 192.200.1.1 # optional, DHCP relay server address, default is "" + vrf_dhcp1: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr2: 192.200.1.2 # optional, DHCP relay server address, default is "" + vrf_dhcp2: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr3: 192.200.1.3 # optional, DHCP relay server address, default is "" + vrf_dhcp3: blue # optional, VRF to reach DHCP server. default is "" + adv_subnet_in_underlay: true # optional, flag to enable/disable advertisements of subnets into underlay, default is "false" + enable_netflow: false # optional, flag to enable netflow, default is "false" + netflow_monitor: svi1001 # optional, name of netflow monitor, default is "" + hsrp_version: 1 # optional, HSRP protocol version, default is 1 + preempt: true # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + description: Switched vlan interface 1001 # optional, Interface description, default is "" + +- name: Replace SVI interface + cisco.dcnm.dcnm_interface: &svi_replace + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: replaced # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + int_vrf: red # optional, Interface VRF name, default is "default" + ipv4_addr: 192.169.2.1 # optional, Interfae IP, default is "" + ipv4_mask_len: 20 # optional, IP mask length, default is "" + mtu: 9210 # optional, MTU default is "" + route_tag: 1002 # optional, Routing TAG, default is "" + disable_ip_redirects: false # optional, flag to enable/disable IP redirects, default is "false" + cmds: # Freeform config + - no shutdown + admin_state: false # Flag to enable/disable Vlan interaface + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.169.2.100 # optional, Virtual IP address for HSRP, default is "" + hsrp_group: 11 # optional, HSRP group, default is "" + hsrp_priority: 5 # optional, HSRP priority, default is "" + hsrp_vmac: 0000.0102.ac0a # optional, HSRP virtual MAC, default is "" + dhcp_server_addr1: 193.200.1.1 # optional, DHCP relay server address, default is "" + vrf_dhcp1: green # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr2: 193.200.1.2 # optional, DHCP relay server address, default is "" + vrf_dhcp2: green # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr3: 193.200.1.3 # optional, DHCP relay server address, default is "" + vrf_dhcp3: green # optional, VRF to reach DHCP server. default is "" + adv_subnet_in_underlay: false # optional, flag to enable/disable advertisements of subnets into underlay, default is "false" + enable_netflow: false # optional, flag to enable netflow, default is "false" + netflow_monitor: svi1002 # optional, name of netflow monitor, default is "" + hsrp_version: 2 # optional, HSRP protocol version, default is 1 + preempt: false # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + description: Switched vlan interface 1001 - Rep # optional, Interface description, default is "" + +- name: Delete SVI interfaces + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: deleted # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch where to deploy the config + + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch where to deploy the config + +- name: Override SVI interface + cisco.dcnm.dcnm_interface: &svi_override + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1002 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + + # QUERY - name: Query interface details @@ -969,22 +1223,22 @@ state: query # only choose from [merged, replaced, deleted, overridden, query] config: - switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" - name: po350 switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" - name: lo450 switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" - name: eth1/1 switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" - name: eth1/15.2 switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" - name: vpc750 switch: - - "192.172.1.1" # provide the switch information where the config is to be deployed + - "192.172.1.1" """ @@ -1004,10 +1258,6 @@ dcnm_version_supported, ) -LOG_ERROR = 0 -LOG_DEBUG = 4 -LOG_VERBOSE = 5 - class DcnmIntf: @@ -1046,8 +1296,8 @@ def __init__(self, module): self.have_all_list = [] self.diff_create = [] self.diff_replace = [] - self.diff_delete = [[], [], [], [], []] - self.diff_delete_deploy = [[], [], [], [], []] + self.diff_delete = [[], [], [], [], [], []] + self.diff_delete_deploy = [[], [], [], [], [], []] self.diff_deploy = [] self.diff_query = [] self.log_verbosity = 0 @@ -1067,7 +1317,9 @@ def __init__(self, module): self.dcnm_version = dcnm_version_supported(self.module) - self.inventory_data = get_fabric_inventory_details(self.module, self.fabric) + self.inventory_data = get_fabric_inventory_details( + self.module, self.fabric + ) self.ip_sn, self.hn_sn = get_ip_sn_dict(self.inventory_data) self.paths = self.dcnm_intf_paths[self.dcnm_version] @@ -1081,6 +1333,23 @@ def __init__(self, module): # New Interfaces # To map keys from self.have to keys from config self.keymap = { + "DISABLE_IP_REDIRECTS": "disable_ip_redirects", + "ENABLE_HSRP": "enable_hsrp", + "HSRP_VIP": "hsrp_vip", + "HSRP_GROUP": "hsrp_group", + "PREEMPT": "preempt", + "HSRP_VERSION": "hsrp_version", + "HSRP_PRIORITY": "hsrp_priority", + "MAC": "hsrp_vmac", + "dhcpServerAddr1": "dhcp_server_addr1", + "dhcpServerAddr2": "dhcp_server_addr2", + "dhcpServerAddr3": "dhcp_server_addr3", + "vrfDhcp1": "vrf_dhcp1", + "vrfDhcp2": "vrf_dhcp2", + "vrfDhcp3": "vrf_dhcp3", + "advSubnetInUnderlay": "adv_subnet_in_unbderlay", + "ENABLE_NETFLOW": "enable_netflow", + "NETFLOW_MONITOR": "netflow_monitor", "policy": "policy", "ifName": "ifname", "serialNumber": "sno", @@ -1089,6 +1358,7 @@ def __init__(self, module): "INTF_VRF": "int_vrf", "V6IP": "ipv6_addr", "IPv6": "ipv6_addr", + "PREFIX": "ipv4_mask_len", "IPv6_PREFIX": "ipv6_mask_len", "ROUTING_TAG": "route_tag", "ROUTE_MAP_TAG": "route_tag", @@ -1104,7 +1374,6 @@ def __init__(self, module): "SPEED": "speed", "ALLOWED_VLANS": "allowed_vlans", "ACCESS_VLAN": "access_vlan", - "PREFIX": "ipv4_mask_len", "INTF_NAME": "ifname", "PO_ID": "ifname", "PEER1_PCID": "peer1_pcid", @@ -1137,6 +1406,8 @@ def __init__(self, module): "eth_epl_routed": "epl_routed_intf", "vpc_trunk": "int_vpc_trunk_host_11_1", "vpc_access": "int_vpc_access_host_11_1", + "svi_vlan": "int_vlan", + "svi_vlan_admin_state": "int_vlan_admin_state", }, 12: { "pc_monitor": "int_monitor_port_channel", @@ -1152,6 +1423,8 @@ def __init__(self, module): "eth_epl_routed": "epl_routed_intf", "vpc_trunk": "int_vpc_trunk_host", "vpc_access": "int_vpc_access_host", + "svi_vlan": "int_vlan", + "svi_vlan_admin_state": "int_vlan_admin_state", }, } @@ -1162,6 +1435,7 @@ def __init__(self, module): "sub_int": "SUBINTERFACE", "lo": "INTERFACE_LOOPBACK", "eth": "INTERFACE_ETHERNET", + "svi": "INTERFACE_VLAN", } # New Interfaces @@ -1171,12 +1445,13 @@ def __init__(self, module): "INTERFACE_ETHERNET": 2, "INTERFACE_LOOPBACK": 3, "SUBINTERFACE": 4, + "INTERFACE_VLAN": 5, } def log_msg(self, msg): if self.fd is None: - self.fd = open("interface.log", "w+") + self.fd = open("dcnm_intf.log", "a+") if self.fd is not None: self.fd.write(msg) self.fd.write("\n") @@ -1200,6 +1475,9 @@ def dcnm_intf_get_if_name(self, name, if_type): if "eth" == if_type: port_id = re.findall(r"\d+\/\d+", name) return ("Ethernet" + str(port_id[0]), port_id[0]) + if "svi" == if_type: + port_id = re.findall(r"\d+", name) + return ("vlan" + str(port_id[0]), port_id[0]) def dcnm_intf_get_vpc_serial_number(self, sw): @@ -1237,24 +1515,41 @@ def dcnm_intf_copy_config(self): msg=" element, which is mandatory is missing in config" ) - pol_ind_str = cfg["type"] + "_" + cfg["profile"]["mode"] - c[ck]["fabric"] = self.dcnm_intf_facts["fabric"] if cfg["type"] == "vpc": if self.vpc_ip_sn.get(sw, None) is None: - self.module.fail_json(msg="Switch '{0}' is not part of VPC pair, but given I/F '{1}' is of type VPC".format(sw, c['name'])) + self.module.fail_json( + msg="Switch '{0}' is not part of VPC pair, but given I/F '{1}' is of type VPC".format( + sw, c["name"] + ) + ) else: - c[ck]['sno'] = self.vpc_ip_sn[sw] + c[ck]["sno"] = self.vpc_ip_sn[sw] else: c[ck]["sno"] = self.ip_sn[sw] ifname, port_id = self.dcnm_intf_get_if_name( c["name"], c["type"] ) + + if "mode" not in cfg["profile"]: + self.module.fail_json( + msg="Invalid parameters in playbook: while processing interface " + + ifname + + ", mode : Required parameter not found" + ) + pol_ind_str = ( + cfg["type"] + "_" + cfg["profile"]["mode"] + ) + c[ck]["ifname"] = ifname - c[ck]["policy"] = self.pol_types[self.dcnm_version][pol_ind_str] + c[ck]["policy"] = self.pol_types[self.dcnm_version][ + pol_ind_str + ] self.pb_input.append(c[ck]) - def dcnm_intf_validate_interface_input(self, config, common_spec, prof_spec): + def dcnm_intf_validate_interface_input( + self, config, common_spec, prof_spec + ): plist = [] @@ -1275,19 +1570,22 @@ def dcnm_intf_validate_interface_input(self, config, common_spec, prof_spec): for item in intf_info: plist.append(item["profile"]) - intf_profile, invalid_params = validate_list_of_dicts(plist, prof_spec) + intf_profile, invalid_params = validate_list_of_dicts( + plist, prof_spec + ) # Merge the info from the intf_profile into the intf_info to have a single dict to be used for building # payloads item["profile"].update(intf_profile[0]) plist.remove(item["profile"]) + if invalid_params: mesg = "Invalid parameters in playbook: {0}".format( "while processing interface " + config[0]["name"] - + "\n" - + "\n".join(invalid_params) + + ", " + + ", ".join(invalid_params) ) self.module.fail_json(msg=mesg) @@ -1342,13 +1640,17 @@ def dcnm_intf_validate_port_channel_input(self, config): ) if "trunk" == config[0]["profile"]["mode"]: - self.dcnm_intf_validate_interface_input(config, pc_spec, pc_prof_spec_trunk) + self.dcnm_intf_validate_interface_input( + config, pc_spec, pc_prof_spec_trunk + ) if "access" == config[0]["profile"]["mode"]: self.dcnm_intf_validate_interface_input( config, pc_spec, pc_prof_spec_access ) if "l3" == config[0]["profile"]["mode"]: - self.dcnm_intf_validate_interface_input(config, pc_spec, pc_prof_spec_l3) + self.dcnm_intf_validate_interface_input( + config, pc_spec, pc_prof_spec_l3 + ) if "monitor" == config[0]["profile"]["mode"]: self.dcnm_intf_validate_interface_input(config, pc_spec, None) @@ -1364,8 +1666,12 @@ def dcnm_intf_validate_virtual_port_channel_input(self, cfg): vpc_prof_spec_trunk = dict( mode=dict(required=True, type="str"), - peer1_pcid=dict(type="int", default=0, range_min=1, range_max=4096), - peer2_pcid=dict(type="int", default=0, range_min=1, range_max=4096), + peer1_pcid=dict( + type="int", default=0, range_min=1, range_max=4096 + ), + peer2_pcid=dict( + type="int", default=0, range_min=1, range_max=4096 + ), peer1_members=dict(type="list"), peer2_members=dict(type="list"), pc_mode=dict(type="str", default="active"), @@ -1383,8 +1689,12 @@ def dcnm_intf_validate_virtual_port_channel_input(self, cfg): vpc_prof_spec_access = dict( mode=dict(required=True, type="str"), - peer1_pcid=dict(type="int", default=0, range_min=1, range_max=4096), - peer2_pcid=dict(type="int", default=0, range_min=1, range_max=4096), + peer1_pcid=dict( + type="int", default=0, range_min=1, range_max=4096 + ), + peer2_pcid=dict( + type="int", default=0, range_min=1, range_max=4096 + ), peer1_members=dict(type="list"), peer2_members=dict(type="list"), pc_mode=dict(type="str", default="active"), @@ -1401,9 +1711,13 @@ def dcnm_intf_validate_virtual_port_channel_input(self, cfg): ) if "trunk" == cfg[0]["profile"]["mode"]: - self.dcnm_intf_validate_interface_input(cfg, vpc_spec, vpc_prof_spec_trunk) + self.dcnm_intf_validate_interface_input( + cfg, vpc_spec, vpc_prof_spec_trunk + ) if "access" == cfg[0]["profile"]["mode"]: - self.dcnm_intf_validate_interface_input(cfg, vpc_spec, vpc_prof_spec_access) + self.dcnm_intf_validate_interface_input( + cfg, vpc_spec, vpc_prof_spec_access + ) def dcnm_intf_validate_sub_interface_input(self, cfg): @@ -1419,10 +1733,14 @@ def dcnm_intf_validate_sub_interface_input(self, cfg): mode=dict(required=True, type="str"), vlan=dict(required=True, type="int", range_min=2, range_max=3967), ipv4_addr=dict(required=True, type="ipv4"), - ipv4_mask_len=dict(required=True, type="int", range_min=8, range_max=31), + ipv4_mask_len=dict( + required=True, type="int", range_min=8, range_max=31 + ), int_vrf=dict(type="str", default="default"), ipv6_addr=dict(type="ipv6", default=""), - ipv6_mask_len=dict(type="int", range_min=64, range_max=127, default=64), + ipv6_mask_len=dict( + type="int", range_min=64, range_max=127, default=64 + ), mtu=dict(type="int", range_min=576, range_max=9216, default=9216), cmds=dict(type="list"), description=dict(type="str", default=""), @@ -1468,7 +1786,9 @@ def dcnm_intf_validate_ethernet_interface_input(self, cfg): mode=dict(required=True, type="str"), bpdu_guard=dict(type="str", default="true"), port_type_fast=dict(type="bool", default=True), - mtu=dict(type="str", default="jumbo", choices=["jumbo", "default"]), + mtu=dict( + type="str", default="jumbo", choices=["jumbo", "default"] + ), speed=dict(type="str", default="Auto"), allowed_vlans=dict(type="str", default="none"), cmds=dict(type="list"), @@ -1480,7 +1800,9 @@ def dcnm_intf_validate_ethernet_interface_input(self, cfg): mode=dict(required=True, type="str"), bpdu_guard=dict(type="str", default="true"), port_type_fast=dict(type="bool", default=True), - mtu=dict(type="str", default="jumbo", choices=["jumbo", "default"]), + mtu=dict( + type="str", default="jumbo", choices=["jumbo", "default"] + ), speed=dict(type="str", default="Auto"), access_vlan=dict(type="str", default=""), cmds=dict(type="list"), @@ -1505,7 +1827,9 @@ def dcnm_intf_validate_ethernet_interface_input(self, cfg): ipv4_addr=dict(required=True, type="ipv4"), ipv4_mask_len=dict(type="int", default=8), ipv6_addr=dict(type="ipv6", default=""), - ipv6_mask_len=dict(type="int", range_min=64, range_max=127, default=64), + ipv6_mask_len=dict( + type="int", range_min=64, range_max=127, default=64 + ), route_tag=dict(type="str", default=""), mtu=dict(type="int", default=1500, range_max=9216), speed=dict(type="str", default="Auto"), @@ -1515,9 +1839,13 @@ def dcnm_intf_validate_ethernet_interface_input(self, cfg): ) if "trunk" == cfg[0]["profile"]["mode"]: - self.dcnm_intf_validate_interface_input(cfg, eth_spec, eth_prof_spec_trunk) + self.dcnm_intf_validate_interface_input( + cfg, eth_spec, eth_prof_spec_trunk + ) if "access" == cfg[0]["profile"]["mode"]: - self.dcnm_intf_validate_interface_input(cfg, eth_spec, eth_prof_spec_access) + self.dcnm_intf_validate_interface_input( + cfg, eth_spec, eth_prof_spec_access + ) if "routed" == cfg[0]["profile"]["mode"]: self.dcnm_intf_validate_interface_input( cfg, eth_spec, eth_prof_spec_routed_host @@ -1529,6 +1857,84 @@ def dcnm_intf_validate_ethernet_interface_input(self, cfg): cfg, eth_spec, eth_prof_spec_epl_routed_host ) + def dcnm_intf_validate_vlan_interface_input(self, cfg): + + svi_spec = dict( + name=dict(required=True, type="str"), + switch=dict(required=True, type="list"), + type=dict(required=True, type="str"), + deploy=dict(type="str", default=True), + profile=dict(required=True, type="dict"), + ) + + svi_prof_spec = dict( + mode=dict(required=True, type="str"), + ipv4_addr=dict(type="ipv4", default=""), + int_vrf=dict(type="str", default="default"), + mtu=dict(type="int", range_min=68, range_max=9216, default=9216), + cmds=dict(type="list", default=""), + description=dict(type="str", default=""), + admin_state=dict(required=True, type="bool", default=True), + route_tag=dict(type=str, default=""), + disable_ip_redirects=dict(type="bool", default=True), + dhcp_server_addr1=dict(type="ipv4", default=""), + dhcp_server_addr2=dict(type="ipv4", default=""), + dhcp_server_addr3=dict(type="ipv4", default=""), + adv_subnet_in_underlay=dict(type="bool", default=False), + enable_hsrp=dict(type="bool", default=False), + enable_netflow=dict(type="bool", default=False), + ) + + if cfg[0]["profile"].get("dhcp_server_addr1", "") != "": + svi_prof_spec["vrf_dhcp1"] = dict(required=True, type="str") + else: + svi_prof_spec["vrf_dhcp1"] = dict(type="str", default="") + + if cfg[0]["profile"].get("dhcp_server_addr2", "") != "": + svi_prof_spec["vrf_dhcp2"] = dict(required=True, type="str") + else: + svi_prof_spec["vrf_dhcp2"] = dict(type="str", default="") + + if cfg[0]["profile"].get("dhcp_server_addr3", "") != "": + svi_prof_spec["vrf_dhcp3"] = dict(required=True, type="str") + else: + svi_prof_spec["vrf_dhcp3"] = dict(type="str", default="") + + if cfg[0]["profile"].get("ipv4_addr", False) is not False: + svi_prof_spec["ipv4_mask_len"] = dict( + required=True, type="int", range_min=1, range_max=31 + ) + else: + svi_prof_spec["ipv4_mask_len"] = dict( + type="int", range_min=1, range_max=31, default="" + ) + + if cfg[0]["profile"].get("enable_hsrp", False) is True: + svi_prof_spec["hsrp_vip"] = dict(required=True, type="ipv4") + svi_prof_spec["hsrp_group"] = dict(required=True, type="int") + svi_prof_spec["preempt"] = dict(type="bool", default=False) + else: + svi_prof_spec["hsrp_vip"] = dict(type="ipv4", default="") + svi_prof_spec["hsrp_group"] = dict(type="int", default="") + if cfg[0]["profile"].get("preempt", False) is not False: + self.module.fail_json( + msg="Invalid parameters in playbook: while processing interface " + + cfg[0]["name"] + + ", preempt : Not a valid parameter" + ) + svi_prof_spec["hsrp_priority"] = dict( + type="int", range_min=0, range_max=255, default="" + ) + svi_prof_spec["hsrp_vmac"] = dict(type="str", default="") + svi_prof_spec["hsrp_version"] = dict( + type="int", range_min=1, range_max=2, default="" + ) + + if cfg[0]["profile"].get("enable_netflow", False) is True: + svi_prof_spec["netflow_monitor"] = dict(required=True, type="str") + + self.dcnm_intf_validate_interface_input(cfg, svi_spec, svi_prof_spec) + def dcnm_intf_validate_delete_state_input(self, cfg): del_spec = dict( @@ -1605,30 +2011,38 @@ def dcnm_intf_validate_input(self): self.dcnm_intf_validate_loopback_interface_input(cfg) if item["type"] == "eth": self.dcnm_intf_validate_ethernet_interface_input(cfg) + if item["type"] == "svi": + self.dcnm_intf_validate_vlan_interface_input(cfg) cfg.remove(citem) def dcnm_intf_get_pc_payload(self, delem, intf, profile): # Extract port id from the given name, which is of the form 'po300' - ifname, port_id = self.dcnm_intf_get_if_name(delem["name"], delem["type"]) + ifname, port_id = self.dcnm_intf_get_if_name( + delem["name"], delem["type"] + ) intf["interfaces"][0].update({"ifName": ifname}) if delem[profile]["mode"] == "trunk": if delem[profile]["members"] is None: intf["interfaces"][0]["nvPairs"]["MEMBER_INTERFACES"] = "" else: - intf["interfaces"][0]["nvPairs"]["MEMBER_INTERFACES"] = ",".join( - delem[profile]["members"] - ) - intf["interfaces"][0]["nvPairs"]["PC_MODE"] = delem[profile]["pc_mode"] - intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[profile][ - "bpdu_guard" - ].lower() + intf["interfaces"][0]["nvPairs"][ + "MEMBER_INTERFACES" + ] = ",".join(delem[profile]["members"]) + intf["interfaces"][0]["nvPairs"]["PC_MODE"] = delem[profile][ + "pc_mode" + ] + intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[ + profile + ]["bpdu_guard"].lower() intf["interfaces"][0]["nvPairs"]["PORTTYPE_FAST_ENABLED"] = str( delem[profile]["port_type_fast"] ).lower() - intf["interfaces"][0]["nvPairs"]["MTU"] = str(delem[profile]["mtu"]) + intf["interfaces"][0]["nvPairs"]["MTU"] = str( + delem[profile]["mtu"] + ) intf["interfaces"][0]["nvPairs"]["ALLOWED_VLANS"] = delem[profile][ "allowed_vlans" ] @@ -1637,17 +2051,21 @@ def dcnm_intf_get_pc_payload(self, delem, intf, profile): if delem[profile]["members"] is None: intf["interfaces"][0]["nvPairs"]["MEMBER_INTERFACES"] = "" else: - intf["interfaces"][0]["nvPairs"]["MEMBER_INTERFACES"] = ",".join( - delem[profile]["members"] - ) - intf["interfaces"][0]["nvPairs"]["PC_MODE"] = delem[profile]["pc_mode"] - intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[profile][ - "bpdu_guard" - ].lower() + intf["interfaces"][0]["nvPairs"][ + "MEMBER_INTERFACES" + ] = ",".join(delem[profile]["members"]) + intf["interfaces"][0]["nvPairs"]["PC_MODE"] = delem[profile][ + "pc_mode" + ] + intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[ + profile + ]["bpdu_guard"].lower() intf["interfaces"][0]["nvPairs"]["PORTTYPE_FAST_ENABLED"] = str( delem[profile]["port_type_fast"] ).lower() - intf["interfaces"][0]["nvPairs"]["MTU"] = str(delem[profile]["mtu"]) + intf["interfaces"][0]["nvPairs"]["MTU"] = str( + delem[profile]["mtu"] + ) intf["interfaces"][0]["nvPairs"]["ACCESS_VLAN"] = delem[profile][ "access_vlan" ] @@ -1656,12 +2074,18 @@ def dcnm_intf_get_pc_payload(self, delem, intf, profile): if delem[profile]["members"] is None: intf["interfaces"][0]["nvPairs"]["MEMBER_INTERFACES"] = "" else: - intf["interfaces"][0]["nvPairs"]["MEMBER_INTERFACES"] = ",".join( - delem[profile]["members"] - ) - intf["interfaces"][0]["nvPairs"]["PC_MODE"] = delem[profile]["pc_mode"] - intf["interfaces"][0]["nvPairs"]["INTF_VRF"] = delem[profile]["int_vrf"] - intf["interfaces"][0]["nvPairs"]["IP"] = str(delem[profile]["ipv4_addr"]) + intf["interfaces"][0]["nvPairs"][ + "MEMBER_INTERFACES" + ] = ",".join(delem[profile]["members"]) + intf["interfaces"][0]["nvPairs"]["PC_MODE"] = delem[profile][ + "pc_mode" + ] + intf["interfaces"][0]["nvPairs"]["INTF_VRF"] = delem[profile][ + "int_vrf" + ] + intf["interfaces"][0]["nvPairs"]["IP"] = str( + delem[profile]["ipv4_addr"] + ) if delem[profile]["ipv4_addr"] != "": intf["interfaces"][0]["nvPairs"]["PREFIX"] = str( delem[profile]["ipv4_mask_len"] @@ -1672,12 +2096,16 @@ def dcnm_intf_get_pc_payload(self, delem, intf, profile): "route_tag" ] intf["interfaces"][0]["nvPairs"]["PO_ID"] = ifname - intf["interfaces"][0]["nvPairs"]["MTU"] = str(delem[profile]["mtu"]) + intf["interfaces"][0]["nvPairs"]["MTU"] = str( + delem[profile]["mtu"] + ) if delem[profile]["mode"] == "monitor": intf["interfaces"][0]["nvPairs"]["INTF_NAME"] = ifname if delem[profile]["mode"] != "monitor": - intf["interfaces"][0]["nvPairs"]["DESC"] = delem[profile]["description"] + intf["interfaces"][0]["nvPairs"]["DESC"] = delem[profile][ + "description" + ] if delem[profile]["cmds"] is None: intf["interfaces"][0]["nvPairs"]["CONF"] = "" else: @@ -1692,39 +2120,49 @@ def dcnm_intf_get_vpc_payload(self, delem, intf, profile): # Extract port id from the given name, which is of the form 'vpc300' - ifname, port_id = self.dcnm_intf_get_if_name(delem["name"], delem["type"]) + ifname, port_id = self.dcnm_intf_get_if_name( + delem["name"], delem["type"] + ) intf["interfaces"][0].update({"ifName": ifname}) if delem[profile]["mode"] == "trunk": if delem[profile]["peer1_members"] is None: - intf["interfaces"][0]["nvPairs"]["PEER1_MEMBER_INTERFACES"] = "" + intf["interfaces"][0]["nvPairs"][ + "PEER1_MEMBER_INTERFACES" + ] = "" else: - intf["interfaces"][0]["nvPairs"]["PEER1_MEMBER_INTERFACES"] = ",".join( - delem[profile]["peer1_members"] - ) + intf["interfaces"][0]["nvPairs"][ + "PEER1_MEMBER_INTERFACES" + ] = ",".join(delem[profile]["peer1_members"]) if delem[profile]["peer2_members"] is None: - intf["interfaces"][0]["nvPairs"]["PEER2_MEMBER_INTERFACES"] = "" + intf["interfaces"][0]["nvPairs"][ + "PEER2_MEMBER_INTERFACES" + ] = "" else: - intf["interfaces"][0]["nvPairs"]["PEER2_MEMBER_INTERFACES"] = ",".join( - delem[profile]["peer2_members"] - ) + intf["interfaces"][0]["nvPairs"][ + "PEER2_MEMBER_INTERFACES" + ] = ",".join(delem[profile]["peer2_members"]) - intf["interfaces"][0]["nvPairs"]["PC_MODE"] = delem[profile]["pc_mode"] - intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[profile][ - "bpdu_guard" - ].lower() + intf["interfaces"][0]["nvPairs"]["PC_MODE"] = delem[profile][ + "pc_mode" + ] + intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[ + profile + ]["bpdu_guard"].lower() intf["interfaces"][0]["nvPairs"]["PORTTYPE_FAST_ENABLED"] = str( delem[profile]["port_type_fast"] ).lower() - intf["interfaces"][0]["nvPairs"]["MTU"] = str(delem[profile]["mtu"]) - intf["interfaces"][0]["nvPairs"]["PEER1_ALLOWED_VLANS"] = delem[profile][ - "peer1_allowed_vlans" - ] - intf["interfaces"][0]["nvPairs"]["PEER2_ALLOWED_VLANS"] = delem[profile][ - "peer2_allowed_vlans" - ] + intf["interfaces"][0]["nvPairs"]["MTU"] = str( + delem[profile]["mtu"] + ) + intf["interfaces"][0]["nvPairs"]["PEER1_ALLOWED_VLANS"] = delem[ + profile + ]["peer1_allowed_vlans"] + intf["interfaces"][0]["nvPairs"]["PEER2_ALLOWED_VLANS"] = delem[ + profile + ]["peer2_allowed_vlans"] if delem[profile]["peer1_pcid"] == 0: intf["interfaces"][0]["nvPairs"]["PEER1_PCID"] = str(port_id) @@ -1743,33 +2181,41 @@ def dcnm_intf_get_vpc_payload(self, delem, intf, profile): if delem[profile]["mode"] == "access": if delem[profile]["peer1_members"] is None: - intf["interfaces"][0]["nvPairs"]["PEER1_MEMBER_INTERFACES"] = "" + intf["interfaces"][0]["nvPairs"][ + "PEER1_MEMBER_INTERFACES" + ] = "" else: - intf["interfaces"][0]["nvPairs"]["PEER1_MEMBER_INTERFACES"] = ",".join( - delem[profile]["peer1_members"] - ) + intf["interfaces"][0]["nvPairs"][ + "PEER1_MEMBER_INTERFACES" + ] = ",".join(delem[profile]["peer1_members"]) if delem[profile]["peer2_members"] is None: - intf["interfaces"][0]["nvPairs"]["PEER2_MEMBER_INTERFACES"] = "" + intf["interfaces"][0]["nvPairs"][ + "PEER2_MEMBER_INTERFACES" + ] = "" else: - intf["interfaces"][0]["nvPairs"]["PEER2_MEMBER_INTERFACES"] = ",".join( - delem[profile]["peer2_members"] - ) + intf["interfaces"][0]["nvPairs"][ + "PEER2_MEMBER_INTERFACES" + ] = ",".join(delem[profile]["peer2_members"]) - intf["interfaces"][0]["nvPairs"]["PC_MODE"] = delem[profile]["pc_mode"] - intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[profile][ - "bpdu_guard" - ].lower() + intf["interfaces"][0]["nvPairs"]["PC_MODE"] = delem[profile][ + "pc_mode" + ] + intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[ + profile + ]["bpdu_guard"].lower() intf["interfaces"][0]["nvPairs"]["PORTTYPE_FAST_ENABLED"] = str( delem[profile]["port_type_fast"] ).lower() - intf["interfaces"][0]["nvPairs"]["MTU"] = str(delem[profile]["mtu"]) - intf["interfaces"][0]["nvPairs"]["PEER1_ACCESS_VLAN"] = delem[profile][ - "peer1_access_vlan" - ] - intf["interfaces"][0]["nvPairs"]["PEER2_ACCESS_VLAN"] = delem[profile][ - "peer2_access_vlan" - ] + intf["interfaces"][0]["nvPairs"]["MTU"] = str( + delem[profile]["mtu"] + ) + intf["interfaces"][0]["nvPairs"]["PEER1_ACCESS_VLAN"] = delem[ + profile + ]["peer1_access_vlan"] + intf["interfaces"][0]["nvPairs"]["PEER2_ACCESS_VLAN"] = delem[ + profile + ]["peer2_access_vlan"] if delem[profile]["peer1_pcid"] == 0: intf["interfaces"][0]["nvPairs"]["PEER1_PCID"] = str(port_id) @@ -1812,17 +2258,25 @@ def dcnm_intf_get_sub_intf_payload(self, delem, intf, profile): # Extract port id from the given name, which is of the form 'po300' - ifname, port_id = self.dcnm_intf_get_if_name(delem["name"], delem["type"]) + ifname, port_id = self.dcnm_intf_get_if_name( + delem["name"], delem["type"] + ) intf["interfaces"][0].update({"ifName": ifname}) intf["interfaces"][0]["nvPairs"]["VLAN"] = str(delem[profile]["vlan"]) - intf["interfaces"][0]["nvPairs"]["INTF_VRF"] = delem[profile]["int_vrf"] - intf["interfaces"][0]["nvPairs"]["IP"] = str(delem[profile]["ipv4_addr"]) + intf["interfaces"][0]["nvPairs"]["INTF_VRF"] = delem[profile][ + "int_vrf" + ] + intf["interfaces"][0]["nvPairs"]["IP"] = str( + delem[profile]["ipv4_addr"] + ) intf["interfaces"][0]["nvPairs"]["PREFIX"] = str( delem[profile]["ipv4_mask_len"] ) if delem[profile]["ipv6_addr"]: - intf["interfaces"][0]["nvPairs"]["IPv6"] = str(delem[profile]["ipv6_addr"]) + intf["interfaces"][0]["nvPairs"]["IPv6"] = str( + delem[profile]["ipv6_addr"] + ) intf["interfaces"][0]["nvPairs"]["IPv6_PREFIX"] = str( delem[profile]["ipv6_mask_len"] ) @@ -1831,11 +2285,15 @@ def dcnm_intf_get_sub_intf_payload(self, delem, intf, profile): intf["interfaces"][0]["nvPairs"]["IPv6_PREFIX"] = "" intf["interfaces"][0]["nvPairs"]["MTU"] = str(delem[profile]["mtu"]) intf["interfaces"][0]["nvPairs"]["INTF_NAME"] = ifname - intf["interfaces"][0]["nvPairs"]["DESC"] = delem[profile]["description"] + intf["interfaces"][0]["nvPairs"]["DESC"] = delem[profile][ + "description" + ] if delem[profile]["cmds"] is None: intf["interfaces"][0]["nvPairs"]["CONF"] = "" else: - intf["interfaces"][0]["nvPairs"]["CONF"] = "\n".join(delem[profile]["cmds"]) + intf["interfaces"][0]["nvPairs"]["CONF"] = "\n".join( + delem[profile]["cmds"] + ) intf["interfaces"][0]["nvPairs"]["ADMIN_STATE"] = str( delem[profile]["admin_state"] ).lower() @@ -1844,19 +2302,33 @@ def dcnm_intf_get_loopback_payload(self, delem, intf, profile): # Extract port id from the given name, which is of the form 'po300' - ifname, port_id = self.dcnm_intf_get_if_name(delem["name"], delem["type"]) + ifname, port_id = self.dcnm_intf_get_if_name( + delem["name"], delem["type"] + ) intf["interfaces"][0].update({"ifName": ifname}) - intf["interfaces"][0]["nvPairs"]["INTF_VRF"] = delem[profile]["int_vrf"] - intf["interfaces"][0]["nvPairs"]["IP"] = str(delem[profile]["ipv4_addr"]) - intf["interfaces"][0]["nvPairs"]["V6IP"] = str(delem[profile]["ipv6_addr"]) - intf["interfaces"][0]["nvPairs"]["ROUTE_MAP_TAG"] = delem[profile]["route_tag"] + intf["interfaces"][0]["nvPairs"]["INTF_VRF"] = delem[profile][ + "int_vrf" + ] + intf["interfaces"][0]["nvPairs"]["IP"] = str( + delem[profile]["ipv4_addr"] + ) + intf["interfaces"][0]["nvPairs"]["V6IP"] = str( + delem[profile]["ipv6_addr"] + ) + intf["interfaces"][0]["nvPairs"]["ROUTE_MAP_TAG"] = delem[profile][ + "route_tag" + ] intf["interfaces"][0]["nvPairs"]["INTF_NAME"] = ifname - intf["interfaces"][0]["nvPairs"]["DESC"] = delem[profile]["description"] + intf["interfaces"][0]["nvPairs"]["DESC"] = delem[profile][ + "description" + ] if delem[profile]["cmds"] is None: intf["interfaces"][0]["nvPairs"]["CONF"] = "" else: - intf["interfaces"][0]["nvPairs"]["CONF"] = "\n".join(delem[profile]["cmds"]) + intf["interfaces"][0]["nvPairs"]["CONF"] = "\n".join( + delem[profile]["cmds"] + ) intf["interfaces"][0]["nvPairs"]["ADMIN_STATE"] = str( delem[profile]["admin_state"] ).lower() @@ -1865,38 +2337,52 @@ def dcnm_intf_get_eth_payload(self, delem, intf, profile): # Extract port id from the given name, which is of the form 'po300' - ifname, port_id = self.dcnm_intf_get_if_name(delem["name"], delem["type"]) + ifname, port_id = self.dcnm_intf_get_if_name( + delem["name"], delem["type"] + ) intf["interfaces"][0].update({"ifName": ifname}) if delem[profile]["mode"] == "trunk": - intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[profile][ - "bpdu_guard" - ].lower() + intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[ + profile + ]["bpdu_guard"].lower() intf["interfaces"][0]["nvPairs"]["PORTTYPE_FAST_ENABLED"] = str( delem[profile]["port_type_fast"] ).lower() - intf["interfaces"][0]["nvPairs"]["MTU"] = str(delem[profile]["mtu"]) - intf["interfaces"][0]["nvPairs"]["SPEED"] = str(delem[profile]["speed"]) + intf["interfaces"][0]["nvPairs"]["MTU"] = str( + delem[profile]["mtu"] + ) + intf["interfaces"][0]["nvPairs"]["SPEED"] = str( + delem[profile]["speed"] + ) intf["interfaces"][0]["nvPairs"]["ALLOWED_VLANS"] = delem[profile][ "allowed_vlans" ] intf["interfaces"][0]["nvPairs"]["INTF_NAME"] = ifname if delem[profile]["mode"] == "access": - intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[profile][ - "bpdu_guard" - ].lower() + intf["interfaces"][0]["nvPairs"]["BPDUGUARD_ENABLED"] = delem[ + profile + ]["bpdu_guard"].lower() intf["interfaces"][0]["nvPairs"]["PORTTYPE_FAST_ENABLED"] = str( delem[profile]["port_type_fast"] ).lower() - intf["interfaces"][0]["nvPairs"]["MTU"] = str(delem[profile]["mtu"]) - intf["interfaces"][0]["nvPairs"]["SPEED"] = str(delem[profile]["speed"]) + intf["interfaces"][0]["nvPairs"]["MTU"] = str( + delem[profile]["mtu"] + ) + intf["interfaces"][0]["nvPairs"]["SPEED"] = str( + delem[profile]["speed"] + ) intf["interfaces"][0]["nvPairs"]["ACCESS_VLAN"] = delem[profile][ "access_vlan" ] intf["interfaces"][0]["nvPairs"]["INTF_NAME"] = ifname if delem[profile]["mode"] == "routed": - intf["interfaces"][0]["nvPairs"]["INTF_VRF"] = delem[profile]["int_vrf"] - intf["interfaces"][0]["nvPairs"]["IP"] = str(delem[profile]["ipv4_addr"]) + intf["interfaces"][0]["nvPairs"]["INTF_VRF"] = delem[profile][ + "int_vrf" + ] + intf["interfaces"][0]["nvPairs"]["IP"] = str( + delem[profile]["ipv4_addr"] + ) if delem[profile]["ipv4_addr"] != "": intf["interfaces"][0]["nvPairs"]["PREFIX"] = str( delem[profile]["ipv4_mask_len"] @@ -1906,38 +2392,158 @@ def dcnm_intf_get_eth_payload(self, delem, intf, profile): intf["interfaces"][0]["nvPairs"]["ROUTING_TAG"] = delem[profile][ "route_tag" ] - intf["interfaces"][0]["nvPairs"]["MTU"] = str(delem[profile]["mtu"]) - intf["interfaces"][0]["nvPairs"]["SPEED"] = str(delem[profile]["speed"]) + intf["interfaces"][0]["nvPairs"]["MTU"] = str( + delem[profile]["mtu"] + ) + intf["interfaces"][0]["nvPairs"]["SPEED"] = str( + delem[profile]["speed"] + ) intf["interfaces"][0]["nvPairs"]["INTF_NAME"] = ifname if delem[profile]["mode"] == "monitor": intf["interfaces"][0]["nvPairs"]["INTF_NAME"] = ifname if delem[profile]["mode"] == "epl_routed": - intf["interfaces"][0]["nvPairs"]["IP"] = str(delem[profile]["ipv4_addr"]) + intf["interfaces"][0]["nvPairs"]["IP"] = str( + delem[profile]["ipv4_addr"] + ) intf["interfaces"][0]["nvPairs"]["PREFIX"] = str( delem[profile]["ipv4_mask_len"] ) - intf["interfaces"][0]["nvPairs"]["IPv6"] = str(delem[profile]["ipv6_addr"]) + intf["interfaces"][0]["nvPairs"]["IPv6"] = str( + delem[profile]["ipv6_addr"] + ) intf["interfaces"][0]["nvPairs"]["IPv6_PREFIX"] = str( delem[profile]["ipv6_mask_len"] ) intf["interfaces"][0]["nvPairs"]["ROUTING_TAG"] = delem[profile][ "route_tag" ] - intf["interfaces"][0]["nvPairs"]["MTU"] = str(delem[profile]["mtu"]) - intf["interfaces"][0]["nvPairs"]["SPEED"] = str(delem[profile]["speed"]) + intf["interfaces"][0]["nvPairs"]["MTU"] = str( + delem[profile]["mtu"] + ) + intf["interfaces"][0]["nvPairs"]["SPEED"] = str( + delem[profile]["speed"] + ) intf["interfaces"][0]["nvPairs"]["INTF_NAME"] = ifname if delem[profile]["mode"] != "monitor": - intf["interfaces"][0]["nvPairs"]["DESC"] = delem[profile]["description"] + intf["interfaces"][0]["nvPairs"]["DESC"] = delem[profile][ + "description" + ] + if delem[profile]["cmds"] is None: + intf["interfaces"][0]["nvPairs"]["CONF"] = "" + else: + intf["interfaces"][0]["nvPairs"]["CONF"] = "\n".join( + delem[profile]["cmds"] + ) + intf["interfaces"][0]["nvPairs"]["ADMIN_STATE"] = str( + delem[profile]["admin_state"] + ).lower() + + def dcnm_intf_get_svi_payload(self, delem, intf, profile): + + # Extract port id from the given name, which is of the form 'po300' + + ifname, port_id = self.dcnm_intf_get_if_name( + delem["name"], delem["type"] + ) + intf["interfaces"][0].update({"ifName": ifname}) + + if delem[profile]["mode"] == "vlan": + intf["interfaces"][0]["nvPairs"]["INTF_VRF"] = delem[profile][ + "int_vrf" + ] + intf["interfaces"][0]["nvPairs"]["IP"] = str( + delem[profile]["ipv4_addr"] + ) + intf["interfaces"][0]["nvPairs"]["PREFIX"] = str( + delem[profile]["ipv4_mask_len"] + ) + intf["interfaces"][0]["nvPairs"]["MTU"] = str( + delem[profile]["mtu"] + ) + intf["interfaces"][0]["nvPairs"]["ROUTING_TAG"] = str( + delem[profile]["route_tag"] + ) + intf["interfaces"][0]["nvPairs"]["DISABLE_IP_REDIRECTS"] = str( + delem[profile]["disable_ip_redirects"] + ).lower() + intf["interfaces"][0]["nvPairs"]["DESC"] = delem[profile][ + "description" + ] + if delem[profile]["cmds"] is None: intf["interfaces"][0]["nvPairs"]["CONF"] = "" else: intf["interfaces"][0]["nvPairs"]["CONF"] = "\n".join( delem[profile]["cmds"] ) + # intf["interfaces"][0]["nvPairs"]["CONF"] = str(delem[profile]["cmds"]) intf["interfaces"][0]["nvPairs"]["ADMIN_STATE"] = str( delem[profile]["admin_state"] ).lower() + intf["interfaces"][0]["nvPairs"]["ENABLE_HSRP"] = str( + delem[profile]["enable_hsrp"] + ).lower() + intf["interfaces"][0]["nvPairs"]["HSRP_VIP"] = str( + delem[profile]["hsrp_vip"] + ) + intf["interfaces"][0]["nvPairs"]["HSRP_GROUP"] = str( + delem[profile]["hsrp_group"] + ) + if str(delem[profile]["enable_hsrp"]).lower() == "true": + intf["interfaces"][0]["nvPairs"]["PREEMPT"] = str( + delem[profile]["preempt"] + ).lower() + else: + intf["interfaces"][0]["nvPairs"]["PREEMPT"] = str( + False + ).lower() + + intf["interfaces"][0]["nvPairs"]["HSRP_VERSION"] = str( + delem[profile]["hsrp_version"] + ) + intf["interfaces"][0]["nvPairs"]["HSRP_PRIORITY"] = str( + delem[profile]["hsrp_priority"] + ) + intf["interfaces"][0]["nvPairs"]["MAC"] = str( + delem[profile]["hsrp_vmac"] + ) + intf["interfaces"][0]["nvPairs"]["dhcpServerAddr1"] = str( + delem[profile]["dhcp_server_addr1"] + ) + intf["interfaces"][0]["nvPairs"]["vrfDhcp1"] = str( + delem[profile]["vrf_dhcp1"] + ) + intf["interfaces"][0]["nvPairs"]["dhcpServerAddr2"] = str( + delem[profile]["dhcp_server_addr2"] + ) + intf["interfaces"][0]["nvPairs"]["vrfDhcp2"] = str( + delem[profile]["vrf_dhcp2"] + ) + intf["interfaces"][0]["nvPairs"]["dhcpServerAddr3"] = str( + delem[profile]["dhcp_server_addr3"] + ) + intf["interfaces"][0]["nvPairs"]["vrfDhcp3"] = str( + delem[profile]["vrf_dhcp3"] + ) + intf["interfaces"][0]["nvPairs"]["advSubnetInUnderlay"] = str( + delem[profile]["adv_subnet_in_underlay"] + ).lower() + intf["interfaces"][0]["nvPairs"]["ENABLE_NETFLOW"] = str( + delem[profile]["enable_netflow"] + ).lower() + + if str(delem[profile]["enable_netflow"]).lower() == "true": + intf["interfaces"][0]["nvPairs"]["NETFLOW_MONITOR"] = str( + delem[profile]["netflow_monitor"] + ) + else: + intf["interfaces"][0]["nvPairs"]["NETFLOW_MONITOR"] = "" + + intf["interfaces"][0]["nvPairs"]["INTF_NAME"] = ifname + + # we don't need SPEED for SVI interfaces + intf["interfaces"][0]["nvPairs"].pop("SPEED") # New Interfaces def dcnm_get_intf_payload(self, delem, sw): @@ -1975,18 +2581,24 @@ def dcnm_get_intf_payload(self, delem, sw): # intf.update ({"interfaceType" : self.int_types[delem['type']]}) if "vpc" == delem["type"]: - intf["interfaces"][0].update({"serialNumber": str(self.vpc_ip_sn[sw])}) + intf["interfaces"][0].update( + {"serialNumber": str(self.vpc_ip_sn[sw])} + ) else: intf["interfaces"][0].update({"serialNumber": str(self.ip_sn[sw])}) - intf["interfaces"][0].update({"interfaceType": self.int_types[delem["type"]]}) + intf["interfaces"][0].update( + {"interfaceType": self.int_types[delem["type"]]} + ) intf["interfaces"][0].update({"fabricName": self.fabric}) if "profile" not in delem.keys(): # for state 'deleted', 'profile' construct is not included. So just update the ifName here # and return. Rest of the code is all 'profile' specific and hence not required for 'deleted' - ifname, port_id = self.dcnm_intf_get_if_name(delem["name"], delem["type"]) + ifname, port_id = self.dcnm_intf_get_if_name( + delem["name"], delem["type"] + ) intf["interfaces"][0].update({"ifName": ifname}) return intf @@ -2011,6 +2623,9 @@ def dcnm_get_intf_payload(self, delem, sw): # them out intf.pop("skipResourceCheck") + if "svi" == delem["type"]: + self.dcnm_intf_get_svi_payload(delem, intf, "profile") + return intf def dcnm_intf_merge_intf_info(self, intf_info, if_head): @@ -2117,15 +2732,19 @@ def dcnm_intf_get_have(self): def dcnm_intf_translate_elements(self, ie1, ie2): if sys.version_info[0] >= 3: - # Python version 3 onwards trfeats unicode as strings. No special treatment is required + # Python version 3 onwards treats unicode as strings. No special treatment is required e1 = ie1 e2 = ie2 else: - if isinstance(ie1, unicode): # noqa pylint: disable=undefined-variable + if isinstance( + ie1, unicode # noqa pylint: disable=undefined-variable + ): e1 = ie1.encode("utf-8") else: e1 = ie1 - if isinstance(ie2, unicode): # noqa pylint: disable=undefined-variable + if isinstance( + ie2, unicode # noqa pylint: disable=undefined-variable + ): e2 = ie2.encode("utf-8") else: e2 = ie2 @@ -2143,17 +2762,19 @@ def dcnm_intf_merge_want_and_have(self, key, wvalue, hvalue): elif e2 == "": comb_key = e1 else: - comb_key = e2 + '\n' + e1 + comb_key = e2 + "\n" + e1 else: if e1 == "": comb_key = e2 elif e2 == "": comb_key = e1 else: - comb_key = e2 + ',' + e1 + comb_key = e2 + "," + e1 return comb_key - def dcnm_intf_compare_elements(self, name, sno, fabric, ie1, ie2, k, state): + def dcnm_intf_compare_elements( + self, name, sno, fabric, ie1, ie2, k, state + ): # unicode encoded strings must be decoded to get proper strings which is required # for comparison purposes @@ -2198,6 +2819,7 @@ def dcnm_intf_compare_elements(self, name, sno, fabric, ie1, ie2, k, state): t_e2 = e2 if t_e1 != t_e2: + if (state == "replaced") or (state == "overridden"): return "add" elif state == "merged": @@ -2324,7 +2946,8 @@ def dcnm_intf_compare_want_and_have(self, state): for ik in if_keys: if ik == "nvPairs": nv_keys = list(want[k][0][ik].keys()) - nv_keys.remove("SPEED") + if "SPEED" in nv_keys: + nv_keys.remove("SPEED") for nk in nv_keys: # HAVE may have an entry with a list # of interfaces. Check all the # interface entries for a match. Even if one entry matches do not @@ -2342,11 +2965,21 @@ def dcnm_intf_compare_want_and_have(self, state): if res == "dont_add": break if res == "copy_and_add": - want[k][0][ik][nk] = d[k][0][ik][nk] + want[k][0][ik][nk] = d[k][0][ik][ + nk + ] continue - if (res == "merge_and_add"): - want[k][0][ik][nk] = self.dcnm_intf_merge_want_and_have(nk, want[k][0][ik][nk], d[k][0][ik][nk]) - changed_dict[k][0][ik][nk] = want[k][0][ik][nk] + if res == "merge_and_add": + want[k][0][ik][ + nk + ] = self.dcnm_intf_merge_want_and_have( + nk, + want[k][0][ik][nk], + d[k][0][ik][nk], + ) + changed_dict[k][0][ik][nk] = want[ + k + ][0][ik][nk] if res != "dont_add": action = "update" else: @@ -2371,8 +3004,12 @@ def dcnm_intf_compare_want_and_have(self, state): if res == "copy_and_add": want[k][0][ik] = d[k][0][ik] continue - if (res == "merge_and_add"): - want[k][0][ik] = self.dcnm_intf_merge_want_and_have(ik, want[k][0][ik], d[k][0][ik]) + if res == "merge_and_add": + want[k][0][ + ik + ] = self.dcnm_intf_merge_want_and_have( + ik, want[k][0][ik], d[k][0][ik] + ) changed_dict[k][0][ik] = want[k][0][ik] if res != "dont_add": action = "update" @@ -2388,8 +3025,10 @@ def dcnm_intf_compare_want_and_have(self, state): if res == "copy_and_add": want[k] = d[k] continue - if (res == "merge_and_add"): - want[k] = self.dcnm_intf_merge_want_and_have(k, want[k], d[k]) + if res == "merge_and_add": + want[k] = self.dcnm_intf_merge_want_and_have( + k, want[k], d[k] + ) changed_dict[k] = want[k] if res != "dont_add": action = "update" @@ -2443,8 +3082,8 @@ def dcnm_intf_compare_want_and_have(self, state): def dcnm_intf_get_diff_replaced(self): self.diff_create = [] - self.diff_delete = [[], [], [], [], []] - self.diff_delete_deploy = [[], [], [], [], []] + self.diff_delete = [[], [], [], [], [], []] + self.diff_delete_deploy = [[], [], [], [], [], []] self.diff_deploy = [] self.diff_replace = [] @@ -2460,7 +3099,7 @@ def dcnm_intf_get_diff_replaced(self): def dcnm_intf_get_diff_merge(self): self.diff_create = [] - self.diff_delete_deploy = [[], [], [], [], []] + self.diff_delete_deploy = [[], [], [], [], [], []] self.diff_deploy = [] self.diff_replace = [] @@ -2549,7 +3188,8 @@ def dcnm_intf_can_be_replaced(self, have): return False, item["ifname"] if item.get("members"): if have["ifName"] in [ - self.dcnm_intf_get_if_name(mem, "eth")[0] for mem in item["members"] + self.dcnm_intf_get_if_name(mem, "eth")[0] + for mem in item["members"] ]: return False, item["ifname"] elif (item.get("peer1_members")) or (item.get("peer2_members")): @@ -2586,15 +3226,16 @@ def dcnm_intf_process_config(self, cfg): # both the switches. So check before adding to have_all if not any( - d.get("serialNo", None) == self.ip_sn[sw] for d in self.have_all + d.get("serialNo", None) == self.ip_sn[sw] + for d in self.have_all ): self.dcnm_intf_get_have_all(sw) def dcnm_intf_get_diff_overridden(self, cfg): self.diff_create = [] - self.diff_delete = [[], [], [], [], []] - self.diff_delete_deploy = [[], [], [], [], []] + self.diff_delete = [[], [], [], [], [], []] + self.diff_delete_deploy = [[], [], [], [], [], []] self.diff_deploy = [] self.diff_replace = [] @@ -2635,7 +3276,9 @@ def dcnm_intf_get_diff_overridden(self, cfg): defer_list.append(have) continue - uelem = self.dcnm_intf_get_default_eth_payload(name, sno, fabric) + uelem = self.dcnm_intf_get_default_eth_payload( + name, sno, fabric + ) # Before we add the interface to replace list, check if the default payload is same as # what is already present. If both are same, skip the interface. # So during idempotence, we may add the same interface again if we don't compare @@ -2643,7 +3286,16 @@ def dcnm_intf_get_diff_overridden(self, cfg): intf = self.dcnm_intf_get_intf_info( have["ifName"], have["serialNo"], have["ifType"] ) - if self.dcnm_compare_default_payload(uelem, intf) == "DCNM_INTF_MATCH": + + if intf == []: + # In case of LANClassic fabrics, a GET on policy details for Ethernet interfaces will return [] since + # these interfaces dont have any policies configured by default. In that case there is nothing to be done + continue + + if ( + self.dcnm_compare_default_payload(uelem, intf) + == "DCNM_INTF_MATCH" + ): continue if uelem is not None: @@ -2651,13 +3303,19 @@ def dcnm_intf_get_diff_overridden(self, cfg): # member of any port-channel. If so, do not default that rc, intf = self.dcnm_intf_can_be_replaced(have) if rc is True: - self.dcnm_intf_merge_intf_info(uelem, self.diff_replace) - self.changed_dict[0]["replaced"].append(copy.deepcopy(uelem)) + self.dcnm_intf_merge_intf_info( + uelem, self.diff_replace + ) + self.changed_dict[0]["replaced"].append( + copy.deepcopy(uelem) + ) delem["serialNumber"] = sno delem["ifName"] = name delem["fabricName"] = self.fabric self.diff_deploy.append(delem) - self.changed_dict[0]["deploy"].append(copy.deepcopy(delem)) + self.changed_dict[0]["deploy"].append( + copy.deepcopy(delem) + ) # Sub-interafces are returned as INTERFACE_ETHERNET in have_all. So do an # additional check to see if it is physical. If not assume it to be sub-interface @@ -2669,6 +3327,7 @@ def dcnm_intf_get_diff_overridden(self, cfg): or (have["ifType"] == "INTERFACE_LOOPBACK") or (have["ifType"] == "SUBINTERFACE") or (have["ifType"] == "INTERFACE_VPC") + or (have["ifType"] == "INTERFACE_VLAN") or ( (have["ifType"] == "INTERFACE_ETHERNET") and ( @@ -2702,7 +3361,10 @@ def dcnm_intf_get_diff_overridden(self, cfg): d for d in self.want if ( - (name.lower() == d["interfaces"][0]["ifName"].lower()) + ( + name.lower() + == d["interfaces"][0]["ifName"].lower() + ) and (sno == d["interfaces"][0]["serialNumber"]) and (fabric == d["interfaces"][0]["fabricName"]) ) @@ -2718,12 +3380,18 @@ def dcnm_intf_get_diff_overridden(self, cfg): delem["serialNumber"] = sno delem["fabricName"] = fabric - self.diff_delete[self.int_index[have["ifType"]]].append(delem) - if have["mode"] is not None: - self.diff_delete_deploy[ - self.int_index[have["ifType"]] - ].append(delem) - self.changed_dict[0]["deleted"].append(copy.deepcopy(delem)) + self.diff_delete[ + self.int_index[have["ifType"]] + ].append(delem) + + # For INTERFACE_VLAN the "mode" argment is not set in have_all. + # if have["mode"] is not None or have["ifType"] == "INTERFACE_VLAN": + self.diff_delete_deploy[ + self.int_index[have["ifType"]] + ].append(delem) + self.changed_dict[0]["deleted"].append( + copy.deepcopy(delem) + ) del_list.append(have) for intf in defer_list: @@ -2763,8 +3431,8 @@ def dcnm_intf_get_diff_overridden(self, cfg): def dcnm_intf_get_diff_deleted(self): self.diff_create = [] - self.diff_delete = [[], [], [], [], []] - self.diff_delete_deploy = [[], [], [], [], []] + self.diff_delete = [[], [], [], [], [], []] + self.diff_delete_deploy = [[], [], [], [], [], []] self.diff_deploy = [] self.diff_replace = [] @@ -2834,21 +3502,38 @@ def dcnm_intf_get_diff_deleted(self): have for have in self.have_all if ( - (intf["ifName"].lower() == have["ifName"].lower()) - and (intf["serialNumber"] == have["serialNo"]) + ( + intf["ifName"].lower() + == have["ifName"].lower() + ) + and ( + intf["serialNumber"] + == have["serialNo"] + ) ) ][0] if ( match_have - and (str(match_have["isPhysical"]).lower() != "none") - and (str(match_have["isPhysical"]).lower() == "true") + and ( + str(match_have["isPhysical"]).lower() + != "none" + ) + and ( + str(match_have["isPhysical"]).lower() + == "true" + ) ): - if str(match_have["deletable"]).lower() == "false": + if ( + str(match_have["deletable"]).lower() + == "false" + ): continue uelem = self.dcnm_intf_get_default_eth_payload( - intf["ifName"], intf["serialNumber"], self.fabric + intf["ifName"], + intf["serialNumber"], + self.fabric, ) intf_payload = self.dcnm_intf_get_intf_info_from_dcnm( intf @@ -2877,15 +3562,19 @@ def dcnm_intf_get_diff_deleted(self): self.dcnm_intf_merge_intf_info( uelem, self.diff_replace ) - self.changed_dict[0]["replaced"].append( - copy.deepcopy(uelem) - ) - delem["serialNumber"] = intf["serialNumber"] + self.changed_dict[0][ + "replaced" + ].append(copy.deepcopy(uelem)) + delem["serialNumber"] = intf[ + "serialNumber" + ] delem["ifName"] = if_name delem["fabricName"] = self.fabric self.diff_deploy.append(delem) else: - intf_payload = self.dcnm_intf_get_intf_info_from_dcnm(intf) + intf_payload = self.dcnm_intf_get_intf_info_from_dcnm( + intf + ) if intf_payload != []: delem["interfaceDbId"] = 0 @@ -2894,7 +3583,9 @@ def dcnm_intf_get_diff_deleted(self): delem["serialNumber"] = intf["serialNumber"] delem["fabricName"] = self.fabric - self.diff_delete[self.int_index[if_type]].append(delem) + self.diff_delete[ + self.int_index[if_type] + ].append(delem) if "monitor" not in intf_payload["policy"]: self.diff_delete_deploy[ self.int_index[if_type] @@ -2915,14 +3606,21 @@ def dcnm_extract_if_name(self, cfg): if_type = "INTERFACE_LOOPBACK" elif cfg["name"][0:3].lower() == "eth": if "." not in cfg["name"]: - if_name, port_id = self.dcnm_intf_get_if_name(cfg["name"], "eth") + if_name, port_id = self.dcnm_intf_get_if_name( + cfg["name"], "eth" + ) if_type = "INTERFACE_ETHERNET" else: - if_name, port_id = self.dcnm_intf_get_if_name(cfg["name"], "sub_int") + if_name, port_id = self.dcnm_intf_get_if_name( + cfg["name"], "sub_int" + ) if_type = "SUBINTERFACE" elif cfg["name"][0:3].lower() == "vpc": if_name, port_id = self.dcnm_intf_get_if_name(cfg["name"], "vpc") if_type = "INTERFACE_VPC" + elif cfg["name"][0:4].lower() == "vlan": + if_name, port_id = self.dcnm_intf_get_if_name(cfg["name"], "svi") + if_type = "INTERFACE_VLAN" else: if_name = "" if_type = "" @@ -2966,11 +3664,14 @@ def dcnm_parse_response(self, resp): ent_resp = {} for ent in entities: + ent_resp[ent] = "No Error" if isinstance(resp["DATA"], list): for data in resp["DATA"]: host = data.get("entity") + if host: + host = host.split(":")[0] if self.hn_sn.get(host) == ent: ent_resp[ent] = data.get("message") else: @@ -2978,24 +3679,21 @@ def dcnm_parse_response(self, resp): elif isinstance(resp["DATA"], str): ent_resp[ent] = resp["DATA"] + succ_resp["ORIG_MSG"] = [] for ent in entities: if ent_resp[ent] == "No Error": - # Consider this case as success. - succ_resp["REQUEST_PATH"] = resp["REQUEST_PATH"] - succ_resp["MESSAGE"] = "OK" - succ_resp["METHOD"] = resp["METHOD"] - succ_resp["RETURN_CODE"] = 200 - return succ_resp, True + continue elif ( ("No Commands to execute" in ent_resp[ent]) or (ent_resp[ent] == "Failed to fetch policies") or (ent_resp[ent] == "Failed to fetch switch configuration") + or (ent_resp[ent] == "In-Sync") ): # Consider this case as success. succ_resp["REQUEST_PATH"] = resp["REQUEST_PATH"] succ_resp["MESSAGE"] = "OK" succ_resp["METHOD"] = resp["METHOD"] - succ_resp["ORIG_MSG"] = ent_resp[ent] + succ_resp["ORIG_MSG"].append(ent_resp[ent]) succ_resp["RETURN_CODE"] = 200 else: failed = True @@ -3018,7 +3716,9 @@ def dcnm_intf_send_message_handle_retry(self, action, path, payload, cmd): # Consider that as success and mark the change flag as 'False; to indicate # nothinbg actually changed - if (resp.get("MESSAGE") == "OK") and (resp.get("RETURN_CODE") == 200): + if (resp.get("MESSAGE") == "OK") and ( + resp.get("RETURN_CODE") == 200 + ): return resp, True presp, changed = self.dcnm_parse_response(resp) @@ -3065,9 +3765,11 @@ def dcnm_intf_check_deployment_status(self, deploy_list): path = self.paths["GLOBAL_IF_DEPLOY"] + resp = {} + for item in deploy_list: retries = 0 - while retries < 50: + while retries < 60: retries += 1 name = item["ifName"] sno = item["serialNumber"] @@ -3093,21 +3795,29 @@ def dcnm_intf_check_deployment_status(self, deploy_list): "fabricName": self.fabric, } ) - dcnm_send(self.module, "POST", path, json_payload) - time.sleep(20) + resp = dcnm_send( + self.module, "POST", path, json_payload + ) + time.sleep(5) self.have_all = [] self.dcnm_intf_get_have_all_with_sno(sno) else: # For merge state, the interfaces would have been created just now. Fetch them again before checking self.have_all = [] self.dcnm_intf_get_have_all_with_sno(sno) - if match_have == [] or match_have[0]["complianceStatus"] != "In-Sync": + if ( + match_have == [] + or match_have[0]["complianceStatus"] != "In-Sync" + ): self.module.fail_json( msg={ "FAILURE REASON": "Interafce " + name + " did not reach 'In-Sync' State", "Compliance Status": match_have[0]["complianceStatus"], + # "CHANGED": self.changed_dict, + # "RESP": resp + "RESULT": self.result, } ) @@ -3131,7 +3841,7 @@ def dcnm_intf_send_message_to_dcnm(self): if delem == []: continue - if (self.dcnm_version < 12): + if self.dcnm_version < 12: json_payload = json.dumps(delem) else: send_payload = copy.deepcopy(delem) @@ -3156,7 +3866,9 @@ def dcnm_intf_send_message_to_dcnm(self): resp["RETURN_CODE"] = 200 resp["MESSAGE"] = "OK" - if (resp.get("MESSAGE") != "OK") or (resp.get("RETURN_CODE") != 200): + if (resp.get("MESSAGE") != "OK") or ( + resp.get("RETURN_CODE") != 200 + ): # there may be cases which are not actual failures. retry the # action @@ -3173,6 +3885,7 @@ def dcnm_intf_send_message_to_dcnm(self): (resp.get("MESSAGE") != "OK") and ("No Commands to execute" not in resp.get("MESSAGE")) ) or (resp.get("RETURN_CODE") != 200): + resp["CHANGED"] = self.changed_dict self.module.fail_json(msg=resp) else: changed = True @@ -3195,7 +3908,7 @@ def dcnm_intf_send_message_to_dcnm(self): if index != self.int_index["INTERFACE_VPC"]: # Deploy just requires ifName and serialNumber - if (self.dcnm_version < 12): + if self.dcnm_version < 12: [ [ item.pop("interfaceType"), @@ -3205,12 +3918,7 @@ def dcnm_intf_send_message_to_dcnm(self): for item in delem ] else: - [ - [ - item.pop("interfaceDbId"), - ] - for item in delem - ] + [[item.pop("interfaceDbId")] for item in delem] else: [ [item.pop("interfaceType"), item.pop("interfaceDbId")] @@ -3240,7 +3948,10 @@ def dcnm_intf_send_message_to_dcnm(self): resp = dcnm_send(self.module, "PUT", path, json_payload) self.result["response"].append(resp) - if (resp.get("MESSAGE") != "OK") or (resp.get("RETURN_CODE") != 200): + if (resp.get("MESSAGE") != "OK") or ( + resp.get("RETURN_CODE") != 200 + ): + resp["CHANGED"] = self.changed_dict self.module.fail_json(msg=resp) else: replace = True @@ -3254,7 +3965,10 @@ def dcnm_intf_send_message_to_dcnm(self): resp = dcnm_send(self.module, "POST", path, json_payload) self.result["response"].append(resp) - if (resp.get("MESSAGE") != "OK") or (resp.get("RETURN_CODE") != 200): + if (resp.get("MESSAGE") != "OK") or ( + resp.get("RETURN_CODE") != 200 + ): + resp["CHANGED"] = self.changed_dict self.module.fail_json(msg=resp) else: create = True @@ -3268,7 +3982,9 @@ def dcnm_intf_send_message_to_dcnm(self): resp = dcnm_send(self.module, "POST", path, json_payload) - if (resp.get("MESSAGE") != "OK") and (resp.get("RETURN_CODE") != 200): + if (resp.get("MESSAGE") != "OK") and ( + resp.get("RETURN_CODE") != 200 + ): resp, rc = self.dcnm_parse_response(resp) changed = rc else: @@ -3278,6 +3994,13 @@ def dcnm_intf_send_message_to_dcnm(self): self.result["response"].append(resp) + # Continue further only if original deploy is success. Fail otherwise + if (resp.get("MESSAGE") != "OK") and ( + resp.get("RETURN_CODE") != 200 + ): + resp["CHANGED"] = self.changed_dict + self.module.fail_json(msg=resp) + resp = None if self.diff_deploy: @@ -3318,7 +4041,9 @@ def dcnm_translate_switch_info(self, config, ip_sn, hn_sn): if None is cfg.get("switch", None): continue for sw_elem in cfg["switch"]: - addr_info = dcnm_get_ip_addr_info(self.module, sw_elem, ip_sn, hn_sn) + addr_info = dcnm_get_ip_addr_info( + self.module, sw_elem, ip_sn, hn_sn + ) cfg["switch"][index] = addr_info index = index + 1 @@ -3345,7 +4070,9 @@ def main(): check_deploy=dict(type="bool", default=False), ) - module = AnsibleModule(argument_spec=element_spec, supports_check_mode=True) + module = AnsibleModule( + argument_spec=element_spec, supports_check_mode=True + ) dcnm_intf = DcnmIntf(module) @@ -3379,7 +4106,9 @@ def main(): dcnm_intf.dcnm_intf_validate_input() # state 'deleted' may not include all the information - if (module.params["state"] != "query") and (module.params["state"] != "deleted"): + if (module.params["state"] != "query") and ( + module.params["state"] != "deleted" + ): dcnm_intf.dcnm_intf_get_want() dcnm_intf.dcnm_intf_get_have() @@ -3412,6 +4141,7 @@ def main(): or dcnm_intf.diff_delete[dcnm_intf.int_index["INTERFACE_ETHERNET"]] or dcnm_intf.diff_delete[dcnm_intf.int_index["SUBINTERFACE"]] or dcnm_intf.diff_delete[dcnm_intf.int_index["INTERFACE_LOOPBACK"]] + or dcnm_intf.diff_delete[dcnm_intf.int_index["INTERFACE_VLAN"]] ): dcnm_intf.result["changed"] = True else: @@ -3422,6 +4152,7 @@ def main(): module.exit_json(**dcnm_intf.result) dcnm_intf.dcnm_intf_send_message_to_dcnm() + module.exit_json(**dcnm_intf.result) diff --git a/tests/integration/targets/dcnm_interface/meta/main.yaml b/tests/integration/targets/dcnm_interface/meta/main.yaml index 5514b6a40..a888edc3b 100644 --- a/tests/integration/targets/dcnm_interface/meta/main.yaml +++ b/tests/integration/targets/dcnm_interface/meta/main.yaml @@ -1 +1,2 @@ -dependencies: [] \ No newline at end of file +dependencies: + - prepare_dcnm_intf \ No newline at end of file diff --git a/tests/integration/targets/dcnm_interface/tasks/dcnm.yaml b/tests/integration/targets/dcnm_interface/tasks/dcnm.yaml index 881b81cb6..9b38cb1b1 100644 --- a/tests/integration/targets/dcnm_interface/tasks/dcnm.yaml +++ b/tests/integration/targets/dcnm_interface/tasks/dcnm.yaml @@ -18,3 +18,33 @@ with_items: "{{ test_items }}" loop_control: loop_var: test_case_to_run + +- name: Final Cleanup - delete feature_interface_vlan and hsrp policies that we created during init + cisco.dcnm.dcnm_policy: + fabric: "{{ ansible_it_fabric }}" + state: deleted # only choose form [merged, deleted, query] + config: + - name: my_interface_vlan + - name: my_hsrp + - switch: + - ip: "{{ ansible_switch1 }}" + - ip: "{{ ansible_switch2 }}" + register: result + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +- name: Final Cleanup - delete all templates created during init + cisco.dcnm.dcnm_template: + state: deleted # only choose form [merged, deleted, query] + config: + - name: my_interface_vlan + - name: my_hsrp + register: result + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' \ No newline at end of file diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_delete_diff_options.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_delete_diff_options.yaml index 25972a462..86d26a8d6 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_delete_diff_options.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_delete_diff_options.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -29,7 +29,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po333 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -37,7 +37,7 @@ mode: trunk # choose from [trunk, access, l3, monitor] - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -48,7 +48,7 @@ ipv4_mask_len: 24 # choose between [min:8, max:31] - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -57,7 +57,7 @@ ipv4_addr: 193.168.2.1 # ipv4 address for the loopback interface - name: vpc100 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -131,7 +131,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po333 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -139,7 +139,7 @@ mode: trunk # choose from [trunk, access, l3, monitor] - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -150,7 +150,7 @@ ipv4_mask_len: 24 # choose between [min:8, max:31] - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -159,7 +159,7 @@ ipv4_addr: 193.168.2.1 # ipv4 address for the loopback interface - name: vpc100 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -238,7 +238,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po333 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -246,7 +246,7 @@ mode: trunk # choose from [trunk, access, l3, monitor] - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -257,7 +257,7 @@ ipv4_mask_len: 24 # choose between [min:8, max:31] - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -266,7 +266,7 @@ ipv4_addr: 193.168.2.1 # ipv4 address for the loopback interface - name: vpc100 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_delete.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_delete.yaml index a85eef0f1..2a704a4b8 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_delete.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_delete.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf7 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -47,7 +47,7 @@ description: "eth interface acting as trunk" - name: "{{ ansible_eth_intf8 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -64,7 +64,7 @@ description: "eth interface acting as access" - name: "{{ ansible_eth_intf9 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -82,7 +82,7 @@ description: "eth interface acting as routed" - name: "{{ ansible_eth_intf10 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -102,7 +102,7 @@ description: "eth interface acting as epl_routed" - name: "{{ ansible_eth_intf11 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -135,7 +135,7 @@ state: deleted # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf7 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -152,7 +152,7 @@ description: "eth interface acting as trunk" - name: "{{ ansible_eth_intf8 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -169,7 +169,7 @@ description: "eth interface acting as access" - name: "{{ ansible_eth_intf9 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -187,7 +187,7 @@ description: "eth interface acting as routed" - name: "{{ ansible_eth_intf10 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -207,7 +207,7 @@ description: "eth interface acting as epl_routed" - name: "{{ ansible_eth_intf11 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -258,7 +258,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf7 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -300,7 +300,7 @@ state: deleted # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf7 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -349,4 +349,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_merge.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_merge.yaml index 0c5a6ca11..574ae5908 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_merge.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_merge.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf7 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -47,7 +47,7 @@ description: "eth interface acting as trunk" - name: "{{ ansible_eth_intf8 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -64,7 +64,7 @@ description: "eth interface acting as access" - name: "{{ ansible_eth_intf9 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -82,7 +82,7 @@ description: "eth interface acting as routed" - name: "{{ ansible_eth_intf10 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -102,7 +102,7 @@ description: "eth interface acting as epl_routed" - name: "{{ ansible_eth_intf11 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -160,4 +160,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_override.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_override.yaml index 53dd16c1e..e47f71c9f 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_override.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_override.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf7 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -47,7 +47,7 @@ description: "eth interface acting as trunk" - name: "{{ ansible_eth_intf8 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -64,7 +64,7 @@ description: "eth interface acting as access" - name: "{{ ansible_eth_intf9 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -82,7 +82,7 @@ description: "eth interface acting as routed" - name: "{{ ansible_eth_intf10 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -102,7 +102,7 @@ description: "eth interface acting as epl_routed" - name: "{{ ansible_eth_intf11 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -135,7 +135,7 @@ state: overridden # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf12 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -200,4 +200,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_replace.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_replace.yaml index c8d0faee2..cc9c28910 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_replace.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_eth_replace.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf7 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -47,7 +47,7 @@ description: "eth interface acting as trunk" - name: "{{ ansible_eth_intf8 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -64,7 +64,7 @@ description: "eth interface acting as access" - name: "{{ ansible_eth_intf9 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -82,7 +82,7 @@ description: "eth interface acting as routed" - name: "{{ ansible_eth_intf10 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -102,7 +102,7 @@ description: "eth interface acting as epl_routed" - name: "{{ ansible_eth_intf11 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -135,7 +135,7 @@ state: replaced # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf7 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -152,7 +152,7 @@ description: "eth interface acting as trunk - replace" - name: "{{ ansible_eth_intf8 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -169,7 +169,7 @@ description: "eth interface acting as access - replace" - name: "{{ ansible_eth_intf9 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -187,7 +187,7 @@ description: "eth interface acting as routed - replace" - name: "{{ ansible_eth_intf10 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -257,4 +257,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_multi_intf_merge.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_multi_intf_merge.yaml index d74a2397f..a133c1eba 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_multi_intf_merge.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_multi_intf_merge.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf7 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -47,7 +47,7 @@ description: "eth interface acting as trunk" - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -63,7 +63,7 @@ description: "loopback interface 100 configuration" - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -103,7 +103,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf7 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -120,7 +120,7 @@ description: "eth interface acting as trunk" - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -136,7 +136,7 @@ description: "loopback interface 100 configuration" - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -184,7 +184,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -203,7 +203,7 @@ description: "port channel acting as trunk" - name: po310 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -238,7 +238,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -269,7 +269,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -310,7 +310,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po310 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -345,7 +345,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po310 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -390,7 +390,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc750 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" deploy: true # choose from [true, false] @@ -437,7 +437,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc750 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" deploy: true # choose from [true, false] diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_multi_switches.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_multi_switches.yaml index 779260d62..18b8934d1 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_multi_switches.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_multi_switches.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po333 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed @@ -40,7 +40,7 @@ mode: trunk # choose from [trunk, access, l3, monitor] - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed - "{{ ansible_switch2 }}" # provide the switch information where the config is to be deployed @@ -53,7 +53,7 @@ ipv4_mask_len: 24 # choose between [min:8, max:31] - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config - "{{ ansible_switch1 }}" # provide the switch where to deploy the config @@ -112,4 +112,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_no_optional_elems.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_no_optional_elems.yaml index ab3aac284..27dda0df8 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_no_optional_elems.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_no_optional_elems.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -38,7 +38,7 @@ mode: trunk # choose from [trunk, access, l3, monitor] - name: po301 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -46,7 +46,7 @@ mode: access # choose from [trunk, access, l3, monitor] - name: po302 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -54,7 +54,7 @@ mode: l3 # choose from [trunk, access, l3, monitor] - name: po303 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -62,7 +62,7 @@ mode: monitor # choose from [trunk, access, l3, monitor] - name: "{{ ansible_eth_intf2 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -70,23 +70,15 @@ mode: trunk # choose from [trunk, access, routed, monitor, epl_routed] - name: "{{ ansible_eth_intf3 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] profile: mode: access # choose from [trunk, access, routed, monitor, epl_routed] - - name: "{{ ansible_eth_intf4 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] - switch: - - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed - deploy: true # choose from [true, false] - profile: - mode: routed # choose from [trunk, access, routed, monitor, epl_routed] - - name: "{{ ansible_eth_intf5 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -95,7 +87,7 @@ ipv4_addr: 192.168.1.1 # ipv4 address for the interface - name: "{{ ansible_eth_intf6 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -103,7 +95,7 @@ mode: monitor # choose from [trunk, access, routed, monitor, epl_routed] - name: "{{ ansible_sub_intf1 }}" # should be of the form eth - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -114,7 +106,7 @@ ipv4_mask_len: 24 # choose between [min:8, max:31] - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -148,7 +140,7 @@ state: deleted # only choose form [merged, replaced, deleted, overridden,query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -156,7 +148,7 @@ mode: trunk # choose from [trunk, access, l3, monitor] - name: po301 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -164,7 +156,7 @@ mode: access # choose from [trunk, access, l3, monitor] - name: po302 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -172,7 +164,7 @@ mode: l3 # choose from [trunk, access, l3, monitor] - name: po303 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -180,7 +172,7 @@ mode: monitor # choose from [trunk, access, l3, monitor] - name: "{{ ansible_eth_intf2 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -188,23 +180,15 @@ mode: trunk # choose from [trunk, access, routed, monitor, epl_routed] - name: "{{ ansible_eth_intf3 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] profile: mode: access # choose from [trunk, access, routed, monitor, epl_routed] - - name: "{{ ansible_eth_intf4 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] - switch: - - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed - deploy: true # choose from [true, false] - profile: - mode: routed # choose from [trunk, access, routed, monitor, epl_routed] - - name: "{{ ansible_eth_intf5 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -213,7 +197,7 @@ ipv4_addr: 192.168.1.1 # ipv4 address for the interface - name: "{{ ansible_eth_intf6 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -221,7 +205,7 @@ mode: monitor # choose from [trunk, access, routed, monitor, epl_routed] - name: "{{ ansible_sub_intf1 }}" # should be of the form eth - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -232,7 +216,7 @@ ipv4_mask_len: 24 # choose between [min:8, max:31] - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_query.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_query.yaml index 19222844e..d62d5da7d 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_query.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_intf_query.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -46,7 +46,7 @@ description: "loopback interface 100 configuration" - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -65,7 +65,7 @@ description: "sub interface eth1/1.1 configuration" - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -84,7 +84,7 @@ description: "port channel acting as trunk" - name: vpc750 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -216,4 +216,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_delete.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_delete.yaml index 8b2e8e7a5..87cbd0353 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_delete.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_delete.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -46,7 +46,7 @@ description: "loopback interface 100 configuration" - name: lo101 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -87,7 +87,7 @@ state: deleted # only choose form [merged, replaced, deleted, overridden, query] config: - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -103,7 +103,7 @@ description: "loopback interface 100 configuration" - name: lo101 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -169,4 +169,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_merge.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_merge.yaml index 047c0a932..205de256a 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_merge.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_merge.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -46,7 +46,7 @@ description: "loopback interface 100 configuration" - name: lo101 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -112,4 +112,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_override.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_override.yaml index 16b16f1f3..81ae96a96 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_override.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_override.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -46,7 +46,7 @@ description: "loopback interface 100 configuration" - name: lo101 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -87,7 +87,7 @@ state: overridden # only choose form [merged, replaced, deleted, overridden, query] config: - name: lo103 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -103,7 +103,7 @@ description: "loopback interface 103 configuration - overridden" - name: lo101 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -169,4 +169,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_replace.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_replace.yaml index e9341a1e7..f2f6f3c3e 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_replace.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_lo_replace.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -46,7 +46,7 @@ description: "loopback interface 100 configuration" - name: lo101 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -87,7 +87,7 @@ state: replaced # only choose form [merged, replaced, deleted, overridden, query] config: - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true ## choose from [true, false] @@ -103,7 +103,7 @@ description: "loopback interface 100 configuration - replaced" - name: lo101 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -169,4 +169,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_old_format_pb.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_old_format_pb.yaml index 942f88e0f..05cb3046d 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_old_format_pb.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_old_format_pb.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -50,7 +50,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po301 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -70,7 +70,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_eth_intf2 }}" # should be of the form eth - type: eth # choose from this list [pc, vpc, sub_int, lo, eth] + type: eth # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -90,7 +90,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_sub_intf1 }}" # should be of the form eth - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -113,7 +113,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: lo100 # should be of the form lo - type: lo # choose from this list [pc, vpc, sub_int, lo, eth] + type: lo # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch where to deploy the config deploy: true # choose from [true, false] @@ -134,7 +134,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc750 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -155,7 +155,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc751 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - ["{{ ansible_switch1 }}", "{{ ansible_switch2 }}"] @@ -167,4 +167,4 @@ - assert: that: - 'result.failed == false' - - 'result.changed == true' \ No newline at end of file + - 'result.changed == true' diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_delete.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_delete.yaml index dbdc33ac5..235d78eeb 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_delete.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_delete.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -49,7 +49,7 @@ description: "port channel acting as trunk" - name: po301 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -68,7 +68,7 @@ description: "port channel acting as access" - name: po302 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -88,7 +88,7 @@ description: "port channel acting as l3" - name: po303 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -121,7 +121,7 @@ state: deleted # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -140,7 +140,7 @@ description: "port channel acting as trunk" - name: po301 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -159,7 +159,7 @@ description: "port channel acting as access" - name: po302 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -179,7 +179,7 @@ description: "port channel acting as l3" - name: po303 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -238,4 +238,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_merge.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_merge.yaml index 509113686..99f29ff07 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_merge.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_merge.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -49,7 +49,7 @@ description: "port channel acting as trunk" - name: po301 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -68,7 +68,7 @@ description: "port channel acting as access" - name: po302 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -88,7 +88,7 @@ description: "port channel acting as l3" - name: po303 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -146,4 +146,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_override.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_override.yaml index 0f25433e8..a45723575 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_override.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_override.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -49,7 +49,7 @@ description: "port channel acting as trunk" - name: po301 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -68,7 +68,7 @@ description: "port channel acting as access" - name: po302 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -88,7 +88,7 @@ description: "port channel acting as l3" - name: po303 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -121,7 +121,7 @@ state: overridden # only choose form [merged, replaced, deleted, overridden, query] config: - name: po320 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -189,4 +189,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_replace.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_replace.yaml index f404c5c14..d03444212 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_replace.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_pc_replace.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -49,7 +49,7 @@ description: "port channel acting as trunk" - name: po301 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -68,7 +68,7 @@ description: "port channel acting as access" - name: po302 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -88,7 +88,7 @@ description: "port channel acting as l3" - name: po303 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -121,7 +121,7 @@ state: replaced # only choose form [merged, replaced, deleted, overridden, query] config: - name: po300 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -140,7 +140,7 @@ description: "port channel acting as trunk - replace" - name: po301 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -159,7 +159,7 @@ description: "port channel acting as access - replace" - name: po302 # should be of the form po - type: pc # choose from this list [pc, vpc, sub_int, lo, eth] + type: pc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -229,4 +229,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_delete.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_delete.yaml index ab209c044..4f539823b 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_delete.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_delete.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -49,7 +49,7 @@ description: "sub interface eth1/1.1 configuration" - name: "{{ ansible_sub_intf2 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -93,7 +93,7 @@ state: deleted # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -112,7 +112,7 @@ description: "sub interface eth1/1.1 configuration" - name: "{{ ansible_sub_intf2 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -182,4 +182,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_merge.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_merge.yaml index 88b24f5b1..39976360f 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_merge.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_merge.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -49,7 +49,7 @@ description: "sub interface eth1/1.1 configuration" - name: "{{ ansible_sub_intf2 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -118,4 +118,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_override.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_override.yaml index ed33dffa5..2fca0607d 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_override.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_override.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -49,7 +49,7 @@ description: "sub interface eth1/1.1 configuration" - name: "{{ ansible_sub_intf2 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -93,7 +93,7 @@ state: overridden # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_sub_intf3 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -162,4 +162,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_replace.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_replace.yaml index b19ee84df..b3e87afa9 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_replace.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_sub_replace.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -49,7 +49,7 @@ description: "sub interface eth1/1.1 configuration" - name: "{{ ansible_sub_intf2 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -93,7 +93,7 @@ state: replaced # only choose form [merged, replaced, deleted, overridden, query] config: - name: "{{ ansible_sub_intf1 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -112,7 +112,7 @@ description: "sub interface eth1/1.1 configuration - replace" - name: "{{ ansible_sub_intf2 }}" # should be of the form eth. - type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth] + type: sub_int # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed deploy: true # choose from [true, false] @@ -181,4 +181,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_delete.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_delete.yaml new file mode 100644 index 000000000..0447f27c7 --- /dev/null +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_delete.yaml @@ -0,0 +1,154 @@ +############################################## +## SETUP ## +############################################## + +- name: Remove local log file + local_action: command rm -f dcnm_intf.log + +- name: Put the fabric to default state + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + register: result + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +- block: + +############################################## +## MERGE ## +############################################## + + - name: Create SVI interfaces w/o optional parameters + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + int_vrf: blue # optional, Interface VRF name, default is "default" + ipv4_addr: 192.168.2.1 # optional, Interfae IP, default is "" + ipv4_mask_len: 24 # optional, IP mask length, default is "" + mtu: 9216 # optional, MTU default is "" + route_tag: 1001 # optional, Routing TAG, default is "" + disable_ip_redirects: true # optional, flag to enable/disable IP redirects, default is "false" + cmds: # Freeform config + - no shutdown + admin_state: true # Flag to enable/disable Vlan interaface + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.168.2.100 # optional, Virtual IP address for HSRP, default is "" + hsrp_group: 10 # optional, HSRP group, default is "" + hsrp_priority: 5 # optional, HSRP priority, default is "" + hsrp_vmac: 0000.0101.ac0a # optional, HSRP virtual MAC, default is "" + dhcp_server_addr1: 192.200.1.1 # optional, DHCP relay server address, default is "" + vrf_dhcp1: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr2: 192.200.1.2 # optional, DHCP relay server address, default is "" + vrf_dhcp2: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr3: 192.200.1.3 # optional, DHCP relay server address, default is "" + vrf_dhcp3: blue # optional, VRF to reach DHCP server. default is "" + adv_subnet_in_underlay: true # optional, flag to enable/disable advertisements of subnets into underlay, default is "false" + enable_netflow: false # optional, flag to enable netflow, default is "false" + netflow_monitor: svi1001 # optional, name of netflow monitor, default is "" + hsrp_version: 1 # optional, HSRP protocol version, default is 1 + preempt: true # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + description: Switched vlan interface 1001 # optional, Interface description, default is "" + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 2' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 2' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +############################################## +## DELETEE ## +############################################## + + - name: Delete SVI interfaces + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: deleted # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch where to deploy the config + + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch where to deploy the config + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 0' + - '(result["diff"][0]["deleted"] | length) == 2' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 0' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + + - name: Delete SVI interfaces - Idempotence + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: deleted # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch where to deploy the config + + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch where to deploy the config + register: result + + - assert: + that: + - 'result.changed == false' + - '(result["diff"][0]["merged"] | length) == 0' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 0' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_invalid_params.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_invalid_params.yaml new file mode 100644 index 000000000..3fe93bab3 --- /dev/null +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_invalid_params.yaml @@ -0,0 +1,241 @@ +############################################## +## SETUP ## +############################################## + +- name: Remove local log file + local_action: command rm -f dcnm_intf.log + +- block: + +############################################## +## MERGE ## +############################################## + + - name: Create SVI interfaces w/o admin state + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan3000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + ignore_errors: yes + register: result + + - assert: + that: + - '("Invalid parameters" in result["msg"])' + - '("admin_state" in result["msg"])' + + + - name: Create SVI interfaces w/o mode + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan3000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + #mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + ignore_errors: yes + register: result + + - assert: + that: + - '("Invalid parameters" in result["msg"])' + - '("mode" in result["msg"])' + + - name: Create SVI interfaces with ipv4_addr but w/o mask length + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan3000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + ipv4_addr: 10.1.1.1 # optional, Interfae IP, default is "" + ignore_errors: yes + register: result + + - assert: + that: + - '("Invalid parameters" in result["msg"])' + - '("ipv4_mask_len" in result["msg"])' + + - name: Create SVI interfaces with HSRP not enabled, but preempt included + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan3000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + preempt: true # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + ignore_errors: yes + register: result + + - assert: + that: + - '("Invalid parameters" in result["msg"])' + - '("preempt" in result["msg"])' + + - name: Create SVI interfaces with HSRP enabled but w/o HSRP VIP + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan3000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + ignore_errors: yes + register: result + + - assert: + that: + - '("Invalid parameters" in result["msg"])' + - '("hsrp_vip" in result["msg"])' + + - name: Create SVI interfaces with HSRP enabled but w/o HSRP Group + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan3000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.129.1.1 # optional, Virtual IP address for HSRP, default is "" + ignore_errors: yes + register: result + + - assert: + that: + - '("Invalid parameters" in result["msg"])' + - '("hsrp_group" in result["msg"])' + + - name: Create SVI interfaces with NETFLOW enabled but w/o NETFLOW Monitor + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan3000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + enable_netflow: true # optional, flag to enable netflow, default is "false" + ignore_errors: yes + register: result + + - assert: + that: + - '("Invalid parameters" in result["msg"])' + - '("netflow_monitor" in result["msg"])' + + - name: Create SVI interfaces with DHCP1 Server Address but w/o VRF DHCP1 + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan3000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + dhcp_server_addr1: 192.200.1.1 + ignore_errors: yes + register: result + + - assert: + that: + - '("Invalid parameters" in result["msg"])' + - '("vrf_dhcp1" in result["msg"])' + + - name: Create SVI interfaces with DHCP2 Server Address but w/o VRF DHCP2 + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan3000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + dhcp_server_addr2: 192.200.1.1 + ignore_errors: yes + register: result + + - assert: + that: + - '("Invalid parameters" in result["msg"])' + - '("vrf_dhcp2" in result["msg"])' + + - name: Create SVI interfaces with DHCP3 Server Address but w/o VRF DHCP3 + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan3000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + dhcp_server_addr3: 192.200.1.1 + ignore_errors: yes + register: result + + - assert: + that: + - '("Invalid parameters" in result["msg"])' + - '("vrf_dhcp3" in result["msg"])' diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_merge.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_merge.yaml new file mode 100644 index 000000000..c0687b332 --- /dev/null +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_merge.yaml @@ -0,0 +1,263 @@ +############################################## +## SETUP ## +############################################## + +- name: Remove local log file + local_action: command rm -f dcnm_intf.log + +- name: Put the fabric to default state + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + register: result + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +- block: + +############################################## +## MERGE ## +############################################## + + - name: Create SVI interfaces w/o optional parameters + cisco.dcnm.dcnm_interface: &svi_merge + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 1' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 1' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + + - name: Create SVI interfaces - Idempotence + cisco.dcnm.dcnm_interface: *svi_merge + register: result + + - assert: + that: + - 'result.changed == false' + - '(result["diff"][0]["merged"] | length) == 0' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 0' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +############################################## +## MERGE ## +############################################## + + - name: Create SVI interfaces including optional parameters + cisco.dcnm.dcnm_interface: &svi_merge2 + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + int_vrf: blue # optional, Interface VRF name, default is "default" + ipv4_addr: 192.168.2.1 # optional, Interfae IP, default is "" + ipv4_mask_len: 24 # optional, IP mask length, default is "" + mtu: 9216 # optional, MTU default is "" + route_tag: 1001 # optional, Routing TAG, default is "" + disable_ip_redirects: true # optional, flag to enable/disable IP redirects, default is "false" + cmds: # Freeform config + - no shutdown + admin_state: true # Flag to enable/disable Vlan interaface + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.168.2.100 # optional, Virtual IP address for HSRP, default is "" + hsrp_group: 10 # optional, HSRP group, default is "" + hsrp_priority: 5 # optional, HSRP priority, default is "" + hsrp_vmac: 0000.0101.ac0a # optional, HSRP virtual MAC, default is "" + dhcp_server_addr1: 192.200.1.1 # optional, DHCP relay server address, default is "" + vrf_dhcp1: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr2: 192.200.1.2 # optional, DHCP relay server address, default is "" + vrf_dhcp2: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr3: 192.200.1.3 # optional, DHCP relay server address, default is "" + vrf_dhcp3: blue # optional, VRF to reach DHCP server. default is "" + adv_subnet_in_underlay: true # optional, flag to enable/disable advertisements of subnets into underlay, default is "false" + enable_netflow: false # optional, flag to enable netflow, default is "false" + netflow_monitor: svi1001 # optional, name of netflow monitor, default is "" + hsrp_version: 1 # optional, HSRP protocol version, default is 1 + preempt: true # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + description: Switched vlan interface 1001 # optional, Interface description, default is "" + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 1' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 1' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + + - name: Create SVI interfaces - Idempotence + cisco.dcnm.dcnm_interface: *svi_merge2 + register: result + + - assert: + that: + - 'result.changed == false' + - '(result["diff"][0]["merged"] | length) == 0' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 0' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +############################################## +## MERGE ## +############################################## + + - name: Merge SVI interface + cisco.dcnm.dcnm_interface: + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + int_vrf: green # optional, Interface VRF name, default is "default" + ipv4_addr: 192.169.2.1 # optional, Interfae IP, default is "" + ipv4_mask_len: 24 # optional, IP mask length, default is "" + mtu: 9216 # optional, MTU default is "" + route_tag: 1001 # optional, Routing TAG, default is "" + disable_ip_redirects: true # optional, flag to enable/disable IP redirects, default is "false" + cmds: # Freeform config + - no shutdown + admin_state: true # Flag to enable/disable Vlan interaface + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.169.2.100 # optional, Virtual IP address for HSRP, default is "" + hsrp_group: 10 # optional, HSRP group, default is "" + hsrp_priority: 5 # optional, HSRP priority, default is "" + hsrp_vmac: 0000.0103.ac0a # optional, HSRP virtual MAC, default is "" + dhcp_server_addr1: 192.210.1.1 # optional, DHCP relay server address, default is "" + vrf_dhcp1: green # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr2: 192.210.1.2 # optional, DHCP relay server address, default is "" + vrf_dhcp2: green # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr3: 192.210.1.3 # optional, DHCP relay server address, default is "" + vrf_dhcp3: green # optional, VRF to reach DHCP server. default is "" + adv_subnet_in_underlay: true # optional, flag to enable/disable advertisements of subnets into underlay, default is "false" + enable_netflow: false # optional, flag to enable netflow, default is "false" + netflow_monitor: svi1001 # optional, name of netflow monitor, default is "" + hsrp_version: 1 # optional, HSRP protocol version, default is 1 + preempt: true # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + description: Switched vlan interface 1001 # optional, Interface description, default is "" + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 1' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 1' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +############################################## +## MERGE ## +############################################## + + - name: Create SVI interfaces - multiple switches + cisco.dcnm.dcnm_interface: + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1010 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + - "{{ ansible_switch2 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 2' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 2' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +############################################## +## CLEANUP ## +############################################## + + always: + + - name: Put fabric to default state + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + register: result + when: IT_CONTEXT is not defined + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_override.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_override.yaml new file mode 100644 index 000000000..cdf4b8f9b --- /dev/null +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_override.yaml @@ -0,0 +1,151 @@ +############################################## +## SETUP ## +############################################## + +- name: Remove local log file + local_action: command rm -f dcnm_intf.log + +- name: Put the fabric to default state + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + register: result + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +- block: + +############################################## +## MERGE ## +############################################## + + - name: Create SVI interfaces including optional parameters + cisco.dcnm.dcnm_interface: + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + int_vrf: blue # optional, Interface VRF name, default is "default" + ipv4_addr: 192.168.2.1 # optional, Interfae IP, default is "" + ipv4_mask_len: 24 # optional, IP mask length, default is "" + mtu: 9216 # optional, MTU default is "" + route_tag: 1001 # optional, Routing TAG, default is "" + disable_ip_redirects: true # optional, flag to enable/disable IP redirects, default is "false" + cmds: # Freeform config + - no shutdown + admin_state: true # Flag to enable/disable Vlan interaface + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.168.2.100 # optional, Virtual IP address for HSRP, default is "" + hsrp_group: 10 # optional, HSRP group, default is "" + hsrp_priority: 5 # optional, HSRP priority, default is "" + hsrp_vmac: 0000.0101.ac0a # optional, HSRP virtual MAC, default is "" + dhcp_server_addr1: 192.200.1.1 # optional, DHCP relay server address, default is "" + vrf_dhcp1: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr2: 192.200.1.2 # optional, DHCP relay server address, default is "" + vrf_dhcp2: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr3: 192.200.1.3 # optional, DHCP relay server address, default is "" + vrf_dhcp3: blue # optional, VRF to reach DHCP server. default is "" + adv_subnet_in_underlay: true # optional, flag to enable/disable advertisements of subnets into underlay, default is "false" + enable_netflow: false # optional, flag to enable netflow, default is "false" + netflow_monitor: svi1001 # optional, name of netflow monitor, default is "" + hsrp_version: 1 # optional, HSRP protocol version, default is 1 + preempt: true # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + description: Switched vlan interface 1001 # optional, Interface description, default is "" + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 1' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 1' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +############################################## +## OVERRIDDEN ## +############################################## + + - name: Override SVI interface + cisco.dcnm.dcnm_interface: &svi_override + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1002 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 0' + - '(result["diff"][0]["deleted"] | length) == 1' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 1' + - '(result["diff"][0]["deploy"] | length) == 1' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + + - name: Override SVI interfaces - Idempotence + cisco.dcnm.dcnm_interface: *svi_override + register: result + + - assert: + that: + - 'result.changed == false' + - '(result["diff"][0]["merged"] | length) == 0' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 0' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +############################################## +## CLEANUP ## +############################################## + + always: + + - name: Put fabric to default state + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + register: result + when: IT_CONTEXT is not defined + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_query.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_query.yaml new file mode 100644 index 000000000..0d790f956 --- /dev/null +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_query.yaml @@ -0,0 +1,122 @@ +############################################## +## SETUP ## +############################################## + +- name: Remove local log file + local_action: command rm -f dcnm_intf.log + +- name: Put the fabric to default state + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + register: result + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +- block: + +############################################## +## MERGE ## +############################################## + + - name: Create SVI interfaces w/o optional parameters + cisco.dcnm.dcnm_interface: &svi_merge + check_deploy: false + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1000 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + admin_state: true # Flag to enable/disable Vlan interaface + mode: vlan # choose from [int_vlan, int_vlan_admin_state], default is "int_vlan" + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 2' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 2' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +############################################## +## QUERY ## +############################################## + + - name: Query interface details - Existing + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: query # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1000 + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + - name: vlan1001 + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + register: result + + - assert: + that: + - 'result.changed == false' + - '(result["diff"][0]["merged"] | length) == 0' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 0' + - '(result["diff"][0]["query"] | length) == 2' + + - assert: + that: + - '(result["response"] | length) != 0' + + - name: Query interface details - Non-Existing + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: query # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1009 + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + - name: vlan1008 + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + register: result + + - assert: + that: + - 'result.changed == false' + - '(result["diff"][0]["merged"] | length) == 0' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 0' + - '(result["diff"][0]["query"] | length) == 0' + + - assert: + that: + - '(result["response"] | length) == 0' diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_replace.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_replace.yaml new file mode 100644 index 000000000..74e6e3dd4 --- /dev/null +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_svi_replace.yaml @@ -0,0 +1,176 @@ +############################################## +## SETUP ## +############################################## + +- name: Remove local log file + local_action: command rm -f dcnm_intf.log + +- name: Put the fabric to default state + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + register: result + +- assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +- block: + +############################################## +## MERGE ## +############################################## + + - name: Create SVI interfaces including optional parameters + cisco.dcnm.dcnm_interface: + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: merged # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + int_vrf: blue # optional, Interface VRF name, default is "default" + ipv4_addr: 192.168.2.1 # optional, Interfae IP, default is "" + ipv4_mask_len: 24 # optional, IP mask length, default is "" + mtu: 9216 # optional, MTU default is "" + route_tag: 1001 # optional, Routing TAG, default is "" + disable_ip_redirects: true # optional, flag to enable/disable IP redirects, default is "false" + cmds: # Freeform config + - no shutdown + admin_state: true # Flag to enable/disable Vlan interaface + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.168.2.100 # optional, Virtual IP address for HSRP, default is "" + hsrp_group: 10 # optional, HSRP group, default is "" + hsrp_priority: 5 # optional, HSRP priority, default is "" + hsrp_vmac: 0000.0101.ac0a # optional, HSRP virtual MAC, default is "" + dhcp_server_addr1: 192.200.1.1 # optional, DHCP relay server address, default is "" + vrf_dhcp1: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr2: 192.200.1.2 # optional, DHCP relay server address, default is "" + vrf_dhcp2: blue # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr3: 192.200.1.3 # optional, DHCP relay server address, default is "" + vrf_dhcp3: blue # optional, VRF to reach DHCP server. default is "" + adv_subnet_in_underlay: true # optional, flag to enable/disable advertisements of subnets into underlay, default is "false" + enable_netflow: false # optional, flag to enable netflow, default is "false" + netflow_monitor: svi1001 # optional, name of netflow monitor, default is "" + hsrp_version: 1 # optional, HSRP protocol version, default is 1 + preempt: true # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + description: Switched vlan interface 1001 # optional, Interface description, default is "" + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 1' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 1' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +############################################## +## REPLACE ## +############################################## + + - name: Replace SVI interface + cisco.dcnm.dcnm_interface: &svi_replace + check_deploy: true + fabric: "{{ ansible_svi_fabric }}" + state: replaced # only choose form [merged, replaced, deleted, overridden, query] + config: + - name: vlan1001 # should be of the form vlan + type: svi # choose from this list [pc, vpc, sub_int, lo, eth, svi] + switch: + - "{{ ansible_switch1 }}" # provide the switch information where the config is to be deployed + deploy: true # choose from [true, false] + profile: + int_vrf: red # optional, Interface VRF name, default is "default" + ipv4_addr: 192.169.2.1 # optional, Interfae IP, default is "" + ipv4_mask_len: 20 # optional, IP mask length, default is "" + mtu: 9210 # optional, MTU default is "" + route_tag: 1002 # optional, Routing TAG, default is "" + disable_ip_redirects: false # optional, flag to enable/disable IP redirects, default is "false" + cmds: # Freeform config + - no shutdown + admin_state: false # Flag to enable/disable Vlan interaface + enable_hsrp: true # optional, flag to enable/disable HSRP on the interface, default is "false" + hsrp_vip: 192.169.2.100 # optional, Virtual IP address for HSRP, default is "" + hsrp_group: 11 # optional, HSRP group, default is "" + hsrp_priority: 5 # optional, HSRP priority, default is "" + hsrp_vmac: 0000.0102.ac0a # optional, HSRP virtual MAC, default is "" + dhcp_server_addr1: 193.200.1.1 # optional, DHCP relay server address, default is "" + vrf_dhcp1: green # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr2: 193.200.1.2 # optional, DHCP relay server address, default is "" + vrf_dhcp2: green # optional, VRF to reach DHCP server. default is "" + dhcp_server_addr3: 193.200.1.3 # optional, DHCP relay server address, default is "" + vrf_dhcp3: green # optional, VRF to reach DHCP server. default is "" + adv_subnet_in_underlay: false # optional, flag to enable/disable advertisements of subnets into underlay, default is "false" + enable_netflow: false # optional, flag to enable netflow, default is "false" + netflow_monitor: svi1002 # optional, name of netflow monitor, default is "" + hsrp_version: 2 # optional, HSRP protocol version, default is 1 + preempt: false # optional, flag to enable/disable overthrow of low priority active routers, optional is "false" + mode: vlan # choose from [vlan, vlan_admin_state], default is "vlan" + description: Switched vlan interface 1001 - Rep # optional, Interface description, default is "" + register: result + + - assert: + that: + - 'result.changed == true' + - '(result["diff"][0]["merged"] | length) == 0' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 1' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 1' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + + - name: Replace SVI interfaces - Idempotence + cisco.dcnm.dcnm_interface: *svi_replace + register: result + + - assert: + that: + - 'result.changed == false' + - '(result["diff"][0]["merged"] | length) == 0' + - '(result["diff"][0]["deleted"] | length) == 0' + - '(result["diff"][0]["replaced"] | length) == 0' + - '(result["diff"][0]["overridden"] | length) == 0' + - '(result["diff"][0]["deploy"] | length) == 0' + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + +############################################## +## CLEANUP ## +############################################## + + always: + + - name: Put fabric to default state + cisco.dcnm.dcnm_interface: + check_deploy: True + fabric: "{{ ansible_svi_fabric }}" + state: overridden # only choose form [merged, replaced, deleted, overridden, query] + register: result + when: IT_CONTEXT is not defined + + - assert: + that: + - 'item["RETURN_CODE"] == 200' + loop: '{{ result.response }}' + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_delete.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_delete.yaml index 2d08f26ce..79d2ec8cb 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_delete.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_delete.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc750 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -54,7 +54,7 @@ peer2_description: "VPC acting as trunk peer2" - name: vpc751 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -103,7 +103,7 @@ state: deleted # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc750 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -127,7 +127,7 @@ peer2_description: "VPC acting as trunk peer2" - name: vpc751 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -201,4 +201,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_merge.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_merge.yaml index c482c4eba..12007dfb0 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_merge.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_merge.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc750 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -54,7 +54,7 @@ peer2_description: "VPC acting as trunk peer2" - name: vpc751 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -127,4 +127,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_override.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_override.yaml index d86e70862..9ca24d084 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_override.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_override.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc750 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -54,7 +54,7 @@ peer2_description: "VPC acting as trunk peer2" - name: vpc751 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -103,7 +103,7 @@ state: overridden # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc752 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -181,4 +181,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_replace.yaml b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_replace.yaml index f9880cd2a..e86dee6ee 100644 --- a/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_replace.yaml +++ b/tests/integration/targets/dcnm_interface/tests/dcnm/dcnm_vpc_replace.yaml @@ -3,7 +3,7 @@ ############################################## - name: Remove local log file - local_action: command rm -f interface.log + local_action: command rm -f dcnm_intf.log - name: Put the fabric to default state cisco.dcnm.dcnm_interface: @@ -30,7 +30,7 @@ state: merged # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc750 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -54,7 +54,7 @@ peer2_description: "VPC acting as trunk peer2" - name: vpc751 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -103,7 +103,7 @@ state: replaced # only choose form [merged, replaced, deleted, overridden, query] config: - name: vpc750 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -132,7 +132,7 @@ - name: vpc751 # should be of the form vpc - type: vpc # choose from this list [pc, vpc, sub_int, lo, eth] + type: vpc # choose from this list [pc, vpc, sub_int, lo, eth, svi] switch: # provide switches of vPC pair - "{{ ansible_switch1 }}" - "{{ ansible_switch2 }}" @@ -209,4 +209,4 @@ that: - 'item["RETURN_CODE"] == 200' loop: '{{ result.response }}' - when: IT_CONTEXT is not defined \ No newline at end of file + when: IT_CONTEXT is not defined diff --git a/tests/integration/targets/prepare_dcnm_intf/tasks/main.yaml b/tests/integration/targets/prepare_dcnm_intf/tasks/main.yaml new file mode 100644 index 000000000..ee4e24460 --- /dev/null +++ b/tests/integration/targets/prepare_dcnm_intf/tasks/main.yaml @@ -0,0 +1,46 @@ + # SVI interfaces require interface-vlan and hsrp features to be enabled + + - name: Create templates for interface-vlan and hsrp features + cisco.dcnm.dcnm_template: + state: merged # only choose form [merged, deleted, query] + config: + - name: my_interface_vlan + tags: "interface_vlan" + description: "internal template for enabling interface-vlan feature" + content: | + ## + ##template content + + feature interface-vlan + + ## + - name: my_hsrp + tags: "hsrp" + description: "internal template for enabling hsrp feature" + content: | + ## + ##template content + + feature hsrp + + ## + register: result + +# Create the policy to deploy interface-vlan and hsrp features on the switches + - name: Create interface-vlan and hsrp policies + cisco.dcnm.dcnm_policy: + fabric: "{{ ansible_it_fabric }}" + config: + - name: my_interface_vlan # This must be a valid template name + create_additional_policy: false # Do not create a policy if it already exists + priority: 101 + - name: my_hsrp # This must be a valid template name + create_additional_policy: false # Do not create a policy if it already exists + priority: 101 + + - switch: + - ip: "{{ ansible_switch1 }}" + - ip: "{{ ansible_switch2 }}" + deploy: true + state: merged + register: result \ No newline at end of file diff --git a/tests/unit/modules/dcnm/fixtures/dcnm_intf_have_all_payloads.json b/tests/unit/modules/dcnm/fixtures/dcnm_intf_have_all_payloads.json index 4d4ca1f38..a6eb86147 100644 --- a/tests/unit/modules/dcnm/fixtures/dcnm_intf_have_all_payloads.json +++ b/tests/unit/modules/dcnm/fixtures/dcnm_intf_have_all_payloads.json @@ -871,6 +871,29 @@ "MESSAGE": "OK", "REQUEST_PATH": "https://10.122.197.6:443/rest/interface?serialNumber=SAL1819SAN8", "DATA": [ + { + "ifName": "vlan2001", + "mode": "vlan", + "serialNo": "SAL1819SAN8", + "fabricName": "test_fabric", + "ifType": "INTERFACE_VLAN", + "isPhysical": "False", + "deletable": "True", + "markDeleted": "False", + "alias": "", + "complianceStatus": "In-Sync", + "underlayPolicies": [ + { + "source" : "" + } + ], + "interfaces": [ + { + "nvPairs": { + } + } + ] + }, { "ifName": "port-channel1000", "mode": "access", @@ -1128,4 +1151,5 @@ "RETURN_CODE": 200, "METHOD": "GET" } + } diff --git a/tests/unit/modules/dcnm/fixtures/dcnm_intf_svi_configs.json b/tests/unit/modules/dcnm/fixtures/dcnm_intf_svi_configs.json new file mode 100644 index 000000000..33d92d86d --- /dev/null +++ b/tests/unit/modules/dcnm/fixtures/dcnm_intf_svi_configs.json @@ -0,0 +1,235 @@ +{ + "mock_fab_inv_data": { + "192.168.1.108": { + "isVpcConfigured": "True", + "vpcDomain": 1 + }, + "192.168.1.109": { + "isVpcConfigured": "True", + "vpcDomain": 1 + } + }, + + "mock_ip_sn" : { + "192.168.1.109": "FOX1821H035", + "192.168.1.108": "SAL1819SAN8" + }, + + "mock_vpc_sno" : { + "192.168.1.108" : "FOX1821H035~SAL1819SAN8", + "192.168.1.109" : "FOX1821H035~SAL1819SAN8" + }, + + "mock_vpc_resp" : { + "MESSAGE": "OK", + "REQUEST_PATH": "https://10.122.197.6:443/rest/interface/vpcpair_serial_number?serial_number=FOX1821H035", + "DATA": { + "vpc_pair_sn": "FOX1821H035~SAL1819SAN8" + }, + "RETURN_CODE": 200, + "METHOD": "GET" + }, + + "mock_succ_resp" : { + "DATA": {}, + "MESSAGE": "OK", + "METHOD": "POST", + "REQUEST_PATH": "https://10.122.197.6:443/rest/globalInterface", + "RETURN_CODE": 200 + }, + + "mock_deploy_resp" : { + "DATA": {}, + "MESSAGE": "OK", + "METHOD": "POST", + "REQUEST_PATH": "https://10.122.197.6:443/rest/globalInterface/deploy", + "RETURN_CODE": 200 + }, + + "svi_state_missing_config" : [ + { + "name": "vlan1001", + "profile": { + "admin_state": true, + "adv_subnet_in_underlay": true, + "cmds": [ + "no shutdown" + ], + "description": "Switched vlan interface 1001", + "dhcp_server_addr1": "192.200.1.1", + "dhcp_server_addr2": "192.200.1.2", + "dhcp_server_addr3": "192.200.1.3", + "disable_ip_redirects": true, + "enable_hsrp": true, + "enable_netflow": false, + "hsrp_group": 10, + "hsrp_priority": 5, + "hsrp_version": 1, + "hsrp_vip": "192.168.2.100", + "hsrp_vmac": "0000.0101.ac0a", + "int_vrf": "blue", + "ipv4_addr": "192.168.2.1", + "ipv4_mask_len": 24, + "mode": "vlan", + "mtu": 9216, + "netflow_monitor": "svi1001", + "preempt": true, + "route_tag": 1001, + "vrf_dhcp1": "blue", + "vrf_dhcp2": "blue", + "vrf_dhcp3": "blue" + }, + "switch": [ + "192.168.1.108" + ], + "type": "svi" + }], + + "svi_type_missing_config" : [ + { + "name": "vlan1001", + "profile": { + "admin_state": true, + "adv_subnet_in_underlay": true, + "cmds": [ + "no shutdown" + ], + "description": "Switched vlan interface 1001", + "dhcp_server_addr1": "192.200.1.1", + "dhcp_server_addr2": "192.200.1.2", + "dhcp_server_addr3": "192.200.1.3", + "disable_ip_redirects": true, + "enable_hsrp": true, + "enable_netflow": false, + "hsrp_group": 10, + "hsrp_priority": 5, + "hsrp_version": 1, + "hsrp_vip": "192.168.2.100", + "hsrp_vmac": "0000.0101.ac0a", + "int_vrf": "blue", + "ipv4_addr": "192.168.2.1", + "ipv4_mask_len": 24, + "mode": "vlan", + "mtu": 9216, + "netflow_monitor": "svi1001", + "preempt": true, + "route_tag": 1001, + "vrf_dhcp1": "blue", + "vrf_dhcp2": "blue", + "vrf_dhcp3": "blue" + }, + "switch": [ + "192.168.1.108" + ], + "type": "svi" + }], + + "svi_merged_config" : [ + { + "name": "vlan1001", + "profile": { + "admin_state": true, + "adv_subnet_in_underlay": true, + "cmds": [ + "no shutdown" + ], + "description": "Switched vlan interface 1001", + "dhcp_server_addr1": "192.200.1.1", + "dhcp_server_addr2": "192.200.1.2", + "dhcp_server_addr3": "192.200.1.3", + "disable_ip_redirects": true, + "enable_hsrp": true, + "enable_netflow": false, + "hsrp_group": 10, + "hsrp_priority": 5, + "hsrp_version": 1, + "hsrp_vip": "192.168.2.100", + "hsrp_vmac": "0000.0101.ac0a", + "int_vrf": "blue", + "ipv4_addr": "192.168.2.1", + "ipv4_mask_len": 24, + "mode": "vlan", + "mtu": 9216, + "netflow_monitor": "", + "preempt": true, + "route_tag": 1001, + "vrf_dhcp1": "blue", + "vrf_dhcp2": "blue", + "vrf_dhcp3": "blue" + }, + "switch": [ + "192.168.1.108" + ], + "type": "svi" + }], + + "svi_deleted_existing_config" : [ + { + "name": "vlan1001", + "type": "svi", + "switch": [ + "192.168.1.108" + ] + }], + + "svi_deleted_non_existing_config" : [ + { + "name": "vlan1111", + "type": "svi", + "switch": [ + "192.168.1.108" + ] + }], + + "svi_replaced_config" : [ + { + "name": "vlan1001", + "profile": { + "admin_state": false, + "adv_subnet_in_underlay": false, + "cmds": [ + "no shutdown" + ], + "description": "Switched vlan interface 1001 - REP", + "dhcp_server_addr1": "192.201.1.1", + "dhcp_server_addr2": "192.201.1.2", + "dhcp_server_addr3": "192.201.1.3", + "disable_ip_redirects": false, + "enable_hsrp": true, + "enable_netflow": false, + "hsrp_group": 11, + "hsrp_priority": 6, + "hsrp_version": 1, + "hsrp_vip": "192.168.3.100", + "hsrp_vmac": "0000.0102.ac0a", + "int_vrf": "green", + "ipv4_addr": "192.168.3.1", + "ipv4_mask_len": 24, + "mode": "vlan", + "mtu": 3216, + "netflow_monitor": "svi1002", + "preempt": false, + "route_tag": 1002, + "vrf_dhcp1": "green", + "vrf_dhcp2": "green", + "vrf_dhcp3": "green" + }, + "switch": [ + "192.168.1.108" + ], + "type": "svi" + }], + + "svi_overridden_config" : [ + { + "name": "vlan1010", + "profile": { + "admin_state": false, + "mode": "vlan" + }, + "switch": [ + "192.168.1.108" + ], + "type": "svi" + }] +} diff --git a/tests/unit/modules/dcnm/fixtures/dcnm_intf_svi_payloads.json b/tests/unit/modules/dcnm/fixtures/dcnm_intf_svi_payloads.json new file mode 100644 index 000000000..f1cff7a42 --- /dev/null +++ b/tests/unit/modules/dcnm/fixtures/dcnm_intf_svi_payloads.json @@ -0,0 +1,51 @@ +{ + "svi_merged_payloads" : + { + "MESSAGE": "OK", + "REQUEST_PATH": "https://10.122.197.6:443/rest/interface?serialNumber=SAL1819SAN8&ifName=vlan1001", + "DATA": [ + { + "interfaceType": "INTERFACE_VLAN", + "interfaces": [ + { + "fabricName": "test_fabric", + "ifName": "vlan1001", + "interfaceType": "INTERFACE_VLAN", + "nvPairs": { + "ADMIN_STATE": "true", + "CONF": "no shutdown", + "DESC": "Switched vlan interface 1001", + "DISABLE_IP_REDIRECTS": "true", + "ENABLE_HSRP": "true", + "ENABLE_NETFLOW": "false", + "HSRP_GROUP": "10", + "HSRP_PRIORITY": "5", + "HSRP_VERSION": "1", + "HSRP_VIP": "192.168.2.100", + "INTF_NAME": "vlan1001", + "INTF_VRF": "blue", + "IP": "192.168.2.1", + "MAC": "0000.0101.ac0a", + "MTU": "9216", + "NETFLOW_MONITOR": "", + "PREEMPT": "true", + "PREFIX": "24", + "ROUTING_TAG": "1001", + "advSubnetInUnderlay": "true", + "dhcpServerAddr1": "192.200.1.1", + "dhcpServerAddr2": "192.200.1.2", + "dhcpServerAddr3": "192.200.1.3", + "vrfDhcp1": "blue", + "vrfDhcp2": "blue", + "vrfDhcp3": "blue" + }, + "serialNumber": "SAL1819SAN8" + } + ], + "policy": "int_vlan", + "skipResourceCheck": "false" + }], + "RETURN_CODE": 200, + "METHOD": "GET" + } +} diff --git a/tests/unit/modules/dcnm/test_dcnm_intf.py b/tests/unit/modules/dcnm/test_dcnm_intf.py index f2f697288..bb812fb4d 100644 --- a/tests/unit/modules/dcnm/test_dcnm_intf.py +++ b/tests/unit/modules/dcnm/test_dcnm_intf.py @@ -60,7 +60,9 @@ def setUp(self): self.mock_dcnm_version_supported = patch( "ansible_collections.cisco.dcnm.plugins.modules.dcnm_interface.dcnm_version_supported" ) - self.run_dcnm_version_supported = self.mock_dcnm_version_supported.start() + self.run_dcnm_version_supported = ( + self.mock_dcnm_version_supported.start() + ) self.mock_dcnm_send = patch( "ansible_collections.cisco.dcnm.plugins.modules.dcnm_interface.dcnm_send" @@ -86,7 +88,9 @@ def load_multi_intf_fixtures(self): playbook_subint_intf = [] playbook_lo_intf = [] playbook_eth_intf = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -118,15 +122,19 @@ def load_multi_intf_fixtures(self): playbook_deployed_data, ] - if ("_multi_intf_merged_exist" in self._testMethodName): + if "_multi_intf_merged_exist" in self._testMethodName: # No I/F exists case playbook_pc_intf = self.payloads_data.get("pc_payload") playbook_lo_intf = self.payloads_data.get("lo_payload") playbook_eth_intf = self.payloads_data.get("eth_payload") playbook_subint_intf = self.payloads_data.get("subint_payload") playbook_vpc_intf = self.payloads_data.get("vpc_payload") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") - playbook_deployed_data = self.have_all_payloads_data.get("deployed_payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) + playbook_deployed_data = self.have_all_payloads_data.get( + "deployed_payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -152,7 +160,8 @@ def load_multi_intf_fixtures(self): self.playbook_mock_succ_resp, self.playbook_mock_succ_resp, self.playbook_mock_succ_resp, - playbook_deployed_data] + playbook_deployed_data, + ] def load_missing_intf_elems_fixtures(self): @@ -163,7 +172,9 @@ def load_missing_intf_elems_fixtures(self): playbook_vpc_intf = [] playbook_eth_intf = [] playbook_subint_intf = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -204,7 +215,9 @@ def load_mixed_intf_elems_fixtures(self): playbook_lo_intf = [] playbook_subint_intf = [] playbook_vpc_intf = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -250,7 +263,9 @@ def load_bunched_intf_elems_fixtures(self): playbook_eth_intf4 = [] playbook_vpc_intf1 = [] playbook_vpc_intf2 = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -292,7 +307,9 @@ def load_missing_members_fixtures(self): if "_missing_peer_members" in self._testMethodName: # No I/F exists case playbook_intf = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -324,7 +341,9 @@ def load_type_missing_fixtures(self): if "_type_missing_merged_new" in self._testMethodName: # No I/F exists case playbook_pc_intf = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, playbook_pc_intf, @@ -349,7 +368,9 @@ def load_missing_state_fixtures(self): if "_missing_state" in self._testMethodName: # No I/F exists case playbook_pc_intf = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, playbook_pc_intf, @@ -379,7 +400,9 @@ def load_query_state_fixtures(self): playbook_eth_intf = self.payloads_data.get("eth_payload") playbook_sub_intf = self.payloads_data.get("subint_payload") playbook_vpc_intf = self.payloads_data.get("vpc_payload") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -406,6 +429,158 @@ def load_query_state_fixtures(self): self.playbook_mock_succ_resp, ] + # -------------------------- SVI-FIXTURES -------------------------- + + def load_svi_fixtures(self): + + if "_svi_merged_new" in self._testMethodName: + # No I/F exists case + playbook_svi_intf1 = [] + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) + + self.run_dcnm_send.side_effect = [ + self.playbook_mock_vpc_resp, + playbook_svi_intf1, + playbook_have_all_data, + playbook_have_all_data, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + ] + + if "_svi_merged_idempotent" in self._testMethodName: + playbook_svi_intf1 = self.payloads_data.get("svi_merged_payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) + + self.run_dcnm_send.side_effect = [ + self.playbook_mock_vpc_resp, + playbook_svi_intf1, + playbook_have_all_data, + playbook_have_all_data, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + ] + + # Use the same payloads that we use for creating new. + if "_svi_deleted_existing" in self._testMethodName: + playbook_svi_intf1 = self.payloads_data.get("svi_merged_payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) + + self.run_dcnm_send.side_effect = [ + self.playbook_mock_vpc_resp, + playbook_svi_intf1, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + ] + + if "_svi_deleted_non_existing" in self._testMethodName: + playbook_svi_intf1 = [] + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) + + self.run_dcnm_send.side_effect = [ + self.playbook_mock_vpc_resp, + playbook_svi_intf1, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + ] + if "_svi_replaced_existing" in self._testMethodName: + playbook_svi_intf1 = self.payloads_data.get("svi_merged_payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) + + self.run_dcnm_send.side_effect = [ + self.playbook_mock_vpc_resp, + playbook_svi_intf1, + playbook_have_all_data, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + ] + + if "_svi_overridden_existing" in self._testMethodName: + + playbook_svi_intf1 = self.payloads_data.get("svi_merged_payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) + + self.run_dcnm_send.side_effect = [ + self.playbook_mock_vpc_resp, + playbook_svi_intf1, + playbook_have_all_data, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + self.playbook_mock_succ_resp, + ] + # -------------------------- PC-FIXTURES -------------------------- def load_pc_fixtures(self): @@ -416,7 +591,9 @@ def load_pc_fixtures(self): playbook_pc_intf2 = [] playbook_pc_intf3 = [] playbook_pc_intf4 = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -441,8 +618,12 @@ def load_pc_fixtures(self): self.playbook_mock_succ_resp, ] if "_pc_merged_policy_change" in self._testMethodName: - playbook_pc_intf1 = self.payloads_data.get("pc_merged_trunk_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_pc_intf1 = self.payloads_data.get( + "pc_merged_trunk_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -464,11 +645,19 @@ def load_pc_fixtures(self): ] if "_pc_merged_idempotent" in self._testMethodName: - playbook_pc_intf1 = self.payloads_data.get("pc_merged_trunk_payloads") - playbook_pc_intf2 = self.payloads_data.get("pc_merged_access_payloads") + playbook_pc_intf1 = self.payloads_data.get( + "pc_merged_trunk_payloads" + ) + playbook_pc_intf2 = self.payloads_data.get( + "pc_merged_access_payloads" + ) playbook_pc_intf3 = self.payloads_data.get("pc_merged_l3_payloads") - playbook_pc_intf4 = self.payloads_data.get("pc_merged_monitor_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_pc_intf4 = self.payloads_data.get( + "pc_merged_monitor_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -495,11 +684,19 @@ def load_pc_fixtures(self): # Use the same payloads that we use for creating new. if "_pc_deleted_existing" in self._testMethodName: - playbook_pc_intf1 = self.payloads_data.get("pc_merged_trunk_payloads") - playbook_pc_intf2 = self.payloads_data.get("pc_merged_access_payloads") + playbook_pc_intf1 = self.payloads_data.get( + "pc_merged_trunk_payloads" + ) + playbook_pc_intf2 = self.payloads_data.get( + "pc_merged_access_payloads" + ) playbook_pc_intf3 = self.payloads_data.get("pc_merged_l3_payloads") - playbook_pc_intf4 = self.payloads_data.get("pc_merged_monitor_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_pc_intf4 = self.payloads_data.get( + "pc_merged_monitor_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -519,11 +716,19 @@ def load_pc_fixtures(self): ] if "_pc_replaced_existing" in self._testMethodName: - playbook_pc_intf1 = self.payloads_data.get("pc_merged_trunk_payloads") - playbook_pc_intf2 = self.payloads_data.get("pc_merged_access_payloads") + playbook_pc_intf1 = self.payloads_data.get( + "pc_merged_trunk_payloads" + ) + playbook_pc_intf2 = self.payloads_data.get( + "pc_merged_access_payloads" + ) playbook_pc_intf3 = self.payloads_data.get("pc_merged_l3_payloads") - playbook_pc_intf4 = self.payloads_data.get("pc_merged_monitor_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_pc_intf4 = self.payloads_data.get( + "pc_merged_monitor_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -549,8 +754,12 @@ def load_pc_fixtures(self): if "_pc_overridden_existing" in self._testMethodName: - playbook_pc_intf1 = self.payloads_data.get("pc_merged_trunk_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_pc_intf1 = self.payloads_data.get( + "pc_merged_trunk_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -586,7 +795,9 @@ def load_eth_fixtures(self): playbook_eth_intf3 = [] playbook_eth_intf4 = [] playbook_eth_intf5 = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -621,7 +832,9 @@ def load_eth_fixtures(self): playbook_eth_intf1 = self.payloads_data.get( "eth_merged_routed_payloads_eth_1_2" ) - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -645,14 +858,24 @@ def load_eth_fixtures(self): if "_eth_merged_idempotent" in self._testMethodName: - playbook_eth_intf1 = self.payloads_data.get("eth_merged_trunk_payloads") - playbook_eth_intf2 = self.payloads_data.get("eth_merged_access_payloads") - playbook_eth_intf3 = self.payloads_data.get("eth_merged_routed_payloads") + playbook_eth_intf1 = self.payloads_data.get( + "eth_merged_trunk_payloads" + ) + playbook_eth_intf2 = self.payloads_data.get( + "eth_merged_access_payloads" + ) + playbook_eth_intf3 = self.payloads_data.get( + "eth_merged_routed_payloads" + ) playbook_eth_intf4 = self.payloads_data.get( "eth_merged_epl_routed_payloads" ) - playbook_eth_intf5 = self.payloads_data.get("eth_merged_monitor_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_eth_intf5 = self.payloads_data.get( + "eth_merged_monitor_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -681,14 +904,24 @@ def load_eth_fixtures(self): if "_eth_replaced_existing" in self._testMethodName: - playbook_eth_intf1 = self.payloads_data.get("eth_merged_trunk_payloads") - playbook_eth_intf2 = self.payloads_data.get("eth_merged_access_payloads") - playbook_eth_intf3 = self.payloads_data.get("eth_merged_routed_payloads") + playbook_eth_intf1 = self.payloads_data.get( + "eth_merged_trunk_payloads" + ) + playbook_eth_intf2 = self.payloads_data.get( + "eth_merged_access_payloads" + ) + playbook_eth_intf3 = self.payloads_data.get( + "eth_merged_routed_payloads" + ) playbook_eth_intf4 = self.payloads_data.get( "eth_merged_epl_routed_payloads" ) - playbook_eth_intf5 = self.payloads_data.get("eth_merged_monitor_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_eth_intf5 = self.payloads_data.get( + "eth_merged_monitor_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -720,14 +953,24 @@ def load_eth_fixtures(self): if "_eth_deleted_existing" in self._testMethodName: - playbook_eth_intf1 = self.payloads_data.get("eth_merged_trunk_payloads") - playbook_eth_intf2 = self.payloads_data.get("eth_merged_access_payloads") - playbook_eth_intf3 = self.payloads_data.get("eth_merged_routed_payloads") + playbook_eth_intf1 = self.payloads_data.get( + "eth_merged_trunk_payloads" + ) + playbook_eth_intf2 = self.payloads_data.get( + "eth_merged_access_payloads" + ) + playbook_eth_intf3 = self.payloads_data.get( + "eth_merged_routed_payloads" + ) playbook_eth_intf4 = self.payloads_data.get( "eth_merged_epl_routed_payloads" ) - playbook_eth_intf5 = self.payloads_data.get("eth_merged_monitor_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("eth_payloads") + playbook_eth_intf5 = self.payloads_data.get( + "eth_merged_monitor_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "eth_payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -755,8 +998,12 @@ def load_eth_fixtures(self): if "_eth_overridden_existing" in self._testMethodName: - playbook_eth_intf1 = self.payloads_data.get("eth_merged_trunk_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_eth_intf1 = self.payloads_data.get( + "eth_merged_trunk_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -793,7 +1040,9 @@ def load_subint_fixtures(self): # No I/F exists case playbook_sub_intf1 = [] playbook_sub_intf2 = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -819,9 +1068,15 @@ def load_subint_fixtures(self): if "_subint_merged_idempotent" in self._testMethodName: - playbook_subint_intf1 = self.payloads_data.get("subint_merged_payloads_1") - playbook_subint_intf2 = self.payloads_data.get("subint_merged_payloads_2") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_subint_intf1 = self.payloads_data.get( + "subint_merged_payloads_1" + ) + playbook_subint_intf2 = self.payloads_data.get( + "subint_merged_payloads_2" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -834,9 +1089,15 @@ def load_subint_fixtures(self): if "_subint_replaced_existing" in self._testMethodName: - playbook_subint_intf1 = self.payloads_data.get("subint_merged_payloads_1") - playbook_subint_intf2 = self.payloads_data.get("subint_merged_payloads_2") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_subint_intf1 = self.payloads_data.get( + "subint_merged_payloads_1" + ) + playbook_subint_intf2 = self.payloads_data.get( + "subint_merged_payloads_2" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -861,8 +1122,12 @@ def load_subint_fixtures(self): if "_subint_replaced_non_existing" in self._testMethodName: - playbook_subint_intf1 = self.payloads_data.get("subint_merged_payloads_1") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_subint_intf1 = self.payloads_data.get( + "subint_merged_payloads_1" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -886,9 +1151,15 @@ def load_subint_fixtures(self): if "_subint_deleted_existing" in self._testMethodName: - playbook_subint_intf1 = self.payloads_data.get("subint_merged_payloads_1") - playbook_subint_intf2 = self.payloads_data.get("subint_merged_payloads_2") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_subint_intf1 = self.payloads_data.get( + "subint_merged_payloads_1" + ) + playbook_subint_intf2 = self.payloads_data.get( + "subint_merged_payloads_2" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -908,7 +1179,9 @@ def load_subint_fixtures(self): if "_subint_deleted_non_existing" in self._testMethodName: - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, [], @@ -926,8 +1199,12 @@ def load_subint_fixtures(self): if "_subint_overridden_existing" in self._testMethodName: - playbook_subint_intf1 = self.payloads_data.get("subint_merged_payloads_1") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_subint_intf1 = self.payloads_data.get( + "subint_merged_payloads_1" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -961,7 +1238,9 @@ def load_lo_fixtures(self): # No I/F exists case playbook_lo_intf1 = [] playbook_lo_intf2 = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -989,7 +1268,9 @@ def load_lo_fixtures(self): playbook_lo_intf1 = self.payloads_data.get("lo_merged_payloads_1") playbook_lo_intf2 = self.payloads_data.get("lo_merged_payloads_2") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -1012,7 +1293,9 @@ def load_lo_fixtures(self): if "_lo_merged_existing" in self._testMethodName: playbook_lo_intf1 = self.payloads_data.get("lo_merged_payloads_1") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -1038,7 +1321,9 @@ def load_lo_fixtures(self): playbook_lo_intf1 = self.payloads_data.get("lo_merged_payloads_1") playbook_lo_intf2 = self.payloads_data.get("lo_merged_payloads_2") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -1065,7 +1350,9 @@ def load_lo_fixtures(self): playbook_lo_intf1 = self.payloads_data.get("lo_merged_payloads_1") playbook_lo_intf2 = self.payloads_data.get("lo_merged_payloads_2") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -1089,7 +1376,9 @@ def load_lo_fixtures(self): playbook_lo_intf1 = self.payloads_data.get("lo_merged_payloads_1") playbook_lo_intf2 = self.payloads_data.get("lo_merged_payloads_2") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -1119,7 +1408,9 @@ def load_lo_fixtures(self): if "_lo_overridden_non_existing" in self._testMethodName: playbook_lo_intf1 = self.payloads_data.get("lo_merged_payloads_1") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -1148,7 +1439,9 @@ def load_lo_fixtures(self): if "_lo_overridden_existing_2" in self._testMethodName: playbook_lo_intf1 = self.payloads_data.get("lo_merged_payloads_3") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -1178,7 +1471,9 @@ def load_vpc_fixtures(self): # No I/F exists case playbook_vpc_intf1 = [] playbook_vpc_intf2 = [] - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -1206,9 +1501,15 @@ def load_vpc_fixtures(self): ] if "_vpc_merged_idempotent" in self._testMethodName: - playbook_vpc_intf1 = self.payloads_data.get("vpc_merged_trunk_payloads") - playbook_vpc_intf2 = self.payloads_data.get("vpc_merged_access_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_vpc_intf1 = self.payloads_data.get( + "vpc_merged_trunk_payloads" + ) + playbook_vpc_intf2 = self.payloads_data.get( + "vpc_merged_access_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -1233,9 +1534,15 @@ def load_vpc_fixtures(self): # Use the same payloads that we use for creating new. if "_vpc_deleted_existing" in self._testMethodName: - playbook_vpc_intf1 = self.payloads_data.get("vpc_merged_trunk_payloads") - playbook_vpc_intf2 = self.payloads_data.get("vpc_merged_access_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_vpc_intf1 = self.payloads_data.get( + "vpc_merged_trunk_payloads" + ) + playbook_vpc_intf2 = self.payloads_data.get( + "vpc_merged_access_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) self.run_dcnm_send.side_effect = [ self.playbook_mock_vpc_resp, @@ -1254,9 +1561,15 @@ def load_vpc_fixtures(self): ] if "_vpc_replaced_existing" in self._testMethodName: - playbook_vpc_intf1 = self.payloads_data.get("vpc_merged_trunk_payloads") - playbook_vpc_intf2 = self.payloads_data.get("vpc_merged_access_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_vpc_intf1 = self.payloads_data.get( + "vpc_merged_trunk_payloads" + ) + playbook_vpc_intf2 = self.payloads_data.get( + "vpc_merged_access_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -1285,8 +1598,12 @@ def load_vpc_fixtures(self): if "_vpc_overridden_existing" in self._testMethodName: - playbook_vpc_intf1 = self.payloads_data.get("vpc_merged_trunk_payloads") - playbook_have_all_data = self.have_all_payloads_data.get("payloads") + playbook_vpc_intf1 = self.payloads_data.get( + "vpc_merged_trunk_payloads" + ) + playbook_have_all_data = self.have_all_payloads_data.get( + "payloads" + ) playbook_deployed_data = self.have_all_payloads_data.get( "deployed_payloads" ) @@ -1325,6 +1642,9 @@ def load_fixtures(self, response=None, device=""): self.run_dcnm_ip_sn.side_effect = [[self.mock_ip_sn, []]] self.run_dcnm_version_supported.side_effect = [11] + # Load SVI fixtures + self.load_svi_fixtures() + # Load port channel related side-effects self.load_pc_fixtures() @@ -1364,7 +1684,9 @@ def test_dcnm_intf_multi_intf_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_multi_intf_configs") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) self.payloads_data = [] # load required config data @@ -1375,7 +1697,11 @@ def test_dcnm_intf_multi_intf_merged_new(self): self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -1400,44 +1726,68 @@ def test_dcnm_intf_multi_intf_merged_exist(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_multi_intf_configs") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) self.payloads_data = loadPlaybookData("dcnm_intf_multi_intf_payloads") # load required config data - self.playbook_config = self.config_data.get("multi_intf_merged_config_exist") + self.playbook_config = self.config_data.get( + "multi_intf_merged_config_exist" + ) self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") self.mock_ip_sn = self.config_data.get("mock_ip_sn") self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") - set_module_args(dict(state="merged", - fabric="test_fabric", - config=self.playbook_config)) + set_module_args( + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) + ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 5) for d in result["diff"][0]["merged"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"] in ["Port-channel300", - "vPC301", - "Ethernet1/1.1", - "Ethernet1/10", - "Loopback303"]), True) + self.assertEqual( + ( + intf["ifName"] + in [ + "Port-channel300", + "vPC301", + "Ethernet1/1.1", + "Ethernet1/10", + "Loopback303", + ] + ), + True, + ) for key in intf["nvPairs"]: if "MEMBER_INTERFACES" in key: - self.assertEqual(len(intf["nvPairs"][key].split(",")), 2) + self.assertEqual( + len(intf["nvPairs"][key].split(",")), 2 + ) if "CONF" in key: - self.assertEqual(len(intf["nvPairs"][key].split("\n")), 2) + self.assertEqual( + len(intf["nvPairs"][key].split("\n")), 2 + ) def test_dcnm_intf_missing_intf_elems_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_multi_intf_configs") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) self.payloads_data = [] # load required config data - self.playbook_config = self.config_data.get("missing_intf_elems_config") + self.playbook_config = self.config_data.get( + "missing_intf_elems_config" + ) self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") self.mock_ip_sn = self.config_data.get("mock_ip_sn") @@ -1445,7 +1795,11 @@ def test_dcnm_intf_missing_intf_elems_merged_new(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -1470,7 +1824,9 @@ def test_dcnm_intf_check_multi_intf_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_multi_intf_configs") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) self.payloads_data = [] # load required config data @@ -1515,7 +1871,9 @@ def test_dcnm_intf_pc_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_pc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_pc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("pc_merged_config") @@ -1525,7 +1883,11 @@ def test_dcnm_intf_pc_merged_new(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 4) @@ -1549,7 +1911,9 @@ def test_dcnm_intf_pc_merged_idempotent(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_pc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_pc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("pc_merged_config") @@ -1562,7 +1926,11 @@ def test_dcnm_intf_pc_merged_idempotent(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=False, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 0) @@ -1572,7 +1940,9 @@ def test_dcnm_intf_pc_merged_policy_change(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_pc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_pc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config_data = self.config_data.get( @@ -1584,7 +1954,11 @@ def test_dcnm_intf_pc_merged_policy_change(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config_data) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config_data, + ) ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 1) @@ -1594,7 +1968,9 @@ def test_dcnm_intf_pc_deleted_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_pc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_pc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("pc_deleted_config") @@ -1604,7 +1980,11 @@ def test_dcnm_intf_pc_deleted_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="deleted", fabric="test_fabric", config=self.playbook_config) + dict( + state="deleted", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -1628,7 +2008,9 @@ def test_dcnm_intf_pc_replaced_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_pc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_pc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("pc_replaced_config") @@ -1638,7 +2020,11 @@ def test_dcnm_intf_pc_replaced_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="replaced", fabric="test_fabric", config=self.playbook_config) + dict( + state="replaced", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -1658,13 +2044,15 @@ def test_dcnm_intf_pc_replaced_existing(self): "PREFIX", "ROUTING_TAG", "SPEED", - "CONF" + "CONF", ] for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: if_keys = list(intf["nvPairs"].keys()) - self.assertEqual((set(if_keys).issubset(set(changed_objs))), True) + self.assertEqual( + (set(if_keys).issubset(set(changed_objs))), True + ) # Monitor port wil not be deployes self.assertEqual(len(result["diff"][0]["deploy"]), 3) @@ -1673,7 +2061,9 @@ def test_dcnm_intf_pc_overridden_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_pc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_pc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("pc_overridden_config") @@ -1683,11 +2073,15 @@ def test_dcnm_intf_pc_overridden_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="overridden", fabric="test_fabric", config=self.playbook_config) + dict( + state="overridden", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) - self.assertEqual(len(result["diff"][0]["deleted"]), 6) + self.assertEqual(len(result["diff"][0]["deleted"]), 7) del_if_names = [ "port-channel301", @@ -1696,9 +2090,15 @@ def test_dcnm_intf_pc_overridden_existing(self): "ethernet1/3.2", "loopback200", "vpc300", + "vlan2001", ] - rep_if_names = ["ethernet1/3.2", "ethernet1/1", "ethernet1/2", "ethernet3/2"] + rep_if_names = [ + "ethernet1/3.2", + "ethernet1/1", + "ethernet1/2", + "ethernet3/2", + ] ovr_if_names = ["port-channel300"] for intf in result["diff"][0]["deleted"]: @@ -1706,11 +2106,15 @@ def test_dcnm_intf_pc_overridden_existing(self): for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in rep_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in rep_if_names), True + ) for d in result["diff"][0]["overridden"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in ovr_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in ovr_if_names), True + ) # -------------------------- ETH -------------------------- @@ -1719,17 +2123,25 @@ def test_dcnm_intf_eth_merged_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_eth_configs") self.payloads_data = loadPlaybookData("dcnm_intf_eth_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data - self.playbook_config = self.config_data.get("eth_merged_config_existing") + self.playbook_config = self.config_data.get( + "eth_merged_config_existing" + ) self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") self.mock_ip_sn = self.config_data.get("mock_ip_sn") self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 1) @@ -1742,7 +2154,9 @@ def test_dcnm_intf_eth_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_eth_configs") self.payloads_data = loadPlaybookData("dcnm_intf_eth_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("eth_merged_config") @@ -1752,7 +2166,11 @@ def test_dcnm_intf_eth_merged_new(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 5) @@ -1777,7 +2195,9 @@ def test_dcnm_intf_eth_merged_idempotent(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_eth_configs") self.payloads_data = loadPlaybookData("dcnm_intf_eth_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("eth_merged_config") @@ -1790,7 +2210,11 @@ def test_dcnm_intf_eth_merged_idempotent(self): cfg["deploy"] = "False" set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=False, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 0) @@ -1800,7 +2224,9 @@ def test_dcnm_intf_eth_replaced_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_eth_configs") self.payloads_data = loadPlaybookData("dcnm_intf_eth_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("eth_replaced_config") @@ -1810,7 +2236,11 @@ def test_dcnm_intf_eth_replaced_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="replaced", fabric="test_fabric", config=self.playbook_config) + dict( + state="replaced", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -1838,7 +2268,9 @@ def test_dcnm_intf_eth_replaced_existing(self): for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: if_keys = list(intf["nvPairs"].keys()) - self.assertEqual((set(if_keys).issubset(set(changed_objs))), True) + self.assertEqual( + (set(if_keys).issubset(set(changed_objs))), True + ) # Monitor port will not bedeployed self.assertEqual(len(result["diff"][0]["deploy"]), 4) @@ -1847,7 +2279,9 @@ def test_dcnm_intf_eth_deleted_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_eth_configs") self.payloads_data = loadPlaybookData("dcnm_intf_eth_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("eth_deleted_config") @@ -1857,7 +2291,11 @@ def test_dcnm_intf_eth_deleted_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="deleted", fabric="test_fabric", config=self.playbook_config) + dict( + state="deleted", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -1870,7 +2308,9 @@ def test_dcnm_intf_eth_overridden_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_eth_configs") self.payloads_data = loadPlaybookData("dcnm_intf_eth_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("eth_overridden_config") @@ -1880,11 +2320,15 @@ def test_dcnm_intf_eth_overridden_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="overridden", fabric="test_fabric", config=self.playbook_config) + dict( + state="overridden", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) - self.assertEqual(len(result["diff"][0]["deleted"]), 7) + self.assertEqual(len(result["diff"][0]["deleted"]), 8) del_if_names = [ "port-channel301", @@ -1894,6 +2338,7 @@ def test_dcnm_intf_eth_overridden_existing(self): "ethernet1/3.2", "loopback200", "vpc300", + "vlan2001", ] rep_if_names = ["ethernet1/1", "ethernet1/2", "ethernet3/2"] @@ -1904,11 +2349,15 @@ def test_dcnm_intf_eth_overridden_existing(self): for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in rep_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in rep_if_names), True + ) for d in result["diff"][0]["overridden"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in ovr_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in ovr_if_names), True + ) # -------------------------- SUBINT -------------------------- @@ -1917,7 +2366,9 @@ def test_dcnm_intf_subint_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_subint_configs") self.payloads_data = loadPlaybookData("dcnm_intf_subint_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("subint_merged_config") @@ -1927,14 +2378,19 @@ def test_dcnm_intf_subint_merged_new(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 2) for d in result["diff"][0]["merged"]: for intf in d["interfaces"]: self.assertEqual( - (intf["ifName"] in ["Ethernet1/25.1", "Ethernet1/25.2"]), True + (intf["ifName"] in ["Ethernet1/25.1", "Ethernet1/25.2"]), + True, ) def test_dcnm_intf_subint_merged_idempotent(self): @@ -1942,7 +2398,9 @@ def test_dcnm_intf_subint_merged_idempotent(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_subint_configs") self.payloads_data = loadPlaybookData("dcnm_intf_subint_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("subint_merged_config") @@ -1955,7 +2413,11 @@ def test_dcnm_intf_subint_merged_idempotent(self): cfg["deploy"] = "False" set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=False, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 0) @@ -1965,7 +2427,9 @@ def test_dcnm_intf_subint_replaced_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_subint_configs") self.payloads_data = loadPlaybookData("dcnm_intf_subint_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("subint_replaced_config") @@ -1975,7 +2439,11 @@ def test_dcnm_intf_subint_replaced_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="replaced", fabric="test_fabric", config=self.playbook_config) + dict( + state="replaced", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -1998,7 +2466,9 @@ def test_dcnm_intf_subint_replaced_existing(self): for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: if_keys = list(intf["nvPairs"].keys()) - self.assertEqual((set(if_keys).issubset(set(changed_objs))), True) + self.assertEqual( + (set(if_keys).issubset(set(changed_objs))), True + ) # All 2 will be deployed, even though we have not changed the monitor port self.assertEqual(len(result["diff"][0]["deploy"]), 2) @@ -2007,17 +2477,25 @@ def test_dcnm_intf_subint_replaced_non_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_subint_configs") self.payloads_data = loadPlaybookData("dcnm_intf_subint_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data - self.playbook_config = self.config_data.get("subint_replaced_config_non_exist") + self.playbook_config = self.config_data.get( + "subint_replaced_config_non_exist" + ) self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") self.mock_ip_sn = self.config_data.get("mock_ip_sn") self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="replaced", fabric="test_fabric", config=self.playbook_config) + dict( + state="replaced", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -2029,7 +2507,9 @@ def test_dcnm_intf_subint_deleted_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_subint_configs") self.payloads_data = loadPlaybookData("dcnm_intf_subint_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("subint_deleted_config") @@ -2039,7 +2519,11 @@ def test_dcnm_intf_subint_deleted_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="deleted", fabric="test_fabric", config=self.playbook_config) + dict( + state="deleted", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -2054,7 +2538,9 @@ def test_dcnm_intf_subint_deleted_non_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_subint_configs") self.payloads_data = loadPlaybookData("dcnm_intf_subint_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get( @@ -2066,7 +2552,11 @@ def test_dcnm_intf_subint_deleted_non_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="deleted", fabric="test_fabric", config=self.playbook_config) + dict( + state="deleted", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=False, failed=False) @@ -2077,7 +2567,9 @@ def test_dcnm_intf_subint_overridden_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_subint_configs") self.payloads_data = loadPlaybookData("dcnm_intf_subint_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("subint_overridden_config") @@ -2087,11 +2579,15 @@ def test_dcnm_intf_subint_overridden_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="overridden", fabric="test_fabric", config=self.playbook_config) + dict( + state="overridden", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) - self.assertEqual(len(result["diff"][0]["deleted"]), 7) + self.assertEqual(len(result["diff"][0]["deleted"]), 8) del_if_names = [ "port-channel301", @@ -2101,6 +2597,7 @@ def test_dcnm_intf_subint_overridden_existing(self): "ethernet1/3.2", "loopback200", "vpc300", + "vlan2001", ] rep_if_names = ["ethernet1/1", "ethernet1/2", "ethernet3/2"] @@ -2111,11 +2608,15 @@ def test_dcnm_intf_subint_overridden_existing(self): for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in rep_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in rep_if_names), True + ) for d in result["diff"][0]["overridden"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in ovr_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in ovr_if_names), True + ) # -------------------------- LOOPBACK -------------------------- @@ -2124,7 +2625,9 @@ def test_dcnm_intf_lo_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_lo_configs") self.payloads_data = loadPlaybookData("dcnm_intf_lo_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("lo_merged_config") @@ -2134,7 +2637,11 @@ def test_dcnm_intf_lo_merged_new(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 2) @@ -2149,17 +2656,25 @@ def test_dcnm_intf_lo_merged_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_lo_configs") self.payloads_data = loadPlaybookData("dcnm_intf_lo_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data - self.playbook_config = self.config_data.get("lo_merged_existing_config") + self.playbook_config = self.config_data.get( + "lo_merged_existing_config" + ) self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") self.mock_ip_sn = self.config_data.get("mock_ip_sn") self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 1) @@ -2172,7 +2687,9 @@ def test_dcnm_intf_lo_merged_idempotent(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_lo_configs") self.payloads_data = loadPlaybookData("dcnm_intf_lo_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("lo_merged_config") @@ -2185,7 +2702,11 @@ def test_dcnm_intf_lo_merged_idempotent(self): cfg["deploy"] = "False" set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=False, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 0) @@ -2195,7 +2716,9 @@ def test_dcnm_intf_lo_replaced_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_lo_configs") self.payloads_data = loadPlaybookData("dcnm_intf_lo_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("lo_replaced_config") @@ -2205,7 +2728,11 @@ def test_dcnm_intf_lo_replaced_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="replaced", fabric="test_fabric", config=self.playbook_config) + dict( + state="replaced", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -2225,7 +2752,9 @@ def test_dcnm_intf_lo_replaced_existing(self): for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: if_keys = list(intf["nvPairs"].keys()) - self.assertEqual((set(if_keys).issubset(set(changed_objs))), True) + self.assertEqual( + (set(if_keys).issubset(set(changed_objs))), True + ) # All 2 will be deployed, even though we have not changed the monitor port self.assertEqual(len(result["diff"][0]["deploy"]), 2) @@ -2234,7 +2763,9 @@ def test_dcnm_intf_lo_deleted_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_lo_configs") self.payloads_data = loadPlaybookData("dcnm_intf_lo_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("lo_deleted_config") @@ -2244,20 +2775,28 @@ def test_dcnm_intf_lo_deleted_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="deleted", fabric="test_fabric", config=self.playbook_config) + dict( + state="deleted", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["deleted"]), 2) for intf in result["diff"][0]["deleted"]: - self.assertEqual((intf["ifName"] in ["Loopback100", "Loopback101"]), True) + self.assertEqual( + (intf["ifName"] in ["Loopback100", "Loopback101"]), True + ) def test_dcnm_intf_lo_overridden_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_lo_configs") self.payloads_data = loadPlaybookData("dcnm_intf_lo_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("lo_overridden_config") @@ -2267,11 +2806,15 @@ def test_dcnm_intf_lo_overridden_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="overridden", fabric="test_fabric", config=self.playbook_config) + dict( + state="overridden", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) - self.assertEqual(len(result["diff"][0]["deleted"]), 7) + self.assertEqual(len(result["diff"][0]["deleted"]), 8) del_if_names = [ "port-channel301", @@ -2281,6 +2824,7 @@ def test_dcnm_intf_lo_overridden_existing(self): "ethernet1/3.2", "loopback200", "vpc300", + "vlan2001", ] rep_if_names = ["ethernet1/1", "ethernet1/2", "ethernet3/2"] @@ -2291,32 +2835,44 @@ def test_dcnm_intf_lo_overridden_existing(self): for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in rep_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in rep_if_names), True + ) for d in result["diff"][0]["overridden"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in ovr_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in ovr_if_names), True + ) def test_dcnm_intf_lo_overridden_existing_2(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_lo_configs") self.payloads_data = loadPlaybookData("dcnm_intf_lo_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data - self.playbook_config = self.config_data.get("lo_overridden_existing_config") + self.playbook_config = self.config_data.get( + "lo_overridden_existing_config" + ) self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") self.mock_ip_sn = self.config_data.get("mock_ip_sn") self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="overridden", fabric="test_fabric", config=self.playbook_config) + dict( + state="overridden", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) - self.assertEqual(len(result["diff"][0]["deleted"]), 6) + self.assertEqual(len(result["diff"][0]["deleted"]), 7) del_if_names = [ "port-channel301", @@ -2325,6 +2881,7 @@ def test_dcnm_intf_lo_overridden_existing_2(self): "port-channel300", "ethernet1/3.2", "vpc300", + "vlan2001", ] rep_if_names = ["ethernet1/1", "ethernet1/2", "ethernet3/2"] @@ -2335,32 +2892,44 @@ def test_dcnm_intf_lo_overridden_existing_2(self): for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in rep_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in rep_if_names), True + ) for d in result["diff"][0]["overridden"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in ovr_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in ovr_if_names), True + ) def test_dcnm_intf_lo_overridden_non_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_lo_configs") self.payloads_data = loadPlaybookData("dcnm_intf_lo_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data - self.playbook_config = self.config_data.get("lo_overridden_non_existing_config") + self.playbook_config = self.config_data.get( + "lo_overridden_non_existing_config" + ) self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") self.mock_ip_sn = self.config_data.get("mock_ip_sn") self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="overridden", fabric="test_fabric", config=self.playbook_config) + dict( + state="overridden", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) - self.assertEqual(len(result["diff"][0]["deleted"]), 7) + self.assertEqual(len(result["diff"][0]["deleted"]), 8) del_if_names = [ "port-channel301", @@ -2370,6 +2939,7 @@ def test_dcnm_intf_lo_overridden_non_existing(self): "ethernet1/3.2", "loopback200", "vpc300", + "vlan2001", ] rep_if_names = ["ethernet1/1", "ethernet1/2", "ethernet3/2"] @@ -2380,11 +2950,15 @@ def test_dcnm_intf_lo_overridden_non_existing(self): for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in rep_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in rep_if_names), True + ) for d in result["diff"][0]["overridden"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in ovr_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in ovr_if_names), True + ) # -------------------------- vPC -------------------------- @@ -2393,7 +2967,9 @@ def test_dcnm_intf_vpc_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_vpc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_vpc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("vpc_merged_config") @@ -2404,20 +2980,28 @@ def test_dcnm_intf_vpc_merged_new(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 2) for d in result["diff"][0]["merged"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"] in ["vPC750", "vPC751"]), True) + self.assertEqual( + (intf["ifName"] in ["vPC750", "vPC751"]), True + ) def test_dcnm_intf_vpc_merged_idempotent(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_vpc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_vpc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("vpc_merged_config") @@ -2434,7 +3018,11 @@ def test_dcnm_intf_vpc_merged_idempotent(self): self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=False, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 0) @@ -2444,7 +3032,9 @@ def test_dcnm_intf_vpc_deleted_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_vpc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_vpc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("vpc_deleted_config") @@ -2455,7 +3045,11 @@ def test_dcnm_intf_vpc_deleted_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="deleted", fabric="test_fabric", config=self.playbook_config) + dict( + state="deleted", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -2468,7 +3062,9 @@ def test_dcnm_intf_vpc_replaced_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_vpc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_vpc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("vpc_replaced_config") @@ -2479,7 +3075,11 @@ def test_dcnm_intf_vpc_replaced_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="replaced", fabric="test_fabric", config=self.playbook_config) + dict( + state="replaced", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -2504,13 +3104,15 @@ def test_dcnm_intf_vpc_replaced_existing(self): "PEER2_CONF", "PEER1_PO_CONF", "PEER2_PO_CONF", - "INTF_NAME" + "INTF_NAME", ] for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: if_keys = list(intf["nvPairs"].keys()) - self.assertEqual((set(if_keys).issubset(set(changed_objs))), True) + self.assertEqual( + (set(if_keys).issubset(set(changed_objs))), True + ) # All 4 will be deployed, even though we have not changed the monitor port self.assertEqual(len(result["diff"][0]["deploy"]), 2) @@ -2519,7 +3121,9 @@ def test_dcnm_intf_vpc_overridden_existing(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_vpc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_vpc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("vpc_overridden_config") @@ -2530,11 +3134,15 @@ def test_dcnm_intf_vpc_overridden_existing(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="overridden", fabric="test_fabric", config=self.playbook_config) + dict( + state="overridden", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) - self.assertEqual(len(result["diff"][0]["deleted"]), 7) + self.assertEqual(len(result["diff"][0]["deleted"]), 8) del_if_names = [ "port-channel301", @@ -2544,9 +3152,15 @@ def test_dcnm_intf_vpc_overridden_existing(self): "ethernet1/3.2", "loopback200", "vpc300", + "vlan2001", ] - rep_if_names = ["ethernet1/3.2", "ethernet1/1", "ethernet1/2", "ethernet3/2"] + rep_if_names = [ + "ethernet1/3.2", + "ethernet1/1", + "ethernet1/2", + "ethernet3/2", + ] ovr_if_names = ["vPC750"] for intf in result["diff"][0]["deleted"]: @@ -2554,11 +3168,269 @@ def test_dcnm_intf_vpc_overridden_existing(self): for d in result["diff"][0]["replaced"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in rep_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in rep_if_names), True + ) + + for d in result["diff"][0]["overridden"]: + for intf in d["interfaces"]: + self.assertEqual( + (intf["ifName"].lower() in ovr_if_names), True + ) + + # -------------------------- SVI -------------------------- + + def test_dcnm_intf_svi_merged_new(self): + + # load the json from playbooks + self.config_data = loadPlaybookData("dcnm_intf_svi_configs") + self.payloads_data = loadPlaybookData("dcnm_intf_svi_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) + + # load required config data + self.playbook_config = self.config_data.get("svi_merged_config") + self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") + self.mock_ip_sn = self.config_data.get("mock_ip_sn") + self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") + self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") + + set_module_args( + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) + ) + result = self.execute_module(changed=True, failed=False) + self.assertEqual(len(result["diff"][0]["merged"]), 1) + for d in result["diff"][0]["merged"]: + for intf in d["interfaces"]: + self.assertEqual((intf["ifName"] in ["vlan1001"]), True) + + def test_dcnm_intf_svi_merged_idempotent(self): + + # load the json from playbooks + self.config_data = loadPlaybookData("dcnm_intf_svi_configs") + self.payloads_data = loadPlaybookData("dcnm_intf_svi_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) + + # load required config data + self.playbook_config = self.config_data.get("svi_merged_config") + + for cfg in self.playbook_config: + cfg["deploy"] = "False" + self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") + self.mock_ip_sn = self.config_data.get("mock_ip_sn") + self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") + self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") + + set_module_args( + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) + ) + result = self.execute_module(changed=False, failed=False) + self.assertEqual(len(result["diff"][0]["merged"]), 0) + + def test_dcnm_intf_svi_deleted_existing(self): + + # load the json from playbooks + self.config_data = loadPlaybookData("dcnm_intf_svi_configs") + self.payloads_data = loadPlaybookData("dcnm_intf_svi_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) + + # load required config data + self.playbook_config = self.config_data.get( + "svi_deleted_existing_config" + ) + self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") + self.mock_ip_sn = self.config_data.get("mock_ip_sn") + self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") + self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") + + set_module_args( + dict( + state="deleted", + fabric="test_fabric", + config=self.playbook_config, + ) + ) + result = self.execute_module(changed=True, failed=False) + + self.assertEqual(len(result["diff"][0]["deleted"]), 1) + for intf in result["diff"][0]["deleted"]: + self.assertEqual((intf["ifName"] in ["vlan1001"]), True) + + def test_dcnm_intf_svi_deleted_non_existing(self): + + # load the json from playbooks + self.config_data = loadPlaybookData("dcnm_intf_svi_configs") + self.payloads_data = loadPlaybookData("dcnm_intf_svi_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) + + # load required config data + self.playbook_config = self.config_data.get( + "svi_deleted_non_existing_config" + ) + self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") + self.mock_ip_sn = self.config_data.get("mock_ip_sn") + self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") + self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") + + set_module_args( + dict( + state="deleted", + fabric="test_fabric", + config=self.playbook_config, + ) + ) + result = self.execute_module(changed=False, failed=False) + + self.assertEqual(len(result["diff"][0]["deleted"]), 0) + for intf in result["diff"][0]["deleted"]: + self.assertEqual((intf["ifName"] in ["vlan1001"]), True) + + def test_dcnm_intf_svi_replaced_existing(self): + + # load the json from playbooks + self.config_data = loadPlaybookData("dcnm_intf_svi_configs") + self.payloads_data = loadPlaybookData("dcnm_intf_svi_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) + + # load required config data + self.playbook_config = self.config_data.get("svi_replaced_config") + self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") + self.mock_ip_sn = self.config_data.get("mock_ip_sn") + self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") + self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") + + set_module_args( + dict( + state="replaced", + fabric="test_fabric", + config=self.playbook_config, + ) + ) + result = self.execute_module(changed=True, failed=False) + + self.assertEqual(len(result["diff"][0]["replaced"]), 1) + + changed_objs = [ + "MEMBER_INTERFACES", + "PC_MODE", + "BPDUGUARD_ENABLED", + "PORTTYPE_FAST_ENABLED", + "MTU", + "ALLOWED_VLANS", + "DESC", + "ADMIN_STATE", + "INTF_VRF", + "IP", + "PREFIX", + "ROUTING_TAG", + "SPEED", + "CONF", + "DISABLE_IP_REDIRECTS", + "ENABLE_HSRP", + "ENABLE_NETFLOW", + "HSRP_GROUP", + "HSRP_PRIORITY", + "HSRP_VERSION", + "HSRP_VIP", + "INTF_NAME", + "MAC", + "NETFLOW_MONITOR", + "PREEMPT", + "advSubnetInUnderlay", + "dhcpServerAddr1", + "dhcpServerAddr2", + "dhcpServerAddr3", + "vrfDhcp1", + "vrfDhcp2", + "vrfDhcp3", + ] + + for d in result["diff"][0]["replaced"]: + for intf in d["interfaces"]: + if_keys = list(intf["nvPairs"].keys()) + self.assertEqual( + (set(if_keys).issubset(set(changed_objs))), True + ) + # Monitor port wil not be deployes + self.assertEqual(len(result["diff"][0]["deploy"]), 1) + + def test_dcnm_intf_svi_overridden_existing(self): + + # load the json from playbooks + self.config_data = loadPlaybookData("dcnm_intf_svi_configs") + self.payloads_data = loadPlaybookData("dcnm_intf_svi_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) + + # load required config data + self.playbook_config = self.config_data.get("svi_overridden_config") + self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") + self.mock_ip_sn = self.config_data.get("mock_ip_sn") + self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") + self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") + + set_module_args( + dict( + state="overridden", + fabric="test_fabric", + config=self.playbook_config, + ) + ) + result = self.execute_module(changed=True, failed=False) + + self.assertEqual(len(result["diff"][0]["deleted"]), 8) + + del_if_names = [ + "port-channel301", + "port-channel302", + "port-channel303", + "port-channel300", + "ethernet1/3.2", + "loopback200", + "vpc300", + "vlan2001", + ] + + rep_if_names = [ + "ethernet1/3.2", + "ethernet1/1", + "ethernet1/2", + "ethernet3/2", + ] + ovr_if_names = ["vlan1010"] + + for intf in result["diff"][0]["deleted"]: + self.assertEqual((intf["ifName"].lower() in del_if_names), True) + + for d in result["diff"][0]["replaced"]: + for intf in d["interfaces"]: + self.assertEqual( + (intf["ifName"].lower() in rep_if_names), True + ) for d in result["diff"][0]["overridden"]: for intf in d["interfaces"]: - self.assertEqual((intf["ifName"].lower() in ovr_if_names), True) + self.assertEqual( + (intf["ifName"].lower() in ovr_if_names), True + ) # -------------------------- GENERAL -------------------------- @@ -2567,7 +3439,9 @@ def test_dcnm_intf_gen_missing_ip_sn(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_pc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_pc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("pc_merged_config") @@ -2576,7 +3450,11 @@ def test_dcnm_intf_gen_missing_ip_sn(self): self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=False, failed=True) @@ -2590,7 +3468,9 @@ def test_dcnm_intf_mixed_intf_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_mixed_configs") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("mixed_merged_config") @@ -2600,7 +3480,11 @@ def test_dcnm_intf_mixed_intf_merged_new(self): self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -2615,7 +3499,9 @@ def test_dcnm_intf_bunched_intf_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_bunched_configs") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("bunched_merged_config") @@ -2625,7 +3511,11 @@ def test_dcnm_intf_bunched_intf_merged_new(self): self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -2658,7 +3548,9 @@ def test_dcnm_intf_type_missing_merged_new(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_pc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_pc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("pc_type_missing_config") @@ -2668,12 +3560,17 @@ def test_dcnm_intf_type_missing_merged_new(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="merged", fabric="test_fabric", config=self.playbook_config) + dict( + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=False, failed=True) self.assertEqual( - result["msg"], " element, which is mandatory is missing in config" + result["msg"], + " element, which is mandatory is missing in config", ) self.assertEqual(result["failed"], True) @@ -2681,7 +3578,9 @@ def test_dcnm_intf_missing_state(self): self.config_data = loadPlaybookData("dcnm_intf_pc_configs") self.payloads_data = loadPlaybookData("dcnm_intf_pc_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("pc_state_missing_config") @@ -2690,7 +3589,9 @@ def test_dcnm_intf_missing_state(self): self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") - set_module_args(dict(fabric="test_fabric", config=self.playbook_config)) + set_module_args( + dict(fabric="test_fabric", config=self.playbook_config) + ) result = self.execute_module(changed=True, failed=False) self.assertEqual(len(result["diff"][0]["merged"]), 1) @@ -2707,17 +3608,25 @@ def test_dcnm_intf_missing_state(self): def test_dcnm_intf_missing_peer_members(self): self.config_data = loadPlaybookData("dcnm_intf_vpc_configs") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data - self.playbook_config = self.config_data.get("vpc_members_missing_config") + self.playbook_config = self.config_data.get( + "vpc_members_missing_config" + ) self.playbook_mock_succ_resp = self.config_data.get("mock_succ_resp") self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") self.mock_ip_sn = self.config_data.get("mock_ip_sn") self.mock_fab_inv = self.config_data.get("mock_fab_inv_data") set_module_args( - dict(fabric="test_fabric", state="merged", config=self.playbook_config) + dict( + fabric="test_fabric", + state="merged", + config=self.playbook_config, + ) ) result = self.execute_module(changed=True, failed=False) @@ -2737,7 +3646,9 @@ def test_dcnm_intf_query(self): # load the json from playbooks self.config_data = loadPlaybookData("dcnm_intf_query_configs") self.payloads_data = loadPlaybookData("dcnm_intf_query_payloads") - self.have_all_payloads_data = loadPlaybookData("dcnm_intf_have_all_payloads") + self.have_all_payloads_data = loadPlaybookData( + "dcnm_intf_have_all_payloads" + ) # load required config data self.playbook_config = self.config_data.get("query_config") @@ -2747,7 +3658,11 @@ def test_dcnm_intf_query(self): self.playbook_mock_vpc_resp = self.config_data.get("mock_vpc_resp") set_module_args( - dict(state="query", fabric="test_fabric", config=self.playbook_config) + dict( + state="query", + fabric="test_fabric", + config=self.playbook_config, + ) ) result = self.execute_module(changed=False, failed=False) From c3dc5e19d12be1c6de3b3014912a5ee55a21de28 Mon Sep 17 00:00:00 2001 From: praveenramoorthy <62758226+praveenramoorthy@users.noreply.github.com> Date: Thu, 17 Nov 2022 16:27:52 +0530 Subject: [PATCH 23/26] Update CHANGELOG.md --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bb1dd1f3..e0d138151 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +## [2.4.0] - 2022-11-17 + +### Added + +* POAP support in `dcnm_inventory` module +* SVI interface support in `dcnm_interface` module + +### Fixed + +* Fix for a problem where networks cannot be deleted when detach/undeploy fails and network is in an out of sync state. +* Fix default value for `multicast_group_address` property in `dcnm_network` + ## [2.3.0] - 2022-10-28 ### Added @@ -196,6 +208,7 @@ The Ansible Cisco Data Center Network Manager (DCNM) collection includes modules * cisco.dcnm.dcnm_network - Add and remove Networks from a DCNM managed VXLAN fabric. * cisco.dcnm.dcnm_interface - DCNM Ansible Module for managing interfaces. +[2.4.0]: https://github.com/CiscoDevNet/ansible-dcnm/compare/2.3.0...2.4.0 [2.3.0]: https://github.com/CiscoDevNet/ansible-dcnm/compare/2.2.0...2.3.0 [2.2.0]: https://github.com/CiscoDevNet/ansible-dcnm/compare/2.1.1...2.2.0 [2.1.1]: https://github.com/CiscoDevNet/ansible-dcnm/compare/2.1.0...2.1.1 From 5ae547193b93dd881eb3b2da93bc4ae04aeb3e76 Mon Sep 17 00:00:00 2001 From: praveenramoorthy <62758226+praveenramoorthy@users.noreply.github.com> Date: Thu, 17 Nov 2022 16:28:38 +0530 Subject: [PATCH 24/26] Update galaxy.yml --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index cdcaecc13..8919eeb24 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: cisco name: dcnm -version: 2.3.0 +version: 2.4.0 readme: README.md authors: - Shrishail Kariyappanavar From 5973d03ecf30bb4fd0d576e906d9508815253218 Mon Sep 17 00:00:00 2001 From: praveenramoorthy <62758226+praveenramoorthy@users.noreply.github.com> Date: Thu, 17 Nov 2022 16:32:43 +0530 Subject: [PATCH 25/26] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bf831a932..6b8d1a297 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ You can also include it in a `requirements.yml` file and install it with `ansibl --- collections: - name: cisco.dcnm - version: 2.2.0 + version: 2.4.0 ``` ## Using this collection From 8396e6d1de23219dc4b5687e9e0bad7dd4d51705 Mon Sep 17 00:00:00 2001 From: praveenramoorthy <62758226+praveenramoorthy@users.noreply.github.com> Date: Thu, 17 Nov 2022 16:56:32 +0530 Subject: [PATCH 26/26] Update CHANGELOG.md --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0d138151..6051e1283 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,6 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## Unreleased - ## [2.4.0] - 2022-11-17 ### Added