fix: complete hardware collection diagnostics
This commit is contained in:
@@ -43,6 +43,9 @@ func (s *System) RunNvidiaConfigCheckPack(ctx context.Context, baseDir string, l
|
||||
if settings, err := s.ListNvidiaGPUSettings(); err != nil {
|
||||
status.Notes = append(status.Notes, "nvidia-smi GPU settings unavailable (no driver, or no GPU present): "+err.Error())
|
||||
} else {
|
||||
if len(settings) == 0 {
|
||||
status.NoGPUs = true
|
||||
}
|
||||
for _, g := range settings {
|
||||
f := NvidiaGPUConfigFinding{
|
||||
Index: g.Index, Name: g.Name,
|
||||
@@ -127,6 +130,7 @@ type NvidiaConfigCheckStatus struct {
|
||||
|
||||
GPUs []NvidiaGPUConfigFinding `json:"gpus,omitempty"`
|
||||
NVLinkPairs []NvidiaNVLinkPairFinding `json:"nvlink_pairs,omitempty"`
|
||||
NoGPUs bool `json:"no_gpus"`
|
||||
|
||||
// Confidential Computing readiness — informational only, does not gate
|
||||
// overall_status: an unconfigured/NOT_READY CC state is a
|
||||
@@ -466,7 +470,10 @@ func renderNvidiaConfigCheckSummary(status NvidiaConfigCheckStatus) string {
|
||||
fmt.Fprintf(&b, "cc_state=%s\n", status.CCState)
|
||||
fmt.Fprintf(&b, "cpu_cc_capability=%s\n", status.CPUCCCapability)
|
||||
fmt.Fprintf(&b, "gpu_cc_capability=%s\n", status.GPUCCCapability)
|
||||
if len(status.Warnings) == 0 {
|
||||
if status.NoGPUs {
|
||||
fmt.Fprintln(&b, "overall_status=UNSUPPORTED")
|
||||
fmt.Fprintln(&b, "reason=no_nvidia_gpus_detected")
|
||||
} else if len(status.Warnings) == 0 {
|
||||
fmt.Fprintln(&b, "overall_status=OK")
|
||||
} else {
|
||||
fmt.Fprintln(&b, "overall_status=FAILED")
|
||||
|
||||
@@ -165,6 +165,10 @@ func TestRenderNvidiaConfigCheckSummaryOverallStatus(t *testing.T) {
|
||||
if got := renderNvidiaConfigCheckSummary(withWarning); !strings.Contains(got, "overall_status=FAILED") {
|
||||
t.Fatalf("status with warnings missing overall_status=FAILED:\n%s", got)
|
||||
}
|
||||
noGPU := NvidiaConfigCheckStatus{NoGPUs: true}
|
||||
if got := renderNvidiaConfigCheckSummary(noGPU); !strings.Contains(got, "overall_status=UNSUPPORTED") {
|
||||
t.Fatalf("no-GPU summary must be unsupported:\n%s", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRenderNvidiaConfigCheckSummaryIncludesWarningsField guards that a
|
||||
|
||||
@@ -920,6 +920,12 @@ func (s *System) RunStorageAcceptancePack(ctx context.Context, baseDir string, e
|
||||
runSyncBracketHook(job, "after", logFunc)
|
||||
}
|
||||
status, rc := classifySATResult(job.name, out, err)
|
||||
// A zero smartctl exit status only proves the command ran. If the
|
||||
// drive did not return its overall-health verdict, it must not turn
|
||||
// the storage SAT green.
|
||||
if job.name == "smartctl-health" && status == "OK" && !hasSMARTOverallHealth(out) {
|
||||
status = "UNSUPPORTED"
|
||||
}
|
||||
stats.Add(status)
|
||||
key := filepath.Base(devPath) + "_" + strings.ReplaceAll(job.name, "-", "_")
|
||||
fmt.Fprintf(&summary, "%s_rc=%d\n", key, rc)
|
||||
@@ -1560,6 +1566,11 @@ func classifySATResult(name string, out []byte, err error) (string, int) {
|
||||
return "FAILED", rc
|
||||
}
|
||||
|
||||
func hasSMARTOverallHealth(out []byte) bool {
|
||||
m := smartHealthRE.FindStringSubmatch(string(out))
|
||||
return len(m) > 1 && strings.TrimSpace(m[1]) != ""
|
||||
}
|
||||
|
||||
func runSATCommand(verboseLog, name string, cmd []string, logFunc func(string)) ([]byte, error) {
|
||||
start := time.Now().UTC()
|
||||
resolvedCmd, err := resolveSATCommand(cmd)
|
||||
|
||||
@@ -124,6 +124,7 @@ func writeNVMeReport(b *strings.Builder, outputs map[string][]byte) {
|
||||
writtenBytes: writtenBytes,
|
||||
readBytes: readBytes,
|
||||
capacityBytes: capacityBytes,
|
||||
healthKnown: true,
|
||||
}
|
||||
writeResourceSection(b, ri)
|
||||
|
||||
@@ -232,6 +233,7 @@ func writeSATAReport(b *strings.Builder, outputs map[string][]byte) {
|
||||
capacityBytes: capacityBytes,
|
||||
readPercent: 100 - readValue,
|
||||
hasReadPercent: hasReadValue,
|
||||
healthKnown: !strings.EqualFold(health, "unknown"),
|
||||
}
|
||||
writeResourceSection(b, ri)
|
||||
|
||||
@@ -341,6 +343,7 @@ const (
|
||||
|
||||
type resourceInfo struct {
|
||||
powerOnHours uint64
|
||||
healthKnown bool
|
||||
powerCycles uint64
|
||||
writtenBytes uint64
|
||||
readBytes uint64
|
||||
@@ -393,7 +396,10 @@ func writeConclusionSection(b *strings.Builder, r resourceInfo) {
|
||||
writeSectionHeader(b, "Conclusion")
|
||||
|
||||
var reasons, notes []string
|
||||
isNew := true
|
||||
isNew := r.healthKnown
|
||||
if !r.healthKnown {
|
||||
notes = append(notes, "SMART overall health unavailable — disk cannot be accepted as NEW")
|
||||
}
|
||||
|
||||
if r.capacityBytes > 0 {
|
||||
writtenFrac := float64(r.writtenBytes) / float64(r.capacityBytes)
|
||||
@@ -424,7 +430,9 @@ func writeConclusionSection(b *strings.Builder, r resourceInfo) {
|
||||
reasons = append(reasons, fmt.Sprintf("power cycles %s", formatUint(r.powerCycles)))
|
||||
}
|
||||
|
||||
if isNew {
|
||||
if !r.healthKnown {
|
||||
writeField(b, "Disk Condition", "UNVERIFIED")
|
||||
} else if isNew {
|
||||
writeField(b, "Disk Condition", "NEW")
|
||||
} else {
|
||||
writeField(b, "Disk Condition", "USED")
|
||||
|
||||
@@ -141,6 +141,15 @@ func TestGenerateDiskReportSATA(t *testing.T) {
|
||||
assertContains(t, report, "Power_On_Hours")
|
||||
}
|
||||
|
||||
func TestHasSMARTOverallHealth(t *testing.T) {
|
||||
if !hasSMARTOverallHealth([]byte("SMART overall-health self-assessment test result: PASSED\n")) {
|
||||
t.Fatal("expected SMART health verdict to be recognized")
|
||||
}
|
||||
if hasSMARTOverallHealth([]byte("SMART support is: Available\n")) {
|
||||
t.Fatal("availability alone must not be accepted as a health verdict")
|
||||
}
|
||||
}
|
||||
|
||||
func assertContains(t *testing.T, text string, needles ...string) {
|
||||
t.Helper()
|
||||
for _, needle := range needles {
|
||||
|
||||
Reference in New Issue
Block a user