Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)"):
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'])
pod_id = dn.group("pod")
node_id = dn.group("node")
for mo in presListener:
if (
node_id in mo['presListener']['attributes']['lstDn'] and
'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:
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 ----

Expand Down Expand Up @@ -6188,6 +6230,7 @@ class CheckManager:
standby_sup_sync_check,
isis_database_byte_check,
configpush_shard_check,
pres_listener_mo_check,

]
ssh_checks = [
Expand Down
12 changes: 12 additions & 0 deletions docs/docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
[Missing presListener MO][d29] | CSCwn81692 | :white_check_mark: |

[d1]: #ep-announce-compatibility
[d2]: #eventmgr-db-size-defect-susceptibility
Expand Down Expand Up @@ -222,6 +223,7 @@ Items | Defect | This Script
[d26]: #stale-pconsra-object
[d27]: #isis-dteps-byte-size
[d28]: #policydist-configpushshardcont-crash
[d29]: #missing-preslistener-mo


## General Check Details
Expand Down Expand Up @@ -2648,6 +2650,15 @@ 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.


### Missing presListener MO

In ACI there is a special class called presListener that handles policy download/deployment upon a Switch clean reload or upgrade.

Due to [CSCwn81692][62], after upgrade (or clean-reload) if the presListener MO is missing for a leaf , the infraAccPortP and infraAccBndlGrp MOs will fail to program in the interfaces, this puts the ports in "out-of-service" status.

If there any missing presListener, contact Cisco TAC to provide a workaround before an upgrade.


[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
Expand Down Expand Up @@ -2710,3 +2721,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
95 changes: 95 additions & 0 deletions tests/checks/pres_listener_mo_check/fabricNode.json
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)"
}
}
}
]
38 changes: 38 additions & 0 deletions tests/checks/pres_listener_mo_check/presListener-Neg.json
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": ""
}
}
}
]
50 changes: 50 additions & 0 deletions tests/checks/pres_listener_mo_check/presListener-Pos.json
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": ""
}
}
}
]
67 changes: 67 additions & 0 deletions tests/checks/pres_listener_mo_check/test_pres_listener_mo_check.py
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