Skip to content

Commit 39dd487

Browse files
authored
feat #264: Auto Firmware Update on Switch Discovery with 6.0.3+ (#278)
1 parent 128ab55 commit 39dd487

5 files changed

Lines changed: 233 additions & 2 deletions

File tree

aci-preupgrade-validation-script.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6026,6 +6026,29 @@ def apic_downgrade_compat_warning_check(cversion, tversion, **kwargs):
60266026
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)
60276027

60286028

6029+
@check_wrapper(check_title='Auto Firmware Update on Switch Discovery')
6030+
def auto_firmware_update_on_switch_check(cversion, tversion, **kwargs):
6031+
result = PASS
6032+
headers = ["Auto Firmware Update Status", "Default Firmware Version", "Upgrade Target Version"]
6033+
data = []
6034+
recommended_action = 'Disable Auto Firmware Update before the upgrade as a precaution. See the reference doc for details.'
6035+
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#auto-firmware-update-on-switch-discovery'
6036+
6037+
if not tversion or not cversion:
6038+
return Result(result=MANUAL, msg=TVER_MISSING)
6039+
6040+
if tversion.older_than("6.0(3a)") or (
6041+
cversion.newer_than("6.0(3a)") or (cversion.major1 == "5" and cversion.newer_than("5.2(8a)"))
6042+
):
6043+
return Result(result=NA, msg=VER_NOT_AFFECTED)
6044+
6045+
fwrepop = icurl("mo", "uni/fabric/fwrepop.json")
6046+
if fwrepop and fwrepop[0]["firmwareRepoP"]["attributes"]["enforceBootscriptVersionValidation"] == "yes":
6047+
data.append(["Enabled", fwrepop[0]["firmwareRepoP"]["attributes"]["defaultSwitchVersion"], str(tversion)])
6048+
result = MANUAL
6049+
6050+
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)
6051+
60296052
# ---- Script Execution ----
60306053

60316054

@@ -6188,6 +6211,7 @@ class CheckManager:
61886211
standby_sup_sync_check,
61896212
isis_database_byte_check,
61906213
configpush_shard_check,
6214+
auto_firmware_update_on_switch_check,
61916215

61926216
]
61936217
ssh_checks = [

docs/docs/validations.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ Items | Defect | This Script
192192
[Observer Database Size][d25] | CSCvw45531 | :white_check_mark: | :no_entry_sign:
193193
[Stale pconsRA Object][d26] | CSCwp22212 | :warning:{title="Deprecated"} | :no_entry_sign:
194194
[ISIS DTEPs Byte Size][d27] | CSCwp15375 | :white_check_mark: | :no_entry_sign:
195-
[Policydist configpushShardCont Crash][d28] | CSCwp95515 | :white_check_mark: |
195+
[Policydist configpushShardCont Crash][d28] | CSCwp95515 | :white_check_mark: | :no_entry_sign:
196+
[Auto Firmware Update on Switch Discovery][d29] | CSCwe83941 | :white_check_mark: | :no_entry_sign:
196197

197198
[d1]: #ep-announce-compatibility
198199
[d2]: #eventmgr-db-size-defect-susceptibility
@@ -222,7 +223,7 @@ Items | Defect | This Script
222223
[d26]: #stale-pconsra-object
223224
[d27]: #isis-dteps-byte-size
224225
[d28]: #policydist-configpushshardcont-crash
225-
226+
[d29]: #auto-firmware-update-on-switch-discovery
226227

227228
## General Check Details
228229

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

26482649
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.
26492650

2651+
### Auto Firmware Update on Switch Discovery
2652+
2653+
[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:
2654+
2655+
* when adding a new switch to expand the fabric
2656+
* when replacing an existing switch
2657+
* when initializing and rediscovering an existing switch
2658+
2659+
It does not activate during regular upgrades initiated through the APIC.
2660+
2661+
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.
2662+
2663+
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.
2664+
2665+
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.
2666+
2667+
!!! note
2668+
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.
2669+
26502670

26512671
[0]: https://github.com/datacenter/ACI-Pre-Upgrade-Validation-Script
26522672
[1]: https://www.cisco.com/c/dam/en/us/td/docs/Website/datacenter/apicmatrix/index.html
@@ -2710,3 +2730,5 @@ If any instances of `configpushShardCont` are flagged by this script, Cisco TAC
27102730
[59]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwp95515
27112731
[60]: https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-743951.html#Inter
27122732
[61]: https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-743951.html#EnablePolicyCompression
2733+
[62]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwe83941
2734+
[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
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[
2+
{
3+
"firmwareRepoP": {
4+
"attributes": {
5+
"annotation": "",
6+
"childAction": "",
7+
"defaultSwitchVersion": "n9000-16.0(9d)",
8+
"descr": "",
9+
"dn": "uni/fabric/fwrepop",
10+
"enforceBootscriptVersionValidation": "no",
11+
"extMngdBy": "",
12+
"lcOwn": "local",
13+
"modTs": "2025-08-13T17:50:54.830+00:00",
14+
"monPolDn": "uni/fabric/monfab-default",
15+
"name": "default",
16+
"nameAlias": "",
17+
"ownerKey": "",
18+
"ownerTag": "",
19+
"status": "",
20+
"uid": "0",
21+
"userdom": "all"
22+
}
23+
}
24+
}
25+
]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[
2+
{
3+
"firmwareRepoP": {
4+
"attributes": {
5+
"annotation": "",
6+
"childAction": "",
7+
"defaultSwitchVersion": "n9000-16.0(9d)",
8+
"descr": "",
9+
"dn": "uni/fabric/fwrepop",
10+
"enforceBootscriptVersionValidation": "yes",
11+
"extMngdBy": "",
12+
"lcOwn": "local",
13+
"modTs": "2025-08-13T17:50:54.830+00:00",
14+
"monPolDn": "uni/fabric/monfab-default",
15+
"name": "default",
16+
"nameAlias": "",
17+
"ownerKey": "",
18+
"ownerTag": "",
19+
"status": "",
20+
"uid": "0",
21+
"userdom": "all"
22+
}
23+
}
24+
}
25+
]
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import os
2+
import pytest
3+
import logging
4+
import importlib
5+
from helpers.utils import read_data
6+
7+
script = importlib.import_module("aci-preupgrade-validation-script")
8+
9+
log = logging.getLogger(__name__)
10+
dir = os.path.dirname(os.path.abspath(__file__))
11+
12+
test_function = "auto_firmware_update_on_switch_check"
13+
14+
# icurl queries
15+
auto_firmware_update_api = "uni/fabric/fwrepop.json"
16+
17+
18+
@pytest.mark.parametrize(
19+
"icurl_outputs, cversion, tversion, expected_result, expected_data",
20+
[
21+
# MANUAL cases
22+
(
23+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
24+
None,
25+
None,
26+
script.MANUAL,
27+
[],
28+
),
29+
(
30+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
31+
"5.2(7a)",
32+
None,
33+
script.MANUAL,
34+
[],
35+
),
36+
(
37+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
38+
None,
39+
"6.0(3d)",
40+
script.MANUAL,
41+
[],
42+
),
43+
# NA cases
44+
# firmwareRepoP cversion < 5.2(7) , tversion < 6.0(3) Result NA
45+
(
46+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
47+
"5.2(7a)",
48+
"6.0(2d)",
49+
script.NA,
50+
[],
51+
),
52+
# firmwareRepoP 5.2(7) < cversion < 6.0(1) , tversion < 6.0(3) Result NA
53+
(
54+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
55+
"5.3(2a)",
56+
"6.0(2d)",
57+
script.NA,
58+
[],
59+
),
60+
# firmwareRepoP 5.2(7) < cversion < 6.0(1) , tversion > 6.0(3) Result NA
61+
(
62+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
63+
"5.3(2a)",
64+
"6.0(9d)",
65+
script.NA,
66+
[],
67+
),
68+
# firmwareRepoP cversion > 6.0(3) , tversion > 6.0(3) Result NA
69+
(
70+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
71+
"6.0(3d)",
72+
"6.0(9d)",
73+
script.NA,
74+
[],
75+
),
76+
# Failure cases
77+
# firmwareRepoP cversion < 5.2(7) , tversion > 6.0(3) Result MANUAL
78+
(
79+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
80+
"5.2(7a)",
81+
"6.0(3d)",
82+
script.MANUAL,
83+
[["Enabled", "n9000-16.0(9d)", "6.0(3d)"]],
84+
),
85+
# firmwareRepoP cversion is 6.0(1) or 6.0(2) , tversion > 6.0(3) Result MANUAL
86+
(
87+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-pos.json")},
88+
"6.0(2a)",
89+
"6.0(3d)",
90+
script.MANUAL,
91+
[["Enabled", "n9000-16.0(9d)", "6.0(3d)"]],
92+
),
93+
# Pass cases
94+
# no firmwareRepoP cversion is < 5.2(7) , tversion > 6.0(3) Result PASS
95+
(
96+
{auto_firmware_update_api: []},
97+
"5.2(7a)",
98+
"6.0(3d)",
99+
script.PASS,
100+
[],
101+
),
102+
# no firmwareRepoP cversion is 6.0(1) or 6.0(2) , tversion > 6.0(3) Result PASS
103+
(
104+
{auto_firmware_update_api: []},
105+
"6.0(2a)",
106+
"6.0(3d)",
107+
script.PASS,
108+
[],
109+
),
110+
# no firmwareRepoP cversion is < 5.2(7) , tversion > 6.0(3) Result PASS
111+
(
112+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-neg.json")},
113+
"5.2(7a)",
114+
"6.0(3d)",
115+
script.PASS,
116+
[],
117+
),
118+
# no firmwareRepoP cversion is 6.0(1) or 6.0(2) , tversion > 6.0(3) Result PASS
119+
(
120+
{auto_firmware_update_api: read_data(dir, "firmwareRepoP-neg.json")},
121+
"6.0(2a)",
122+
"6.0(3d)",
123+
script.PASS,
124+
[],
125+
),
126+
],
127+
)
128+
def test_logic(run_check, mock_icurl, cversion, tversion, expected_result, expected_data):
129+
130+
result = run_check(
131+
cversion=script.AciVersion(cversion) if cversion else None,
132+
tversion=script.AciVersion(tversion) if tversion else None,
133+
)
134+
assert result.result == expected_result
135+
assert result.data == expected_data

0 commit comments

Comments
 (0)