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
159 changes: 158 additions & 1 deletion aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -6458,7 +6458,7 @@ def bgpProto_timer_policy_already_existing_check(tversion, cversion, **kwargs):

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


@check_wrapper(check_title="WRED with Affected FM Models")
def wred_affected_model_check(tversion, fabric_nodes, **kwargs):
result = PASS
Expand Down Expand Up @@ -6510,7 +6510,163 @@ def wred_affected_model_check(tversion, fabric_nodes, **kwargs):
return Result(result=FAIL_O, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)

return Result(result=NA, msg="No affected Fabric module found.")


@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 @@ -6685,6 +6841,7 @@ class CheckManager:
inband_management_policy_misconfig_check,
bgpProto_timer_policy_already_existing_check,
wred_affected_model_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 @@ -205,6 +205,8 @@ Items | Defect | This Script
[Inband Management Policy Misconfiguration][d33]| CSCwd40071 | :white_check_mark: | :no_entry_sign:
[BgpProto timer policy already existing][d34] | CSCwt78235 | :white_check_mark: | :no_entry_sign:
[WRED with Affected FM Models][d35] | CSCwt50713 | :white_check_mark: | :no_entry_sign:
[vzany_svcgraph_stretched_vrf_check][d36] | CSCwt14573 | :white_check_mark: | :no_entry_sign:


[d1]: #ep-announce-compatibility
[d2]: #eventmgr-db-size-defect-susceptibility
Expand Down Expand Up @@ -241,6 +243,8 @@ Items | Defect | This Script
[d33]: #inband-management-policy-misconfiguration
[d34]: #bgpProto-timer-policy-already-existing
[d35]: #wred-with-affected-fm-models
[d36]: #vzany-service-graph-stretched-vrf


## General Check Details

Expand Down Expand Up @@ -2816,6 +2820,27 @@ To avoid this issue, disable WRED on the affected nodes or upgrade to a release

This bug [CSCwt78235][71] validates `F0467` faults where `changeSet` contains 'bgpProt-policy-already-existing'. The fault indicates conflicting BGP protocol timer policy under an L3Outs deployed in same vrf under same node. If this fault is not resolved, l3out will not be programmed properly in the leaf after the clean reboot or the upgrade.

### vzAny Service Graph on Stretched VRF

Due to [CSCwn95571], 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][73].


[0]: https://github.com/datacenter/ACI-Pre-Upgrade-Validation-Script
[1]: https://www.cisco.com/c/dam/en/us/td/docs/Website/datacenter/apicmatrix/index.html
Expand Down Expand Up @@ -2890,3 +2915,5 @@ This bug [CSCwt78235][71] validates `F0467` faults where `changeSet` contains 'b
[70]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCvo27498
[71]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt78235
[72]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt50713
[73]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt14573

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