diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index af8ddb3..125a3a4 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -51,6 +51,7 @@ NA = 'N/A' # message constants TVER_MISSING = "Target version not supplied. Skipping." +CVER_MISSING = "Current version not supplied. Skipping." VER_NOT_AFFECTED = "Version not affected." # regex constants node_regex = r'topology/pod-(?P\d+)/node-(?P\d+)' @@ -6701,10 +6702,10 @@ def stale_dbgacEpgSummaryTask_check(tversion, **kwargs): return Result(result=NA, msg=VER_NOT_AFFECTED, doc_url=doc_url) try: - from datetime import timezone - threshold = datetime.now(timezone.utc).replace(tzinfo=None) - timedelta(hours=24) + from datetime import timezone + threshold = datetime.now(timezone.utc).replace(tzinfo=None) - timedelta(hours=24) except ImportError: - threshold = datetime.utcnow() - timedelta(hours=24) + threshold = datetime.utcnow() - timedelta(hours=24) for obj in icurl("class", 'dbgacEpgSummaryTask.json?query-target-filter=eq(dbgacEpgSummaryTask.operSt,"processing")'): attr = obj["dbgacEpgSummaryTask"]["attributes"] @@ -6797,6 +6798,277 @@ def infravlan_overlap_access_policy_check(tversion, **kwargs): return Result(result=result, msg=msg, headers=headers, data=data, unformatted_headers=unformatted_headers, unformatted_data=unformatted_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-([^/]+)/" + + def device_dn_from_lif_match(lif_dn_match): + return "uni/tn-{}/lDevVip-{}".format(lif_dn_match.group("tenant"), lif_dn_match.group("device")) + + def cif_name_from_lif_name(lif_name): + # LIF names may carry a numeric suffix for multi-connector graphs (e.g. "intf-cons-1"), + # so the cons/prov role must be matched explicitly rather than taking the last "-" token. + role_match = re.search(r"-(cons|prov)(?:-\d+)?$", lif_name) + if role_match: + return role_match.group(1) + return lif_name.rsplit("-", 1)[-1] + + def build_row(lif_dn, lif_dn_match): + lif_name = lif_dn_match.group("lif") if lif_dn_match else "" + return [ + lif_dn_match.group("tenant") if lif_dn_match else "", + lif_dn_match.group("device") if lif_dn_match else "", + lif_name, + cif_name_from_lif_name(lif_name), + lif_dn, + ] + + def safe_extract_attrs(fn): + try: + return fn() + except (KeyError, TypeError, AttributeError): + return None + + 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) + if not cversion: + return Result(result=MANUAL, msg=CVER_MISSING, doc_url=doc_url) + + post_cifatt_delete = 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 []: + attrs = safe_extract_attrs(lambda: ( + entry["vnsGraphInst"]["attributes"]["ctrctDn"].strip(), + entry["vnsGraphInst"]["attributes"]["graphDn"].strip(), + )) + if attrs is None: + continue + contract_dn, graph_dn = attrs + 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 []: + parsed = safe_extract_attrs(lambda: ( + entry["vnsLDevCtx"], + entry["vnsLDevCtx"]["attributes"]["ctrctNameOrLbl"].strip(), + entry["vnsLDevCtx"]["attributes"]["graphNameOrLbl"].strip(), + entry["vnsLDevCtx"]["attributes"]["dn"].strip(), + )) + if parsed is None: + continue + ldev_ctx_mo, contract, graph, ctx_dn = parsed + + 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: + c_norm = contract.split("-", 1)[-1] if "-" in contract else contract + g_norm = graph[:-9] if graph.endswith("-imported") else graph + if not any((c, g) in graph_keys for c in (contract, c_norm) for g in (graph, g_norm)): + 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 + parsed = safe_extract_attrs(lambda: (lif_relation["attributes"].get("tCl", ""), lif_relation["attributes"]["tDn"].strip())) + if parsed is None: + continue + target_class, target_dn = parsed + 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 = device_dn_from_lif_match(lif_dn_match) + 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(device_dn_from_lif_match(lif_dn_match)) + + # One consolidated collection for vnsLIf + vnsRsCIfAtt + vnsRsCIfAttN. + vns_lif_with_rel_query = ( + "vnsLIf.json?rsp-prop-include=config-only" + "&rsp-subtree=children" + "&rsp-subtree-class=vnsRsCIfAtt,vnsRsCIfAttN" + ) + vns_lif_mos = icurl("class", vns_lif_with_rel_query) or [] + + vnsRsCIfAtts = [] + vnsRsCIfAttNs = [] + lifs_with_new_relation = set() + + for entry in vns_lif_mos: + parsed = safe_extract_attrs(lambda: (entry["vnsLIf"], entry["vnsLIf"]["attributes"]["dn"].strip())) + if parsed is None: + continue + lif_mo, lif_dn = parsed + + lif_dn_match = re.search(lif_dn_regex, lif_dn) + if lif_dn_match: + dev_dn = device_dn_from_lif_match(lif_dn_match) + 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]) + + for child in lif_mo.get("children", []) or []: + if not isinstance(child, dict): + continue + if child.get("vnsRsCIfAtt"): + vnsRsCIfAtts.append(child) + if child.get("vnsRsCIfAttN"): + vnsRsCIfAttNs.append(child) + lifs_with_new_relation.add(lif_dn) + + if not lif_dns: + return Result(result=PASS, msg="No deployed service graph interfaces found.", doc_url=doc_url) + + # ── Step 2: Missing vnsRsCIfAttN under deployed LIFs (from consolidated data) ── + + missing_rscifattn_rows = [] + 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) + missing_rscifattn_rows.append(build_row(lif_dn, lif_dn_match)) + 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 missing_rscifattn_rows: + missing_rscifattn_rows.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 missing_rscifattn_rows], recommended_action=recommended_action, doc_url=doc_url) + return Result(result=PASS, msg="All deployed service graph interfaces have vnsRsCIfAttN.", doc_url=doc_url) + + # version not having new object path reuses relation objects collected above. + + if missing_rscifattn_rows: + missing_rscifattn_rows.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=missing_rscifattn_rows, 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(lifs_with_new_relation) # 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: + relation_dn = safe_extract_attrs(lambda: relation_mo["vnsRsCIfAttN"]["attributes"]["dn"].strip()) + if relation_dn is None: + 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) + data.append(build_row(lif_dn, lif_dn_match)) + 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: + old_dn = safe_extract_attrs(lambda: old_relation_mo["vnsRsCIfAtt"]["attributes"]["dn"].strip()) + if old_dn is None: + 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 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 ---- @@ -6889,7 +7161,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, @@ -6973,7 +7245,7 @@ class CheckManager: n9k_c93180yc_fx3_switch_memory_check, stale_dbgacEpgSummaryTask_check, infravlan_overlap_access_policy_check, - + vnsRsCIfAtt_deprecation_check, ] ssh_checks = [ # General diff --git a/docs/docs/validations.md b/docs/docs/validations.md index 6695fc9..09159d1 100644 --- a/docs/docs/validations.md +++ b/docs/docs/validations.md @@ -207,6 +207,7 @@ Items | Defect | This Script [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: [InfraVLAN Overlap in Access Policy VLAN Pools][d38] | CSCwt58626 | :white_check_mark: | :no_entry_sign: +[vnsRsCIfAtt Deprecation Check][d39] | CSCwr51759 | :white_check_mark: | :no_entry_sign: [d1]: #ep-announce-compatibility [d2]: #eventmgr-db-size-defect-susceptibility @@ -246,6 +247,7 @@ Items | Defect | This Script [d36]: #n9k-c93180yc-fx3-switch-memory-less-than-32gb [d37]: #stale-dbgacepgsummarytask-objects [d38]: #infravlan-overlap-access-policy-check +[d39]: #vnsrscifatt-deprecation-check ## General Check Details @@ -2788,7 +2790,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. @@ -2851,13 +2852,24 @@ 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]. - ### Infravlan Overlap Access Policy Check Due to the bug [CSCwt58626][77] , If Apic upgrade planned for target versions 6.1(3f), 6.1(3g), 6.1(4h), 6.1(5e) and 6.2(1g), be aware of fault F4701 being raised if the InfraVLAN overlaps with any user-configured VLAN pool in Access Policies. This also affects vlan pool created by NDO/MSO, VMM, Kubernetes. After the upgrade, domains associated with those VLAN pools cannot be linked to new EPGs, although existing EPGs continue to function. To avoid this issue, modify the user VLAN pool ranges so that the InfraVLAN does not overlap with any configured block, or select a non-impacted fixed version. After upgrading to a fixed version this fault and Restriction have been removed. +### vnsRsCIfAtt Deprecation Check + +Due to [CSCwr51759][78], 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, then 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 [2]: https://www.cisco.com/c/en/us/support/switches/nexus-9000-series-switches/products-release-notes-list.html @@ -2935,3 +2947,5 @@ To avoid this issue, modify the user VLAN pool ranges so that the InfraVLAN does [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/CSCwt58626 +[78]: 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..1b4337a --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/test_vnsrscifatt_deprecation_check.py @@ -0,0 +1,417 @@ +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 (only the 3 queries vnsRsCIfAtt_deprecation_check actually issues) +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' +) + +@pytest.mark.parametrize( + "icurl_outputs, tversion, expected_result, expected_data, expected_msg", + [ + # Target version missing + ( + { + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_empty.json"), + }, + None, + script.MANUAL, + [], + script.TVER_MISSING, + ), + # 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, + [], + script.VER_NOT_AFFECTED, + ), + # 6.0(3d) is affected; if service graph is unconfigured, return PASS + ( + { + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_empty.json"), + }, + "6.0(3d)", + script.PASS, + [], + "No deployed service graph interfaces found.", + ), + # Both vnsRsCIfAtt and vnsRsCIfAttN are missing but service graph is unconfigured -> PASS + ( + { + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_empty.json"), + }, + "6.1(5e)", + script.PASS, + [], + "No deployed service graph interfaces found.", + ), + # Both vnsRsCIfAtt and vnsRsCIfAttN are missing while vnsLIf exists, but service graph is unconfigured -> PASS + ( + { + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_only.json"), + }, + "6.1(5e)", + script.PASS, + [], + "No deployed service graph interfaces found.", + ), + # Legacy behavior: when vnsRsCIfAtt is absent but vnsRsCIfAttN exists, return PASS + ( + { + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_new_match.json"), + }, + "6.1(5e)", + script.PASS, + [], + "No deployed service graph interfaces found.", + ), + # All vnsRsCIfAtt relations have matching vnsRsCIfAttN relations + ( + { + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_old_new_match.json"), + }, + "6.1(5e)", + script.PASS, + [], + "No deployed service graph interfaces found.", + ), + # One vnsRsCIfAtt relation (cons) missing in vnsRsCIfAttN, but service graph unconfigured -> PASS + ( + { + 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, + [], + "No deployed service graph interfaces found.", + ), + # vnsRsCIfAttN is empty and old relations exist, but service graph unconfigured -> PASS + ( + { + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_old_only.json"), + }, + "6.1(5e)", + script.PASS, + [], + "No deployed service graph interfaces found.", + ), + # vnsLIf target from vnsLIfCtx relation is covered when global vnsRsCIfAttN exists + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLIf_missing_rscifattn.json"), + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_new_match.json"), + }, + "6.1(5e)", + script.PASS, + [], + "No user-configured vnsRsCIfAtt payload found.", + ), + # vnsLDevIfLIf target must be converted to vnsLIf DN before subtree check + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_rscifattn.json"), + 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 + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLIf_missing_rscifattn.json"), + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_new_match.json"), + }, + "6.1(5e)", + script.PASS, + [], + "No user-configured vnsRsCIfAtt payload found.", + ), + # vnsRsLIfCtxToLIf may omit tCl; infer LDevIfLIf from tDn pattern and still fail when missing + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_tcl.json"), + 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 + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_tcl.json"), + 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", + ], + ], + "vnsLIf has neither vnsRsCIfAtt nor vnsRsCIfAttN. Missing concrete interface mapping can cause service graph inconsistency.", + ), + # Imported graph label should still map to applied graph and detect missing common interfaces + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_tcl_imported.json"), + 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", + ], + ], + "vnsLIf has neither vnsRsCIfAtt nor vnsRsCIfAttN. Missing concrete interface mapping can cause service graph inconsistency.", + ), + # If both vnsRsCIfAtt and vnsRsCIfAttN are globally empty, result should be FAIL + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_tcl.json"), + 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", + ], + ], + "vnsLIf has neither vnsRsCIfAtt nor vnsRsCIfAttN. Missing concrete interface mapping can cause service graph inconsistency.", + ), + # LIF names with a numeric multi-connector suffix (e.g. "-cons-1") must still resolve the + # missing concrete interface role to "cons"/"prov", not the trailing digit + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLIf_numbered_suffix.json"), + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_common_numbered.json"), + }, + "6.1(5e)", + script.FAIL_O, + [ + [ + "common", + "common-device", + "common-cons-1", + "cons", + "uni/tn-common/lDevVip-common-device/lIf-common-cons-1", + ], + [ + "common", + "common-device", + "common-prov-1", + "prov", + "uni/tn-common/lDevVip-common-device/lIf-common-prov-1", + ], + ], + "vnsLIf has neither vnsRsCIfAtt nor vnsRsCIfAttN. Missing concrete interface mapping can cause service graph inconsistency.", + ), + # Consistency check: a stale vnsRsCIfAtt pointing at a different concrete interface than the + # matching vnsRsCIfAttN must still be flagged, even though the LIF itself is otherwise covered + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLIf_cons_and_prov.json"), + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_stale_old_cif_mismatch.json"), + }, + "6.1(5e)", + script.FAIL_O, + [ + [ + "user-11", + "test", + "intf-cons", + "cons-old", + "uni/tn-user-11/lDevVip-test/lIf-intf-cons/rscIfAtt-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons-old]]", + ], + ], + "", + ), + ], +) +def test_logic(run_check, mock_icurl, icurl_outputs, tversion, expected_result, expected_data, expected_msg): + # cversion is fixed to a pre-6.0(3d) release here so every case above continues to exercise + # the legacy (post_cifatt_delete=False) path; the post-6.0(3d) path is covered separately below. + result = run_check( + cversion=script.AciVersion("5.2(8h)"), + tversion=script.AciVersion(tversion) if tversion else None, + ) + assert result.result == expected_result + assert result.data == expected_data + assert result.msg == expected_msg + + +@pytest.mark.parametrize( + "icurl_outputs, tversion, cversion, expected_result, expected_data, expected_msg", + [ + # Current version not supplied while target version is affected -> MANUAL + ( + { + vnsGraphInst_api: [], + vnsLDevCtx_all_api: [], + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_empty.json"), + }, + "6.1(5e)", + None, + script.MANUAL, + [], + script.CVER_MISSING, + ), + # Post-cifatt-delete (cversion >= 6.0(3d)): deployed LIF missing vnsRsCIfAttN, and the LIF's tenant + # ("common") differs from the referencing contract's tenant ("user") -> implicit-objects message + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLDevIfLIf_missing_rscifattn.json"), + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_new_match.json"), + }, + "6.1(5e)", + "6.0(3d)", + script.FAIL_O, + [ + ["common", "common-device", "common-cons"], + ], + "Graph is rendered with implicit objects", + ), + # Post-cifatt-delete (cversion >= 6.0(3d)): deployed LIFs missing vnsRsCIfAttN, but the tenant is + # not "common" so the implicit-objects check does not apply -> generic missing message + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLIf_missing_rscifattn.json"), + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_only.json"), + }, + "6.1(5e)", + "6.1(5e)", + script.FAIL_O, + [ + ["user-11", "test", "intf-cons"], + ["user-11", "test", "intf-prov"], + ], + "vnsRsCIfAttN is missing under deployed L4-L7 cluster interfaces.", + ), + # Post-cifatt-delete (cversion >= 6.0(3d)): deployed LIF already has vnsRsCIfAttN -> PASS + ( + { + vnsGraphInst_api: read_data(dir, "vnsGraphInst_applied_single.json"), + vnsLDevCtx_all_api: read_data(dir, "vnsLDevCtx_vnsLIf_missing_rscifattn.json"), + vnsLIf_with_rel_api: read_data(dir, "vnsLIf_with_rel_new_match.json"), + }, + "6.1(5e)", + "6.1(5e)", + script.PASS, + [], + "All deployed service graph interfaces have vnsRsCIfAttN.", + ), + ], +) +def test_cversion_and_post_delete_branch(run_check, mock_icurl, icurl_outputs, tversion, cversion, expected_result, expected_data, expected_msg): + result = run_check( + cversion=script.AciVersion(cversion) if cversion else None, + tversion=script.AciVersion(tversion) if tversion else None, + ) + assert result.result == expected_result + assert result.data == expected_data + assert result.msg == expected_msg 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..8cc603a --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsGraphInst_applied_single.json @@ -0,0 +1,11 @@ +[ + { + "vnsGraphInst": { + "attributes": { + "configSt": "applied", + "ctrctDn": "uni/tn-user-11/brc-epg-epg", + "graphDn": "uni/tn-user-11/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..692cc95 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLDevIfLIf_missing_rscifattn.json @@ -0,0 +1,30 @@ +[ + { + "vnsLDevCtx": { + "attributes": { + "dn": "uni/tn-user-11/ldevCtx-c-epg-epg-g-test-n-test-node", + "ctrctNameOrLbl": "epg-epg", + "graphNameOrLbl": "test" + }, + "children": [ + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-user-11/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_cons_and_prov.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLIf_cons_and_prov.json new file mode 100644 index 0000000..0657fc5 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLIf_cons_and_prov.json @@ -0,0 +1,47 @@ +[ + { + "vnsLDevCtx": { + "attributes": { + "dn": "uni/tn-user-11/ldevCtx-c-epg-epg-g-test-n-test-node", + "ctrctNameOrLbl": "epg-epg", + "graphNameOrLbl": "test" + }, + "children": [ + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-user-11/ldevCtx-c-epg-epg-g-test-n-test-node/lifCtx-cons" + }, + "children": [ + { + "vnsRsLIfCtxToLIf": { + "attributes": { + "tCl": "vnsLIf", + "tDn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons" + } + } + } + ] + } + }, + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-user-11/ldevCtx-c-epg-epg-g-test-n-test-node/lifCtx-prov" + }, + "children": [ + { + "vnsRsLIfCtxToLIf": { + "attributes": { + "tCl": "vnsLIf", + "tDn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov" + } + } + } + ] + } + } + ] + } + } +] 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..8f38d42 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLIf_missing_rscifattn.json @@ -0,0 +1,30 @@ +[ + { + "vnsLDevCtx": { + "attributes": { + "dn": "uni/tn-user-11/ldevCtx-c-epg-epg-g-test-n-test-node", + "ctrctNameOrLbl": "epg-epg", + "graphNameOrLbl": "test" + }, + "children": [ + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-user-11/ldevCtx-c-epg-epg-g-test-n-test-node/lifCtx-1" + }, + "children": [ + { + "vnsRsLIfCtxToLIf": { + "attributes": { + "tCl": "vnsLIf", + "tDn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov" + } + } + } + ] + } + } + ] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLIf_numbered_suffix.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLIf_numbered_suffix.json new file mode 100644 index 0000000..62e65a4 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLDevCtx_vnsLIf_numbered_suffix.json @@ -0,0 +1,47 @@ +[ + { + "vnsLDevCtx": { + "attributes": { + "dn": "uni/tn-common/ldevCtx-c-epg-epg-g-test-n-test-node", + "ctrctNameOrLbl": "epg-epg", + "graphNameOrLbl": "test" + }, + "children": [ + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-common/ldevCtx-c-epg-epg-g-test-n-test-node/lifCtx-cons" + }, + "children": [ + { + "vnsRsLIfCtxToLIf": { + "attributes": { + "tCl": "vnsLIf", + "tDn": "uni/tn-common/lDevVip-common-device/lIf-common-cons-1" + } + } + } + ] + } + }, + { + "vnsLIfCtx": { + "attributes": { + "dn": "uni/tn-common/ldevCtx-c-epg-epg-g-test-n-test-node/lifCtx-prov" + }, + "children": [ + { + "vnsRsLIfCtxToLIf": { + "attributes": { + "tCl": "vnsLIf", + "tDn": "uni/tn-common/lDevVip-common-device/lIf-common-prov-1" + } + } + } + ] + } + } + ] + } + } +] 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_common_numbered.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_common_numbered.json new file mode 100644 index 0000000..c11be35 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_common_numbered.json @@ -0,0 +1,18 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-common/lDevVip-common-device/lIf-common-cons-1" + }, + "children": [] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-common/lDevVip-common-device/lIf-common-prov-1" + }, + "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..f49b8bb --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_new_match.json @@ -0,0 +1,38 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [ + { + "vnsRsCIfAttN": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons/rscIfAttN-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]]", + "tDn": "uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]" + } + } + } + ] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [ + { + "vnsRsCIfAttN": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov/rscIfAttN-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[prov]]", + "tDn": "uni/tn-user-11/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..2d534c4 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_new_match.json @@ -0,0 +1,54 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [ + { + "vnsRsCIfAtt": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons/rscIfAtt-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]]", + "tDn": "uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]" + } + } + }, + { + "vnsRsCIfAttN": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons/rscIfAttN-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]]", + "tDn": "uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]" + } + } + } + ] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [ + { + "vnsRsCIfAtt": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov/rscIfAtt-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[prov]]", + "tDn": "uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[prov]" + } + } + }, + { + "vnsRsCIfAttN": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov/rscIfAttN-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[prov]]", + "tDn": "uni/tn-user-11/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..0dd601d --- /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-user-11/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [ + { + "vnsRsCIfAtt": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons/rscIfAtt-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]]", + "tDn": "uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]" + } + } + } + ] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [ + { + "vnsRsCIfAtt": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov/rscIfAtt-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[prov]]", + "tDn": "uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[prov]" + } + } + }, + { + "vnsRsCIfAttN": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov/rscIfAttN-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[prov]]", + "tDn": "uni/tn-user-11/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..a8d6de9 --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_old_only.json @@ -0,0 +1,38 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [ + { + "vnsRsCIfAtt": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons/rscIfAtt-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]]", + "tDn": "uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]" + } + } + } + ] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [ + { + "vnsRsCIfAtt": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov/rscIfAtt-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[prov]]", + "tDn": "uni/tn-user-11/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..c40143c --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_only.json @@ -0,0 +1,20 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [] + } + } +] diff --git a/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_stale_old_cif_mismatch.json b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_stale_old_cif_mismatch.json new file mode 100644 index 0000000..8aec0ee --- /dev/null +++ b/tests/checks/vnsrscifatt_deprecation_check/vnsLIf_with_rel_stale_old_cif_mismatch.json @@ -0,0 +1,46 @@ +[ + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons", + "name": "intf-cons" + }, + "children": [ + { + "vnsRsCIfAtt": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons/rscIfAtt-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons-old]]", + "tDn": "uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons-old]" + } + } + }, + { + "vnsRsCIfAttN": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-cons/rscIfAttN-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]]", + "tDn": "uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[cons]" + } + } + } + ] + } + }, + { + "vnsLIf": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov", + "name": "intf-prov" + }, + "children": [ + { + "vnsRsCIfAttN": { + "attributes": { + "dn": "uni/tn-user-11/lDevVip-test/lIf-intf-prov/rscIfAttN-[uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[prov]]", + "tDn": "uni/tn-user-11/lDevVip-test/cDev-cdev/cIf-[prov]" + } + } + } + ] + } + } +]