diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index 1cb18e5..27b2449 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -5300,6 +5300,29 @@ def apic_database_size_check(cversion, **kwargs): result = FAIL_UF 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_ondiscovery_check(cversion, tversion, **kwargs): + result = NA + headers = ["Auto firmware Policy Dn", "Switch enforced Version"] + data = [] + recommended_action = 'Disable the Auto Firmware Update on Switch Discovery policy before upgrade' + doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#auto-firmware-update-on-switch-discovery' + + if not tversion or not cversion: + return Result(result=MANUAL, msg=TVER_MISSING) + if (cversion.older_than("5.2(7a)") and tversion.newer_than("6.0(3a)")) or (cversion.older_than("6.0(3a)") and tversion.newer_than("6.0(3a)")): + result = PASS + auto_firmware_update_api = 'firmwareRepoP.json' + auto_firmware_update_api += '?query-target-filter=eq(firmwareRepoP.enforceBootscriptVersionValidation,"true")' + auto_firmware_update = icurl('class', auto_firmware_update_api) + if auto_firmware_update: + data.append([auto_firmware_update[0]["firmwareRepoP"]["attributes"]["dn"], auto_firmware_update[0]["firmwareRepoP"]["attributes"]["defaultSwitchVersion"]]) + if data: + result = FAIL_O + + return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) + # ---- Script Execution ---- def parse_args(args): @@ -5455,6 +5478,7 @@ def get_checks(api_only, debug_function): standby_sup_sync_check, stale_pcons_ra_mo_check, isis_database_byte_check, + auto_firmware_update_ondiscovery_check, ] conn_checks = [ diff --git a/docs/docs/validations.md b/docs/docs/validations.md index e46a881..511e893 100644 --- a/docs/docs/validations.md +++ b/docs/docs/validations.md @@ -188,7 +188,7 @@ Items | Defect | This Script [Observer Database Size][d25] | CSCvw45531 | :white_check_mark: | :no_entry_sign: |:no_entry_sign: [Stale pconsRA Object][d26] | CSCwp22212 | :white_check_mark: | :no_entry_sign: |:no_entry_sign: [ISIS DTEPs Byte Size][d27] | CSCwp15375 | :white_check_mark: | :no_entry_sign: |:no_entry_sign: - +[Auto Firmware Update on Switch Discovery][d28] | CSCwe83941 | :white_check_mark: | :no_entry_sign: |:no_entry_sign: [d1]: #ep-announce-compatibility [d2]: #eventmgr-db-size-defect-susceptibility @@ -217,7 +217,7 @@ Items | Defect | This Script [d25]: #observer-database-size [d26]: #stale-pconsra-object [d27]: #isis-dteps-byte-size - +[d28]: #auto-firmware-update-on-switch-discovery ## General Check Details @@ -2588,6 +2588,14 @@ Do not upgrade to any affected ACI software release if this check fails. !!! note Nexus Dashboard Insights (NDI) integration can cause ACI tech support generation to happen automatically as part of the bug scan feature. +### Auto Firmware Update on Switch Discovery + +Due to [CSCwe83941][59] if 'Auto Firmware Update on Switch Discovery' is enabled with the target release of 16.0(3) and later, a new switch commissioned to ACI Fabric could fail discovery. + +The download of firmware image fails, causing the switch to become "soft-brick" , the switch needs to be recovered. + +Do not upgrade with 'Auto Firmware Update on Switch Discovery' enabled, to avoid this escenario. + [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 @@ -2648,3 +2656,4 @@ Do not upgrade to any affected ACI software release if this check fails. [56]: https://www.cisco.com/c/en/us/td/docs/dcn/whitepapers/cisco-aci-virtual-edge-migration.html [57]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp22212 [58]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp15375 +[59]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwe83941 \ No newline at end of file diff --git a/tests/auto_firmware_update_ondiscovery_check/firmwareRepoP-pos.json b/tests/auto_firmware_update_ondiscovery_check/firmwareRepoP-pos.json new file mode 100644 index 0000000..06e58bb --- /dev/null +++ b/tests/auto_firmware_update_ondiscovery_check/firmwareRepoP-pos.json @@ -0,0 +1,25 @@ +[ + { + "firmwareRepoP": { + "attributes": { + "annotation": "", + "childAction": "", + "defaultSwitchVersion": "n9000-16.0(9d)", + "descr": "", + "dn": "uni/fabric/fwrepop", + "enforceBootscriptVersionValidation": "yes", + "extMngdBy": "", + "lcOwn": "local", + "modTs": "2025-08-13T17:50:54.830+00:00", + "monPolDn": "uni/fabric/monfab-default", + "name": "default", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "", + "status": "", + "uid": "0", + "userdom": "all" + } + } + } +] \ No newline at end of file diff --git a/tests/auto_firmware_update_ondiscovery_check/test_auto_firmware_update_ondiscovery_check.py b/tests/auto_firmware_update_ondiscovery_check/test_auto_firmware_update_ondiscovery_check.py new file mode 100644 index 0000000..825305c --- /dev/null +++ b/tests/auto_firmware_update_ondiscovery_check/test_auto_firmware_update_ondiscovery_check.py @@ -0,0 +1,82 @@ +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__)) + +# icurl queries +auto_firmware_update_api = 'firmwareRepoP.json' +auto_firmware_update_api += '?query-target-filter=eq(firmwareRepoP.enforceBootscriptVersionValidation,"true")' + +@pytest.mark.parametrize( + "icurl_outputs, cversion, tversion, expected_result", + [ + + # MANUAL cases + ( + {auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")}, + None, None, + script.MANUAL, + ), + ( + {auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")}, + "5.2(7a)", None, + script.MANUAL, + ), + ( + {auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")}, + None, "6.0(3d)", + script.MANUAL, + ), + # NA cases + # firmwareRepoP cversion < 5.2(7) , tversion < 6.0(3) Result NA + ( + {auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")}, + "5.2(7a)", "6.0(2d)", + script.NA, + ), + # firmwareRepoP cversion > 6.0(3) , tversion > 6.0(3) Result NA + ( + {auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")}, + "6.0(3a)", "6.0(9d)", + script.NA, + ), + # Failure cases + # firmwareRepoP cversion < 5.2(7) , tversion > 6.0(3) Result FAIL_O + ( + {auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")}, + "5.2(7a)", "6.0(3d)", + script.FAIL_O, + ), + # firmwareRepoP cversion is < 6.0(2) , tversion > 6.0(3) Result FAIL_O + ( + {auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")}, + "6.0(2a)", "6.0(3d)", + script.FAIL_O, + ), + # Pass cases + # no firmwareRepoP cversion is < 5.2(7) , tversion > 6.0(3) Result PASS + ( + {auto_firmware_update_api: []}, + "5.2(7a)", "6.0(3d)", + script.PASS, + ), + # no firmwareRepoP cversion is < 6.0(2) , tversion > 6.0(3) Result PASS + ( + {auto_firmware_update_api: []}, + "5.2(7a)", "6.0(3d)", + script.PASS, + ), + ] +) +def test_logic(mock_icurl, cversion, tversion, expected_result): + tversion = script.AciVersion(tversion) if tversion else None + cversion = script.AciVersion(cversion) if cversion else None + + result = script.auto_firmware_update_ondiscovery_check(1, 1, cversion, tversion) + assert result == expected_result \ No newline at end of file