diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index 5e79f56..68ced39 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -22,7 +22,7 @@ from textwrap import TextWrapper from getpass import getpass from collections import defaultdict, OrderedDict -from datetime import datetime +from datetime import datetime,timedelta from argparse import ArgumentParser from itertools import chain import threading @@ -6702,6 +6702,276 @@ def stale_dbgacEpgSummaryTask_check(tversion, **kwargs): return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) +@check_wrapper(check_title="Vnsrscifatt Deprecation Check") +def vnsrscifatt_deprecation_check(tversion, cversion, **kwargs): + result = PASS + doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#vnsrscifatt-deprecation-check" + + lif_dn_regex = r"uni/tn-(?P[^/]+)/lDevVip-(?P[^/]+)/lIf-(?P[^/]+)$" + ldeviflif_regex = r"^uni/tn-[^/]+/lDevIf-\[(?Puni/tn-[^\]]+/lDevVip-[^\]]+)\]/lDevIfLIf-(?P[^/]+)$" + tn_regex = r"^uni/tn-([^/]+)/" + + 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) + + post_cifatt_delete = bool(cversion and (cversion.same_as("6.0(3d)") or cversion.newer_than("6.0(3d)"))) + if post_cifatt_delete: + headers = ["Tenant", "Device Name", "Cluster Interface"] + recommended_action = "Please review the concrete interface attachments under the flagged device cluster interfaces and reattach them using the UI: Tenant → Services → L4-L7 → Devices → Cluster Interface → Concrete Interface → + → Select the respective interface from the drop-down list → Submit" + else: + headers = ["Tenant", "Device Name", "Cluster Interface", "Missing Concrete Interface", "vnsRsCIfAtt DN"] + recommended_action = "Please reattach concrete interfaces again using the UI (without deleting the existing attachment objects): Tenant → Services → L4-L7 → Devices → Cluster Interface → Concrete Interface → + → Select the respective interface from the drop-down list → Submit" + + # ── Step 1: Collect deployed service-graph LIF DNs ────────────────────── + + # Build (contract_name, graph_name) keys from applied vnsGraphInst objects + graph_keys = set() + for entry in icurl("class", "vnsGraphInst.json?rsp-prop-include=config-only") or []: + try: + contract_dn = entry["vnsGraphInst"]["attributes"]["ctrctDn"].strip() + graph_dn = entry["vnsGraphInst"]["attributes"]["graphDn"].strip() + except (KeyError, TypeError, AttributeError): + continue + contract_match = re.search(r"/brc-([^/]+)$", contract_dn) + graph_match = re.search(r"/AbsGraph-([^/]+)$", graph_dn) + if contract_match and graph_match: + graph_keys.add((contract_match.group(1), graph_match.group(1))) + + lif_dns = set() + lif_source_tenants = {} # lif_dn -> set(contract tenants) for implicit-object detection + dev_source_tenants = {} # device-prefix DN -> set(contract tenants) + + ldev_ctx_query = ( + "vnsLDevCtx.json?rsp-prop-include=config-only" + "&rsp-subtree=full" + "&rsp-subtree-class=vnsLIfCtx,vnsRsLIfCtxToLIf" + "&rsp-subtree-include=required" + ) + for entry in icurl("class", ldev_ctx_query) or []: + try: + ldev_ctx_mo = entry["vnsLDevCtx"] + ldev_ctx_attrs = ldev_ctx_mo["attributes"] + contract = ldev_ctx_attrs["ctrctNameOrLbl"].strip() + graph = ldev_ctx_attrs["graphNameOrLbl"].strip() + ctx_dn = ldev_ctx_attrs["dn"].strip() + except (KeyError, TypeError, AttributeError): + continue + + ctx_tenant_match = re.search(tn_regex, ctx_dn) + ctx_tenant = ctx_tenant_match.group(1) if ctx_tenant_match else "" + + # Filter to contexts matching an active graph (fall back to all if no graphs found) + if graph_keys: + contract_parts = [part for part in contract.split("-") if part] + contract_without_prefix = "-".join(contract_parts[1:]) if len(contract_parts) > 1 else contract + graph_base_name = graph[:-9] if graph.endswith("-imported") else graph + if not any( + (contract_name, graph_name) in graph_keys + for contract_name in (contract, contract_without_prefix) + for graph_name in (graph, graph_base_name) + ): + continue + elif not contract or not graph: + continue # Fallback mode: skip incomplete contexts + + # DFS through vnsLIfCtx children to collect vnsRsLIfCtxToLIf references + stack = [ldev_ctx_mo] + while stack: + current_ctx = stack.pop() + for child in current_ctx.get("children", []) or []: + if child.get("vnsLIfCtx"): + stack.append(child["vnsLIfCtx"]) + lif_relation = child.get("vnsRsLIfCtxToLIf") + if not lif_relation: + continue + try: + target_class = lif_relation["attributes"].get("tCl", "") + target_dn = lif_relation["attributes"]["tDn"].strip() + except (KeyError, TypeError, AttributeError): + continue + if not target_dn: + continue + + if target_class == "vnsLIf" or re.search(lif_dn_regex, target_dn): + lif_dns.add(target_dn) + lif_dn_match = re.search(lif_dn_regex, target_dn) + if lif_dn_match and ctx_tenant: + dev_dn = "uni/tn-{}/lDevVip-{}".format(lif_dn_match.group("tenant"), lif_dn_match.group("device")) + lif_source_tenants.setdefault(target_dn, set()).add(ctx_tenant) + dev_source_tenants.setdefault(dev_dn, set()).add(ctx_tenant) + elif target_class == "vnsLDevIfLIf" or ("/lDevIf-[" in target_dn and "/lDevIfLIf-" in target_dn): + ldeviflif_match = re.search(ldeviflif_regex, target_dn) + if ldeviflif_match: + converted = "{}/lIf-{}".format(ldeviflif_match.group("base"), ldeviflif_match.group("lif")) + lif_dns.add(converted) + if ctx_tenant: + lif_source_tenants.setdefault(converted, set()).add(ctx_tenant) + dev_source_tenants.setdefault(ldeviflif_match.group("base"), set()).add(ctx_tenant) + + # Expand to all LIFs under the same device clusters + dev_prefixes = set() + for lif_dn in lif_dns: + lif_dn_match = re.search(lif_dn_regex, lif_dn) + if lif_dn_match: + dev_prefixes.add("uni/tn-{}/lDevVip-{}".format(lif_dn_match.group("tenant"), lif_dn_match.group("device"))) + + if not lif_dns: + return Result(result=PASS, msg="No deployed service graph interfaces found.", doc_url=doc_url) + + # Fetch all LIFs with relation children once and reuse this collection in Step 2/3. + vns_lif_with_rel_query = ( + "vnsLIf.json?rsp-prop-include=config-only" + "&rsp-subtree=children" + "&rsp-subtree-class=vnsRsCIfAtt,vnsRsCIfAttN" + ) + vnsLIfs = icurl("class", vns_lif_with_rel_query) or [] + + vnsRsCIfAtts = [] + vnsRsCIfAttNs = [] + lifs_with_new_relation = set() + + for entry in vnsLIfs: + try: + lif_mo = entry["vnsLIf"] + lif_attrs = lif_mo["attributes"] + lif_dn = lif_attrs["dn"].strip() + except (KeyError, TypeError, AttributeError): + continue + + for child in lif_mo.get("children", []) or []: + if isinstance(child, dict) and child.get("vnsRsCIfAtt"): + vnsRsCIfAtts.append(child) + if isinstance(child, dict) and child.get("vnsRsCIfAttN"): + vnsRsCIfAttNs.append(child) + lifs_with_new_relation.add(lif_dn) + + if not dev_prefixes: + continue + + lif_dn_match = re.search(lif_dn_regex, lif_dn) + if not lif_dn_match: + continue + dev_dn = "uni/tn-{}/lDevVip-{}".format(lif_dn_match.group("tenant"), lif_dn_match.group("device")) + if dev_dn in dev_prefixes: + lif_dns.add(lif_dn) + if dev_source_tenants.get(dev_dn): + lif_source_tenants.setdefault(lif_dn, set()).update(dev_source_tenants[dev_dn]) + + # ── Step 2: Per-LIF subtree check for missing vnsRsCIfAttN ────────────── + + sg_data = [] + has_implicit_objects = False + for lif_dn in sorted(lif_dns): + if lif_dn in lifs_with_new_relation: + continue + lif_dn_match = re.search(lif_dn_regex, lif_dn) + lif_name = lif_dn_match.group("lif") if lif_dn_match else "" + lif_parts = [part for part in lif_name.split("-") if part] + missing_cif = lif_parts[1] if len(lif_parts) > 1 else lif_name + sg_data.append([ + lif_dn_match.group("tenant") if lif_dn_match else "", + lif_dn_match.group("device") if lif_dn_match else "", + lif_name, + missing_cif, + lif_dn, + ]) + if post_cifatt_delete and lif_dn_match and lif_dn_match.group("tenant") == "common": + if any(t and t != "common" for t in lif_source_tenants.get(lif_dn, set())): + has_implicit_objects = True + + # ── Step 3: Return results ─────────────────────────────────────────────── + + if post_cifatt_delete: + if sg_data: + sg_data.sort(key=lambda r: r[-1]) + msg = "Graph is rendered with implicit objects" if has_implicit_objects else "vnsRsCIfAttN is missing under deployed L4-L7 cluster interfaces." + return Result(result=FAIL_O, msg=msg, headers=headers, data=[[r[0], r[1], r[2]] for r in sg_data], recommended_action=recommended_action, doc_url=doc_url) + return Result(result=PASS, msg="All deployed service graph interfaces have vnsRsCIfAttN.", doc_url=doc_url) + + # Pre-upgrade: reuse relation objects collected from vnsLIf subtrees. + + if sg_data: + sg_data.sort(key=lambda r: r[-1]) + msg = "vnsLIf has neither vnsRsCIfAtt nor vnsRsCIfAttN. Missing concrete interface mapping can cause service graph inconsistency." if not vnsRsCIfAtts and not vnsRsCIfAttNs else "" + return Result(result=FAIL_O, msg=msg, headers=headers, data=sg_data, recommended_action=recommended_action, doc_url=doc_url) + + if not vnsRsCIfAtts and not vnsRsCIfAttNs: + return Result(result=FAIL_O, msg="Both vnsRsCIfAtt and vnsRsCIfAttN are missing. Reattach concrete interface mappings before upgrade.", headers=headers, data=[], recommended_action=recommended_action, doc_url=doc_url) + + if not vnsRsCIfAtts: + return Result(result=PASS, msg="No user-configured vnsRsCIfAtt payload found.", doc_url=doc_url) + + # Build new-object lookup sets in a single pass over vnsRsCIfAttNs + new_lif_dns = set() # LIF DNs covered by vnsRsCIfAttN (for coverage check) + new_dn_keys = set() # New DNs rewritten as old-style keys (for consistency check) + for relation_mo in vnsRsCIfAttNs: + try: + relation_dn = relation_mo["vnsRsCIfAttN"]["attributes"]["dn"].strip() + except (KeyError, TypeError, AttributeError): + continue + if not relation_dn: + continue + lif_parent_match = re.search(r"^(uni/tn-[^/]+/lDevVip-[^/]+/lIf-[^/]+)/rscIfAttN-\[", relation_dn) + if lif_parent_match: + new_lif_dns.add(lif_parent_match.group(1)) + new_dn_keys.add(relation_dn.replace("/rscIfAttN-[", "/rscIfAtt-[", 1)) + + # Secondary coverage check: any deployed LIF not yet covered by a vnsRsCIfAttN + data = [] + for lif_dn in sorted(lif_dns): + if lif_dn in new_lif_dns: + continue + lif_dn_match = re.search(lif_dn_regex, lif_dn) + lif_name = lif_dn_match.group("lif") if lif_dn_match else "" + lif_parts = [part for part in lif_name.split("-") if part] + missing_cif = lif_parts[1] if len(lif_parts) > 1 else lif_name + data.append([ + lif_dn_match.group("tenant") if lif_dn_match else "", + lif_dn_match.group("device") if lif_dn_match else "", + lif_name, + missing_cif, + lif_dn, + ]) + if data: + return Result(result=FAIL_O, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) + + # Consistency check: each old vnsRsCIfAtt must have a matching new vnsRsCIfAttN + data = [] + for old_relation_mo in vnsRsCIfAtts: + try: + old_dn = old_relation_mo["vnsRsCIfAtt"]["attributes"]["dn"].strip() + except (KeyError, TypeError, AttributeError): + continue + if not old_dn: + continue + old_lif_match = re.search(r"^(uni/tn-[^/]+/lDevVip-[^/]+/lIf-[^/]+)/", old_dn) + old_lif = old_lif_match.group(1) if old_lif_match else "" + if lif_dns and old_lif and old_lif not in lif_dns: + continue + if old_dn.replace("/rscIfAttN-[", "/rscIfAtt-[", 1) in new_dn_keys: + continue + old_relation_match = re.search( + r"uni/tn-(?P[^/]+)/lDevVip-(?P[^/]+)/lIf-(?P[^/]+)/" + r"rscIfAtt-\[.*?/cIf-\[(?P[^\]]+)\]\]", + old_dn, + ) + data.append([ + old_relation_match.group("tenant") if old_relation_match else "", + old_relation_match.group("device") if old_relation_match else "", + old_relation_match.group("lif") if old_relation_match else "", + old_relation_match.group("cif") if old_relation_match else "", + old_dn, + ]) + + data.sort(key=lambda r: r[-1]) + if data: + result = FAIL_O + + return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) + + # ---- Script Execution ---- @@ -6794,7 +7064,7 @@ class CheckManager: fabric_link_redundancy_check, apic_downgrade_compat_warning_check, svccore_excessive_data_check, - + # Faults apic_disk_space_faults_check, switch_bootflash_usage_check, @@ -6877,7 +7147,7 @@ class CheckManager: wred_affected_model_check, n9k_c93180yc_fx3_switch_memory_check, stale_dbgacEpgSummaryTask_check, - + vnsrscifatt_deprecation_check, ] ssh_checks = [ # General diff --git a/docs/docs/validations.md b/docs/docs/validations.md index f788681..5e0c40a 100644 --- a/docs/docs/validations.md +++ b/docs/docs/validations.md @@ -40,6 +40,7 @@ Items | This Script [Supported Hardware Compatibility][g20] | :white_check_mark: | :no_entry_sign: [Svccore Excessive Data Check][g21] | :white_check_mark: | :no_entry_sign: + [g1]: #compatibility-target-aci-version [g2]: #compatibility-cimc-version [g3]: #compatibility-switch-hardware @@ -206,6 +207,8 @@ Items | Defect | This Script [WRED with Affected FM Models][d35] | CSCwt50713 | :white_check_mark: | :no_entry_sign: [N9K-C93180YC-FX3 Switch Memory Less Than 32GB][d36] | CSCwm42741 | :white_check_mark: | :no_entry_sign: [Stale dbgacEpgSummaryTask Objects][d37] | CSCwt69100 | :white_check_mark: | :no_entry_sign: +[Vnsrscifatt Deprecation Check][d38] | CSCwr51759 | :white_check_mark: | :no_entry_sign: + [d1]: #ep-announce-compatibility [d2]: #eventmgr-db-size-defect-susceptibility @@ -244,6 +247,7 @@ Items | Defect | This Script [d35]: #wred-with-affected-fm-models [d36]: #n9k-c93180yc-fx3-switch-memory-less-than-32gb [d37]: #stale-dbgacepgsummarytask-objects +[d38]: #vnsrscifatt-deprecation-check ## General Check Details @@ -2782,7 +2786,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. - ### 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. @@ -2845,6 +2848,17 @@ Affected versions: 6.1(5e) and below, or 6.2(1g). Contact Cisco TAC for next steps. For more details, refer to the workaround in [CSCwt69100][75]. +### Vnsrscifatt Deprecation Check + +Due to [CSCwr51759][77], After upgrading ACI to 6.0(3d) or later release, one or more L4-L7 service graph device cluster interfaces are missing their concrete interface attachment, causing the service graph to fail to render and resulting in a traffic outage for PBR/L4-L7 redirected traffic. + +This occurs when a deployed service graph's cluster interface (vnsLIf) concrete interface mapping is defined using the deprecated relation object vnsRsCIfAtt, and the object was never migrated to its replacement, vnsRsCIfAttN, prior to upgrading to 6.0(3d) or later. +Because vnsRsCIfAtt is deleted during the upgrade to 6.0(3d)+, any cluster interface still relying solely on it loses its concrete interface mapping, and no equivalent vnsRsCIfAttN object exists to take its place. + +Before upgrading (current version older than 6.0(3d)): Reattach the concrete interface via the APIC GUI without deleting the existing attachment object — Tenant → Services → L4-L7 → Devices → Cluster Interface → Concrete Interface → + → select the interface → Submit. This creates the new vnsRsCIfAttN object alongside the old one so the mapping survives the upgrade. +After upgrading (current version 6.0(3d)) or later, verify all concrete device interface attachments to ensure there are none missing, Reattach the concrete interface using the same UI path to recreate the missing vnsRsCIfAttN object. + +Starting with ACI 6.0(3d), the object model for L4-L7 service graph concrete interface attachment changed: the legacy relation object vnsRsCIfAtt (under vnsLIf) was deprecated in favor of vnsRsCIfAttN. vnsRsCIfAtt objects are removed by the switchover/upgrade to 6.0(3d) or later, but this removal is not paired with an automatic creation of the equivalent vnsRsCIfAttN object for cluster interfaces that had never been re-attached under the new object. [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 @@ -2923,4 +2937,5 @@ Contact Cisco TAC for next steps. For more details, refer to the workaround in [ [74]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwm42741 [75]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt69100 [76]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt38698 +[77]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwr51759 diff --git a/tests/checks/vnsrscifatt_deprecation_check/test_vnsrscifatt_deprecation_check.py b/tests/checks/vnsrscifatt_deprecation_check/test_vnsrscifatt_deprecation_check.py new file mode 100644 index 0000000..d2cec3b --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/test_vnsrscifatt_deprecation_check.py @@ -0,0 +1,341 @@ +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 = "vnsrscifatt_deprecation_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" +vnsLIf_with_rel_api = ( + "vnsLIf.json?rsp-prop-include=config-only" + "&rsp-subtree=children" + "&rsp-subtree-class=vnsRsCIfAtt,vnsRsCIfAttN" +) +vnsGraphInst_api = 'vnsGraphInst.json?rsp-prop-include=config-only' +vnsLDevCtx_all_api = ( + 'vnsLDevCtx.json?' + 'rsp-prop-include=config-only' + '&rsp-subtree=full' + '&rsp-subtree-class=vnsLIfCtx,vnsRsLIfCtxToLIf' + '&rsp-subtree-include=required' +) +vnsLIf_intf_prov_subtree_api = ( + 'uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov.json?query-target=subtree' + '&target-subtree-class=vnsRsCIfAttN' + '&rsp-prop-include=config-only' +) +vnsLIf_common_cons_subtree_api = ( + 'uni/tn-common/lDevVip-common-device/lIf-common-cons.json?query-target=subtree' + '&target-subtree-class=vnsRsCIfAttN' + '&rsp-prop-include=config-only' +) +vnsLIf_common_prov_subtree_api = ( + 'uni/tn-common/lDevVip-common-device/lIf-common-prov.json?query-target=subtree' + '&target-subtree-class=vnsRsCIfAttN' + '&rsp-prop-include=config-only' +) + +@pytest.mark.parametrize( + "icurl_outputs, tversion, expected_result, expected_data", + [ + # Target version missing + ( + { + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_empty.json"), + }, + None, + script.MANUAL, + [], + ), + # Target version is not affected (< 6.0(3d)) + ( + { + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_empty.json"), + }, + "6.0(2h)", + script.NA, + [], + ), + # 6.0(3d) is affected; if service graph is unconfigured, return PASS + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsLIf_api: [], + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_empty.json"), + }, + "6.0(3d)", + script.PASS, + [], + ), + # Both vnsRsCIfAtt and vnsRsCIfAttN are missing but service graph is unconfigured -> PASS + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsLIf_api: [], + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_empty.json"), + }, + "6.1(5e)", + script.PASS, + [], + ), + # Both vnsRsCIfAtt and vnsRsCIfAttN are missing while vnsLIf exists, but service graph is unconfigured -> PASS + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsLIf_api: read_data(dir, "vnsLIf_only.json"), + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_only.json"), + }, + "6.1(5e)", + script.PASS, + [], + ), + # Legacy behavior: when vnsRsCIfAtt is absent but vnsRsCIfAttN exists, return PASS + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAttN_match.json"), + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_new_match.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"), + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_old_new_match.json"), + }, + "6.1(5e)", + script.PASS, + [], + ), + # One vnsRsCIfAtt relation (cons) missing in vnsRsCIfAttN, but service graph unconfigured -> PASS + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_match.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAttN_missing_cons.json"), + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_old_new_missing_cons.json"), + }, + "6.1(5e)", + script.PASS, + [], + ), + # vnsRsCIfAttN is empty and old relations exist, but service graph unconfigured -> PASS + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_match.json"), + vnsRsCIfAttN_api: [], + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_old_only.json"), + }, + "6.1(5e)", + script.PASS, + [], + ), + # vnsLIf target from vnsLIfCtx relation is covered when global vnsRsCIfAttN exists + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAttN_match.json"), + vnsLIf_api: [], + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLIf_missing_rscifattn.json"), + vnsLIf_intf_prov_subtree_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_new_match.json"), + }, + "6.1(5e)", + script.PASS, + [], + ), + # vnsLDevIfLIf target must be converted to vnsLIf DN before subtree check + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAttN_match.json"), + vnsLIf_api: [], + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_rscifattn.json"), + vnsLIf_common_cons_subtree_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_new_match.json"), + }, + "6.1(5e)", + script.FAIL_O, + [ + [ + "common", + "common-device", + "common-cons", + "cons", + "uni/tn-common/lDevVip-common-device/lIf-common-cons", + ] + ], + ), + # Parent-only subtree no longer affects result when global vnsRsCIfAttN exists + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAttN_match.json"), + vnsLIf_api: [], + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLIf_missing_rscifattn.json"), + vnsLIf_intf_prov_subtree_api: read_data(dir, "vnsLIf_subtree_parent_only.json"), + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_new_match.json"), + }, + "6.1(5e)", + script.PASS, + [], + ), + # vnsRsLIfCtxToLIf may omit tCl; infer LDevIfLIf from tDn pattern and still fail when missing + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAttN_match.json"), + vnsLIf_api: [], + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_tcl.json"), + vnsLIf_common_cons_subtree_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_new_match.json"), + }, + "6.1(5e)", + script.FAIL_O, + [ + [ + "common", + "common-device", + "common-cons", + "cons", + "uni/tn-common/lDevVip-common-device/lIf-common-cons", + ] + ], + ), + # If only one common interface is referenced in vnsLDevCtx, expand to all LIfs under the same device + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAttN_match.json"), + vnsLIf_api: read_data(dir, "vnsLIf_common_device_both.json"), + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_tcl.json"), + vnsLIf_common_cons_subtree_api: [], + vnsLIf_common_prov_subtree_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_common_empty.json"), + }, + "6.1(5e)", + script.FAIL_O, + [ + [ + "common", + "common-device", + "common-cons", + "cons", + "uni/tn-common/lDevVip-common-device/lIf-common-cons", + ], + [ + "common", + "common-device", + "common-prov", + "prov", + "uni/tn-common/lDevVip-common-device/lIf-common-prov", + ], + ], + ), + # Imported graph label should still map to applied graph and detect missing common interfaces + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAttN_match.json"), + vnsLIf_api: read_data(dir, "vnsLIf_common_device_both.json"), + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_tcl_imported.json"), + vnsLIf_common_cons_subtree_api: [], + vnsLIf_common_prov_subtree_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_common_empty.json"), + }, + "6.1(5e)", + script.FAIL_O, + [ + [ + "common", + "common-device", + "common-cons", + "cons", + "uni/tn-common/lDevVip-common-device/lIf-common-cons", + ], + [ + "common", + "common-device", + "common-prov", + "prov", + "uni/tn-common/lDevVip-common-device/lIf-common-prov", + ], + ], + ), + # If both vnsRsCIfAtt and vnsRsCIfAttN are globally empty, result should be FAIL + ( + { + vnsRsCIfAtt_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsRsCIfAttN_api: read_data(dir, "vnsRsCIfAtt_empty.json"), + vnsLIf_api: read_data(dir, "vnsLIf_common_device_both.json"), + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_tcl.json"), + vnsLIf_common_cons_subtree_api: [], + vnsLIf_common_prov_subtree_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_common_empty.json"), + }, + "6.1(5e)", + script.FAIL_O, + [ + [ + "common", + "common-device", + "common-cons", + "cons", + "uni/tn-common/lDevVip-common-device/lIf-common-cons", + ], + [ + "common", + "common-device", + "common-prov", + "prov", + "uni/tn-common/lDevVip-common-device/lIf-common-prov", + ], + ], + ), + ], +) +def test_logic(run_check, mock_icurl, icurl_outputs, tversion, expected_result, expected_data): + result = run_check( + cversion=None, + tversion=script.AciVersion(tversion) if tversion else None, + ) + assert result.result == expected_result + assert result.data == expected_data diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsGraphInst_applied_single.json b/tests/checks/vnsrscifatt_deprecation_check/vnsGraphInst_applied_single.json new file mode 100644 index 0000000..80fe130 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsGraphInst_applied_single.json @@ -0,0 +1,11 @@ +[ + { + "vnsGraphInst": { + "attributes": { + "configSt": "applied", + "ctrctDn": "uni/tn-CSCwj49418/brc-epg-epg", + "graphDn": "uni/tn-CSCwj49418/AbsGraph-test" + } + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLDevIfLIf_missing_rscifattn.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLDevIfLIf_missing_rscifattn.json new file mode 100644 index 0000000..691fd7e --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLDevIfLIf_missing_rscifattn.json @@ -0,0 +1,30 @@ +[ + { + "vnsLDevCtx": { + "attributes": { + "dn": "uni/tn-CSCwj49418/ldevCtx-c-epg-epg-g-test-n-test-node", + "ctrctNameOrLbl": "epg-epg", + "graphNameOrLbl": "test" + }, + "children": [ + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-CSCwj49418/ldevCtx-c-epg-epg-g-test-n-test-node/lifCtx-2" + }, + "children": [ + { + "vnsRsLIfCtxToLIf": { + "attributes": { + "tCl": "vnsLDevIfLIf", + "tDn": "uni/tn-user/lDevIf-[uni/tn-common/lDevVip-common-device]/lDevIfLIf-common-cons" + } + } + } + ] + } + } + ] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLDevIfLIf_missing_tcl.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLDevIfLIf_missing_tcl.json new file mode 100644 index 0000000..9145c08 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLDevIfLIf_missing_tcl.json @@ -0,0 +1,29 @@ +[ + { + "vnsLDevCtx": { + "attributes": { + "dn": "uni/tn-user/ldevCtx-c-common-epg-epg-g-test-n-N1", + "ctrctNameOrLbl": "common-epg-epg", + "graphNameOrLbl": "test" + }, + "children": [ + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-user/ldevCtx-c-common-epg-epg-g-test-n-N1/lifCtx-cons" + }, + "children": [ + { + "vnsRsLIfCtxToLIf": { + "attributes": { + "tDn": "uni/tn-user/lDevIf-[uni/tn-common/lDevVip-common-device]/lDevIfLIf-common-cons" + } + } + } + ] + } + } + ] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLDevIfLIf_missing_tcl_imported.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLDevIfLIf_missing_tcl_imported.json new file mode 100644 index 0000000..bdb5b3f --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLDevIfLIf_missing_tcl_imported.json @@ -0,0 +1,45 @@ +[ + { + "vnsLDevCtx": { + "attributes": { + "dn": "uni/tn-user/ldevCtx-c-common-epg-epg-g-test-imported-n-N1", + "ctrctNameOrLbl": "common-epg-epg", + "graphNameOrLbl": "test-imported" + }, + "children": [ + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-user/ldevCtx-c-common-epg-epg-g-test-imported-n-N1/lifCtx-cons" + }, + "children": [ + { + "vnsRsLIfCtxToLIf": { + "attributes": { + "tDn": "uni/tn-user/lDevIf-[uni/tn-common/lDevVip-common-device]/lDevIfLIf-common-cons" + } + } + } + ] + } + }, + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-user/ldevCtx-c-common-epg-epg-g-test-imported-n-N1/lifCtx-prov" + }, + "children": [ + { + "vnsRsLIfCtxToLIf": { + "attributes": { + "tDn": "uni/tn-user/lDevIf-[uni/tn-common/lDevVip-common-device]/lDevIfLIf-common-cons" + } + } + } + ] + } + } + ] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLIf_missing_rscifattn.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLIf_missing_rscifattn.json new file mode 100644 index 0000000..96183d3 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLIf_missing_rscifattn.json @@ -0,0 +1,30 @@ +[ + { + "vnsLDevCtx": { + "attributes": { + "dn": "uni/tn-CSCwj49418/ldevCtx-c-epg-epg-g-test-n-test-node", + "ctrctNameOrLbl": "epg-epg", + "graphNameOrLbl": "test" + }, + "children": [ + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-CSCwj49418/ldevCtx-c-epg-epg-g-test-n-test-node/lifCtx-1" + }, + "children": [ + { + "vnsRsLIfCtxToLIf": { + "attributes": { + "tCl": "vnsLIf", + "tDn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov" + } + } + } + ] + } + } + ] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_common_device_both.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_common_device_both.json new file mode 100644 index 0000000..8944e08 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_common_device_both.json @@ -0,0 +1,16 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-common/lDevVip-common-device/lIf-common-cons" + } + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-common/lDevVip-common-device/lIf-common-prov" + } + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_only.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_only.json new file mode 100644 index 0000000..0eb80a8 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_only.json @@ -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" + } + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_subtree_parent_only.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_subtree_parent_only.json new file mode 100644 index 0000000..65b7531 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_subtree_parent_only.json @@ -0,0 +1,9 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov" + } + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_common_empty.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_common_empty.json new file mode 100644 index 0000000..aeb8306 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_common_empty.json @@ -0,0 +1,18 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-common/lDevVip-common-device/lIf-common-cons" + }, + "children": [] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-common/lDevVip-common-device/lIf-common-prov" + }, + "children": [] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_empty.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_empty.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_empty.json @@ -0,0 +1 @@ +[] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_new_match.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_new_match.json new file mode 100644 index 0000000..8b0e460 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_new_match.json @@ -0,0 +1,38 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [ + { + "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]" + } + } + } + ] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [ + { + "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]" + } + } + } + ] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_new_match.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_new_match.json new file mode 100644 index 0000000..53f72e9 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_new_match.json @@ -0,0 +1,54 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [ + { + "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]" + } + } + }, + { + "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]" + } + } + } + ] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [ + { + "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]" + } + } + }, + { + "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]" + } + } + } + ] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_new_missing_cons.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_new_missing_cons.json new file mode 100644 index 0000000..e7ab27a --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_new_missing_cons.json @@ -0,0 +1,46 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [ + { + "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]" + } + } + } + ] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [ + { + "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]" + } + } + }, + { + "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]" + } + } + } + ] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_only.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_only.json new file mode 100644 index 0000000..54c3fb7 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_only.json @@ -0,0 +1,38 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [ + { + "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]" + } + } + } + ] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [ + { + "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]" + } + } + } + ] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_only.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_only.json new file mode 100644 index 0000000..3a18d2e --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_only.json @@ -0,0 +1,20 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-CSCwj49418/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAttN_match.json b/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAttN_match.json new file mode 100644 index 0000000..2344730 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAttN_match.json @@ -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]" + } + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAttN_missing_cons.json b/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAttN_missing_cons.json new file mode 100644 index 0000000..25de6eb --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAttN_missing_cons.json @@ -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]" + } + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAtt_empty.json b/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAtt_empty.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAtt_empty.json @@ -0,0 +1 @@ +[] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAtt_match.json b/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAtt_match.json new file mode 100644 index 0000000..a9b0696 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsRsCIfAtt_match.json @@ -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]" + } + } + } +]