@@ -5025,6 +5025,24 @@ def dcnm_intf_get_diff_overridden(self, cfg):
50255025 )
50265026 continue
50275027
5028+ # Skip TOR uplink member interfaces
5029+ if have .get ("underlayPolicies" ) is not None :
5030+ is_tor_member = False
5031+ for policy in have .get ("underlayPolicies" ):
5032+ if policy .get ("templateName" ) in ["int_vpc_uplink_access_po_member" ]:
5033+ is_tor_member = True
5034+ break
5035+
5036+ if is_tor_member :
5037+ self .changed_dict [0 ]["skipped" ].append (
5038+ {
5039+ "Name" : name ,
5040+ "Alias" : have ["alias" ],
5041+ "Reason" : "TOR uplink member interface" ,
5042+ }
5043+ )
5044+ continue
5045+
50285046 if str (have ["deletable" ]).lower () == "false" :
50295047 # Add this 'have to a deferred list. We will process this list once we have processed all the 'haves'
50305048 defer_list .append (have )
@@ -5157,6 +5175,48 @@ def dcnm_intf_get_diff_overridden(self, cfg):
51575175 }
51585176 )
51595177 continue
5178+ # Port-channel which are created as part of TOR uplink should not be deleted
5179+ # This includes both TOR-side and leaf-side port-channels:
5180+ # - TOR side: "tor-connected-to-vPC-leaf:" or "tor-connected-to-leaf:"
5181+ # - Leaf side: "leaf-connected-to-vPC-tor:" or "leaf-connected-to-tor:"
5182+ if have .get ("alias" ) is not None and (
5183+ "tor-connected-to" in have .get ("alias" )
5184+ or "leaf-connected-to-vPC-tor" in have .get ("alias" )
5185+ or "leaf-connected-to-tor" in have .get ("alias" )
5186+ ):
5187+ self .changed_dict [0 ]["skipped" ].append (
5188+ {
5189+ "Name" : name ,
5190+ "Alias" : have ["alias" ],
5191+ "Underlay Policies" : have [
5192+ "underlayPolicies"
5193+ ],
5194+ }
5195+ )
5196+ continue
5197+ # Also check if port-channel uses TOR uplink templates
5198+ skip_pc = False
5199+ if have .get ("underlayPolicies" ) is not None :
5200+ tor_uplink_templates = [
5201+ "int_vpc_uplink_access_po" ,
5202+ "int_port_channel_uplink_access" ,
5203+ ]
5204+ for policy in have .get ("underlayPolicies" ):
5205+ if policy .get ("templateName" ) in tor_uplink_templates :
5206+ self .changed_dict [0 ]["skipped" ].append (
5207+ {
5208+ "Name" : name ,
5209+ "Alias" : have ["alias" ],
5210+ "Underlay Policies" : have [
5211+ "underlayPolicies"
5212+ ],
5213+ "Skip Reason" : "TOR uplink port-channel (template match)" ,
5214+ }
5215+ )
5216+ skip_pc = True
5217+ break
5218+ if skip_pc :
5219+ continue
51605220 else :
51615221 self .changed_dict [0 ]["debugs" ].append (
51625222 {
@@ -5168,10 +5228,49 @@ def dcnm_intf_get_diff_overridden(self, cfg):
51685228 }
51695229 )
51705230
5231+ # TOR uplink vPC interfaces should NEVER be deleted through interface override.
5232+ # TOR uplink vPCs are auto-generated by NDFC when TOR switches are paired
5233+ # with leaf switches. These vPC interfaces (vPC1, vPC2, etc.) represent
5234+ # the uplink bundle between TOR and leaf switches.
5235+ #
5236+ # TOR uplink vPCs can be identified by:
5237+ # - ifType: INTERFACE_VPC
5238+ # - underlayPolicies templateName starting with "int_vpc_uplink_"
5239+ # - alias patterns: "tor-connected-to", "leaf-connected-to-vPC-tor"
5240+ if have .get ("ifType" ) == "INTERFACE_VPC" :
5241+ is_tor_uplink = False
5242+
5243+ # Check alias patterns for TOR uplinks
5244+ if have .get ("alias" ) is not None and (
5245+ "tor-connected-to" in have .get ("alias" )
5246+ or "leaf-connected-to-vPC-tor" in have .get ("alias" )
5247+ or "leaf-connected-to-tor" in have .get ("alias" )
5248+ ):
5249+ is_tor_uplink = True
5250+
5251+ # Check underlayPolicies templateName for TOR uplink patterns
5252+ if not is_tor_uplink and have .get ("underlayPolicies" ):
5253+ for policy in have ["underlayPolicies" ]:
5254+ template_name = policy .get ("templateName" , "" )
5255+ if template_name and template_name .startswith ("int_vpc_uplink_" ):
5256+ is_tor_uplink = True
5257+ break
5258+
5259+ if is_tor_uplink :
5260+ self .changed_dict [0 ]["skipped" ].append (
5261+ {
5262+ "Name" : name ,
5263+ "Alias" : have ["alias" ],
5264+ "Underlay Policies" : have ["underlayPolicies" ],
5265+ "Reason" : "TOR uplink vPC interface" ,
5266+ }
5267+ )
5268+ continue
5269+
51715270 # Interfaces sometimes take time to get deleted from DCNM. Such interfaces will have
51725271 # underlayPolicies set to "None". Such interfaces need not be deleted again
51735272
5174- if have [ "underlayPolicies" ] is None :
5273+ if have . get ( "underlayPolicies" ) is None :
51755274 self .changed_dict [0 ]["skipped" ].append (
51765275 {
51775276 "Name" : name ,
0 commit comments