diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index 5e79f56..64bf2aa 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -6404,6 +6404,59 @@ def apic_storage_inode_check(**kwargs): 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="Switch RTC Battery Voltage (F2421 equipment-diags-failed)") +def rtc_battery_voltage_low_check(**kwargs): + result = FAIL_UF + headers = ['Fault', 'Pod', 'Node', 'Supervisor', 'Severity', 'Lifecycle'] + data = [] + unformatted_headers = ['Fault', 'Fault DN', 'Description', 'Severity', 'Lifecycle'] + unformatted_data = [] + recommended_action = 'Contact Cisco TAC to replace the RTC battery before upgrading or power cycling the affected switch' + doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#switch-rtc-battery-voltage' + dn_regex = node_regex + r'/.+/supslot-(?P\d+)/sup\]/fault-F2421$' + fault_reason = 'The RTC battery voltage is low' + fault_api = 'faultInst.json?query-target-filter=and(eq(faultInst.code,"F2421"),wcard(faultInst.descr,"reason:The RTC battery voltage is low"))' + + faultInsts = icurl('class', fault_api) + for faultInst in faultInsts: + attributes = faultInst['faultInst']['attributes'] + lc = attributes['lc'] + if lc not in ['raised', 'soaking']: + continue + description = attributes['descr'] + if 'reason:' not in description or description.split('reason:', 1)[1] != fault_reason: + continue + dn = re.search(dn_regex, attributes['dn']) + if dn: + data.append([ + attributes['code'], + dn.group('pod'), + dn.group('node'), + dn.group('slot'), + attributes['severity'], + lc, + ]) + else: + unformatted_data.append([ + attributes['code'], + attributes['dn'], + description, + attributes['severity'], + lc, + ]) + 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, + ) + + # Connection Based Check @check_wrapper(check_title="Multi-Pod Modular Spine Bootscript File") def multipod_modular_spine_bootscript_check(tversion, fabric_nodes, username, password, **kwargs): @@ -6816,6 +6869,7 @@ class CheckManager: equipment_disk_limits_exceeded, apic_vmm_inventory_sync_faults_check, apic_storage_inode_check, + rtc_battery_voltage_low_check, # Configurations vpc_paired_switches_check, diff --git a/docs/docs/validations.md b/docs/docs/validations.md index f788681..e2b3ab4 100644 --- a/docs/docs/validations.md +++ b/docs/docs/validations.md @@ -87,6 +87,7 @@ Items | Faults | This Script [Equipment Disk Limits][f20] | F1820: 80% -minor
F1821: -major
F1822: -critical | :white_check_mark: | :no_entry_sign: [VMM Inventory Partially Synced][f21] | F0132: comp-ctrlr-operational-issues | :white_check_mark: | :no_entry_sign: [APIC Storage Inode Usage][f22] | F4388: 75% - 85% -warning
F4389: 85% - 90% -major
F4390: 90% or more -critical | :white_check_mark: | :no_entry_sign: +[Switch RTC Battery Voltage][f23] | F2421: RTC battery voltage is low | :white_check_mark: | :no_entry_sign: [f1]: #apic-disk-space-usage [f2]: #standby-apic-disk-space-usage @@ -110,6 +111,7 @@ Items | Faults | This Script [f20]: #equipment-disk-limits [f21]: #vmm-inventory-partially-synced [f22]: #apic-storage-inode-usage +[f23]: #switch-rtc-battery-voltage ### Configuration Checks @@ -1638,8 +1640,14 @@ To recover from this fault, try the following action subject : equipment-full type : operational ``` - - + +### Switch RTC Battery Voltage + +This check detects active F2421 equipment diagnostic faults whose reason is `The RTC battery voltage is low`. The RTC battery maintains the switch system clock while the switch is powered off. If the battery voltage is low, a power cycle during an upgrade can reset the clock and prevent certificate validation, which can stop the switch from rejoining the fabric. + +The RTC battery should be replaced before upgrading or power cycling an affected switch. Contact Cisco TAC to coordinate replacement and confirm that the fault has cleared. + + ## Configuration Check Details ### VPC-paired Leaf switches @@ -2923,4 +2931,3 @@ Contact Cisco TAC for next steps. For more details, refer to the workaround in [ [74]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwm42741 [75]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt69100 [76]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt38698 - diff --git a/tests/checks/rtc_battery_voltage_low_check/f2421.json b/tests/checks/rtc_battery_voltage_low_check/f2421.json new file mode 100644 index 0000000..defb7c0 --- /dev/null +++ b/tests/checks/rtc_battery_voltage_low_check/f2421.json @@ -0,0 +1,37 @@ +{ + "totalCount": "1", + "imdata": [ + { + "faultInst": { + "attributes": { + "ack": "no", + "alert": "no", + "cause": "equipment-diags-failed", + "changeSet": "operStQual:The RTC battery voltage is low", + "childAction": "", + "code": "F2421", + "created": "2026-02-09T16:27:10.975+00:00", + "delegated": "no", + "descr": "Diagnostics test failed. reason:The RTC battery voltage is low", + "dn": "topology/pod-1/node-122/sys/diag/rule-rtc-test-trig-forever/subj-[topology/pod-1/node-122/sys/ch/supslot-1/sup]/fault-F2421", + "domain": "infra", + "extMngdBy": "", + "highestSeverity": "minor", + "lastTransition": "2026-02-09T16:27:10.975+00:00", + "lc": "raised", + "modTs": "never", + "occur": "1", + "origSeverity": "minor", + "prevSeverity": "cleared", + "rn": "fault-F2421", + "rule": "diag-rtctest-failed", + "severity": "minor", + "status": "", + "subject": "equipment-diags-failed", + "title": "", + "type": "operational" + } + } + } + ] +} diff --git a/tests/checks/rtc_battery_voltage_low_check/test_rtc_battery_voltage_low_check.py b/tests/checks/rtc_battery_voltage_low_check/test_rtc_battery_voltage_low_check.py new file mode 100644 index 0000000..17a4476 --- /dev/null +++ b/tests/checks/rtc_battery_voltage_low_check/test_rtc_battery_voltage_low_check.py @@ -0,0 +1,78 @@ +import copy +import importlib +import logging +import os + +import pytest + +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 = "rtc_battery_voltage_low_check" +faultInst_api = ( + 'faultInst.json?query-target-filter=and(eq(faultInst.code,"F2421"),' + 'wcard(faultInst.descr,"reason:The RTC battery voltage is low"))' +) +active_faults = read_data(dir, "f2421.json")["imdata"] +retaining_faults = copy.deepcopy(active_faults) +retaining_faults[0]["faultInst"]["attributes"]["lc"] = "retaining" +malformed_faults = copy.deepcopy(active_faults) +malformed_faults[0]["faultInst"]["attributes"]["dn"] = ( + "topology/pod-1/node-122/sys/diag/rule-rtc-test-trig-forever/" + "subj-[topology/pod-1/node-122/sys/ch/supslot-1/sup-bad]/fault-F2421" +) +non_exact_reason_faults = [] +for reason in [ + "The RTC battery voltage is low or unavailable", + "Warning: The RTC battery voltage is low", + "Power-on self-test failed", +]: + fault = copy.deepcopy(active_faults[0]) + fault["faultInst"]["attributes"]["descr"] = ( + "Diagnostics test failed. reason:" + reason + ) + non_exact_reason_faults.append(fault) + + +@pytest.mark.parametrize( + "icurl_outputs, expected_result, expected_data, expected_unformatted_data", + [ + ( + {faultInst_api: active_faults}, + script.FAIL_UF, + [["F2421", "1", "122", "1", "minor", "raised"]], + [], + ), + ({faultInst_api: []}, script.PASS, [], []), + ({faultInst_api: retaining_faults}, script.PASS, [], []), + ({faultInst_api: non_exact_reason_faults}, script.PASS, [], []), + ( + {faultInst_api: active_faults + malformed_faults}, + script.FAIL_UF, + [["F2421", "1", "122", "1", "minor", "raised"]], + [[ + "F2421", + "topology/pod-1/node-122/sys/diag/rule-rtc-test-trig-forever/" + "subj-[topology/pod-1/node-122/sys/ch/supslot-1/sup-bad]/fault-F2421", + "Diagnostics test failed. reason:The RTC battery voltage is low", + "minor", + "raised", + ]], + ), + ], +) +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