sync file-type support across upload/convert and fix collected_at timezone handling
This commit is contained in:
@@ -86,8 +86,11 @@ func ReplayRedfishFromRawPayloads(rawPayloads map[string]any, emit ProgressFn) (
|
||||
firmware = dedupeFirmwareInfo(append(firmware, r.collectFirmwareInventory()...))
|
||||
boardInfo := parseBoardInfoWithFallback(systemDoc, chassisDoc, fruDoc)
|
||||
applyBoardInfoFallbackFromDocs(&boardInfo, boardFallbackDocs)
|
||||
collectedAt, sourceTimezone := inferRedfishCollectionTime(managerDoc, rawPayloads)
|
||||
|
||||
result := &models.AnalysisResult{
|
||||
CollectedAt: collectedAt,
|
||||
SourceTimezone: sourceTimezone,
|
||||
Events: append(append(append(make([]models.Event, 0, len(discreteEvents)+len(healthEvents)+len(driveFetchWarningEvents)+1), healthEvents...), discreteEvents...), driveFetchWarningEvents...),
|
||||
FRU: make([]models.FRUInfo, 0),
|
||||
Sensors: dedupeSensorReadings(append(append(thresholdSensors, thermalSensors...), powerSensors...)),
|
||||
@@ -105,10 +108,41 @@ func ReplayRedfishFromRawPayloads(rawPayloads map[string]any, emit ProgressFn) (
|
||||
Firmware: firmware,
|
||||
},
|
||||
}
|
||||
if strings.TrimSpace(sourceTimezone) != "" {
|
||||
if result.RawPayloads == nil {
|
||||
result.RawPayloads = map[string]any{}
|
||||
}
|
||||
result.RawPayloads["source_timezone"] = sourceTimezone
|
||||
}
|
||||
appendMissingServerModelWarning(result, systemDoc, joinPath(primarySystem, "/Oem/Public/FRU"), joinPath(primaryChassis, "/Oem/Public/FRU"))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func inferRedfishCollectionTime(managerDoc map[string]interface{}, rawPayloads map[string]any) (time.Time, string) {
|
||||
dateTime := strings.TrimSpace(asString(managerDoc["DateTime"]))
|
||||
offset := strings.TrimSpace(asString(managerDoc["DateTimeLocalOffset"]))
|
||||
if dateTime != "" {
|
||||
if ts, err := time.Parse(time.RFC3339Nano, dateTime); err == nil {
|
||||
if offset == "" {
|
||||
offset = ts.Format("-07:00")
|
||||
}
|
||||
return ts.UTC(), offset
|
||||
}
|
||||
if ts, err := time.Parse(time.RFC3339, dateTime); err == nil {
|
||||
if offset == "" {
|
||||
offset = ts.Format("-07:00")
|
||||
}
|
||||
return ts.UTC(), offset
|
||||
}
|
||||
}
|
||||
if offset == "" && len(rawPayloads) > 0 {
|
||||
if tz, ok := rawPayloads["source_timezone"].(string); ok {
|
||||
offset = strings.TrimSpace(tz)
|
||||
}
|
||||
}
|
||||
return time.Time{}, offset
|
||||
}
|
||||
|
||||
func appendMissingServerModelWarning(result *models.AnalysisResult, systemDoc map[string]interface{}, systemFRUPath, chassisFRUPath string) {
|
||||
if result == nil || result.Hardware == nil {
|
||||
return
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.mchus.pro/mchus/logpile/internal/models"
|
||||
)
|
||||
@@ -305,6 +306,52 @@ func TestReplayRedfishFromRawPayloads_FallbackCollectionMembersByPrefix(t *testi
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplayRedfishFromRawPayloads_PreservesSourceTimezoneAndUTCCollectedAt(t *testing.T) {
|
||||
raw := map[string]any{
|
||||
"redfish_tree": map[string]interface{}{
|
||||
"/redfish/v1": map[string]interface{}{
|
||||
"Systems": map[string]interface{}{"@odata.id": "/redfish/v1/Systems"},
|
||||
"Chassis": map[string]interface{}{"@odata.id": "/redfish/v1/Chassis"},
|
||||
"Managers": map[string]interface{}{"@odata.id": "/redfish/v1/Managers"},
|
||||
},
|
||||
"/redfish/v1/Systems": map[string]interface{}{
|
||||
"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"}},
|
||||
},
|
||||
"/redfish/v1/Systems/1": map[string]interface{}{
|
||||
"Manufacturer": "Inspur",
|
||||
"Model": "NF5688M7",
|
||||
"SerialNumber": "23E100051",
|
||||
},
|
||||
"/redfish/v1/Chassis": map[string]interface{}{
|
||||
"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"}},
|
||||
},
|
||||
"/redfish/v1/Chassis/1": map[string]interface{}{"Id": "1"},
|
||||
"/redfish/v1/Managers": map[string]interface{}{
|
||||
"Members": []interface{}{map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"}},
|
||||
},
|
||||
"/redfish/v1/Managers/1": map[string]interface{}{
|
||||
"DateTime": "2026-02-28T04:18:18+08:00",
|
||||
"DateTimeLocalOffset": "+08:00",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got, err := ReplayRedfishFromRawPayloads(raw, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("replay failed: %v", err)
|
||||
}
|
||||
if got.SourceTimezone != "+08:00" {
|
||||
t.Fatalf("expected source_timezone +08:00, got %q", got.SourceTimezone)
|
||||
}
|
||||
wantCollectedAt := time.Date(2026, 2, 27, 20, 18, 18, 0, time.UTC)
|
||||
if !got.CollectedAt.Equal(wantCollectedAt) {
|
||||
t.Fatalf("expected collected_at %s, got %s", wantCollectedAt, got.CollectedAt)
|
||||
}
|
||||
if got.RawPayloads["source_timezone"] != "+08:00" {
|
||||
t.Fatalf("expected source_timezone in raw payloads, got %#v", got.RawPayloads["source_timezone"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplayRedfishFromRawPayloads_ParsesInlineThresholdAndDiscreteSensors(t *testing.T) {
|
||||
raw := map[string]any{
|
||||
"redfish_tree": map[string]interface{}{
|
||||
|
||||
Reference in New Issue
Block a user