@@ -835,14 +835,17 @@ def to_bool(self, key, dict_with_key):
835835
836836 # pylint: enable=inconsistent-return-statements
837837 @staticmethod
838- def compare_properties (dict1 , dict2 , property_list ):
838+ def compare_properties (dict1 , dict2 , property_list , skip_prop = None ):
839839 """
840- Given two dictionaries and a list of keys:
840+ Given two dictionaries, a list of keys and keys that can be
841+ skipped, compare the values of the keys in both dictionaries:
841842
842843 - Return True if all property values match.
843844 - Return False otherwise
844845 """
845846 for prop in property_list :
847+ if skip_prop and prop in skip_prop :
848+ continue
846849 if dict1 .get (prop ) != dict2 .get (prop ):
847850 return False
848851 return True
@@ -954,8 +957,11 @@ def diff_for_attach_deploy(self, want_a, have_a, replace=False):
954957 continue
955958 found = True
956959 interface_match = True
960+ skip_prop = []
961+ if not wlite ["DOT1Q_ID" ]:
962+ skip_prop .append ("DOT1Q_ID" )
957963 if not self .compare_properties (
958- wlite , hlite , self .vrf_lite_properties
964+ wlite , hlite , self .vrf_lite_properties , skip_prop
959965 ):
960966 found = False
961967 break
@@ -1216,14 +1222,16 @@ def update_attach_params_extension_values(self, attach) -> dict:
12161222 else :
12171223 extension_values ["VRF_LITE_CONN" ] = copy .deepcopy (vrf_lite_connections )
12181224
1219- extension_values ["VRF_LITE_CONN" ] = json .dumps (
1220- extension_values ["VRF_LITE_CONN" ]
1221- )
1222-
1223- msg = "Returning extension_values: "
1225+ msg = "Building extension_values: "
12241226 msg += f"{ json .dumps (extension_values , indent = 4 , sort_keys = True )} "
12251227 self .log .debug (msg )
12261228
1229+ extension_values ["VRF_LITE_CONN" ] = json .dumps (
1230+ extension_values ["VRF_LITE_CONN" ]
1231+ )
1232+ msg = "Returning extension_values: "
1233+ msg += f"{ json .dumps (extension_values , indent = 4 , sort_keys = True )} "
1234+ self .log .debug (msg )
12271235 return copy .deepcopy (extension_values )
12281236
12291237 def update_attach_params (self , attach , vrf_name , deploy , vlan_id ) -> dict :
@@ -1393,10 +1401,13 @@ def diff_for_create(self, want, have):
13931401 # remove it here (as we did with the other params that are
13941402 # compared in the call to self.dict_values_differ())
13951403 vlan_id_want = str (json_to_dict_want .get ("vrfVlanId" , "" ))
1404+ vrfSegmentId_want = json_to_dict_want .get ("vrfSegmentId" )
13961405
13971406 skip_keys = []
13981407 if vlan_id_want == "0" :
13991408 skip_keys = ["vrfVlanId" ]
1409+ if vrfSegmentId_want is None :
1410+ skip_keys .append ("vrfSegmentId" )
14001411 templates_differ = self .dict_values_differ (
14011412 json_to_dict_want , json_to_dict_have , skip_keys = skip_keys
14021413 )
@@ -3093,6 +3104,54 @@ def get_extension_values_from_lite_objects(self, lite: list[dict]) -> list:
30933104
30943105 return extension_values_list
30953106
3107+ def get_vrf_lite_dot1q_id (self , serial_number : str , vrf_name : str , interface : str ) -> int :
3108+ """
3109+ # Summary
3110+
3111+ Given a switch serial, vrf name and ifname, return the dot1q ID
3112+ reserved for the vrf_lite extension on that switch.
3113+
3114+ ## Raises
3115+
3116+ Calls fail_json if DNCM fails to reserve the dot1q ID.
3117+ """
3118+ caller = inspect .stack ()[1 ][3 ]
3119+
3120+ msg = "ENTERED. "
3121+ msg += f"caller: { caller } . "
3122+ msg += f"serial_number: { serial_number } "
3123+ msg += f"vrf name: { vrf_name } "
3124+ msg += f"interface: { interface } "
3125+ self .log .debug (msg )
3126+
3127+ dot1q_id = None
3128+ path = "/appcenter/cisco/ndfc/api/v1/lan-fabric"
3129+ path += "/rest/resource-manager/reserve-id"
3130+ verb = "POST"
3131+ payload = {"scopeType" : "DeviceInterface" ,
3132+ "usageType" : "TOP_DOWN_L3_DOT1Q" ,
3133+ "serialNumber" : serial_number ,
3134+ "ifName" : interface ,
3135+ "allocatedTo" : vrf_name }
3136+
3137+ resp = dcnm_send (self .module , verb , path , json .dumps (payload ))
3138+ if resp .get ("RETURN_CODE" ) != 200 :
3139+ msg = f"{ self .class_name } .get_vrf_lite_dot1q_id: "
3140+ msg += f"caller: { caller } . "
3141+ msg += "Failed to get dot1q ID for vrf_lite extension on switch "
3142+ msg += f"{ serial_number } for vrf { vrf_name } and interface { interface } . "
3143+ msg += f"Response: { resp } "
3144+ self .module .fail_json (msg = msg )
3145+ else :
3146+ msg = f"{ self .class_name } .get_vrf_lite_dot1q_id: "
3147+ msg += f"caller: { caller } . "
3148+ msg += "Successfully got dot1q ID for vrf_lite extension on switch "
3149+ msg += f"{ serial_number } for vrf { vrf_name } and interface { interface } . "
3150+ msg += f"Response: { resp } "
3151+ self .log .debug (msg )
3152+ dot1q_id = resp .get ("DATA" )
3153+ return dot1q_id
3154+
30963155 def update_vrf_attach_vrf_lite_extensions (self , vrf_attach , lite ) -> dict :
30973156 """
30983157 # Summary
@@ -3223,7 +3282,20 @@ def update_vrf_attach_vrf_lite_extensions(self, vrf_attach, lite) -> dict:
32233282 if item ["user" ]["dot1q" ]:
32243283 nbr_dict ["DOT1Q_ID" ] = str (item ["user" ]["dot1q" ])
32253284 else :
3226- nbr_dict ["DOT1Q_ID" ] = str (item ["switch" ]["DOT1Q_ID" ])
3285+ dot1q_vlan = self .get_vrf_lite_dot1q_id (
3286+ serial_number ,
3287+ vrf_attach .get ("vrfName" ),
3288+ nbr_dict ["IF_NAME" ]
3289+ )
3290+ if dot1q_vlan is not None :
3291+ nbr_dict ["DOT1Q_ID" ] = str (dot1q_vlan )
3292+ else :
3293+ msg = f"{ self .class_name } .{ method_name } : "
3294+ msg += f"caller: { caller } . "
3295+ msg += "Failed to get dot1q ID for vrf_lite extension "
3296+ msg += f"on switch { serial_number } for vrf { vrf_attach .get ('vrfName' )} "
3297+ msg += f"and interface { nbr_dict ['IF_NAME' ]} "
3298+ self .module .fail_json (msg = msg )
32273299
32283300 if item ["user" ]["ipv4_addr" ]:
32293301 nbr_dict ["IP_MASK" ] = item ["user" ]["ipv4_addr" ]
@@ -3269,9 +3341,9 @@ def update_vrf_attach_vrf_lite_extensions(self, vrf_attach, lite) -> dict:
32693341 ms_con ["MULTISITE_CONN" ] = []
32703342 extension_values ["MULTISITE_CONN" ] = json .dumps (ms_con )
32713343
3272- extension_values ["VRF_LITE_CONN" ] = json .dumps (
3273- extension_values ["VRF_LITE_CONN" ]
3274- )
3344+ extension_values ["VRF_LITE_CONN" ] = json .dumps (
3345+ extension_values ["VRF_LITE_CONN" ]
3346+ )
32753347 vrf_attach ["extensionValues" ] = json .dumps (extension_values ).replace (" " , "" )
32763348 if vrf_attach .get ("vrf_lite" ) is not None :
32773349 del vrf_attach ["vrf_lite" ]
@@ -3736,44 +3808,48 @@ def release_orphaned_resources(self, vrf_del_list, is_rollback=False):
37363808
37373809 path = "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/"
37383810 path += f"resource-manager/fabric/{ self .fabric } /"
3739- path += "pools/TOP_DOWN_VRF_VLAN"
3740- resp = dcnm_send (self .module , "GET" , path )
3741- self .result ["response" ].append (resp )
3742- fail , self .result ["changed" ] = self .handle_response (resp , "deploy" )
3743- if fail :
3744- if is_rollback :
3745- self .failed_to_rollback = True
3746- return
3747- self .failure (resp )
3811+ resource_pool = ["TOP_DOWN_VRF_VLAN" , "TOP_DOWN_L3_DOT1Q" ]
3812+ for pool in resource_pool :
3813+ msg = f"Processing orphaned resources in pool:{ pool } "
3814+ self .log .debug (msg )
3815+ req_path = path + f"pools/{ pool } "
3816+ resp = dcnm_send (self .module , "GET" , req_path )
3817+ self .result ["response" ].append (resp )
3818+ fail , self .result ["changed" ] = self .handle_response (resp , "deploy" )
3819+ if fail :
3820+ if is_rollback :
3821+ self .failed_to_rollback = True
3822+ return
3823+ self .failure (resp )
37483824
3749- delete_ids = []
3750- for item in resp ["DATA" ]:
3751- if "entityName" not in item :
3752- continue
3753- if item ["entityName" ] not in vrf_del_list :
3754- continue
3755- if item .get ("allocatedFlag" ) is not False :
3756- continue
3757- if item .get ("id" ) is None :
3758- continue
3759- # Resources with no ipAddress or switchName
3760- # are invalid and of Fabric's scope and
3761- # should not be attempted to be deleted here.
3762- if not item .get ("ipAddress" ):
3763- continue
3764- if not item .get ("switchName" ):
3765- continue
3825+ delete_ids = []
3826+ for item in resp ["DATA" ]:
3827+ if "entityName" not in item :
3828+ continue
3829+ if item ["entityName" ] not in vrf_del_list :
3830+ continue
3831+ if item .get ("allocatedFlag" ) is not False :
3832+ continue
3833+ if item .get ("id" ) is None :
3834+ continue
3835+ # Resources with no ipAddress or switchName
3836+ # are invalid and of Fabric's scope and
3837+ # should not be attempted to be deleted here.
3838+ if not item .get ("ipAddress" ):
3839+ continue
3840+ if not item .get ("switchName" ):
3841+ continue
37663842
3767- msg = f"item { json .dumps (item , indent = 4 , sort_keys = True )} "
3768- self .log .debug (msg )
3843+ msg = f"item { json .dumps (item , indent = 4 , sort_keys = True )} "
3844+ self .log .debug (msg )
37693845
3770- delete_ids .append (item ["id" ])
3846+ delete_ids .append (item ["id" ])
37713847
3772- if len (delete_ids ) == 0 :
3773- return
3774- msg = f"Releasing orphaned resources with IDs:{ delete_ids } "
3775- self .log .debug (msg )
3776- self .release_resources_by_id (delete_ids )
3848+ if len (delete_ids ) == 0 :
3849+ return
3850+ msg = f"Releasing orphaned resources with IDs:{ delete_ids } "
3851+ self .log .debug (msg )
3852+ self .release_resources_by_id (delete_ids )
37773853
37783854 def push_to_remote (self , is_rollback = False ):
37793855 """
0 commit comments