Skip to content

Commit 569d89e

Browse files
authored
Bug Fix - Lan Automation workflow Manager - Improved validation while updating port channels (#189)
2 parents bac847f + 5fa970a commit 569d89e

File tree

3 files changed

+217
-8
lines changed

3 files changed

+217
-8
lines changed

plugins/modules/lan_automation_workflow_manager.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,7 +1359,7 @@ def get_port_channels(
13591359
"Source device management IP address is required to fetch Port Channel configurations.",
13601360
"ERROR",
13611361
)
1362-
return None
1362+
return []
13631363

13641364
if not self.is_valid_ipv4(source_device_management_ip_address):
13651365
self.msg = (
@@ -1432,7 +1432,7 @@ def get_port_channels(
14321432
f"Destination IP: '{destination_device_management_ip_address}', Response is empty."
14331433
)
14341434
self.log(self.msg, "DEBUG")
1435-
return None
1435+
return []
14361436

14371437
port_channel_info = response.get("response")
14381438

@@ -1465,7 +1465,7 @@ def get_port_channels(
14651465
f"Destination IP: '{destination_device_management_ip_address}', Port Channel Number: '{port_channel_number}'."
14661466
)
14671467
self.log(self.msg, "DEBUG")
1468-
return None
1468+
return []
14691469

14701470
self.log(
14711471
f"Filtered Port Channel configurations: {self.pprint(filtered_info)}",
@@ -1635,26 +1635,38 @@ def extract_port_channel_config_from_catalyst_center(self):
16351635
port_channel_number,
16361636
)
16371637

1638+
want_links = port_channel_config.get("links")
16381639
if not fetched_port_channel_configs:
16391640
self.log(
16401641
f"No matching port channel configurations found in Catalyst Center for [Config {idx}].",
16411642
"INFO",
16421643
)
1644+
if want_links and port_channel_number:
1645+
# Port channel number and links both are provided, i.e. expecting to update, so fail if existing is not found.
1646+
self.log(
1647+
f"[Config {idx}] No existing Port Channel config found with provided port_channel_number.",
1648+
"DEBUG",
1649+
)
1650+
self.msg = (
1651+
"No existing Port Channel configuration found with the provided "
1652+
f"port_channel_number: {port_channel_number}. When both port_channel_number and links "
1653+
"are specified, an existing Port Channel is expected for update. "
1654+
"If you want to create a new Port Channel, please remove the "
1655+
"port_channel_number parameter from your playbook configuration "
1656+
"and try again."
1657+
)
1658+
self.fail_and_exit(self.msg)
1659+
16431660
have_port_channel_configs.append(None)
16441661
continue
16451662

1646-
self.log(
1647-
f"[Config {idx}] Retrieved {len(fetched_port_channel_configs)} raw configs.",
1648-
"DEBUG",
1649-
)
16501663
self.log(
16511664
f"Retrieved {len(fetched_port_channel_configs)} raw port channel configurations from Catalyst Center for [Config {idx}]",
16521665
"DEBUG",
16531666
)
16541667
formatted_port_channel_config_list = self.format_port_channels_have_configs(
16551668
fetched_port_channel_configs
16561669
)
1657-
want_links = port_channel_config.get("links")
16581670

16591671
if want_links:
16601672
self.log(

tests/unit/modules/dnac/fixtures/lan_automation_workflow_manager.json

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,5 +1440,160 @@
14401440
}
14411441
],
14421442
"version": "1.0"
1443+
},
1444+
"update_port_channel_negative_testcase_playbook_case_9": [
1445+
{
1446+
"port_channel": [
1447+
{
1448+
"source_device_management_ip_address": "172.101.1.1",
1449+
"destination_device_management_ip_address": "172.254.0.2",
1450+
"port_channel_number": 11,
1451+
"links": [
1452+
{
1453+
"source_port": "GigabitEthernet1/0/1",
1454+
"destination_port": "GigabitEthernet1/0/2"
1455+
},
1456+
{
1457+
"source_port": "GigabitEthernet1/0/3",
1458+
"destination_port": "GigabitEthernet1/0/4"
1459+
}
1460+
]
1461+
}
1462+
]
1463+
}
1464+
],
1465+
"get_device_list_call_1_case_9": {
1466+
"response": [
1467+
{
1468+
"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20241115:085712 [BLD_V1715_THROTTLE_LATEST_20241115_081107:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 15 netconf enabled",
1469+
"lastUpdateTime": 1763977060385,
1470+
"associatedWlcIp": "",
1471+
"apEthernetMacAddress": null,
1472+
"errorCode": null,
1473+
"errorDescription": null,
1474+
"lastDeviceResyncStartTime": "2025-11-24 09:37:51",
1475+
"lineCardCount": "0",
1476+
"lineCardId": "",
1477+
"managedAtleastOnce": true,
1478+
"memorySize": "NA",
1479+
"tagCount": "0",
1480+
"tunnelUdpPort": null,
1481+
"uptimeSeconds": 20282418,
1482+
"vendor": "Cisco",
1483+
"waasDeviceMode": null,
1484+
"newRoleReason": null,
1485+
"newRoleValue": null,
1486+
"roleSource": "AUTO",
1487+
"interfaceCount": "0",
1488+
"lastUpdated": "2025-11-24 09:37:40",
1489+
"bootDateTime": "2025-04-03 15:38:40",
1490+
"apManagerInterfaceIp": "",
1491+
"collectionStatus": "In Progress",
1492+
"family": "Switches and Hubs",
1493+
"hostname": "S1-BGL-CORE-1.pavankk.com",
1494+
"locationName": null,
1495+
"managementIpAddress": "172.101.1.1",
1496+
"platformId": "C9606R",
1497+
"reachabilityFailureReason": "",
1498+
"reachabilityStatus": "Reachable",
1499+
"series": "Cisco Catalyst 9600 Series Switches",
1500+
"snmpContact": "",
1501+
"snmpLocation": "",
1502+
"macAddress": "6c:b2:ae:4a:44:c0",
1503+
"deviceSupportLevel": "Supported",
1504+
"softwareType": "IOS-XE",
1505+
"softwareVersion": "17.15.20241115:085712",
1506+
"serialNumber": "FXS2240Q0QX",
1507+
"inventoryStatusDetail": "",
1508+
"collectionInterval": "Global Default",
1509+
"dnsResolvedManagementAddress": "172.101.1.1",
1510+
"syncRequestedByApp": "SDA",
1511+
"lastManagedResyncReasons": "Link Up/Down Event",
1512+
"managementState": "Managed",
1513+
"pendingSyncRequestsCount": "0",
1514+
"reasonsForDeviceResync": "Application Requested",
1515+
"reasonsForPendingSyncRequests": "",
1516+
"upTime": "234 days, 17:59:57.07",
1517+
"type": "Cisco Catalyst 9606R Switch",
1518+
"location": null,
1519+
"role": "DISTRIBUTION",
1520+
"instanceTenantId": "68f9d3a03064e76251a3a50f",
1521+
"instanceUuid": "9e11c8f3-3843-453f-bda1-4d2947b88b56",
1522+
"id": "9e11c8f3-3843-453f-bda1-4d2947b88b56"
1523+
}
1524+
],
1525+
"version": "1.0"
1526+
},
1527+
"get_device_list_call_2_case_9": {
1528+
"response": [
1529+
{
1530+
"description": "Cisco IOS Software [Dublin], Catalyst L3 Switch Software (CAT9K_IOSXE), Version 17.12.5, RELEASE SOFTWARE (fc5) Technical Support: www.cisco.com/techsupport Copyright (c) 1986-2025 by Cisco Systems, Inc. Compiled Fri 14-Mar-25 02:41 by mcpre netconf enabled",
1531+
"lastUpdateTime": 1763969578547,
1532+
"associatedWlcIp": "",
1533+
"apEthernetMacAddress": null,
1534+
"errorCode": null,
1535+
"errorDescription": null,
1536+
"lastDeviceResyncStartTime": "2025-11-24 09:37:51",
1537+
"lineCardCount": "0",
1538+
"lineCardId": "",
1539+
"managedAtleastOnce": true,
1540+
"memorySize": "NA",
1541+
"tagCount": "0",
1542+
"tunnelUdpPort": null,
1543+
"uptimeSeconds": 10680,
1544+
"vendor": "Cisco",
1545+
"waasDeviceMode": null,
1546+
"newRoleReason": null,
1547+
"newRoleValue": null,
1548+
"roleSource": "AUTO",
1549+
"interfaceCount": "0",
1550+
"lastUpdated": "2025-11-24 07:32:58",
1551+
"bootDateTime": "2025-11-24 06:40:58",
1552+
"apManagerInterfaceIp": "",
1553+
"collectionStatus": "In Progress",
1554+
"family": "Switches and Hubs",
1555+
"hostname": "BGL-EDGE3",
1556+
"locationName": null,
1557+
"managementIpAddress": "172.254.0.2",
1558+
"platformId": "C9300-48U",
1559+
"reachabilityFailureReason": "",
1560+
"reachabilityStatus": "Reachable",
1561+
"series": "Cisco Catalyst 9300 Series Switches",
1562+
"snmpContact": "",
1563+
"snmpLocation": "",
1564+
"macAddress": "70:0b:4f:c5:1d:80",
1565+
"deviceSupportLevel": "Supported",
1566+
"softwareType": "IOS-XE",
1567+
"softwareVersion": "17.12.5",
1568+
"serialNumber": "FCW2236E0LH",
1569+
"inventoryStatusDetail": "",
1570+
"collectionInterval": "Global Default",
1571+
"dnsResolvedManagementAddress": "172.254.0.2",
1572+
"syncRequestedByApp": "SDA",
1573+
"lastManagedResyncReasons": "Link Up/Down Event",
1574+
"managementState": "Managed",
1575+
"pendingSyncRequestsCount": "0",
1576+
"reasonsForDeviceResync": "Application Requested",
1577+
"reasonsForPendingSyncRequests": "",
1578+
"upTime": "0:52:36.54",
1579+
"type": "Cisco Catalyst 9300 Switch",
1580+
"location": null,
1581+
"role": "ACCESS",
1582+
"instanceTenantId": "68f9d3a03064e76251a3a50f",
1583+
"instanceUuid": "679ded4d-9474-436d-b00f-43418efcc60d",
1584+
"id": "679ded4d-9474-436d-b00f-43418efcc60d"
1585+
}
1586+
],
1587+
"version": "1.0"
1588+
},
1589+
"get_lan_automation_status_call_1_case_9": {
1590+
"response": {}
1591+
},
1592+
"get_active_lan_automation_sessions_call_1_case_9": {
1593+
"response": {}
1594+
},
1595+
"get_port_channel_call_1_case_9": {
1596+
"response": [],
1597+
"version": "1.0"
14431598
}
14441599
}

tests/unit/modules/dnac/test_lan_automation_workflow_manager.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class TestDnacLanAutomationWorkflow(TestDnacModule):
6464
playbook_config_create_port_channel_negative_testcase_playbook_case_8 = (
6565
test_data.get("create_port_channel_negative_testcase_playbook_case_8")
6666
)
67+
playbook_config_update_port_channel_negative_testcase_playbook_case_9 = (
68+
test_data.get("update_port_channel_negative_testcase_playbook_case_9")
69+
)
6770

6871
def setUp(self):
6972
super(TestDnacLanAutomationWorkflow, self).setUp()
@@ -191,6 +194,17 @@ def load_fixtures(self, response=None, device=""):
191194
self.test_data.get("get_device_list_call_1_case_8"),
192195
self.test_data.get("get_device_list_call_2_case_8"),
193196
]
197+
elif (
198+
"update_port_channel_negative_testcase_playbook_case_9"
199+
in self._testMethodName
200+
):
201+
self.run_dnac_exec.side_effect = [
202+
self.test_data.get("get_device_list_call_1_case_9"),
203+
self.test_data.get("get_device_list_call_2_case_9"),
204+
self.test_data.get("get_lan_automation_status_call_1_case_9"),
205+
self.test_data.get("get_active_lan_automation_sessions_call_1_case_9"),
206+
self.test_data.get("get_port_channel_call_1_case_9"),
207+
]
194208

195209
def test_delete_port_channel_when_it_doesnot_exist_case_1(self):
196210
# Test Description: Delete port channel when it does not exist between source and destination device.
@@ -369,3 +383,31 @@ def test_create_port_channel_negative_testcase_playbook_case_8(self):
369383
"Missing links parameter for merged state - at least one link must be specified",
370384
result.get("msg"),
371385
)
386+
387+
def test_update_port_channel_negative_testcase_playbook_case_9(self):
388+
# Test Description: Update port channel by specifying port_channel_number that does not exist.
389+
# Expected Result: Module should fail with appropriate error message indicating the port channel
390+
# number does not exist and suggesting to remove the parameter to create a new port channel.
391+
set_module_args(
392+
dict(
393+
dnac_host="1.1.1.1",
394+
dnac_username="dummy",
395+
dnac_password="dummy",
396+
dnac_version="3.1.3.0",
397+
dnac_log=True,
398+
state="merged",
399+
config_verify=True,
400+
dnac_log_level="DEBUG",
401+
config=self.playbook_config_update_port_channel_negative_testcase_playbook_case_9,
402+
)
403+
)
404+
result = self.execute_module(changed=False, failed=True)
405+
self.assertIn(
406+
"No existing Port Channel configuration found with the provided "
407+
"port_channel_number: 11. When both port_channel_number and links "
408+
"are specified, an existing Port Channel is expected for update. "
409+
"If you want to create a new Port Channel, please remove the "
410+
"port_channel_number parameter from your playbook configuration "
411+
"and try again.",
412+
result.get("msg"),
413+
)

0 commit comments

Comments
 (0)