diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index fd6f076..528d845 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -6293,6 +6293,77 @@ def multipod_modular_spine_bootscript_check(tversion, fabric_nodes, username, pa return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) +@check_wrapper(check_title='WRED with Affected FM Models') +def wred_affected_model_check(tversion, fabric_nodes, **kwargs): + result = PASS + headers = ["Node ID", "Node Name", "Source", "Model"] + data = [] + recommended_action = 'Disable WRED on the affected nodes or move to a release newer than 6.1(5e) in the 6.1(x) train or newer than 6.2(1g) in the 6.2(x) train.' + doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#wred-with-affected-fm-models' + + if not tversion: + return Result(result=MANUAL, msg=TVER_MISSING) + + version_affected = ( + (tversion.major1 == '6' and tversion.major2 == '1' and (tversion.older_than('6.1(5e)') or tversion.same_as('6.1(5e)'))) + or (tversion.major1 == '6' and tversion.major2 == '2' and (tversion.older_than('6.2(1g)') or tversion.same_as('6.2(1g)'))) + ) + if not version_affected: + return Result(result=NA, msg=VER_NOT_AFFECTED) + + affected_models = { + 'N9K-C9504-FM-E', + 'N9K-C9508-FM-E', + 'N9K-C9516-FM-E', + } + + node_name_map = {} + for node in fabric_nodes: + node_id = node['fabricNode']['attributes']['id'] + node_name_map[node_id] = node['fabricNode']['attributes']['name'] + + impacted = set() + + # FM model gate + for obj in icurl('class', 'eqptFC.json'): + model = obj['eqptFC']['attributes']['model'] + if model not in affected_models: + continue + dn = obj['eqptFC']['attributes']['dn'] + dn_match = re.search(node_regex, dn) + if not dn_match: + continue + node_id = dn_match.group('node') + impacted.add((node_id, node_name_map.get(node_id, ''), 'FM', model)) + + if not impacted: + return Result(result=PASS, msg='No affected hardware models found. Skipping.') + + qosCong = icurl('class', 'qosCong.json') + wred_enabled = False + for cong in qosCong: + algo = cong.get('qosCong', {}).get('attributes', {}).get('algo', '') + if algo == 'wred': + wred_enabled = True + break + + if not wred_enabled: + return Result(result=PASS, msg='WRED not enabled. Skipping.') + + def sort_key(row): + node_id = row[0] + try: + node_key = int(node_id) + except (TypeError, ValueError): + node_key = node_id + return (node_key, row[2], row[3]) + + data = [list(row) for row in sorted(impacted, key=sort_key)] + result = FAIL_O + + return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) + + # ---- Script Execution ---- @@ -6474,6 +6545,7 @@ class CheckManager: # Bugs observer_db_size_check, multipod_modular_spine_bootscript_check, + wred_affected_model_check, ] cli_checks = [ # General diff --git a/docs/docs/validations.md b/docs/docs/validations.md index 49fe535..eac050b 100644 --- a/docs/docs/validations.md +++ b/docs/docs/validations.md @@ -198,6 +198,7 @@ Items | Defect | This Script [Rogue EP Exception List missing on switches][d30] | CSCwp64296 | :white_check_mark: | :no_entry_sign: [N9K-C9408 with more than 5 N9K-X9400-16W LEMs][d31] | CSCws82819 | :white_check_mark: | :no_entry_sign: [Multi-Pod Modular Spine Bootscript File][d32] | CSCwr66848 | :white_check_mark: | :no_entry_sign: +[WRED with Affected FM Models][d33] | CSCwt50713 | :white_check_mark: | :no_entry_sign: [d1]: #ep-announce-compatibility [d2]: #eventmgr-db-size-defect-susceptibility @@ -231,6 +232,7 @@ Items | Defect | This Script [d30]: #rogue-ep-exception-list-missing-on-switches [d31]: #n9k-c9408-with-more-than-5-n9k-x9400-16w-lems [d32]: #multi-pod-modular-spine-bootscript-file +[d33]: #wred-with-affected-fm-models ## General Check Details @@ -2753,6 +2755,17 @@ This issue happens only when the target version is specifically 6.1(4h). To avoid this issue, change the target version to another version. Or verify that the `bootscript` file exists in the bootflash of each modular spine switch prior to upgrading to 6.1(4h). If the file is missing, you have to do clean reboot on the impacted spine to ensure that `/bootflash/bootscript` gets created again. In case you already upgraded your spine and you are experiencing the traffic impact due to this issue, clean reboot of the spine will restore the traffic. +### WRED with Affected FM Models + +Due to [CSCwt50713][67], when WRED (Weighted Random Early Detection) is enabled and specific Fabric Module (FM) hardware models are present in the fabric, the spine switch may crash after moving to an affected ACI release in the 6.1(x) or 6.2(x) range. + +Affected versions: ACI 6.1(x) up to and including 6.1(5e), and ACI 6.2(x) up to and including 6.2(1g). + +Affected hardware models: N9K-C9504-FM-E, N9K-C9508-FM-E, N9K-C9516-FM-E. + +To avoid this issue, disable WRED on the affected nodes or move to a release newer than 6.1(5e) in the 6.1(x) train or newer than 6.2(1g) in the 6.2(x) train. + + [0]: https://github.com/datacenter/ACI-Pre-Upgrade-Validation-Script [1]: https://www.cisco.com/c/dam/en/us/td/docs/Website/datacenter/apicmatrix/index.html [2]: https://www.cisco.com/c/en/us/support/switches/nexus-9000-series-switches/products-release-notes-list.html @@ -2820,3 +2833,4 @@ To avoid this issue, change the target version to another version. Or verify tha [64]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp64296 [65]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCws82819 [66]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwr66848 +[67]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt50713 diff --git a/tests/checks/wred_affected_model_check/eqptFC_affected.json b/tests/checks/wred_affected_model_check/eqptFC_affected.json new file mode 100644 index 0000000..4e87550 --- /dev/null +++ b/tests/checks/wred_affected_model_check/eqptFC_affected.json @@ -0,0 +1,10 @@ +[ + { + "eqptFC": { + "attributes": { + "dn": "topology/pod-1/node-1001/sys/ch/fcslot-1/fc", + "model": "N9K-C9508-FM-E" + } + } + } +] diff --git a/tests/checks/wred_affected_model_check/eqptFC_empty.json b/tests/checks/wred_affected_model_check/eqptFC_empty.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/tests/checks/wred_affected_model_check/eqptFC_empty.json @@ -0,0 +1 @@ +[] diff --git a/tests/checks/wred_affected_model_check/eqptLC_affected.json b/tests/checks/wred_affected_model_check/eqptLC_affected.json new file mode 100644 index 0000000..819f6d6 --- /dev/null +++ b/tests/checks/wred_affected_model_check/eqptLC_affected.json @@ -0,0 +1,10 @@ +[ + { + "eqptLC": { + "attributes": { + "dn": "topology/pod-1/node-1001/sys/ch/lcslot-1/lc", + "model": "N9K-C92304QC" + } + } + } +] diff --git a/tests/checks/wred_affected_model_check/eqptLC_empty.json b/tests/checks/wred_affected_model_check/eqptLC_empty.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/tests/checks/wred_affected_model_check/eqptLC_empty.json @@ -0,0 +1 @@ +[] diff --git a/tests/checks/wred_affected_model_check/fabricNode_leaf_affected.json b/tests/checks/wred_affected_model_check/fabricNode_leaf_affected.json new file mode 100644 index 0000000..53a02c4 --- /dev/null +++ b/tests/checks/wred_affected_model_check/fabricNode_leaf_affected.json @@ -0,0 +1,13 @@ +[ + { + "fabricNode": { + "attributes": { + "dn": "topology/pod-1/node-101", + "id": "101", + "name": "leaf101", + "role": "leaf", + "model": "N9K-C9236C" + } + } + } +] diff --git a/tests/checks/wred_affected_model_check/fabricNode_spine.json b/tests/checks/wred_affected_model_check/fabricNode_spine.json new file mode 100644 index 0000000..c70eb8f --- /dev/null +++ b/tests/checks/wred_affected_model_check/fabricNode_spine.json @@ -0,0 +1,13 @@ +[ + { + "fabricNode": { + "attributes": { + "dn": "topology/pod-1/node-1001", + "id": "1001", + "name": "spine1001", + "role": "spine", + "model": "N9K-C9504" + } + } + } +] diff --git a/tests/checks/wred_affected_model_check/qosCong_mixed.json b/tests/checks/wred_affected_model_check/qosCong_mixed.json new file mode 100644 index 0000000..cb3a900 --- /dev/null +++ b/tests/checks/wred_affected_model_check/qosCong_mixed.json @@ -0,0 +1,16 @@ +[ + { + "qosCong": { + "attributes": { + "algo": "tail-drop" + } + } + }, + { + "qosCong": { + "attributes": { + "algo": "wred" + } + } + } +] diff --git a/tests/checks/wred_affected_model_check/qosCong_tail_drop.json b/tests/checks/wred_affected_model_check/qosCong_tail_drop.json new file mode 100644 index 0000000..dd814ec --- /dev/null +++ b/tests/checks/wred_affected_model_check/qosCong_tail_drop.json @@ -0,0 +1,9 @@ +[ + { + "qosCong": { + "attributes": { + "algo": "tail-drop" + } + } + } +] diff --git a/tests/checks/wred_affected_model_check/qosCong_wred.json b/tests/checks/wred_affected_model_check/qosCong_wred.json new file mode 100644 index 0000000..1c297c8 --- /dev/null +++ b/tests/checks/wred_affected_model_check/qosCong_wred.json @@ -0,0 +1,9 @@ +[ + { + "qosCong": { + "attributes": { + "algo": "wred" + } + } + } +] diff --git a/tests/checks/wred_affected_model_check/test_wred_affected_model_check.py b/tests/checks/wred_affected_model_check/test_wred_affected_model_check.py new file mode 100644 index 0000000..3e3726e --- /dev/null +++ b/tests/checks/wred_affected_model_check/test_wred_affected_model_check.py @@ -0,0 +1,82 @@ +import os +import pytest +import importlib +from helpers.utils import read_data + +script = importlib.import_module("aci-preupgrade-validation-script") + +dir = os.path.dirname(os.path.abspath(__file__)) + +test_function = "wred_affected_model_check" + +# icurl queries +qosCong_api = "qosCong.json" +eqptFC_api = "eqptFC.json" + + +@pytest.mark.parametrize( + "tversion, fabric_nodes, icurl_outputs, expected_result, expected_data", + [ + # Case 1: No target version provided (-t flag missing). + # Check cannot determine version gate. Expected: MANUAL CHECK REQUIRED. + ( + None, + read_data(dir, "fabricNode_leaf_affected.json"), + {}, + script.MANUAL, + [], + ), + # Case 2: Target version 6.2(2a) is the first fixed release and not in the affected range. + # Version gate fails. Expected: NA without any API calls. + ( + "6.2(2a)", + read_data(dir, "fabricNode_leaf_affected.json"), + {}, + script.NA, + [], + ), + # Case 3: All 3 gates triggered via an affected FM on a spine node. + # Version 6.2(1f) is in affected range, WRED is enabled, FM model N9K-C9508-FM-E is affected. + # Expected: FAIL_O with node 1001 reported under Source=FM. + ( + "6.2(1f)", + read_data(dir, "fabricNode_spine.json"), + { + qosCong_api: read_data(dir, "qosCong_wred.json"), + eqptFC_api: read_data(dir, "eqptFC_affected.json"), + }, + script.FAIL_O, + [["1001", "spine1001", "FM", "N9K-C9508-FM-E"]], + ), + # Case 4: Version is affected but no affected FM hardware found. + # Hardware gate fails before WRED is checked. Expected: PASS. + ( + "6.1(5e)", + read_data(dir, "fabricNode_spine.json"), + { + eqptFC_api: read_data(dir, "eqptFC_empty.json"), + }, + script.PASS, + [], + ), + # Case 5: Version is affected and FM is affected, but WRED is not enabled (tail-drop). + # WRED gate fails. Expected: PASS - confirms all 3 gates must be true simultaneously. + ( + "6.1(5e)", + read_data(dir, "fabricNode_spine.json"), + { + qosCong_api: read_data(dir, "qosCong_tail_drop.json"), + eqptFC_api: read_data(dir, "eqptFC_affected.json"), + }, + script.PASS, + [], + ), + ], +) +def test_logic(run_check, mock_icurl, tversion, fabric_nodes, expected_result, expected_data): + result = run_check( + tversion=script.AciVersion(tversion) if tversion else None, + fabric_nodes=fabric_nodes, + ) + assert result.result == expected_result + assert result.data == expected_data