Add GPU serial number extraction for NVIDIA diagnostics
Parse inventory/output.log to extract GPU serial numbers from lspci output, expose them via serials API, and add GPU category to web UI. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -391,6 +391,24 @@ func (s *Server) handleGetSerials(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// GPUs
|
||||
for _, gpu := range result.Hardware.GPUs {
|
||||
if gpu.SerialNumber == "" {
|
||||
continue
|
||||
}
|
||||
model := gpu.Model
|
||||
if model == "" {
|
||||
model = "GPU"
|
||||
}
|
||||
serials = append(serials, SerialEntry{
|
||||
Component: model,
|
||||
Location: gpu.Slot,
|
||||
SerialNumber: gpu.SerialNumber,
|
||||
Manufacturer: gpu.Manufacturer,
|
||||
Category: "GPU",
|
||||
})
|
||||
}
|
||||
|
||||
// PCIe devices
|
||||
for _, pcie := range result.Hardware.PCIeDevices {
|
||||
if pcie.SerialNumber == "" {
|
||||
|
||||
132
internal/server/handlers_gpu_test.go
Normal file
132
internal/server/handlers_gpu_test.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"git.mchus.pro/mchus/logpile/internal/models"
|
||||
)
|
||||
|
||||
func TestHandleGetSerials_WithGPUs(t *testing.T) {
|
||||
// Create test server with GPU data
|
||||
srv := &Server{}
|
||||
|
||||
testResult := &models.AnalysisResult{
|
||||
Hardware: &models.HardwareConfig{
|
||||
GPUs: []models.GPU{
|
||||
{
|
||||
Slot: "GPUSXM1",
|
||||
Model: "NVIDIA Device 2335",
|
||||
Manufacturer: "NVIDIA Corporation",
|
||||
SerialNumber: "48:B0:2D:BB:8E:51:9E:E5",
|
||||
Firmware: "96.00.D0.00.03",
|
||||
BDF: "0000:3a:00.0",
|
||||
},
|
||||
{
|
||||
Slot: "GPUSXM2",
|
||||
Model: "NVIDIA Device 2335",
|
||||
Manufacturer: "NVIDIA Corporation",
|
||||
SerialNumber: "48:B0:2D:EE:DA:27:CF:78",
|
||||
Firmware: "96.00.D0.00.03",
|
||||
BDF: "0000:18:00.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
srv.SetResult(testResult)
|
||||
|
||||
// Create request
|
||||
req := httptest.NewRequest("GET", "/api/serials", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Call handler
|
||||
srv.handleGetSerials(w, req)
|
||||
|
||||
// Check response
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
// Parse response
|
||||
var serials []struct {
|
||||
Component string `json:"component"`
|
||||
Location string `json:"location,omitempty"`
|
||||
SerialNumber string `json:"serial_number"`
|
||||
Manufacturer string `json:"manufacturer,omitempty"`
|
||||
Category string `json:"category"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(w.Body).Decode(&serials); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
// Check that we have GPU entries
|
||||
gpuCount := 0
|
||||
for _, s := range serials {
|
||||
if s.Category == "GPU" {
|
||||
gpuCount++
|
||||
t.Logf("Found GPU: %s (%s) S/N: %s", s.Component, s.Location, s.SerialNumber)
|
||||
|
||||
// Verify fields are set
|
||||
if s.SerialNumber == "" {
|
||||
t.Errorf("GPU serial number is empty")
|
||||
}
|
||||
if s.Location == "" {
|
||||
t.Errorf("GPU location is empty")
|
||||
}
|
||||
if s.Manufacturer == "" {
|
||||
t.Errorf("GPU manufacturer is empty")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if gpuCount != 2 {
|
||||
t.Errorf("Expected 2 GPUs in serials, got %d", gpuCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGetSerials_WithoutGPUSerials(t *testing.T) {
|
||||
// Create test server with GPUs but no serial numbers
|
||||
srv := &Server{}
|
||||
|
||||
testResult := &models.AnalysisResult{
|
||||
Hardware: &models.HardwareConfig{
|
||||
GPUs: []models.GPU{
|
||||
{
|
||||
Slot: "GPU0",
|
||||
Model: "Some GPU",
|
||||
Manufacturer: "Vendor",
|
||||
SerialNumber: "", // No serial number
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
srv.SetResult(testResult)
|
||||
|
||||
// Create request
|
||||
req := httptest.NewRequest("GET", "/api/serials", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Call handler
|
||||
srv.handleGetSerials(w, req)
|
||||
|
||||
// Parse response
|
||||
var serials []struct {
|
||||
Category string `json:"category"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(w.Body).Decode(&serials); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
// Check that GPUs without serial numbers are not included
|
||||
for _, s := range serials {
|
||||
if s.Category == "GPU" {
|
||||
t.Error("GPU without serial number should not be included in serials list")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user