-
Notifications
You must be signed in to change notification settings - Fork 48
issue NewValidation: CSCwn81692 #339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v4.2.0-dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6025,6 +6025,48 @@ def apic_downgrade_compat_warning_check(cversion, tversion, **kwargs): | |
|
|
||
| return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) | ||
|
|
||
| @check_wrapper(check_title="presListener MO Status") | ||
| def pres_listener_mo_check(fabric_nodes, tversion, **kwargs): | ||
| result = PASS | ||
| msg = '' | ||
| headers = ['Pod-ID', 'Node-ID', 'State'] | ||
| data = [] | ||
| recommended_action = 'Contact TAC to apply the workaround for the listed Nodes BEFORE Upgrade.' | ||
| doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations#missing-preslistener-mo' | ||
| # fabricNode.fabricSt shows `disabled` for both Decommissioned and Maintenance (GIR). | ||
| # fabricRsDecommissionNode.debug==yes is required to show `disabled (Maintenance)`. | ||
| presListener = icurl('class','presListener.json') | ||
| if not tversion: | ||
| return Result(result=MANUAL, msg=TVER_MISSING) | ||
|
|
||
| if tversion.newer_than("6.1(3f)"): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. High: The exact release boundary is not documented or tested: this condition keeps |
||
| result = NA | ||
| return Result(result=result, msg=msg, headers=headers, data=data, recommended_action=recommended_action) | ||
|
|
||
| for fabric_node in fabric_nodes: | ||
| found = False | ||
| if fabric_node['fabricNode']['attributes']['role'] != "leaf": | ||
| continue | ||
| state = fabric_node['fabricNode']['attributes']['fabricSt'] | ||
| if state != 'active': | ||
| continue | ||
| dn = re.search(node_regex, fabric_node['fabricNode']['attributes']['dn']) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. High: The regex result is dereferenced without validation. One malformed active-leaf DN raises an exception, changes the whole check to |
||
| pod_id = dn.group("pod") | ||
| node_id = dn.group("node") | ||
| for mo in presListener: | ||
| if ( | ||
| node_id in mo['presListener']['attributes']['lstDn'] and | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blocking: This substring check treats leaf |
||
| 'class-4307' in mo['presListener']['attributes']['dn'] | ||
| ): | ||
| found = True | ||
| continue | ||
| if not found: | ||
| result = FAIL_O | ||
| data.append([pod_id, node_id, state]) | ||
| if not fabric_nodes: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blocking: |
||
| result = MANUAL | ||
| msg = 'Active Switch Leaf fabricNode not found!' | ||
| return Result(result=result, msg=msg, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) | ||
|
|
||
| # ---- Script Execution ---- | ||
|
|
||
|
|
@@ -6188,6 +6230,7 @@ class CheckManager: | |
| standby_sup_sync_check, | ||
| isis_database_byte_check, | ||
| configpush_shard_check, | ||
| pres_listener_mo_check, | ||
|
|
||
| ] | ||
| ssh_checks = [ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| [ | ||
| { | ||
| "fabricNode": { | ||
| "attributes": { | ||
| "adSt": "off", | ||
| "address": "10.2.176.64", | ||
| "annotation": "", | ||
| "apicType": "apic", | ||
| "childAction": "", | ||
| "delayedHeartbeat": "no", | ||
| "dn": "topology/pod-1/node-101", | ||
| "extMngdBy": "", | ||
| "fabricSt": "active", | ||
| "id": "101", | ||
| "lastStateModTs": "2026-01-28T22:24:54.843+00:00", | ||
| "lcOwn": "local", | ||
| "modTs": "2026-01-28T22:25:42.207+00:00", | ||
| "model": "N9K-C93180YC-EX", | ||
| "monPolDn": "uni/fabric/monfab-default", | ||
| "name": "SITE2-L101", | ||
| "nameAlias": "", | ||
| "nodeType": "unspecified", | ||
| "role": "leaf", | ||
| "serial": "FDO204520TY", | ||
| "status": "", | ||
| "uid": "0", | ||
| "userdom": "all", | ||
| "vendor": "Cisco Systems, Inc", | ||
| "version": "n9000-15.2(6e)" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| "fabricNode": { | ||
| "attributes": { | ||
| "adSt": "off", | ||
| "address": "10.2.176.66", | ||
| "annotation": "", | ||
| "apicType": "apic", | ||
| "childAction": "", | ||
| "delayedHeartbeat": "no", | ||
| "dn": "topology/pod-1/node-102", | ||
| "extMngdBy": "", | ||
| "fabricSt": "active", | ||
| "id": "102", | ||
| "lastStateModTs": "2026-01-28T22:25:45.863+00:00", | ||
| "lcOwn": "local", | ||
| "modTs": "2026-01-28T22:26:42.234+00:00", | ||
| "model": "N9K-C93180YC-EX", | ||
| "monPolDn": "uni/fabric/monfab-default", | ||
| "name": "SITE2-L102", | ||
| "nameAlias": "", | ||
| "nodeType": "unspecified", | ||
| "role": "leaf", | ||
| "serial": "FDO204520SZ", | ||
| "status": "", | ||
| "uid": "0", | ||
| "userdom": "all", | ||
| "vendor": "Cisco Systems, Inc", | ||
| "version": "n9000-15.2(6e)" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| "fabricNode": { | ||
| "attributes": { | ||
| "adSt": "off", | ||
| "address": "10.2.176.67", | ||
| "annotation": "", | ||
| "apicType": "apic", | ||
| "childAction": "", | ||
| "delayedHeartbeat": "no", | ||
| "dn": "topology/pod-1/node-103", | ||
| "extMngdBy": "", | ||
| "fabricSt": "active", | ||
| "id": "103", | ||
| "lastStateModTs": "2026-01-28T22:25:45.863+00:00", | ||
| "lcOwn": "local", | ||
| "modTs": "2026-01-28T22:26:42.231+00:00", | ||
| "model": "N9K-C93240YC-FX2", | ||
| "monPolDn": "uni/fabric/monfab-default", | ||
| "name": "SITE2-L103", | ||
| "nameAlias": "", | ||
| "nodeType": "unspecified", | ||
| "role": "leaf", | ||
| "serial": "FDO25210JA1", | ||
| "status": "", | ||
| "uid": "0", | ||
| "userdom": "all", | ||
| "vendor": "Cisco Systems, Inc", | ||
| "version": "n9000-15.3(2f)" | ||
| } | ||
| } | ||
| } | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| [ | ||
| { | ||
| "presListener": { | ||
| "attributes": { | ||
| "childAction": "", | ||
| "dn": "resregistry/resregistry-20/relnholder/rspresClass-[resregistry/resregistry-20/class-4307]/list-[uni/infra/nodecfgcont]", | ||
| "lcOwn": "local", | ||
| "lstDn": "uni/infra/nodecfgcont", | ||
| "modTs": "2026-01-21T01:44:22.201+00:00", | ||
| "status": "" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| "presListener": { | ||
| "attributes": { | ||
| "childAction": "", | ||
| "dn": "resregistry/resregistry-20/relnholder/rspresClass-[resregistry/resregistry-20/class-4307]/list-[uni/infra/nodecfgcont/node-102]", | ||
| "lcOwn": "local", | ||
| "lstDn": "uni/infra/nodecfgcont/node-102", | ||
| "modTs": "2026-01-21T16:02:15.685+00:00", | ||
| "status": "" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| "presListener": { | ||
| "attributes": { | ||
| "childAction": "", | ||
| "dn": "resregistry/resregistry-20/relnholder/rspresClass-[resregistry/resregistry-20/class-4307]/list-[uni/infra/nodecfgcont/node-101]", | ||
| "lcOwn": "local", | ||
| "lstDn": "uni/infra/nodecfgcont/node-101", | ||
| "modTs": "2026-01-21T16:02:15.685+00:00", | ||
| "status": "" | ||
| } | ||
| } | ||
| } | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| [ | ||
| { | ||
| "presListener": { | ||
| "attributes": { | ||
| "childAction": "", | ||
| "dn": "resregistry/resregistry-20/relnholder/rspresClass-[resregistry/resregistry-20/class-4307]/list-[uni/infra/nodecfgcont]", | ||
| "lcOwn": "local", | ||
| "lstDn": "uni/infra/nodecfgcont", | ||
| "modTs": "2026-01-21T01:44:22.201+00:00", | ||
| "status": "" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| "presListener": { | ||
| "attributes": { | ||
| "childAction": "", | ||
| "dn": "resregistry/resregistry-20/relnholder/rspresClass-[resregistry/resregistry-20/class-4307]/list-[uni/infra/nodecfgcont/node-102]", | ||
| "lcOwn": "local", | ||
| "lstDn": "uni/infra/nodecfgcont/node-102", | ||
| "modTs": "2026-01-21T16:02:15.685+00:00", | ||
| "status": "" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| "presListener": { | ||
| "attributes": { | ||
| "childAction": "", | ||
| "dn": "resregistry/resregistry-20/relnholder/rspresClass-[resregistry/resregistry-20/class-4307]/list-[uni/infra/nodecfgcont/node-101]", | ||
| "lcOwn": "local", | ||
| "lstDn": "uni/infra/nodecfgcont/node-101", | ||
| "modTs": "2026-01-21T16:02:15.685+00:00", | ||
| "status": "" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| "presListener": { | ||
| "attributes": { | ||
| "childAction": "", | ||
| "dn": "resregistry/resregistry-20/relnholder/rspresClass-[resregistry/resregistry-20/class-4307]/list-[uni/infra/nodecfgcont/node-103]", | ||
| "lcOwn": "local", | ||
| "lstDn": "uni/infra/nodecfgcont/node-103", | ||
| "modTs": "2026-01-21T18:39:38.321+00:00", | ||
| "status": "" | ||
| } | ||
| } | ||
| } | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| 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 = "pres_listener_mo_check" | ||
|
|
||
| # icurl queries | ||
| # fabricNode = "fabricNode.json" | ||
| # fabricNode += '?query-target-filter=and(eq(fabricNode.role,"leaf"),eq(fabricNode.fabricSt,"active"))' | ||
|
|
||
| presListener = "presListener.json" | ||
|
|
||
| @pytest.mark.parametrize( | ||
| "icurl_outputs, fabric_nodes, tversion, expected_result", | ||
| [ | ||
| # No tVersion, MANUAL | ||
| ( | ||
| {presListener: read_data(dir, "presListener-Pos.json")}, | ||
| [], | ||
| None, | ||
| script.MANUAL | ||
| ), | ||
| # FabricNodes missing, MANUAL | ||
| ( | ||
| {presListener: read_data(dir, "presListener-Pos.json")}, | ||
| [], | ||
| "6.1(3a)", | ||
| script.MANUAL | ||
| ), | ||
| # tVersion newer than 6.1-3f , NA | ||
| ( | ||
| {presListener: read_data(dir, "presListener-Pos.json")}, | ||
| [], | ||
| "6.1(4a)", | ||
| script.NA | ||
| ), | ||
| # PASS TESTS | ||
| # Version can be recovered with testapi (>=6.1-3f) | ||
| ( | ||
| {presListener: read_data(dir, "presListener-Pos.json")}, | ||
| read_data(dir, "fabricNode.json"), | ||
| "6.1(3a)", | ||
| script.PASS, | ||
| ), | ||
| # FAIL_O TESTS | ||
| # Version can be recovered with testapi (>=6.1-3f) | ||
| ( | ||
| {presListener: read_data(dir, "presListener-Neg.json")}, | ||
| read_data(dir, "fabricNode.json"), | ||
| "6.1(3a)", | ||
| script.FAIL_O, | ||
| ), | ||
| ], | ||
| ) | ||
| def test_logic(run_check, mock_icurl, fabric_nodes, tversion, expected_result): | ||
| result = run_check( | ||
| fabric_nodes=fabric_nodes, | ||
| tversion=script.AciVersion(tversion) if tversion else None, | ||
| ) | ||
| assert result.result == expected_result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
High: This full-class query runs before the target-version guards. Therefore a missing or unaffected target can become
ERRORif collection fails even though the result should beMANUALorN/A, and every run unnecessarily retrieves allpresListenerobjects. Move the guards before collection and use a server-side filter for the relevant class-4307/node entries. Please test thaticurlis not called for missing or unaffected targets.