- Dell NICView: strip " - XX:XX:XX:XX:XX:XX" suffix from ProductName (Dell TSR embeds MAC in this field for every NIC port) - Dell SoftwareIdentity: same strip applied to ElementName; store FQDD in FirmwareInfo.Description so exporter can filter device-bound entries - Exporter: add isDeviceBoundFirmwareFQDD() to filter firmware entries whose Description matches NIC./PSU./Disk./RAID.Backplane./GPU. FQDD prefixes (prevents device firmware from appearing in hardware.firmware) - Exporter: extend isDeviceBoundFirmwareName() to filter HGX GPU/NVSwitch firmware inventory IDs (_fw_gpu_, _fw_nvswitch_, _inforom_gpu_) - Inspur: remove HDD firmware from Hardware.Firmware — already present in Storage.Firmware, duplicating it violates ADL-016 - bible-local/06-parsers.md: document firmware and MAC stripping rules - bible-local/10-decisions.md: add ADL-016 (device-bound firmware) and ADL-017 (vendor-embedded MAC in model name fields) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
225 lines
8.9 KiB
Go
225 lines
8.9 KiB
Go
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(`<CIM><MESSAGE><SIMPLEREQ/></MESSAGE></CIM>`),
|
|
})
|
|
|
|
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 = `<CIM><MESSAGE><SIMPLEREQ>
|
|
<VALUE.NAMEDINSTANCE><INSTANCE CLASSNAME="DCIM_SystemView">
|
|
<PROPERTY NAME="Manufacturer"><VALUE>Dell Inc.</VALUE></PROPERTY>
|
|
<PROPERTY NAME="Model"><VALUE>PowerEdge R750</VALUE></PROPERTY>
|
|
<PROPERTY NAME="ServiceTag"><VALUE>G37Q064</VALUE></PROPERTY>
|
|
<PROPERTY NAME="BIOSVersionString"><VALUE>2.19.1</VALUE></PROPERTY>
|
|
<PROPERTY NAME="LifecycleControllerVersion"><VALUE>7.00.30.00</VALUE></PROPERTY>
|
|
</INSTANCE></VALUE.NAMEDINSTANCE>
|
|
<VALUE.NAMEDINSTANCE><INSTANCE CLASSNAME="DCIM_CPUView">
|
|
<PROPERTY NAME="FQDD"><VALUE>CPU.Socket.1</VALUE></PROPERTY>
|
|
<PROPERTY NAME="Model"><VALUE>Intel(R) Xeon(R) Gold 6330</VALUE></PROPERTY>
|
|
<PROPERTY NAME="Manufacturer"><VALUE>Intel</VALUE></PROPERTY>
|
|
<PROPERTY NAME="NumberOfEnabledCores"><VALUE>28</VALUE></PROPERTY>
|
|
<PROPERTY NAME="NumberOfEnabledThreads"><VALUE>56</VALUE></PROPERTY>
|
|
<PROPERTY NAME="CurrentClockSpeed"><VALUE>2000</VALUE></PROPERTY>
|
|
<PROPERTY NAME="MaxClockSpeed"><VALUE>3100</VALUE></PROPERTY>
|
|
<PROPERTY NAME="PPIN"><VALUE>ABCD</VALUE></PROPERTY>
|
|
</INSTANCE></VALUE.NAMEDINSTANCE>
|
|
<VALUE.NAMEDINSTANCE><INSTANCE CLASSNAME="DCIM_NICView">
|
|
<PROPERTY NAME="FQDD"><VALUE>NIC.Slot.1-1-1</VALUE></PROPERTY>
|
|
<PROPERTY NAME="ProductName"><VALUE>Broadcom 57414 Dual Port 10/25GbE SFP28 Adapter</VALUE></PROPERTY>
|
|
<PROPERTY NAME="VendorName"><VALUE>Broadcom</VALUE></PROPERTY>
|
|
<PROPERTY NAME="CurrentMACAddress"><VALUE>00:11:22:33:44:55</VALUE></PROPERTY>
|
|
<PROPERTY NAME="SerialNumber"><VALUE>NICSERIAL1</VALUE></PROPERTY>
|
|
<PROPERTY NAME="FamilyVersion"><VALUE>22.80.17</VALUE></PROPERTY>
|
|
<PROPERTY NAME="PCIVendorID"><VALUE>0x14e4</VALUE></PROPERTY>
|
|
<PROPERTY NAME="PCIDeviceID"><VALUE>0x16d7</VALUE></PROPERTY>
|
|
</INSTANCE></VALUE.NAMEDINSTANCE>
|
|
<VALUE.NAMEDINSTANCE><INSTANCE CLASSNAME="DCIM_PowerSupplyView">
|
|
<PROPERTY NAME="FQDD"><VALUE>PSU.Slot.1</VALUE></PROPERTY>
|
|
<PROPERTY NAME="Model"><VALUE>D1400E-S0</VALUE></PROPERTY>
|
|
<PROPERTY NAME="Manufacturer"><VALUE>Dell</VALUE></PROPERTY>
|
|
<PROPERTY NAME="SerialNumber"><VALUE>PSUSERIAL1</VALUE></PROPERTY>
|
|
<PROPERTY NAME="FirmwareVersion"><VALUE>00.1A</VALUE></PROPERTY>
|
|
<PROPERTY NAME="TotalOutputPower"><VALUE>1400</VALUE></PROPERTY>
|
|
</INSTANCE></VALUE.NAMEDINSTANCE>
|
|
<VALUE.NAMEDINSTANCE><INSTANCE CLASSNAME="DCIM_VideoView">
|
|
<PROPERTY NAME="FQDD"><VALUE>Video.Slot.38-1</VALUE></PROPERTY>
|
|
<PROPERTY NAME="MarketingName"><VALUE>NVIDIA H100 PCIe</VALUE></PROPERTY>
|
|
<PROPERTY NAME="Description"><VALUE>GH100 [H100 PCIe]</VALUE></PROPERTY>
|
|
<PROPERTY NAME="Manufacturer"><VALUE>NVIDIA Corporation</VALUE></PROPERTY>
|
|
<PROPERTY NAME="PCIVendorID"><VALUE>10DE</VALUE></PROPERTY>
|
|
<PROPERTY NAME="PCIDeviceID"><VALUE>2331</VALUE></PROPERTY>
|
|
<PROPERTY NAME="BusNumber"><VALUE>74</VALUE></PROPERTY>
|
|
<PROPERTY NAME="DeviceNumber"><VALUE>0</VALUE></PROPERTY>
|
|
<PROPERTY NAME="FunctionNumber"><VALUE>0</VALUE></PROPERTY>
|
|
<PROPERTY NAME="SerialNumber"><VALUE>1793924039808</VALUE></PROPERTY>
|
|
<PROPERTY NAME="FirmwareVersion"><VALUE>96.00.AF.00.01</VALUE></PROPERTY>
|
|
<PROPERTY NAME="GPUGUID"><VALUE>bc681a6d4785dde08c21f49c46c05cc3</VALUE></PROPERTY>
|
|
</INSTANCE></VALUE.NAMEDINSTANCE>
|
|
</SIMPLEREQ></MESSAGE></CIM>`
|
|
|
|
const swXML = `<CIM><MESSAGE><SIMPLEREQ>
|
|
<VALUE.NAMEDINSTANCE><INSTANCE CLASSNAME="DCIM_SoftwareIdentity">
|
|
<PROPERTY NAME="ElementName"><VALUE>NIC.Slot.1-1-1</VALUE></PROPERTY>
|
|
<PROPERTY NAME="VersionString"><VALUE>22.80.17</VALUE></PROPERTY>
|
|
<PROPERTY NAME="ComponentType"><VALUE>Network</VALUE></PROPERTY>
|
|
</INSTANCE></VALUE.NAMEDINSTANCE>
|
|
</SIMPLEREQ></MESSAGE></CIM>`
|
|
|
|
const eventsXML = `<Log>
|
|
<Event AgentID="Lifecycle Controller" Category="System Health" Severity="Warning" Timestamp="2024-11-19T14:39:01-0800">
|
|
<MessageID>SYS1001</MessageID>
|
|
<Message>Link is down</Message>
|
|
<FQDD>NIC.Slot.1-1-1</FQDD>
|
|
</Event>
|
|
</Log>`
|
|
|
|
const cimSensorXML = `<CIM><MESSAGE><SIMPLEREQ>
|
|
<VALUE.NAMEDINSTANCE><INSTANCE CLASSNAME="DCIM_GPUSensor">
|
|
<PROPERTY NAME="DeviceID"><VALUE>Video.Slot.38-1</VALUE></PROPERTY>
|
|
<PROPERTY NAME="PrimaryGPUTemperature"><VALUE>290</VALUE></PROPERTY>
|
|
<PROPERTY NAME="MemoryTemperature"><VALUE>440</VALUE></PROPERTY>
|
|
<PROPERTY NAME="PowerConsumption"><VALUE>295</VALUE></PROPERTY>
|
|
<PROPERTY NAME="ThermalAlertStatus"><VALUE>5</VALUE></PROPERTY>
|
|
</INSTANCE></VALUE.NAMEDINSTANCE>
|
|
<VALUE.NAMEDINSTANCE><INSTANCE CLASSNAME="CIM_NumericSensor">
|
|
<PROPERTY NAME="ElementName"><VALUE>PS1 Voltage 1</VALUE></PROPERTY>
|
|
<PROPERTY NAME="CurrentReading"><VALUE>224.0</VALUE></PROPERTY>
|
|
<PROPERTY NAME="BaseUnits"><VALUE>5</VALUE></PROPERTY>
|
|
<PROPERTY NAME="UnitModifier"><VALUE>0</VALUE></PROPERTY>
|
|
<PROPERTY NAME="PrimaryStatus"><VALUE>5</VALUE></PROPERTY>
|
|
</INSTANCE></VALUE.NAMEDINSTANCE>
|
|
</SIMPLEREQ></MESSAGE></CIM>`
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
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()
|
|
}
|