From 5b39edbd5f28890e4e966a4022c53072b9a40191 Mon Sep 17 00:00:00 2001 From: Alexandra Bara Date: Mon, 6 Jul 2026 20:27:53 -0500 Subject: [PATCH] update for CPU str in dmesg log --- nodescraper/plugins/inband/dmesg/mce_utils.py | 25 +++++++++---- test/unit/plugin/test_dmesg_analyzer.py | 20 +++++++++++ test/unit/plugin/test_mce_utils.py | 36 +++++++++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/nodescraper/plugins/inband/dmesg/mce_utils.py b/nodescraper/plugins/inband/dmesg/mce_utils.py index 4519e6e2..9d2b0692 100644 --- a/nodescraper/plugins/inband/dmesg/mce_utils.py +++ b/nodescraper/plugins/inband/dmesg/mce_utils.py @@ -30,7 +30,7 @@ _CORRECTABLE_SUMMARY_RE = re.compile( r"(?P\d+)\s+correctable hardware errors detected in total in (?P\w+) block" - r"(?:\s+on\s+(?PCPU\d+))?", + r"(?:\s+on\s+(?PCPU:?\d+))?", re.IGNORECASE, ) @@ -52,16 +52,20 @@ ) _MCE_CE_STATUS_RE = re.compile( - r"\[Hardware Error\]:.*?(?PCPU\d+).*?MC\d+_STATUS\[[^\]]*\|CE\|[^\]]*\]", + r"\[Hardware Error\]:.*?(?PCPU:?\d+).*?MC\d+_STATUS\[[^\]]*\|CE\|[^\]]*\]", re.IGNORECASE, ) _MCE_UC_STATUS_RE = re.compile( - r"\[Hardware Error\]:.*?(?PCPU\d+).*?MC\d+_STATUS\[[^\]]*\|UC\|[^\]]*\]", + r"\[Hardware Error\]:.*?(?PCPU:?\d+).*?MC\d+_STATUS\[[^\]]*\|UC\|[^\]]*\]", re.IGNORECASE, ) +def _normalize_cpu_label(cpu: str) -> str: + return cpu.replace(":", "") + + def _add_count(counts: dict[str, int], part: str, amount: int) -> None: counts[part] = counts.get(part, 0) + amount @@ -120,8 +124,9 @@ def parse_correctable_mce_counts( summary_match = _CORRECTABLE_SUMMARY_RE.search(line) if summary_match: + cpu = summary_match.group("cpu") part = _part_label( - cpu=summary_match.group("cpu"), + cpu=_normalize_cpu_label(cpu) if cpu else None, block=summary_match.group("block"), ) _add_count(counts, part, int(summary_match.group("count"))) @@ -132,7 +137,11 @@ def parse_correctable_mce_counts( bank = extract_mce_bank_from_line(line) if bank is not None and bank in ignored: continue - part = status_match.group("cpu") if status_match.group("cpu") else "unknown" + part = ( + _normalize_cpu_label(status_match.group("cpu")) + if status_match.group("cpu") + else "unknown" + ) _add_count(counts, part, 1) return counts @@ -170,7 +179,11 @@ def parse_uncorrectable_mce_counts( bank = extract_mce_bank_from_line(line) if bank is not None and bank in ignored: continue - part = status_match.group("cpu") if status_match.group("cpu") else "unknown" + part = ( + _normalize_cpu_label(status_match.group("cpu")) + if status_match.group("cpu") + else "unknown" + ) _add_count(counts, part, 1) return counts diff --git a/test/unit/plugin/test_dmesg_analyzer.py b/test/unit/plugin/test_dmesg_analyzer.py index 41911dbf..d4697f9e 100644 --- a/test/unit/plugin/test_dmesg_analyzer.py +++ b/test/unit/plugin/test_dmesg_analyzer.py @@ -1043,6 +1043,26 @@ def test_mce_threshold_raises_error_for_gpu(system_info): assert res.status == ExecutionStatus.ERROR +def test_mce_threshold_raises_error_for_cpu_colon_status(system_info): + dmesg_content = ( + "kern :err : 2038-01-19T00:00:00,000000+00:00 " + "[Hardware Error]: CPU:72 (00:00:0) MC60_STATUS[-|CE|Misc]: 0xabc\n" + ) + + analyzer = DmesgAnalyzer(system_info=system_info) + res = analyzer.analyze_data( + DmesgData(dmesg_content=dmesg_content), + args=DmesgAnalyzerArgs(check_unknown_dmesg_errors=False, mce_threshold=1), + ) + + threshold_events = [e for e in res.events if e.data.get("mce_threshold") == 1] + assert len(threshold_events) == 1 + assert threshold_events[0].priority == EventPriority.ERROR + assert threshold_events[0].data["part"] == "CPU72" + assert threshold_events[0].data["correctable_mce_count"] == 1 + assert res.status == ExecutionStatus.ERROR + + def test_mce_threshold_not_triggered_below_limit(system_info): dmesg_content = ( "kern :warn : 2024-06-11T14:30:00,123456+00:00 " diff --git a/test/unit/plugin/test_mce_utils.py b/test/unit/plugin/test_mce_utils.py index b9ed5e4a..c8fd20f2 100644 --- a/test/unit/plugin/test_mce_utils.py +++ b/test/unit/plugin/test_mce_utils.py @@ -76,3 +76,39 @@ def test_parse_correctable_mce_counts_skips_ignored_banks(): counts = parse_correctable_mce_counts(content, ignore_banks=frozenset({1, 2})) assert counts == {"CPU0": 1} + + +def test_parse_correctable_mce_counts_cpu_colon_status(): + content = ( + "kern :err : 2038-01-19T00:00:00,000000+00:00 " + "[Hardware Error]: CPU:72 (00:00:0) MC60_STATUS[-|CE|Misc]: 0xabc\n" + ) + + counts = parse_correctable_mce_counts(content) + + assert counts == {"CPU72": 1} + + +def test_parse_correctable_mce_counts_both_cpu_formats(): + content = ( + "[Hardware Error]: CPU0 MC1_STATUS[0x0|CE|]: 0x1\n" + "kern :err : 2038-01-19T00:00:00,000000+00:00 " + "[Hardware Error]: CPU:72 (00:00:0) MC60_STATUS[-|CE|Misc]: 0xabc\n" + "kern :warn : 2024-06-11T14:30:00,123456+00:00 " + "mce: 2 correctable hardware errors detected in total in mc0 block on CPU:1\n" + ) + + counts = parse_correctable_mce_counts(content) + + assert counts == {"CPU0": 1, "CPU72": 1, "CPU1/mc0": 2} + + +def test_parse_uncorrectable_mce_counts_cpu_colon_status(): + content = ( + "kern :err : 2038-01-19T00:00:01,000000+00:00 " + "[Hardware Error]: CPU:72 (00:00:0) MC60_STATUS[-|UC|Misc]: 0xdef\n" + ) + + counts = parse_uncorrectable_mce_counts(content) + + assert counts == {"CPU72": 1}