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
102 changes: 102 additions & 0 deletions aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -6410,6 +6410,107 @@ def svccore_excessive_data_check(**kwargs):
return Result(result=ERROR, msg="Error occurred while fetching svccore object counts: {}".format(str(e)), doc_url=doc_url)


@check_wrapper(check_title="Check missing vnsRsCIfAttN")
def vnsrscifattn_missing_check(tversion, **kwargs):
result = PASS
headers = ["Tenant", "Device Name", "Cluster Interface", "Missing Concrete Interface", "vnsRsCIfAtt DN"]
manual_headers = ["Tenant", "Device Name", "Cluster Interface", "Missing Concrete Interface", "vnsLIf DN"]
data = []
recommended_action = (
"From 6.0(3) release, Mo vnsRsCIfAtt is deprecated. Before upgrade, reattach any missing concrete interface mapping under the same L4-L7 device cluster interface so the corresponding vnsRsCIfAttN relation exists."
)
doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#check-missing-vnsrscifattn"

if not tversion:
return Result(result=MANUAL, msg=TVER_MISSING, doc_url=doc_url)

if tversion.older_than("6.0(3d)"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add cversion check
current code is pre-6.0.3

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, cversion check is not required. When we are providing tversion as 6.0(3) and above if both old mo and new mo are not configured then we are alerting the customer with recommended action. This is equivalent by checking the cversion <6.0(3) and verifying the old and new mo config unavailability and alerting the customer.

return Result(result=NA, msg=VER_NOT_AFFECTED, doc_url=doc_url)

vnsRsCIfAtts = icurl("class", "vnsRsCIfAtt.json?rsp-prop-include=config-only")
vnsRsCIfAttNs = icurl("class", "vnsRsCIfAttN.json?rsp-prop-include=config-only")

if not vnsRsCIfAtts and not vnsRsCIfAttNs:
vnsLIfs = icurl("class", "vnsLIf.json?rsp-prop-include=config-only")
for vnsLIf in vnsLIfs:
try:
lif_dn = vnsLIf["vnsLIf"]["attributes"]["dn"].strip()
except (KeyError, TypeError, AttributeError):
continue
if not lif_dn:
continue

match = re.search(
r"uni/tn-(?P<tenant>[^/]+)/lDevVip-(?P<device>[^/]+)/lIf-(?P<lif>[^/]+)$",
lif_dn,
)
data.append([
match.group("tenant") if match else "",
match.group("device") if match else "",
match.group("lif") if match else "",
"N/A",
lif_dn,
])

data.sort(key=lambda row: row[-1])

if data:
msg = "vnsLIf has neither vnsRsCIfAtt nor vnsRsCIfAttN. Manual verification required to avoid service graph inconsistency."
manual_action = (
"Under each impacted L4-L7 device cluster interface, manually verify and reattach concrete interfaces as needed."
)
return Result(result=MANUAL, msg=msg, headers=manual_headers, data=data, recommended_action=manual_action, doc_url=doc_url)

msg = "Both vnsRsCIfAtt and vnsRsCIfAttN are missing. Manual verification required for service graph interface attachments."
manual_action = (
"Validate L4-L7 device cluster interfaces and reattach concrete interfaces where needed."
)
return Result(result=MANUAL, msg=msg, recommended_action=manual_action, doc_url=doc_url)

if not vnsRsCIfAtts:
return Result(result=PASS, msg="No user-configured vnsRsCIfAtt payload found.", doc_url=doc_url)

new_dn_keys = set()
for new_mo in vnsRsCIfAttNs:
try:
dn = new_mo["vnsRsCIfAttN"]["attributes"]["dn"].strip()
except (KeyError, TypeError, AttributeError):
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try this optimized script.

new_dn_keys = set()
for new_no in vnsRsCIfAttNs:
try:
dn = new_no["vnsRsCIfAttN"]["attributes"]["dn"].strip()
except (KeyError, TypeError, AttributeError):
continue
if dn:
new_dn_keys.add(dn.replace("/rscIfAttN-[", "/rscIfAtt-[", 1))

for old_mo in vnsRsCIfAtts:
try:
old_dn = old_mo["vnsRsCIfAtt"]["attributes"]["dn"].strip()
except (KeyError, TypeError, AttributeError):
continue
if not old_dn:
continue

if old_dn.replace("/rscIfAttN-[", "/rscIfAtt-[", 1) in new_dn_keys:
    continue

match = re.search(
    r"uni/tn-(?P<tenant>[^/]+)/lDevVip-(?P<device>[^/]+)/lIf-(?P<lif>[^/]+)/"
    r"rscIfAtt-\[.*?/cIf-\[(?P<cif>[^\]]+)\]\]",
    old_dn,
)
data.append([
    match.group("tenant") if match else "",
    match.group("device") if match else "",
    match.group("lif") if match else "",
    match.group("cif") if match else "",
    old_dn,
])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above suggested optimized loop logic works functionally, but without sorting it can return rows in different orders based on API/input order. Our Pytest case compares as an ordered list, so it fails intermittently on order only. I added sorting to make pytest stable and output consistent.

if dn:
new_dn_keys.add(dn.replace("/rscIfAttN-[", "/rscIfAtt-[", 1))

for old_mo in vnsRsCIfAtts:
try:
old_dn = old_mo["vnsRsCIfAtt"]["attributes"]["dn"].strip()
except (KeyError, TypeError, AttributeError):
continue
if not old_dn:
continue

if old_dn.replace("/rscIfAttN-[", "/rscIfAtt-[", 1) in new_dn_keys:
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add this check

if none of vnsRsCIfAtt and vnsRsCIfAttN present. only in case Both of Mo missing attached to given vnsLif—> vnsLIf has NEITHER relation - service graph broken, ask Cu to do manual check warning only

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added manual check warning when both old and new mo config is unavailable.

match = re.search(
r"uni/tn-(?P<tenant>[^/]+)/lDevVip-(?P<device>[^/]+)/lIf-(?P<lif>[^/]+)/"
r"rscIfAtt-\[.*?/cIf-\[(?P<cif>[^\]]+)\]\]",
old_dn,
)
data.append([
match.group("tenant") if match else "",
match.group("device") if match else "",
match.group("lif") if match else "",
match.group("cif") if match else "",
old_dn,
])

data.sort(key=lambda row: row[-1])

if data:
result = FAIL_O

return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)


# ---- Script Execution ----


Expand Down Expand Up @@ -6502,6 +6603,7 @@ class CheckManager:
fabric_link_redundancy_check,
apic_downgrade_compat_warning_check,
svccore_excessive_data_check,
vnsrscifattn_missing_check,

# Faults
apic_disk_space_faults_check,
Expand Down
12 changes: 10 additions & 2 deletions docs/docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Items | This Script
[APIC downgrade compatibility when crossing 6.2 release][g19]| :white_check_mark: | :no_entry_sign:
[Supported Hardware Compatibility][g20] | :white_check_mark: | :no_entry_sign:
[Svccore Excessive Data Check][g21] | :white_check_mark: | :no_entry_sign:
[Check missing vnsRsCIfAttN][g22] | :white_check_mark: | :no_entry_sign:

[g1]: #compatibility-target-aci-version
[g2]: #compatibility-cimc-version
Expand All @@ -61,6 +62,7 @@ Items | This Script
[g19]: #apic-downgrade-compatibility-when-crossing-62-release
[g20]: #supported-hardware-compatibility
[g21]: #svccore-excessive-data-check
[g22]: #check-missing-vnsrscifattn

### Fault Checks
Items | Faults | This Script | APIC built-in
Expand Down Expand Up @@ -2769,7 +2771,6 @@ 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.


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unwanted new lines

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed unwanted lines at line numbers 43 and 210

### Inband Management Policy Misconfiguration

Due to the defect [CSCwh80837][67], starting from version 6.0(4c), mgmtRsInBStNode policy get modified in leaf/spine during Apic upgrade.
Expand Down Expand Up @@ -2797,6 +2798,13 @@ Administrators may be unable to access or operate the APIC GUI, potentially impa

This check will verify the count of the `svccoreCtrlr` Managed Object and raise and alarm with the bug if object count found more than 240. Remove the content or objects of `svccoreCtrlr` or `svccoreNode`. Contact Cisco TAC or upgrade to a release containing the fix for CSCws84232 before proceeding with an upgrade.

### Check missing vnsRsCIfAttN

When upgrading to 6.0(3) and above, 'vnsRsCIfAtt' is deleted without creating 'vnsRsCIfAttN' under 'vnsLIf'. This leaves the service graph interface attachment in an inconsistent state.

For all impacted DNs in this check, reattach the Concrete interfaces associated with the cluster interface under Devices in the Services > L4-L7 tab.

Tenant --> Services --> L4-L7 --> Devices (Device_name) --> cluster interface --> Concrete interfaces

[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
Expand Down Expand Up @@ -2867,4 +2875,4 @@ This check will verify the count of the `svccoreCtrlr` Managed Object and raise
[66]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwr66848
[67]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwh80837
[68]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwd40071
[69]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCws84232
[69]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCws84232
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
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 = "vnsrscifattn_missing_check"

# icurl queries
vnsRsCIfAtt_api = "vnsRsCIfAtt.json?rsp-prop-include=config-only"
vnsRsCIfAttN_api = "vnsRsCIfAttN.json?rsp-prop-include=config-only"
vnsLIf_api = "vnsLIf.json?rsp-prop-include=config-only"

@pytest.mark.parametrize(
"icurl_outputs, tversion, expected_result, expected_data",
[
# Target version missing
(
{},
None,
script.MANUAL,
[],
),
# Target version is not affected (< 6.0(3d))
(
{},
"6.0(2h)",
script.NA,
[],
),
# Both vnsRsCIfAtt and vnsRsCIfAttN are missing (no vnsLIf rows)
(
{
vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"),
vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAtt_empty.json"),
vnsLIf_api: [],
},
"6.1(5e)",
script.MANUAL,
[],
),
# Both vnsRsCIfAtt and vnsRsCIfAttN are missing while vnsLIf exists
(
{
vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"),
vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAtt_empty.json"),
vnsLIf_api: read_data(dir, "vnsLIf_only.json"),
},
"6.1(5e)",
script.MANUAL,
[
[
"CSCwj49418",
"test",
"intf-cons",
"N/A",
"uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons",
],
[
"CSCwj49418",
"test",
"intf-prov",
"N/A",
"uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov",
],
],
),
# All vnsRsCIfAtt relations have matching vnsRsCIfAttN relations
(
{
vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_match.json"),
vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAttN_match.json"),
},
"6.1(5e)",
script.PASS,
[],
),
# One vnsRsCIfAtt relation (cons) missing in vnsRsCIfAttN
(
{
vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_match.json"),
vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAttN_missing_cons.json"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add case for empty vnsRsCIfAttN.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added test case for empty vnsRsCIfAttN scenario

},
"6.1(5e)",
script.FAIL_O,
[
[
"CSCwj49418",
"test",
"intf-cons",
"cons",
"uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons/rscIfAtt-[uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[cons]]",
]
],
),
# vnsRsCIfAttN is empty; all vnsRsCIfAtt relations are missing
(
{
vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_match.json"),
vnsRsCIfAttN_api: [],
},
"6.1(5e)",
script.FAIL_O,
[
[
"CSCwj49418",
"test",
"intf-cons",
"cons",
"uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons/rscIfAtt-[uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[cons]]",
],
[
"CSCwj49418",
"test",
"intf-prov",
"prov",
"uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov/rscIfAtt-[uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[prov]]",
],
],
),
],
)
def test_logic(run_check, mock_icurl, tversion, expected_result, expected_data):
result = run_check(
tversion=script.AciVersion(tversion) if tversion else None,
)
assert result.result == expected_result
assert result.data == expected_data
18 changes: 18 additions & 0 deletions tests/checks/vnsrscifattn_missing_check/vnsLIf_only.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"vnsLIf": {
"attributes": {
"dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons",
"name": "intf-cons"
}
}
},
{
"vnsLIf": {
"attributes": {
"dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov",
"name": "intf-prov"
}
}
}
]
18 changes: 18 additions & 0 deletions tests/checks/vnsrscifattn_missing_check/vnsRsCIfAttN_match.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"vnsRsCIfAttN": {
"attributes": {
"dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov/rscIfAttN-[uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[prov]]",
"tDn": "uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[prov]"
}
}
},
{
"vnsRsCIfAttN": {
"attributes": {
"dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons/rscIfAttN-[uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[cons]]",
"tDn": "uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[cons]"
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"vnsRsCIfAttN": {
"attributes": {
"dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov/rscIfAttN-[uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[prov]]",
"tDn": "uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[prov]"
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
18 changes: 18 additions & 0 deletions tests/checks/vnsrscifattn_missing_check/vnsRsCIfAtt_match.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"vnsRsCIfAtt": {
"attributes": {
"dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov/rscIfAtt-[uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[prov]]",
"tDn": "uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[prov]"
}
}
},
{
"vnsRsCIfAtt": {
"attributes": {
"dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons/rscIfAtt-[uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[cons]]",
"tDn": "uni/tn-CSCwj49418/lDevVip-test/cDev-cdev/cIf-[cons]"
}
}
}
]