Skip to content
Merged
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
24 changes: 24 additions & 0 deletions aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -6026,6 +6026,29 @@ 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='Auto Firmware Update on Switch Discovery')
def auto_firmware_update_on_switch_check(cversion, tversion, **kwargs):
result = PASS
headers = ["Auto Firmware Update Status", "Default Firmware Version", "Upgrade Target Version"]
data = []
recommended_action = 'Disable Auto Firmware Update before the upgrade as a precaution. See the reference doc for details.'
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 tversion.older_than("6.0(3a)") or (
cversion.newer_than("6.0(3a)") or (cversion.major1 == "5" and cversion.newer_than("5.2(8a)"))
):
return Result(result=NA, msg=VER_NOT_AFFECTED)

fwrepop = icurl("mo", "uni/fabric/fwrepop.json")
if fwrepop and fwrepop[0]["firmwareRepoP"]["attributes"]["enforceBootscriptVersionValidation"] == "yes":
data.append(["Enabled", fwrepop[0]["firmwareRepoP"]["attributes"]["defaultSwitchVersion"], str(tversion)])
result = MANUAL

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

# ---- Script Execution ----


Expand Down Expand Up @@ -6188,6 +6211,7 @@ class CheckManager:
standby_sup_sync_check,
isis_database_byte_check,
configpush_shard_check,
auto_firmware_update_on_switch_check,

]
ssh_checks = [
Expand Down
26 changes: 24 additions & 2 deletions docs/docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ Items | Defect | This Script
[Observer Database Size][d25] | CSCvw45531 | :white_check_mark: | :no_entry_sign:
[Stale pconsRA Object][d26] | CSCwp22212 | :warning:{title="Deprecated"} | :no_entry_sign:
[ISIS DTEPs Byte Size][d27] | CSCwp15375 | :white_check_mark: | :no_entry_sign:
[Policydist configpushShardCont Crash][d28] | CSCwp95515 | :white_check_mark: |
[Policydist configpushShardCont Crash][d28] | CSCwp95515 | :white_check_mark: | :no_entry_sign:
[Auto Firmware Update on Switch Discovery][d29] | CSCwe83941 | :white_check_mark: | :no_entry_sign:

[d1]: #ep-announce-compatibility
[d2]: #eventmgr-db-size-defect-susceptibility
Expand Down Expand Up @@ -222,7 +223,7 @@ Items | Defect | This Script
[d26]: #stale-pconsra-object
[d27]: #isis-dteps-byte-size
[d28]: #policydist-configpushshardcont-crash

[d29]: #auto-firmware-update-on-switch-discovery

## General Check Details

Expand Down Expand Up @@ -2647,6 +2648,25 @@ Due to [CSCwp95515][59], upgrading to an affected version while having any `conf

If any instances of `configpushShardCont` are flagged by this script, Cisco TAC must be contacted to identify and resolve the underlying issue before performing the upgrade.

### Auto Firmware Update on Switch Discovery

[Auto Firmware Update on Switch Discovery][63] automatically upgrades a new switch to the target firmware version before registering it to the ACI fabric. This feature activates in three scenarios:

* when adding a new switch to expand the fabric
* when replacing an existing switch
* when initializing and rediscovering an existing switch

It does not activate during regular upgrades initiated through the APIC.

Due to [CSCwe83941][62], if a new switch is running 6.0(1), 6.0(2) or any version older than 5.2(8), attempting to upgrade it to 6.0(3)+ using Auto Firmware Update will fail. The switch will become unusable until a manual recovery procedure is performed directly on the device.

While this issue does not occur during standard upgrades, it is important to be aware of the risk when your target version is 6.0(3) or newer and the switch is running 6.0(1), 6.0(2), or a version older than 5.2(8). Auto Firmware Update may get triggered and hit this issue during switch replacement in an upgrade window or if you need to re-initialize a switch after a failed upgrade.

To avoid this risk, consider disabling Auto Firmware Update before upgrading to 6.0(3)+ if any switches are running the affected older versions. In the future, ensure that any new switch is running a compatible version before re-enabling Auto Firmware Update and registering it to the fabric.

!!! note
This issue occurs because older switch firmware versions are not compatible with switch images 6.0(3) or newer. The APIC version is not a factor.


[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
Expand Down Expand Up @@ -2710,3 +2730,5 @@ If any instances of `configpushShardCont` are flagged by this script, Cisco TAC
[59]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp95515
[60]: https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-743951.html#Inter
[61]: https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-743951.html#EnablePolicyCompression
[62]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwe83941
[63]: https://www.cisco.com/c/en/us/td/docs/dcn/aci/apic/all/apic-installation-aci-upgrade-downgrade/Cisco-APIC-Installation-ACI-Upgrade-Downgrade-Guide/m-auto-firmware-update.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"firmwareRepoP": {
"attributes": {
"annotation": "",
"childAction": "",
"defaultSwitchVersion": "n9000-16.0(9d)",
"descr": "",
"dn": "uni/fabric/fwrepop",
"enforceBootscriptVersionValidation": "no",
"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"
}
}
}
]
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
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 = "auto_firmware_update_on_switch_check"

# icurl queries
auto_firmware_update_api = "uni/fabric/fwrepop.json"


@pytest.mark.parametrize(
"icurl_outputs, cversion, tversion, expected_result, expected_data",
[
# 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 5.2(7) < cversion < 6.0(1) , tversion < 6.0(3) Result NA
(
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
"5.3(2a)",
"6.0(2d)",
script.NA,
[],
),
# firmwareRepoP 5.2(7) < cversion < 6.0(1) , tversion > 6.0(3) Result NA
(
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
"5.3(2a)",
"6.0(9d)",
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(3d)",
"6.0(9d)",
script.NA,
[],
),
# Failure cases
# firmwareRepoP cversion < 5.2(7) , tversion > 6.0(3) Result MANUAL
(
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
"5.2(7a)",
"6.0(3d)",
script.MANUAL,
[["Enabled", "n9000-16.0(9d)", "6.0(3d)"]],
),
# firmwareRepoP cversion is 6.0(1) or 6.0(2) , tversion > 6.0(3) Result MANUAL
(
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
"6.0(2a)",
"6.0(3d)",
script.MANUAL,
[["Enabled", "n9000-16.0(9d)", "6.0(3d)"]],
),
# 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(1) or 6.0(2) , tversion > 6.0(3) Result PASS
(
{auto_firmware_update_api: []},
"6.0(2a)",
"6.0(3d)",
script.PASS,
[],
),
# no firmwareRepoP cversion is < 5.2(7) , tversion > 6.0(3) Result PASS
(
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-neg.json")},
"5.2(7a)",
"6.0(3d)",
script.PASS,
[],
),
# no firmwareRepoP cversion is 6.0(1) or 6.0(2) , tversion > 6.0(3) Result PASS
(
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-neg.json")},
"6.0(2a)",
"6.0(3d)",
script.PASS,
[],
),
],
)
def test_logic(run_check, mock_icurl, cversion, tversion, expected_result, expected_data):

result = run_check(
cversion=script.AciVersion(cversion) if cversion else None,
tversion=script.AciVersion(tversion) if tversion else None,
)
assert result.result == expected_result
assert result.data == expected_data