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
32 changes: 32 additions & 0 deletions aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -4355,6 +4355,37 @@ def access_untagged_check(**kwargs):
)


@check_wrapper(check_title='BGP Timer Policy Already Existing (F0467 bgpProt-policy-already-existing)')
def bgpProto_timer_policy_already_existing_check(**kwargs):
result = FAIL_O
headers = ['Fault', 'Tenant', 'L3Out', 'changeSet']
data = []
unformatted_headers = ['Fault', 'Affected', 'changeSet']
unformatted_data = []
recommended_action = 'Remove the fault by keeping Single bgp timer policy per vrf for different l3out.'
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#bgpProto-timer-policy-already-existing'

affected_regex = r'uni/tn-(?P<tenant>[^/]+)/out-(?P<l3out>[^\]]+)'
filter = 'faultDelegate.json?query-target-filter=and(eq(faultDelegate.code,"F0467"),wcard(faultDelegate.changeSet,"bgpProt-policy-already-existing"))'
fault_delegates = icurl('class', filter)

for fault_delegate in fault_delegates:
attributes = fault_delegate['faultDelegate']['attributes']
fault_code = attributes.get('code', '')
affected = attributes.get('affected', '')
change_set = attributes.get('changeSet', '')
affected_array = re.search(affected_regex, affected)
if affected_array:
data.append([fault_code, affected_array.group('tenant'), affected_array.group('l3out'), change_set])
else:
unformatted_data.append([fault_code, affected, change_set])

if not data and not unformatted_data:
result = PASS

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="Post Upgrade Callback Integrity")
def post_upgrade_cb_check(cversion, tversion, **kwargs):
result = PASS
Expand Down Expand Up @@ -6152,6 +6183,7 @@ class CheckManager:
prefix_already_in_use_check,
encap_already_in_use_check,
access_untagged_check,
bgpProto_timer_policy_already_existing_check,
bd_subnet_overlap_check,
bd_duplicate_subnet_check,
vmm_controller_status_check,
Expand Down
33 changes: 33 additions & 0 deletions docs/docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Items | Faults | This Script
[Fabric Port Status][f19] | F1394: ethpm-if-port-down-fabric | :white_check_mark: | :no_entry_sign:
[Equipment Disk Limits][f20] | F1820: 80% -minor<br>F1821: -major<br>F1822: -critical | :white_check_mark: | :no_entry_sign:
[VMM Inventory Partially Synced][f21] | F0132: comp-ctrlr-operational-issues | :white_check_mark: | :no_entry_sign:
[BgpProto timer policy already existing][f22] | F0467: bgpProt-policy-already-existing | :white_check_mark: | :no_entry_sign:


[f1]: #apic-disk-space-usage
Expand All @@ -105,6 +106,7 @@ Items | Faults | This Script
[f19]: #fabric-port-status
[f20]: #equipment-disk-limits
[f21]: #vmm-inventory-partially-synced
[f22]: #bgpProto-timer-policy-already-existing

### Configuration Checks

Expand Down Expand Up @@ -1551,6 +1553,37 @@ EPGs using the `pre-provision` resolution immediacy do not rely on the VMM inven

This check returns a `MANUAL` result as there are many reasons for a partial inventory sync to be reported. The goal is to ensure that the VMM inventory sync has fully completed before triggering the APIC upgrade to reduce any chance for unexpected inventory changes to occur.

### BgpProto Timer Policy Already Existing

This check 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.

Resolve these faults before upgrade by reviewing the affected L3Out BGP proto timer policy.

Example:

# fault.Delegate
affected : resPolCont/rtdOutCont/rtdOutDef-[uni/tn-common/out-L3outY]/nwissues
code : F0467
ack : no
cause : configuration-failed
changeSet : configQual:bgpProt-policy-already-existing, configSt:failed-to-apply, temporaryError:no
childAction :
created : 2026-03-25T11:31:16.724+00:00
descr : Fault delegate: Configuration failed for uni/tn-common/out-L3outY due to A specific leaf node can hold only a single bgpProtP config; this fault is raised when inconsistent configuration is detected, debug message:
dn : uni/tn-common/out-L3outY/fd-[resPolCont/rtdOutCont/rtdOutDef-[uni/tn-common/out-L3outY]/nwissues]-fault-F0467
domain : tenant
highestSeverity : critical
lc : raised
occur : 1
origSeverity : critical
prevSeverity : critical
rn : fd-[resPolCont/rtdOutCont/rtdOutDef-[uni/tn-common/out-L3outY]/nwissues]-fault-F0467
rule : fv-nw-issues-config-failed
severity : critical
subject : management
type : config

## Configuration Check Details

### VPC-paired Leaf switches
Expand Down
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,20 @@
[
{
"faultDelegate": {
"attributes": {
"affected": "resPolCont/rtdOutCont/rtdOutDef-[uni/tn-common/out-L3outY]/nwissues",
"code": "F0467",
"changeSet": "configQual:bgpProt-policy-already-existing, configSt:failed-to-apply, temporaryError:no"
}
}
},
{
"faultDelegate": {
"attributes": {
"affected": "resPolCont/rtdOutCont/rtdOutDef-[uni/tn-prod/out-L3outA]/nwissues",
"code": "F0467",
"changeSet": "configQual:bgpProt-policy-already-existing, configSt:failed-to-apply, temporaryError:no"
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"faultDelegate": {
"attributes": {
"affected": "resPolCont/rtdOutCont/rtdOutDef-[uni/invalid]/nwissues",
"code": "F0467",
"changeSet": "configQual:bgpProt-policy-already-existing, configSt:failed-to-apply, temporaryError:no"
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import os
import pytest
import logging
import importlib
from helpers.utils import read_data

script = importlib.import_module("aci-preupgrade-validation-script")
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
test_function = "bgpProto_timer_policy_already_existing_check"
# icurl queries
faultDelegates = 'faultDelegate.json?query-target-filter=and(eq(faultDelegate.code,"F0467"),wcard(faultDelegate.changeSet,"bgpProt-policy-already-existing"))'

@pytest.mark.parametrize(
"icurl_outputs, expected_result, expected_data, expected_unformatted_data",
[
(
{faultDelegates: read_data(dir, "faultDelegate_POS.json")},
script.FAIL_O,
[
[
"F0467",
"common",
"L3outY",
"configQual:bgpProt-policy-already-existing, configSt:failed-to-apply, temporaryError:no",
],
[
"F0467",
"prod",
"L3outA",
"configQual:bgpProt-policy-already-existing, configSt:failed-to-apply, temporaryError:no",
],
],
[],
),
(
{faultDelegates: read_data(dir, "faultDelegate_UNFORMATTED.json")},
script.FAIL_O,
[],
[
[
"F0467",
"resPolCont/rtdOutCont/rtdOutDef-[uni/invalid]/nwissues",
"configQual:bgpProt-policy-already-existing, configSt:failed-to-apply, temporaryError:no",
],
],
),
(
{faultDelegates: read_data(dir, "faultDelegate_NEG.json")},
script.PASS,
[],
[],
),
],
)
def test_logic(run_check, mock_icurl, expected_result, expected_data, expected_unformatted_data):
result = run_check()
assert result.result == expected_result
assert result.data == expected_data
assert result.unformatted_data == expected_unformatted_data