From d8b05069e0db3db882e6f03cedb535399a6a8ea3 Mon Sep 17 00:00:00 2001 From: Mikhail Chusavitin Date: Fri, 4 Sep 2026 11:43:41 +0300 Subject: [PATCH] fix(webui): move Fan Ceiling Check to the CPU/Memory/Storage row MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is a platform cooling test (CPU + memory always; GPU only as extra heat when present), not a GPU test — so it belongs with the other whole-box checks, not in the NVIDIA section. Still Load-page only, via the new renderLoadOnlySATCard (inverse of renderCheckOnlySATCard). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_019VHG21rgTUiR1G3qFHTVmN --- audit/internal/webui/page_validate.go | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/audit/internal/webui/page_validate.go b/audit/internal/webui/page_validate.go index ac735df..4fcd370 100644 --- a/audit/internal/webui/page_validate.go +++ b/audit/internal/webui/page_validate.go @@ -114,15 +114,19 @@ func renderValidateMode(opts HandlerOptions, stressDefault bool) string { `Tests power supply transient response by pulsing all GPUs simultaneously between idle and full load. Synchronous pulses across all GPUs create worst-case PSU load spikes — running per-GPU would miss PSU-level failures.`, `dcgmi diag pulse_test`, validateFmtDur(platform.SATEstimatedNvidiaPulseTestSec)+` (all GPUs simultaneously; measured on 8-GPU system).`, - )) + - renderSATCard("fan", "Fan Ceiling Check", "runSAT('fan')", "", renderValidateCardBody( - "All system fans reported over IPMI / lm-sensors.", - `Drives CPU (+memory) and, when a GPU is present, the hottest sustained GPU load (dcgmproftester targeted-power — the Power/Thermal Fit engine) at the same time, and watches every fan until none has climbed for ~1 min. The peak RPM reached is recorded as each fan's ceiling and is what the Topology view sizes the fan tiles and duty-cycle fill against. Success once every fan plateaus (or the time cap is hit). A fan reading 0 RPM or an IPMI status of cr/nr under full load fails. IPMI polling backs off automatically if the BMC gets slow under load. If the host cannot be loaded at all, or exposes no fan sensors, the task is cancelled as "not applicable" rather than failed — the platform does not support forcing fans, so this is the closest safe equivalent.`, - `stressapptest / stress-ng + dcgmproftester -t 1004 / rvs gst; ipmitool sdr type Fan`, - `~3–10 min depending on how fast the fan curve settles (hard cap 15 min).`, )) } + // Fan Ceiling Check is a platform cooling test (CPU/memory always, GPU only + // as extra heat when present), so it sits with CPU/Memory/Storage, not in + // the NVIDIA section. Load page only. + fanCard := renderLoadOnlySATCard(stressDefault, renderSATCard("fan", "Fan Ceiling Check", "runSAT('fan')", "", renderValidateCardBody( + "All system fans reported over IPMI / lm-sensors.", + `Drives CPU (+memory) and, when a GPU is present, the hottest sustained GPU load (dcgmproftester targeted-power — the Power/Thermal Fit engine) at the same time, and watches every fan until none has climbed for ~1 min. The peak RPM reached is recorded as each fan's ceiling and is what the Topology view sizes the fan tiles and duty-cycle fill against. Success once every fan plateaus (or the time cap is hit). A fan reading 0 RPM or an IPMI status of cr/nr under full load fails. IPMI polling backs off automatically if the BMC gets slow under load. If the host cannot be loaded at all, or exposes no fan sensors, the task is cancelled as "not applicable" rather than failed — the platform does not support forcing fans, so this is the closest safe equivalent.`, + `stressapptest / stress-ng + dcgmproftester -t 1004 / rvs gst; ipmitool sdr type Fan`, + `~3–10 min depending on how fast the fan curve settles (hard cap 15 min).`, + ))) + satStressModeJS := "function satStressMode() { return false; }" if stressDefault { satStressModeJS = "function satStressMode() { return true; }" @@ -161,6 +165,7 @@ func renderValidateMode(opts HandlerOptions, stressDefault bool) string { `tpm2_getcap properties-fixed, tpm2_getcap pcrs, tpm2_pcrread, tpm2_gettestresult`, `Seconds - read-only queries; no ownership, NV, PCR, or key changes.`, ))) + + fanCard + `
@@ -621,6 +626,15 @@ func renderCheckOnlySATCard(stressMode bool, card string) string { return card } +// renderLoadOnlySATCard is the inverse of renderCheckOnlySATCard: the card +// shows only on the Load page (a sustained-load test, not a Check-tier one). +func renderLoadOnlySATCard(stressMode bool, card string) string { + if !stressMode { + return "" + } + return card +} + // renderCheck renders the non-destructive Check page (step 2). // Shows validate-mode tests only: CPU, Memory, Storage, NVIDIA L2, NCCL, NVBandwidth, AMD. // Stress-mode tests (targeted-stress, targeted-power, pulse) are on the Load page.