Add raw export reanalyze flow for Redfish snapshots
This commit is contained in:
@@ -542,15 +542,15 @@ func (c *RedfishConnector) collectRawRedfishTree(ctx context.Context, client *ht
|
||||
enqueue(ref)
|
||||
}
|
||||
}
|
||||
n := atomic.AddInt32(&processed, 1)
|
||||
if err != nil {
|
||||
c.debugSnapshotf("worker=%d fetch error path=%s err=%v", workerID, current, err)
|
||||
if emit != nil {
|
||||
emit(Progress{
|
||||
Status: "running",
|
||||
Progress: 92 + int(minInt32(n/200, 6)),
|
||||
Message: fmt.Sprintf("Redfish snapshot: ошибка на %s", compactProgressPath(current)),
|
||||
})
|
||||
n := atomic.AddInt32(&processed, 1)
|
||||
if err != nil {
|
||||
c.debugSnapshotf("worker=%d fetch error path=%s err=%v", workerID, current, err)
|
||||
if emit != nil && shouldReportSnapshotFetchError(err) {
|
||||
emit(Progress{
|
||||
Status: "running",
|
||||
Progress: 92 + int(minInt32(n/200, 6)),
|
||||
Message: fmt.Sprintf("Redfish snapshot: ошибка на %s", compactProgressPath(current)),
|
||||
})
|
||||
}
|
||||
}
|
||||
if emit != nil && n%40 == 0 {
|
||||
@@ -894,14 +894,20 @@ func parsePSU(doc map[string]interface{}, idx int) models.PSU {
|
||||
}
|
||||
|
||||
func parseGPU(doc map[string]interface{}, functionDocs []map[string]interface{}, idx int) models.GPU {
|
||||
slot := firstNonEmpty(asString(doc["Slot"]), asString(doc["Name"]), asString(doc["Id"]))
|
||||
slot := firstNonEmpty(
|
||||
redfishLocationLabel(doc["Slot"]),
|
||||
redfishLocationLabel(doc["Location"]),
|
||||
redfishLocationLabel(doc["PhysicalLocation"]),
|
||||
asString(doc["Name"]),
|
||||
asString(doc["Id"]),
|
||||
)
|
||||
if slot == "" {
|
||||
slot = fmt.Sprintf("GPU%d", idx)
|
||||
}
|
||||
|
||||
gpu := models.GPU{
|
||||
Slot: slot,
|
||||
Location: firstNonEmpty(asString(doc["Location"]), asString(doc["PhysicalLocation"])),
|
||||
Location: firstNonEmpty(redfishLocationLabel(doc["Location"]), redfishLocationLabel(doc["PhysicalLocation"])),
|
||||
Model: firstNonEmpty(asString(doc["Model"]), asString(doc["Name"])),
|
||||
Manufacturer: asString(doc["Manufacturer"]),
|
||||
SerialNumber: asString(doc["SerialNumber"]),
|
||||
@@ -958,7 +964,7 @@ func parseGPU(doc map[string]interface{}, functionDocs []map[string]interface{},
|
||||
|
||||
func parsePCIeDevice(doc map[string]interface{}, functionDocs []map[string]interface{}) models.PCIeDevice {
|
||||
dev := models.PCIeDevice{
|
||||
Slot: firstNonEmpty(asString(doc["Slot"]), asString(doc["Name"]), asString(doc["Id"])),
|
||||
Slot: firstNonEmpty(redfishLocationLabel(doc["Slot"]), redfishLocationLabel(doc["Location"]), asString(doc["Name"]), asString(doc["Id"])),
|
||||
BDF: asString(doc["BDF"]),
|
||||
DeviceClass: firstNonEmpty(asString(doc["DeviceType"]), asString(doc["PCIeType"])),
|
||||
Manufacturer: asString(doc["Manufacturer"]),
|
||||
@@ -1013,7 +1019,7 @@ func parsePCIeDevice(doc map[string]interface{}, functionDocs []map[string]inter
|
||||
}
|
||||
|
||||
func parsePCIeFunction(doc map[string]interface{}, idx int) models.PCIeDevice {
|
||||
slot := firstNonEmpty(asString(doc["Id"]), asString(doc["Name"]))
|
||||
slot := firstNonEmpty(redfishLocationLabel(doc["Location"]), asString(doc["Id"]), asString(doc["Name"]))
|
||||
if slot == "" {
|
||||
slot = fmt.Sprintf("PCIeFn%d", idx)
|
||||
}
|
||||
@@ -1367,6 +1373,9 @@ func normalizeRedfishPath(raw string) string {
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
if i := strings.Index(raw, "#"); i >= 0 {
|
||||
raw = raw[:i]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(raw, "http://") || strings.HasPrefix(raw, "https://") {
|
||||
u, err := url.Parse(raw)
|
||||
@@ -1444,6 +1453,45 @@ func topRoots(counts map[string]int, limit int) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
func redfishLocationLabel(v interface{}) string {
|
||||
switch typed := v.(type) {
|
||||
case nil:
|
||||
return ""
|
||||
case string:
|
||||
return strings.TrimSpace(typed)
|
||||
case map[string]interface{}:
|
||||
// Common shapes:
|
||||
// Slot.Location.PartLocation.ServiceLabel
|
||||
// Location.PartLocation.ServiceLabel
|
||||
// PartLocation.ServiceLabel
|
||||
if nested := redfishLocationLabel(typed["Location"]); nested != "" {
|
||||
return nested
|
||||
}
|
||||
if nested := redfishLocationLabel(typed["PartLocation"]); nested != "" {
|
||||
return nested
|
||||
}
|
||||
serviceLabel := asString(typed["ServiceLabel"])
|
||||
locationType := asString(typed["LocationType"])
|
||||
ordinal := asString(typed["LocationOrdinalValue"])
|
||||
if serviceLabel != "" {
|
||||
return serviceLabel
|
||||
}
|
||||
if locationType != "" && ordinal != "" {
|
||||
return fmt.Sprintf("%s %s", locationType, ordinal)
|
||||
}
|
||||
if locationType != "" {
|
||||
return locationType
|
||||
}
|
||||
if ordinal != "" {
|
||||
return "Slot " + ordinal
|
||||
}
|
||||
return ""
|
||||
default:
|
||||
// Avoid fmt.Sprint(map[]) style garbage for complex objects in UI/export.
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func compactProgressPath(p string) string {
|
||||
const maxLen = 72
|
||||
if len(p) <= maxLen {
|
||||
@@ -1452,6 +1500,20 @@ func compactProgressPath(p string) string {
|
||||
return "..." + p[len(p)-maxLen+3:]
|
||||
}
|
||||
|
||||
func shouldReportSnapshotFetchError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
msg := err.Error()
|
||||
if strings.HasPrefix(msg, "status 404 ") ||
|
||||
strings.HasPrefix(msg, "status 405 ") ||
|
||||
strings.HasPrefix(msg, "status 410 ") ||
|
||||
strings.HasPrefix(msg, "status 501 ") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func minInt32(a, b int32) int32 {
|
||||
if a < b {
|
||||
return a
|
||||
|
||||
Reference in New Issue
Block a user