diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index fd6f076..8e904ab 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -6293,7 +6293,42 @@ def multipod_modular_spine_bootscript_check(tversion, fabric_nodes, username, pa return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) -# ---- Script Execution ---- +@check_wrapper(check_title='BGP Timer Policy Already Existing (F0467 bgpProt-policy-already-existing)') +def bgpProto_timer_policy_already_existing_check(tversion, **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' + + if tversion.newer_than("6.2(1g)") or ( + tversion.major1 == "6" and tversion.major2 == "1" and tversion.newer_than("6.1(5e)")): + return Result(result=NA, msg=VER_NOT_AFFECTED) + + affected_regex = r'uni/tn-(?P[^/]+)/out-(?P[^\]]+)' + 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) + + +# ---- Script Execution ----. def parse_args(args): @@ -6461,7 +6496,8 @@ class CheckManager: configpush_shard_check, auto_firmware_update_on_switch_check, rogue_ep_coop_exception_mac_check, - n9k_c9408_model_lem_count_check, + n9k_c9408_model_lem_count_check, + bgpProto_timer_policy_already_existing_check, ] ssh_checks = [ # General diff --git a/docs/docs/validations.md b/docs/docs/validations.md index 49fe535..cdde4d3 100644 --- a/docs/docs/validations.md +++ b/docs/docs/validations.md @@ -107,6 +107,7 @@ Items | Faults | This Script [f21]: #vmm-inventory-partially-synced [f22]: #apic-storage-inode-usage + ### Configuration Checks Items | This Script | APIC built-in @@ -198,6 +199,7 @@ Items | Defect | This Script [Rogue EP Exception List missing on switches][d30] | CSCwp64296 | :white_check_mark: | :no_entry_sign: [N9K-C9408 with more than 5 N9K-X9400-16W LEMs][d31] | CSCws82819 | :white_check_mark: | :no_entry_sign: [Multi-Pod Modular Spine Bootscript File][d32] | CSCwr66848 | :white_check_mark: | :no_entry_sign: +[BgpProto timer policy already existing][d33] | CSCwt78235 | :white_check_mark: | :no_entry_sign: [d1]: #ep-announce-compatibility [d2]: #eventmgr-db-size-defect-susceptibility @@ -231,6 +233,7 @@ Items | Defect | This Script [d30]: #rogue-ep-exception-list-missing-on-switches [d31]: #n9k-c9408-with-more-than-5-n9k-x9400-16w-lems [d32]: #multi-pod-modular-spine-bootscript-file +[d33]: #bgpProto-timer-policy-already-existing ## General Check Details @@ -2753,6 +2756,10 @@ This issue happens only when the target version is specifically 6.1(4h). To avoid this issue, change the target version to another version. Or verify that the `bootscript` file exists in the bootflash of each modular spine switch prior to upgrading to 6.1(4h). If the file is missing, you have to do clean reboot on the impacted spine to ensure that `/bootflash/bootscript` gets created again. In case you already upgraded your spine and you are experiencing the traffic impact due to this issue, clean reboot of the spine will restore the traffic. +### BgpProto Timer Policy Already Existing + +This bug [CSCwt78235][67] 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 upgrade. + [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 @@ -2820,3 +2827,4 @@ To avoid this issue, change the target version to another version. Or verify tha [64]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp64296 [65]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCws82819 [66]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwr66848 +[67]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt78235 \ No newline at end of file diff --git a/tests/checks/bgp_timer_policy_already_existing_check/faultDelegate_NEG.json b/tests/checks/bgp_timer_policy_already_existing_check/faultDelegate_NEG.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/tests/checks/bgp_timer_policy_already_existing_check/faultDelegate_NEG.json @@ -0,0 +1 @@ +[] diff --git a/tests/checks/bgp_timer_policy_already_existing_check/faultDelegate_POS.json b/tests/checks/bgp_timer_policy_already_existing_check/faultDelegate_POS.json new file mode 100644 index 0000000..3aa62e0 --- /dev/null +++ b/tests/checks/bgp_timer_policy_already_existing_check/faultDelegate_POS.json @@ -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" + } + } + } +] diff --git a/tests/checks/bgp_timer_policy_already_existing_check/faultDelegate_UNFORMATTED.json b/tests/checks/bgp_timer_policy_already_existing_check/faultDelegate_UNFORMATTED.json new file mode 100644 index 0000000..a451ff6 --- /dev/null +++ b/tests/checks/bgp_timer_policy_already_existing_check/faultDelegate_UNFORMATTED.json @@ -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" + } + } + } +] diff --git a/tests/checks/bgp_timer_policy_already_existing_check/test_bgp_timer_policy_already_existing_check.py b/tests/checks/bgp_timer_policy_already_existing_check/test_bgp_timer_policy_already_existing_check.py new file mode 100644 index 0000000..32029c4 --- /dev/null +++ b/tests/checks/bgp_timer_policy_already_existing_check/test_bgp_timer_policy_already_existing_check.py @@ -0,0 +1,122 @@ +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, tversion, expected_result, expected_data, expected_unformatted_data", + [ + # target release not affected (> 6.2(1g)) + ( + {faultDelegates: read_data(dir, "faultDelegate_POS.json")}, + "6.2(2a)", + script.NA, + [], + [], + ), + # target release not affected on 6.1 train (> 6.1(5e)) + ( + {faultDelegates: read_data(dir, "faultDelegate_POS.json")}, + "6.1(5f)", + script.NA, + [], + [], + ), + # boundary version is still affected for strict newer_than check + ( + {faultDelegates: read_data(dir, "faultDelegate_POS.json")}, + "6.2(1g)", + 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", + ], + ], + [], + ), + # 6.1 boundary version is still affected for strict newer_than check + ( + {faultDelegates: read_data(dir, "faultDelegate_POS.json")}, + "6.1(5e)", + 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", + ], + ], + [], + ), + # target release affected on 6.1 train (< 6.1(5e)) + ( + {faultDelegates: read_data(dir, "faultDelegate_POS.json")}, + "6.1(5a)", + 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")}, + "6.1(5a)", + 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")}, + "6.1(5a)", + script.PASS, + [], + [], + ), + ], +) +def test_logic(run_check, mock_icurl, tversion, expected_result, expected_data, expected_unformatted_data): + result = run_check(tversion=script.AciVersion(tversion)) + assert result.result == expected_result + assert result.data == expected_data + assert result.unformatted_data == expected_unformatted_data