Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 157 additions & 1 deletion aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -7320,6 +7320,162 @@ 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='vzAny Service Graph on Stretched VRF')
def vzany_svcgraph_stretched_vrf_check(tversion, **kwargs):
result = PASS
headers = ['Tenant', 'VRF', 'Contract', 'Graph', 'Issue']
data = []
recommended_action = 'Migrate vzAny service graph configuration to NDO before upgrade using brownfield import. See documentation for detailed migration steps.'
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#vzany-service-graph-stretched-vrf'

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

if not tversion.newer_than("6.1(3g)"):
return Result(result=PASS, msg="Target version does not trigger CSCwn95571 validation", doc_url=doc_url)

has_error = False

try:
graph_insts = icurl('class', 'vnsGraphInst.json?query-target-filter=eq(vnsGraphInst.configSt,"applied")&rsp-subtree=full')
except Exception as e:
return Result(result=ERROR, msg='Error querying applied service graphs: {}'.format(str(e)), doc_url=doc_url)

if not graph_insts:
return Result(result=PASS, msg="No applied service graphs found", doc_url=doc_url)

sg_by_contract = {}
for graph_inst_mo in graph_insts:
gi_attrs = graph_inst_mo.get('vnsGraphInst', {}).get('attributes', {})
gi_dn = gi_attrs.get('dn', '')
contract_dn = gi_attrs.get('ctrctDn', '')
if not contract_dn:
continue

graph_name_match = re.search(r'-G-\[uni/tn-[^/]+/AbsGraph-([^\]]+)\]', gi_dn)
graph_name = graph_name_match.group(1) if graph_name_match else ''
scope_match = re.search(r'-S-\[(.*?)\]', gi_dn)
scope_dn = scope_match.group(1) if scope_match else ''

first_node_name = None
for graph_component_mo in graph_inst_mo.get('vnsGraphInst', {}).get('children', []):
if 'vnsTermNodeInst' not in graph_component_mo:
continue
term_attrs = graph_component_mo['vnsTermNodeInst'].get('attributes', {})
if term_attrs.get('type') != 'consumer':
continue
for term_connection_mo in graph_component_mo['vnsTermNodeInst'].get('children', []):
if 'vnsConnectionInst' not in term_connection_mo:
continue
for conn_rel_mo in term_connection_mo['vnsConnectionInst'].get('children', []):
if 'vnsRsConnectionInstConns' not in conn_rel_mo:
continue
node_tDn = conn_rel_mo['vnsRsConnectionInstConns'].get('attributes', {}).get('tDn', '')
node_match = re.search(r'/NodeInst-([^\]]+)', node_tDn)
if node_match:
first_node_name = node_match.group(1)
break
if first_node_name:
break
if first_node_name:
break

sg_by_contract[contract_dn] = {
'gi_dn': gi_dn,
'graph_name': graph_name,
'scope_dn': scope_dn,
'first_node': first_node_name,
}

if not sg_by_contract:
return Result(result=PASS, msg="No applied service graphs with contracts found", doc_url=doc_url)

stretched_vrf_dns = set()
try:
fvctx_list = icurl('class', 'fvCtx.json?rsp-subtree=children&rsp-subtree-class=fvSiteAssociated&rsp-subtree-include=required')
for vrf_ctx_mo in fvctx_list:
ctx_dn = vrf_ctx_mo.get('fvCtx', {}).get('attributes', {}).get('dn', '')
for site_assoc_mo in vrf_ctx_mo.get('fvCtx', {}).get('children', []):
if 'fvSiteAssociated' not in site_assoc_mo:
continue
for remote_id_mo in site_assoc_mo['fvSiteAssociated'].get('children', []):
if 'fvRemoteId' in remote_id_mo:
stretched_vrf_dns.add(ctx_dn)
break
except Exception as e:
return Result(result=ERROR, msg='Error querying stretched VRFs: {}'.format(str(e)), doc_url=doc_url)

if not stretched_vrf_dns:
return Result(result=PASS, msg="No stretched VRFs found", doc_url=doc_url)

vzany_on_stretched = {}
for rel_class in ('vzRsAnyToCons', 'vzRsAnyToProv'):
try:
rels = icurl('class', '{}.json'.format(rel_class))
except Exception as e:
has_error = True
data.append(['-', '-', '-', '-', 'Error querying {}: {}'.format(rel_class, str(e))])
continue
for rel in rels:
rel_attrs = rel.get(rel_class, {}).get('attributes', {})
contract_dn = rel_attrs.get('tDn', '')
if contract_dn not in sg_by_contract:
continue
rel_dn = rel_attrs.get('dn', '')
vrf_dn_match = re.match(r'(uni/tn-[^/]+/ctx-[^/]+)', rel_dn)
if not vrf_dn_match:
continue
vrf_dn = vrf_dn_match.group(1)
if vrf_dn not in stretched_vrf_dns:
continue
vrf_name_match = re.search(r'ctx-([^/]+)', vrf_dn)
vrf_name = vrf_name_match.group(1) if vrf_name_match else vrf_dn
vzany_on_stretched[contract_dn] = {'vrf_name': vrf_name}

if not vzany_on_stretched and not has_error:
return Result(result=PASS, msg="No vzAny service graph contracts on stretched VRFs", doc_url=doc_url)

for contract_dn, vrf_info in vzany_on_stretched.items():
sg_info = sg_by_contract[contract_dn]
first_node_name = sg_info['first_node']
if not first_node_name:
continue

contract_match = re.match(r'uni/tn-([^/]+)/brc-([^/]+)', contract_dn)
if not contract_match:
continue
tenant = contract_match.group(1)
contract = contract_match.group(2)
graph_name = sg_info['graph_name']
scope_dn = sg_info['scope_dn']
vrf_name = vrf_info['vrf_name']

epg_def_dn = (
"uni/tn-{}/GraphInst_C-[uni/tn-{}/brc-{}]"
"-G-[uni/tn-{}/AbsGraph-{}]-S-[{}]"
"/NodeInst-{}/LegVNode-0/EPgDef-consumer"
).format(tenant, tenant, contract, tenant, graph_name, scope_dn, first_node_name)
xlate_dn = "uni/tn-{}/mscGraphXlateCont/epgDefXlate-[{}]".format(tenant, epg_def_dn)

try:
result_query = icurl('mo', '{}.json'.format(xlate_dn))
has_xlate = len(result_query) > 0
except Exception:
has_xlate = False
has_error = True
data.append([tenant, vrf_name, contract, graph_name, 'Error querying vnsEpgDefXlate'])
continue

if not has_xlate:
data.append([tenant, vrf_name, contract, graph_name, 'Missing vnsEpgDefXlate for 1st node consumer leg'])

if has_error:
result = ERROR
elif data:
result = FAIL_O
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)


# ---- Script Execution ----


Expand Down Expand Up @@ -7498,7 +7654,7 @@ class CheckManager:
n9k_c93180yc_fx3_switch_memory_check,
stale_dbgacEpgSummaryTask_check,
infravlan_overlap_access_policy_check,

vzany_svcgraph_stretched_vrf_check,
]
ssh_checks = [
# General
Expand Down
27 changes: 27 additions & 0 deletions docs/docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,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:
[vzany_svcgraph_stretched_vrf_check][d39] | CSCwt14573 | :white_check_mark: | :no_entry_sign:

[d1]: #ep-announce-compatibility
[d2]: #eventmgr-db-size-defect-susceptibility
Expand Down Expand Up @@ -250,6 +251,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]: #vzany-service-graph-stretched-vrf

## General Check Details

Expand Down Expand Up @@ -2827,6 +2829,29 @@ Due to the bug [CSCwt58626][77] , If Apic upgrade planned for target versions 6.

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.


### vzAny Service Graph on Stretched VRF

Due to [CSCwn95571][80], starting from ACI 6.1(4), a new multisite validation was introduced for service graphs used with vzAny contracts on stretched VRFs. When upgrading to 6.1(4) or later, if a vzAny contract with a service graph is configured locally on the APIC (not through Nexus Dashboard Orchestrator), the service graph will fail to instantiate with faults F0758 and F1690.

The validation checks whether `vnsEpgDefXlate` translation entries exist for the first node's consumer leg of the service graph. These entries are only created by NDO during template deployment. When the configuration is managed locally on the APIC, these entries are absent, causing the graph rendering to fail.

This check detects configurations where **all** of the following conditions are true:

1. The VRF is stretched across multiple sites (has `fvSiteAssociated` with `fvRemoteId` children)
2. vzAny is used as either consumer **or** provider on the stretched VRF
3. The contract has a service graph attached (any type — PBR is **not** required)
4. No `vnsEpgDefXlate` MOs exist for the service graph's first node consumer leg

!!! note
The fault alone does **not** cause traffic impact for already-deployed graphs. Traffic impact only occurs if the service graph is detached and re-attached to the contract while the fault condition is present.

!!! note
This applies to **all** service graph types including firewalls with PBR, load balancers without PBR, and any other L4-L7 service devices.

Recommended action: Migrate the vzAny service graph configuration to NDO before upgrade using brownfield import. NDO 4.2(3e) or later is required for vzAny PBR support on stretched VRFs. This is tracked under [CSCwt14573][79].


[0]: https://github.com/datacenter/ACI-Pre-Upgrade-Validation-Script
[1]: https://www.cisco.com/c/dam/en/us/td/docs/Website/datacenter/apicmatrix/index.html
[2]: https://www.cisco.com/c/en/us/support/switches/nexus-9000-series-switches/products-release-notes-list.html
Expand Down Expand Up @@ -2905,3 +2930,5 @@ To avoid this issue, modify the user VLAN pool ranges so that the InfraVLAN does
[76]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt38698
[77]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt58626
[78]: https://www.cisco.com/c/en/us/td/docs/switches/datacenter/aci/apic/sw/5-x/aci-fundamentals/cisco-aci-fundamentals-50x/m_policy-model.html#concept_tds_vcc_fy
[79]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt14573
[80]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwn95571
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"fvCtx": {
"attributes": {"dn": "uni/tn-Tenant1/ctx-VRF1", "name": "VRF1"},
"children": []
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"fvCtx": {
"attributes": {"dn": "uni/tn-Tenant1/ctx-VRF1", "name": "VRF1"},
"children": [
{
"fvSiteAssociated": {
"children": [
{
"fvRemoteId": {"attributes": {"id": "1"}}
}
]
}
}
]
}
}
]
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,9 @@
[
{
"vnsEpgDefXlate": {
"attributes": {
"dn": "uni/tn-Tenant1/mscGraphXlateCont/epgDefXlate-[...]"
}
}
}
]
Loading
Loading