Skip to content

Commit 34c3dbd

Browse files
amitanilkulkarniAmit Kulkarnitakishida
authored
feat: add consumer vzAny shared-services check for rule expansion (#282)
Add `consumer_vzany_shared_services_check()`: - Warns when a vzAny consumer in VRF-A uses a contract provided from a different VRF-B and the upgrade path triggers a new rule expansion - If the contract has PBR and the target version doesn't support Policy Compression (i.e. < 6.1.4), warn about it as well because the potential workaround (policy compression) is not available. --------- Co-authored-by: Amit Kulkarni <amitkul2@cisco.com> Co-authored-by: tkishida <tkishida@cisco.com>
1 parent 5a620c0 commit 34c3dbd

14 files changed

Lines changed: 1138 additions & 5 deletions

aci-preupgrade-validation-script.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4530,6 +4530,230 @@ def vzany_vzany_service_epg_check(cversion, tversion, **kwargs):
45304530
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)
45314531

45324532

4533+
@check_wrapper(check_title="Shared Services with vzAny Consumers")
4534+
def consumer_vzany_shared_services_check(cversion, tversion, **kwargs):
4535+
headers = ["Contract(Tn:Contract)", "Consumer VRF(Tn:VRF)", "Provider VRF(Tn:VRF)", "Provider DN", "Provider Type"]
4536+
data = []
4537+
recommended_action = (
4538+
"Policy TCAM entries used by these contracts may increase after the upgrade.\n"
4539+
"\tThis may cause overflow of the TCAM space and some contracts may stop working after the upgrade.\n"
4540+
"\tTo avoid such a risk, refer to the provided document and consider enabling Policy Compression as needed."
4541+
)
4542+
recommended_action_for_pbr = ( # added only when it matters (PBR present and tver is pre-6.1.4)
4543+
"\n\tNote that Policy Compression for contracts with PBR (Policy Based Redirection) is not supported prior to 6.1(4). "
4544+
"Change the target version to a newer one if it is required."
4545+
)
4546+
doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#shared-service-with-vzany-consumer"
4547+
4548+
# Ignore if target version is missing or older than 5.3(2d)
4549+
if not tversion:
4550+
return Result(result=MANUAL, msg=TVER_MISSING)
4551+
if tversion.older_than("5.3(2d)"):
4552+
return Result(result=NA, msg=VER_NOT_AFFECTED)
4553+
4554+
# Check if we cross any version lines where additional rule expansion may happen
4555+
should_check_epg_expansion = False
4556+
should_check_esg_expansion = False
4557+
should_check_pbr = False
4558+
4559+
# Rule expansion for EPG/External EPG providers with vzAny consumers
4560+
# For upgrades from pre-5.3(2d) to 5.3(2d)+ except for 6.0(1) and 6.0(2)
4561+
if cversion.older_than("5.3(2d)") and (
4562+
(tversion.major1 == "5" and not tversion.older_than("5.3(2d)"))
4563+
or tversion.newer_than("6.0(3a)")
4564+
):
4565+
should_check_epg_expansion = True
4566+
# For upgrades from 6.0(1)/6.0(2) to 6.0(3) or newer release
4567+
if cversion.newer_than("6.0(1a)") and cversion.older_than("6.0(3a)") and tversion.newer_than("6.0(3a)"):
4568+
should_check_epg_expansion = True
4569+
4570+
# Rule expansion for ESG providers with vzAny consumers
4571+
if cversion.older_than("6.1(2a)") and tversion.newer_than("6.1(2a)"):
4572+
should_check_esg_expansion = True
4573+
4574+
# Look for PBR enabled contracts that undergo rule expansion since enabling
4575+
# compression on them will have no effect in pre-6.1(4) releases
4576+
if tversion.older_than("6.1(4a)"):
4577+
should_check_pbr = True
4578+
4579+
# If no expansion, upgrade path is unaffected
4580+
if not (should_check_epg_expansion or should_check_esg_expansion):
4581+
return Result(result=NA, msg=VER_NOT_AFFECTED)
4582+
4583+
# Helper functions
4584+
def vrf_tn_name_from_dn(vrf_dn):
4585+
m = re.search(r"uni/tn-([^/]+)/ctx-([^/]+)", vrf_dn or "")
4586+
return "{}:{}".format(m.group(1), m.group(2)) if m else vrf_dn or "?"
4587+
4588+
def contract_tn_name_from_dn(c_dn):
4589+
m = re.search(r"uni/tn-([^/]+)/brc-([^/]+)", c_dn or "")
4590+
return "{}:{}".format(m.group(1), m.group(2)) if m else c_dn or "?"
4591+
4592+
def provider_class_from_parent(p_dn, pretty=False):
4593+
if "/ap-" in p_dn and "/epg-" in p_dn:
4594+
return "EPG" if pretty else "fvAEPg"
4595+
if "/ap-" in p_dn and "/esg-" in p_dn:
4596+
return "ESG" if pretty else "fvESg"
4597+
if "/out-" in p_dn and "/instP-" in p_dn:
4598+
return "External EPG" if pretty else "l3extInstP"
4599+
return "Unknown"
4600+
4601+
# Resolve provider VRF VNID.
4602+
# Performs at most one query per provider class (EPG, External EPG, ESG)
4603+
# and caches all results. Subsequent lookups are O(1).
4604+
_provider_dn_to_vrf_vnid = {}
4605+
_queried = {"fvAEPg": False, "l3extInstP": False, "fvESg": False}
4606+
4607+
def get_provider_vrf_vnid(p_dn):
4608+
if p_dn in _provider_dn_to_vrf_vnid:
4609+
return _provider_dn_to_vrf_vnid[p_dn]
4610+
4611+
p_classname = provider_class_from_parent(p_dn)
4612+
4613+
if p_classname == "Unknown":
4614+
_provider_dn_to_vrf_vnid[p_dn] = None
4615+
elif not _queried.get(p_classname):
4616+
for mo in icurl("class", p_classname + ".json") or []:
4617+
attr = mo.get(p_classname, {}).get("attributes", {})
4618+
dn = attr.get("dn")
4619+
vnid = attr.get("scope")
4620+
_provider_dn_to_vrf_vnid[dn] = vnid
4621+
_queried[p_classname] = True
4622+
4623+
return _provider_dn_to_vrf_vnid[p_dn]
4624+
4625+
_pbr_enabled_contracts = set()
4626+
4627+
def populate_pbr_enabled_contracts():
4628+
# Query all applied service graph instances, inspect node instances
4629+
# for routingMode=Redirect. Any contract DN (ctrctDn) with a redirect
4630+
# node is considered PBR-enabled.
4631+
# Relevant only if we are crossing 6.1(4) version in upgrade path.
4632+
graph_api = (
4633+
"vnsGraphInst.json?"
4634+
"query-target-filter=eq(vnsGraphInst.configSt,\"applied\")"
4635+
"&rsp-subtree=children&rsp-subtree-class=vnsNodeInst&rsp-subtree-include=required"
4636+
)
4637+
graph_insts = icurl("class", graph_api) or []
4638+
if not graph_insts:
4639+
return
4640+
for gi in graph_insts:
4641+
gi_mo = gi.get("vnsGraphInst")
4642+
if not gi_mo:
4643+
continue
4644+
ctrct_dn = gi_mo["attributes"].get("ctrctDn")
4645+
if not ctrct_dn:
4646+
continue
4647+
redirect = False
4648+
for child in gi_mo.get("children", []) or []:
4649+
node_inst = child.get("vnsNodeInst")
4650+
if not node_inst:
4651+
continue
4652+
if node_inst["attributes"].get("routingMode") == "Redirect":
4653+
redirect = True
4654+
break
4655+
if redirect:
4656+
_pbr_enabled_contracts.add(ctrct_dn)
4657+
4658+
def is_contract_pbr_enabled(contract_dn):
4659+
return contract_dn in _pbr_enabled_contracts
4660+
4661+
# Gather all VRF VNIDs and look for vzAny consumers
4662+
all_vrfs = icurl("class", "fvCtx.json?rsp-subtree=full&rsp-subtree-class=vzRsAnyToCons") or []
4663+
vnid_to_vrf_dn = {}
4664+
contract_to_vzany_cons_vnids = defaultdict(list)
4665+
for vrf_entry in all_vrfs:
4666+
fvctx = vrf_entry.get("fvCtx", {})
4667+
attr = fvctx.get("attributes", {})
4668+
vrf_dn = attr.get("dn")
4669+
vrf_vnid = attr.get("scope")
4670+
if vrf_dn and vrf_vnid:
4671+
vnid_to_vrf_dn[vrf_vnid] = vrf_dn
4672+
for child in fvctx.get("children", []) or []:
4673+
vzany = child.get("vzAny")
4674+
if not vzany:
4675+
continue
4676+
for vzany_child in vzany.get("children", []) or []:
4677+
if vzany_child.get("vzRsAnyToCons"):
4678+
contract_dn = vzany_child["vzRsAnyToCons"]["attributes"]["tDn"]
4679+
contract_to_vzany_cons_vnids[contract_dn].append(vrf_vnid)
4680+
4681+
# Return if there are no vzAny consumers
4682+
if not contract_to_vzany_cons_vnids:
4683+
return Result(result=PASS, msg="No vzAny consumers")
4684+
4685+
# Look for contracts with global scope
4686+
global_contract_api = (
4687+
'vzBrCP.json?query-target-filter=eq(vzBrCP.scope,"global")'
4688+
'&rsp-subtree=children'
4689+
'&rsp-subtree-class=vzRtProv'
4690+
'&rsp-subtree-include=required'
4691+
)
4692+
global_contracts = icurl("class", global_contract_api) or []
4693+
4694+
if not global_contracts:
4695+
return Result(result=PASS, msg="No contracts with global scope")
4696+
4697+
if should_check_pbr:
4698+
populate_pbr_enabled_contracts()
4699+
4700+
# Go through contract relations
4701+
found_pbr = False
4702+
for entry in global_contracts:
4703+
brc = entry.get("vzBrCP")
4704+
if not brc:
4705+
continue
4706+
contract_dn = brc["attributes"]["dn"]
4707+
# Check consumers (vzAny)
4708+
c_vrf_vnids = contract_to_vzany_cons_vnids.get(contract_dn)
4709+
if not c_vrf_vnids:
4710+
continue # No vzAny consumers for this contract. Skip.
4711+
# Check providers ("fvAEPg", "l3extInstP", "fvESg")
4712+
providers = set()
4713+
for ch in (brc.get("children") or []):
4714+
if ch.get("vzRtProv"):
4715+
p_dn = ch["vzRtProv"]["attributes"].get("tDn")
4716+
p_cl = ch["vzRtProv"]["attributes"].get("tCl")
4717+
if p_dn:
4718+
if should_check_epg_expansion and p_cl in ("fvAEPg", "l3extInstP"):
4719+
providers.add(p_dn)
4720+
elif should_check_esg_expansion and p_cl == "fvESg":
4721+
providers.add(p_dn)
4722+
# Populate data with cons/prov of contract affected by rule expansion
4723+
for c_vrf_vnid in c_vrf_vnids:
4724+
for p_dn in providers:
4725+
p_vrf_vnid = get_provider_vrf_vnid(p_dn)
4726+
if not p_vrf_vnid or p_vrf_vnid == c_vrf_vnid:
4727+
continue # global contract but used within the same VRF. Skip.
4728+
contract_name = contract_tn_name_from_dn(contract_dn)
4729+
if should_check_pbr and is_contract_pbr_enabled(contract_dn):
4730+
contract_name += " [PBR]"
4731+
found_pbr = True
4732+
data.append([
4733+
contract_name,
4734+
vrf_tn_name_from_dn(vnid_to_vrf_dn[c_vrf_vnid]),
4735+
vrf_tn_name_from_dn(vnid_to_vrf_dn[p_vrf_vnid]),
4736+
p_dn,
4737+
provider_class_from_parent(p_dn, pretty=True),
4738+
])
4739+
4740+
if found_pbr:
4741+
recommended_action += recommended_action_for_pbr
4742+
4743+
if data:
4744+
return Result(result=MANUAL,
4745+
headers=headers,
4746+
data=data,
4747+
recommended_action=recommended_action,
4748+
doc_url=doc_url)
4749+
else:
4750+
return Result(result=PASS,
4751+
msg="No shared-service vzAny consumers affected by rule expansion",
4752+
headers=headers,
4753+
data=data,
4754+
doc_url=doc_url)
4755+
4756+
45334757
@check_wrapper(check_title='32 and 64-Bit Firmware Image for Switches')
45344758
def validate_32_64_bit_image_check(cversion, tversion, **kwargs):
45354759
result = PASS
@@ -5464,6 +5688,7 @@ def get_checks(api_only, debug_function):
54645688
aes_encryption_check,
54655689
service_bd_forceful_routing_check,
54665690
ave_eol_check,
5691+
consumer_vzany_shared_services_check,
54675692

54685693
# Bugs
54695694
ep_announce_check,

docs/docs/validations.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ Items | Faults | This Script
107107
### Configuration Checks
108108

109109
Items | This Script | APIC built-in
110-
------------------------------------------------------|--------------------|--------------------------
110+
------------------------------------------------------|--------------------|---------------------------
111111
[VPC-paired Leaf switches][c1] | :white_check_mark: | :white_check_mark: 4.2(4)
112112
[Overlapping VLAN Pool][c2] | :white_check_mark: | :no_entry_sign:
113113
[VNID Mismatch][c3] | :warning:{title="Deprecated"} | :no_entry_sign:
114114
[L3Out MTU][c4] | :white_check_mark: | :no_entry_sign:
115115
[BGP Peer Profile at node level without Loopback][c5] | :white_check_mark: | :no_entry_sign:
116-
[L3Out Route Map import/export direction][c6] | :white_check_mark: | :no_entry_sign
117-
[L3Out Route Map Match Rule with missing-target][c7] | :white_check_mark: | :no_entry_sign
116+
[L3Out Route Map import/export direction][c6] | :white_check_mark: | :no_entry_sign:
117+
[L3Out Route Map Match Rule with missing-target][c7] | :white_check_mark: | :no_entry_sign:
118118
[L3Out Loopback IP Overlap with L3Out Interfaces][c8] | :white_check_mark: | :no_entry_sign:
119119
[ISIS Redistribution Metric for MPod/Msite][c9] | :white_check_mark: | :no_entry_sign:
120120
[BGP Route-target Type for GOLF over L2EVPN][c10] | :white_check_mark: | :no_entry_sign:
@@ -123,14 +123,15 @@ Items | Faults | This Script
123123
[OoB Mgmt Security][c13] | :white_check_mark: | :no_entry_sign:
124124
[EECDH SSL Cipher Disabled][c14] | :white_check_mark: | :no_entry_sign:
125125
[BD and EPG Subnet Scope Consistency][c15] | :white_check_mark: | :no_entry_sign:
126-
[Unsupported FEC Configuration for N9K-C93180YC-EX][c16] | :white_check_mark: | :no_entry_sign:
126+
[Unsupported FEC Configuration for N9K-C93180YC-EX][c16] | :white_check_mark: | :no_entry_sign:
127127
[CloudSec Encryption Deprecated][c17] | :white_check_mark: | :no_entry_sign:
128128
[Out-of-Service Ports][c18] | :white_check_mark: | :no_entry_sign:
129129
[TEP-to-TEP atomic counters Scalability][c19] | :white_check_mark: | :no_entry_sign:
130130
[HTTPS Request Throttle Rate][c20] | :white_check_mark: | :no_entry_sign:
131131
[Global AES Encryption][c21] | :white_check_mark: | :white_check_mark: 6.1(2)
132132
[Service Graph BD Forceful Routing][c22] | :white_check_mark: | :no_entry_sign:
133133
[AVE End-of-life][c23] | :white_check_mark: | :no_entry_sign:
134+
[Shared Service with vzAny Consumer][c24] | :white_check_mark: | :no_entry_sign:
134135

135136

136137
[c1]: #vpc-paired-leaf-switches
@@ -156,6 +157,7 @@ Items | Faults | This Script
156157
[c21]: #global-aes-encryption
157158
[c22]: #service-graph-bd-forceful-routing
158159
[c23]: #ave-end-of-life
160+
[c24]: #shared-service-with-vzany-consumer
159161

160162
### Defect Condition Checks
161163

@@ -2207,6 +2209,19 @@ As outlined in the [End-of-Sale and End-of-Life Announcement for Cisco Applicati
22072209
If planning an upgrade to 6.0+, review the [Cisco ACI Virtual Edge Migration Guide][56] and complete a domain migration prior to performing the upgrade.
22082210

22092211

2212+
### Shared Service with vzAny Consumer
2213+
A shared service (VRF Route Leaking) contract with vzAny as a consumer may use more policy TCAM space after an upgrade depending on the release version and provider type due to a behavior change called "Rule Expansion":
2214+
2215+
* In the case of EPG/External EPG providers, 5.3(2d) and later or 6.0(3) and later releases may use more policy TCAM space than releases older than 5.3(2d) or 6.0(3).
2216+
* In the case of ESG providers, 6.1(2) and later releases may use more policy TCAM space than releases older than 6.1(2).
2217+
2218+
When Rule Expansion takes place after an upgrade, the increase in the TCAM space may result in TCAM overflow which can lead to traffic disruption because contracts that used to work may stop working.
2219+
2220+
See [Inter-VRF contract with vzAny as the consumer][60] in Cisco ACI Contract Guide for details about Rule Expansion and calculate the potential TCAM space usage when the Rule Expansion takes place. If there is a risk of TCAM overflow, consider enabling the policy compression directive on contract filters to mitigate the increase of TCAM usage. However, note that enabling the policy compression directive will result in loss of the statistics capability for those rules. Also, note that policy compression for contracts with PBR is supported only from 6.1(4).
2221+
2222+
See [Enable Policy Compression in Cisco ACI Contract Guide][61] for details about Policy Compression.
2223+
2224+
22102225
## Defect Check Details
22112226

22122227
### EP Announce Compatibility
@@ -2648,4 +2663,6 @@ If any instances of `configpushShardCont` are flagged by this script, Cisco TAC
26482663
[56]: https://www.cisco.com/c/en/us/td/docs/dcn/whitepapers/cisco-aci-virtual-edge-migration.html
26492664
[57]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp22212
26502665
[58]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp15375
2651-
[59]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp95515
2666+
[59]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp95515
2667+
[60]: https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-743951.html#Inter
2668+
[61]: https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-743951.html#EnablePolicyCompression
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[
2+
{
3+
"fvAEPg": {
4+
"attributes": {
5+
"dn": "uni/tn-vzAny-consumer/ap-ap1/epg-epg2-unmatched",
6+
"scope": "2850816"
7+
}
8+
}
9+
}
10+
]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[
2+
{
3+
"fvESg": {
4+
"attributes": {
5+
"dn": "uni/tn-vzAny-consumer/ap-ap1/esg-esg2",
6+
"scope": "2850816"
7+
}
8+
}
9+
},
10+
{
11+
"fvESg": {
12+
"attributes": {
13+
"dn": "uni/tn-vzAny-consumer/ap-ap1/esg-esg2b",
14+
"scope": "2850816"
15+
}
16+
}
17+
},
18+
{
19+
"fvESg": {
20+
"attributes": {
21+
"dn": "uni/tn-vzAny-consumer/ap-ap1/esg-esg1",
22+
"scope": "2916352"
23+
}
24+
}
25+
}
26+
]

0 commit comments

Comments
 (0)