From 3053cb071095439ea0b2c436c2015de288f42c4b Mon Sep 17 00:00:00 2001 From: Michael Chus Date: Sun, 19 Apr 2026 19:03:02 +0300 Subject: [PATCH] Fix PSU slot regex: match MSI underscore format PSU1_POWER_IN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit \b does not fire between a digit and '_' because '_' is \w in RE2. The pattern \bpsu?\s*([0-9]+)\b never matched PSU1_POWER_IN style sensors, so parsePSUSDR (and PSUSlotsFromSDR / samplePSUPower) returned empty results for MSI servers — causing all power graphs to fall back to DCMI which reports ~half actual draw. Added an explicit underscore-terminated pattern first in the list and tests covering the MSI format. Co-Authored-By: Claude Sonnet 4.6 --- audit/internal/collector/psu.go | 3 +++ audit/internal/collector/psu_sdr_test.go | 29 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/audit/internal/collector/psu.go b/audit/internal/collector/psu.go index 280f2ba..8b9cf86 100644 --- a/audit/internal/collector/psu.go +++ b/audit/internal/collector/psu.go @@ -160,6 +160,9 @@ type psuSDR struct { } var psuSlotPatterns = []*regexp.Regexp{ + // MSI/underscore style: PSU1_POWER_IN, PSU2_POWER_OUT — underscore is \w so \b + // does not fire after the digit; match explicitly with underscore terminator. + regexp.MustCompile(`(?i)\bpsu([0-9]+)_`), regexp.MustCompile(`(?i)\bpsu?\s*([0-9]+)\b`), // PSU1, PS1, ps 2 regexp.MustCompile(`(?i)\bps\s*([0-9]+)\b`), // PS 6, PS6 regexp.MustCompile(`(?i)\bpws\s*([0-9]+)\b`), // PWS1 diff --git a/audit/internal/collector/psu_sdr_test.go b/audit/internal/collector/psu_sdr_test.go index fbd5919..b24d43e 100644 --- a/audit/internal/collector/psu_sdr_test.go +++ b/audit/internal/collector/psu_sdr_test.go @@ -49,6 +49,10 @@ func TestParsePSUSlotVendorVariants(t *testing.T) { {name: "PWS1 Status", want: 1}, {name: "Power Supply Bay 8", want: 8}, {name: "PS 6 Input Power", want: 6}, + // MSI underscore format — \b does not fire between digit and '_' + {name: "PSU1_POWER_IN", want: 1}, + {name: "PSU2_POWER_OUT", want: 2}, + {name: "PSU4_STATUS", want: 4}, } for _, tt := range tests { @@ -59,6 +63,31 @@ func TestParsePSUSlotVendorVariants(t *testing.T) { } } +func TestParsePSUSDRMSIFormat(t *testing.T) { + t.Parallel() + raw := ` +PSU1_STATUS | F1h | ok +PSU1_POWER_OUT | 928 Watts | ok +PSU1_POWER_IN | 976 Watts | ok +PSU2_STATUS | F2h | ok +PSU2_POWER_OUT | 944 Watts | ok +PSU2_POWER_IN | 992 Watts | ok +` + got := parsePSUSDR(raw) + if len(got) != 2 { + t.Fatalf("len(got)=%d want 2", len(got)) + } + if got[1].inputPowerW == nil || *got[1].inputPowerW != 976 { + t.Fatalf("psu1 input power=%v want 976", got[1].inputPowerW) + } + if got[1].outputPowerW == nil || *got[1].outputPowerW != 928 { + t.Fatalf("psu1 output power=%v want 928", got[1].outputPowerW) + } + if got[2].inputPowerW == nil || *got[2].inputPowerW != 992 { + t.Fatalf("psu2 input power=%v want 992", got[2].inputPowerW) + } +} + func TestSynthesizePSUsFromSDR(t *testing.T) { t.Parallel()