From f4d6dc4f06c0a760edc5c36b200e98bc50f53bc0 Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Mon, 13 Feb 2023 23:03:56 +0530 Subject: [PATCH 1/8] Support for all network and vrf configs --- plugins/modules/dcnm_network.py | 410 +++++++++++++++++++++- plugins/modules/dcnm_vrf.py | 589 +++++++++++++++++++++++++++++++- 2 files changed, 975 insertions(+), 24 deletions(-) diff --git a/plugins/modules/dcnm_network.py b/plugins/modules/dcnm_network.py index 3768eba6f..a5003f57c 100644 --- a/plugins/modules/dcnm_network.py +++ b/plugins/modules/dcnm_network.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -# Copyright (c) 2020-2022 Cisco and/or its affiliates. +# Copyright (c) 2020-2023 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -167,6 +167,71 @@ - The multicast IP address for the network type: str required: false + gw_ipv6_subnet: + description: + - IPv6 Gateway with prefix for the network + type: str + required: false + secondary_ip_gw1: + description: + - IP address with subnet for secondary gateway 1 + type: str + required: false + secondary_ip_gw2: + description: + - IP address with subnet for secondary gateway 2 + type: str + required: false + secondary_ip_gw3: + description: + - IP address with subnet for secondary gateway 3 + type: str + required: false + secondary_ip_gw4: + description: + - IP address with subnet for secondary gateway 4 + type: str + required: false + trm_enable: + description: + - Enable Tenant Routed Multicast + type: bool + required: false + default: false + route_target_both: + description: + - Enable both L2 VNI Route-Target + type: bool + required: false + default: false + l3gw_on_border: + description: + - Enable L3 Gateway on Border + type: bool + required: false + default: false + netflow_enable: + description: + - Enable Netflow + - Netflow is supported only if it is enabled on fabric + - Netflow configs are supported on NDFC only + type: bool + required: false + default: false + intfvlan_nf_monitor: + description: + - Interface Vlan Netflow Monitor + - Applicable only if 'Layer 2 Only' is not enabled. Provide monitor name defined in fabric setting for Layer 3 Record + - Netflow configs are supported on NDFC only + type: str + required: false + vlan_nf_monitor: + description: + - Vlan Netflow Monitor + - Provide monitor name defined in fabric setting for Layer 3 Record + - Netflow configs are supported on NDFC only + type: str + required: false attach: description: - List of network attachment details @@ -687,6 +752,17 @@ def diff_for_create(self, want, have): dhcp3_vrf_changed = False dhcp_loopback_changed = False multicast_group_address_changed = False + gwv6_changed = False + sec_gw1_changed = False + sec_gw2_changed = False + sec_gw3_changed = False + sec_gw4_changed = False + trm_en_changed = False + rt_both_changed = False + l3gw_onbd_changed = False + nf_en_changed = False + intvlan_nfmon_changed = False + vlan_nfmon_changed = False if want.get("networkId") and want["networkId"] != have["networkId"]: self.module.fail_json( @@ -710,7 +786,7 @@ def diff_for_create(self, want, have): gw_ip_have = json_to_dict_have.get("gatewayIpAddress", "") vlanId_want = json_to_dict_want.get("vlanId", "") vlanId_have = json_to_dict_have.get("vlanId") - l2only_want = json_to_dict_want.get("isLayer2Only", "") + l2only_want = str(json_to_dict_want.get("isLayer2Only", "")).lower() l2only_have = json_to_dict_have.get("isLayer2Only", "") vlanName_want = json_to_dict_want.get("vlanName", "") vlanName_have = json_to_dict_have.get("vlanName", "") @@ -718,7 +794,7 @@ def diff_for_create(self, want, have): intDesc_have = json_to_dict_have.get("intfDescription", "") mtu_want = json_to_dict_want.get("mtu", "") mtu_have = json_to_dict_have.get("mtu", "") - arpsup_want = json_to_dict_want.get("suppressArp", "") + arpsup_want = str(json_to_dict_want.get("suppressArp", "")).lower() arpsup_have = json_to_dict_have.get("suppressArp", "") dhcp1_ip_want = json_to_dict_want.get("dhcpServerAddr1", "") dhcp1_ip_want = json_to_dict_want.get("dhcpServerAddr1", "") @@ -737,6 +813,29 @@ def diff_for_create(self, want, have): dhcp_loopback_have = json_to_dict_have.get("loopbackId", "") multicast_group_address_want = json_to_dict_want.get("mcastGroup", "") multicast_group_address_have = json_to_dict_have.get("mcastGroup", "") + gw_ipv6_want = json_to_dict_want.get("gatewayIpV6Address", "") + gw_ipv6_have = json_to_dict_have.get("gatewayIpV6Address", "") + secip_gw1_want = json_to_dict_want.get("secondaryGW1", "") + secip_gw1_have = json_to_dict_have.get("secondaryGW1", "") + secip_gw2_want = json_to_dict_want.get("secondaryGW2", "") + secip_gw2_have = json_to_dict_have.get("secondaryGW2", "") + secip_gw3_want = json_to_dict_want.get("secondaryGW3", "") + secip_gw3_have = json_to_dict_have.get("secondaryGW3", "") + secip_gw4_want = json_to_dict_want.get("secondaryGW4", "") + secip_gw4_have = json_to_dict_have.get("secondaryGW4", "") + trmen_want = str(json_to_dict_want.get("trmEnabled", "")).lower() + trmen_have = json_to_dict_have.get("trmEnabled", "") + rt_both_want = str(json_to_dict_want.get("rtBothAuto", "")).lower() + rt_both_have = json_to_dict_have.get("rtBothAuto", "") + l3gw_onbd_want = str(json_to_dict_want.get("enableL3OnBorder", "")).lower() + l3gw_onbd_have = json_to_dict_have.get("enableL3OnBorder", "") + nf_en_want = str(json_to_dict_want.get("ENABLE_NETFLOW", "")).lower() + nf_en_have = json_to_dict_have.get("ENABLE_NETFLOW", "") + intvlan_nfen_want = json_to_dict_want.get("SVI_NETFLOW_MONITOR", "") + intvlan_nfen_have = json_to_dict_have.get("SVI_NETFLOW_MONITOR", "") + vlan_nfen_want = json_to_dict_want.get("VLAN_NETFLOW_MONITOR", "") + vlan_nfen_have = json_to_dict_have.get("VLAN_NETFLOW_MONITOR", "") + if vlanId_have != "": vlanId_have = int(vlanId_have) tag_want = json_to_dict_want.get("tag", "") @@ -745,14 +844,6 @@ def diff_for_create(self, want, have): tag_have = int(tag_have) if mtu_have != "": mtu_have = int(mtu_have) - if arpsup_have == "true": - arpsup_have = True - elif arpsup_have == "false": - arpsup_have = False - if l2only_have == "true": - l2only_have = True - elif l2only_have == "false": - l2only_have = False if vlanId_want: @@ -775,6 +866,17 @@ def diff_for_create(self, want, have): or dhcp3_vrf_have != dhcp3_vrf_want or dhcp_loopback_have != dhcp_loopback_want or multicast_group_address_have != multicast_group_address_want + or gw_ipv6_have != gw_ipv6_want + or secip_gw1_have != secip_gw1_want + or secip_gw2_have != secip_gw2_want + or secip_gw3_have != secip_gw3_want + or secip_gw4_have != secip_gw4_want + or trmen_have != trmen_want + or rt_both_have != rt_both_want + or l3gw_onbd_have != l3gw_onbd_want + or nf_en_have != nf_en_want + or intvlan_nfen_have != intvlan_nfen_want + or vlan_nfen_have != vlan_nfen_want ): # The network updates with missing networkId will have to use existing # networkId from the instance of the same network on DCNM. @@ -812,6 +914,29 @@ def diff_for_create(self, want, have): dhcp_loopback_changed = True if multicast_group_address_have != multicast_group_address_want: multicast_group_address_changed = True + if gw_ipv6_have != gw_ipv6_want: + gwv6_changed = True + if secip_gw1_have != secip_gw1_want: + sec_gw1_changed = True + if secip_gw2_have != secip_gw2_want: + sec_gw2_changed = True + if secip_gw3_have != secip_gw3_want: + sec_gw3_changed = True + if secip_gw4_have != secip_gw4_want: + sec_gw4_changed = True + if trmen_have != trmen_want: + trm_en_changed = True + if rt_both_have != rt_both_want: + rt_both_changed = True + if l3gw_onbd_have != l3gw_onbd_want: + l3gw_onbd_changed = True + if self.dcnm_version > 11: + if nf_en_have != nf_en_want: + nf_en_changed = True + if intvlan_nfen_have != intvlan_nfen_want: + intvlan_nfmon_changed = True + if vlan_nfen_have != vlan_nfen_want: + vlan_nfmon_changed = True want.update({"networkId": have["networkId"]}) create = want @@ -834,6 +959,19 @@ def diff_for_create(self, want, have): or dhcp1_vrf_have != dhcp1_vrf_want or dhcp2_vrf_have != dhcp2_vrf_want or dhcp3_vrf_have != dhcp3_vrf_want + or dhcp_loopback_have != dhcp_loopback_want + or multicast_group_address_have != multicast_group_address_want + or gw_ipv6_have != gw_ipv6_want + or secip_gw1_have != secip_gw1_want + or secip_gw2_have != secip_gw2_want + or secip_gw3_have != secip_gw3_want + or secip_gw4_have != secip_gw4_want + or trmen_have != trmen_want + or rt_both_have != rt_both_want + or l3gw_onbd_have != l3gw_onbd_want + or nf_en_have != nf_en_want + or intvlan_nfen_have != intvlan_nfen_want + or vlan_nfen_have != vlan_nfen_want ): # The network updates with missing networkId will have to use existing # networkId from the instance of the same network on DCNM. @@ -868,6 +1006,29 @@ def diff_for_create(self, want, have): dhcp_loopback_changed = True if multicast_group_address_have != multicast_group_address_want: multicast_group_address_changed = True + if gw_ipv6_have != gw_ipv6_want: + gwv6_changed = True + if secip_gw1_have != secip_gw1_want: + sec_gw1_changed = True + if secip_gw2_have != secip_gw2_want: + sec_gw2_changed = True + if secip_gw3_have != secip_gw3_want: + sec_gw3_changed = True + if secip_gw4_have != secip_gw4_want: + sec_gw4_changed = True + if trmen_have != trmen_want: + trm_en_changed = True + if rt_both_have != rt_both_want: + rt_both_changed = True + if l3gw_onbd_have != l3gw_onbd_want: + l3gw_onbd_changed = True + if self.dcnm_version > 11: + if nf_en_have != nf_en_want: + nf_en_changed = True + if intvlan_nfen_have != intvlan_nfen_want: + intvlan_nfmon_changed = True + if vlan_nfen_have != vlan_nfen_want: + vlan_nfmon_changed = True want.update({"networkId": have["networkId"]}) create = want @@ -890,6 +1051,17 @@ def diff_for_create(self, want, have): dhcp3_vrf_changed, dhcp_loopback_changed, multicast_group_address_changed, + gwv6_changed, + sec_gw1_changed, + sec_gw2_changed, + sec_gw3_changed, + sec_gw4_changed, + trm_en_changed, + rt_both_changed, + l3gw_onbd_changed, + nf_en_changed, + intvlan_nfmon_changed, + vlan_nfmon_changed ) def update_create_params(self, net): @@ -943,8 +1115,21 @@ def update_create_params(self, net): "vrfDhcp3": net.get("dhcp_srvr3_vrf", ""), "loopbackId": net.get("dhcp_loopback_id", ""), "mcastGroup": net.get("multicast_group_address", ""), + "gatewayIpV6Address": net.get("gw_ipv6_subnet", ""), + "secondaryGW1": net.get("secondary_ip_gw1", ""), + "secondaryGW2": net.get("secondary_ip_gw2", ""), + "secondaryGW3": net.get("secondary_ip_gw3", ""), + "secondaryGW4": net.get("secondary_ip_gw4", ""), + "trmEnabled": net.get("trm_enable", False), + "rtBothAuto": net.get("route_target_both", False), + "enableL3OnBorder": net.get("l3gw_on_border", False), } + if self.dcnm_version > 11: + template_conf.update(ENABLE_NETFLOW = net.get("netflow_enable", False)) + template_conf.update(SVI_NETFLOW_MONITOR = net.get("intfvlan_nf_monitor", "")) + template_conf.update(VLAN_NETFLOW_MONITOR = net.get("vlan_nf_monitor", "")) + if template_conf["vlanId"] is None: template_conf["vlanId"] = "" if template_conf["tag"] is None: @@ -965,6 +1150,21 @@ def update_create_params(self, net): template_conf["loopbackId"] = "" if template_conf["mcastGroup"] is None: template_conf["mcastGroup"] = "" + if template_conf["gatewayIpV6Address"] is None: + template_conf["gatewayIpV6Address"] = "" + if template_conf["secondaryGW1"] is None: + template_conf["secondaryGW1"] = "" + if template_conf["secondaryGW2"] is None: + template_conf["secondaryGW2"] = "" + if template_conf["secondaryGW3"] is None: + template_conf["secondaryGW3"] = "" + if template_conf["secondaryGW4"] is None: + template_conf["secondaryGW4"] = "" + if self.dcnm_version > 11: + if template_conf["SVI_NETFLOW_MONITOR"] is None: + template_conf["SVI_NETFLOW_MONITOR"] = "" + if template_conf["VLAN_NETFLOW_MONITOR"] is None: + template_conf["VLAN_NETFLOW_MONITOR"] = "" net_upd.update({"networkTemplateConfig": json.dumps(template_conf)}) @@ -1049,8 +1249,21 @@ def get_have(self): "vrfDhcp3": json_to_dict.get("vrfDhcp3", ""), "loopbackId": json_to_dict.get("loopbackId", ""), "mcastGroup": json_to_dict.get("mcastGroup", ""), + "gatewayIpV6Address": json_to_dict.get("gatewayIpV6Address", ""), + "secondaryGW1": json_to_dict.get("secondaryGW1", ""), + "secondaryGW2": json_to_dict.get("secondaryGW2", ""), + "secondaryGW3": json_to_dict.get("secondaryGW3", ""), + "secondaryGW4": json_to_dict.get("secondaryGW4", ""), + "trmEnabled": json_to_dict.get("trmEnabled", False), + "rtBothAuto": json_to_dict.get("rtBothAuto", False), + "enableL3OnBorder": json_to_dict.get("enableL3OnBorder", False), } + if self.dcnm_version > 11: + t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW", False)) + t_conf.update(SVI_NETFLOW_MONITOR = json_to_dict.get("SVI_NETFLOW_MONITOR", "")) + t_conf.update(VLAN_NETFLOW_MONITOR = json_to_dict.get("VLAN_NETFLOW_MONITOR", "")) + net.update({"networkTemplateConfig": json.dumps(t_conf)}) del net["displayName"] del net["serviceNetworkTemplate"] @@ -1085,8 +1298,21 @@ def get_have(self): "vrfDhcp3": json_to_dict.get("vrfDhcp3", ""), "loopbackId": json_to_dict.get("loopbackId", ""), "mcastGroup": json_to_dict.get("mcastGroup", ""), + "gatewayIpV6Address": json_to_dict.get("gatewayIpV6Address", ""), + "secondaryGW1": json_to_dict.get("secondaryGW1", ""), + "secondaryGW2": json_to_dict.get("secondaryGW2", ""), + "secondaryGW3": json_to_dict.get("secondaryGW3", ""), + "secondaryGW4": json_to_dict.get("secondaryGW4", ""), + "trmEnabled": json_to_dict.get("trmEnabled", False), + "rtBothAuto": json_to_dict.get("rtBothAuto", False), + "enableL3OnBorder": json_to_dict.get("enableL3OnBorder", False), } + if self.dcnm_version > 11: + t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW", False)) + t_conf.update(SVI_NETFLOW_MONITOR = json_to_dict.get("SVI_NETFLOW_MONITOR", "")) + t_conf.update(VLAN_NETFLOW_MONITOR = json_to_dict.get("VLAN_NETFLOW_MONITOR", "")) + l2net.update({"networkTemplateConfig": json.dumps(t_conf)}) del l2net["displayName"] del l2net["serviceNetworkTemplate"] @@ -1513,6 +1739,18 @@ def get_diff_merge(self, replace=False): dhcp3_vrf_changed = {} dhcp_loopback_changed = {} multicast_group_address_changed = {} + gwv6_changed = {} + sec_gw1_changed = {} + sec_gw2_changed = {} + sec_gw3_changed = {} + sec_gw4_changed = {} + trm_en_changed = {} + rt_both_changed = {} + l3gw_onbd_changed = {} + nf_en_changed = {} + intvlan_nfmon_changed = {} + vlan_nfmon_changed = {} + for want_c in self.want_create: found = False @@ -1538,6 +1776,17 @@ def get_diff_merge(self, replace=False): dhcp3_vrf_chg, dhcp_loopbk_chg, mcast_grp_chg, + gwv6_chg, + sec_gw1_chg, + sec_gw2_chg, + sec_gw3_chg, + sec_gw4_chg, + trm_en_chg, + rt_both_chg, + l3gw_onbd_chg, + nf_en_chg, + intvlan_nfmon_chg, + vlan_nfmon_chg ) = self.diff_for_create(want_c, have_c) gw_changed.update({want_c["networkName"]: gw_chg}) tg_changed.update({want_c["networkName"]: tg_chg}) @@ -1558,6 +1807,17 @@ def get_diff_merge(self, replace=False): multicast_group_address_changed.update( {want_c["networkName"]: mcast_grp_chg} ) + gwv6_changed.update({want_c["networkName"]: gwv6_chg}) + sec_gw1_changed.update({want_c["networkName"]: sec_gw1_chg}) + sec_gw2_changed.update({want_c["networkName"]: sec_gw2_chg}) + sec_gw3_changed.update({want_c["networkName"]: sec_gw3_chg}) + sec_gw4_changed.update({want_c["networkName"]: sec_gw4_chg}) + trm_en_changed.update({want_c["networkName"]: trm_en_chg}) + rt_both_changed.update({want_c["networkName"]: rt_both_chg}) + l3gw_onbd_changed.update({want_c["networkName"]: l3gw_onbd_chg}) + nf_en_changed.update({want_c["networkName"]: nf_en_chg}) + intvlan_nfmon_changed.update({want_c["networkName"]: intvlan_nfmon_chg}) + vlan_nfmon_changed.update({want_c["networkName"]: vlan_nfmon_chg}) if diff: diff_create_update.append(diff) break @@ -1673,6 +1933,17 @@ def get_diff_merge(self, replace=False): or dhcp3_vrf_changed.get(want_a["networkName"], False) or dhcp_loopback_changed.get(want_a["networkName"], False) or multicast_group_address_changed.get(want_a["networkName"], False) + or gwv6_changed.get(want_a["networkName"], False) + or sec_gw1_changed.get(want_a["networkName"], False) + or sec_gw2_changed.get(want_a["networkName"], False) + or sec_gw3_changed.get(want_a["networkName"], False) + or sec_gw4_changed.get(want_a["networkName"], False) + or trm_en_changed.get(want_a["networkName"], False) + or rt_both_changed.get(want_a["networkName"], False) + or l3gw_onbd_changed.get(want_a["networkName"], False) + or nf_en_changed.get(want_a["networkName"], False) + or intvlan_nfmon_changed.get(want_a["networkName"], False) + or vlan_nfmon_changed.get(want_a["networkName"], False) ): dep_net = want_a["networkName"] @@ -1763,6 +2034,18 @@ def format_diff(self): found_c.update({"dhcp_srvr3_vrf": json_to_dict.get("vrfDhcp3", "")}) found_c.update({"dhcp_loopback_id": json_to_dict.get("loopbackId", "")}) found_c.update({"multicast_group_address": json_to_dict.get("mcastGroup", "")}) + found_c.update({"gw_ipv6_subnet": json_to_dict.get("gatewayIpV6Address", "")}) + found_c.update({"secondary_ip_gw1": json_to_dict.get("secondaryGW1", "")}) + found_c.update({"secondary_ip_gw2": json_to_dict.get("secondaryGW2", "")}) + found_c.update({"secondary_ip_gw3": json_to_dict.get("secondaryGW3", "")}) + found_c.update({"secondary_ip_gw4": json_to_dict.get("secondaryGW4", "")}) + found_c.update({"trm_enable": json_to_dict.get("trmEnabled", False)}) + found_c.update({"route_target_both": json_to_dict.get("rtBothAuto", False)}) + found_c.update({"l3gw_on_border": json_to_dict.get("enableL3OnBorder", False)}) + if self.dcnm_version > 11: + found_c.update({"netflow_enable": json_to_dict.get("ENABLE_NETFLOW", False)}) + found_c.update({"intfvlan_nf_monitor": json_to_dict.get("SVI_NETFLOW_MONITOR", "")}) + found_c.update({"vlan_nf_monitor": json_to_dict.get("VLAN_NETFLOW_MONITOR", "")}) found_c.update({"attach": []}) del found_c["fabric"] @@ -2091,8 +2374,21 @@ def push_to_remote(self, is_rollback=False): "vrfDhcp3": json_to_dict.get("vrfDhcp3", ""), "loopbackId": json_to_dict.get("loopbackId", ""), "mcastGroup": json_to_dict.get("mcastGroup", ""), + "gatewayIpV6Address": json_to_dict.get("gatewayIpV6Address", ""), + "secondaryGW1": json_to_dict.get("secondaryGW1", ""), + "secondaryGW2": json_to_dict.get("secondaryGW2", ""), + "secondaryGW3": json_to_dict.get("secondaryGW3", ""), + "secondaryGW4": json_to_dict.get("secondaryGW4", ""), + "trmEnabled": json_to_dict.get("trmEnabled", False), + "rtBothAuto": json_to_dict.get("rtBothAuto", False), + "enableL3OnBorder": json_to_dict.get("enableL3OnBorder", False), } + if self.dcnm_version > 11: + t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW", False)) + t_conf.update(SVI_NETFLOW_MONITOR = json_to_dict.get("SVI_NETFLOW_MONITOR", "")) + t_conf.update(VLAN_NETFLOW_MONITOR = json_to_dict.get("VLAN_NETFLOW_MONITOR", "")) + net.update({"networkTemplateConfig": json.dumps(t_conf)}) method = "POST" @@ -2159,6 +2455,11 @@ def validate_input(self): if state == "query": + if self.dcnm_version > 11: + mcast_group_addr = "239.1.1.1" + else: + mcast_group_addr = "239.1.1.0" + net_spec = dict( net_name=dict(required=True, type="str", length_max=64), net_id=dict(type="int", range_max=16777214), @@ -2184,7 +2485,18 @@ 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"), + multicast_group_address=dict(type="ipv4", default=mcast_group_addr), + gw_ipv6_subnet=dict(type="ipv6_subnet", default=""), + secondary_ip_gw1=dict(type="ipv4", default=""), + secondary_ip_gw2=dict(type="ipv4", default=""), + secondary_ip_gw3=dict(type="ipv4", default=""), + secondary_ip_gw4=dict(type="ipv4", default=""), + trm_enable=dict(type="bool", default=False), + route_target_both=dict(type="bool", default=False), + l3gw_on_border=dict(type="bool", default=False), + netflow_enable=dict(type="bool", default=False), + intfvlan_nf_monitor=dict(type="str"), + vlan_nf_monitor=dict(type="str"), ) att_spec = dict( ip_address=dict(required=True, type="str"), @@ -2223,6 +2535,11 @@ def validate_input(self): else: + if self.dcnm_version > 11: + mcast_group_addr = "239.1.1.1" + else: + mcast_group_addr = "239.1.1.0" + net_spec = dict( net_name=dict(required=True, type="str", length_max=64), net_id=dict(type="int", range_max=16777214), @@ -2248,7 +2565,18 @@ 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"), + multicast_group_address=dict(type="ipv4", default=mcast_group_addr), + gw_ipv6_subnet=dict(type="ipv6_subnet", default=""), + secondary_ip_gw1=dict(type="ipv4", default=""), + secondary_ip_gw2=dict(type="ipv4", default=""), + secondary_ip_gw3=dict(type="ipv4", default=""), + secondary_ip_gw4=dict(type="ipv4", default=""), + trm_enable=dict(type="bool", default=False), + route_target_both=dict(type="bool", default=False), + l3gw_on_border=dict(type="bool", default=False), + netflow_enable=dict(type="bool", default=False), + intfvlan_nf_monitor=dict(type="str"), + vlan_nf_monitor=dict(type="str"), ) att_spec = dict( ip_address=dict(required=True, type="str"), @@ -2317,6 +2645,12 @@ def validate_input(self): "DHCP server IP should be specified along with DHCP server VRF" ) + if self.dcnm_version == 11: + if net.get("netflow_enable") or net.get("intfvlan_nf_monitor") or net.get("vlan_nf_monitor"): + invalid_params.append( + "Netflow configurations are supported only on NDFC" + ) + self.validated.append(net) if invalid_params: @@ -2506,6 +2840,56 @@ def dcnm_update_network_information(self, want, have, cfg): if cfg.get("multicast_group_address", None) is None: json_to_dict_want["mcastGroup"] = json_to_dict_have["mcastGroup"] + if cfg.get("gw_ipv6_subnet", None) is None: + json_to_dict_want["gatewayIpV6Address"] = json_to_dict_have["gatewayIpV6Address"] + + if cfg.get("secondary_ip_gw1", None) is None: + json_to_dict_want["secondaryGW1"] = json_to_dict_have["secondaryGW1"] + + if cfg.get("secondary_ip_gw2", None) is None: + json_to_dict_want["secondaryGW2"] = json_to_dict_have["secondaryGW2"] + + if cfg.get("secondary_ip_gw3", None) is None: + json_to_dict_want["secondaryGW3"] = json_to_dict_have["secondaryGW3"] + + if cfg.get("secondary_ip_gw4", None) is None: + json_to_dict_want["secondaryGW4"] = json_to_dict_have["secondaryGW4"] + + if cfg.get("trm_enable", None) is None: + json_to_dict_want["trmEnabled"] = json_to_dict_have["trmEnabled"] + if str(json_to_dict_want["trmEnabled"]).lower() == "true": + json_to_dict_want["trmEnabled"] = True + else: + json_to_dict_want["trmEnabled"] = False + + if cfg.get("route_target_both", None) is None: + json_to_dict_want["rtBothAuto"] = json_to_dict_have["rtBothAuto"] + if str(json_to_dict_want["rtBothAuto"]).lower() == "true": + json_to_dict_want["rtBothAuto"] = True + else: + json_to_dict_want["rtBothAuto"] = False + + if cfg.get("l3gw_on_border", None) is None: + json_to_dict_want["enableL3OnBorder"] = json_to_dict_have["enableL3OnBorder"] + if str(json_to_dict_want["enableL3OnBorder"]).lower() == "true": + json_to_dict_want["enableL3OnBorder"] = True + else: + json_to_dict_want["enableL3OnBorder"] = False + + if self.dcnm_version > 11: + if cfg.get("netflow_enable", None) is None: + json_to_dict_want["ENABLE_NETFLOW"] = json_to_dict_have["ENABLE_NETFLOW"] + if str(json_to_dict_want["ENABLE_NETFLOW"]).lower() == "true": + json_to_dict_want["ENABLE_NETFLOW"] = True + else: + json_to_dict_want["ENABLE_NETFLOW"] = False + + if cfg.get("intfvlan_nf_monitor", None) is None: + json_to_dict_want["SVI_NETFLOW_MONITOR"] = json_to_dict_have["SVI_NETFLOW_MONITOR"] + + if cfg.get("vlan_nf_monitor", None) is None: + json_to_dict_want["VLAN_NETFLOW_MONITOR"] = json_to_dict_have["VLAN_NETFLOW_MONITOR"] + want.update({"networkTemplateConfig": json.dumps(json_to_dict_want)}) def update_want(self): diff --git a/plugins/modules/dcnm_vrf.py b/plugins/modules/dcnm_vrf.py index 1af9c7d6b..7346db85a 100644 --- a/plugins/modules/dcnm_vrf.py +++ b/plugins/modules/dcnm_vrf.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -# Copyright (c) 2020-2022 Cisco and/or its affiliates. +# Copyright (c) 2020-2023 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -84,6 +84,211 @@ - Service vrf template type: str default: None + vrf_vlan_name: + description: + - VRF Vlan Name + - if > 32 chars enable:system vlan long-name + type: str + required: false + vrf_intf_desc: + description: + - VRF Intf Description + type: str + required: false + vrf_description: + description: + - VRF Description + type: str + required: false + vrf_int_mtu: + description: + - VRF interface MTU + type: int + required: false + default: 9216 + loopback_route_tag: + description: + - Loopback Routing Tag + type: int + required: false + default: 12345 + redist_direct_rmap: + description: + - Redistribute Direct Route Map + type: str + required: false + default: 'FABRIC-RMAP-REDIST-SUBNET' + max_bgp_paths: + description: + - Max BGP Paths + type: int + required: false + default: 1 + max_ibgp_paths: + description: + - Max iBGP Paths + type: int + required: false + default: 2 + ipv6_linklocal_enable: + description: + - Enable IPv6 link-local Option + type: bool + required: false + default: true + trm_enable: + description: + - Enable Tenant Routed Multicast + type: bool + required: false + default: false + no_rp: + description: + - No RP, only SSM is used + - supported on NDFC only + type: bool + required: false + default: false + rp_external: + description: + - Specifies if RP is external to the fabric + - Can be configured only when TRM is enabled + type: bool + required: false + default: false + rp_address: + description: + - IPv4 Address of RP + - Can be configured only when TRM is enabled + type: str + required: false + rp_loopback_id: + description: + - loopback ID of RP + - Can be configured only when TRM is enabled + type: int + required: false + underlay_mcast_ip: + description: + - Underlay IPv4 Multicast Address + - Can be configured only when TRM is enabled + type: str + required: false + overlay_mcast_group: + description: + - Underlay IPv4 Multicast group (224.0.0.0/4 to 239.255.255.255/4) + - Can be configured only when TRM is enabled + type: str + required: false + trm_bgw_msite: + description: + - Enable TRM on Border Gateway Multisite + - Can be configured only when TRM is enabled + type: bool + required: false + default: false + adv_host_routes: + description: + - Flag to Control Advertisement of /32 and /128 Routes to Edge Routers + type: bool + required: false + default: false + adv_default_routes: + description: + - Flag to Control Advertisement of Default Route Internally + type: bool + required: false + default: true + static_default_route: + description: + - Flag to Control Static Default Route Configuration + type: bool + required: false + default: true + bgp_password: + description: + - VRF Lite BGP neighbor password + type: str + required: false + bgp_passwd_encrypt: + description: + - VRF Lite BGP Key Encryption Type + type: int + choices: + - 3 (3DES) + - 7 (Cisco) + required: false + default: 3 + netflow_enable: + description: + - Enable netflow on VRF-LITE Sub-interface + - Netflow is supported only if it is enabled on fabric + - Netflow configs are supported on NDFC only + type: bool + required: false + default: false + nf_monitor: + description: + - Netflow Monitor + - Netflow configs are supported on NDFC only + type: str + required: false + disable_rt_auto: + description: + - Disable RT Auto-Generate + - supported on NDFC only + type: bool + required: false + default: false + import_vpn_rt: + description: + - VPN routes to import + - supported on NDFC only + - Use ',' to separate multiple route-targets(eg: 1:1,2:2) + type: list + required: false + default: [] + export_vpn_rt: + description: + - VPN routes to export + - supported on NDFC only + - Use ',' to separate multiple route-targets(eg: 1:1,2:2) + type: str + required: false + import_evpn_rt: + description: + - EVPN routes to import + - supported on NDFC only + - Use ',' to separate multiple route-targets(eg: 1:1,2:2) + type: list + required: false + default: [] + export_evpn_rt: + description: + - EVPN routes to export + - supported on NDFC only + - Use ',' to separate multiple route-targets(eg: 1:1,2:2) + type: list + required: false + default: [] + import_mvpn_rt: + description: + - MVPN routes to import + - supported on NDFC only + - Can be configured only when TRM is enabled + - Use ',' to separate multiple route-targets(eg: 1:1,2:2) + type: list + required: false + default: [] + export_mvpn_rt: + description: + - MVPN routes to export + - supported on NDFC only + - Can be configured only when TRM is enabled + - Use ',' to separate multiple route-targets(eg: 1:1,2:2) + type: list + required: false + default: [] attach: description: - List of vrf attachment details @@ -642,6 +847,7 @@ def update_attach_params(self, attach, vrf_name, deploy, vlanId): def diff_for_create(self, want, have): + conf_changed = False if not have: return {} @@ -650,8 +856,70 @@ def diff_for_create(self, want, have): json_to_dict_want = json.loads(want["vrfTemplateConfig"]) json_to_dict_have = json.loads(have["vrfTemplateConfig"]) - vlanId_want = str(json_to_dict_want.get("vlanId", "")) - vlanId_have = json_to_dict_have.get("vlanId", "") + vlanId_want = str(json_to_dict_want.get("vrfVlanId", "")) + vlanId_have = json_to_dict_have.get("vrfVlanId", "") + vlanName_want = json_to_dict_want.get("vrfVlanName", "") + vlanName_have = json_to_dict_have.get("vrfVlanName", "") + vlanIntf_want = json_to_dict_want.get("vrfIntfDescription", "") + vlanIntf_have = json_to_dict_have.get("vrfIntfDescription", "") + vrfDesc_want = json_to_dict_want.get("vrfDescription", "") + vrfDesc_have = json_to_dict_have.get("vrfDescription", "") + vrfMtu_want = str(json_to_dict_want.get("mtu", "")) + vrfMtu_have = json_to_dict_have.get("mtu", "") + vrfTag_want = str(json_to_dict_want.get("tag", "")) + vrfTag_have = json_to_dict_have.get("tag", "") + redRmap_want = json_to_dict_want.get("vrfRouteMap", "") + redRmap_have = json_to_dict_have.get("vrfRouteMap", "") + maxBgp_want = str(json_to_dict_want.get("maxBgpPaths", "")) + maxBgp_have = json_to_dict_have.get("maxBgpPaths", "") + maxiBgp_want = str(json_to_dict_want.get("maxIbgpPaths", "")) + maxiBgp_have = json_to_dict_have.get("maxIbgpPaths", "") + ipv6ll_want = str(json_to_dict_want.get("ipv6LinkLocalFlag", "")).lower() + ipv6ll_have = json_to_dict_have.get("ipv6LinkLocalFlag", "") + trmen_want = str(json_to_dict_want.get("trmEnabled", "")).lower() + trmen_have = json_to_dict_have.get("trmEnabled", "") + norp_want = str(json_to_dict_want.get("isRPAbsent", "")).lower() + norp_have = json_to_dict_have.get("isRPAbsent", "") + rpext_want = str(json_to_dict_want.get("isRPExternal", "")).lower() + rpext_have = json_to_dict_have.get("isRPExternal", "") + rpadd_want = json_to_dict_want.get("rpAddress", "") + rpadd_have = json_to_dict_have.get("rpAddress", "") + rploid_want = str(json_to_dict_want.get("loopbackNumber", "")) + rploid_have = json_to_dict_have.get("loopbackNumber", "") + mcastadd_want = json_to_dict_want.get("L3VniMcastGroup", "") + mcastadd_have = json_to_dict_have.get("L3VniMcastGroup", "") + mcastgrp_want = json_to_dict_want.get("multicastGroup", "") + mcastgrp_have = json_to_dict_have.get("multicastGroup", "") + trmBgwms_want = str(json_to_dict_want.get("trmBGWMSiteEnabled", "")).lower() + trmBgwms_have = json_to_dict_have.get("trmBGWMSiteEnabled", "") + advhrt_want = str(json_to_dict_want.get("advertiseHostRouteFlag", "")).lower() + advhrt_have = json_to_dict_have.get("advertiseHostRouteFlag", "") + advdrt_want = str(json_to_dict_want.get("advertiseDefaultRouteFlag", "")).lower() + advdrt_have = json_to_dict_have.get("advertiseDefaultRouteFlag", "") + constd_want = str(json_to_dict_want.get("configureStaticDefaultRouteFlag", "")).lower() + constd_have = json_to_dict_have.get("configureStaticDefaultRouteFlag", "") + bgppass_want = json_to_dict_want.get("bgpPassword", "") + bgppass_have = json_to_dict_have.get("bgpPassword", "") + bgppasskey_want = str(json_to_dict_want.get("bgpPasswordKeyType", "")) + bgppasskey_have = json_to_dict_have.get("bgpPasswordKeyType", "") + nfen_want = str(json_to_dict_want.get("ENABLE_NETFLOW", "")).lower() + nfen_have = json_to_dict_have.get("ENABLE_NETFLOW", "") + nfmon_want = json_to_dict_want.get("NETFLOW_MONITOR", "") + nfmon_have = json_to_dict_have.get("NETFLOW_MONITOR", "") + disrtauto_want = str(json_to_dict_want.get("disableRtAuto", "")).lower() + disrtauto_have = json_to_dict_have.get("disableRtAuto", "") + rtvpnImp_want = json_to_dict_want.get("routeTargetImport", "") + rtvpnImp_have = json_to_dict_have.get("routeTargetImport", "") + rtvpnExp_want = json_to_dict_want.get("routeTargetExport", "") + rtvpnExp_have = json_to_dict_have.get("routeTargetExport", "") + rtevpnImp_want = json_to_dict_want.get("routeTargetImportEvpn", "") + rtevpnImp_have = json_to_dict_have.get("routeTargetImportEvpn", "") + rtevpnExp_want = json_to_dict_want.get("routeTargetExportEvpn", "") + rtevpnExp_have = json_to_dict_have.get("routeTargetExportEvpn", "") + rtmvpnImp_want = json_to_dict_want.get("routeTargetImportMvpn", "") + rtmvpnImp_have = json_to_dict_have.get("routeTargetImportMvpn", "") + rtmvpnExp_want = json_to_dict_want.get("routeTargetExportMvpn", "") + rtmvpnExp_have = json_to_dict_have.get("routeTargetExportMvpn", "") if vlanId_want != "0": @@ -666,8 +934,40 @@ def diff_for_create(self, want, have): or have["vrfTemplate"] != want["vrfTemplate"] or have["vrfExtensionTemplate"] != want["vrfExtensionTemplate"] or vlanId_have != vlanId_want + or vlanName_have != vlanName_want + or vlanIntf_have != vlanIntf_want + or vrfDesc_have != vrfDesc_want + or vrfMtu_have != vrfMtu_want + or vrfTag_have != vrfTag_want + or redRmap_have != redRmap_want + or maxBgp_have != maxBgp_want + or maxiBgp_have != maxiBgp_want + or ipv6ll_have != ipv6ll_want + or trmen_have != trmen_want + or norp_have != norp_want + or rpext_have != rpext_want + or rpadd_have != rpadd_want + or rploid_have != rploid_want + or mcastadd_have != mcastadd_want + or mcastgrp_have != mcastgrp_want + or trmBgwms_have != trmBgwms_want + or advhrt_have != advhrt_want + or advdrt_have != advdrt_want + or constd_have != constd_want + or bgppass_have != bgppass_want + or bgppasskey_have != bgppasskey_want + or nfen_have != nfen_want + or nfmon_have != nfmon_want + or disrtauto_have != disrtauto_want + or rtvpnImp_have != rtvpnImp_want + or rtvpnExp_have != rtvpnExp_want + or rtevpnImp_have != rtevpnImp_want + or rtevpnExp_have != rtevpnExp_want + or rtmvpnImp_have != rtmvpnImp_want + or rtmvpnExp_have != rtmvpnExp_want ): + conf_changed = True if want["vrfId"] is None: # The vrf updates with missing vrfId will have to use existing # vrfId from the instance of the same vrf on DCNM. @@ -688,8 +988,40 @@ def diff_for_create(self, want, have): have["serviceVrfTemplate"] != want["serviceVrfTemplate"] or have["vrfTemplate"] != want["vrfTemplate"] or have["vrfExtensionTemplate"] != want["vrfExtensionTemplate"] + or vlanName_have != vlanName_want + or vlanIntf_have != vlanIntf_want + or vrfDesc_have != vrfDesc_want + or vrfMtu_have != vrfMtu_want + or vrfTag_have != vrfTag_want + or redRmap_have != redRmap_want + or maxBgp_have != maxBgp_want + or maxiBgp_have != maxiBgp_want + or ipv6ll_have != ipv6ll_want + or trmen_have != trmen_want + or norp_have != norp_want + or rpext_have != rpext_want + or rpadd_have != rpadd_want + or rploid_have != rploid_want + or mcastadd_have != mcastadd_want + or mcastgrp_have != mcastgrp_want + or trmBgwms_have != trmBgwms_want + or advhrt_have != advhrt_want + or advdrt_have != advdrt_want + or constd_have != constd_want + or bgppass_have != bgppass_want + or bgppasskey_have != bgppasskey_want + or nfen_have != nfen_want + or nfmon_have != nfmon_want + or disrtauto_have != disrtauto_want + or rtvpnImp_have != rtvpnImp_want + or rtvpnExp_have != rtvpnExp_want + or rtevpnImp_have != rtevpnImp_want + or rtevpnExp_have != rtevpnExp_want + or rtmvpnImp_have != rtmvpnImp_want + or rtmvpnExp_have != rtmvpnExp_want ): + conf_changed = True if want["vrfId"] is None: # The vrf updates with missing vrfId will have to use existing # vrfId from the instance of the same vrf on DCNM. @@ -698,7 +1030,7 @@ def diff_for_create(self, want, have): else: pass - return create + return create, conf_changed def update_create_params(self, vrf, vlanId=""): @@ -726,8 +1058,41 @@ def update_create_params(self, vrf, vlanId=""): template_conf = { "vrfSegmentId": vrf.get("vrf_id", None), "vrfName": vrf["vrf_name"], - "vlanId": vlanId, + "vrfVlanId": vlanId, + "vrfVlanName": vrf.get("vrf_vlan_name", ""), + "vrfIntfDescription": vrf.get("vrf_intf_desc", ""), + "vrfDescription": vrf.get("vrf_description", ""), + "mtu": vrf.get("vrf_int_mtu", ""), + "tag": vrf.get("loopback_route_tag", ""), + "vrfRouteMap": vrf.get("redist_direct_rmap", ""), + "maxBgpPaths": vrf.get("max_bgp_paths", ""), + "maxIbgpPaths": vrf.get("max_ibgp_paths", ""), + "ipv6LinkLocalFlag": vrf.get("ipv6_linklocal_enable", True), + "trmEnabled": vrf.get("trm_enable", False), + "isRPExternal": vrf.get("rp_external", False), + "rpAddress": vrf.get("rp_address", ""), + "loopbackNumber": vrf.get("rp_loopback_id", ""), + "L3VniMcastGroup": vrf.get("underlay_mcast_ip", ""), + "multicastGroup": vrf.get("overlay_mcast_group", ""), + "trmBGWMSiteEnabled": vrf.get("trm_bgw_msite", False), + "advertiseHostRouteFlag": vrf.get("adv_host_routes", False), + "advertiseDefaultRouteFlag": vrf.get("adv_default_routes", True), + "configureStaticDefaultRouteFlag": vrf.get("static_default_route", True), + "bgpPassword": vrf.get("bgp_password", ""), + "bgpPasswordKeyType": vrf.get("bgp_passwd_encrypt", ""), } + if self.dcnm_version > 11: + template_conf.update(isRPAbsent = vrf.get("no_rp", False)) + template_conf.update(ENABLE_NETFLOW = vrf.get("netflow_enable", False)) + template_conf.update(NETFLOW_MONITOR = vrf.get("nf_monitor", "")) + template_conf.update(disableRtAuto = vrf.get("disable_rt_auto", False)) + template_conf.update(routeTargetImport = vrf.get("import_vpn_rt", "")) + template_conf.update(routeTargetExport = vrf.get("export_vpn_rt", "")) + template_conf.update(routeTargetImportEvpn = vrf.get("import_evpn_rt", "")) + template_conf.update(routeTargetExportEvpn = vrf.get("export_evpn_rt", "")) + template_conf.update(routeTargetImportMvpn = vrf.get("import_mvpn_rt", "")) + template_conf.update(routeTargetExportMvpn = vrf.get("export_mvpn_rt", "")) + vrf_upd.update({"vrfTemplateConfig": json.dumps(template_conf)}) return vrf_upd @@ -774,9 +1139,42 @@ def get_have(self): t_conf = { "vrfSegmentId": vrf["vrfId"], "vrfName": vrf["vrfName"], - "vlanId": json_to_dict.get("vlanId", 0), + "vrfVlanId": json_to_dict.get("vrfVlanId", 0), + "vrfVlanName": json_to_dict.get("vrfVlanName", ""), + "vrfIntfDescription": json_to_dict.get("vrfIntfDescription", ""), + "vrfDescription": json_to_dict.get("vrfDescription", ""), + "mtu": json_to_dict.get("mtu", 9216), + "tag": json_to_dict.get("tag", 12345), + "vrfRouteMap": json_to_dict.get("vrfRouteMap", ""), + "maxBgpPaths": json_to_dict.get("maxBgpPaths", 1), + "maxIbgpPaths": json_to_dict.get("maxIbgpPaths", 2), + "ipv6LinkLocalFlag": json_to_dict.get("ipv6LinkLocalFlag", True), + "trmEnabled": json_to_dict.get("trmEnabled", False), + "isRPExternal": json_to_dict.get("isRPExternal", False), + "rpAddress": json_to_dict.get("rpAddress", ""), + "loopbackNumber": json_to_dict.get("loopbackNumber", ""), + "L3VniMcastGroup": json_to_dict.get("L3VniMcastGroup", ""), + "multicastGroup": json_to_dict.get("multicastGroup", ""), + "trmBGWMSiteEnabled": json_to_dict.get("trmBGWMSiteEnabled", False), + "advertiseHostRouteFlag": json_to_dict.get("advertiseHostRouteFlag", False), + "advertiseDefaultRouteFlag": json_to_dict.get("advertiseDefaultRouteFlag", True), + "configureStaticDefaultRouteFlag": json_to_dict.get("configureStaticDefaultRouteFlag", True), + "bgpPassword": json_to_dict.get("bgpPassword", ""), + "bgpPasswordKeyType": json_to_dict.get("bgpPasswordKeyType", ""), } + if self.dcnm_version > 11: + t_conf.update(isRPAbsent = json_to_dict.get("isRPAbsent")) + t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW")) + t_conf.update(NETFLOW_MONITOR = json_to_dict.get("NETFLOW_MONITOR")) + t_conf.update(disableRtAuto = json_to_dict.get("disableRtAuto")) + t_conf.update(routeTargetImport = json_to_dict.get("routeTargetImport")) + t_conf.update(routeTargetExport = json_to_dict.get("routeTargetExport")) + t_conf.update(routeTargetImportEvpn = json_to_dict.get("routeTargetImportEvpn")) + t_conf.update(routeTargetExportEvpn = json_to_dict.get("routeTargetExportEvpn")) + t_conf.update(routeTargetImportMvpn = json_to_dict.get("routeTargetImportMvpn")) + t_conf.update(routeTargetExportMvpn = json_to_dict.get("routeTargetExportMvpn")) + vrf.update({"vrfTemplateConfig": json.dumps(t_conf)}) del vrf["vrfStatus"] have_create.append(vrf) @@ -1175,6 +1573,7 @@ def get_diff_merge(self): diff_attach = [] diff_deploy = {} prev_vrf_id_fetched = None + conf_changed = {} all_vrfs = "" @@ -1185,7 +1584,8 @@ def get_diff_merge(self): for have_c in self.have_create: if want_c["vrfName"] == have_c["vrfName"]: vrf_found = True - diff = self.diff_for_create(want_c, have_c) + diff, conf_chg = self.diff_for_create(want_c, have_c) + conf_changed.update({want_c["vrfName"]: conf_chg}) if diff: diff_create_update.append(diff) break @@ -1235,10 +1635,46 @@ def get_diff_merge(self): if vrf_id != prev_vrf_id_fetched: want_c.update({"vrfId": vrf_id}) + json_to_dict = json.loads(vrf["vrfTemplateConfig"]) template_conf = { "vrfSegmentId": vrf_id, "vrfName": want_c["vrfName"], + "vrfVlanId": json_to_dict.get("vrfVlanId"), + "vrfVlanName": json_to_dict.get("vrfVlanName"), + "vrfIntfDescription": json_to_dict.get("vrfIntfDescription"), + "vrfDescription": json_to_dict.get("vrfDescription"), + "mtu": json_to_dict.get("mtu"), + "tag": json_to_dict.get("tag"), + "vrfRouteMap": json_to_dict.get("vrfRouteMap"), + "maxBgpPaths": json_to_dict.get("maxBgpPaths"), + "maxIbgpPaths": json_to_dict.get("maxIbgpPaths"), + "ipv6LinkLocalFlag": json_to_dict.get("ipv6LinkLocalFlag"), + "trmEnabled": json_to_dict.get("trmEnabled"), + "isRPExternal": json_to_dict.get("isRPExternal"), + "rpAddress": json_to_dict.get("rpAddress"), + "loopbackNumber": json_to_dict.get("loopbackNumber"), + "L3VniMcastGroup": json_to_dict.get("L3VniMcastGroup"), + "multicastGroup": json_to_dict.get("multicastGroup"), + "trmBGWMSiteEnabled": json_to_dict.get("trmBGWMSiteEnabled"), + "advertiseHostRouteFlag": json_to_dict.get("advertiseHostRouteFlag"), + "advertiseDefaultRouteFlag": json_to_dict.get("advertiseDefaultRouteFlag"), + "configureStaticDefaultRouteFlag": json_to_dict.get("configureStaticDefaultRouteFlag"), + "bgpPassword": json_to_dict.get("bgpPassword"), + "bgpPasswordKeyType": json_to_dict.get("bgpPasswordKeyType"), } + + if self.dcnm_version > 11: + template_conf.update(isRPAbsent = json_to_dict.get("isRPAbsent")) + template_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW")) + template_conf.update(NETFLOW_MONITOR = json_to_dict.get("NETFLOW_MONITOR")) + template_conf.update(disableRtAuto = json_to_dict.get("disableRtAuto")) + template_conf.update(routeTargetImport = json_to_dict.get("routeTargetImport")) + template_conf.update(routeTargetExport = json_to_dict.get("routeTargetExport")) + template_conf.update(routeTargetImportEvpn = json_to_dict.get("routeTargetImportEvpn")) + template_conf.update(routeTargetExportEvpn = json_to_dict.get("routeTargetExportEvpn")) + template_conf.update(routeTargetImportMvpn = json_to_dict.get("routeTargetImportMvpn")) + template_conf.update(routeTargetExportMvpn = json_to_dict.get("routeTargetExportMvpn")) + want_c.update( {"vrfTemplateConfig": json.dumps(template_conf)} ) @@ -1286,7 +1722,7 @@ def get_diff_merge(self): diff_attach.append(base) dep_vrf = want_a["vrfName"] else: - if vrf: + if vrf or conf_changed.get(want_a["vrfName"], False): dep_vrf = want_a["vrfName"] if not attach_found and want_a.get("lanAttachList"): @@ -1349,6 +1785,7 @@ def format_diff(self): found_c = want_d + src = found_c["source"] found_c.update({"vrf_name": found_c["vrfName"]}) found_c.update({"vrf_id": found_c["vrfId"]}) @@ -1359,6 +1796,40 @@ def format_diff(self): found_c.update({"service_vrf_template": found_c["serviceVrfTemplate"]}) found_c.update({"attach": []}) + json_to_dict = json.loads(found_c["vrfTemplateConfig"]) + found_c.update({"vrf_vlan_name": json_to_dict.get("vrfVlanName", "")}) + found_c.update({"vrf_intf_desc": json_to_dict.get("vrfIntfDescription", "")}) + found_c.update({"vrf_description": json_to_dict.get("vrfDescription", "")}) + found_c.update({"vrf_int_mtu": json_to_dict.get("mtu", "")}) + found_c.update({"loopback_route_tag": json_to_dict.get("tag", "")}) + found_c.update({"redist_direct_rmap": json_to_dict.get("vrfRouteMap", "")}) + found_c.update({"max_bgp_paths": json_to_dict.get("maxBgpPaths", "")}) + found_c.update({"max_ibgp_paths": json_to_dict.get("maxIbgpPaths", "")}) + found_c.update({"ipv6_linklocal_enable": json_to_dict.get("ipv6LinkLocalFlag", True)}) + found_c.update({"trm_enable": json_to_dict.get("trmEnabled", False)}) + found_c.update({"rp_external": json_to_dict.get("isRPExternal", False)}) + found_c.update({"rp_address": json_to_dict.get("rpAddress", "")}) + found_c.update({"rp_loopback_id": json_to_dict.get("loopbackNumber", "")}) + found_c.update({"underlay_mcast_ip": json_to_dict.get("L3VniMcastGroup", "")}) + found_c.update({"overlay_mcast_group": json_to_dict.get("multicastGroup", "")}) + found_c.update({"trm_bgw_msite": json_to_dict.get("trmBGWMSiteEnabled", False)}) + found_c.update({"adv_host_routes": json_to_dict.get("advertiseHostRouteFlag", False)}) + found_c.update({"adv_default_routes": json_to_dict.get("advertiseDefaultRouteFlag", True)}) + found_c.update({"static_default_route": json_to_dict.get("configureStaticDefaultRouteFlag", True)}) + found_c.update({"bgp_password": json_to_dict.get("bgpPassword", "")}) + found_c.update({"bgp_passwd_encrypt": json_to_dict.get("bgpPasswordKeyType", "")}) + if self.dcnm_version > 11: + found_c.update({"no_rp": json_to_dict.get("isRPAbsent", False)}) + found_c.update({"netflow_enable": json_to_dict.get("ENABLE_NETFLOW", True)}) + found_c.update({"nf_monitor": json_to_dict.get("NETFLOW_MONITOR", "")}) + found_c.update({"disable_rt_auto": json_to_dict.get("disableRtAuto", False)}) + found_c.update({"import_vpn_rt": json_to_dict.get("routeTargetImport", "")}) + found_c.update({"export_vpn_rt": json_to_dict.get("routeTargetExport", "")}) + found_c.update({"import_evpn_rt": json_to_dict.get("routeTargetImportEvpn", "")}) + found_c.update({"export_evpn_rt": json_to_dict.get("routeTargetExportEvpn", "")}) + found_c.update({"import_mvpn_rt": json_to_dict.get("routeTargetImportMvpn", "")}) + found_c.update({"export_mvpn_rt": json_to_dict.get("routeTargetExportMvpn", "")}) + del found_c["fabric"] del found_c["vrfName"] del found_c["vrfId"] @@ -1634,7 +2105,7 @@ def push_to_remote(self, is_rollback=False): for vrf in self.diff_create: json_to_dict = json.loads(vrf["vrfTemplateConfig"]) - vlanId = json_to_dict.get("vlanId", "0") + vlanId = json_to_dict.get("vrfVlanId", "0") if vlanId == 0: vlan_path = self.paths["GET_VLAN"].format(self.fabric) @@ -1649,11 +2120,44 @@ def push_to_remote(self, is_rollback=False): vlanId = vlan_data["DATA"] t_conf = { - "vrfSegmentId": json_to_dict.get("vrfId", ""), + "vrfSegmentId": vrf["vrfId"], "vrfName": json_to_dict.get("vrfName", ""), - "vlanId": vlanId, + "vrfVlanId": vlanId, + "vrfVlanName": json_to_dict.get("vrfVlanName"), + "vrfIntfDescription": json_to_dict.get("vrfIntfDescription"), + "vrfDescription": json_to_dict.get("vrfDescription"), + "mtu": json_to_dict.get("mtu"), + "tag": json_to_dict.get("tag"), + "vrfRouteMap": json_to_dict.get("vrfRouteMap"), + "maxBgpPaths": json_to_dict.get("maxBgpPaths"), + "maxIbgpPaths": json_to_dict.get("maxIbgpPaths"), + "ipv6LinkLocalFlag": json_to_dict.get("ipv6LinkLocalFlag"), + "trmEnabled": json_to_dict.get("trmEnabled"), + "isRPExternal": json_to_dict.get("isRPExternal"), + "rpAddress": json_to_dict.get("rpAddress"), + "loopbackNumber": json_to_dict.get("loopbackNumber"), + "L3VniMcastGroup": json_to_dict.get("L3VniMcastGroup"), + "multicastGroup": json_to_dict.get("multicastGroup"), + "trmBGWMSiteEnabled": json_to_dict.get("trmBGWMSiteEnabled"), + "advertiseHostRouteFlag": json_to_dict.get("advertiseHostRouteFlag"), + "advertiseDefaultRouteFlag": json_to_dict.get("advertiseDefaultRouteFlag"), + "configureStaticDefaultRouteFlag": json_to_dict.get("configureStaticDefaultRouteFlag"), + "bgpPassword": json_to_dict.get("bgpPassword"), + "bgpPasswordKeyType": json_to_dict.get("bgpPasswordKeyType"), } + if self.dcnm_version > 11: + t_conf.update(isRPAbsent = json_to_dict.get("isRPAbsent")) + t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW")) + t_conf.update(NETFLOW_MONITOR = json_to_dict.get("NETFLOW_MONITOR")) + t_conf.update(disableRtAuto = json_to_dict.get("disableRtAuto")) + t_conf.update(routeTargetImport = json_to_dict.get("routeTargetImport")) + t_conf.update(routeTargetExport = json_to_dict.get("routeTargetExport")) + t_conf.update(routeTargetImportEvpn = json_to_dict.get("routeTargetImportEvpn")) + t_conf.update(routeTargetExportEvpn = json_to_dict.get("routeTargetExportEvpn")) + t_conf.update(routeTargetImportMvpn = json_to_dict.get("routeTargetImportMvpn")) + t_conf.update(routeTargetExportMvpn = json_to_dict.get("routeTargetExportMvpn")) + vrf.update({"vrfTemplateConfig": json.dumps(t_conf)}) resp = dcnm_send(self.module, method, path, json.dumps(vrf)) @@ -1668,6 +2172,7 @@ def push_to_remote(self, is_rollback=False): if self.diff_attach: for d_a in self.diff_attach: for v_a in d_a["lanAttachList"]: + v_a.update(vlan = 0) if v_a.get("vrf_lite"): """Before apply the vrf_lite config, need double check if the switch role is started wth Border""" r = re.search(r"\bborder\b", self.role.lower()) @@ -1878,6 +2383,37 @@ def validate_input(self): service_vrf_template=dict(type="str", default=None), attach=dict(type="list"), deploy=dict(type="bool", default=True), + vrf_vlan_name=dict(type="str", default=""), + vrf_intf_desc=dict(type="str", default=""), + vrf_description=dict(type="str", default=""), + vrf_int_mtu=dict(type="int", range_min=68, range_max=9216, default=9216), + loopback_route_tag=dict(type="int", default=12345, range_max=4294967295), + redist_direct_rmap=dict(type="str", default="FABRIC-RMAP-REDIST-SUBNET"), + max_bgp_paths=dict(type="int", range_min=1, range_max=64, default=1), + max_ibgp_paths=dict(type="int", range_min=1, range_max=64, default=2), + ipv6_linklocal_enable=dict(type="bool", default=True), + trm_enable=dict(type="bool", default=False), + no_rp=dict(type="bool", default=False), + rp_external=dict(type="bool", default=False), + rp_address=dict(type="str", default=""), + rp_loopback_id=dict(type="int", range_max=1023, default=""), + underlay_mcast_ip=dict(type="str", default=""), + overlay_mcast_group=dict(type="str", default=""), + trm_bgw_msite=dict(type="bool", default=False), + adv_host_routes=dict(type="bool", default=False), + adv_default_routes=dict(type="bool", default=True), + static_default_route=dict(type="bool", default=True), + bgp_password=dict(type="str", default=""), + bgp_passwd_encrypt=dict(type="int", default=3, choices=[3, 7]), + netflow_enable=dict(type="bool", default=False), + nf_monitor=dict(type="str", default=""), + disable_rt_auto=dict(type="bool", default=False), + import_vpn_rt=dict(type="str", default=""), + export_vpn_rt=dict(type="str", default=""), + import_evpn_rt=dict(type="str", default=""), + export_evpn_rt=dict(type="str", default=""), + import_mvpn_rt=dict(type="str", default=""), + export_mvpn_rt=dict(type="str", default=""), ) att_spec = dict( ip_address=dict(required=True, type="str"), @@ -1979,6 +2515,37 @@ def validate_input(self): service_vrf_template=dict(type="str", default=None), attach=dict(type="list"), deploy=dict(type="bool"), + vrf_vlan_name=dict(type="str", default=""), + vrf_intf_desc=dict(type="str", default=""), + vrf_description=dict(type="str", default=""), + vrf_int_mtu=dict(type="int", range_min=68, range_max=9216, default=9216), + loopback_route_tag=dict(type="int", default=12345, range_max=4294967295), + redist_direct_rmap=dict(type="str", default="FABRIC-RMAP-REDIST-SUBNET"), + max_bgp_paths=dict(type="int", range_min=1, range_max=64, default=1), + max_ibgp_paths=dict(type="int", range_min=1, range_max=64, default=2), + ipv6_linklocal_enable=dict(type="bool", default=True), + trm_enable=dict(type="bool", default=False), + no_rp=dict(type="bool", default=False), + rp_external=dict(type="bool", default=False), + rp_address=dict(type="str", default=""), + rp_loopback_id=dict(type="int", range_max=1023, default=""), + underlay_mcast_ip=dict(type="str", default=""), + overlay_mcast_group=dict(type="str", default=""), + trm_bgw_msite=dict(type="bool", default=False), + adv_host_routes=dict(type="bool", default=False), + adv_default_routes=dict(type="bool", default=True), + static_default_route=dict(type="bool", default=True), + bgp_password=dict(type="str", default=""), + bgp_passwd_encrypt=dict(type="int", default=3, choices=[3, 7]), + netflow_enable=dict(type="bool", default=False), + nf_monitor=dict(type="str", default=""), + disable_rt_auto=dict(type="bool", default=False), + import_vpn_rt=dict(type="str", default=""), + export_vpn_rt=dict(type="str", default=""), + import_evpn_rt=dict(type="str", default=""), + export_evpn_rt=dict(type="str", default=""), + import_mvpn_rt=dict(type="str", default=""), + export_mvpn_rt=dict(type="str", default=""), ) att_spec = dict( ip_address=dict(required=True, type="str"), From cd2ddb6324d2d0fdac4c84567df0844bac5fdb48 Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Wed, 22 Feb 2023 11:36:10 +0530 Subject: [PATCH 2/8] Doc commit --- docs/cisco.dcnm.dcnm_network_module.rst | 200 +++++++- docs/cisco.dcnm.dcnm_vrf_module.rst | 577 ++++++++++++++++++++++++ 2 files changed, 776 insertions(+), 1 deletion(-) diff --git a/docs/cisco.dcnm.dcnm_network_module.rst b/docs/cisco.dcnm.dcnm_network_module.rst index 6d9ec7c3b..a95b1e6a5 100644 --- a/docs/cisco.dcnm.dcnm_network_module.rst +++ b/docs/cisco.dcnm.dcnm_network_module.rst @@ -300,6 +300,22 @@ Parameters
Gateway with subnet for the network
+ + + +
+ gw_ipv6_subnet + +
+ string +
+ + + + +
IPv6 Gateway with prefix for the network
+ + @@ -316,6 +332,24 @@ Parameters
Description for the interface
+ + + +
+ intfvlan_nf_monitor + +
+ string +
+ + + + +
Interface Vlan Netflow Monitor
+
Applicable only if 'Layer 2 Only' is not enabled. Provide monitor name defined in fabric setting for Layer 3 Record
+
Netflow configs are supported on NDFC only
+ + @@ -337,6 +371,26 @@ Parameters
If specified as true, VRF Name(vrf_name) should not be specified or can be specified as ""
+ + + +
+ l3gw_on_border + +
+ boolean +
+ + + + + +
Enable L3 Gateway on Border
+ + @@ -438,6 +492,48 @@ Parameters
Name of the config template to be used
+ + + +
+ netflow_enable + +
+ boolean +
+ + + + + +
Enable Netflow
+
Netflow is supported only if it is enabled on fabric
+
Netflow configs are supported on NDFC only
+ + + + + +
+ route_target_both + +
+ boolean +
+ + + + + +
Enable both L2 VNI Route-Target
+ + @@ -455,6 +551,90 @@ Parameters
Routing Tag for the network profile
+ + + +
+ secondary_ip_gw1 + +
+ string +
+ + + + +
IP address with subnet for secondary gateway 1
+ + + + + +
+ secondary_ip_gw2 + +
+ string +
+ + + + +
IP address with subnet for secondary gateway 2
+ + + + + +
+ secondary_ip_gw3 + +
+ string +
+ + + + +
IP address with subnet for secondary gateway 3
+ + + + + +
+ secondary_ip_gw4 + +
+ string +
+ + + + +
IP address with subnet for secondary gateway 4
+ + + + + +
+ trm_enable + +
+ boolean +
+ + + + + +
Enable Tenant Routed Multicast
+ + @@ -489,6 +669,24 @@ Parameters
if > 32 chars enable, system vlan long-name on switch
+ + + +
+ vlan_nf_monitor + +
+ string +
+ + + + +
Vlan Netflow Monitor
+
Provide monitor name defined in fabric setting for Layer 3 Record
+
Netflow configs are supported on NDFC only
+ + @@ -554,7 +752,7 @@ Parameters Examples -------- -.. code-block:: yaml+jinja +.. code-block:: yaml # This module supports the following states: # diff --git a/docs/cisco.dcnm.dcnm_vrf_module.rst b/docs/cisco.dcnm.dcnm_vrf_module.rst index 25590ebbc..407d1e63e 100644 --- a/docs/cisco.dcnm.dcnm_vrf_module.rst +++ b/docs/cisco.dcnm.dcnm_vrf_module.rst @@ -53,6 +53,46 @@ Parameters + +
+ adv_default_routes + +
+ boolean +
+ + + + + +
Flag to Control Advertisement of Default Route Internally
+ + + + + +
+ adv_host_routes + +
+ boolean +
+ + + + + +
Flag to Control Advertisement of /32 and /128 Routes to Edge Routers
+ + + +
attach @@ -264,6 +304,43 @@ Parameters + + + +
+ bgp_passwd_encrypt + +
+ integer +
+ + + + Default:
3
+ + +
VRF Lite BGP Key Encryption Type
+ + + + + +
+ bgp_password + +
+ string +
+ + + + +
VRF Lite BGP neighbor password
+ + @@ -288,6 +365,362 @@ Parameters
Attachments specified in the playbook will always be created in DCNM. This knob, when set to "True", will deploy the attachment in DCNM, by pushing the configs to switch. If set to "False", the attachments will be created in DCNM, but will not be deployed
+ + + +
+ disable_rt_auto + +
+ boolean +
+ + + + + +
Disable RT Auto-Generate
+
supported on NDFC only
+ + + + + +
+ export_evpn_rt + +
+ list +
+ + + Default:
[]
+ + +
EVPN routes to export
+
supported on NDFC only
+
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+ + + + + +
+ export_mvpn_rt + +
+ list +
+ + + Default:
[]
+ + +
MVPN routes to export
+
supported on NDFC only
+
Can be configured only when TRM is enabled
+
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+ + + + + +
+ export_vpn_rt + +
+ string +
+ + + + +
VPN routes to export
+
supported on NDFC only
+
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+ + + + + +
+ import_evpn_rt + +
+ list +
+ + + Default:
[]
+ + +
EVPN routes to import
+
supported on NDFC only
+
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+ + + + + +
+ import_mvpn_rt + +
+ list +
+ + + Default:
[]
+ + +
MVPN routes to import
+
supported on NDFC only
+
Can be configured only when TRM is enabled
+
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+ + + + + +
+ import_vpn_rt + +
+ list +
+ + + Default:
[]
+ + +
VPN routes to import
+
supported on NDFC only
+
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+ + + + + +
+ ipv6_linklocal_enable + +
+ boolean +
+ + + + + +
Enable IPv6 link-local Option
+ + + + + +
+ loopback_route_tag + +
+ integer +
+ + + Default:
12345
+ + +
Loopback Routing Tag
+ + + + + +
+ max_bgp_paths + +
+ integer +
+ + + Default:
1
+ + +
Max BGP Paths
+ + + + + +
+ max_ibgp_paths + +
+ integer +
+ + + Default:
2
+ + +
Max iBGP Paths
+ + + + + +
+ netflow_enable + +
+ boolean +
+ + + + + +
Enable netflow on VRF-LITE Sub-interface
+
Netflow is supported only if it is enabled on fabric
+
Netflow configs are supported on NDFC only
+ + + + + +
+ nf_monitor + +
+ string +
+ + + + +
Netflow Monitor
+
Netflow configs are supported on NDFC only
+ + + + + +
+ no_rp + +
+ boolean +
+ + + + + +
No RP, only SSM is used
+
supported on NDFC only
+ + + + + +
+ overlay_mcast_group + +
+ string +
+ + + + +
Underlay IPv4 Multicast group (224.0.0.0/4 to 239.255.255.255/4)
+
Can be configured only when TRM is enabled
+ + + + + +
+ redist_direct_rmap + +
+ string +
+ + + Default:
"FABRIC-RMAP-REDIST-SUBNET"
+ + +
Redistribute Direct Route Map
+ + + + + +
+ rp_address + +
+ string +
+ + + + +
IPv4 Address of RP
+
Can be configured only when TRM is enabled
+ + + + + +
+ rp_external + +
+ boolean +
+ + + + + +
Specifies if RP is external to the fabric
+
Can be configured only when TRM is enabled
+ + + + + +
+ rp_loopback_id + +
+ integer +
+ + + + +
loopback ID of RP
+
Can be configured only when TRM is enabled
+ + @@ -305,6 +738,84 @@ Parameters
Service vrf template
+ + + +
+ static_default_route + +
+ boolean +
+ + + + + +
Flag to Control Static Default Route Configuration
+ + + + + +
+ trm_bgw_msite + +
+ boolean +
+ + + + + +
Enable TRM on Border Gateway Multisite
+
Can be configured only when TRM is enabled
+ + + + + +
+ trm_enable + +
+ boolean +
+ + + + + +
Enable Tenant Routed Multicast
+ + + + + +
+ underlay_mcast_ip + +
+ string +
+ + + + +
Underlay IPv4 Multicast Address
+
Can be configured only when TRM is enabled
+ + @@ -322,6 +833,22 @@ Parameters
If not specified in the playbook, DCNM will auto-select an available vlan_id
+ + + +
+ vrf_description + +
+ string +
+ + + + +
VRF Description
+ + @@ -355,6 +882,39 @@ Parameters
ID of the vrf being managed
+ + + +
+ vrf_int_mtu + +
+ integer +
+ + + Default:
9216
+ + +
VRF interface MTU
+ + + + + +
+ vrf_intf_desc + +
+ string +
+ + + + +
VRF Intf Description
+ + @@ -389,6 +949,23 @@ Parameters
Name of the config template to be used
+ + + +
+ vrf_vlan_name + +
+ string +
+ + + + +
VRF Vlan Name
+
if > 32 chars enable:system vlan long-name
+ + From 15ce97a661736682079be6e1871ffa86f08e361b Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Fri, 10 Mar 2023 15:50:10 +0530 Subject: [PATCH 3/8] UT/IT commit for network and vrf --- docs/cisco.dcnm.dcnm_network_module.rst | 2 +- docs/cisco.dcnm.dcnm_vrf_module.rst | 34 +- plugins/modules/dcnm_network.py | 7 +- plugins/modules/dcnm_vrf.py | 21 +- .../dcnm_network/tests/dcnm/query.yaml | 4 +- .../dcnm_network/tests/dcnm/replaced.yaml | 12 +- .../self-contained-tests/deleted_net_all.yaml | 104 +++ .../self-contained-tests/merged_net_all.yaml | 74 ++ .../overridden_net_all.yaml | 133 ++++ .../replaced_net_all.yaml | 129 +++ .../targets/dcnm_vrf/tests/dcnm/merged.yaml | 16 + .../self-contained-tests/deleted_vrf_all.yaml | 115 +++ .../self-contained-tests/merged_vrf_all.yaml | 97 +++ .../overridden_vrf_all.yaml | 165 ++++ .../replaced_vrf_all.yaml | 148 ++++ .../unit/modules/dcnm/fixtures/dcnm_net.json | 555 +++++++++++++ .../unit/modules/dcnm/fixtures/dcnm_vrf.json | 22 +- tests/unit/modules/dcnm/test_dcnm_net.py | 738 ++++++++++++++++++ tests/unit/modules/dcnm/test_dcnm_vrf.py | 68 +- 19 files changed, 2398 insertions(+), 46 deletions(-) create mode 100644 tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/deleted_net_all.yaml create mode 100644 tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/merged_net_all.yaml create mode 100644 tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/overridden_net_all.yaml create mode 100644 tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/replaced_net_all.yaml create mode 100644 tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/deleted_vrf_all.yaml create mode 100644 tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/merged_vrf_all.yaml create mode 100644 tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/overridden_vrf_all.yaml create mode 100644 tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/replaced_vrf_all.yaml create mode 100644 tests/unit/modules/dcnm/fixtures/dcnm_net.json create mode 100644 tests/unit/modules/dcnm/test_dcnm_net.py diff --git a/docs/cisco.dcnm.dcnm_network_module.rst b/docs/cisco.dcnm.dcnm_network_module.rst index a95b1e6a5..057b2f983 100644 --- a/docs/cisco.dcnm.dcnm_network_module.rst +++ b/docs/cisco.dcnm.dcnm_network_module.rst @@ -136,7 +136,7 @@ Parameters ports
- string + list / required
diff --git a/docs/cisco.dcnm.dcnm_vrf_module.rst b/docs/cisco.dcnm.dcnm_vrf_module.rst index 407d1e63e..3e629d1e9 100644 --- a/docs/cisco.dcnm.dcnm_vrf_module.rst +++ b/docs/cisco.dcnm.dcnm_vrf_module.rst @@ -316,13 +316,13 @@ Parameters
    Choices: -
  • 3 (3DES)
  • -
  • 7 (Cisco)
  • +
  • 3 ←
  • +
  • 7
- Default:
3
VRF Lite BGP Key Encryption Type
+
Allowed values are 3 (3DES) and 7 (Cisco)
@@ -339,6 +339,7 @@ Parameters
VRF Lite BGP neighbor password
+
Password should be in Hex string format
@@ -393,16 +394,15 @@ Parameters export_evpn_rt
- list + string
- Default:
[]
EVPN routes to export
supported on NDFC only
-
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+
Use ',' to separate multiple route-targets
@@ -412,17 +412,16 @@ Parameters export_mvpn_rt
- list + string
- Default:
[]
MVPN routes to export
supported on NDFC only
Can be configured only when TRM is enabled
-
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+
Use ',' to separate multiple route-targets
@@ -440,7 +439,7 @@ Parameters
VPN routes to export
supported on NDFC only
-
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+
Use ',' to separate multiple route-targets
@@ -450,16 +449,15 @@ Parameters import_evpn_rt
- list + string
- Default:
[]
EVPN routes to import
supported on NDFC only
-
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+
Use ',' to separate multiple route-targets
@@ -469,17 +467,16 @@ Parameters import_mvpn_rt
- list + string
- Default:
[]
MVPN routes to import
supported on NDFC only
Can be configured only when TRM is enabled
-
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+
Use ',' to separate multiple route-targets
@@ -489,16 +486,15 @@ Parameters import_vpn_rt
- list + string
- Default:
[]
VPN routes to import
supported on NDFC only
-
{"Use ',' to separate multiple route-targets(eg": '1:1,2:2)'}
+
Use ',' to separate multiple route-targets
diff --git a/plugins/modules/dcnm_network.py b/plugins/modules/dcnm_network.py index a5003f57c..7f582b617 100644 --- a/plugins/modules/dcnm_network.py +++ b/plugins/modules/dcnm_network.py @@ -246,7 +246,7 @@ ports: description: - List of switch interfaces where the network will be attached - type: str + type: list required: true deploy: description: @@ -483,7 +483,7 @@ class DcnmNetwork: "GET_NET_ID": "/rest/managed-pool/fabrics/{}/segments/ids", "GET_NET": "/rest/top-down/fabrics/{}/networks", "GET_NET_NAME": "/rest/top-down/fabrics/{}/networks/{}", - "GET_VLAN": "/rest/resource-manager/vlan/{}?vlanUsageType=TOP_DOWN_VRF_VLAN", + "GET_VLAN": "/rest/resource-manager/vlan/{}?vlanUsageType=TOP_DOWN_NETWORK_VLAN", }, 12: { "GET_VRF": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{}/vrfs", @@ -492,7 +492,7 @@ class DcnmNetwork: "GET_NET_ID": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{}/netinfo", "GET_NET": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{}/networks", "GET_NET_NAME": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{}/networks/{}", - "GET_VLAN": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/resource-manager/vlan/{}?vlanUsageType=TOP_DOWN_VRF_VLAN", + "GET_VLAN": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/resource-manager/vlan/{}?vlanUsageType=TOP_DOWN_NETWORK_VLAN", }, } @@ -502,7 +502,6 @@ def __init__(self, module): self.fabric = module.params["fabric"] self.config = copy.deepcopy(module.params.get("config")) self.check_mode = False - self.conn = Connection(module._socket_path) self.have_create = [] self.want_create = [] self.diff_create = [] diff --git a/plugins/modules/dcnm_vrf.py b/plugins/modules/dcnm_vrf.py index 7346db85a..33f9d9079 100644 --- a/plugins/modules/dcnm_vrf.py +++ b/plugins/modules/dcnm_vrf.py @@ -208,6 +208,7 @@ bgp_password: description: - VRF Lite BGP neighbor password + - Password should be in Hex string format type: str required: false bgp_passwd_encrypt: @@ -1164,16 +1165,16 @@ def get_have(self): } if self.dcnm_version > 11: - t_conf.update(isRPAbsent = json_to_dict.get("isRPAbsent")) - t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW")) - t_conf.update(NETFLOW_MONITOR = json_to_dict.get("NETFLOW_MONITOR")) - t_conf.update(disableRtAuto = json_to_dict.get("disableRtAuto")) - t_conf.update(routeTargetImport = json_to_dict.get("routeTargetImport")) - t_conf.update(routeTargetExport = json_to_dict.get("routeTargetExport")) - t_conf.update(routeTargetImportEvpn = json_to_dict.get("routeTargetImportEvpn")) - t_conf.update(routeTargetExportEvpn = json_to_dict.get("routeTargetExportEvpn")) - t_conf.update(routeTargetImportMvpn = json_to_dict.get("routeTargetImportMvpn")) - t_conf.update(routeTargetExportMvpn = json_to_dict.get("routeTargetExportMvpn")) + t_conf.update(isRPAbsent = json_to_dict.get("isRPAbsent", False)) + t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW", False)) + t_conf.update(NETFLOW_MONITOR = json_to_dict.get("NETFLOW_MONITOR", "")) + t_conf.update(disableRtAuto = json_to_dict.get("disableRtAuto", False)) + t_conf.update(routeTargetImport = json_to_dict.get("routeTargetImport", "")) + t_conf.update(routeTargetExport = json_to_dict.get("routeTargetExport", "")) + t_conf.update(routeTargetImportEvpn = json_to_dict.get("routeTargetImportEvpn", "")) + t_conf.update(routeTargetExportEvpn = json_to_dict.get("routeTargetExportEvpn", "")) + t_conf.update(routeTargetImportMvpn = json_to_dict.get("routeTargetImportMvpn", "")) + t_conf.update(routeTargetExportMvpn = json_to_dict.get("routeTargetExportMvpn", "")) vrf.update({"vrfTemplateConfig": json.dumps(t_conf)}) del vrf["vrfStatus"] diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml index 0b04d2d08..36f765540 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/query.yaml @@ -44,7 +44,7 @@ gw_ip_subnet: '192.168.30.1/24' attach: - ip_address: "{{ ansible_switch1 }}" - ports: [Ethernet1/2, Ethernet1/3] + ports: ["{{ ansible_sw1_int1 }}", "{{ ansible_sw1_int2 }}"] deploy: true - net_name: ansible-net12 vrf_name: Tenant-2 @@ -55,7 +55,7 @@ gw_ip_subnet: '192.168.40.1/24' attach: - ip_address: "{{ ansible_switch2 }}" - ports: [Ethernet1/12, Ethernet1/13] + ports: ["{{ ansible_sw2_int1 }}", "{{ ansible_sw2_int2 }}"] deploy: true deploy: false register: result diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/replaced.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/replaced.yaml index 9f17ad8ec..8a7600d05 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/replaced.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/replaced.yaml @@ -115,14 +115,14 @@ deploy: true register: result -- name: Query fabric state until networkStatus transitions to DEPLOYED state +- name: Query fabric state until networkStatus transitions to NA 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')" + - "query_result.response[0].parent.networkStatus is search('NA')" + - "query_result.response[1].parent.networkStatus is search('NA')" retries: 30 delay: 2 @@ -354,14 +354,14 @@ dhcp_srvr3_vrf: three register: result -- name: Query fabric state until networkStatus transitions to DEPLOYED state +- name: Query fabric state until networkStatus transitions to NA 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')" + - "query_result.response[0].parent.networkStatus is search('NA')" + - "query_result.response[1].parent.networkStatus is search('NA')" retries: 30 delay: 2 diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/deleted_net_all.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/deleted_net_all.yaml new file mode 100644 index 000000000..3644901f0 --- /dev/null +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/deleted_net_all.yaml @@ -0,0 +1,104 @@ +############################################## +## MERGED ## +############################################## + +- name: DELETED_ALL - Create New Network with many params + cisco.dcnm.dcnm_network: &conf + fabric: "{{ test_fabric }}" + state: merged + config: + - net_name: ansible-net13 + vrf_name: Tenant-1 + net_id: 7005 + net_template: Default_Network_Universal + net_extension_template: Default_Network_Extension_Universal + vlan_id: 1500 + gw_ip_subnet: '192.168.30.1/24' + gw_ipv6_subnet: '2001::1/64' + secondary_ip_gw1: '192.0.2.1/24' + secondary_ip_gw2: '192.1.2.1/24' + secondary_ip_gw3: '192.2.2.1/24' + secondary_ip_gw4: '192.3.2.1/24' + route_target_both: true + l3gw_on_border: true + attach: + - ip_address: "{{ ansible_switch1 }}" + ports: ["{{ ansible_sw1_int1 }}", "{{ ansible_sw1_int2 }}"] + deploy: true + deploy: true + register: result + +- name: DELETED_ALL - 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')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" + retries: 5 + delay: 2 + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - '(result.response[1].DATA|dict2items)[0].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - '"{{ 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"' + +- name: DELETED_ALL - conf - Idempotence + cisco.dcnm.dcnm_network: *conf + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + +- name: DELETED_ALL - setup - Delete any existing networks + cisco.dcnm.dcnm_network: &conf1 + fabric: "{{ test_fabric }}" + state: deleted + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - 'result.response[0].MESSAGE == "OK"' + - 'result.response[1].MESSAGE == "OK"' + - 'result.response[2].MESSAGE == "OK"' + - 'result.response[2].METHOD == "DELETE"' + - '(result.response[0].DATA|dict2items)[0].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == false' + - '"{{ ansible_switch1 }}" in result.diff[0].attach[0].ip_address' + - 'result.diff[0].net_name == "ansible-net13"' + +- name: DELETED - conf - Idempotence + cisco.dcnm.dcnm_network: *conf1 + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + - 'result.diff|length == 0' + +############################################## +## CLEAN-UP ## +############################################## + +- name: DELETED_ALL - setup - Clean up any existing networks + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: deleted diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/merged_net_all.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/merged_net_all.yaml new file mode 100644 index 000000000..7c8af046a --- /dev/null +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/merged_net_all.yaml @@ -0,0 +1,74 @@ +############################################## +## MERGED ## +############################################## + +- name: MERGED_ALL - Create New Network with many params + cisco.dcnm.dcnm_network: &conf + fabric: "{{ test_fabric }}" + state: merged + config: + - net_name: ansible-net13 + vrf_name: Tenant-1 + net_id: 7005 + net_template: Default_Network_Universal + net_extension_template: Default_Network_Extension_Universal + vlan_id: 1500 + gw_ip_subnet: '192.168.30.1/24' + gw_ipv6_subnet: '2001::1/64' + secondary_ip_gw1: '192.0.2.1/24' + secondary_ip_gw2: '192.1.2.1/24' + secondary_ip_gw3: '192.2.2.1/24' + secondary_ip_gw4: '192.3.2.1/24' + route_target_both: true + l3gw_on_border: true + attach: + - ip_address: "{{ ansible_switch1 }}" + ports: ["{{ ansible_sw1_int1 }}", "{{ ansible_sw1_int2 }}"] + deploy: true + deploy: true + register: result + +- name: MERGED_ALL - 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')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" + retries: 5 + delay: 2 + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - '(result.response[1].DATA|dict2items)[0].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - '"{{ 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"' + +- name: MERGED_ALL - conf - Idempotence + cisco.dcnm.dcnm_network: *conf + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + +############################################## +## CLEAN-UP ## +############################################## + +- name: MERGED_ALL - setup - Clean up any existing networks + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: deleted + diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/overridden_net_all.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/overridden_net_all.yaml new file mode 100644 index 000000000..a8c7ad317 --- /dev/null +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/overridden_net_all.yaml @@ -0,0 +1,133 @@ +############################################## +## MERGED ## +############################################## + +- name: OVERRIDDEN_ALL - Create New Network with many params + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: merged + config: + - net_name: ansible-net13 + vrf_name: Tenant-1 + net_id: 7005 + net_template: Default_Network_Universal + net_extension_template: Default_Network_Extension_Universal + vlan_id: 1500 + gw_ip_subnet: '192.168.30.1/24' + gw_ipv6_subnet: '2001::1/64' + secondary_ip_gw1: '192.0.2.1/24' + secondary_ip_gw2: '192.1.2.1/24' + secondary_ip_gw3: '192.2.2.1/24' + secondary_ip_gw4: '192.3.2.1/24' + route_target_both: true + l3gw_on_border: true + attach: + - ip_address: "{{ ansible_switch1 }}" + ports: ["{{ ansible_sw1_int1 }}", "{{ ansible_sw1_int2 }}"] + deploy: true + deploy: true + register: result + +- name: OVERRIDDEN_ALL 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')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" + retries: 5 + delay: 2 + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - '(result.response[1].DATA|dict2items)[0].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - '"{{ 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"' + +- name: OVERRIDDEN_ALL - Override Network with many params + cisco.dcnm.dcnm_network: &conf + fabric: "{{ test_fabric }}" + state: overridden + config: + - net_name: ansible-net14 + vrf_name: Tenant-1 + net_id: 7005 + net_template: Default_Network_Universal + net_extension_template: Default_Network_Extension_Universal + vlan_id: 1500 + gw_ip_subnet: '192.168.30.1/24' + gw_ipv6_subnet: '2004::1/64' + secondary_ip_gw1: '192.0.3.1/24' + secondary_ip_gw2: '192.1.3.1/24' + secondary_ip_gw3: '192.2.3.1/24' + secondary_ip_gw4: '192.3.3.1/24' + route_target_both: true + l3gw_on_border: true + attach: + - ip_address: "{{ ansible_switch2 }}" + ports: ["{{ ansible_sw1_int1 }}", "{{ ansible_sw1_int2 }}"] + deploy: true + deploy: true + register: result + +- name: OVERRIDDEN_ALL - 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-net14')" + - "query_result.response[0].parent.networkId is search('7005')" + - "query_result.response[0].parent.vrf is search('Tenant-1')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" + retries: 5 + delay: 2 + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - 'result.response[3].RETURN_CODE == 200' + - 'result.response[4].RETURN_CODE == 200' + - 'result.response[5].RETURN_CODE == 200' + - '(result.response[0].DATA|dict2items)[0].value == "SUCCESS"' + - '(result.response[4].DATA|dict2items)[0].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - 'result.diff[1].attach[0].deploy == false' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[0].ip_address' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[1].attach[0].ip_address' + - 'result.diff[0].net_name == "ansible-net14"' + - 'result.diff[1].net_name == "ansible-net13"' + - 'result.diff[0].net_id == 7005' + - 'result.diff[0].vrf_name == "Tenant-1"' + +- name: OVERRIDDEN_ALL - conf - Idempotence + cisco.dcnm.dcnm_network: *conf + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + +############################################## +## CLEAN-UP ## +############################################## + +- name: OVERRIDDEN_ALL - setup - Clean up any existing networks + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: deleted + diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/replaced_net_all.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/replaced_net_all.yaml new file mode 100644 index 000000000..b1ce2baa5 --- /dev/null +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/replaced_net_all.yaml @@ -0,0 +1,129 @@ +############################################## +## MERGED ## +############################################## + +- name: REPLACE_ALL - Create New Network with many params + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: merged + config: + - net_name: ansible-net13 + vrf_name: Tenant-1 + net_id: 7005 + net_template: Default_Network_Universal + net_extension_template: Default_Network_Extension_Universal + vlan_id: 1500 + gw_ip_subnet: '192.168.30.1/24' + gw_ipv6_subnet: '2001::1/64' + secondary_ip_gw1: '192.0.2.1/24' + secondary_ip_gw2: '192.1.2.1/24' + secondary_ip_gw3: '192.2.2.1/24' + secondary_ip_gw4: '192.3.2.1/24' + route_target_both: true + l3gw_on_border: true + attach: + - ip_address: "{{ ansible_switch1 }}" + ports: ["{{ ansible_sw1_int1 }}", "{{ ansible_sw1_int2 }}"] + deploy: true + deploy: true + register: result + +- name: REPLACE_ALL - 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')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" + retries: 5 + delay: 2 + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - '(result.response[1].DATA|dict2items)[0].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - '"{{ 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"' + +- name: REPLACE_ALL - Replace Network with many params + cisco.dcnm.dcnm_network: &conf + fabric: "{{ test_fabric }}" + state: replaced + config: + - net_name: ansible-net13 + vrf_name: Tenant-1 + net_id: 7005 + net_template: Default_Network_Universal + net_extension_template: Default_Network_Extension_Universal + vlan_id: 1500 + gw_ip_subnet: '192.168.30.1/24' + gw_ipv6_subnet: '2004::1/64' + secondary_ip_gw1: '192.0.3.1/24' + secondary_ip_gw2: '192.1.3.1/24' + secondary_ip_gw3: '192.2.3.1/24' + secondary_ip_gw4: '192.3.3.1/24' + route_target_both: true + l3gw_on_border: true + attach: + - ip_address: "{{ ansible_switch2 }}" + ports: ["{{ ansible_sw1_int1 }}", "{{ ansible_sw1_int2 }}"] + deploy: true + deploy: true + register: result + +- name: REPLACE_ALL - 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')" + - "query_result.response[0].parent.networkStatus is search('DEPLOYED|PENDING')" + retries: 5 + delay: 2 + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - '(result.response[1].DATA|dict2items)[0].value == "SUCCESS"' + - '(result.response[1].DATA|dict2items)[1].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - 'result.diff[0].attach[1].deploy == false' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[0].ip_address' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' + - 'result.diff[0].net_name == "ansible-net13"' + - 'result.diff[0].net_id == 7005' + - 'result.diff[0].vrf_name == "Tenant-1"' + +- name: REPLACE_ALL - conf - Idempotence + cisco.dcnm.dcnm_network: *conf + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + +############################################## +## CLEAN-UP ## +############################################## + +- name: REPLACE_ALL - setup - Clean up any existing networks + cisco.dcnm.dcnm_network: + fabric: "{{ test_fabric }}" + state: deleted + diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/merged.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/merged.yaml index 5e686f501..f9688073e 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/merged.yaml +++ b/tests/integration/targets/dcnm_vrf/tests/dcnm/merged.yaml @@ -65,6 +65,10 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' +- name: MERGED - sleep for 40 seconds for DCNM to completely update the state + wait_for: + timeout: 40 + - name: MERGED - conf1 - Idempotence cisco.dcnm.dcnm_vrf: *conf register: result @@ -114,6 +118,10 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' +- name: MERGED - sleep for 40 seconds for DCNM to completely update the state + wait_for: + timeout: 40 + - name: MERGED - conf2 - Idempotence cisco.dcnm.dcnm_vrf: *conf2 register: result @@ -172,6 +180,10 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' +- name: MERGED - sleep for 40 seconds for DCNM to completely update the state + wait_for: + timeout: 40 + - name: MERGED - conf3 - Idempotence cisco.dcnm.dcnm_vrf: *conf3 register: result @@ -224,6 +236,10 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' +- name: MERGED - sleep for 40 seconds for DCNM to completely update the state + wait_for: + timeout: 40 + - name: MERGED - Create, Attach and Deploy new VRF - Update with incorrect VRF ID. cisco.dcnm.dcnm_vrf: fabric: "{{ ansible_it_fabric }}" diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/deleted_vrf_all.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/deleted_vrf_all.yaml new file mode 100644 index 000000000..c12f8f01b --- /dev/null +++ b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/deleted_vrf_all.yaml @@ -0,0 +1,115 @@ +############################################## +## SETUP ## +############################################## + +- 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" + +- name: DELETE_ALL - Verify if fabric is deployed. + cisco.dcnm.dcnm_rest: + method: GET + path: "{{ rest_path }}" + register: result + +- assert: + that: + - 'result.response.DATA != None' + +- name: DELETE_ALL - Clean up any existing vrfs + cisco.dcnm.dcnm_vrf: + fabric: "{{ ansible_it_fabric }}" + state: deleted + +############################################### +### MERGED ## +############################################### + +- name: DELETE_ALL - Create, Attach and Deploy new VRF with all values + cisco.dcnm.dcnm_vrf: + fabric: "{{ ansible_it_fabric }}" + state: merged + config: + - vrf_name: ansible-vrf-int1 + vrf_id: 9008011 + vrf_template: Default_VRF_Universal + vrf_extension_template: Default_VRF_Extension_Universal + vlan_id: 500 + source: null + service_vrf_template: null + vrf_vlan_name: "test" + vrf_intf_desc: "test vrf interface" + vrf_description: "test vrf" + vrf_int_mtu: 2100 + loopback_route_tag: 54321 + max_bgp_paths: 6 + max_ibgp_paths: 7 + ipv6_linklocal_enable: false + adv_host_routes: true + adv_default_routes: false + static_default_route: false + bgp_password: "74657374" + bgp_passwd_encrypt: 7 + attach: + - ip_address: "{{ ansible_switch1 }}" + - ip_address: "{{ ansible_switch2 }}" + deploy: true + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - '(result.response[1].DATA|dict2items)[0].value == "SUCCESS"' + - '(result.response[1].DATA|dict2items)[1].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - 'result.diff[0].attach[1].deploy == true' + - '"{{ ansible_switch1 }}" or "{{ ansible_switch2 }}" in result.diff[0].attach[0].ip_address' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' + - 'result.diff[0].vrf_name == "ansible-vrf-int1"' + +- name: DELETE_ALL - sleep for 40 seconds for DCNM to completely update the state + wait_for: + timeout: 40 + +- name: DELETE_ALL - Clean existing vrfs + cisco.dcnm.dcnm_vrf: &conf + fabric: "{{ ansible_it_fabric }}" + state: deleted + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - '(result.response[0].DATA|dict2items)[0].value == "SUCCESS"' + - '(result.response[0].DATA|dict2items)[1].value == "SUCCESS"' + - '"{{ ansible_switch1 }}" or "{{ ansible_switch2 }}" in result.diff[0].attach[0].ip_address' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' + - 'result.diff[0].vrf_name == "ansible-vrf-int1"' + +- name: DELETE_ALL - conf - Idempotence + cisco.dcnm.dcnm_vrf: *conf + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + +############################################### +### CLEAN-UP ## +############################################### + +- name: DELETE_ALL - Clean up any existing vrfs + cisco.dcnm.dcnm_vrf: + fabric: "{{ ansible_it_fabric }}" + state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/merged_vrf_all.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/merged_vrf_all.yaml new file mode 100644 index 000000000..f6b9ab259 --- /dev/null +++ b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/merged_vrf_all.yaml @@ -0,0 +1,97 @@ +############################################## +## SETUP ## +############################################## + +- 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" + +- name: MERGED_ALL - Verify if fabric is deployed. + cisco.dcnm.dcnm_rest: + method: GET + path: "{{ rest_path }}" + register: result + +- assert: + that: + - 'result.response.DATA != None' + +- name: MERGED_ALL - Clean up any existing vrfs + cisco.dcnm.dcnm_vrf: + fabric: "{{ ansible_it_fabric }}" + state: deleted + +############################################### +### MERGED ## +############################################### + +- name: MERGED_ALL - Create, Attach and Deploy new VRF with all values + cisco.dcnm.dcnm_vrf: &conf + fabric: "{{ ansible_it_fabric }}" + state: merged + config: + - vrf_name: ansible-vrf-int1 + vrf_id: 9008011 + vrf_template: Default_VRF_Universal + vrf_extension_template: Default_VRF_Extension_Universal + vlan_id: 500 + source: null + service_vrf_template: null + vrf_vlan_name: "test" + vrf_intf_desc: "test vrf interface" + vrf_description: "test vrf" + vrf_int_mtu: 2100 + loopback_route_tag: 54321 + max_bgp_paths: 6 + max_ibgp_paths: 7 + ipv6_linklocal_enable: false + adv_host_routes: true + adv_default_routes: false + static_default_route: false + bgp_password: "74657374" + bgp_passwd_encrypt: 7 + attach: + - ip_address: "{{ ansible_switch1 }}" + - ip_address: "{{ ansible_switch2 }}" + deploy: true + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - '(result.response[1].DATA|dict2items)[0].value == "SUCCESS"' + - '(result.response[1].DATA|dict2items)[1].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - 'result.diff[0].attach[1].deploy == true' + - '"{{ ansible_switch1 }}" or "{{ ansible_switch2 }}" in result.diff[0].attach[0].ip_address' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' + - 'result.diff[0].vrf_name == "ansible-vrf-int1"' + +- name: MERGED_ALL - sleep for 40 seconds for DCNM to completely update the state + wait_for: + timeout: 40 + +- name: MERGED_ALL - conf - Idempotence + cisco.dcnm.dcnm_vrf: *conf + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + +############################################### +### CLEAN-UP ## +############################################### + +- name: MERGED_ALL - Clean up any existing vrfs + cisco.dcnm.dcnm_vrf: + fabric: "{{ ansible_it_fabric }}" + state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/overridden_vrf_all.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/overridden_vrf_all.yaml new file mode 100644 index 000000000..c963aaabb --- /dev/null +++ b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/overridden_vrf_all.yaml @@ -0,0 +1,165 @@ +############################################## +## SETUP ## +############################################## + +- 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" + +- name: OVERRIDDEN_ALL - Verify if fabric is deployed. + cisco.dcnm.dcnm_rest: + method: GET + path: "{{ rest_path }}" + register: result + +- assert: + that: + - 'result.response.DATA != None' + +- name: OVERRIDDEN_ALL - Clean up any existing vrfs + cisco.dcnm.dcnm_vrf: + fabric: "{{ ansible_it_fabric }}" + state: deleted + +############################################### +### MERGED ## +############################################### + +- name: OVERRIDDEN_ALL - Create, Attach and Deploy new VRF with all values + cisco.dcnm.dcnm_vrf: &conf + fabric: "{{ ansible_it_fabric }}" + state: merged + config: + - vrf_name: ansible-vrf-int1 + vrf_id: 9008011 + vrf_template: Default_VRF_Universal + vrf_extension_template: Default_VRF_Extension_Universal + vlan_id: 500 + source: null + service_vrf_template: null + vrf_vlan_name: "test" + vrf_intf_desc: "test vrf interface" + vrf_description: "test vrf" + vrf_int_mtu: 2100 + loopback_route_tag: 54321 + max_bgp_paths: 6 + max_ibgp_paths: 7 + ipv6_linklocal_enable: false + adv_host_routes: true + adv_default_routes: false + static_default_route: false + bgp_password: "74657374" + bgp_passwd_encrypt: 7 + attach: + - ip_address: "{{ ansible_switch1 }}" + - ip_address: "{{ ansible_switch2 }}" + deploy: true + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - '(result.response[1].DATA|dict2items)[0].value == "SUCCESS"' + - '(result.response[1].DATA|dict2items)[1].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - 'result.diff[0].attach[1].deploy == true' + - '"{{ ansible_switch1 }}" or "{{ ansible_switch2 }}" in result.diff[0].attach[0].ip_address' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' + - 'result.diff[0].vrf_name == "ansible-vrf-int1"' + +- name: OVERRIDDEN_ALL - sleep for 40 seconds for DCNM to completely update the state + wait_for: + timeout: 40 + +- name: OVERRIDDEN_ALL - conf - Idempotence + cisco.dcnm.dcnm_vrf: *conf + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + +- name: OVERRIDDEN_ALL - Override a existing VRF with a new one + cisco.dcnm.dcnm_vrf: &conf1 + fabric: "{{ ansible_it_fabric }}" + state: overridden + config: + - vrf_name: ansible-vrf-int2 + vrf_id: 9008011 + vrf_template: Default_VRF_Universal + vrf_extension_template: Default_VRF_Extension_Universal + vlan_id: 500 + source: null + service_vrf_template: null + vrf_vlan_name: "testvlan" + vrf_intf_desc: "test interface" + vrf_description: "vrf" + vrf_int_mtu: 2100 + loopback_route_tag: 54321 + max_bgp_paths: 6 + max_ibgp_paths: 7 + ipv6_linklocal_enable: true + adv_host_routes: true + adv_default_routes: false + static_default_route: false + bgp_password: "74657374" + bgp_passwd_encrypt: 7 + attach: + - ip_address: "{{ ansible_switch1 }}" + - ip_address: "{{ ansible_switch2 }}" + deploy: true + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - 'result.response[3].RETURN_CODE == 200' + - 'result.response[4].RETURN_CODE == 200' + - 'result.response[5].RETURN_CODE == 200' + - '(result.response[0].DATA|dict2items)[0].value == "SUCCESS"' + - '(result.response[0].DATA|dict2items)[1].value == "SUCCESS"' + - '(result.response[4].DATA|dict2items)[0].value == "SUCCESS"' + - '(result.response[4].DATA|dict2items)[1].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - 'result.diff[0].attach[1].deploy == true' + - 'result.diff[1].attach[0].deploy == false' + - 'result.diff[1].attach[1].deploy == false' + - '"{{ ansible_switch1 }}" or "{{ ansible_switch2 }}" in result.diff[0].attach[0].ip_address' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' + - '"{{ ansible_switch1 }}" or "{{ ansible_switch2 }}" in result.diff[1].attach[0].ip_address' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[1].attach[1].ip_address' + - 'result.diff[0].vrf_name == "ansible-vrf-int2"' + - 'result.diff[1].vrf_name == "ansible-vrf-int1"' + +- name: OVERRIDDEN_ALL - sleep for 40 seconds for DCNM to completely update the state + wait_for: + timeout: 40 + +- name: OVERRIDDEN_ALL - conf1 - Idempotence + cisco.dcnm.dcnm_vrf: *conf1 + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + +############################################### +### CLEAN-UP ## +############################################### + +- name: OVERRIDDEN_ALL - Clean up any existing vrfs + cisco.dcnm.dcnm_vrf: + fabric: "{{ ansible_it_fabric }}" + state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/replaced_vrf_all.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/replaced_vrf_all.yaml new file mode 100644 index 000000000..c00b5debd --- /dev/null +++ b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/replaced_vrf_all.yaml @@ -0,0 +1,148 @@ +############################################## +## SETUP ## +############################################## + +- 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" + +- name: REPLACED_ALL - Verify if fabric is deployed. + cisco.dcnm.dcnm_rest: + method: GET + path: "{{ rest_path }}" + register: result + +- assert: + that: + - 'result.response.DATA != None' + +- name: REPLACED_ALL - Clean up any existing vrfs + cisco.dcnm.dcnm_vrf: + fabric: "{{ ansible_it_fabric }}" + state: deleted + +############################################### +### MERGED ## +############################################### + +- name: REPLACED_ALL - Create, Attach and Deploy new VRF with all values + cisco.dcnm.dcnm_vrf: &conf + fabric: "{{ ansible_it_fabric }}" + state: merged + config: + - vrf_name: ansible-vrf-int1 + vrf_id: 9008011 + vrf_template: Default_VRF_Universal + vrf_extension_template: Default_VRF_Extension_Universal + vlan_id: 500 + source: null + service_vrf_template: null + vrf_vlan_name: "test" + vrf_intf_desc: "test vrf interface" + vrf_description: "test vrf" + vrf_int_mtu: 2100 + loopback_route_tag: 54321 + max_bgp_paths: 6 + max_ibgp_paths: 7 + ipv6_linklocal_enable: false + adv_host_routes: true + adv_default_routes: false + static_default_route: false + bgp_password: "74657374" + bgp_passwd_encrypt: 7 + attach: + - ip_address: "{{ ansible_switch1 }}" + - ip_address: "{{ ansible_switch2 }}" + deploy: true + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.response[2].RETURN_CODE == 200' + - '(result.response[1].DATA|dict2items)[0].value == "SUCCESS"' + - '(result.response[1].DATA|dict2items)[1].value == "SUCCESS"' + - 'result.diff[0].attach[0].deploy == true' + - 'result.diff[0].attach[1].deploy == true' + - '"{{ ansible_switch1 }}" or "{{ ansible_switch2 }}" in result.diff[0].attach[0].ip_address' + - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' + - 'result.diff[0].vrf_name == "ansible-vrf-int1"' + +- name: REPLACED_ALL - sleep for 40 seconds for DCNM to completely update the state + wait_for: + timeout: 40 + +- name: REPLACED_ALL - conf - Idempotence + cisco.dcnm.dcnm_vrf: *conf + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + +- name: REPLACED_ALL - Replace VRF with all values + cisco.dcnm.dcnm_vrf: &conf1 + fabric: "{{ ansible_it_fabric }}" + state: replaced + config: + - vrf_name: ansible-vrf-int1 + vrf_id: 9008011 + vrf_template: Default_VRF_Universal + vrf_extension_template: Default_VRF_Extension_Universal + vlan_id: 500 + source: null + service_vrf_template: null + vrf_vlan_name: "testvlan" + vrf_intf_desc: "test interface" + vrf_description: "vrf" + vrf_int_mtu: 2100 + loopback_route_tag: 54321 + max_bgp_paths: 6 + max_ibgp_paths: 7 + ipv6_linklocal_enable: true + adv_host_routes: true + adv_default_routes: false + static_default_route: false + bgp_password: "74657374" + bgp_passwd_encrypt: 7 + attach: + - ip_address: "{{ ansible_switch1 }}" + - ip_address: "{{ ansible_switch2 }}" + deploy: true + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.response[0].RETURN_CODE == 200' + - 'result.response[1].RETURN_CODE == 200' + - 'result.diff[0].vrf_name == "ansible-vrf-int1"' + +- name: REPLACED_ALL - sleep for 40 seconds for DCNM to completely update the state + wait_for: + timeout: 40 + +- name: REPLACED_ALL - conf1 - Idempotence + cisco.dcnm.dcnm_vrf: *conf1 + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.response|length == 0' + +############################################### +### CLEAN-UP ## +############################################### + +- name: REPLACED_ALL - Clean up any existing vrfs + cisco.dcnm.dcnm_vrf: + fabric: "{{ ansible_it_fabric }}" + state: deleted diff --git a/tests/unit/modules/dcnm/fixtures/dcnm_net.json b/tests/unit/modules/dcnm/fixtures/dcnm_net.json new file mode 100644 index 000000000..df426a4da --- /dev/null +++ b/tests/unit/modules/dcnm/fixtures/dcnm_net.json @@ -0,0 +1,555 @@ +{ + "mock_ip_sn" : { + "10.10.10.217": "9NN7E41N16A", + "10.10.10.218": "9YO9A29F27U", + "10.10.10.226": "XYZKSJHSMK3", + "10.10.10.227": "XYZKSJHSMK4", + "10.10.10.228": "XYZKSJHSMK5" + }, + "playbook_config" : [ + { + "net_name": "test_network", + "vrf_name": "ansible-vrf-int1", + "net_id": "9008011", + "net_template": "Default_Network_Universal", + "net_extension_template": "Default_Network_Extension_Universal", + "vlan_id": "202", + "gw_ip_subnet": "192.168.30.1/24", + "attach": [ + { + "ip_address": "10.10.10.217", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + }, + { + "ip_address": "10.10.10.218", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + } + ], + "deploy": true + } + ], + "playbook_config_incorrect_netid" : [ + { + "net_name": "test_network", + "vrf_name": "ansible-vrf-int1", + "net_id": "9008012", + "net_template": "Default_Network_Universal", + "net_extension_template": "Default_Network_Extension_Universal", + "vlan_id": "202", + "gw_ip_subnet": "192.168.30.1/24", + "attach": [ + { + "ip_address": "10.10.10.217", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + }, + { + "ip_address": "10.10.10.218", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + } + ], + "deploy": true + } + ], + "playbook_config_incorrect_vrf" : [ + { + "net_name": "test_network", + "vrf_name": "ansible-vrf-int2", + "net_id": "9008011", + "net_template": "Default_Network_Universal", + "net_extension_template": "Default_Network_Extension_Universal", + "vlan_id": "202", + "gw_ip_subnet": "192.168.30.1/24", + "attach": [ + { + "ip_address": "10.10.10.217", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + }, + { + "ip_address": "10.10.10.218", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + } + ], + "deploy": true + } + ], + "playbook_config_update" : [ + { + "net_name": "test_network", + "vrf_name": "ansible-vrf-int1", + "net_id": "9008011", + "net_template": "Default_Network_Universal", + "net_extension_template": "Default_Network_Extension_Universal", + "vlan_id": "203", + "gw_ip_subnet": "192.168.30.1/24", + "attach": [ + { + "ip_address": "10.10.10.226", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + }, + { + "ip_address": "10.10.10.227", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + } + ], + "deploy": true + } + ], + "playbook_config_replace" : [ + { + "net_name": "test_network", + "vrf_name": "ansible-vrf-int1", + "net_id": "9008011", + "net_template": "Default_Network_Universal", + "net_extension_template": "Default_Network_Extension_Universal", + "vlan_id": "203", + "gw_ip_subnet": "192.168.30.1/24", + "attach": [ + { + "ip_address": "10.10.10.218", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + }, + { + "ip_address": "10.10.10.226", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + } + ], + "deploy": true + } + ], + "playbook_config_replace_no_atch" : [ + { + "net_name": "test_network", + "vrf_name": "ansible-vrf-int1", + "net_id": "9008011", + "net_template": "Default_Network_Universal", + "net_extension_template": "Default_Network_Extension_Universal", + "vlan_id": "202", + "gw_ip_subnet": "192.168.30.1/24", + "deploy": true + } + ], + "playbook_config_override" : [ + { + "net_name": "test_network1", + "vrf_name": "ansible-vrf-int1", + "net_id": "9008012", + "net_template": "Default_Network_Universal", + "net_extension_template": "Default_Network_Extension_Universal", + "vlan_id": "303", + "gw_ip_subnet": "192.168.30.1/24", + "attach": [ + { + "ip_address": "10.10.10.218", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + }, + { + "ip_address": "10.10.10.226", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + } + ], + "deploy": true + } + ], + "playbook_config_novlan" : [ + { + "net_name": "test_network", + "vrf_name": "ansible-vrf-int1", + "net_id": "9008011", + "net_template": "Default_Network_Universal", + "net_extension_template": "Default_Network_Extension_Universal", + "gw_ip_subnet": "192.168.30.1/24", + "attach": [ + { + "ip_address": "10.10.10.217", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + }, + { + "ip_address": "10.10.10.218", + "ports": ["Ethernet1/13", "Ethernet1/14"], + "deploy": true + } + ], + "deploy": true + } + ], + "mock_vrf_object" : { + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200, + "DATA": [ + { + "fabric": "test_network", + "vrfName": "ansible-vrf-int1", + "vrfTemplate": "Default_VRF_Universal", + "vrfExtensionTemplate": "Default_VRF_Extension_Universal", + "vrfTemplateConfig": "{\"advertiseDefaultRouteFlag\":\"true\",\"vrfVlanId\":\"202\",\"isRPExternal\":\"false\",\"vrfDescription\":\"\",\"L3VniMcastGroup\":\"\",\"vrfSegmentId\":\"9008011\",\"maxBgpPaths\":\"1\",\"maxIbgpPaths\":\"2\",\"ipv6LinkLocalFlag\":\"true\",\"vrfRouteMap\":\"FABRIC-RMAP-REDIST-SUBNET\",\"configureStaticDefaultRouteFlag\":\"true\",\"tag\":\"12345\",\"rpAddress\":\"\",\"trmBGWMSiteEnabled\":\"false\",\"nveId\":\"1\",\"bgpPasswordKeyType\":\"3\",\"bgpPassword\":\"\",\"mtu\":\"9216\",\"multicastGroup\":\"\",\"advertiseHostRouteFlag\":\"false\",\"vrfVlanName\":\"\",\"trmEnabled\":\"false\",\"loopbackNumber\":\"\",\"asn\":\"34343\",\"vrfIntfDescription\":\"\",\"vrfName\":\"ansible-vrf-int1\"}", + "tenantName": null, + "vrfId": 9008011, + "serviceVrfTemplate": null, + "source": null, + "vrfStatus": "DEPLOYED" + } + ] + }, + "mock_net_object" : { + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200, + "DATA": [ + { + "fabric": "test_network", + "networkName": "test_network", + "displayName": "test_network", + "networkId": 9008011, + "networkTemplate": "Default_Network_Universal", + "networkExtensionTemplate": "Default_Network_Extension_Universal", + "networkTemplateConfig": "{\"secondaryGW3\":\"\",\"suppressArp\":\"false\",\"secondaryGW2\":\"\",\"secondaryGW1\":\"\",\"loopbackId\":\"\",\"enableL3OnBorder\":\"false\",\"networkName\":\"test_network\",\"enableIR\":\"false\",\"rtBothAuto\":\"false\",\"isLayer2Only\":\"false\",\"vrfDhcp3\":\"\",\"segmentId\":\"9008011\",\"vrfDhcp2\":\"\",\"dhcpServerAddr3\":\"\",\"gatewayIpV6Address\":\"\",\"dhcpServerAddr2\":\"\",\"dhcpServerAddr1\":\"\",\"tag\":\"12345\",\"nveId\":\"1\",\"vrfDhcp\":\"\",\"secondaryGW4\":\"\",\"vlanId\":\"202\",\"gatewayIpAddress\":\"192.168.30.1/24\",\"vlanName\":\"\",\"mtu\":\"\",\"intfDescription\":\"\",\"mcastGroup\":\"239.1.1.0\",\"trmEnabled\":\"false\",\"vrfName\":\"ansible-vrf-int1\"}", + "vrf": "ansible-vrf-int1", + "tenantName": null, + "serviceNetworkTemplate": null, + "source": null, + "interfaceGroups": null, + "networkStatus": "NA" + } + ] + }, + "mock_net_query_object" : { + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200, + "DATA": + { + "fabric": "test_network", + "networkName": "test_network", + "displayName": "test_network", + "networkId": 9008011, + "networkTemplate": "Default_Network_Universal", + "networkExtensionTemplate": "Default_Network_Extension_Universal", + "networkTemplateConfig": "{\"secondaryGW3\":\"\",\"suppressArp\":\"false\",\"secondaryGW2\":\"\",\"secondaryGW1\":\"\",\"loopbackId\":\"\",\"enableL3OnBorder\":\"false\",\"networkName\":\"test_network\",\"enableIR\":\"false\",\"rtBothAuto\":\"false\",\"isLayer2Only\":\"false\",\"vrfDhcp3\":\"\",\"segmentId\":\"9008011\",\"vrfDhcp2\":\"\",\"dhcpServerAddr3\":\"\",\"gatewayIpV6Address\":\"\",\"dhcpServerAddr2\":\"\",\"dhcpServerAddr1\":\"\",\"tag\":\"12345\",\"nveId\":\"1\",\"vrfDhcp\":\"\",\"secondaryGW4\":\"\",\"vlanId\":\"202\",\"gatewayIpAddress\":\"192.168.30.1/24\",\"vlanName\":\"\",\"mtu\":\"\",\"intfDescription\":\"\",\"mcastGroup\":\"239.1.1.0\",\"trmEnabled\":\"false\",\"vrfName\":\"ansible-vrf-int1\"}", + "vrf": "ansible-vrf-int1", + "tenantName": null, + "serviceNetworkTemplate": null, + "source": null, + "interfaceGroups": null, + "networkStatus": "NA" + } + }, + "mock_net_attach_object" : { + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200, + "DATA": [ + { + "networkName": "test_network", + "lanAttachList": [ + { + "networkName": "test_network", + "displayName": "test_network", + "switchName": "n9kv-218", + "switchRole": "leaf", + "fabricName": "test_net", + "lanAttachState": "DEPLOYED", + "isLanAttached": true, + "portNames": "Ethernet1/13,Ethernet1/14", + "switchSerialNo": "9YO9A29F27U", + "switchDbId": 4191270, + "ipAddress": "10.10.10.218", + "networkId": 9008011, + "vlanId": 202, + "interfaceGroups": null + }, + { + "networkName": "test_network", + "displayName": "test_network", + "switchName": "n9kv-217", + "switchRole": "leaf", + "fabricName": "test_net", + "lanAttachState": "DEPLOYED", + "isLanAttached": true, + "portNames": "Ethernet1/13,Ethernet1/14", + "switchSerialNo": "9NN7E41N16A", + "switchDbId": 4195850, + "ipAddress": "10.10.10.217", + "networkId": 9008011, + "vlanId": 202, + "interfaceGroups": null + } + ] + } + ] + }, + "mock_net_attach_object_pending" : { + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200, + "DATA": [ + { + "networkName": "test_network", + "lanAttachList": [ + { + "networkName": "test_network", + "displayName": "test_network", + "switchName": "n9kv-218", + "switchRole": "leaf", + "fabricName": "test_net", + "lanAttachState": "PENDING", + "isLanAttached": true, + "portNames": "Ethernet1/13,Ethernet1/14", + "switchSerialNo": "9YO9A29F27U", + "switchDbId": 4191270, + "ipAddress": "10.10.10.218", + "networkId": 9008011, + "vlanId": 202, + "interfaceGroups": null + }, + { + "networkName": "test_network", + "displayName": "test_network", + "switchName": "n9kv-217", + "switchRole": "leaf", + "fabricName": "test_net", + "lanAttachState": "PENDING", + "isLanAttached": true, + "portNames": "Ethernet1/13,Ethernet1/14", + "switchSerialNo": "9NN7E41N16A", + "switchDbId": 4195850, + "ipAddress": "10.10.10.217", + "networkId": 9008011, + "vlanId": 202, + "interfaceGroups": null + } + ] + } + ] + }, + "mock_net_attach_object_del_ready": { + "ERROR": "", + "RETURN_CODE": 200, + "MESSAGE": "OK", + "DATA": [ + { + "networkName": "test_network", + "lanAttachList": [ + { + "lanAttachState": "NA" + }, + { + "lanAttachState": "NA" + } + ] + } + ] + }, + "mock_net_attach_object_del_not_ready": { + "ERROR": "", + "RETURN_CODE": 200, + "MESSAGE": "OK", + "DATA": [ + { + "networkName": "test_network", + "lanAttachList": [ + { + "lanAttachState": "DEPLOYED" + }, + { + "lanAttachState": "DEPLOYED" + } + ] + } + ] + }, + "mock_vlan_get": { + "DATA": "202", + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200 + }, + "attach_success_resp": { + "DATA": { + "test-network--9NN7E41N16A(leaf1)": "SUCCESS", + "test-network--9YO9A29F27U(leaf2)": "SUCCESS" + }, + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200 + }, + "attach_success_resp2": { + "DATA": { + "test-network--9YO9A29F27U(leaf2)": "SUCCESS", + "test-network--XYZKSJHSMK3(leaf3)": "SUCCESS" + }, + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200 + }, + "attach_success_resp3": { + "DATA": { + "test-network--9YO9A29F27U(leaf1)": "SUCCESS", + "test-network--XYZKSJHSMK3(leaf4)": "SUCCESS" + }, + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200 + }, + "deploy_success_resp": { + "DATA": {"status": ""}, + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200 + }, + "blank_data": { + "DATA": "", + "MESSAGE": "OK", + "METHOD": "POST", + "RETURN_CODE": 200 + }, + "get_have_failure": { + "DATA": "Invalid JSON response: Invalid Fabric: demo-fabric-123", + "ERROR": "Not Found", + "METHOD": "GET", + "RETURN_CODE": 404, + "MESSAGE": "OK" + }, + "error1": { + "DATA": { + "test-network--9NN7E41N16A(leaf1)": "Invalid network" + }, + "ERROR": "There is an error", + "METHOD": "POST", + "RETURN_CODE": 400, + "MESSAGE": "OK" + }, + "error2": { + "DATA": { + "test-network--9NN7E41N16A(leaf1)": "Entered Network VLAN ID 203 is in use already", + "test-network--9YO9A29F27U(leaf2)": "SUCCESS" + }, + "ERROR": "", + "METHOD": "POST", + "RETURN_CODE": 200, + "MESSAGE": "OK" + }, + "error3": { + "DATA": "No switches PENDING for deployment", + "ERROR": "", + "METHOD": "POST", + "RETURN_CODE": 200, + "MESSAGE": "OK" + }, + "delete_success_resp": { + "ERROR": "", + "METHOD": "POST", + "RETURN_CODE": 200, + "MESSAGE": "OK" + }, + "net_inv_data": { + "10.10.10.217":{ + "ipAddress": "10.10.10.217", + "logicalName": "dt-n9k1", + "serialNumber": "9NN7E41N16A", + "switchRole": "leaf" + }, + "10.10.10.218":{ + "ipAddress": "10.10.10.218", + "logicalName": "dt-n9k2", + "serialNumber": "9YO9A29F27U", + "switchRole": "leaf" + }, + "10.10.10.226":{ + "ipAddress": "10.10.10.226", + "logicalName": "dt-n9k3", + "serialNumber": "XYZKSJHSMK3", + "switchRole": "leaf" + }, + "10.10.10.227":{ + "ipAddress": "10.10.10.227", + "logicalName": "dt-n9k4", + "serialNumber": "XYZKSJHSMK4", + "switchRole": "border spine" + }, + "10.10.10.228":{ + "ipAddress": "10.10.10.228", + "logicalName": "dt-n9k5", + "serialNumber": "XYZKSJHSMK5", + "switchRole": "border" + } + }, + "fabric_details": { + "createdOn": 1613750822779, + "deviceType": "n9k", + "fabricId": "FABRIC-15", + "fabricName": "MS-fabric", + "fabricTechnology": "VXLANFabric", + "fabricTechnologyFriendly": "VXLAN Fabric", + "fabricType": "MFD", + "fabricTypeFriendly": "Multi-Fabric Domain", + "id": 15, + "modifiedOn": 1613750822779, + "networkExtensionTemplate": "Default_Network_Extension_Universal", + "networkTemplate": "Default_Network_Universal", + "nvPairs": { + "ANYCAST_GW_MAC": "2020.0000.00aa", + "BGP_RP_ASN": "", + "BORDER_GWY_CONNECTIONS": "Direct_To_BGWS", + "CLOUDSEC_ALGORITHM": "", + "CLOUDSEC_AUTOCONFIG": "false", + "CLOUDSEC_ENFORCEMENT": "", + "CLOUDSEC_KEY_STRING": "", + "DCI_SUBNET_RANGE": "10.10.1.0/24", + "DCI_SUBNET_TARGET_MASK": "30", + "DELAY_RESTORE": "300", + "FABRIC_NAME": "MS-fabric", + "FABRIC_TYPE": "MFD", + "FF": "MSD", + "L2_SEGMENT_ID_RANGE": "30000-49000", + "L3_PARTITION_ID_RANGE": "50000-59000", + "LOOPBACK100_IP_RANGE": "10.10.0.0/24", + "MS_LOOPBACK_ID": "100", + "MS_UNDERLAY_AUTOCONFIG": "true", + "RP_SERVER_IP": "", + "TOR_AUTO_DEPLOY": "false", + "default_network": "Default_Network_Universal", + "default_vrf": "Default_VRF_Universal", + "enableScheduledBackup": "false", + "network_extension_template": "Default_Network_Extension_Universal", + "scheduledTime": "", + "vrf_extension_template": "Default_VRF_Extension_Universal" + }, + "provisionMode": "DCNMTopDown", + "replicationMode": "IngressReplication", + "templateName": "MSD_Fabric_11_1", + "vrfExtensionTemplate": "Default_VRF_Extension_Universal", + "vrfTemplate": "Default_VRF_Universal" + }, + "mock_vrf12_object": { + "ERROR": "", + "RETURN_CODE": 200, + "MESSAGE":"OK", + "DATA": [ + { + "fabric": "test_network", + "serviceVrfTemplate": "None", + "source": "None", + "vrfExtensionTemplate": "Default_VRF_Extension_Universal", + "vrfId": 9008011, + "vrfName": "test_vrf_1", + "vrfTemplate": "Default_VRF_Universal", + "vrfTemplateConfig": "{\"advertiseDefaultRouteFlag\":\"true\",\"routeTargetImport\":\"\",\"vrfVlanId\":\"202\",\"isRPExternal\":\"false\",\"vrfDescription\":\"\",\"disableRtAuto\":\"false\",\"L3VniMcastGroup\":\"\",\"vrfSegmentId\":\"9008013\",\"maxBgpPaths\":\"1\",\"maxIbgpPaths\":\"2\",\"routeTargetExport\":\"\",\"ipv6LinkLocalFlag\":\"true\",\"vrfRouteMap\":\"FABRIC-RMAP-REDIST-SUBNET\",\"routeTargetExportMvpn\":\"\",\"ENABLE_NETFLOW\":\"false\",\"configureStaticDefaultRouteFlag\":\"true\",\"tag\":\"12345\",\"rpAddress\":\"\",\"trmBGWMSiteEnabled\":\"false\",\"nveId\":\"1\",\"routeTargetExportEvpn\":\"\",\"NETFLOW_MONITOR\":\"\",\"bgpPasswordKeyType\":\"3\",\"bgpPassword\":\"\",\"mtu\":\"9216\",\"multicastGroup\":\"\",\"routeTargetImportMvpn\":\"\",\"isRPAbsent\":\"false\",\"advertiseHostRouteFlag\":\"false\",\"vrfVlanName\":\"\",\"trmEnabled\":\"false\",\"loopbackNumber\":\"\",\"asn\":\"52125\",\"vrfIntfDescription\":\"\",\"vrfName\":\"test_vrf_1\",\"routeTargetImportEvpn\":\"\"}", + "vrfStatus": "DEPLOYED" + } + ] + } +} diff --git a/tests/unit/modules/dcnm/fixtures/dcnm_vrf.json b/tests/unit/modules/dcnm/fixtures/dcnm_vrf.json index 1a6b68854..74be2ddb7 100644 --- a/tests/unit/modules/dcnm/fixtures/dcnm_vrf.json +++ b/tests/unit/modules/dcnm/fixtures/dcnm_vrf.json @@ -396,7 +396,7 @@ "vrfId": 9008011, "vrfName": "test_vrf_1", "vrfTemplate": "Default_VRF_Universal", - "vrfTemplateConfig": "{\"vrfSegmentId\": 9008011, \"vrfName\": \"test_vrf_1\", \"vlanId\": \"202\"}", + "vrfTemplateConfig": "{\"advertiseDefaultRouteFlag\":\"true\",\"vrfVlanId\":\"202\",\"isRPExternal\":\"false\",\"vrfDescription\":\"\",\"L3VniMcastGroup\":\"\",\"maxBgpPaths\":\"1\",\"maxIbgpPaths\":\"2\",\"vrfSegmentId\":\"9008013\",\"ipv6LinkLocalFlag\":\"true\",\"vrfRouteMap\":\"FABRIC-RMAP-REDIST-SUBNET\",\"configureStaticDefaultRouteFlag\":\"true\",\"trmBGWMSiteEnabled\":\"false\",\"tag\":\"12345\",\"rpAddress\":\"\",\"nveId\":\"1\",\"bgpPasswordKeyType\":\"3\",\"bgpPassword\":\"\",\"mtu\":\"9216\",\"multicastGroup\":\"\",\"advertiseHostRouteFlag\":\"false\",\"vrfVlanName\":\"\",\"trmEnabled\":\"false\",\"loopbackNumber\":\"\",\"asn\":\"34343\",\"vrfIntfDescription\":\"\",\"vrfName\":\"test_vrf_1\"}", "vrfStatus": "DEPLOYED" } ] @@ -627,7 +627,7 @@ "serviceVrfTemplate": "None", "source": "None", "vrfStatus": "DEPLOYED", - "vrfTemplateConfig": "{\"vrfSegmentId\": 9008013, \"vrfName\": \"test_vrf_dcnm\", \"vlanId\": \"202\"}", + "vrfTemplateConfig": "{\"advertiseDefaultRouteFlag\":\"true\",\"vrfVlanId\":\"202\",\"isRPExternal\":\"false\",\"vrfDescription\":\"\",\"L3VniMcastGroup\":\"\",\"maxBgpPaths\":\"1\",\"maxIbgpPaths\":\"2\",\"vrfSegmentId\":\"9008013\",\"ipv6LinkLocalFlag\":\"true\",\"vrfRouteMap\":\"FABRIC-RMAP-REDIST-SUBNET\",\"configureStaticDefaultRouteFlag\":\"true\",\"trmBGWMSiteEnabled\":\"false\",\"tag\":\"12345\",\"rpAddress\":\"\",\"nveId\":\"1\",\"bgpPasswordKeyType\":\"3\",\"bgpPassword\":\"\",\"mtu\":\"9216\",\"multicastGroup\":\"\",\"advertiseHostRouteFlag\":\"false\",\"vrfVlanName\":\"\",\"trmEnabled\":\"false\",\"loopbackNumber\":\"\",\"asn\":\"34343\",\"vrfIntfDescription\":\"\",\"vrfName\":\"test_vrf_dcnm\"}", "vrfId": "9008013" } ] @@ -1057,5 +1057,23 @@ "templateName": "MSD_Fabric_11_1", "vrfExtensionTemplate": "Default_VRF_Extension_Universal", "vrfTemplate": "Default_VRF_Universal" + }, + "mock_vrf12_object": { + "ERROR": "", + "RETURN_CODE": 200, + "MESSAGE":"OK", + "DATA": [ + { + "fabric": "test_fabric", + "serviceVrfTemplate": "None", + "source": "None", + "vrfExtensionTemplate": "Default_VRF_Extension_Universal", + "vrfId": 9008011, + "vrfName": "test_vrf_1", + "vrfTemplate": "Default_VRF_Universal", + "vrfTemplateConfig": "{\"advertiseDefaultRouteFlag\":\"true\",\"routeTargetImport\":\"\",\"vrfVlanId\":\"202\",\"isRPExternal\":\"false\",\"vrfDescription\":\"\",\"disableRtAuto\":\"false\",\"L3VniMcastGroup\":\"\",\"vrfSegmentId\":\"9008013\",\"maxBgpPaths\":\"1\",\"maxIbgpPaths\":\"2\",\"routeTargetExport\":\"\",\"ipv6LinkLocalFlag\":\"true\",\"vrfRouteMap\":\"FABRIC-RMAP-REDIST-SUBNET\",\"routeTargetExportMvpn\":\"\",\"ENABLE_NETFLOW\":\"false\",\"configureStaticDefaultRouteFlag\":\"true\",\"tag\":\"12345\",\"rpAddress\":\"\",\"trmBGWMSiteEnabled\":\"false\",\"nveId\":\"1\",\"routeTargetExportEvpn\":\"\",\"NETFLOW_MONITOR\":\"\",\"bgpPasswordKeyType\":\"3\",\"bgpPassword\":\"\",\"mtu\":\"9216\",\"multicastGroup\":\"\",\"routeTargetImportMvpn\":\"\",\"isRPAbsent\":\"false\",\"advertiseHostRouteFlag\":\"false\",\"vrfVlanName\":\"\",\"trmEnabled\":\"false\",\"loopbackNumber\":\"\",\"asn\":\"52125\",\"vrfIntfDescription\":\"\",\"vrfName\":\"test_vrf_1\",\"routeTargetImportEvpn\":\"\"}", + "vrfStatus": "DEPLOYED" + } + ] } } diff --git a/tests/unit/modules/dcnm/test_dcnm_net.py b/tests/unit/modules/dcnm/test_dcnm_net.py new file mode 100644 index 000000000..2faa937e0 --- /dev/null +++ b/tests/unit/modules/dcnm/test_dcnm_net.py @@ -0,0 +1,738 @@ +# Copyright (c) 2020-2023 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from unittest.mock import patch + +# from units.compat.mock import patch + +from ansible_collections.cisco.dcnm.plugins.modules import dcnm_network +from .dcnm_module import TestDcnmModule, set_module_args, loadPlaybookData + +import json +import copy + + +class TestDcnmNetworkModule(TestDcnmModule): + + module = dcnm_network + + test_data = loadPlaybookData("dcnm_net") + + SUCCESS_RETURN_CODE = 200 + + version = 11 + + mock_ip_sn = test_data.get("mock_ip_sn") + net_inv_data = test_data.get("net_inv_data") + fabric_details = test_data.get("fabric_details") + + playbook_config = test_data.get("playbook_config") + playbook_config_incorrect_netid = test_data.get("playbook_config_incorrect_netid") + playbook_config_incorrect_vrf = test_data.get("playbook_config_incorrect_vrf") + playbook_config_update = test_data.get("playbook_config_update") + playbook_config_novlan = test_data.get("playbook_config_novlan") + + playbook_config_replace = test_data.get("playbook_config_replace") + playbook_config_replace_no_atch = test_data.get("playbook_config_replace_no_atch") + playbook_config_override = test_data.get("playbook_config_override") + mock_net_attach_object_del_not_ready = test_data.get( + "mock_net_attach_object_del_not_ready" + ) + mock_net_attach_object_del_ready = test_data.get("mock_net_attach_object_del_ready") + + attach_success_resp = test_data.get("attach_success_resp") + attach_success_resp2 = test_data.get("attach_success_resp2") + deploy_success_resp = test_data.get("deploy_success_resp") + error1 = test_data.get("error1") + error2 = test_data.get("error2") + error3 = test_data.get("error3") + get_have_failure = test_data.get("get_have_failure") + + delete_success_resp = test_data.get("delete_success_resp") + blank_data = test_data.get("blank_data") + + def init_data(self): + # Some of the mock data is re-initialized after each test as previous test might have altered portions + # of the mock data. + + self.mock_net_object = copy.deepcopy(self.test_data.get("mock_net_object")) + self.mock_vrf_object = copy.deepcopy(self.test_data.get("mock_vrf_object")) + self.mock_net_attach_object = copy.deepcopy(self.test_data.get("mock_net_attach_object")) + self.mock_net_attach_object_pending = copy.deepcopy( + self.test_data.get("mock_net_attach_object_pending") + ) + self.mock_net_query_object = copy.deepcopy(self.test_data.get("mock_net_query_object")) + self.mock_vlan_get = copy.deepcopy(self.test_data.get("mock_vlan_get")) + + def setUp(self): + super(TestDcnmNetworkModule, self).setUp() + + self.mock_dcnm_ip_sn = patch( + "ansible_collections.cisco.dcnm.plugins.modules.dcnm_network.get_fabric_inventory_details" + ) + self.run_dcnm_ip_sn = self.mock_dcnm_ip_sn.start() + + self.mock_dcnm_send = patch( + "ansible_collections.cisco.dcnm.plugins.modules.dcnm_network.dcnm_send" + ) + self.run_dcnm_send = self.mock_dcnm_send.start() + + self.mock_dcnm_fabric_details = patch( + "ansible_collections.cisco.dcnm.plugins.modules.dcnm_network.get_fabric_details" + ) + self.run_dcnm_fabric_details = self.mock_dcnm_fabric_details.start() + + self.mock_dcnm_version_supported = patch( + "ansible_collections.cisco.dcnm.plugins.modules.dcnm_network.dcnm_version_supported" + ) + self.run_dcnm_version_supported = self.mock_dcnm_version_supported.start() + + self.mock_dcnm_get_url = patch( + "ansible_collections.cisco.dcnm.plugins.modules.dcnm_network.dcnm_get_url" + ) + self.run_dcnm_get_url = self.mock_dcnm_get_url.start() + + def tearDown(self): + super(TestDcnmNetworkModule, self).tearDown() + self.mock_dcnm_send.stop() + self.mock_dcnm_ip_sn.stop() + self.mock_dcnm_fabric_details.stop() + self.mock_dcnm_version_supported.stop() + self.mock_dcnm_get_url.stop() + + def load_fixtures(self, response=None, device=""): + + if self.version == 12: + self.run_dcnm_version_supported.return_value = 12 + else: + self.run_dcnm_version_supported.return_value = 11 + + if "net_blank_fabric" in self._testMethodName: + self.run_dcnm_ip_sn.side_effect = [{}] + else: + self.run_dcnm_ip_sn.side_effect = [self.net_inv_data] + + self.run_dcnm_fabric_details.side_effect = [self.fabric_details] + + if "get_have_failure" in self._testMethodName: + self.run_dcnm_send.side_effect = [self.get_have_failure] + + elif "_check_mode" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.blank_data, + ] + + elif "_12check_mode" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.blank_data, + ] + + elif "_merged_new" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.blank_data, + self.blank_data, + self.attach_success_resp, + self.deploy_success_resp, + ] + + elif "_12merged_new" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.blank_data, + self.blank_data, + self.attach_success_resp, + self.deploy_success_resp, + ] + + elif "_merged_novlan_new" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.blank_data, + self.mock_vlan_get, + self.blank_data, + self.attach_success_resp, + self.deploy_success_resp, + ] + + elif "error1" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.blank_data, + self.blank_data, + self.error1, + self.blank_data, + ] + + elif "error2" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.blank_data, + self.blank_data, + self.error2, + self.blank_data, + ] + elif "error3" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.blank_data, + self.blank_data, + self.attach_success_resp, + self.error3, + self.blank_data, + ] + + elif "_merged_duplicate" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + ] + + elif "_merged_with_incorrect_netid" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + ] + + elif "_merged_with_incorrect_vrf" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + ] + + elif "_merged_with_update" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + self.blank_data, + self.attach_success_resp, + self.deploy_success_resp, + ] + + elif "replace_with_no_atch" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + self.attach_success_resp, + self.deploy_success_resp, + self.delete_success_resp, + ] + + elif "replace_with_changes" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + self.attach_success_resp, + self.deploy_success_resp, + self.delete_success_resp, + ] + + elif "replace_without_changes" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + ] + + elif "_merged_redeploy" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object_pending] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + self.blank_data, + self.deploy_success_resp, + ] + + elif "override_with_additions" in self._testMethodName: + self.init_data() + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.blank_data, + self.blank_data, + self.attach_success_resp, + self.deploy_success_resp, + ] + + elif "override_without_changes" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + ] + + elif "override_with_deletions" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + self.attach_success_resp, + self.deploy_success_resp, + self.mock_net_attach_object_del_not_ready, + self.mock_net_attach_object_del_ready, + self.mock_net_attach_object_del_ready, + self.delete_success_resp, + self.blank_data, + self.attach_success_resp2, + self.deploy_success_resp, + ] + + elif "delete_std" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + self.blank_data, + self.attach_success_resp, + self.deploy_success_resp, + self.mock_net_attach_object_del_not_ready, + self.mock_net_attach_object_del_ready, + self.mock_net_attach_object_del_ready, + self.delete_success_resp, + ] + + elif "delete_without_config" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + self.blank_data, + self.attach_success_resp, + self.deploy_success_resp, + self.mock_net_attach_object_del_not_ready, + self.mock_net_attach_object_del_ready, + self.mock_net_attach_object_del_ready, + self.delete_success_resp, + ] + + elif "query_with_config" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + self.mock_vrf_object, + self.mock_net_query_object, + self.mock_net_attach_object + ] + + elif "query_without_config" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_net_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf_object, + self.mock_net_object, + self.mock_vrf_object, + self.mock_net_object, + self.mock_net_attach_object + ] + + else: + pass + + def test_dcnm_net_blank_fabric(self): + set_module_args( + dict(state="merged", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get("msg"), + "Fabric test_network missing on DCNM or does not have any switches", + ) + + def test_dcnm_net_get_have_failure(self): + set_module_args( + dict(state="merged", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual(result.get("msg"), "Fabric test_network not present on DCNM") + + def test_dcnm_net_check_mode(self): + set_module_args( + dict( + _ansible_check_mode=True, + state="merged", + fabric="test_network", + config=self.playbook_config, + ) + ) + result = self.execute_module(changed=False, failed=False) + self.assertTrue(result.get("diff")) + self.assertFalse(result.get("response")) + + def test_dcnm_net_12check_mode(self): + self.version = 12 + set_module_args( + dict( + _ansible_check_mode=True, + state="merged", + fabric="test_network", + config=self.playbook_config, + ) + ) + result = self.execute_module(changed=False, failed=False) + self.version = 11 + self.assertTrue(result.get("diff")) + self.assertFalse(result.get("response")) + + def test_dcnm_net_merged_new(self): + set_module_args( + dict(state="merged", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=True, failed=False) + self.assertTrue(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertTrue(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual( + result.get("diff")[0]["attach"][0]["ip_address"], "10.10.10.217" + ) + + def test_dcnm_net_12merged_new(self): + self.version = 12 + set_module_args( + dict(state="merged", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=True, failed=False) + self.version = 11 + self.assertTrue(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertTrue(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual( + result.get("diff")[0]["attach"][0]["ip_address"], "10.10.10.217" + ) + + def test_dcnm_net_merged_novlan_new(self): + set_module_args( + dict(state="merged", fabric="test_network", config=self.playbook_config_novlan) + ) + result = self.execute_module(changed=True, failed=False) + self.assertTrue(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertTrue(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual( + result.get("diff")[0]["attach"][0]["ip_address"], "10.10.10.217" + ) + + def test_dcnm_net_error1(self): + set_module_args( + dict(state="merged", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual(result["msg"]["RETURN_CODE"], 400) + self.assertEqual(result["msg"]["ERROR"], "There is an error") + + def test_dcnm_net_error2(self): + set_module_args( + dict(state="merged", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=False, failed=True) + self.assertIn( + "Entered Network VLAN ID 203 is in use already", + str(result["msg"]["DATA"].values()), + ) + + def test_dcnm_net_error3(self): + set_module_args( + dict(state="merged", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=False, failed=False) + self.assertEqual( + result["response"][2]["DATA"], "No switches PENDING for deployment" + ) + + def test_dcnm_net_merged_duplicate(self): + set_module_args( + dict(state="merged", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=False, failed=False) + self.assertFalse(result.get("diff")) + + def test_dcnm_net_merged_with_incorrect_netid(self): + set_module_args( + dict( + state="merged", + fabric="test_network", + config=self.playbook_config_incorrect_netid, + ) + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get("msg"), + "networkId can not be updated on existing network: test_network", + ) + + def test_dcnm_net_merged_with_incorrect_vrf(self): + set_module_args( + dict( + state="merged", + fabric="test_network", + config=self.playbook_config_incorrect_vrf, + ) + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get("msg"), + "VRF: ansible-vrf-int2 is missing in fabric: test_network", + ) + + def test_dcnm_net_merged_with_update(self): + set_module_args( + dict( + state="merged", fabric="test_network", config=self.playbook_config_update + ) + ) + result = self.execute_module(changed=True, failed=False) + self.assertTrue(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertTrue(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual( + result.get("diff")[0]["attach"][0]["ip_address"], "10.10.10.226" + ) + self.assertEqual( + result.get("diff")[0]["attach"][1]["ip_address"], "10.10.10.227" + ) + self.assertEqual(result.get("diff")[0]["vrf_name"], "ansible-vrf-int1") + + def test_dcnm_net_replace_with_changes(self): + set_module_args( + dict( + state="replaced", + fabric="test_network", + config=self.playbook_config_replace, + ) + ) + result = self.execute_module(changed=True, failed=False) + self.assertEqual(result.get("diff")[0]["vlan_id"], 203) + self.assertTrue(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertFalse(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual( + result["response"][0]["DATA"]["test-network--9NN7E41N16A(leaf1)"], "SUCCESS" + ) + self.assertEqual( + result["response"][0]["DATA"]["test-network--9YO9A29F27U(leaf2)"], "SUCCESS" + ) + self.assertEqual(result["response"][1]["DATA"]["status"], "") + self.assertEqual(result["response"][1]["RETURN_CODE"], self.SUCCESS_RETURN_CODE) + + def test_dcnm_net_replace_with_no_atch(self): + set_module_args( + dict( + state="replaced", + fabric="test_network", + config=self.playbook_config_replace_no_atch, + ) + ) + result = self.execute_module(changed=True, failed=False) + self.assertFalse(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertFalse(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual(result.get("diff")[0]["net_name"], "test_network") + self.assertEqual( + result["response"][0]["DATA"]["test-network--9NN7E41N16A(leaf1)"], "SUCCESS" + ) + self.assertEqual( + result["response"][0]["DATA"]["test-network--9YO9A29F27U(leaf2)"], "SUCCESS" + ) + self.assertEqual(result["response"][1]["DATA"]["status"], "") + self.assertEqual(result["response"][1]["RETURN_CODE"], self.SUCCESS_RETURN_CODE) + + def test_dcnm_net_replace_without_changes(self): + set_module_args( + dict(state="replaced", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=False, failed=False) + self.assertFalse(result.get("diff")) + self.assertFalse(result.get("response")) + + def test_dcnm_vrf_merged_redeploy(self): + set_module_args( + dict(state="merged", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=True, failed=False) + self.assertEqual(result.get("diff")[0]["net_name"], "test_network") + + def test_dcnm_net_override_with_additions(self): + set_module_args( + dict(state="overridden", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=True, failed=False) + self.assertTrue(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertTrue(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual( + result.get("diff")[0]["attach"][0]["ip_address"], "10.10.10.217" + ) + self.assertEqual( + result.get("diff")[0]["attach"][1]["ip_address"], "10.10.10.218" + ) + self.assertEqual(result.get("diff")[0]["net_id"], 9008011) + self.assertEqual( + result["response"][1]["DATA"]["test-network--9NN7E41N16A(leaf1)"], "SUCCESS" + ) + self.assertEqual( + result["response"][1]["DATA"]["test-network--9YO9A29F27U(leaf2)"], "SUCCESS" + ) + self.assertEqual(result["response"][2]["DATA"]["status"], "") + self.assertEqual(result["response"][2]["RETURN_CODE"], self.SUCCESS_RETURN_CODE) + + def test_dcnm_net_override_without_changes(self): + set_module_args( + dict(state="overridden", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=False, failed=False) + self.assertFalse(result.get("diff")) + self.assertFalse(result.get("response")) + + def test_dcnm_net_override_with_deletions(self): + set_module_args( + dict( + state="overridden", + fabric="test_network", + config=self.playbook_config_override, + ) + ) + result = self.execute_module(changed=True, failed=False) + self.assertTrue(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertTrue(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual(result.get("diff")[0]["vlan_id"], 303) + self.assertEqual(result.get("diff")[0]["net_id"], 9008012) + + self.assertFalse(result.get("diff")[1]["attach"][0]["deploy"]) + self.assertFalse(result.get("diff")[1]["attach"][1]["deploy"]) + self.assertEqual(result.get("diff")[1]["net_name"], "test_network") + self.assertNotIn("net_id", result.get("diff")[1]) + + self.assertEqual( + result["response"][0]["DATA"]["test-network--9NN7E41N16A(leaf1)"], "SUCCESS" + ) + self.assertEqual( + result["response"][0]["DATA"]["test-network--9YO9A29F27U(leaf2)"], "SUCCESS" + ) + self.assertEqual(result["response"][1]["DATA"]["status"], "") + self.assertEqual(result["response"][1]["RETURN_CODE"], self.SUCCESS_RETURN_CODE) + self.assertEqual( + result["response"][4]["DATA"]["test-network--9YO9A29F27U(leaf2)"], "SUCCESS" + ) + self.assertEqual( + result["response"][4]["DATA"]["test-network--XYZKSJHSMK3(leaf3)"], "SUCCESS" + ) + + def test_dcnm_net_delete_std(self): + set_module_args( + dict(state="deleted", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=True, failed=False) + self.assertFalse(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertFalse(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual(result.get("diff")[0]["net_name"], "test_network") + self.assertNotIn("net_id", result.get("diff")[0]) + + self.assertEqual( + result["response"][0]["DATA"]["test-network--9NN7E41N16A(leaf1)"], "SUCCESS" + ) + self.assertEqual( + result["response"][0]["DATA"]["test-network--9YO9A29F27U(leaf2)"], "SUCCESS" + ) + self.assertEqual(result["response"][1]["DATA"]["status"], "") + self.assertEqual(result["response"][1]["RETURN_CODE"], self.SUCCESS_RETURN_CODE) + + def test_dcnm_net_delete_without_config(self): + set_module_args(dict(state="deleted", fabric="test_network", config=[])) + result = self.execute_module(changed=True, failed=False) + self.assertFalse(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertFalse(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual(result.get("diff")[0]["net_name"], "test_network") + self.assertNotIn("net_id", result.get("diff")[0]) + + self.assertEqual( + result["response"][0]["DATA"]["test-network--9NN7E41N16A(leaf1)"], "SUCCESS" + ) + self.assertEqual( + result["response"][0]["DATA"]["test-network--9YO9A29F27U(leaf2)"], "SUCCESS" + ) + self.assertEqual(result["response"][1]["DATA"]["status"], "") + self.assertEqual(result["response"][1]["RETURN_CODE"], self.SUCCESS_RETURN_CODE) + + def test_dcnm_net_query_with_config(self): + set_module_args( + dict(state="query", fabric="test_network", config=self.playbook_config) + ) + result = self.execute_module(changed=False, failed=False) + self.assertFalse(result.get("diff")) + self.assertEqual(result.get("response")[0]["parent"]["networkName"], "test_network") + self.assertEqual(result.get("response")[0]["parent"]["networkId"], 9008011) + self.assertTrue( + result.get("response")[0]["attach"][0][ "deployment"], + ) + self.assertEqual( + result.get("response")[0]["attach"][0]["vlan"], + 202, + ) + self.assertTrue( + result.get("response")[0]["attach"][1][ "deployment"], + ) + self.assertEqual( + result.get("response")[0]["attach"][1]["vlan"], + 202, + ) + + def test_dcnm_net_query_without_config(self): + set_module_args( + dict(state="query", fabric="test_network", config=[]) + ) + result = self.execute_module(changed=False, failed=False) + self.assertFalse(result.get("diff")) + self.assertEqual(result.get("response")[0]["parent"]["networkName"], "test_network") + self.assertEqual(result.get("response")[0]["parent"]["networkId"], 9008011) + self.assertTrue( + result.get("response")[0]["attach"][0][ "deployment"], + ) + self.assertEqual( + result.get("response")[0]["attach"][0]["vlan"], + 202, + ) + self.assertTrue( + result.get("response")[0]["attach"][1][ "deployment"], + ) + self.assertEqual( + result.get("response")[0]["attach"][1]["vlan"], + 202, + ) + diff --git a/tests/unit/modules/dcnm/test_dcnm_vrf.py b/tests/unit/modules/dcnm/test_dcnm_vrf.py index 71edb5507..961fb355b 100644 --- a/tests/unit/modules/dcnm/test_dcnm_vrf.py +++ b/tests/unit/modules/dcnm/test_dcnm_vrf.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2022 Cisco and/or its affiliates. +# Copyright (c) 2020-2023 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -36,6 +36,8 @@ class TestDcnmVrfModule(TestDcnmModule): SUCCESS_RETURN_CODE = 200 + version = 11 + mock_ip_sn = test_data.get("mock_ip_sn") vrf_inv_data = test_data.get("vrf_inv_data") fabric_details = test_data.get("fabric_details") @@ -76,6 +78,7 @@ def init_data(self): # of the mock data. self.mock_vrf_object = copy.deepcopy(self.test_data.get("mock_vrf_object")) + self.mock_vrf12_object = copy.deepcopy(self.test_data.get("mock_vrf12_object")) self.mock_vrf_attach_object = copy.deepcopy( self.test_data.get("mock_vrf_attach_object") ) @@ -163,7 +166,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 + if "vrf_blank_fabric" in self._testMethodName: self.run_dcnm_ip_sn.side_effect = [{}] @@ -545,6 +552,23 @@ def load_fixtures(self, response=None, device=""): self.mock_vrf_attach_get_ext_object_merge_att4_only, ] + elif "_12check_mode" in self._testMethodName: + self.init_data() + self.run_dcnm_get_url.side_effect = [self.mock_vrf_attach_object] + self.run_dcnm_send.side_effect = [ + self.mock_vrf12_object, + self.mock_vrf_attach_get_ext_object_merge_att1_only, + self.mock_vrf_attach_get_ext_object_merge_att2_only, + ] + + elif "_12merged_new" in self._testMethodName: + self.run_dcnm_send.side_effect = [ + self.blank_data, + self.blank_data, + self.attach_success_resp, + self.deploy_success_resp, + ] + else: pass @@ -1248,3 +1272,43 @@ def test_dcnm_vrf_validation_no_config(self): self.assertEqual( result["msg"], "config: element is mandatory for this state merged" ) + + def test_dcnm_vrf_12check_mode(self): + self.version = 12 + set_module_args( + dict( + _ansible_check_mode=True, + state="merged", + fabric="test_fabric", + config=self.playbook_config, + ) + ) + result = self.execute_module(changed=False, failed=False) + self.version = 11 + self.assertFalse(result.get("diff")) + self.assertFalse(result.get("response")) + + def test_dcnm_vrf_12merged_new(self): + self.version = 12 + set_module_args( + dict(state="merged", fabric="test_fabric", config=self.playbook_config) + ) + result = self.execute_module(changed=True, failed=False) + self.version = 11 + self.assertTrue(result.get("diff")[0]["attach"][0]["deploy"]) + self.assertTrue(result.get("diff")[0]["attach"][1]["deploy"]) + self.assertEqual( + result.get("diff")[0]["attach"][0]["ip_address"], "10.10.10.224" + ) + self.assertEqual( + result.get("diff")[0]["attach"][1]["ip_address"], "10.10.10.225" + ) + self.assertEqual(result.get("diff")[0]["vrf_id"], 9008011) + self.assertEqual( + result["response"][1]["DATA"]["test-vrf-1--XYZKSJHSMK1(leaf1)"], "SUCCESS" + ) + self.assertEqual( + result["response"][1]["DATA"]["test-vrf-1--XYZKSJHSMK2(leaf2)"], "SUCCESS" + ) + self.assertEqual(result["response"][2]["DATA"]["status"], "") + self.assertEqual(result["response"][2]["RETURN_CODE"], self.SUCCESS_RETURN_CODE) From c577e62dffe1757522657385e477d0f1fe0f31de Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Fri, 10 Mar 2023 16:11:46 +0530 Subject: [PATCH 4/8] UT/IT commit for network and vrf --- plugins/modules/dcnm_network.py | 27 ++-- plugins/modules/dcnm_vrf.py | 117 +++++++++--------- .../self-contained-tests/merged_net_all.yaml | 1 - .../overridden_net_all.yaml | 1 - .../replaced_net_all.yaml | 1 - 5 files changed, 69 insertions(+), 78 deletions(-) diff --git a/plugins/modules/dcnm_network.py b/plugins/modules/dcnm_network.py index 7f582b617..c9b814a20 100644 --- a/plugins/modules/dcnm_network.py +++ b/plugins/modules/dcnm_network.py @@ -1125,9 +1125,9 @@ def update_create_params(self, net): } if self.dcnm_version > 11: - template_conf.update(ENABLE_NETFLOW = net.get("netflow_enable", False)) - template_conf.update(SVI_NETFLOW_MONITOR = net.get("intfvlan_nf_monitor", "")) - template_conf.update(VLAN_NETFLOW_MONITOR = net.get("vlan_nf_monitor", "")) + template_conf.update(ENABLE_NETFLOW=net.get("netflow_enable", False)) + template_conf.update(SVI_NETFLOW_MONITOR=net.get("intfvlan_nf_monitor", "")) + template_conf.update(VLAN_NETFLOW_MONITOR=net.get("vlan_nf_monitor", "")) if template_conf["vlanId"] is None: template_conf["vlanId"] = "" @@ -1259,9 +1259,9 @@ def get_have(self): } if self.dcnm_version > 11: - t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW", False)) - t_conf.update(SVI_NETFLOW_MONITOR = json_to_dict.get("SVI_NETFLOW_MONITOR", "")) - t_conf.update(VLAN_NETFLOW_MONITOR = json_to_dict.get("VLAN_NETFLOW_MONITOR", "")) + t_conf.update(ENABLE_NETFLOW=json_to_dict.get("ENABLE_NETFLOW", False)) + t_conf.update(SVI_NETFLOW_MONITOR=json_to_dict.get("SVI_NETFLOW_MONITOR", "")) + t_conf.update(VLAN_NETFLOW_MONITOR=json_to_dict.get("VLAN_NETFLOW_MONITOR", "")) net.update({"networkTemplateConfig": json.dumps(t_conf)}) del net["displayName"] @@ -1308,9 +1308,9 @@ def get_have(self): } if self.dcnm_version > 11: - t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW", False)) - t_conf.update(SVI_NETFLOW_MONITOR = json_to_dict.get("SVI_NETFLOW_MONITOR", "")) - t_conf.update(VLAN_NETFLOW_MONITOR = json_to_dict.get("VLAN_NETFLOW_MONITOR", "")) + t_conf.update(ENABLE_NETFLOW=json_to_dict.get("ENABLE_NETFLOW", False)) + t_conf.update(SVI_NETFLOW_MONITOR=json_to_dict.get("SVI_NETFLOW_MONITOR", "")) + t_conf.update(VLAN_NETFLOW_MONITOR=json_to_dict.get("VLAN_NETFLOW_MONITOR", "")) l2net.update({"networkTemplateConfig": json.dumps(t_conf)}) del l2net["displayName"] @@ -1750,7 +1750,6 @@ def get_diff_merge(self, replace=False): intvlan_nfmon_changed = {} vlan_nfmon_changed = {} - for want_c in self.want_create: found = False for have_c in self.have_create: @@ -2384,9 +2383,9 @@ def push_to_remote(self, is_rollback=False): } if self.dcnm_version > 11: - t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW", False)) - t_conf.update(SVI_NETFLOW_MONITOR = json_to_dict.get("SVI_NETFLOW_MONITOR", "")) - t_conf.update(VLAN_NETFLOW_MONITOR = json_to_dict.get("VLAN_NETFLOW_MONITOR", "")) + t_conf.update(ENABLE_NETFLOW=json_to_dict.get("ENABLE_NETFLOW", False)) + t_conf.update(SVI_NETFLOW_MONITOR=json_to_dict.get("SVI_NETFLOW_MONITOR", "")) + t_conf.update(VLAN_NETFLOW_MONITOR=json_to_dict.get("VLAN_NETFLOW_MONITOR", "")) net.update({"networkTemplateConfig": json.dumps(t_conf)}) @@ -2499,7 +2498,7 @@ def validate_input(self): ) att_spec = dict( ip_address=dict(required=True, type="str"), - ports=dict(required=True, type="list"), + ports=dict(required=True, type="list", default=[]), deploy=dict(type="bool", default=True), ) diff --git a/plugins/modules/dcnm_vrf.py b/plugins/modules/dcnm_vrf.py index 33f9d9079..e27c657b9 100644 --- a/plugins/modules/dcnm_vrf.py +++ b/plugins/modules/dcnm_vrf.py @@ -214,10 +214,11 @@ bgp_passwd_encrypt: description: - VRF Lite BGP Key Encryption Type + - Allowed values are 3 (3DES) and 7 (Cisco) type: int choices: - - 3 (3DES) - - 7 (Cisco) + - 3 + - 7 required: false default: 3 netflow_enable: @@ -245,51 +246,46 @@ description: - VPN routes to import - supported on NDFC only - - Use ',' to separate multiple route-targets(eg: 1:1,2:2) - type: list + - Use ',' to separate multiple route-targets + type: str required: false - default: [] export_vpn_rt: description: - VPN routes to export - supported on NDFC only - - Use ',' to separate multiple route-targets(eg: 1:1,2:2) + - Use ',' to separate multiple route-targets type: str required: false import_evpn_rt: description: - EVPN routes to import - supported on NDFC only - - Use ',' to separate multiple route-targets(eg: 1:1,2:2) - type: list + - Use ',' to separate multiple route-targets + type: str required: false - default: [] export_evpn_rt: description: - EVPN routes to export - supported on NDFC only - - Use ',' to separate multiple route-targets(eg: 1:1,2:2) - type: list + - Use ',' to separate multiple route-targets + type: str required: false - default: [] import_mvpn_rt: description: - MVPN routes to import - supported on NDFC only - Can be configured only when TRM is enabled - - Use ',' to separate multiple route-targets(eg: 1:1,2:2) - type: list + - Use ',' to separate multiple route-targets + type: str required: false - default: [] export_mvpn_rt: description: - MVPN routes to export - supported on NDFC only - Can be configured only when TRM is enabled - - Use ',' to separate multiple route-targets(eg: 1:1,2:2) - type: list + - Use ',' to separate multiple route-targets + type: str required: false - default: [] attach: description: - List of vrf attachment details @@ -1083,16 +1079,16 @@ def update_create_params(self, vrf, vlanId=""): "bgpPasswordKeyType": vrf.get("bgp_passwd_encrypt", ""), } if self.dcnm_version > 11: - template_conf.update(isRPAbsent = vrf.get("no_rp", False)) - template_conf.update(ENABLE_NETFLOW = vrf.get("netflow_enable", False)) - template_conf.update(NETFLOW_MONITOR = vrf.get("nf_monitor", "")) - template_conf.update(disableRtAuto = vrf.get("disable_rt_auto", False)) - template_conf.update(routeTargetImport = vrf.get("import_vpn_rt", "")) - template_conf.update(routeTargetExport = vrf.get("export_vpn_rt", "")) - template_conf.update(routeTargetImportEvpn = vrf.get("import_evpn_rt", "")) - template_conf.update(routeTargetExportEvpn = vrf.get("export_evpn_rt", "")) - template_conf.update(routeTargetImportMvpn = vrf.get("import_mvpn_rt", "")) - template_conf.update(routeTargetExportMvpn = vrf.get("export_mvpn_rt", "")) + template_conf.update(isRPAbsent=vrf.get("no_rp", False)) + template_conf.update(ENABLE_NETFLOW=vrf.get("netflow_enable", False)) + template_conf.update(NETFLOW_MONITOR=vrf.get("nf_monitor", "")) + template_conf.update(disableRtAuto=vrf.get("disable_rt_auto", False)) + template_conf.update(routeTargetImport=vrf.get("import_vpn_rt", "")) + template_conf.update(routeTargetExport=vrf.get("export_vpn_rt", "")) + template_conf.update(routeTargetImportEvpn=vrf.get("import_evpn_rt", "")) + template_conf.update(routeTargetExportEvpn=vrf.get("export_evpn_rt", "")) + template_conf.update(routeTargetImportMvpn=vrf.get("import_mvpn_rt", "")) + template_conf.update(routeTargetExportMvpn=vrf.get("export_mvpn_rt", "")) vrf_upd.update({"vrfTemplateConfig": json.dumps(template_conf)}) @@ -1165,16 +1161,16 @@ def get_have(self): } if self.dcnm_version > 11: - t_conf.update(isRPAbsent = json_to_dict.get("isRPAbsent", False)) - t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW", False)) - t_conf.update(NETFLOW_MONITOR = json_to_dict.get("NETFLOW_MONITOR", "")) - t_conf.update(disableRtAuto = json_to_dict.get("disableRtAuto", False)) - t_conf.update(routeTargetImport = json_to_dict.get("routeTargetImport", "")) - t_conf.update(routeTargetExport = json_to_dict.get("routeTargetExport", "")) - t_conf.update(routeTargetImportEvpn = json_to_dict.get("routeTargetImportEvpn", "")) - t_conf.update(routeTargetExportEvpn = json_to_dict.get("routeTargetExportEvpn", "")) - t_conf.update(routeTargetImportMvpn = json_to_dict.get("routeTargetImportMvpn", "")) - t_conf.update(routeTargetExportMvpn = json_to_dict.get("routeTargetExportMvpn", "")) + t_conf.update(isRPAbsent=json_to_dict.get("isRPAbsent", False)) + t_conf.update(ENABLE_NETFLOW=json_to_dict.get("ENABLE_NETFLOW", False)) + t_conf.update(NETFLOW_MONITOR=json_to_dict.get("NETFLOW_MONITOR", "")) + t_conf.update(disableRtAuto=json_to_dict.get("disableRtAuto", False)) + t_conf.update(routeTargetImport=json_to_dict.get("routeTargetImport", "")) + t_conf.update(routeTargetExport=json_to_dict.get("routeTargetExport", "")) + t_conf.update(routeTargetImportEvpn=json_to_dict.get("routeTargetImportEvpn", "")) + t_conf.update(routeTargetExportEvpn=json_to_dict.get("routeTargetExportEvpn", "")) + t_conf.update(routeTargetImportMvpn=json_to_dict.get("routeTargetImportMvpn", "")) + t_conf.update(routeTargetExportMvpn=json_to_dict.get("routeTargetExportMvpn", "")) vrf.update({"vrfTemplateConfig": json.dumps(t_conf)}) del vrf["vrfStatus"] @@ -1636,7 +1632,7 @@ def get_diff_merge(self): if vrf_id != prev_vrf_id_fetched: want_c.update({"vrfId": vrf_id}) - json_to_dict = json.loads(vrf["vrfTemplateConfig"]) + json_to_dict = json.loads(want_c["vrfTemplateConfig"]) template_conf = { "vrfSegmentId": vrf_id, "vrfName": want_c["vrfName"], @@ -1665,16 +1661,16 @@ def get_diff_merge(self): } if self.dcnm_version > 11: - template_conf.update(isRPAbsent = json_to_dict.get("isRPAbsent")) - template_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW")) - template_conf.update(NETFLOW_MONITOR = json_to_dict.get("NETFLOW_MONITOR")) - template_conf.update(disableRtAuto = json_to_dict.get("disableRtAuto")) - template_conf.update(routeTargetImport = json_to_dict.get("routeTargetImport")) - template_conf.update(routeTargetExport = json_to_dict.get("routeTargetExport")) - template_conf.update(routeTargetImportEvpn = json_to_dict.get("routeTargetImportEvpn")) - template_conf.update(routeTargetExportEvpn = json_to_dict.get("routeTargetExportEvpn")) - template_conf.update(routeTargetImportMvpn = json_to_dict.get("routeTargetImportMvpn")) - template_conf.update(routeTargetExportMvpn = json_to_dict.get("routeTargetExportMvpn")) + template_conf.update(isRPAbsent=json_to_dict.get("isRPAbsent")) + template_conf.update(ENABLE_NETFLOW=json_to_dict.get("ENABLE_NETFLOW")) + template_conf.update(NETFLOW_MONITOR=json_to_dict.get("NETFLOW_MONITOR")) + template_conf.update(disableRtAuto=json_to_dict.get("disableRtAuto")) + template_conf.update(routeTargetImport=json_to_dict.get("routeTargetImport")) + template_conf.update(routeTargetExport=json_to_dict.get("routeTargetExport")) + template_conf.update(routeTargetImportEvpn=json_to_dict.get("routeTargetImportEvpn")) + template_conf.update(routeTargetExportEvpn=json_to_dict.get("routeTargetExportEvpn")) + template_conf.update(routeTargetImportMvpn=json_to_dict.get("routeTargetImportMvpn")) + template_conf.update(routeTargetExportMvpn=json_to_dict.get("routeTargetExportMvpn")) want_c.update( {"vrfTemplateConfig": json.dumps(template_conf)} @@ -1786,7 +1782,6 @@ def format_diff(self): found_c = want_d - src = found_c["source"] found_c.update({"vrf_name": found_c["vrfName"]}) found_c.update({"vrf_id": found_c["vrfId"]}) @@ -2148,16 +2143,16 @@ def push_to_remote(self, is_rollback=False): } if self.dcnm_version > 11: - t_conf.update(isRPAbsent = json_to_dict.get("isRPAbsent")) - t_conf.update(ENABLE_NETFLOW = json_to_dict.get("ENABLE_NETFLOW")) - t_conf.update(NETFLOW_MONITOR = json_to_dict.get("NETFLOW_MONITOR")) - t_conf.update(disableRtAuto = json_to_dict.get("disableRtAuto")) - t_conf.update(routeTargetImport = json_to_dict.get("routeTargetImport")) - t_conf.update(routeTargetExport = json_to_dict.get("routeTargetExport")) - t_conf.update(routeTargetImportEvpn = json_to_dict.get("routeTargetImportEvpn")) - t_conf.update(routeTargetExportEvpn = json_to_dict.get("routeTargetExportEvpn")) - t_conf.update(routeTargetImportMvpn = json_to_dict.get("routeTargetImportMvpn")) - t_conf.update(routeTargetExportMvpn = json_to_dict.get("routeTargetExportMvpn")) + t_conf.update(isRPAbsent=json_to_dict.get("isRPAbsent")) + t_conf.update(ENABLE_NETFLOW=json_to_dict.get("ENABLE_NETFLOW")) + t_conf.update(NETFLOW_MONITOR=json_to_dict.get("NETFLOW_MONITOR")) + t_conf.update(disableRtAuto=json_to_dict.get("disableRtAuto")) + t_conf.update(routeTargetImport=json_to_dict.get("routeTargetImport")) + t_conf.update(routeTargetExport=json_to_dict.get("routeTargetExport")) + t_conf.update(routeTargetImportEvpn=json_to_dict.get("routeTargetImportEvpn")) + t_conf.update(routeTargetExportEvpn=json_to_dict.get("routeTargetExportEvpn")) + t_conf.update(routeTargetImportMvpn=json_to_dict.get("routeTargetImportMvpn")) + t_conf.update(routeTargetExportMvpn=json_to_dict.get("routeTargetExportMvpn")) vrf.update({"vrfTemplateConfig": json.dumps(t_conf)}) @@ -2173,7 +2168,7 @@ def push_to_remote(self, is_rollback=False): if self.diff_attach: for d_a in self.diff_attach: for v_a in d_a["lanAttachList"]: - v_a.update(vlan = 0) + v_a.update(vlan=0) if v_a.get("vrf_lite"): """Before apply the vrf_lite config, need double check if the switch role is started wth Border""" r = re.search(r"\bborder\b", self.role.lower()) diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/merged_net_all.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/merged_net_all.yaml index 7c8af046a..de8b230f5 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/merged_net_all.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/merged_net_all.yaml @@ -71,4 +71,3 @@ cisco.dcnm.dcnm_network: fabric: "{{ test_fabric }}" state: deleted - diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/overridden_net_all.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/overridden_net_all.yaml index a8c7ad317..65fdc8ae3 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/overridden_net_all.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/overridden_net_all.yaml @@ -130,4 +130,3 @@ cisco.dcnm.dcnm_network: fabric: "{{ test_fabric }}" state: deleted - diff --git a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/replaced_net_all.yaml b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/replaced_net_all.yaml index b1ce2baa5..1f4352bb9 100644 --- a/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/replaced_net_all.yaml +++ b/tests/integration/targets/dcnm_network/tests/dcnm/self-contained-tests/replaced_net_all.yaml @@ -126,4 +126,3 @@ cisco.dcnm.dcnm_network: fabric: "{{ test_fabric }}" state: deleted - From c40c6dfdd6625d99b0fd7133c5d965fb6906e46d Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Fri, 10 Mar 2023 16:18:35 +0530 Subject: [PATCH 5/8] UT/IT commit for network and vrf --- tests/unit/modules/dcnm/test_dcnm_net.py | 9 ++++----- tests/unit/modules/dcnm/test_dcnm_vrf.py | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/unit/modules/dcnm/test_dcnm_net.py b/tests/unit/modules/dcnm/test_dcnm_net.py index 2faa937e0..d0a3a0d66 100644 --- a/tests/unit/modules/dcnm/test_dcnm_net.py +++ b/tests/unit/modules/dcnm/test_dcnm_net.py @@ -699,14 +699,14 @@ def test_dcnm_net_query_with_config(self): self.assertEqual(result.get("response")[0]["parent"]["networkName"], "test_network") self.assertEqual(result.get("response")[0]["parent"]["networkId"], 9008011) self.assertTrue( - result.get("response")[0]["attach"][0][ "deployment"], + result.get("response")[0]["attach"][0]["deployment"], ) self.assertEqual( result.get("response")[0]["attach"][0]["vlan"], 202, ) self.assertTrue( - result.get("response")[0]["attach"][1][ "deployment"], + result.get("response")[0]["attach"][1]["deployment"], ) self.assertEqual( result.get("response")[0]["attach"][1]["vlan"], @@ -722,17 +722,16 @@ def test_dcnm_net_query_without_config(self): self.assertEqual(result.get("response")[0]["parent"]["networkName"], "test_network") self.assertEqual(result.get("response")[0]["parent"]["networkId"], 9008011) self.assertTrue( - result.get("response")[0]["attach"][0][ "deployment"], + result.get("response")[0]["attach"][0]["deployment"], ) self.assertEqual( result.get("response")[0]["attach"][0]["vlan"], 202, ) self.assertTrue( - result.get("response")[0]["attach"][1][ "deployment"], + result.get("response")[0]["attach"][1]["deployment"], ) self.assertEqual( result.get("response")[0]["attach"][1]["vlan"], 202, ) - diff --git a/tests/unit/modules/dcnm/test_dcnm_vrf.py b/tests/unit/modules/dcnm/test_dcnm_vrf.py index 961fb355b..289e46aca 100644 --- a/tests/unit/modules/dcnm/test_dcnm_vrf.py +++ b/tests/unit/modules/dcnm/test_dcnm_vrf.py @@ -171,7 +171,6 @@ def load_fixtures(self, response=None, device=""): else: self.run_dcnm_version_supported.return_value = 11 - if "vrf_blank_fabric" in self._testMethodName: self.run_dcnm_ip_sn.side_effect = [{}] else: From 8ce1b80d89bdab3e9a92bc9c4fbfc5dd9c9819e2 Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Fri, 10 Mar 2023 21:31:51 +0530 Subject: [PATCH 6/8] UT files name change for network module --- .../modules/dcnm/fixtures/{dcnm_net.json => dcnm_network.json} | 0 .../modules/dcnm/{test_dcnm_net.py => test_dcnm_network.py} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/unit/modules/dcnm/fixtures/{dcnm_net.json => dcnm_network.json} (100%) rename tests/unit/modules/dcnm/{test_dcnm_net.py => test_dcnm_network.py} (99%) diff --git a/tests/unit/modules/dcnm/fixtures/dcnm_net.json b/tests/unit/modules/dcnm/fixtures/dcnm_network.json similarity index 100% rename from tests/unit/modules/dcnm/fixtures/dcnm_net.json rename to tests/unit/modules/dcnm/fixtures/dcnm_network.json diff --git a/tests/unit/modules/dcnm/test_dcnm_net.py b/tests/unit/modules/dcnm/test_dcnm_network.py similarity index 99% rename from tests/unit/modules/dcnm/test_dcnm_net.py rename to tests/unit/modules/dcnm/test_dcnm_network.py index d0a3a0d66..e5042302e 100644 --- a/tests/unit/modules/dcnm/test_dcnm_net.py +++ b/tests/unit/modules/dcnm/test_dcnm_network.py @@ -32,7 +32,7 @@ class TestDcnmNetworkModule(TestDcnmModule): module = dcnm_network - test_data = loadPlaybookData("dcnm_net") + test_data = loadPlaybookData("dcnm_network") SUCCESS_RETURN_CODE = 200 From 3e30507e2f9b0d293892ec7cdc7744de97e5a2a8 Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Fri, 10 Mar 2023 21:38:48 +0530 Subject: [PATCH 7/8] UT files name change for network module --- tests/unit/modules/dcnm/test_dcnm_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/modules/dcnm/test_dcnm_network.py b/tests/unit/modules/dcnm/test_dcnm_network.py index e5042302e..3b0bc2980 100644 --- a/tests/unit/modules/dcnm/test_dcnm_network.py +++ b/tests/unit/modules/dcnm/test_dcnm_network.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2023 Cisco and/or its affiliates. +# Copyright (c) 2023 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 3f3e4a1b663ec15bac7f936612cd3d305e9dffad Mon Sep 17 00:00:00 2001 From: Praveen Ramoorthy Date: Tue, 14 Mar 2023 14:48:42 +0530 Subject: [PATCH 8/8] Review comments addressed --- docs/cisco.dcnm.dcnm_network_module.rst | 1 + docs/cisco.dcnm.dcnm_vrf_module.rst | 2 +- plugins/modules/dcnm_network.py | 2 + plugins/modules/dcnm_vrf.py | 2 +- .../dcnm/self-contained-tests/scale.yaml | 10 +- .../targets/dcnm_vrf/tests/dcnm/deleted.yaml | 90 ++++++------- .../targets/dcnm_vrf/tests/dcnm/merged.yaml | 102 ++++++++------- .../dcnm_vrf/tests/dcnm/overridden.yaml | 106 +++++++++------- .../targets/dcnm_vrf/tests/dcnm/query.yaml | 70 +++++------ .../targets/dcnm_vrf/tests/dcnm/replaced.yaml | 118 +++++++++++------- .../self-contained-tests/deleted_vrf_all.yaml | 26 ++-- .../self-contained-tests/merged_vrf_all.yaml | 24 ++-- .../overridden_vrf_all.yaml | 40 +++--- .../replaced_vrf_all.yaml | 40 +++--- .../dcnm/self-contained-tests/scale.yaml | 22 ++-- 15 files changed, 353 insertions(+), 302 deletions(-) diff --git a/docs/cisco.dcnm.dcnm_network_module.rst b/docs/cisco.dcnm.dcnm_network_module.rst index 057b2f983..749f8acf9 100644 --- a/docs/cisco.dcnm.dcnm_network_module.rst +++ b/docs/cisco.dcnm.dcnm_network_module.rst @@ -137,6 +137,7 @@ Parameters
list + / elements=string / required
diff --git a/docs/cisco.dcnm.dcnm_vrf_module.rst b/docs/cisco.dcnm.dcnm_vrf_module.rst index 3e629d1e9..b09eaa15e 100644 --- a/docs/cisco.dcnm.dcnm_vrf_module.rst +++ b/docs/cisco.dcnm.dcnm_vrf_module.rst @@ -959,7 +959,7 @@ Parameters
VRF Vlan Name
-
if > 32 chars enable:system vlan long-name
+
if > 32 chars enable - system vlan long-name
diff --git a/plugins/modules/dcnm_network.py b/plugins/modules/dcnm_network.py index c9b814a20..7b913ee22 100644 --- a/plugins/modules/dcnm_network.py +++ b/plugins/modules/dcnm_network.py @@ -247,6 +247,7 @@ description: - List of switch interfaces where the network will be attached type: list + elements: str required: true deploy: description: @@ -2533,6 +2534,7 @@ def validate_input(self): else: + # The default value for multicast group address is different for DCNM and NDFC. if self.dcnm_version > 11: mcast_group_addr = "239.1.1.1" else: diff --git a/plugins/modules/dcnm_vrf.py b/plugins/modules/dcnm_vrf.py index e27c657b9..cf92a2a05 100644 --- a/plugins/modules/dcnm_vrf.py +++ b/plugins/modules/dcnm_vrf.py @@ -87,7 +87,7 @@ vrf_vlan_name: description: - VRF Vlan Name - - if > 32 chars enable:system vlan long-name + - if > 32 chars enable - system vlan long-name type: str required: false vrf_intf_desc: 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 cace9f30f..43c54a138 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 @@ -25,10 +25,6 @@ fabric: "{{ test_fabric }}" state: deleted -- name: SCALE - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: Dummy set fact for leaf_attach_list set_fact: leaf_net_attach: [] @@ -50,10 +46,6 @@ fabric: "{{ test_fabric }}" state: deleted -- name: SCALE - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: SCALE - conf - Idempotence cisco.dcnm.dcnm_network: *conf register: result @@ -71,4 +63,4 @@ - name: SCALE - Clean up any existing networks cisco.dcnm.dcnm_network: fabric: "{{ test_fabric }}" - state: deleted \ No newline at end of file + state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/deleted.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/deleted.yaml index 6178b723f..0f7b08217 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/deleted.yaml +++ b/tests/integration/targets/dcnm_vrf/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 is deployed. @@ -22,16 +22,12 @@ - name: DELETED - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - 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 new VRF - VLAN Provided by the User cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -47,6 +43,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -61,17 +67,13 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: DELETED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - ############################################### ### DELETED ## ############################################### - name: DELETED - Delete VRF using deleted state cisco.dcnm.dcnm_vrf: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted config: - vrf_name: ansible-vrf-int1 @@ -96,10 +98,6 @@ - 'result.diff[0].attach[1].deploy == false' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: DELETED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: DELETED - conf - Idempotence cisco.dcnm.dcnm_vrf: *conf register: result @@ -110,13 +108,9 @@ - 'result.response|length == 0' - 'result.diff|length == 0' -- name: DELETED - sleep for 10 seconds for DCNM to completely update the state - wait_for: - timeout: 10 - - name: DELETED - Create, Attach and Deploy new VRF - VLAN/VRF LITE EXTENSION Provided by the User in one switch cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -140,6 +134,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -154,13 +158,9 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: DELETED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: DELETED - Delete VRF/VRF LITE - VLAN/VRF LITE EXTENSION Provided by the User in one switch cisco.dcnm.dcnm_vrf: &conf1 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted config: - vrf_name: ansible-vrf-int1 @@ -198,10 +198,6 @@ - 'result.diff[0].attach[1].deploy == false' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: DELETED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: DELETED - conf1 - Idempotence cisco.dcnm.dcnm_vrf: *conf1 register: result @@ -212,13 +208,9 @@ - 'result.response|length == 0' - 'result.diff|length == 0' -- name: DELETED - sleep for 10 seconds for DCNM to completely update the state - wait_for: - timeout: 10 - - name: DELETED - Create, Attach and Deploy new VRF - VLAN/VRF LITE EXTENSION Provided by the User in one switch cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -242,6 +234,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -256,13 +258,9 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: DELETED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: DELETED - Delete VRF/VRF LITE - WITHOUT ANY CONFIG ELEMENT cisco.dcnm.dcnm_vrf: &conf2 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted config: register: result @@ -281,10 +279,6 @@ - 'result.diff[0].attach[1].deploy == false' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: DELETED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: DELETED - conf1 - Idempotence cisco.dcnm.dcnm_vrf: *conf2 register: result @@ -295,15 +289,11 @@ - 'result.response|length == 0' - 'result.diff|length == 0' -- name: DELETED - sleep for 10 seconds for DCNM to completely update the state - wait_for: - timeout: 10 - ################################################ #### CLEAN-UP ## ################################################ - name: DELETED - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/merged.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/merged.yaml index f9688073e..7ee0dbd0c 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/merged.yaml +++ b/tests/integration/targets/dcnm_vrf/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 is deployed. @@ -22,20 +22,16 @@ - name: MERGED - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - 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, Attach and Deploy new VRF - VLAN Provided by the User cisco.dcnm.dcnm_vrf: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -51,6 +47,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -65,10 +71,6 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - conf1 - Idempotence cisco.dcnm.dcnm_vrf: *conf register: result @@ -80,16 +82,12 @@ - name: MERGED - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 10 seconds for DCNM to completely update the state - wait_for: - timeout: 10 - - name: MERGED - Create, Attach and Deploy new VRF - VLAN Provided by the DCNM cisco.dcnm.dcnm_vrf: &conf2 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -104,6 +102,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -118,10 +126,6 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - conf2 - Idempotence cisco.dcnm.dcnm_vrf: *conf2 register: result @@ -133,16 +137,12 @@ - name: MERGED - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 10 seconds for DCNM to completely update the state - wait_for: - timeout: 10 - - name: MERGED - Create, Attach and Deploy new VRF - VLAN/VRF LITE EXTENSION Provided by the User in one switch cisco.dcnm.dcnm_vrf: &conf3 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -166,6 +166,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -180,10 +190,6 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - conf3 - Idempotence cisco.dcnm.dcnm_vrf: *conf3 register: result @@ -195,16 +201,12 @@ - name: MERGED - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: MERGED - sleep for 10 seconds for DCNM to completely update the state - wait_for: - timeout: 10 - - name: MERGED - Create, Attach and Deploy new VRF - VRF/VRF LITE EXTENSION Provided by the DCNM - Only Mandatory option - Rest are populated from DCNM cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -222,6 +224,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -236,13 +248,9 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: MERGED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED - Create, Attach and Deploy new VRF - Update with incorrect VRF ID. cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -265,7 +273,7 @@ - name: MERGED - Create, Attach and Deploy new VRF - Update with Out of Range VRF ID. cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -288,7 +296,7 @@ - name: MERGED - Create, Attach and Deploy new VRF - Try configuring VRF LITE without mandatory parameter cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -314,7 +322,7 @@ - name: MERGED - Create, Attach and Deploy new VRF - Try configuring VRF LITE to a non border switch cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -349,5 +357,5 @@ - name: MERGED - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/overridden.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/overridden.yaml index f3f2241ad..31dc6f8ca 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/overridden.yaml +++ b/tests/integration/targets/dcnm_vrf/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 is deployed. @@ -22,16 +22,12 @@ - name: OVERRIDDEN - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - 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 new VRF - VLAN Provided by the User cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -47,6 +43,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -61,17 +67,13 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: OVERRIDDEN - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - ############################################### ### OVERRIDDEN ## ############################################### - name: OVERRIDDEN - Update existing VRF using overridden - delete and create cisco.dcnm.dcnm_vrf: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: overridden config: - vrf_name: ansible-vrf-int2 @@ -87,6 +89,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -107,10 +119,6 @@ - 'result.diff[1].attach[1].deploy == false' - 'result.diff[1].vrf_name == "ansible-vrf-int1"' -- name: OVERRIDDEN - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: OVERRIDDEN - conf - Idempotence cisco.dcnm.dcnm_vrf: *conf register: result @@ -120,22 +128,14 @@ - 'result.changed == false' - 'result.response|length == 0' -- name: OVERRIDDEN - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: OVERRIDDEN - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: OVERRIDDEN - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: OVERRIDDEN - Create, Attach and Deploy new VRF - VRF/VRF LITE EXTENSION Provided by the User in one switch cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int2 @@ -159,6 +159,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -173,13 +183,9 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int2"' -- name: OVERRIDDEN - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: OVERRIDDEN - Update existing VRF LITE using overridden - VRF stays Whereas Ext is modified/overridden cisco.dcnm.dcnm_vrf: &conf1 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: overridden config: - vrf_name: ansible-vrf-int2 @@ -203,6 +209,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -212,10 +228,6 @@ - 'result.diff[0].attach[0].deploy == true' - 'result.diff[0].vrf_name == "ansible-vrf-int2"' -- name: OVERRIDDEN - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: OVERRIDDEN - conf - Idempotence cisco.dcnm.dcnm_vrf: *conf1 register: result @@ -225,13 +237,9 @@ - 'result.changed == false' - 'result.response|length == 0' -- name: OVERRIDDEN - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: OVERRIDDEN - Update existing VRF LITE using overridden - VRF modified and Ext is modified - Old ones deleted cisco.dcnm.dcnm_vrf: &conf2 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: overridden config: - vrf_name: ansible-vrf-int1 @@ -255,6 +263,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -276,10 +294,6 @@ - 'result.diff[1].attach[1].deploy == false' - 'result.diff[1].vrf_name == "ansible-vrf-int2"' -- name: OVERRIDDEN - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: OVERRIDDEN - conf - Idempotence cisco.dcnm.dcnm_vrf: *conf2 register: result @@ -295,5 +309,5 @@ - name: OVERRIDDEN - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/query.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/query.yaml index 5032920a9..f30d77e4d 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/query.yaml +++ b/tests/integration/targets/dcnm_vrf/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: QUERY - Verify if fabric is deployed. @@ -22,16 +22,12 @@ - name: QUERY - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: QUERY - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: QUERY - Create, Attach and Deploy new VRF - VLAN Provided by the User cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -47,6 +43,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -61,17 +67,13 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - ############################################### ### QUERY ## ############################################### - name: QUERY - Query the VRF cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query config: - vrf_name: ansible-vrf-int1 @@ -100,13 +102,9 @@ - 'result.response[0].attach[1].switchDetailsList[0].lanAttachedState == "DEPLOYED"' - 'result.response[0].attach[1].switchDetailsList[0].vlan == 500' -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: QUERY - Clean up existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted register: result @@ -124,13 +122,9 @@ - 'result.diff[0].attach[1].deploy == false' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: QUERY - Create, Attach and Deploy new VRF - VRF/VRF LITE EXTENSION Provided by the User in one switch cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int2 @@ -154,6 +148,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -168,13 +172,9 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int2"' -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: QUERY - Query the - VRF/VRF LITE EXTENSION Provided by the User in one switch cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query config: - vrf_name: ansible-vrf-int2 @@ -211,13 +211,9 @@ - 'result.response[0].attach[1].switchDetailsList[0].lanAttachedState == "DEPLOYED"' - 'result.response[0].attach[1].switchDetailsList[0].vlan == 1500' -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: QUERY - Query without the config element cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query register: result @@ -234,13 +230,9 @@ - 'result.response[0].attach[1].switchDetailsList[0].lanAttachedState == "DEPLOYED"' - 'result.response[0].attach[1].switchDetailsList[0].vlan == 1500' -- name: QUERY - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: QUERY - Query the non available VRF cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: query config: - vrf_name: ansible-vrf-int1 @@ -267,5 +259,5 @@ - name: QUERY - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/replaced.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/replaced.yaml index d0aaac228..41132de15 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/replaced.yaml +++ b/tests/integration/targets/dcnm_vrf/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 is deployed. @@ -22,16 +22,12 @@ - name: REPLACED - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - 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 new VRF - VLAN Provided by the User cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -47,6 +43,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -59,17 +65,13 @@ - 'result.diff[0].attach[1].deploy == true' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: REPLACED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - ############################################### ### REPLACED ## ############################################### - name: REPLACED - Update existing VRF using replace - delete attachments cisco.dcnm.dcnm_vrf: &conf1 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: replaced config: - vrf_name: ansible-vrf-int1 @@ -81,6 +83,16 @@ service_vrf_template: null register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -92,10 +104,6 @@ - 'result.diff[0].attach[1].deploy == false' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - conf1 - Idempotence cisco.dcnm.dcnm_vrf: *conf1 register: result @@ -106,7 +114,7 @@ - name: REPLACED - Update existing VRF using replace - create attachments cisco.dcnm.dcnm_vrf: &conf2 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: replaced config: - vrf_name: ansible-vrf-int1 @@ -122,6 +130,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -135,10 +153,6 @@ - 'result.diff[0].attach[0].vlan_id == 500' - 'result.diff[0].attach[1].vlan_id == 500' -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - conf2 - Idempotence cisco.dcnm.dcnm_vrf: *conf2 register: result @@ -149,16 +163,12 @@ - name: REPLACED - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - 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 new VRF - VLAN/VRF LITE EXTENSION Provided by the User in one switch cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -182,6 +192,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -196,13 +216,9 @@ - '"{{ ansible_switch2 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: REPLACED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: REPLACED - Update existing VRF LITE extensions using Replace - Delete VRF LITE Attachment Only cisco.dcnm.dcnm_vrf: &conf3 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: replaced config: - vrf_name: ansible-vrf-int1 @@ -217,6 +233,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -226,10 +252,6 @@ - 'result.diff[0].attach[0].deploy == false' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - conf3 - Idempotence cisco.dcnm.dcnm_vrf: *conf3 register: result @@ -238,13 +260,9 @@ that: - 'result.changed == false' -- name: REPLACED - sleep for 20 seconds for DCNM to completely update the state - wait_for: - timeout: 20 - - name: REPLACED - Update existing VRF LITE extensions using Replace - Create VRF LITE Attachment Only cisco.dcnm.dcnm_vrf: &conf4 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: replaced config: - vrf_name: ansible-vrf-int1 @@ -268,6 +286,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -278,10 +306,6 @@ - 'result.diff[0].vrf_name == "ansible-vrf-int1"' - 'result.diff[0].attach[0].vlan_id == 500' -- name: REPLACED - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED - conf4 - Idempotence cisco.dcnm.dcnm_vrf: *conf4 register: result @@ -296,5 +320,5 @@ - name: REPLACED - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/deleted_vrf_all.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/deleted_vrf_all.yaml index c12f8f01b..16f4222ec 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/deleted_vrf_all.yaml +++ b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/deleted_vrf_all.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: DELETE_ALL - Verify if fabric is deployed. @@ -22,7 +22,7 @@ - name: DELETE_ALL - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted ############################################### @@ -31,7 +31,7 @@ - name: DELETE_ALL - Create, Attach and Deploy new VRF with all values cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -60,6 +60,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -74,13 +84,9 @@ - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: DELETE_ALL - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: DELETE_ALL - Clean existing vrfs cisco.dcnm.dcnm_vrf: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted register: result @@ -111,5 +117,5 @@ - name: DELETE_ALL - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/merged_vrf_all.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/merged_vrf_all.yaml index f6b9ab259..0f3f69c87 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/merged_vrf_all.yaml +++ b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/merged_vrf_all.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_ALL - Verify if fabric is deployed. @@ -22,7 +22,7 @@ - name: MERGED_ALL - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted ############################################### @@ -31,7 +31,7 @@ - name: MERGED_ALL - Create, Attach and Deploy new VRF with all values cisco.dcnm.dcnm_vrf: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -60,6 +60,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -74,10 +84,6 @@ - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: MERGED_ALL - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: MERGED_ALL - conf - Idempotence cisco.dcnm.dcnm_vrf: *conf register: result @@ -93,5 +99,5 @@ - name: MERGED_ALL - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/overridden_vrf_all.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/overridden_vrf_all.yaml index c963aaabb..f5d518ba1 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/overridden_vrf_all.yaml +++ b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/overridden_vrf_all.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_ALL - Verify if fabric is deployed. @@ -22,7 +22,7 @@ - name: OVERRIDDEN_ALL - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted ############################################### @@ -31,7 +31,7 @@ - name: OVERRIDDEN_ALL - Create, Attach and Deploy new VRF with all values cisco.dcnm.dcnm_vrf: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -60,6 +60,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -74,10 +84,6 @@ - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: OVERRIDDEN_ALL - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: OVERRIDDEN_ALL - conf - Idempotence cisco.dcnm.dcnm_vrf: *conf register: result @@ -89,7 +95,7 @@ - name: OVERRIDDEN_ALL - Override a existing VRF with a new one cisco.dcnm.dcnm_vrf: &conf1 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: overridden config: - vrf_name: ansible-vrf-int2 @@ -118,6 +124,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -142,10 +158,6 @@ - 'result.diff[0].vrf_name == "ansible-vrf-int2"' - 'result.diff[1].vrf_name == "ansible-vrf-int1"' -- name: OVERRIDDEN_ALL - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: OVERRIDDEN_ALL - conf1 - Idempotence cisco.dcnm.dcnm_vrf: *conf1 register: result @@ -161,5 +173,5 @@ - name: OVERRIDDEN_ALL - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/replaced_vrf_all.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/replaced_vrf_all.yaml index c00b5debd..010bca5d8 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/replaced_vrf_all.yaml +++ b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/replaced_vrf_all.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_ALL - Verify if fabric is deployed. @@ -22,7 +22,7 @@ - name: REPLACED_ALL - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted ############################################### @@ -31,7 +31,7 @@ - name: REPLACED_ALL - Create, Attach and Deploy new VRF with all values cisco.dcnm.dcnm_vrf: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: merged config: - vrf_name: ansible-vrf-int1 @@ -60,6 +60,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -74,10 +84,6 @@ - '"{{ ansible_switch2 }}" or "{{ ansible_switch1 }}" in result.diff[0].attach[1].ip_address' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: REPLACED_ALL - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED_ALL - conf - Idempotence cisco.dcnm.dcnm_vrf: *conf register: result @@ -89,7 +95,7 @@ - name: REPLACED_ALL - Replace VRF with all values cisco.dcnm.dcnm_vrf: &conf1 - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: replaced config: - vrf_name: ansible-vrf-int1 @@ -118,6 +124,16 @@ deploy: true register: result +- name: Query fabric state until vrfStatus transitions to DEPLOYED state + cisco.dcnm.dcnm_vrf: + fabric: "{{ test_fabric }}" + state: query + register: query_result + until: + - "query_result.response[0].parent.vrfStatus is search('DEPLOYED')" + retries: 30 + delay: 2 + - assert: that: - 'result.changed == true' @@ -125,10 +141,6 @@ - 'result.response[1].RETURN_CODE == 200' - 'result.diff[0].vrf_name == "ansible-vrf-int1"' -- name: REPLACED_ALL - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: REPLACED_ALL - conf1 - Idempotence cisco.dcnm.dcnm_vrf: *conf1 register: result @@ -144,5 +156,5 @@ - name: REPLACED_ALL - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted diff --git a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/scale.yaml b/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/scale.yaml index 4abe5e06d..76b82792c 100644 --- a/tests/integration/targets/dcnm_vrf/tests/dcnm/self-contained-tests/scale.yaml +++ b/tests/integration/targets/dcnm_vrf/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,13 +22,9 @@ - name: SCALE - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: SCALE - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: Dummy set fact for leaf_attach_list set_fact: leaf_vrf_attach: [] @@ -40,20 +36,16 @@ - name: Push all VRFs to DCNM cisco.dcnm.dcnm_vrf: - fabric: '{{ ansible_it_fabric }}' + fabric: '{{ test_fabric }}' state: merged config: '{{ vrfs_list }}' register: result - name: SCALE - Clean up existing vrfs cisco.dcnm.dcnm_vrf: &conf - fabric: "{{ ansible_it_fabric }}" + fabric: "{{ test_fabric }}" state: deleted -- name: SCALE - sleep for 40 seconds for DCNM to completely update the state - wait_for: - timeout: 40 - - name: SCALE - conf - Idempotence cisco.dcnm.dcnm_vrf: *conf register: result @@ -70,5 +62,5 @@ - name: SCALE - Clean up any existing vrfs cisco.dcnm.dcnm_vrf: - fabric: "{{ ansible_it_fabric }}" - state: deleted \ No newline at end of file + fabric: "{{ test_fabric }}" + state: deleted