317 lines
11 KiB
Go
317 lines
11 KiB
Go
package hpe_ilo_ahs
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"encoding/binary"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"git.mchus.pro/mchus/logpile/internal/parser"
|
|
)
|
|
|
|
func TestDetectAHS(t *testing.T) {
|
|
p := &Parser{}
|
|
score := p.Detect([]parser.ExtractedFile{{
|
|
Path: "HPE_CZ2D1X0GS3_20260330.ahs",
|
|
Content: makeAHSArchive(t, []ahsTestEntry{{Name: "CUST_INFO.DAT", Payload: []byte("x")}}),
|
|
}})
|
|
if score < 80 {
|
|
t.Fatalf("expected high confidence detect, got %d", score)
|
|
}
|
|
}
|
|
|
|
func TestParseAHSInventory(t *testing.T) {
|
|
p := &Parser{}
|
|
content := makeAHSArchive(t, []ahsTestEntry{
|
|
{Name: "CUST_INFO.DAT", Payload: make([]byte, 16)},
|
|
{Name: "0000088-2026-03-30.zbb", Payload: gzipBytes(t, []byte(sampleInventoryBlob()))},
|
|
{Name: "bcert.pkg", Payload: []byte(sampleBCertBlob())},
|
|
})
|
|
|
|
result, err := p.Parse([]parser.ExtractedFile{{
|
|
Path: "HPE_CZ2D1X0GS3_20260330.ahs",
|
|
Content: content,
|
|
}})
|
|
if err != nil {
|
|
t.Fatalf("parse failed: %v", err)
|
|
}
|
|
if result.Hardware == nil {
|
|
t.Fatalf("expected hardware section")
|
|
}
|
|
|
|
board := result.Hardware.BoardInfo
|
|
if board.Manufacturer != "HPE" {
|
|
t.Fatalf("unexpected board manufacturer: %q", board.Manufacturer)
|
|
}
|
|
if board.ProductName != "ProLiant DL380 Gen11" {
|
|
t.Fatalf("unexpected board product: %q", board.ProductName)
|
|
}
|
|
if board.SerialNumber != "CZ2D1X0GS3" {
|
|
t.Fatalf("unexpected board serial: %q", board.SerialNumber)
|
|
}
|
|
if board.PartNumber != "P52560-421" {
|
|
t.Fatalf("unexpected board part number: %q", board.PartNumber)
|
|
}
|
|
|
|
if len(result.Hardware.CPUs) != 1 || result.Hardware.CPUs[0].Model != "Intel(R) Xeon(R) Gold 6444Y" {
|
|
t.Fatalf("unexpected CPUs: %+v", result.Hardware.CPUs)
|
|
}
|
|
if len(result.Hardware.Memory) != 1 {
|
|
t.Fatalf("expected one DIMM, got %d", len(result.Hardware.Memory))
|
|
}
|
|
if result.Hardware.Memory[0].PartNumber != "HMCG88AEBRA115N" {
|
|
t.Fatalf("unexpected DIMM part number: %q", result.Hardware.Memory[0].PartNumber)
|
|
}
|
|
|
|
if len(result.Hardware.NetworkAdapters) != 2 {
|
|
t.Fatalf("expected two network adapters, got %d", len(result.Hardware.NetworkAdapters))
|
|
}
|
|
if len(result.Hardware.PowerSupply) != 1 {
|
|
t.Fatalf("expected one PSU, got %d", len(result.Hardware.PowerSupply))
|
|
}
|
|
if result.Hardware.PowerSupply[0].SerialNumber != "5XUWB0C4DJG4BV" {
|
|
t.Fatalf("unexpected PSU serial: %q", result.Hardware.PowerSupply[0].SerialNumber)
|
|
}
|
|
if result.Hardware.PowerSupply[0].Firmware != "2.00" {
|
|
t.Fatalf("unexpected PSU firmware: %q", result.Hardware.PowerSupply[0].Firmware)
|
|
}
|
|
|
|
if len(result.Hardware.Storage) != 1 {
|
|
t.Fatalf("expected one physical drive, got %d", len(result.Hardware.Storage))
|
|
}
|
|
drive := result.Hardware.Storage[0]
|
|
if drive.Model != "SAMSUNGMZ7L3480HCHQ-00A07" {
|
|
t.Fatalf("unexpected drive model: %q", drive.Model)
|
|
}
|
|
if drive.SerialNumber != "S664NC0Y502720" {
|
|
t.Fatalf("unexpected drive serial: %q", drive.SerialNumber)
|
|
}
|
|
if drive.SizeGB != 480 {
|
|
t.Fatalf("unexpected drive size: %d", drive.SizeGB)
|
|
}
|
|
|
|
if len(result.Hardware.Firmware) == 0 {
|
|
t.Fatalf("expected firmware inventory")
|
|
}
|
|
foundILO := false
|
|
foundControllerFW := false
|
|
foundNICFW := false
|
|
foundBackplaneFW := false
|
|
for _, item := range result.Hardware.Firmware {
|
|
if item.DeviceName == "iLO 6" && item.Version == "v1.63p20" {
|
|
foundILO = true
|
|
}
|
|
if item.DeviceName == "HPE MR408i-o Gen11" && item.Version == "52.26.3-5379" {
|
|
foundControllerFW = true
|
|
}
|
|
if item.DeviceName == "BCM 5719 1Gb 4p BASE-T OCP Adptr" && item.Version == "20.28.41" {
|
|
foundNICFW = true
|
|
}
|
|
if item.DeviceName == "8 SFF 24G x1NVMe/SAS UBM3 BC BP" && item.Version == "1.24" {
|
|
foundBackplaneFW = true
|
|
}
|
|
}
|
|
if !foundILO {
|
|
t.Fatalf("expected iLO firmware entry")
|
|
}
|
|
if !foundControllerFW {
|
|
t.Fatalf("expected controller firmware entry")
|
|
}
|
|
if !foundNICFW {
|
|
t.Fatalf("expected broadcom firmware entry")
|
|
}
|
|
if !foundBackplaneFW {
|
|
t.Fatalf("expected backplane firmware entry")
|
|
}
|
|
|
|
broadcomFound := false
|
|
backplaneFound := false
|
|
for _, nic := range result.Hardware.NetworkAdapters {
|
|
if nic.SerialNumber == "1CH0150001" && nic.Firmware == "20.28.41" {
|
|
broadcomFound = true
|
|
}
|
|
}
|
|
for _, dev := range result.Hardware.Devices {
|
|
if dev.DeviceClass == "storage_backplane" && dev.Firmware == "1.24" {
|
|
backplaneFound = true
|
|
}
|
|
}
|
|
if !broadcomFound {
|
|
t.Fatalf("expected broadcom adapter firmware to be enriched")
|
|
}
|
|
if !backplaneFound {
|
|
t.Fatalf("expected backplane canonical device")
|
|
}
|
|
|
|
if len(result.Hardware.Devices) < 6 {
|
|
t.Fatalf("expected canonical devices, got %d", len(result.Hardware.Devices))
|
|
}
|
|
if len(result.Events) == 0 {
|
|
t.Fatalf("expected parsed events")
|
|
}
|
|
}
|
|
|
|
func TestParseExampleAHS(t *testing.T) {
|
|
path := filepath.Join("..", "..", "..", "..", "example", "HPE_CZ2D1X0GS3_20260330.ahs")
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Skipf("example fixture unavailable: %v", err)
|
|
}
|
|
|
|
p := &Parser{}
|
|
result, err := p.Parse([]parser.ExtractedFile{{
|
|
Path: filepath.Base(path),
|
|
Content: content,
|
|
}})
|
|
if err != nil {
|
|
t.Fatalf("parse example failed: %v", err)
|
|
}
|
|
if result.Hardware == nil {
|
|
t.Fatalf("expected hardware section")
|
|
}
|
|
|
|
board := result.Hardware.BoardInfo
|
|
if board.ProductName != "ProLiant DL380 Gen11" {
|
|
t.Fatalf("unexpected board product: %q", board.ProductName)
|
|
}
|
|
if board.SerialNumber != "CZ2D1X0GS3" {
|
|
t.Fatalf("unexpected board serial: %q", board.SerialNumber)
|
|
}
|
|
|
|
if len(result.Hardware.Storage) < 2 {
|
|
t.Fatalf("expected at least two drives, got %d", len(result.Hardware.Storage))
|
|
}
|
|
if len(result.Hardware.PowerSupply) != 2 {
|
|
t.Fatalf("expected exactly two PSUs, got %d: %+v", len(result.Hardware.PowerSupply), result.Hardware.PowerSupply)
|
|
}
|
|
|
|
foundController := false
|
|
foundBackplaneFW := false
|
|
foundNICFW := false
|
|
for _, device := range result.Hardware.Devices {
|
|
if device.Model == "HPE MR408i-o Gen11" && device.SerialNumber == "PXSFQ0BBIJY3B3" {
|
|
foundController = true
|
|
}
|
|
if device.DeviceClass == "storage_backplane" && device.Firmware == "1.24" {
|
|
foundBackplaneFW = true
|
|
}
|
|
}
|
|
if !foundController {
|
|
t.Fatalf("expected MR408i-o controller in canonical devices")
|
|
}
|
|
for _, fw := range result.Hardware.Firmware {
|
|
if fw.DeviceName == "BCM 5719 1Gb 4p BASE-T OCP Adptr" && fw.Version == "20.28.41" {
|
|
foundNICFW = true
|
|
}
|
|
}
|
|
if !foundBackplaneFW {
|
|
t.Fatalf("expected backplane device in canonical devices")
|
|
}
|
|
if !foundNICFW {
|
|
t.Fatalf("expected broadcom firmware from bcert/pkg lockdown")
|
|
}
|
|
}
|
|
|
|
type ahsTestEntry struct {
|
|
Name string
|
|
Payload []byte
|
|
Flag uint32
|
|
}
|
|
|
|
func makeAHSArchive(t *testing.T, entries []ahsTestEntry) []byte {
|
|
t.Helper()
|
|
|
|
var buf bytes.Buffer
|
|
for _, entry := range entries {
|
|
header := make([]byte, ahsHeaderSize)
|
|
copy(header[:4], []byte("ABJR"))
|
|
binary.LittleEndian.PutUint16(header[4:6], 0x0300)
|
|
binary.LittleEndian.PutUint16(header[6:8], 0x0002)
|
|
binary.LittleEndian.PutUint32(header[8:12], uint32(len(entry.Payload)))
|
|
flag := entry.Flag
|
|
if flag == 0 {
|
|
flag = 0x80000002
|
|
if len(entry.Payload) >= 2 && entry.Payload[0] == 0x1f && entry.Payload[1] == 0x8b {
|
|
flag = 0x80000001
|
|
}
|
|
}
|
|
binary.LittleEndian.PutUint32(header[16:20], flag)
|
|
copy(header[20:52], []byte(entry.Name))
|
|
buf.Write(header)
|
|
buf.Write(entry.Payload)
|
|
}
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func gzipBytes(t *testing.T, payload []byte) []byte {
|
|
t.Helper()
|
|
|
|
var buf bytes.Buffer
|
|
zw := gzip.NewWriter(&buf)
|
|
if _, err := zw.Write(payload); err != nil {
|
|
t.Fatalf("gzip payload: %v", err)
|
|
}
|
|
if err := zw.Close(); err != nil {
|
|
t.Fatalf("close gzip writer: %v", err)
|
|
}
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func sampleInventoryBlob() string {
|
|
return stringsJoin(
|
|
"iLO 6 v1.63p20 built on Sep 13 2024",
|
|
"HPE",
|
|
"ProLiant DL380 Gen11",
|
|
"CZ2D1X0GS3",
|
|
"P52560-421",
|
|
"Proc 1",
|
|
"Intel(R) Corporation",
|
|
"Intel(R) Xeon(R) Gold 6444Y",
|
|
"PROC 1 DIMM 3",
|
|
"Hynix",
|
|
"HMCG88AEBRA115N",
|
|
"2B5F92C6",
|
|
"Power Supply 1",
|
|
"5XUWB0C4DJG4BV",
|
|
"P03178-B21",
|
|
"PciRoot(0x1)/Pci(0x5,0x0)/Pci(0x0,0x0)",
|
|
"NIC.Slot.1.1",
|
|
"Network Controller",
|
|
"Slot 1",
|
|
"MCX512A-ACAT",
|
|
"MT2230478382",
|
|
"PciRoot(0x3)/Pci(0x1,0x0)/Pci(0x0,0x0)",
|
|
"OCP.Slot.15.1",
|
|
"Broadcom NetXtreme Gigabit Ethernet - NIC",
|
|
"OCP Slot 15",
|
|
"P51183-001",
|
|
"1CH0150001",
|
|
"20.28.41",
|
|
"System ROM",
|
|
"v2.22 (06/19/2024)",
|
|
"03/30/2026 09:47:33",
|
|
"iLO network link down.",
|
|
`{"@odata.id":"/redfish/v1/Systems/1/Storage/DE00A000/Controllers/0","@odata.type":"#StorageController.v1_7_0.StorageController","Id":"0","Name":"HPE MR408i-o Gen11","FirmwareVersion":"52.26.3-5379","Manufacturer":"HPE","Model":"HPE MR408i-o Gen11","PartNumber":"P58543-001","SKU":"P58335-B21","SerialNumber":"PXSFQ0BBIJY3B3","Status":{"State":"Enabled","Health":"OK"},"Location":{"PartLocation":{"ServiceLabel":"Slot=14","LocationType":"Slot","LocationOrdinalValue":14}},"PCIeInterface":{"PCIeType":"Gen4","LanesInUse":8}}`,
|
|
`{"@odata.id":"/redfish/v1/Fabrics/DE00A000","@odata.type":"#Fabric.v1_3_0.Fabric","Id":"DE00A000","Name":"8 SFF 24G x1NVMe/SAS UBM3 BC BP","FabricType":"MultiProtocol"}`,
|
|
`{"@odata.id":"/redfish/v1/Fabrics/DE00A000/Switches/1","@odata.type":"#Switch.v1_9_1.Switch","Id":"1","Name":"Direct Attached","Model":"UBM3","FirmwareVersion":"1.24","SupportedProtocols":["SAS","SATA","NVMe"],"SwitchType":"MultiProtocol","Status":{"State":"Enabled","Health":"OK"}}`,
|
|
`{"@odata.id":"/redfish/v1/Chassis/DE00A000/Drives/0","@odata.type":"#Drive.v1_17_0.Drive","Id":"0","Name":"480GB 6G SATA SSD","Status":{"State":"StandbyOffline","Health":"OK"},"PhysicalLocation":{"PartLocation":{"ServiceLabel":"Slot=14:Port=1:Box=3:Bay=1","LocationType":"Bay","LocationOrdinalValue":1}},"CapacityBytes":480103981056,"MediaType":"SSD","Model":"SAMSUNGMZ7L3480HCHQ-00A07","Protocol":"SATA","Revision":"JXTC604Q","SerialNumber":"S664NC0Y502720","PredictedMediaLifeLeftPercent":100}`,
|
|
`{"@odata.id":"/redfish/v1/Chassis/DE00A000/Drives/64515","@odata.type":"#Drive.v1_17_0.Drive","Id":"64515","Name":"Empty Bay","Status":{"State":"Absent","Health":"OK"}}`,
|
|
)
|
|
}
|
|
|
|
func sampleBCertBlob() string {
|
|
return `<BC><MfgRecord><PowerSupplySlot id="0"><Present>Yes</Present><SerialNumber>5XUWB0C4DJG4BV</SerialNumber><FirmwareVersion>2.00</FirmwareVersion><SparePartNumber>P44412-001</SparePartNumber></PowerSupplySlot><FirmwareLockdown><SystemProgrammableLogicDevice>0x12</SystemProgrammableLogicDevice><ServerPlatformServicesSPSFirmware>6.1.4.47</ServerPlatformServicesSPSFirmware><STMicroGen11TPM>1.512</STMicroGen11TPM><HPEMR408i-oGen11>52.26.3-5379</HPEMR408i-oGen11><UBM3>UBM3/1.24</UBM3><BCM57191Gb4pBASE-TOCP3>20.28.41</BCM57191Gb4pBASE-TOCP3></FirmwareLockdown></MfgRecord></BC>`
|
|
}
|
|
|
|
func stringsJoin(parts ...string) string {
|
|
return string(bytes.Join(func() [][]byte {
|
|
out := make([][]byte, 0, len(parts))
|
|
for _, part := range parts {
|
|
out = append(out, []byte(part))
|
|
}
|
|
return out
|
|
}(), []byte{0}))
|
|
}
|