feat: Redfish hardware event log collection + MSI ghost GPU filter + inventory improvements
- Collect hardware event logs (last 7 days) from Systems and Managers/SEL LogServices - Parse AMI raw IPMI dump messages into readable descriptions (Sensor_Type: Event_Type) - Filter out audit/journal/non-hardware log services; only SEL from Managers - MSI ghost GPU filter: exclude processor GPU entries with temperature=0 when host is powered on - Reanimator collected_at uses InventoryData/Status.LastModifiedTime (30-day fallback) - Invalidate Redfish inventory CRC groups before host power-on - Log inventory LastModifiedTime age in collection logs - Drop SecureBoot collection (SecureBootMode, SecureBootDatabases) — not hardware inventory - Add build version to UI footer via template - Add MSI Redfish API reference doc to bible-local/docs/ ADL-032–ADL-035 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -53,7 +53,7 @@ func ReplayRedfishFromRawPayloads(rawPayloads map[string]any, emit ProgressFn) (
|
||||
chassisDoc, _ := r.getJSON(primaryChassis)
|
||||
managerDoc, _ := r.getJSON(primaryManager)
|
||||
biosDoc, _ := r.getJSON(joinPath(primarySystem, "/Bios"))
|
||||
secureBootDoc, _ := r.getJSON(joinPath(primarySystem, "/SecureBoot"))
|
||||
|
||||
systemFRUDoc, _ := r.getJSON(joinPath(primarySystem, "/Oem/Public/FRU"))
|
||||
chassisFRUDoc, _ := r.getJSON(joinPath(primaryChassis, "/Oem/Public/FRU"))
|
||||
fruDoc := systemFRUDoc
|
||||
@@ -96,16 +96,19 @@ func ReplayRedfishFromRawPayloads(rawPayloads map[string]any, emit ProgressFn) (
|
||||
healthEvents := r.collectHealthSummaryEvents(chassisPaths)
|
||||
driveFetchWarningEvents := buildDriveFetchWarningEvents(rawPayloads)
|
||||
networkProtocolDoc, _ := r.getJSON(joinPath(primaryManager, "/NetworkProtocol"))
|
||||
firmware := parseFirmware(systemDoc, biosDoc, managerDoc, secureBootDoc, networkProtocolDoc)
|
||||
firmware := parseFirmware(systemDoc, biosDoc, managerDoc, networkProtocolDoc)
|
||||
firmware = dedupeFirmwareInfo(append(firmware, r.collectFirmwareInventory()...))
|
||||
boardInfo.BMCMACAddress = r.collectBMCMAC(managerPaths)
|
||||
assemblyFRU := r.collectAssemblyFRU(chassisPaths)
|
||||
collectedAt, sourceTimezone := inferRedfishCollectionTime(managerDoc, rawPayloads)
|
||||
inventoryLastModifiedAt := inferInventoryLastModifiedTime(r.tree)
|
||||
logEntryEvents := parseRedfishLogEntries(rawPayloads, collectedAt)
|
||||
|
||||
result := &models.AnalysisResult{
|
||||
CollectedAt: collectedAt,
|
||||
CollectedAt: collectedAt,
|
||||
InventoryLastModifiedAt: inventoryLastModifiedAt,
|
||||
SourceTimezone: sourceTimezone,
|
||||
Events: append(append(append(make([]models.Event, 0, len(discreteEvents)+len(healthEvents)+len(driveFetchWarningEvents)+1), healthEvents...), discreteEvents...), driveFetchWarningEvents...),
|
||||
Events: append(append(append(append(make([]models.Event, 0, len(discreteEvents)+len(healthEvents)+len(driveFetchWarningEvents)+len(logEntryEvents)+1), healthEvents...), discreteEvents...), driveFetchWarningEvents...), logEntryEvents...),
|
||||
FRU: assemblyFRU,
|
||||
Sensors: dedupeSensorReadings(append(append(thresholdSensors, thermalSensors...), powerSensors...)),
|
||||
RawPayloads: cloneRawPayloads(rawPayloads),
|
||||
@@ -183,6 +186,35 @@ func inferRedfishCollectionTime(managerDoc map[string]interface{}, rawPayloads m
|
||||
return time.Time{}, offset
|
||||
}
|
||||
|
||||
// inferInventoryLastModifiedTime reads InventoryData/Status.InventoryData.LastModifiedTime
|
||||
// from the Redfish snapshot. Returns zero time if not present or unparseable.
|
||||
func inferInventoryLastModifiedTime(snapshot map[string]interface{}) time.Time {
|
||||
docAny, ok := snapshot["/redfish/v1/Oem/Ami/InventoryData/Status"]
|
||||
if !ok {
|
||||
return time.Time{}
|
||||
}
|
||||
doc, ok := docAny.(map[string]interface{})
|
||||
if !ok {
|
||||
return time.Time{}
|
||||
}
|
||||
invData, ok := doc["InventoryData"].(map[string]interface{})
|
||||
if !ok {
|
||||
return time.Time{}
|
||||
}
|
||||
raw := strings.TrimSpace(asString(invData["LastModifiedTime"]))
|
||||
if raw == "" {
|
||||
return time.Time{}
|
||||
}
|
||||
for _, layout := range []string{time.RFC3339, time.RFC3339Nano} {
|
||||
if ts, err := time.Parse(layout, raw); err == nil {
|
||||
t := ts.UTC()
|
||||
log.Printf("redfish replay: inventory last modified at %s (InventoryData/Status.LastModifiedTime)", t.Format(time.RFC3339))
|
||||
return t
|
||||
}
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func appendMissingServerModelWarning(result *models.AnalysisResult, systemDoc map[string]interface{}, systemFRUPath, chassisFRUPath string) {
|
||||
if result == nil || result.Hardware == nil {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user