Skip to content

Commit 96dbf31

Browse files
Added WRED with affected Leaf/LC/FM model check
1 parent 07ea2db commit 96dbf31

2 files changed

Lines changed: 220 additions & 0 deletions

File tree

aci-preupgrade-validation-script.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6293,6 +6293,112 @@ def multipod_modular_spine_bootscript_check(tversion, fabric_nodes, username, pa
62936293
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)
62946294

62956295

6296+
@check_wrapper(check_title='WRED with Affected Leaf/LC/FM Models')
6297+
def wred_affected_model_check(tversion, fabric_nodes, **kwargs):
6298+
result = PASS
6299+
headers = ["Node ID", "Node Name", "Source", "Model"]
6300+
data = []
6301+
recommended_action = (
6302+
'Detected affected node(s) with WRED enabled. '
6303+
'Review software fix options and engage TAC.'
6304+
)
6305+
doc_url = 'https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt50713'
6306+
6307+
if not tversion:
6308+
return Result(result=MANUAL, msg=TVER_MISSING)
6309+
6310+
version_affected = (
6311+
(tversion.major1 == '6' and tversion.major2 == '1' and tversion.older_than('6.1(6a)'))
6312+
or (tversion.major1 == '6' and tversion.major2 == '2' and tversion.older_than('6.2(2a)'))
6313+
)
6314+
if not version_affected:
6315+
return Result(result=PASS, msg=VER_NOT_AFFECTED)
6316+
6317+
qosCong = icurl('class', 'qosCong.json')
6318+
wred_enabled = False
6319+
for cong in qosCong:
6320+
algo = cong.get('qosCong', {}).get('attributes', {}).get('algo', '')
6321+
if algo.lower() == 'wred':
6322+
wred_enabled = True
6323+
break
6324+
6325+
if not wred_enabled:
6326+
return Result(result=PASS, msg='WRED not enabled. Skipping.')
6327+
6328+
affected_models = {
6329+
'N9K-C9236C',
6330+
'N9K-C92300YC',
6331+
'N9K-C9272Q',
6332+
'N9K-C92304QC',
6333+
'N9K-C9504-FM-E',
6334+
'N9K-C9508-FM-E',
6335+
'N9K-C9516-FM-E',
6336+
}
6337+
6338+
def is_affected_model(model):
6339+
m = (model or '').upper()
6340+
return m in affected_models or 'LACROSSE' in m
6341+
6342+
node_name_map = {}
6343+
for node in fabric_nodes:
6344+
attr = node.get('fabricNode', {}).get('attributes', {})
6345+
if attr.get('id'):
6346+
node_name_map[attr.get('id')] = attr.get('name', '')
6347+
6348+
impacted = set()
6349+
6350+
# Leaf model gate
6351+
for node in fabric_nodes:
6352+
attr = node.get('fabricNode', {}).get('attributes', {})
6353+
if attr.get('role') != 'leaf':
6354+
continue
6355+
model = attr.get('model', '')
6356+
if is_affected_model(model):
6357+
impacted.add((attr.get('id', ''), attr.get('name', ''), 'Leaf', model))
6358+
6359+
# LC model gate
6360+
eqptLC = icurl('class', 'eqptLC.json')
6361+
for card in eqptLC:
6362+
attr = card.get('eqptLC', {}).get('attributes', {})
6363+
model = attr.get('model', '')
6364+
if not is_affected_model(model):
6365+
continue
6366+
dn = attr.get('dn', '')
6367+
m = re.search(node_regex, dn)
6368+
if not m:
6369+
continue
6370+
node_id = m.group('node')
6371+
impacted.add((node_id, node_name_map.get(node_id, ''), 'LC', model))
6372+
6373+
# FM model gate
6374+
eqptFC = icurl('class', 'eqptFC.json')
6375+
for card in eqptFC:
6376+
attr = card.get('eqptFC', {}).get('attributes', {})
6377+
model = attr.get('model', '')
6378+
if not is_affected_model(model):
6379+
continue
6380+
dn = attr.get('dn', '')
6381+
m = re.search(node_regex, dn)
6382+
if not m:
6383+
continue
6384+
node_id = m.group('node')
6385+
impacted.add((node_id, node_name_map.get(node_id, ''), 'FM', model))
6386+
6387+
if impacted:
6388+
def sort_key(row):
6389+
node_id = row[0]
6390+
try:
6391+
node_key = int(node_id)
6392+
except (TypeError, ValueError):
6393+
node_key = node_id
6394+
return (node_key, row[2], row[3])
6395+
6396+
data = [list(row) for row in sorted(impacted, key=sort_key)]
6397+
result = FAIL_O
6398+
6399+
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)
6400+
6401+
62966402
# ---- Script Execution ----
62976403

62986404

@@ -6474,6 +6580,7 @@ class CheckManager:
64746580
# Bugs
64756581
observer_db_size_check,
64766582
multipod_modular_spine_bootscript_check,
6583+
wred_affected_model_check,
64776584
]
64786585
cli_checks = [
64796586
# General
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import pytest
2+
import importlib
3+
4+
script = importlib.import_module("aci-preupgrade-validation-script")
5+
6+
test_function = "wred_affected_model_check"
7+
8+
9+
def _node(node_id, name, role, model):
10+
return {
11+
"fabricNode": {
12+
"attributes": {
13+
"id": str(node_id),
14+
"name": name,
15+
"role": role,
16+
"model": model,
17+
"dn": "topology/pod-1/node-{}".format(node_id),
18+
}
19+
}
20+
}
21+
22+
23+
@pytest.mark.parametrize(
24+
"tversion, fabric_nodes, icurl_outputs, expected_result, expected_data",
25+
[
26+
(
27+
None,
28+
[_node(101, "leaf101", "leaf", "N9K-C9236C")],
29+
{},
30+
script.MANUAL,
31+
[],
32+
),
33+
(
34+
"6.2(2a)",
35+
[_node(101, "leaf101", "leaf", "N9K-C9236C")],
36+
{},
37+
script.PASS,
38+
[],
39+
),
40+
(
41+
"6.1(5e)",
42+
[_node(101, "leaf101", "leaf", "N9K-C9236C")],
43+
{
44+
"qosCong.json": [
45+
{"qosCong": {"attributes": {"algo": "wred"}}},
46+
],
47+
"eqptLC.json": [],
48+
"eqptFC.json": [],
49+
},
50+
script.FAIL_O,
51+
[["101", "leaf101", "Leaf", "N9K-C9236C"]],
52+
),
53+
(
54+
"6.2(1f)",
55+
[
56+
_node(1001, "spine1001", "spine", "N9K-C9504"),
57+
],
58+
{
59+
"qosCong.json": [
60+
{"qosCong": {"attributes": {"algo": "tail-drop"}}},
61+
{"qosCong": {"attributes": {"algo": "wred"}}},
62+
],
63+
"eqptLC.json": [
64+
{
65+
"eqptLC": {
66+
"attributes": {
67+
"dn": "topology/pod-1/node-1001/sys/ch/lcslot-1/lc",
68+
"model": "N9K-C92304QC",
69+
}
70+
}
71+
}
72+
],
73+
"eqptFC.json": [
74+
{
75+
"eqptFC": {
76+
"attributes": {
77+
"dn": "topology/pod-1/node-1001/sys/ch/fcslot-1/fc",
78+
"model": "N9K-C9508-FM-E",
79+
}
80+
}
81+
}
82+
],
83+
},
84+
script.FAIL_O,
85+
[
86+
["1001", "spine1001", "FM", "N9K-C9508-FM-E"],
87+
["1001", "spine1001", "LC", "N9K-C92304QC"],
88+
],
89+
),
90+
(
91+
"6.1(5e)",
92+
[_node(101, "leaf101", "leaf", "N9K-C9236C")],
93+
{
94+
"qosCong.json": [
95+
{"qosCong": {"attributes": {"algo": "tail-drop"}}},
96+
],
97+
"eqptLC.json": [],
98+
"eqptFC.json": [],
99+
},
100+
script.PASS,
101+
[],
102+
),
103+
],
104+
)
105+
def test_logic(run_check, mock_icurl, tversion, fabric_nodes, expected_result, expected_data):
106+
kwargs = {
107+
"tversion": script.AciVersion(tversion) if tversion else None,
108+
"fabric_nodes": fabric_nodes,
109+
}
110+
111+
result = run_check(**kwargs)
112+
assert result.result == expected_result
113+
assert result.data == expected_data

0 commit comments

Comments
 (0)