-
Notifications
You must be signed in to change notification settings - Fork 50
New validation for CSCwr51759 Cleanup vnsRsCIfAtt usage in service #394
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 |
|---|---|---|
|
|
@@ -6410,6 +6410,118 @@ 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="Cleanup vnsRsCIfAtt usage in services") | ||
| def vns_rscifatt_cleanup_check(tversion, **kwargs): | ||
| result = PASS | ||
| headers = ["Tenant", "Device Name", "Cluster Interface", "Missing Concrete Interface", "vnsRsCIfAtt DN"] | ||
| data = [] | ||
| recommended_action = ( | ||
| "Mo vnsRsCIfAtt is deprecated >=6.0(3d). Before upgrade, under Services, add the missing concrete interface as vnsRsCIfAttN under the same cluster interface" | ||
| ) | ||
| doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#cleanup-vnsrscifatt-usage-in-services" | ||
|
|
||
| if not tversion: | ||
| return Result(result=MANUAL, msg=TVER_MISSING, doc_url=doc_url) | ||
|
|
||
| if tversion.older_than("6.0(3d)"): | ||
| return Result(result=NA, msg=VER_NOT_AFFECTED, doc_url=doc_url) | ||
|
|
||
| vnsRsCIfAtts = icurl("class", "vnsRsCIfAtt.json?rsp-prop-include=config-only") | ||
| if not vnsRsCIfAtts: | ||
| return Result(result=PASS, msg="No user-configured vnsRsCIfAtt payload found.", doc_url=doc_url) | ||
|
|
||
| vnsRsCIfAttNs = icurl("class", "vnsRsCIfAttN.json?rsp-prop-include=config-only") | ||
|
|
||
| def get_target_dn(relation_attributes): | ||
| target_dn = relation_attributes["tDn"].strip() if "tDn" in relation_attributes else "" | ||
| if target_dn: | ||
| return target_dn | ||
| if "dn" not in relation_attributes: | ||
| return "" | ||
| relation_dn = relation_attributes["dn"] | ||
| target_dn_match = re.search(r"\[(.*)\]$", relation_dn) | ||
| return target_dn_match.group(1).strip() if target_dn_match else "" | ||
|
|
||
| def get_parent_dn(relation_dn): | ||
| relation_dn = relation_dn.strip() | ||
| if "/rscIfAtt-[" in relation_dn: | ||
| return relation_dn.split("/rscIfAtt-[", 1)[0] | ||
| if "/rscIfAttN-[" in relation_dn: | ||
| return relation_dn.split("/rscIfAttN-[", 1)[0] | ||
| return relation_dn.rsplit("/", 1)[0] if "/" in relation_dn else "" | ||
|
|
||
| def parse_relation_context(relation_dn): | ||
| tenant_name = "" | ||
| device_name = "" | ||
| logical_interface = "" | ||
| concrete_interface = "" | ||
|
|
||
| dn_match = re.search( | ||
| r"uni/tn-(?P<tenant>[^/]+)/lDevVip-(?P<device>[^/]+)/lIf-(?P<lif>[^/]+)/" | ||
| r"rscIfAtt-\[.*?/cIf-\[(?P<cif>[^\]]+)\]\]", | ||
| relation_dn, | ||
| ) | ||
| if dn_match: | ||
| tenant_name = dn_match.group("tenant") | ||
| device_name = dn_match.group("device") | ||
| logical_interface = dn_match.group("lif") | ||
| concrete_interface = dn_match.group("cif") | ||
| return tenant_name, device_name, logical_interface, concrete_interface | ||
|
|
||
| old_relation_dn_by_key = {} | ||
| for vnsRsCIfAtt in vnsRsCIfAtts: | ||
| if "vnsRsCIfAtt" not in vnsRsCIfAtt: | ||
| continue | ||
| if "attributes" not in vnsRsCIfAtt["vnsRsCIfAtt"]: | ||
| continue | ||
| relation_attributes = vnsRsCIfAtt["vnsRsCIfAtt"]["attributes"] | ||
| if "dn" not in relation_attributes: | ||
| continue | ||
|
|
||
| relation_dn = relation_attributes["dn"].strip() | ||
| if not relation_dn: | ||
| continue | ||
| target_dn = get_target_dn(relation_attributes) | ||
|
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. dn is the full path of the relationship object, and tDn is the path of the target interface object it is attached to. No need of check for tDN. |
||
| if not target_dn: | ||
| continue | ||
| relation_key = (get_parent_dn(relation_dn), target_dn) | ||
| old_relation_dn_by_key[relation_key] = relation_dn | ||
|
|
||
| if not old_relation_dn_by_key: | ||
| return Result(result=PASS, msg="No user-configured vnsRsCIfAtt payload found.", doc_url=doc_url) | ||
|
|
||
| new_relation_keys = set() | ||
| for vnsRsCIfAttN in vnsRsCIfAttNs: | ||
| if "vnsRsCIfAttN" not in vnsRsCIfAttN: | ||
| continue | ||
| if "attributes" not in vnsRsCIfAttN["vnsRsCIfAttN"]: | ||
| continue | ||
| relation_attributes = vnsRsCIfAttN["vnsRsCIfAttN"]["attributes"] | ||
| if "dn" not in relation_attributes: | ||
| continue | ||
|
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. when mo is not configured, vnsRsCIfAttNs will be empty.. above if conditions not needed. |
||
|
|
||
| relation_dn = relation_attributes["dn"].strip() | ||
| if not relation_dn: | ||
| continue | ||
| target_dn = get_target_dn(relation_attributes) | ||
| if not target_dn: | ||
| continue | ||
| relation_key = (get_parent_dn(relation_dn), target_dn) | ||
| new_relation_keys.add(relation_key) | ||
|
|
||
| for relation_key in sorted(old_relation_dn_by_key.keys()): | ||
| if relation_key in new_relation_keys: | ||
| continue | ||
| missing_dn = old_relation_dn_by_key[relation_key] | ||
| tenant_name, device_name, logical_interface, concrete_interface = parse_relation_context(missing_dn) | ||
| data.append([tenant_name, device_name, logical_interface, concrete_interface, missing_dn]) | ||
|
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. where is that fault case handled for post upgrade case.. fault : F1690 |
||
|
|
||
| if data: | ||
| result = FAIL_O | ||
|
|
||
| return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) | ||
|
|
||
|
|
||
| # ---- Script Execution ---- | ||
|
|
||
|
|
||
|
|
@@ -6581,6 +6693,7 @@ class CheckManager: | |
| rogue_ep_coop_exception_mac_check, | ||
| n9k_c9408_model_lem_count_check, | ||
| inband_management_policy_misconfig_check, | ||
| vns_rscifatt_cleanup_check, | ||
| ] | ||
| ssh_checks = [ | ||
| # General | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -203,6 +203,7 @@ Items | Defect | This Script | |
| [N9K-C9408 with more than 5 N9K-X9400-16W LEMs][d31] | CSCws82819 | :white_check_mark: | :no_entry_sign: | ||
| [Multi-Pod Modular Spine Bootscript File][d32] | CSCwr66848 | :white_check_mark: | :no_entry_sign: | ||
| [Inband Management Policy Misconfiguration][d33]| CSCwd40071 | :white_check_mark: | :no_entry_sign: | ||
| [Cleanup vnsRsCIfAtt usage in services][d34] | CSCwr51759 | :white_check_mark: | :no_entry_sign: | ||
|
|
||
| [d1]: #ep-announce-compatibility | ||
| [d2]: #eventmgr-db-size-defect-susceptibility | ||
|
|
@@ -237,6 +238,7 @@ Items | Defect | This Script | |
| [d31]: #n9k-c9408-with-more-than-5-n9k-x9400-16w-lems | ||
| [d32]: #multi-pod-modular-spine-bootscript-file | ||
| [d33]: #inband-management-policy-misconfiguration | ||
| [d34]: #cleanup-vnsrscifatt-usage-in-services | ||
|
|
||
| ## General Check Details | ||
|
|
||
|
|
@@ -2797,6 +2799,19 @@ 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. | ||
|
|
||
| ### Cleanup vnsRsCIfAtt usage in services | ||
|
|
||
| Due to [CSCwr51759][70], when targeting 6.0(3)+, having only `vnsRsCIfAtt` without the corresponding `vnsRsCIfAttN` under the same `vnsLIf` can leave service graph interface attachment in an inconsistent state. | ||
|
|
||
| Impact: | ||
|
|
||
| If any `vnsRsCIfAtt` relation exists without a matching `vnsRsCIfAttN` for the same concrete interface target (`tDn`), the upgrade is outage-risky and should be treated as affected. | ||
|
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. please update the document as per impact and requirement. |
||
|
|
||
| Suggestion: | ||
|
|
||
| Before the upgrade, add the missing `vnsRsCIfAttN` relation under the same cluster interface (`vnsLIf`) with the same concrete interface target (`tDn`). | ||
|
|
||
|
|
||
|
|
||
| [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 | ||
|
|
@@ -2867,4 +2882,5 @@ 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 | ||
| [70]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwr51759 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| 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 = "vns_rscifatt_cleanup_check" | ||
|
|
||
| # icurl queries | ||
| vnsRsCIfAtt_api = "vnsRsCIfAtt.json?rsp-prop-include=config-only" | ||
| vnsRsCIfAttN_api = "vnsRsCIfAttN.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, | ||
| [], | ||
| ), | ||
| # No user-configured vnsRsCIfAtt payload | ||
| ( | ||
| { | ||
| vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), | ||
| }, | ||
| "6.1(5e)", | ||
| script.PASS, | ||
| [], | ||
| ), | ||
| # 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"), | ||
| }, | ||
| "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]]", | ||
| ] | ||
| ], | ||
| ), | ||
| ], | ||
| ) | ||
| 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 |
| 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 @@ | ||
| [] |
| 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]" | ||
| } | ||
| } | ||
| } | ||
| ] |
Uh oh!
There was an error while loading. Please reload this page.
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.
change the proper function name and title