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
182 changes: 182 additions & 0 deletions aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -6397,6 +6397,187 @@ def apic_downgrade_compat_warning_check(cversion, tversion, **kwargs):
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)


@check_wrapper(check_title='Shared Services Providers with Preferred Group enabled')
def pg_and_shared_svc_contract_check(cversion, tversion, **kwargs):
result= PASS
headers = ["Shared Service Contract", "Provider in Preferred Group", "PcTag", "Affected Consumer"]
data = []
recommended_action = (
"Before upgrading, remove each listed provider from the Preferred Group, "
"stop it from providing the listed shared-service contract, or remove the "
"unsupported L3Out/vzAny consumer relationship. Re-deploy the policy and "
"confirm the contract and Preferred Group configuration deploy successfully. "
"On releases that enforce this restriction, verify that F0467 or F4684 clears."
)
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#preferred-group-shared-service-provider'

if not tversion:
return Result(result=MANUAL, msg=TVER_MISSING)
# Only releases 4.2 and later are in scope for this validation.
if tversion.older_than("4.2(1a)"):
return Result(result=NA)
shrd_contracts_api = 'vzBrCP.json'
shrd_contracts_api += '?query-target-filter=or(eq(vzBrCP.scope,"global"),eq(vzBrCP.scope,"tenant"))'
shrd_contracts = icurl('class', shrd_contracts_api)
if not shrd_contracts:
return Result(result=NA)
shared_contract_scopes = {}
for shrd_contract in shrd_contracts:
contract_attributes = shrd_contract["vzBrCP"]["attributes"]
shared_contract_scopes[contract_attributes["dn"]] = contract_attributes["scope"]

glbl_epgs_api = 'fvAEPg.json'
glbl_epgs_api += '?query-target-filter=and(le(fvAEPg.pcTag,"16385"),ge(fvAEPg.pcTag,"17"),eq(fvAEPg.prefGrMemb,"include"))'
glbl_epgs_api += '&rsp-subtree=children&rsp-subtree-class=fvRsProv'
glbl_epgs = icurl('class', glbl_epgs_api)

glbl_ext_epgs_api = 'l3extInstP.json'
glbl_ext_epgs_api += '?query-target-filter=and(le(l3extInstP.pcTag,"16385"),ge(l3extInstP.pcTag,"17"),eq(l3extInstP.prefGrMemb,"include"))'
glbl_ext_epgs_api += '&rsp-subtree=children&rsp-subtree-class=fvRsProv'
glbl_ext_epgs = icurl('class', glbl_ext_epgs_api)

shared_service_providers = []
for provider_class, providers in (("fvAEPg", glbl_epgs), ("l3extInstP", glbl_ext_epgs)):
for provider_mo in providers:
provider = provider_mo[provider_class]
provider_attributes = provider["attributes"]
for prov_contract in provider.get("children") or []:
contract = prov_contract["fvRsProv"]["attributes"]["tDn"]
if contract in shared_contract_scopes:
shared_service_providers.append((contract, provider_attributes))

if shared_service_providers:
providers_by_dn = {}
for contract, provider_attributes in shared_service_providers:
providers_by_dn.setdefault(provider_attributes["dn"], []).append(
(contract, provider_attributes)
)

ctx_defs = icurl('class', 'fvCtxDef.json')
ctx_def_by_scope = {}
for ctx_def in ctx_defs:
ctx_attributes = ctx_def["fvCtxDef"]["attributes"]
ctx_def_by_scope[ctx_attributes["scope"]] = ctx_attributes["dn"]

provider_relationships_api = 'vzFromEPg.json'
provider_relationships_api += '?query-target-filter=and(eq(vzFromEPg.membType,"prov"),'
provider_relationships_api += 'le(vzFromEPg.pcTag,"16385"),ge(vzFromEPg.pcTag,"17"))'
provider_relationships_api += '&rsp-subtree=children&rsp-subtree-class=vzToEPg'
provider_relationships = icurl('class', provider_relationships_api)
cross_context_relationships = []
relationship_errors = []

def tenant_dn(dn):
dn_parts = dn.split("/", 2)
if len(dn_parts) >= 2 and dn_parts[0] == "uni" and dn_parts[1].startswith("tn-"):
return "/".join(dn_parts[:2])
return None

for provider_relationship in provider_relationships:
from_epg = provider_relationship["vzFromEPg"]
from_attributes = from_epg["attributes"]
provider_dn = from_attributes["epgDn"]
provider_contracts = providers_by_dn.get(provider_dn, [])
if not provider_contracts:
continue

matching_provider_contracts = [
provider_contract
for provider_contract in provider_contracts
if from_attributes["dn"].startswith(
"cdef-[{}]/".format(provider_contract[0])
)
]
if not matching_provider_contracts:
continue

to_epgs = from_epg.get("children") or []
if not to_epgs:
continue

provider_ctx_def_dn = ctx_def_by_scope.get(from_attributes["scopeId"])
if not provider_ctx_def_dn:
relationship_errors.append([
from_attributes["dn"],
"No fvCtxDef found for scopeId {}".format(from_attributes["scopeId"])
])
continue

for contract, provider_attributes in matching_provider_contracts:
for to_epg_mo in to_epgs:
if "vzToEPg" not in to_epg_mo:
continue
consumer_attributes = to_epg_mo["vzToEPg"]["attributes"]
consumer_dn = consumer_attributes["epgDn"]
consumer_ctx_def_dn = consumer_attributes.get("ctxDefDn")
if not consumer_ctx_def_dn:
relationship_errors.append([
consumer_attributes["dn"],
"vzToEPg.ctxDefDn is empty"
])
continue

if shared_contract_scopes[contract] == "tenant":
contract_tenant = tenant_dn(contract)
if (
contract_tenant != tenant_dn(provider_dn)
or contract_tenant != tenant_dn(consumer_dn)
):
continue

if provider_ctx_def_dn != consumer_ctx_def_dn:
cross_context_relationships.append(
(contract, provider_attributes, consumer_dn)
)

broad_provider_check = tversion.older_than("6.0(1g)")
if broad_provider_check:
affected_relationships = cross_context_relationships
elif cross_context_relationships:
affected_relationships = [
relationship
for relationship in cross_context_relationships
if "/instP-" in relationship[2]
or relationship[2].endswith("/any")
]
else:
affected_relationships = []

reported_relationships = set()
for contract, provider_attributes, consumer_dn in affected_relationships:
result_row = (
contract,
provider_attributes["dn"],
provider_attributes["pcTag"],
consumer_dn
)
if result_row not in reported_relationships:
reported_relationships.add(result_row)
data.append(list(result_row))

if relationship_errors:
return Result(
result=ERROR,
msg="Unable to resolve context for one or more derived contract relationships",
headers=["Derived Relationship", "Context Resolution Error"],
data=relationship_errors,
unformatted_headers=headers,
unformatted_data=data,
recommended_action=(
"Retry the check. Review any confirmed affected relationships "
"shown in the failure details. If context resolution continues "
"to fail, contact Cisco TAC with the listed derived relationship "
"DNs."
),
doc_url=doc_url
)

if data:
result = FAIL_O

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


@check_wrapper(check_title='Auto Firmware Update on Switch Discovery')
def auto_firmware_update_on_switch_check(cversion, tversion, **kwargs):
result = PASS
Expand Down Expand Up @@ -7131,6 +7312,7 @@ class CheckManager:
service_bd_forceful_routing_check,
ave_eol_check,
consumer_vzany_shared_services_check,
pg_and_shared_svc_contract_check,

# Bugs
ep_announce_check,
Expand Down
16 changes: 16 additions & 0 deletions docs/docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Items | Faults | This Script
[Service Graph BD Forceful Routing][c22] | :white_check_mark: | :no_entry_sign:
[AVE End-of-life][c23] | :white_check_mark: | :no_entry_sign:
[Shared Service with vzAny Consumer][c24] | :white_check_mark: | :no_entry_sign:
[Preferred Group Shared Service Provider][c25] | :white_check_mark: | :no_entry_sign:

[c1]: #vpc-paired-leaf-switches
[c2]: #overlapping-vlan-pool
Expand All @@ -166,6 +167,7 @@ Items | Faults | This Script
[c22]: #service-graph-bd-forceful-routing
[c23]: #ave-end-of-life
[c24]: #shared-service-with-vzany-consumer
[c25]: #preferred-group-shared-service-provider

### Defect Condition Checks

Expand Down Expand Up @@ -2372,6 +2374,19 @@ See [Inter-VRF contract with vzAny as the consumer][60] in Cisco ACI Contract Gu
See [Enable Policy Compression in Cisco ACI Contract Guide][61] for details about Policy Compression.


### Preferred Group Shared Service Provider

ACI 4.2 and later configurations can be affected by CSCvm63145 and CSCvv51121 when a Preferred Group member provides a tenant- or global-scope shared-service contract to a consumer in another VRF.

The script reports only materialized, cross-VRF provider-to-consumer relationships represented by `vzFromEPg` and `vzToEPg`. A configured provider without such a relationship is not reported. Tenant-scope contracts are considered only when the contract, provider, and consumer belong to the same tenant. Shared/global pcTags `17` through `16385` are treated as fabric-wide identities; VRF separation is determined independently from the context-definition DNs.

Before 6.0(1g), any consumer class in a materialized cross-VRF relationship can be affected. Depending on the release, the forwarding risk can be silent or the contract can be rejected with F0467 and `invalid-contract-config: Shared service provider cannot be in a Preferred Group`.

Starting with 6.0(1g), ordinary EPG-to-EPG shared service is allowed. The unsupported condition remains only when the Preferred Group provider has a materialized relationship with an L3Out or `vzAny` consumer in another VRF. Same-VRF L3Out and `vzAny` relationships are not reported. Starting with 6.1(3f), this condition may be reported through F4684.

Before upgrading, use the provider and consumer DNs shown in the result to remove the provider from the Preferred Group, stop it from providing the shared-service contract, or remove the unsupported relationship. See the [ACI Policy Model][78] for additional background.


## Defect Check Details

### EP Announce Compatibility
Expand Down Expand Up @@ -2947,3 +2962,4 @@ 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://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
Loading
Loading