redfish: MSI support, fix zero dates, BMC MAC, Assembly FRU, crawler cleanup

- Add MSI CG480-S5063 (H100 SXM5) support:
  - collectGPUsFromProcessors: find GPUs via Processors/ProcessorType=GPU,
    resolve serials from Chassis/<GpuId>
  - looksLikeGPU: skip Description="Display Device" PCIe sidecars
  - isVirtualStorageDrive: filter AMI virtual USB drives (0-byte)
  - enrichNICMACsFromNetworkDeviceFunctions: pull MACs for MSI NICs
  - parseCPUs: filter by ProcessorType, parse Socket, L1/L2/L3 from ProcessorMemory
  - parseMemory: Location.PartLocation.ServiceLabel slot fallback
  - shouldCrawlPath: block /SubProcessors subtrees
- Fix status_checked_at/status_changed_at serializing as 0001-01-01:
  change all StatusCheckedAt/StatusChangedAt fields to *time.Time
- Redfish crawler cleanup:
  - Block non-inventory branches: AccountService, CertificateService,
    EventService, Registries, SessionService, TaskService, manager config paths,
    OperatingConfigs, BootOptions, HostPostCode, Bios/Settings, OEM KVM paths
  - Add Assembly to critical endpoints (FRU data)
  - Remove BootOptions from priority seeds
- collectBMCMAC: read BMC MAC from Managers/*/EthernetInterfaces
- collectAssemblyFRU: extract FRU serial/part from Chassis/*/Assembly
- Firmware: remove NetworkProtocol noise, fix SecureBoot field,
  filter BMCImageN redundant backup slots

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 08:12:17 +03:00
parent 21ea129933
commit 8d80048117
8 changed files with 430 additions and 54 deletions

View File

@@ -385,10 +385,10 @@ func mergeCanonicalDevice(primary, secondary models.HardwareDevice) models.Hardw
fillFloat(&primary.InputVoltage, secondary.InputVoltage)
fillInt(&primary.TemperatureC, secondary.TemperatureC)
fillString(&primary.Status, secondary.Status)
if primary.StatusCheckedAt.IsZero() && !secondary.StatusCheckedAt.IsZero() {
if primary.StatusCheckedAt == nil && secondary.StatusCheckedAt != nil {
primary.StatusCheckedAt = secondary.StatusCheckedAt
}
if primary.StatusChangedAt.IsZero() && !secondary.StatusChangedAt.IsZero() {
if primary.StatusChangedAt == nil && secondary.StatusChangedAt != nil {
primary.StatusChangedAt = secondary.StatusChangedAt
}
if primary.StatusAtCollect == nil && secondary.StatusAtCollect != nil {
@@ -1130,8 +1130,8 @@ type convertedStatusMeta struct {
func buildStatusMeta(
currentStatus string,
checkedAt time.Time,
changedAt time.Time,
checkedAt *time.Time,
changedAt *time.Time,
statusAtCollection *models.StatusAtCollection,
history []models.StatusHistoryEntry,
errorDescription string,
@@ -1145,7 +1145,7 @@ func buildStatusMeta(
convertedHistory := make([]ReanimatorStatusHistoryEntry, 0, len(history))
for _, h := range history {
changed := formatOptionalRFC3339(h.ChangedAt)
changed := formatOptionalRFC3339(&h.ChangedAt)
if changed == "" {
continue
}
@@ -1166,7 +1166,7 @@ func buildStatusMeta(
}
if statusAtCollection != nil {
at := formatOptionalRFC3339(statusAtCollection.At)
at := formatOptionalRFC3339(&statusAtCollection.At)
if at != "" && strings.TrimSpace(statusAtCollection.Status) != "" {
meta.StatusAtCollection = &ReanimatorStatusAtCollection{
Status: normalizeStatus(statusAtCollection.Status, true),
@@ -1191,8 +1191,8 @@ func buildStatusMeta(
return meta
}
func formatOptionalRFC3339(t time.Time) string {
if t.IsZero() {
func formatOptionalRFC3339(t *time.Time) string {
if t == nil || t.IsZero() {
return ""
}
return t.UTC().Format(time.RFC3339)