@@ -1886,8 +1886,8 @@ def __init__(self, module):
18861886 self .diff_replace = []
18871887 self .diff_delete = [[], [], [], [], [], [], [], [], []]
18881888 self .diff_delete_deploy = [[], [], [], [], [], [], [], [], []]
1889- self .breakout = []
18901889 self .want_breakout = []
1890+ self .have_breakout = []
18911891 self .diff_create_breakout = []
18921892 self .diff_delete_breakout = []
18931893 self .diff_deploy = []
@@ -2099,11 +2099,6 @@ def __init__(self, module):
20992099 "BREAKOUT" : 8 ,
21002100 }
21012101
2102- # Build breakout list in config
2103- for interface in self .config :
2104- if interface .get ("type" ) and interface ["type" ] == "breakout" :
2105- self .breakout .append (interface ["name" ])
2106-
21072102 msg = "ENTERED DcnmIntf: "
21082103 self .log .debug (msg )
21092104
@@ -2121,19 +2116,32 @@ def dcnm_intf_breakout_format(self, if_name):
21212116 # Return False and None if the pattern does not match
21222117 return False , None
21232118
2124- def dcnm_intf_get_parent (self , cfg , name ):
2125- # Extract the parent interface ID (e.g., "1/2" from "1/2/3")
2119+ def dcnm_intf_get_parent (self , name , switch ):
2120+ """
2121+ Given an interface name and switch serial number, determine if the parent breakout interface exists in the target config.
2122+
2123+ Args:
2124+ cfg (list): List of interface configuration dictionaries.
2125+ name (str): Interface name to check (e.g., "Ethernet1/100/1").
2126+ switch (str): Switch Mgmt IP Address.
2127+
2128+ Returns:
2129+ tuple: (True, type) if parent breakout interface exists, else (False, None).
2130+ """
2131+ # Extract the parent interface ID (e.g., "1/100" from "Ethernet1/100/1")
21262132 match = re .search (r"(\d+/\d+)/\d+" , name )
21272133 if not match :
21282134 return False , None # Return early if the pattern doesn't match
21292135
2130- parent_port_id = f"ethernet{ match .group (1 )} "
2131-
2132- # Search for the parent interface in the configuration
2133- for interface in cfg :
2134- if interface .get ("name" ).lower () == parent_port_id :
2136+ parent_intf = f"Ethernet{ match .group (1 )} "
2137+ # Check if the parent interface exists in the config for the given switch
2138+ for interface in self .config :
2139+ if (
2140+ interface ["name" ].lower () == parent_intf .lower ()
2141+ and interface .get ("type" , "" ).lower () == "breakout"
2142+ and interface ["switch" ][0 ] == switch
2143+ ):
21352144 return True , interface .get ("type" , None )
2136-
21372145 return False , None
21382146
21392147 def dcnm_intf_dump_have_all (self ):
@@ -3872,11 +3880,13 @@ def dcnm_intf_get_want(self):
38723880 # Process non-breakout_interface policies
38733881 if intf_payload not in self .want :
38743882 intf = intf_payload ["interfaces" ][0 ]["ifName" ]
3875- is_valid_format , formatted_interface = self .dcnm_intf_breakout_format (intf )
3876-
3877- # Add to self.want only if the interface does not match invalid breakout conditions
3878- if not (is_valid_format and formatted_interface not in self .breakout ):
3879-
3883+ is_breakout_format , formatted_interface = self .dcnm_intf_breakout_format (intf )
3884+ parent_breakout_found , parent_type = self .dcnm_intf_get_parent (intf , sw )
3885+ # Add breakout interface to self.want only if breakout is configured in state overridden
3886+ # When state is replaced, we don't check if parent is configured.
3887+ if is_breakout_format is False or parent_breakout_found is True and self .params ['state' ] == "overridden" :
3888+ self .want .append (intf_payload )
3889+ else :
38803890 self .want .append (intf_payload )
38813891
38823892 def dcnm_intf_get_intf_info (self , ifName , serialNumber , ifType ):
@@ -3926,6 +3936,19 @@ def dcnm_intf_get_have_all_with_sno(self, sno):
39263936 if resp and "DATA" in resp and resp ["DATA" ]:
39273937 self .have_all .extend (resp ["DATA" ])
39283938
3939+ def dcnm_intf_get_have_all_breakout_interfaces (self , sno ):
3940+ # This function will get policies for a given serial number and
3941+ # populate the breakout interfaces in self.have_breakout
3942+ path = "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/control/policies/switches/{}" .format (sno )
3943+ resp = dcnm_send (self .module , "GET" , path )
3944+
3945+ breakout = []
3946+ if resp and "DATA" in resp and resp ["DATA" ]:
3947+ for elem in resp ["DATA" ]:
3948+ if elem .get ('templateName' , None ) == "breakout_interface" :
3949+ breakout .append (elem ['entityName' ])
3950+ self .have_breakout .append ({sno : breakout })
3951+
39293952 def dcnm_intf_get_have_all (self , sw ):
39303953
39313954 # Check if you have already got the details for this switch
@@ -3942,6 +3965,7 @@ def dcnm_intf_get_have_all(self, sw):
39423965
39433966 self .have_all_list .append (sw )
39443967 self .dcnm_intf_get_have_all_with_sno (sno )
3968+ self .dcnm_intf_get_have_all_breakout_interfaces (sno )
39453969
39463970 def dcnm_intf_get_have (self ):
39473971
@@ -4242,30 +4266,34 @@ def dcnm_intf_compare_want_and_have(self, state):
42424266 if state != "deleted" :
42434267 for want_breakout in self .want_breakout :
42444268 want_intf = want_breakout ["interfaces" ][0 ]["ifName" ]
4269+ want_serialnumber = want_breakout ["interfaces" ][0 ]["serialNumber" ]
42454270 match_create = False
4246- for have_intf in self .have_all :
4247- is_valid , intf = self .dcnm_intf_breakout_format (have_intf ['ifName' ])
4248- if is_valid and want_intf == intf :
4249- match_create = True
4250-
4251- # If interface in want_breakout and interface e1/x/y not present
4252- # add to diff_create_breakout
4271+ # Search if interface is in have_breakout
4272+ for elem in self .have_breakout :
4273+ if want_serialnumber == list (elem .keys ())[0 ]:
4274+ for interface in elem [want_serialnumber ]:
4275+ if interface .lower () == want_intf .lower ():
4276+ # If the breakout interface is already present in have,
4277+ # skip adding it to diff_create_breakout
4278+ match_create = True
4279+ break
42534280 if not match_create :
4254- want_breakout ["interfaces" ][0 ].pop ("fabricName" )
4255- want_breakout ["interfaces" ][0 ].pop ("interfaceType" )
42564281 self .diff_create_breakout .append (want_breakout ["interfaces" ])
42574282
42584283 if state == "deleted" or state == "overridden" :
4259- for have in self .have_all :
4260- have_intf = have ['ifName' ]
4261- if re .search (r"\d+\/\d+\/\d+" , have_intf ):
4262- found , parent_type = self .dcnm_intf_get_parent (self .config , have_intf )
4263- # If have not in want breakout and if match to E1/x/1 add to dict
4264- # Else if match E1/x/2, etc. silently ignore, because we delete the breakout
4265- # with the first sub if.
4266- if re .search (r"\d+\/\d+\/1$" , have_intf ) and not found :
4267- payload = {'serialNumber' : have ['serialNo' ],
4268- 'ifName' : have ['ifName' ]}
4284+ for have_breakout in self .have_breakout :
4285+ for interface in list (have_breakout .values ())[0 ]:
4286+ match_delete_interface = True
4287+ for want_breakout in self .want_breakout :
4288+ if list (have_breakout .keys ())[0 ] == want_breakout ["interfaces" ][0 ]["serialNumber" ]:
4289+ if want_breakout ["interfaces" ][0 ]["ifName" ] == interface :
4290+ match_delete_interface = False
4291+ break
4292+ if match_delete_interface :
4293+ payload = {
4294+ "serialNumber" : list (have_breakout .keys ())[0 ],
4295+ "ifName" : interface + "/1"
4296+ }
42694297 self .diff_delete_breakout .append (payload )
42704298
42714299 for want in self .want :
@@ -4288,7 +4316,6 @@ def dcnm_intf_compare_want_and_have(self, state):
42884316 and (sno == d ["interfaces" ][0 ]["serialNumber" ])
42894317 )
42904318 ]
4291-
42924319 if not match_have :
42934320 changed_dict = copy .deepcopy (want )
42944321
@@ -4447,12 +4474,11 @@ def dcnm_intf_compare_want_and_have(self, state):
44474474 if action == "add" :
44484475 # If E1/x/y do not create. Interface is created with breakout
44494476 if re .search (r"\d+\/\d+\/\d+" , name ):
4450- found , parent_type = self .dcnm_intf_get_parent (self .config , name )
4451- if found and parent_type == "breakout" :
4452- if want .get ("interfaceType" , None ) is not None :
4453- want .pop ("interfaceType" )
4454- self .diff_replace .append (want )
4455- self .changed_dict [0 ][state ].append (changed_dict )
4477+ if want .get ("interfaceType" , None ) is not None :
4478+ want .pop ("interfaceType" )
4479+ self .dcnm_intf_merge_intf_info (want , self .diff_replace )
4480+ self .changed_dict [0 ][state ].append (changed_dict )
4481+ intf_changed = True
44564482 continue
44574483 self .dcnm_intf_merge_intf_info (want , self .diff_create )
44584484 # Add the changed_dict to self.changed_dict
@@ -4471,7 +4497,7 @@ def dcnm_intf_compare_want_and_have(self, state):
44714497 if str (deploy ).lower () == "true" :
44724498 # Add to diff_deploy,
44734499 # 1. if intf_changed is True
4474- # 2. if intf_changed is Flase , then if 'complianceStatus is
4500+ # 2. if intf_changed is False , then if 'complianceStatus is
44754501 # False then add to diff_deploy.
44764502 # 3. Do not add otherwise
44774503
@@ -4838,8 +4864,7 @@ def dcnm_intf_get_diff_overridden(self, cfg):
48384864 # If a match for Ethernet1/x/y is found, verify if the parent interface Ethernet1/x exists in cfg.
48394865 # If the parent doesn't exist or isn't of type "breakout", remove the breakout interface.
48404866 if re .search (r"\d+\/\d+\/\d+" , name ):
4841- found , parent_type = self .dcnm_intf_get_parent (cfg , name )
4842-
4867+ found , parent_type = self .dcnm_intf_get_parent (name , have ['mgmtIpAddress' ])
48434868 if not (found and parent_type == "breakout" ):
48444869 payload = {
48454870 "serialNumber" : have ["serialNo" ],
@@ -5047,7 +5072,7 @@ def dcnm_intf_get_diff_deleted(self):
50475072 for have in self .have_all :
50485073 have_intf = have ['ifName' ]
50495074 if re .search (r"\d+\/\d+\/\d+" , have_intf ):
5050- found , parent_type = self .dcnm_intf_get_parent (self . config , have_intf )
5075+ found , parent_type = self .dcnm_intf_get_parent (have_intf , have [ 'mgmtIpAddress' ] )
50515076 # If have in want breakout and if match to E1/x/1 add to dict
50525077 # Else if match E1/x/2, etc. silently ignore, because we delete the breakout
50535078 # with the first sub if.
@@ -5505,7 +5530,7 @@ def dcnm_intf_send_message_to_dcnm(self):
55055530 # index 8 is used for breakout interface
55065531 if delete_index == 8 :
55075532 path = "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/interface"
5508-
5533+ break
55095534 json_payload = json .dumps (delem )
55105535
55115536 resp = dcnm_send (self .module , "DELETE" , path , json_payload )
@@ -5596,7 +5621,7 @@ def dcnm_intf_send_message_to_dcnm(self):
55965621 resp ["CHANGED" ] = self .changed_dict
55975622 self .module .fail_json (msg = resp )
55985623 else :
5599- replace = True
5624+ create = True
56005625
56015626 # Update interfaces
56025627 path = self .paths ["INTERFACE" ]
@@ -5629,7 +5654,7 @@ def dcnm_intf_send_message_to_dcnm(self):
56295654 resp ["CHANGED" ] = self .changed_dict
56305655 self .module .fail_json (msg = resp )
56315656 else :
5632- replace = True
5657+ delete = changed
56335658
56345659 resp = None
56355660 path = self .paths ["GLOBAL_IF" ]
0 commit comments