iDRAC10-generation TSR bundles no longer ship sysinfo_dcim_view.xml / sysinfo_dcim_softwareidentity.xml, so the dell parser produced events but no hardware inventory for them. These bundles instead carry redfishidracwalk.tar.gz, a captured dump of the iDRAC's own Redfish tree. Add vendors/redfishtree, a shared helper that reconstructs a path->document map from a tar.gz/zip-packaged Redfish walk (vendor-independent detection: path hint + /redfish/v1 service-root/Systems/Chassis structural check) and replays it through the existing collector.ReplayRedfishFromRawPayloads. vendors/dell uses it to enrich DCIM-XML-derived data (append-only, existing dedupe passes resolve overlaps). Also register vendors/redfishwalk, a low-confidence fallback VendorParser using the same helpers, so any other vendor that starts shipping this kind of raw Redfish walk is picked up automatically without a dedicated parser. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
116 lines
4.1 KiB
Go
116 lines
4.1 KiB
Go
package dell
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"compress/gzip"
|
|
"testing"
|
|
|
|
"git.mchus.pro/mchus/logpile/internal/parser"
|
|
)
|
|
|
|
func makeRedfishWalkTarGz(t *testing.T, files map[string]string) []byte {
|
|
t.Helper()
|
|
var buf bytes.Buffer
|
|
gzw := gzip.NewWriter(&buf)
|
|
tw := tar.NewWriter(gzw)
|
|
for name, content := range files {
|
|
hdr := &tar.Header{
|
|
Name: name,
|
|
Mode: 0o644,
|
|
Size: int64(len(content)),
|
|
}
|
|
if err := tw.WriteHeader(hdr); err != nil {
|
|
t.Fatalf("write tar header %s: %v", name, err)
|
|
}
|
|
if _, err := tw.Write([]byte(content)); err != nil {
|
|
t.Fatalf("write tar content %s: %v", name, err)
|
|
}
|
|
}
|
|
if err := tw.Close(); err != nil {
|
|
t.Fatalf("close tar: %v", err)
|
|
}
|
|
if err := gzw.Close(); err != nil {
|
|
t.Fatalf("close gzip: %v", err)
|
|
}
|
|
return buf.Bytes()
|
|
}
|
|
|
|
// TestParseNestedTSRZip_RedfishWalkFillsInventory reproduces an iDRAC10-generation
|
|
// TSR bundle: no sysinfo_dcim_view.xml / sysinfo_dcim_softwareidentity.xml, only
|
|
// metadata.json, curr_lclog.xml and a captured redfishidracwalk.tar.gz. Hardware
|
|
// inventory must come from the Redfish walk instead of being left empty.
|
|
func TestParseNestedTSRZip_RedfishWalkFillsInventory(t *testing.T) {
|
|
redfishWalk := makeRedfishWalkTarGz(t, map[string]string{
|
|
"redfish/v1/index.json": `{"@odata.id":"/redfish/v1"}`,
|
|
"redfish/v1/Systems/index.json": `{
|
|
"@odata.id":"/redfish/v1/Systems",
|
|
"Members":[{"@odata.id":"/redfish/v1/Systems/System.Embedded.1"}]
|
|
}`,
|
|
"redfish/v1/Systems/System.Embedded.1/index.json": `{
|
|
"@odata.id":"/redfish/v1/Systems/System.Embedded.1",
|
|
"Id":"System.Embedded.1",
|
|
"Manufacturer":"Dell Inc.",
|
|
"Model":"PowerEdge R470",
|
|
"SKU":"1TVFYL4",
|
|
"Processors":{"@odata.id":"/redfish/v1/Systems/System.Embedded.1/Processors"},
|
|
"Memory":{"@odata.id":"/redfish/v1/Systems/System.Embedded.1/Memory"}
|
|
}`,
|
|
"redfish/v1/Systems/System.Embedded.1/Processors/index.json": `{
|
|
"@odata.id":"/redfish/v1/Systems/System.Embedded.1/Processors",
|
|
"Members":[{"@odata.id":"/redfish/v1/Systems/System.Embedded.1/Processors/CPU.Socket.0"}]
|
|
}`,
|
|
"redfish/v1/Systems/System.Embedded.1/Processors/CPU.Socket.0/index.json": `{
|
|
"@odata.id":"/redfish/v1/Systems/System.Embedded.1/Processors/CPU.Socket.0",
|
|
"Id":"CPU.Socket.0",
|
|
"ProcessorType":"CPU",
|
|
"Model":"Intel Xeon 6740P",
|
|
"Manufacturer":"Intel"
|
|
}`,
|
|
"redfish/v1/Systems/System.Embedded.1/Memory/index.json": `{
|
|
"@odata.id":"/redfish/v1/Systems/System.Embedded.1/Memory",
|
|
"Members":[{"@odata.id":"/redfish/v1/Systems/System.Embedded.1/Memory/DIMM.Socket.A1"}]
|
|
}`,
|
|
"redfish/v1/Systems/System.Embedded.1/Memory/DIMM.Socket.A1/index.json": `{
|
|
"@odata.id":"/redfish/v1/Systems/System.Embedded.1/Memory/DIMM.Socket.A1",
|
|
"Id":"DIMM.Socket.A1",
|
|
"DeviceLocator":"DIMM.Socket.A1",
|
|
"CapacityMiB":32768,
|
|
"SerialNumber":"DIMM-A1-SN"
|
|
}`,
|
|
})
|
|
|
|
inner := makeZipArchive(t, map[string][]byte{
|
|
"tsr/metadata.json": []byte(`{"Make":"Dell Inc.","Model":"PowerEdge R470","ServiceTag":"1TVFYL4"}`),
|
|
"tsr/hardware/sysinfo/lcfiles/curr_lclog.xml": []byte(`<CIM><MESSAGE><SIMPLEREQ/></MESSAGE></CIM>`),
|
|
"tsr/hardware/sysinfo/inventory/redfishidracwalk.tar.gz": redfishWalk,
|
|
})
|
|
|
|
p := &Parser{}
|
|
files := []parser.ExtractedFile{
|
|
{Path: "signature", Content: []byte("ok")},
|
|
{Path: "TSR20260721231613_1TVFYL4.pl.zip", Content: inner},
|
|
}
|
|
|
|
if score := p.Detect(files); score < 80 {
|
|
t.Fatalf("expected high detect score for iDRAC10-style TSR without DCIM XML, got %d", score)
|
|
}
|
|
|
|
result, err := p.Parse(files)
|
|
if err != nil {
|
|
t.Fatalf("Parse() failed: %v", err)
|
|
}
|
|
if result.Hardware == nil {
|
|
t.Fatal("expected non-nil Hardware")
|
|
}
|
|
if got := result.Hardware.BoardInfo.Manufacturer; got != "Dell Inc." {
|
|
t.Fatalf("expected board manufacturer from metadata.json, got %q", got)
|
|
}
|
|
if len(result.Hardware.CPUs) != 1 || result.Hardware.CPUs[0].Model != "Intel Xeon 6740P" {
|
|
t.Fatalf("expected CPU inventory from redfishidracwalk.tar.gz, got %+v", result.Hardware.CPUs)
|
|
}
|
|
if len(result.Hardware.Memory) != 1 || result.Hardware.Memory[0].SerialNumber != "DIMM-A1-SN" {
|
|
t.Fatalf("expected memory inventory from redfishidracwalk.tar.gz, got %+v", result.Hardware.Memory)
|
|
}
|
|
}
|