Skip to content

Commit 6b53fd2

Browse files
ccoueffemikewiebe
andauthored
Fix issue with breakout with multiple switch (#484)
* Update dcnm_interface.py * Update dcnm_interface.py * Update dcnm_interface.py * Update dcnm_interface.py * Update dcnm_interface.py * Update dcnm_interface.py * Update dcnm_interface.py * Update dcnm_interface.py * Comment update --------- Co-authored-by: mwiebe <[email protected]>
1 parent eecf674 commit 6b53fd2

File tree

1 file changed

+77
-52
lines changed

1 file changed

+77
-52
lines changed

plugins/modules/dcnm_interface.py

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)