package dell import ( "archive/zip" "bytes" "testing" "git.mchus.pro/mchus/logpile/internal/parser" ) func TestDetectNestedTSRZip(t *testing.T) { inner := makeZipArchive(t, map[string][]byte{ "tsr/metadata.json": []byte(`{"Make":"Dell Inc.","Model":"PowerEdge R750","ServiceTag":"G37Q064"}`), "tsr/hardware/sysinfo/inventory/sysinfo_DCIM_View.xml": []byte(``), }) p := &Parser{} score := p.Detect([]parser.ExtractedFile{ {Path: "signature", Content: []byte("ok")}, {Path: "TSR20241119143901_G37Q064.pl.zip", Content: inner}, }) if score < 80 { t.Fatalf("expected high detect score for nested TSR zip, got %d", score) } } func TestParseNestedTSRZip(t *testing.T) { const viewXML = ` Dell Inc. PowerEdge R750 G37Q064 2.19.1 7.00.30.00 CPU.Socket.1 Intel(R) Xeon(R) Gold 6330 Intel 28 56 2000 3100 ABCD NIC.Slot.1-1-1 Broadcom 57414 Dual Port 10/25GbE SFP28 Adapter Broadcom 00:11:22:33:44:55 NICSERIAL1 22.80.17 0x14e4 0x16d7 PSU.Slot.1 D1400E-S0 Dell PSUSERIAL1 00.1A 1400 Video.Slot.38-1 NVIDIA H100 PCIe GH100 [H100 PCIe] NVIDIA Corporation 10DE 2331 74 0 0 1793924039808 96.00.AF.00.01 bc681a6d4785dde08c21f49c46c05cc3 ` const swXML = ` NIC.Slot.1-1-1 22.80.17 Network ` const eventsXML = ` SYS1001 Link is down NIC.Slot.1-1-1 ` const cimSensorXML = ` Video.Slot.38-1 290 440 295 5 PS1 Voltage 1 224.0 5 0 5 ` inner := makeZipArchive(t, map[string][]byte{ "tsr/metadata.json": []byte(`{ "Make":"Dell Inc.", "Model":"PowerEdge R750", "ServiceTag":"G37Q064", "FirmwareVersion":"7.00.30.00", "CollectionDateTime":"2024-11-19 14:39:01.000-0800" }`), "tsr/hardware/sysinfo/inventory/sysinfo_DCIM_View.xml": []byte(viewXML), "tsr/hardware/sysinfo/inventory/sysinfo_DCIM_SoftwareIdentity.xml": []byte(swXML), "tsr/hardware/sysinfo/inventory/sysinfo_CIM_Sensor.xml": []byte(cimSensorXML), "tsr/hardware/sysinfo/lcfiles/curr_lclog.xml": []byte(eventsXML), }) p := &Parser{} result, err := p.Parse([]parser.ExtractedFile{ {Path: "signature", Content: []byte("ok")}, {Path: "TSR20241119143901_G37Q064.pl.zip", Content: inner}, }) if err != nil { t.Fatalf("parse failed: %v", err) } if result.Hardware == nil { t.Fatalf("expected hardware section") } if got := result.Hardware.BoardInfo.Manufacturer; got != "Dell Inc." { t.Fatalf("unexpected board manufacturer: %q", got) } if got := result.Hardware.BoardInfo.ProductName; got != "PowerEdge R750" { t.Fatalf("unexpected board product: %q", got) } if got := result.Hardware.BoardInfo.SerialNumber; got != "G37Q064" { t.Fatalf("unexpected service tag: %q", got) } if len(result.Hardware.CPUs) != 1 { t.Fatalf("expected 1 cpu, got %d", len(result.Hardware.CPUs)) } if got := result.Hardware.CPUs[0].Model; got != "Intel(R) Xeon(R) Gold 6330" { t.Fatalf("unexpected cpu model: %q", got) } if len(result.Hardware.NetworkAdapters) != 1 { t.Fatalf("expected 1 network adapter, got %d", len(result.Hardware.NetworkAdapters)) } adapter := result.Hardware.NetworkAdapters[0] if adapter.Vendor != "Broadcom" { t.Fatalf("unexpected nic vendor: %q", adapter.Vendor) } if adapter.Firmware != "22.80.17" { t.Fatalf("unexpected nic firmware: %q", adapter.Firmware) } if adapter.SerialNumber != "NICSERIAL1" { t.Fatalf("unexpected nic serial: %q", adapter.SerialNumber) } if len(result.Hardware.PowerSupply) != 1 { t.Fatalf("expected 1 psu, got %d", len(result.Hardware.PowerSupply)) } psu := result.Hardware.PowerSupply[0] if psu.Model != "D1400E-S0" { t.Fatalf("unexpected psu model: %q", psu.Model) } if psu.Firmware != "00.1A" { t.Fatalf("unexpected psu firmware: %q", psu.Firmware) } if len(result.Hardware.Firmware) == 0 { t.Fatalf("expected firmware entries") } if len(result.Hardware.GPUs) != 1 { t.Fatalf("expected 1 gpu, got %d", len(result.Hardware.GPUs)) } if got := result.Hardware.GPUs[0].Model; got != "NVIDIA H100 PCIe" { t.Fatalf("unexpected gpu model: %q", got) } if got := result.Hardware.GPUs[0].SerialNumber; got != "1793924039808" { t.Fatalf("unexpected gpu serial: %q", got) } if got := result.Hardware.GPUs[0].Temperature; got != 29 { t.Fatalf("unexpected gpu temperature: %d", got) } if len(result.Sensors) == 0 { t.Fatalf("expected sensors from CIM_Sensor") } if len(result.Events) != 1 { t.Fatalf("expected one lifecycle event, got %d", len(result.Events)) } if got := string(result.Events[0].Severity); got != "warning" { t.Fatalf("unexpected event severity: %q", got) } } // TestParseDellPhysicalDiskEndurance verifies that RemainingRatedWriteEndurance from // DCIM_PhysicalDiskView is parsed into Storage.RemainingEndurancePct. func TestParseDellPhysicalDiskEndurance(t *testing.T) { const viewXML = ` Dell Inc. PowerEdge R6625 8VS2LG4 Disk.Bay.0:Enclosure.Internal.0-1:RAID.SL.3-1 0 HFS480G3H2X069N ESEAN5254I030B26B 479559942144 Solid State Drive SATA DZ03 100100 % 1OK Disk.Bay.1:Enclosure.Internal.0-1:RAID.SL.3-1 1 TOSHIBA MG08ADA800E X1G0A0YXFVVG 8001563222016 Hard Disk Drive SAS 0104 ` inner := makeZipArchive(t, map[string][]byte{ "tsr/metadata.json": []byte(`{"Make":"Dell Inc.","Model":"PowerEdge R6625","ServiceTag":"8VS2LG4"}`), "tsr/hardware/sysinfo/inventory/sysinfo_DCIM_View.xml": []byte(viewXML), }) p := &Parser{} result, err := p.Parse([]parser.ExtractedFile{ {Path: "signature", Content: []byte("ok")}, {Path: "TSR20260306141852_8VS2LG4.pl.zip", Content: inner}, }) if err != nil { t.Fatalf("parse failed: %v", err) } if len(result.Hardware.Storage) != 2 { t.Fatalf("expected 2 storage devices, got %d", len(result.Hardware.Storage)) } ssd := result.Hardware.Storage[0] if ssd.RemainingEndurancePct == nil { t.Fatalf("SSD slot 0: expected RemainingEndurancePct to be set") } if *ssd.RemainingEndurancePct != 100 { t.Errorf("SSD slot 0: expected RemainingEndurancePct=100, got %d", *ssd.RemainingEndurancePct) } hdd := result.Hardware.Storage[1] if hdd.RemainingEndurancePct != nil { t.Errorf("HDD slot 1: expected RemainingEndurancePct absent, got %d", *hdd.RemainingEndurancePct) } } // TestParseDellInfiniBandView verifies that DCIM_InfiniBandView entries are parsed as // NetworkAdapters (not PCIe devices) and that the corresponding SoftwareIdentity firmware // entry with FQDD "InfiniBand.Slot.*" does not leak into hardware.firmware. // // Regression guard: PowerEdge R6625 (8VS2LG4) — "Mellanox Network Adapter" version // "20.39.35.60" appeared in hardware.firmware because DCIM_InfiniBandView was ignored // (device ended up only in PCIeDevices with model "16x or x16") and SoftwareIdentity // FQDD "InfiniBand.Slot.1-1" was not filtered. (2026-03-15) func TestParseDellInfiniBandView(t *testing.T) { const viewXML = ` Dell Inc. PowerEdge R6625 8VS2LG4 InfiniBand.Slot.1-1 InfiniBand in Slot 1 Port 1 00:1C:FD:D7:5A:E6 20.39.35.60 14.32.17 15B3 101B 0 InfiniBand.Slot.1-1 MT28908 Family [ConnectX-6] InfiniBand in Slot 1 Port 1 Mellanox Technologies 15B3 101B 16x or x16 RAID.SL.3-1 PERC H755 Front 52.30.0-6115 0 ` const swXML = ` Mellanox Network Adapter - 00:1C:FD:D7:5A:E6 InfiniBand.Slot.1-1 20.39.35.60 PERC H755 Front RAID.SL.3-1 52.30.0-6115 BIOS BIOS.Setup.1-1 1.15.3 ` inner := makeZipArchive(t, map[string][]byte{ "tsr/metadata.json": []byte(`{"Make":"Dell Inc.","Model":"PowerEdge R6625","ServiceTag":"8VS2LG4"}`), "tsr/hardware/sysinfo/inventory/sysinfo_DCIM_View.xml": []byte(viewXML), "tsr/hardware/sysinfo/inventory/sysinfo_DCIM_SoftwareIdentity.xml": []byte(swXML), }) p := &Parser{} result, err := p.Parse([]parser.ExtractedFile{ {Path: "signature", Content: []byte("ok")}, {Path: "TSR20260306141852_8VS2LG4.pl.zip", Content: inner}, }) if err != nil { t.Fatalf("parse failed: %v", err) } // InfiniBand adapter must appear as a NetworkAdapter, not a PCIe device. if len(result.Hardware.NetworkAdapters) != 1 { t.Fatalf("expected 1 network adapter, got %d", len(result.Hardware.NetworkAdapters)) } nic := result.Hardware.NetworkAdapters[0] if nic.Slot != "InfiniBand.Slot.1-1" { t.Errorf("unexpected NIC slot: %q", nic.Slot) } if nic.Firmware != "20.39.35.60" { t.Errorf("unexpected NIC firmware: %q", nic.Firmware) } if len(nic.MACAddresses) == 0 || nic.MACAddresses[0] != "00:1C:FD:D7:5A:E6" { t.Errorf("unexpected NIC MAC: %v", nic.MACAddresses) } // pci.ids enrichment: VendorID=0x15B3, DeviceID=0x101B → chip model + vendor name. if nic.Model != "MT28908 Family [ConnectX-6]" { t.Errorf("NIC model = %q, want MT28908 Family [ConnectX-6] (from pci.ids)", nic.Model) } if nic.Vendor != "Mellanox Technologies" { t.Errorf("NIC vendor = %q, want Mellanox Technologies (from pci.ids)", nic.Vendor) } // InfiniBand FQDD must NOT appear in PCIe devices. for _, pcie := range result.Hardware.PCIeDevices { if pcie.Slot == "InfiniBand.Slot.1-1" { t.Errorf("InfiniBand.Slot.1-1 must not appear in PCIeDevices") } } // Firmware entries from SoftwareIdentity and parseControllerView must carry the FQDD // as their Description so the exporter's isDeviceBoundFirmwareFQDD filter can remove them. fqddByName := make(map[string]string) for _, fw := range result.Hardware.Firmware { fqddByName[fw.DeviceName] = fw.Description } if desc := fqddByName["Mellanox Network Adapter"]; desc != "InfiniBand.Slot.1-1" { t.Errorf("Mellanox firmware Description = %q, want InfiniBand.Slot.1-1 for FQDD filter", desc) } if desc := fqddByName["PERC H755 Front"]; desc != "RAID.SL.3-1" { t.Errorf("PERC H755 Front firmware Description = %q, want RAID.SL.3-1 for FQDD filter", desc) } } // TestParseDellCPUAffinity verifies that CPUAffinity is parsed into NUMANode for // NIC, PCIe, and controller views. "Not Applicable" must result in NUMANode=0. func TestParseDellCPUAffinity(t *testing.T) { const viewXML = ` Dell Inc. PowerEdge R750 TESTST1 NIC.Slot.2-1-1 Some NIC 1 0 InfiniBand.Slot.1-1 InfiniBand in Slot 1 2 0 RAID.Slot.1-1 PERC H755 Not Applicable 0 Slot.7-1 Some PCIe Card 2 0 ` inner := makeZipArchive(t, map[string][]byte{ "tsr/metadata.json": []byte(`{"Make":"Dell Inc.","Model":"PowerEdge R750","ServiceTag":"TESTST1"}`), "tsr/hardware/sysinfo/inventory/sysinfo_DCIM_View.xml": []byte(viewXML), }) p := &Parser{} result, err := p.Parse([]parser.ExtractedFile{ {Path: "signature", Content: []byte("ok")}, {Path: "TSR_TESTST1.pl.zip", Content: inner}, }) if err != nil { t.Fatalf("parse failed: %v", err) } // NIC CPUAffinity=1 → NUMANode=1 nicBySlot := make(map[string]int) for _, nic := range result.Hardware.NetworkAdapters { nicBySlot[nic.Slot] = nic.NUMANode } if nicBySlot["NIC.Slot.2-1-1"] != 1 { t.Errorf("NIC.Slot.2-1-1 NUMANode = %d, want 1", nicBySlot["NIC.Slot.2-1-1"]) } if nicBySlot["InfiniBand.Slot.1-1"] != 2 { t.Errorf("InfiniBand.Slot.1-1 NUMANode = %d, want 2", nicBySlot["InfiniBand.Slot.1-1"]) } // PCIe device CPUAffinity=2 → NUMANode=2; controller CPUAffinity="Not Applicable" → NUMANode=0 pcieBySlot := make(map[string]int) for _, pcie := range result.Hardware.PCIeDevices { pcieBySlot[pcie.Slot] = pcie.NUMANode } if pcieBySlot["Slot.7-1"] != 2 { t.Errorf("Slot.7-1 NUMANode = %d, want 2", pcieBySlot["Slot.7-1"]) } if pcieBySlot["RAID.Slot.1-1"] != 0 { t.Errorf("RAID.Slot.1-1 NUMANode = %d, want 0 (Not Applicable)", pcieBySlot["RAID.Slot.1-1"]) } } func makeZipArchive(t *testing.T, files map[string][]byte) []byte { t.Helper() var buf bytes.Buffer zw := zip.NewWriter(&buf) for name, content := range files { w, err := zw.Create(name) if err != nil { t.Fatalf("create zip entry %s: %v", name, err) } if _, err := w.Write(content); err != nil { t.Fatalf("write zip entry %s: %v", name, err) } } if err := zw.Close(); err != nil { t.Fatalf("close zip: %v", err) } return buf.Bytes() }