webui/collector: strip ANSI escapes in nvidia-smi topo parsing, auto-prepare drives for RAID mirror creation
- nvidia-smi underlines the topo -m header with ANSI CSI codes even when writing to a file; both NVLink matrix parsers failed to find the header and /topo showed "No NVLink-bonded GPU pairs found" on bonded systems. - raid-lsi-create-mirror failed with "resources already in use" (exit 11) on JBOD drives; the task now reads drive states and auto-converts JBOD/UBad (set good force), releases hotspares, refuses Frgn/Onln with actionable messages, and dumps preservedcache info when add vd fails. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,11 @@ import (
|
||||
|
||||
var nv5re = regexp.MustCompile(`(?i)^NV(\d+)$`)
|
||||
|
||||
// nvidia-smi underlines the topo -m header row with ANSI CSI sequences
|
||||
// (ESC[4m...ESC[0m) even when stdout is not a TTY, so "GPU0" is not at the
|
||||
// start of the trimmed header line unless they are stripped first.
|
||||
var nvANSIRe = regexp.MustCompile("\x1b\\[[0-9;]*[A-Za-z]")
|
||||
|
||||
// isNVLinkBridgeCandidate returns true for Mellanox PCIe devices that look like
|
||||
// NVLink bridge mezzanine cards: narrow link (x2), no host net interfaces.
|
||||
// These are the CPU-side PCIe control plane of the NVSwitch fabric on HGX/DGX systems.
|
||||
@@ -163,7 +168,7 @@ func locateGPUTopologyColumns(lines []string) (headerIdx int, gpuColIndices []in
|
||||
// (NIC, CPU) which are ignored. Only GPU×GPU cells containing NV# values are
|
||||
// counted. X is self; non-NV tokens (NODE, SYS, PHB, PIX) are skipped.
|
||||
func parseNVIDIATopologyMatrix(raw string) nvlinkTopoResult {
|
||||
lines := strings.Split(raw, "\n")
|
||||
lines := strings.Split(nvANSIRe.ReplaceAllString(raw, ""), "\n")
|
||||
headerIdx, gpuColIndices, gpuCount := locateGPUTopologyColumns(lines)
|
||||
if headerIdx < 0 {
|
||||
return nvlinkTopoResult{}
|
||||
|
||||
@@ -71,6 +71,25 @@ GPU1 NV0 X
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNVIDIATopologyMatrixANSIUnderlinedHeader(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// nvidia-smi underlines the header row with ANSI escapes even when stdout
|
||||
// is not a TTY: the header line starts with ESC[4m, not "GPU0".
|
||||
input := "\x1b[4m\tGPU0\tGPU1\tNIC0\tCPU Affinity\x1b[0m\n" +
|
||||
"GPU0\t X \tNV18\tNODE\t0-31,64-95\n" +
|
||||
"GPU1\tNV18\t X \tNODE\t0-31,64-95\n" +
|
||||
"NIC0\tNODE\tNODE\t X \n"
|
||||
|
||||
got := parseNVIDIATopologyMatrix(input)
|
||||
if got.GPUCount != 2 {
|
||||
t.Fatalf("GPUCount=%d want 2", got.GPUCount)
|
||||
}
|
||||
if !got.AllActive || got.MinNVLinks != 18 {
|
||||
t.Fatalf("AllActive=%v MinNVLinks=%d want true/18", got.AllActive, got.MinNVLinks)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNVIDIATopologyMatrixEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user