From 02e44b1172c7e48789b04482b6c5ae7d2c412c90 Mon Sep 17 00:00:00 2001 From: Michael Chus Date: Sun, 12 Apr 2026 22:46:42 +0300 Subject: [PATCH] Fix USB/RAM status checks; add server model+S/N to dashboard; remove cycles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit USB Export Drive: lsblk reports TRAN only for whole disks, not partitions (/dev/sdc1). Strip trailing partition digits to get parent disk before transport check. LiveCD in RAM: When RunInstallToRAM copies squashfs to /dev/shm/bee-live/ but bind-mount of /run/live/medium fails (CD-ROM boots), /run/live/medium still shows the CD-ROM fstype. Add fallback: if /dev/shm/bee-live/*.squashfs exists, the data is in RAM — report status OK. Dashboard Hardware Summary: Show server Manufacturer + ProductName as heading and S/N as subline above the component table, sourced from hw.Board (dmidecode system-type data). Validate: Remove Cycles input — always run once. cycles=1 hardcoded in runAllSAT(). Co-Authored-By: Claude Sonnet 4.6 --- audit/internal/platform/install_to_ram.go | 10 +- audit/internal/platform/runtime.go | 10 +- audit/internal/webui/pages.go | 168 ++++++++++++++++------ 3 files changed, 141 insertions(+), 47 deletions(-) diff --git a/audit/internal/platform/install_to_ram.go b/audit/internal/platform/install_to_ram.go index c6a7bd0..4f6b399 100644 --- a/audit/internal/platform/install_to_ram.go +++ b/audit/internal/platform/install_to_ram.go @@ -14,9 +14,17 @@ import ( func (s *System) IsLiveMediaInRAM() bool { fsType := mountFSType("/run/live/medium") if fsType == "" { + // No medium mount at all — fall back to toram kernel parameter. return toramActive() } - return strings.EqualFold(fsType, "tmpfs") + if strings.EqualFold(fsType, "tmpfs") { + return true + } + // When RunInstallToRAM copies squashfs to /dev/shm/bee-live but the bind + // mount of /run/live/medium fails (common for CD-ROM boots), the medium + // fstype still shows the CD-ROM type. Check whether the RAM copy exists. + files, _ := filepath.Glob("/dev/shm/bee-live/*.squashfs") + return len(files) > 0 } func (s *System) LiveBootSource() LiveBootSource { diff --git a/audit/internal/platform/runtime.go b/audit/internal/platform/runtime.go index 546f064..538869f 100644 --- a/audit/internal/platform/runtime.go +++ b/audit/internal/platform/runtime.go @@ -244,11 +244,17 @@ func findUSBExportMount() string { if readOnly { continue } - // Check USB transport via lsblk on the device + // Check USB transport via lsblk on the device (or its parent disk for partitions). if !strings.HasPrefix(device, "/dev/") { continue } - if blockDeviceTransport(device) == "usb" { + checkDev := device + // lsblk only reports TRAN for the whole disk, not for partitions (e.g. /dev/sdc1). + // Strip trailing partition digits to get the parent disk name. + if trimmed := strings.TrimRight(device, "0123456789"); trimmed != device && len(trimmed) > len("/dev/") { + checkDev = trimmed + } + if blockDeviceTransport(checkDev) == "usb" { return mountPoint } } diff --git a/audit/internal/webui/pages.go b/audit/internal/webui/pages.go index b2f7d08..ddd437b 100644 --- a/audit/internal/webui/pages.go +++ b/audit/internal/webui/pages.go @@ -330,6 +330,33 @@ func renderHardwareSummaryCard(opts HandlerOptions) string { var b strings.Builder b.WriteString(`
Hardware Summary
`) + + // Server identity block above the component table. + { + var model, serial string + parts := []string{} + if hw.Board.Manufacturer != nil && strings.TrimSpace(*hw.Board.Manufacturer) != "" { + parts = append(parts, strings.TrimSpace(*hw.Board.Manufacturer)) + } + if hw.Board.ProductName != nil && strings.TrimSpace(*hw.Board.ProductName) != "" { + parts = append(parts, strings.TrimSpace(*hw.Board.ProductName)) + } + if len(parts) > 0 { + model = strings.Join(parts, " ") + } + serial = strings.TrimSpace(hw.Board.SerialNumber) + if model != "" || serial != "" { + b.WriteString(`
`) + if model != "" { + fmt.Fprintf(&b, `
%s
`, html.EscapeString(model)) + } + if serial != "" { + fmt.Fprintf(&b, `
S/N: %s
`, html.EscapeString(serial)) + } + b.WriteString(`
`) + } + } + b.WriteString(``) writeRow := func(label, value, badgeHTML string) { b.WriteString(fmt.Sprintf(``, @@ -1279,9 +1306,6 @@ func renderValidate(opts HandlerOptions) string {
Validate Profile
-
-
-
@@ -1331,12 +1355,6 @@ func renderValidate(opts HandlerOptions) string {

Loading NVIDIA GPUs...

Select at least one NVIDIA GPU to enable NVIDIA validate tasks.

-
- -
@@ -1455,10 +1473,6 @@ function satSelectedGPUIndices() { .filter(function(v) { return !Number.isNaN(v); }) .sort(function(a, b) { return a - b; }); } -function satMultiGPUAll() { - const cb = document.getElementById('sat-multi-gpu-all'); - return cb ? cb.checked : true; -} function satUpdateGPUSelectionNote() { const note = document.getElementById('sat-gpu-selection-note'); if (!note) return; @@ -1467,8 +1481,7 @@ function satUpdateGPUSelectionNote() { note.textContent = 'Select at least one NVIDIA GPU to enable NVIDIA validate tasks.'; return; } - const multiAll = satMultiGPUAll(); - note.textContent = 'Selected GPUs: ' + selected.join(', ') + '. Multi-GPU tests: ' + (multiAll ? 'all GPUs in system' : 'selected GPUs only') + '.'; + note.textContent = 'Selected GPUs: ' + selected.join(', ') + '. Multi-GPU tests will use all selected GPUs.'; } function satRenderGPUList(gpus) { const root = document.getElementById('sat-gpu-list'); @@ -1582,15 +1595,8 @@ const nvidiaPerGPUTargets = ['nvidia', 'nvidia-targeted-stress', 'nvidia-targete // pulse_test and fabric tests run on all selected GPUs simultaneously const nvidiaAllGPUTargets = ['nvidia-pulse', 'nvidia-interconnect', 'nvidia-bandwidth']; function satAllGPUIndicesForMulti() { - // If "Multi-GPU tests — all GPUs" is checked, return all detected GPUs. - // Otherwise fall back to the per-GPU selection. - if (satMultiGPUAll()) { - return loadSatNvidiaGPUs().then(function(gpus) { - return gpus.map(function(g) { return Number(g.index); }); - }); - } - const sel = satSelectedGPUIndices(); - return Promise.resolve(sel); + // Multi-GPU tests always use the current GPU selection. + return Promise.resolve(satSelectedGPUIndices()); } function expandSATTarget(target) { if (nvidiaAllGPUTargets.indexOf(target) >= 0) { @@ -1680,7 +1686,7 @@ function runAMDValidateSet() { return runNext(0); } function runAllSAT() { - const cycles = Math.max(1, parseInt(document.getElementById('sat-cycles').value)||1); + const cycles = 1; const status = document.getElementById('sat-all-status'); status.textContent = 'Enqueuing...'; const stressOnlyTargets = ['nvidia-targeted-stress', 'nvidia-targeted-power', 'nvidia-pulse', 'nvidia-interconnect', 'nvidia-bandwidth']; @@ -1967,12 +1973,16 @@ func renderBenchmark(opts HandlerOptions) string { - '; }).join(''); + benchmarkApplyMultiGPUState(gpus.length); + benchmarkUpdateSelectionNote(); +} + +// Disable radio options that require multiple GPUs when only one is present. +function benchmarkApplyMultiGPUState(gpuCount) { + var multiValues = ['parallel', 'ramp-up']; + var radios = document.querySelectorAll('input[name="benchmark-mode"]'); + radios.forEach(function(el) { + var isMulti = multiValues.indexOf(el.value) >= 0; + if (gpuCount < 2 && isMulti) { + el.disabled = true; + if (el.checked) { + // fall back to sequential + var seq = document.querySelector('input[name="benchmark-mode"][value="sequential"]'); + if (seq) seq.checked = true; + } + var label = el.closest('label'); + if (label) label.style.opacity = '0.4'; + } else { + el.disabled = false; + // restore default: ramp-up checked when ≥2 GPUs + if (gpuCount >= 2 && el.value === 'ramp-up') el.checked = true; + var label = el.closest('label'); + if (label) label.style.opacity = ''; + } + }); benchmarkUpdateSelectionNote(); } @@ -2094,8 +2138,9 @@ function runNvidiaBenchmark() { return; } if (benchmarkES) { benchmarkES.close(); benchmarkES = null; } - const rampUp = selected.length > 1 && !!document.getElementById('benchmark-ramp-up').checked; - const parallelGPUs = !rampUp && !!document.getElementById('benchmark-parallel-gpus').checked; + const mode = benchmarkMode(); + const rampUp = mode === 'ramp-up' && selected.length > 1; + const parallelGPUs = mode === 'parallel'; const body = { profile: document.getElementById('benchmark-profile').value || 'standard', gpu_indices: selected, @@ -2371,10 +2416,20 @@ func renderBurn() string {

Loading NVIDIA GPUs...

Select at least one NVIDIA GPU to enable NVIDIA burn recipes.

- +
+ + + +
@@ -2450,9 +2505,30 @@ function burnSelectedGPUIndices() { .sort(function(a, b) { return a - b; }); } -function burnUseNvidiaRampUp() { - const el = document.getElementById('burn-stagger-nvidia'); - return !!(el && el.checked); +function burnNvidiaMode() { + const el = document.querySelector('input[name="burn-nvidia-mode"]:checked'); + return el ? el.value : 'sequential'; +} + +function burnApplyMultiGPUState(gpuCount) { + var multiValues = ['parallel', 'ramp-up']; + var radios = document.querySelectorAll('input[name="burn-nvidia-mode"]'); + radios.forEach(function(el) { + var isMulti = multiValues.indexOf(el.value) >= 0; + if (gpuCount < 2 && isMulti) { + el.disabled = true; + if (el.checked) { + var seq = document.querySelector('input[name="burn-nvidia-mode"][value="sequential"]'); + if (seq) seq.checked = true; + } + var label = el.closest('label'); + if (label) label.style.opacity = '0.4'; + } else { + el.disabled = false; + var label = el.closest('label'); + if (label) label.style.opacity = ''; + } + }); } function burnUpdateSelectionNote() { @@ -2479,6 +2555,7 @@ function burnRenderGPUList(gpus) { + 'GPU ' + gpu.index + ' — ' + gpu.name + mem + '' + ''; }).join(''); + burnApplyMultiGPUState(gpus.length); burnUpdateSelectionNote(); } @@ -2514,8 +2591,11 @@ function enqueueBurnTask(target, label, extra, useSelectedNvidia) { return Promise.reject(new Error('Select at least one NVIDIA GPU.')); } body.gpu_indices = selected; - if (burnUseNvidiaRampUp() && selected.length > 1) { + const bMode = burnNvidiaMode(); + if (bMode === 'ramp-up' && selected.length > 1) { body.stagger_gpu_start = true; + } else if (bMode === 'parallel' && selected.length > 1) { + body.parallel_gpus = true; } } return fetch('/api/sat/' + target + '/run', {
%s%s%s