Files
logpile/internal/collector/redfish_test.go
2026-02-24 17:23:26 +03:00

241 lines
7.2 KiB
Go

package collector
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
func TestRedfishConnectorCollect(t *testing.T) {
mux := http.NewServeMux()
register := func(path string, payload interface{}) {
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(payload)
})
}
register("/redfish/v1", map[string]interface{}{"Name": "ServiceRoot"})
register("/redfish/v1/Systems/1", map[string]interface{}{
"Manufacturer": "Supermicro",
"Model": "SYS-TEST",
"SerialNumber": "SYS123",
"BiosVersion": "2.1a",
})
register("/redfish/v1/Systems/1/Bios", map[string]interface{}{"Version": "2.1a"})
register("/redfish/v1/Systems/1/SecureBoot", map[string]interface{}{"SecureBootCurrentBoot": "Enabled"})
register("/redfish/v1/Systems/1/Processors", map[string]interface{}{
"Members": []map[string]string{
{"@odata.id": "/redfish/v1/Systems/1/Processors/CPU1"},
},
})
register("/redfish/v1/Systems/1/Processors/CPU1", map[string]interface{}{
"Name": "CPU1",
"Model": "Xeon Gold",
"TotalCores": 32,
"TotalThreads": 64,
"MaxSpeedMHz": 3600,
})
register("/redfish/v1/Systems/1/Memory", map[string]interface{}{
"Members": []map[string]string{
{"@odata.id": "/redfish/v1/Systems/1/Memory/DIMM1"},
},
})
register("/redfish/v1/Systems/1/Memory/DIMM1", map[string]interface{}{
"Name": "DIMM A1",
"CapacityMiB": 32768,
"MemoryDeviceType": "DDR5",
"OperatingSpeedMhz": 4800,
"Status": map[string]interface{}{
"Health": "OK",
},
})
register("/redfish/v1/Systems/1/Storage", map[string]interface{}{
"Members": []map[string]string{
{"@odata.id": "/redfish/v1/Systems/1/Storage/1"},
},
})
register("/redfish/v1/Systems/1/Storage/1", map[string]interface{}{
"Drives": []map[string]string{
{"@odata.id": "/redfish/v1/Systems/1/Storage/1/Drives/1"},
},
})
register("/redfish/v1/Systems/1/Storage/1/Drives/1", map[string]interface{}{
"Name": "Drive1",
"Model": "NVMe Test",
"MediaType": "SSD",
"Protocol": "NVMe",
"CapacityGB": 960,
"SerialNumber": "SN123",
})
register("/redfish/v1/Systems/1/PCIeDevices", map[string]interface{}{
"Members": []map[string]string{
{"@odata.id": "/redfish/v1/Systems/1/PCIeDevices/GPU1"},
},
})
register("/redfish/v1/Systems/1/PCIeDevices/GPU1", map[string]interface{}{
"Id": "GPU1",
"Name": "NVIDIA H100",
"Model": "NVIDIA H100 PCIe",
"Manufacturer": "NVIDIA",
"SerialNumber": "GPU-SN-001",
"PCIeFunctions": map[string]interface{}{
"@odata.id": "/redfish/v1/Systems/1/PCIeDevices/GPU1/PCIeFunctions",
},
})
register("/redfish/v1/Systems/1/PCIeDevices/GPU1/PCIeFunctions", map[string]interface{}{
"Members": []map[string]string{
{"@odata.id": "/redfish/v1/Systems/1/PCIeFunctions/GPU1F0"},
},
})
register("/redfish/v1/Systems/1/PCIeFunctions/GPU1F0", map[string]interface{}{
"FunctionId": "0000:65:00.0",
"VendorId": "0x10DE",
"DeviceId": "0x2331",
"ClassCode": "0x030200",
"CurrentLinkWidth": 16,
"CurrentLinkSpeed": "16.0 GT/s",
"MaxLinkWidth": 16,
"MaxLinkSpeed": "16.0 GT/s",
})
register("/redfish/v1/Chassis/1/NetworkAdapters", map[string]interface{}{
"Members": []map[string]string{
{"@odata.id": "/redfish/v1/Chassis/1/NetworkAdapters/1"},
},
})
register("/redfish/v1/Chassis/1/Power", map[string]interface{}{
"PowerSupplies": []map[string]interface{}{
{
"MemberId": "PSU1",
"Name": "PSU Slot 1",
"Model": "PWS-2K01A-1R",
"Manufacturer": "Delta",
"PowerCapacityWatts": 2000,
"PowerInputWatts": 1600,
"LastPowerOutputWatts": 1200,
"LineInputVoltage": 230,
"Status": map[string]interface{}{
"Health": "OK",
"State": "Enabled",
},
},
},
})
register("/redfish/v1/Chassis/1/NetworkAdapters/1", map[string]interface{}{
"Name": "Mellanox",
"Model": "ConnectX-6",
"SerialNumber": "NIC123",
})
register("/redfish/v1/Managers/1", map[string]interface{}{
"FirmwareVersion": "1.25",
})
register("/redfish/v1/Managers/1/NetworkProtocol", map[string]interface{}{
"Id": "NetworkProtocol",
})
ts := httptest.NewServer(mux)
defer ts.Close()
c := NewRedfishConnector()
result, err := c.Collect(context.Background(), Request{
Host: ts.URL,
Port: 443,
Protocol: "redfish",
Username: "admin",
AuthType: "password",
Password: "secret",
TLSMode: "strict",
}, nil)
if err != nil {
t.Fatalf("collect failed: %v", err)
}
if result.Hardware == nil {
t.Fatalf("expected hardware config")
}
if result.Hardware.BoardInfo.ProductName != "SYS-TEST" {
t.Fatalf("unexpected board model: %q", result.Hardware.BoardInfo.ProductName)
}
if len(result.Hardware.CPUs) != 1 {
t.Fatalf("expected one CPU, got %d", len(result.Hardware.CPUs))
}
if len(result.Hardware.Memory) != 1 {
t.Fatalf("expected one DIMM, got %d", len(result.Hardware.Memory))
}
if len(result.Hardware.Storage) != 1 {
t.Fatalf("expected one drive, got %d", len(result.Hardware.Storage))
}
if len(result.Hardware.NetworkAdapters) != 1 {
t.Fatalf("expected one nic, got %d", len(result.Hardware.NetworkAdapters))
}
if len(result.Hardware.GPUs) != 1 {
t.Fatalf("expected one gpu, got %d", len(result.Hardware.GPUs))
}
if result.Hardware.GPUs[0].BDF != "0000:65:00.0" {
t.Fatalf("unexpected gpu BDF: %q", result.Hardware.GPUs[0].BDF)
}
if len(result.Hardware.PCIeDevices) != 1 {
t.Fatalf("expected one pcie device, got %d", len(result.Hardware.PCIeDevices))
}
if len(result.Hardware.PowerSupply) != 1 {
t.Fatalf("expected one psu, got %d", len(result.Hardware.PowerSupply))
}
if result.Hardware.PowerSupply[0].WattageW != 2000 {
t.Fatalf("unexpected psu wattage: %d", result.Hardware.PowerSupply[0].WattageW)
}
if len(result.Hardware.Firmware) == 0 {
t.Fatalf("expected firmware entries")
}
if result.RawPayloads == nil {
t.Fatalf("expected raw payloads")
}
treeAny, ok := result.RawPayloads["redfish_tree"]
if !ok {
t.Fatalf("expected redfish_tree in raw payloads")
}
tree, ok := treeAny.(map[string]interface{})
if !ok || len(tree) == 0 {
t.Fatalf("expected non-empty redfish_tree, got %#v", treeAny)
}
}
func TestParsePCIeDeviceSlot_FromNestedRedfishSlotLocation(t *testing.T) {
doc := map[string]interface{}{
"Id": "NIC1",
"Slot": map[string]interface{}{
"Lanes": 16,
"Location": map[string]interface{}{
"PartLocation": map[string]interface{}{
"LocationOrdinalValue": 1,
"LocationType": "Slot",
"ServiceLabel": "PCIe Slot 1 (1)",
},
},
"PCIeType": "Gen5",
"SlotType": "FullLength",
},
}
got := parsePCIeDevice(doc, nil)
if got.Slot != "PCIe Slot 1 (1)" {
t.Fatalf("unexpected slot: %q", got.Slot)
}
}
func TestParsePCIeDeviceSlot_EmptyMapFallsBackToID(t *testing.T) {
doc := map[string]interface{}{
"Id": "NIC42",
"Slot": map[string]interface{}{},
}
got := parsePCIeDevice(doc, nil)
if got.Slot != "NIC42" {
t.Fatalf("unexpected slot fallback: %q", got.Slot)
}
if got.Slot == "map[]" {
t.Fatalf("slot should not stringify empty map")
}
}