diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index 4b83f4c..f953e74 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -6024,8 +6024,62 @@ def apic_downgrade_compat_warning_check(cversion, tversion, **kwargs): data.append([cversion, tversion, "Downgrading APIC from 6.2(1)+ to pre-6.2(1) will not be supported."]) return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) + + +@check_wrapper(check_title='active_node pres.Listener mo object check') +def active_node_presListener_mo_object_check(tversion,fabric_nodes, **kwargs): + result = PASS + headers = ["Missing Node ID", "Node Status"] + data = [] + fabric_leaf_ids = [] + preslistener_leaf_ids = [] + recommended_action = 'Contact Cisco TAC to investigate missing leaf nodes from class-4307 preslisteners objects' + doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#active_node_presListener_mo_object_check' + + presListener_node_objects = 'presListener.json?query-target-filter=wcard(presListener.dn,"4307")' + + presListeners_object_data = icurl('class', presListener_node_objects) + + node_regex = r"uni/infra/nodecfgcont/node-(?P\d+)" + class_regex = r"resregistry/resregistry-(?P\d+)/class-(?P\d+)" + if not tversion: + return Result(result=MANUAL, msg=TVER_MISSING) + # Only run check if target version < 6.1(3f) + if tversion and tversion.older_than("6.1(3f)"): + + if fabric_nodes: + fabric_active_leaf_nodes = [node for node in fabric_nodes if node["fabricNode"]["attributes"]["role"] == "leaf" and node["fabricNode"]["attributes"]["fabricSt"] == "active"] + for leaf in fabric_active_leaf_nodes: + leaf_id = leaf['fabricNode']['attributes']['id'] + fabric_leaf_ids.append(leaf_id) + + if presListeners_object_data: + for presListener_mo in presListeners_object_data: + dn = presListener_mo['presListener']['attributes']['dn'] + node_match = re.search(node_regex, dn) + class_match = re.search(class_regex, dn) + + if class_match and class_match.group("class") == "4307" and node_match: + node = node_match.group("node") + class_id = class_match.group("class") + preslistener_leaf_ids.append(node) + + missing_nodes = set(fabric_leaf_ids) - set(preslistener_leaf_ids) + + if missing_nodes: + for node_id in sorted(missing_nodes): + data.append([node_id, "active"]) + result = FAIL_O + return Result(result=result,msg="PresListener Object Missing Nodes: {}".format(missing_nodes ), headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) + if not fabric_leaf_ids: + return Result(result=FAIL_UF, msg="Could not retrieve any leaf node data") + return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) + else: + return Result(result=NA, msg=VER_NOT_AFFECTED) + + # ---- Script Execution ---- @@ -6188,6 +6242,7 @@ class CheckManager: standby_sup_sync_check, isis_database_byte_check, configpush_shard_check, + active_node_presListener_mo_object_check ] ssh_checks = [ diff --git a/docs/docs/validations.md b/docs/docs/validations.md index 68ca1c0..4aabcfd 100644 --- a/docs/docs/validations.md +++ b/docs/docs/validations.md @@ -193,6 +193,7 @@ Items | Defect | This Script [Stale pconsRA Object][d26] | CSCwp22212 | :warning:{title="Deprecated"} | :no_entry_sign: [ISIS DTEPs Byte Size][d27] | CSCwp15375 | :white_check_mark: | :no_entry_sign: [Policydist configpushShardCont Crash][d28] | CSCwp95515 | :white_check_mark: | +[active_node_presListener_mo_object_check][d29] | CSCwn81692 | :white_check_mark: | :no_entry_sign: [d1]: #ep-announce-compatibility [d2]: #eventmgr-db-size-defect-susceptibility @@ -222,6 +223,7 @@ Items | Defect | This Script [d26]: #stale-pconsra-object [d27]: #isis-dteps-byte-size [d28]: #policydist-configpushshardcont-crash +[d29]: #active_node_presListener_mo_object_check ## General Check Details @@ -2648,6 +2650,19 @@ Due to [CSCwp95515][59], upgrading to an affected version while having any `conf If any instances of `configpushShardCont` are flagged by this script, Cisco TAC must be contacted to identify and resolve the underlying issue before performing the upgrade. +### active_node_presListener_mo_object_check + +RCA: +After upgrading the leaf to an affected version, interface configurations are not pushed because the "pres.Listener" object is missing in the APIC. +This occurs due to an incomplete/faulty APIC upgrade. + +IMPACT: +If "pres.Listener" is missing after a leaf upgrade or clean reload, leaf ports remain out of service, and infraAccPortP and infraAccBndlGrp cannot program interfaces. + +Suggestion: +Verify that all active leaf node objects are present in the "pres.Listener" list (class 4307) and alert if the affected version is detected. If an alert is raised, upgrade to a version that includes the fix for [CSCwn81692][62]. + + [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 @@ -2710,3 +2725,4 @@ If any instances of `configpushShardCont` are flagged by this script, Cisco TAC [59]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp95515 [60]: https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-743951.html#Inter [61]: https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-743951.html#EnablePolicyCompression +[62]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwn81692 diff --git a/tests/checks/active_node_presListener_mo_object_check/fabricNode.json b/tests/checks/active_node_presListener_mo_object_check/fabricNode.json new file mode 100644 index 0000000..5dca013 --- /dev/null +++ b/tests/checks/active_node_presListener_mo_object_check/fabricNode.json @@ -0,0 +1,24 @@ +[ + { + + "fabricNode": { + "attributes": { + "dn": "topology/pod-1/node-104", + "fabricSt": "active", + "id": "104", + "role": "leaf" + } + } +}, +{ + "fabricNode": { + "attributes": { + "dn": "topology/pod-1/node-105", + "fabricSt": "active", + "id": "105", + "role": "leaf" + } + } +} + +] diff --git a/tests/checks/active_node_presListener_mo_object_check/fabricNode_empty.json b/tests/checks/active_node_presListener_mo_object_check/fabricNode_empty.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/tests/checks/active_node_presListener_mo_object_check/fabricNode_empty.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/checks/active_node_presListener_mo_object_check/presListener.json b/tests/checks/active_node_presListener_mo_object_check/presListener.json new file mode 100644 index 0000000..741e7b5 --- /dev/null +++ b/tests/checks/active_node_presListener_mo_object_check/presListener.json @@ -0,0 +1,21 @@ +[ + { + "presListener": { + "attributes": { + "childAction": "", + "dn": "resregistry/resregistry-20/relnholder/rspresClass-[resregistry/resregistry-20/class-4307]/list-[uni/infra/nodecfgcont/node-104]", + "lstDn": "uni/infra/nodecfgcont/node-104" + } + } + }, + { + "presListener": { + "attributes": { + "dn": "resregistry/resregistry-20/relnholder/rspresClass-[resregistry/resregistry-20/class-4307]/list-[uni/infra/nodecfgcont/node-105]", + "lstDn": "uni/infra/nodecfgcont/node-105" + + } + } + } + +] diff --git a/tests/checks/active_node_presListener_mo_object_check/presListener_empty.json b/tests/checks/active_node_presListener_mo_object_check/presListener_empty.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/tests/checks/active_node_presListener_mo_object_check/presListener_empty.json @@ -0,0 +1 @@ +[] diff --git a/tests/checks/active_node_presListener_mo_object_check/presListener_missing_node.json b/tests/checks/active_node_presListener_mo_object_check/presListener_missing_node.json new file mode 100644 index 0000000..66d396a --- /dev/null +++ b/tests/checks/active_node_presListener_mo_object_check/presListener_missing_node.json @@ -0,0 +1,10 @@ +[ + { + "presListener": { + "attributes": { + "dn": "resregistry/resregistry-20/relnholder/rspresClass-[resregistry/resregistry-20/class-4307]/list-[uni/infra/nodecfgcont/node-104]", + "lstDn": "uni/infra/nodecfgcont/node-104" + } + } +} +] diff --git a/tests/checks/active_node_presListener_mo_object_check/test_active_node_presListener_mo_object_check.py b/tests/checks/active_node_presListener_mo_object_check/test_active_node_presListener_mo_object_check.py new file mode 100644 index 0000000..a04422d --- /dev/null +++ b/tests/checks/active_node_presListener_mo_object_check/test_active_node_presListener_mo_object_check.py @@ -0,0 +1,103 @@ +import os +import pytest +import logging +import importlib +from helpers.utils import read_data + +script = importlib.import_module("aci-preupgrade-validation-script") + +log = logging.getLogger(__name__) +dir = os.path.dirname(os.path.abspath(__file__)) + +test_function = "active_node_presListener_mo_object_check" + +# icurl queries +presListener = 'presListener.json?query-target-filter=wcard(presListener.dn,"4307")' +fabricNode = 'fabricNode.json?query-target-filter=and(wcard(fabricNode.role,"leaf"),wcard(fabricNode.fabricSt,"active"))' + +@pytest.mark.parametrize( + "icurl_outputs, tversion,fabric_nodes,expected_result", + [ + #CASE 1: Check pass case with all nodes present + ( + { + presListener: read_data( + dir, "presListener.json" + ) + }, + "6.1(2f)", + read_data( + dir, "fabricNode.json" + ), + script.PASS, + ), + #CASE 2: Check with missing nodes in presListener mo + ( + { + presListener: read_data( + dir, "presListener_missing_node.json" + ) + }, + "6.1(2f)", + read_data( + dir, "fabricNode.json" + ), + script.FAIL_O + ), + #CASE 3: Check with missing nodes in presListener mo on affected version + ( + { + presListener: read_data( + dir, "presListener_missing_node.json" + ) + }, + "5.2(8h)", + read_data( + dir, "fabricNode.json" + ), + script.FAIL_O, + ), + #CASE 4: Check with empty responses of fabric node and presListener mo for latest version + ( + { + presListener: read_data( + dir, "presListener_empty.json" + ) + }, + "6.1(3h)", + read_data( + dir, "fabricNode_empty.json" + ), + script.NA, + ), + #CASE 5: Check with empty responses of fabric node and presListener mo for affected version + ( + { + presListener: read_data( + dir, "presListener_empty.json" + ), + }, + "5.2(8h)", + read_data( + dir, "fabricNode_empty.json" + ), + script.FAIL_UF, + ), + #CASE 6: tversion not given + ( + { + presListener: read_data( + dir, "presListener_missing_node.json" + ) + }, + None, + read_data( + dir, "fabricNode.json" + ), + script.MANUAL, + ), + ], +) +def test_logic(run_check,mock_icurl, tversion,fabric_nodes, expected_result): + result = run_check(tversion=script.AciVersion(tversion) if tversion else None, fabric_nodes=fabric_nodes) + assert result.result == expected_result