A CPU that had thermally throttled (sysfs thermal_throttle counter > 0) still reported status "OK" everywhere: dmidecode-derived CPU status only distinguishes populated/enabled/disabled and never looked at the throttle flag the collector already recorded next to it, and neither SAT path meant to catch this actually could: - The routine "cpu" SAT pack (RunCPUAcceptancePack) only checked lscpu/sensors/stress-ng exit codes — stress-ng exits 0 whether or not the CPU throttled while running it, so an 89°C/throttled CPU right after a "successful" run still showed cpu:all as OK in component-status.json. - The more thorough platform-stress test already detected throttling and fan-spindown correctly, but wrote its verdict as "Overall: FAIL — ..." with no "=", which parseSATKV can't parse — so even a real detected throttle event never reached the component-status DB. Fixes: - cpu_telemetry.go: escalate a CPU's status to Warning (only-escalate, same severity ranking already used elsewhere) when Throttled is set. - sat.go: add a before/after thermal-throttle-counter check job around the "cpu" pack's stress-ng run, so a throttle event during the run fails that job and (via the existing FAILED->Warning DB mapping) flips cpu:all to Warning. - platform_stress.go: emit a machine-readable overall_status= line alongside the human-readable verdict so platform-stress results actually reach ApplySATResultToDB. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
98 lines
3.1 KiB
Go
98 lines
3.1 KiB
Go
package collector
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"bee/audit/internal/schema"
|
|
)
|
|
|
|
func TestEnrichCPUsWithTelemetry(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
oldBase := cpuSysBaseDir
|
|
cpuSysBaseDir = tmp
|
|
t.Cleanup(func() { cpuSysBaseDir = oldBase })
|
|
|
|
mustWriteFile(t, filepath.Join(tmp, "cpu0", "topology", "physical_package_id"), "0\n")
|
|
mustWriteFile(t, filepath.Join(tmp, "cpu0", "thermal_throttle", "package_throttle_count"), "3\n")
|
|
mustWriteFile(t, filepath.Join(tmp, "cpu1", "topology", "physical_package_id"), "1\n")
|
|
mustWriteFile(t, filepath.Join(tmp, "cpu1", "thermal_throttle", "package_throttle_count"), "0\n")
|
|
|
|
doc := sensorsDoc{
|
|
"coretemp-isa-0000": {
|
|
"Package id 0": map[string]any{"temp1_input": 61.5},
|
|
"Package id 1": map[string]any{"temp2_input": 58.0},
|
|
},
|
|
"intel-rapl-mmio-0": {
|
|
"Package id 0": map[string]any{"power1_average": 180.0},
|
|
"Package id 1": map[string]any{"power2_average": 175.0},
|
|
},
|
|
}
|
|
|
|
socket0 := 0
|
|
socket1 := 1
|
|
status := statusOK
|
|
cpus := []schema.HardwareCPU{
|
|
{Socket: &socket0, HardwareComponentStatus: schema.HardwareComponentStatus{Status: &status}},
|
|
{Socket: &socket1, HardwareComponentStatus: schema.HardwareComponentStatus{Status: &status}},
|
|
}
|
|
|
|
got := enrichCPUsWithTelemetry(cpus, doc)
|
|
|
|
if got[0].TemperatureC == nil || *got[0].TemperatureC != 61.5 {
|
|
t.Fatalf("cpu0 temperature mismatch: %#v", got[0].TemperatureC)
|
|
}
|
|
if got[0].PowerW == nil || *got[0].PowerW != 180.0 {
|
|
t.Fatalf("cpu0 power mismatch: %#v", got[0].PowerW)
|
|
}
|
|
if got[0].Throttled == nil || !*got[0].Throttled {
|
|
t.Fatalf("cpu0 throttled mismatch: %#v", got[0].Throttled)
|
|
}
|
|
if got[0].Status == nil || *got[0].Status != statusWarning {
|
|
t.Fatalf("cpu0 status not escalated to Warning on throttle: %#v", got[0].Status)
|
|
}
|
|
if got[0].ErrorDescription == nil || *got[0].ErrorDescription == "" {
|
|
t.Fatalf("cpu0 error description not set on throttle")
|
|
}
|
|
if got[1].TemperatureC == nil || *got[1].TemperatureC != 58.0 {
|
|
t.Fatalf("cpu1 temperature mismatch: %#v", got[1].TemperatureC)
|
|
}
|
|
if got[1].PowerW == nil || *got[1].PowerW != 175.0 {
|
|
t.Fatalf("cpu1 power mismatch: %#v", got[1].PowerW)
|
|
}
|
|
if got[1].Throttled != nil && *got[1].Throttled {
|
|
t.Fatalf("cpu1 throttled mismatch: %#v", got[1].Throttled)
|
|
}
|
|
if got[1].Status == nil || *got[1].Status != statusOK {
|
|
t.Fatalf("cpu1 status should remain OK when not throttled: %#v", got[1].Status)
|
|
}
|
|
}
|
|
|
|
func TestEscalateCPUThrottleStatusDoesNotDowngradeCritical(t *testing.T) {
|
|
status := statusCritical
|
|
desc := "pre-existing critical finding"
|
|
cpu := schema.HardwareCPU{
|
|
HardwareComponentStatus: schema.HardwareComponentStatus{Status: &status, ErrorDescription: &desc},
|
|
}
|
|
|
|
escalateCPUThrottleStatus(&cpu)
|
|
|
|
if *cpu.Status != statusCritical {
|
|
t.Fatalf("status downgraded from Critical: %#v", cpu.Status)
|
|
}
|
|
if *cpu.ErrorDescription != desc {
|
|
t.Fatalf("error description overwritten: %#v", cpu.ErrorDescription)
|
|
}
|
|
}
|
|
|
|
func mustWriteFile(t *testing.T, path, content string) {
|
|
t.Helper()
|
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
|
t.Fatalf("mkdir %s: %v", path, err)
|
|
}
|
|
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
t.Fatalf("write %s: %v", path, err)
|
|
}
|
|
}
|