Skip to content
56 changes: 56 additions & 0 deletions aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -6007,6 +6007,61 @@ def apic_vmm_inventory_sync_faults_check(**kwargs):
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<node>\d+)"
class_regex = r"resregistry/resregistry-(?P<registry>\d+)/class-(?P<class>\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 ----


Expand Down Expand Up @@ -6168,6 +6223,7 @@ class CheckManager:
standby_sup_sync_check,
isis_database_byte_check,
configpush_shard_check,
active_node_presListener_mo_object_check

]
ssh_checks = [
Expand Down
16 changes: 16 additions & 0 deletions docs/docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,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
Expand Down Expand Up @@ -220,6 +221,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
Expand Down Expand Up @@ -2614,6 +2616,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
Expand Down Expand Up @@ -2676,3 +2691,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
Original file line number Diff line number Diff line change
@@ -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"
}
}
}

]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -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"

}
}
}

]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
]
Original file line number Diff line number Diff line change
@@ -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