Older AMI-BMC Inspur onekeylog archives (NF5466M5 / NF5280M5 generation)
opened to an empty result: they carry none of the files the inspur parser
keys on (no asset.json, devicefrusdr.log, selelist.csv or component.log).
New package internal/parser/vendors/inspur_legacy (vendor id inspur_legacy),
separate from inspur:
- binary IPMI FRU decode (FRU.bin) -> board identity
- Inspur_AssetInfoInventory.log -> CPU / memory / PCIe / PSU inventory
- events from Inspur_<model>_<serial>_IDL, sel.log, blackbox.log,
MegaRAID raid0.log, and the flat AMI <severity>.log files
- no live sensors in this archive class -> recorded as a collection error
- BMC clock timestamps before 2010 dropped as un-set (1970 / ~2005 RTC)
Registry: add optional PrioritizedParser { DetectPriority() int } so a
confidence tie is broken by specificity. inspur_legacy returns 10 and also
declines (Detect 0) when modern Kaytus markers are present, so the two
Inspur parsers never fight over a newer dump.
Docs: ADL-065, 06-parsers.md, releases/v1.32.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017VL8wLGD6Lnp6hZCpqT6cZ
105 lines
3.2 KiB
Go
105 lines
3.2 KiB
Go
package inspurlegacy
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.mchus.pro/mchus/logpile/internal/models"
|
|
)
|
|
|
|
// buildFRU assembles a minimal but spec-shaped IPMI FRU image with a chassis,
|
|
// board and product area. Single-char fields are encoded as 0xC1 (type 3,
|
|
// length 1) exactly as the real Inspur image does.
|
|
func buildFRU() []byte {
|
|
tl3 := func(s string) []byte { return append([]byte{byte(0xC0 | len(s))}, []byte(s)...) }
|
|
|
|
chassis := []byte{0x01, 0x00, 0x17} // version, len placeholder, Rack Mount
|
|
chassis = append(chassis, tl3("0")...)
|
|
chassis = append(chassis, tl3("0")...)
|
|
chassis = pad8(chassis)
|
|
chassis[1] = byte(len(chassis) / 8)
|
|
|
|
board := []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00} // v, len, lang, 3-byte date
|
|
board = append(board, tl3("Inspur")...)
|
|
board = append(board, tl3("NF5280M5")...)
|
|
board = append(board, tl3("MBL911S22243C30")...)
|
|
board = append(board, tl3("YZMB-00882-104")...)
|
|
board = pad8(board)
|
|
board[1] = byte(len(board) / 8)
|
|
|
|
product := []byte{0x01, 0x00, 0x00} // v, len, lang
|
|
product = append(product, tl3("Inspur")...)
|
|
product = append(product, tl3("NF5466M5")...)
|
|
product = append(product, tl3("0")...) // part placeholder
|
|
product = append(product, tl3("0")...) // version placeholder
|
|
product = append(product, tl3("221353113")...)
|
|
product = append(product, tl3("0")...) // asset tag placeholder
|
|
product = pad8(product)
|
|
product[1] = byte(len(product) / 8)
|
|
|
|
header := make([]byte, 8)
|
|
header[0] = 0x01
|
|
header[2] = 1 // chassis at 8
|
|
header[3] = byte((8 + len(chassis)) / 8) // board
|
|
header[4] = byte((8 + len(chassis) + len(board)) / 8)
|
|
|
|
out := append(header, chassis...)
|
|
out = append(out, board...)
|
|
out = append(out, product...)
|
|
return out
|
|
}
|
|
|
|
func pad8(b []byte) []byte {
|
|
for len(b)%8 != 0 {
|
|
b = append(b, 0x00)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func TestDecodeBinaryFRU_Identity(t *testing.T) {
|
|
fru, ok := DecodeBinaryFRU(buildFRU())
|
|
if !ok {
|
|
t.Fatal("decode failed")
|
|
}
|
|
if fru.ProductName != "NF5466M5" {
|
|
t.Errorf("product name = %q", fru.ProductName)
|
|
}
|
|
if fru.ProductSerial != "221353113" {
|
|
t.Errorf("product serial = %q", fru.ProductSerial)
|
|
}
|
|
if fru.BoardPart != "YZMB-00882-104" {
|
|
t.Errorf("board part = %q", fru.BoardPart)
|
|
}
|
|
if fru.ChassisType != "Rack Mount Chassis" {
|
|
t.Errorf("chassis type = %q", fru.ChassisType)
|
|
}
|
|
}
|
|
|
|
func TestBinaryFRU_ApplyToBoardInfo_PrefersProductSerialAndBoardPart(t *testing.T) {
|
|
fru, _ := DecodeBinaryFRU(buildFRU())
|
|
|
|
var b models.BoardInfo
|
|
fru.applyToBoardInfo(&b)
|
|
if b.SerialNumber != "221353113" {
|
|
t.Errorf("board serial = %q, want operator-facing product serial", b.SerialNumber)
|
|
}
|
|
if b.PartNumber != "YZMB-00882-104" {
|
|
t.Errorf("board part = %q, want board part (product part is placeholder)", b.PartNumber)
|
|
}
|
|
if b.Manufacturer != "Inspur" {
|
|
t.Errorf("board manufacturer = %q", b.Manufacturer)
|
|
}
|
|
|
|
// An already-populated field is never overwritten.
|
|
pre := models.BoardInfo{SerialNumber: "EXISTING"}
|
|
fru.applyToBoardInfo(&pre)
|
|
if pre.SerialNumber != "EXISTING" {
|
|
t.Errorf("existing serial overwritten: %q", pre.SerialNumber)
|
|
}
|
|
}
|
|
|
|
func TestDecodeBinaryFRU_RejectsGarbage(t *testing.T) {
|
|
if _, ok := DecodeBinaryFRU([]byte{0x00, 0x00}); ok {
|
|
t.Error("expected decode to fail on short/garbage input")
|
|
}
|
|
}
|