Add Inspur Group OEM Redfish profile

This commit is contained in:
Mikhail Chusavitin
2026-03-25 15:08:40 +03:00
parent 9b71c4a95f
commit 8942991f0c
10 changed files with 583 additions and 28 deletions

View File

@@ -147,6 +147,7 @@ func (c *RedfishConnector) Collect(ctx context.Context, req Request, emit Progre
snapshotClient := c.httpClientWithTimeout(req, redfishSnapshotRequestTimeout())
prefetchClient := c.httpClientWithTimeout(req, redfishPrefetchRequestTimeout())
criticalClient := c.httpClientWithTimeout(req, redfishCriticalRequestTimeout())
hintClient := c.httpClientWithTimeout(req, 4*time.Second)
if emit != nil {
emit(Progress{Status: "running", Progress: 10, Message: "Redfish: подключение к BMC..."})
@@ -178,7 +179,8 @@ func (c *RedfishConnector) Collect(ctx context.Context, req Request, emit Progre
chassisDoc, _ := c.getJSON(discoveryCtx, snapshotClient, req, baseURL, primaryChassis)
managerDoc, _ := c.getJSON(discoveryCtx, snapshotClient, req, baseURL, primaryManager)
resourceHints := append(append([]string{}, systemPaths...), append(chassisPaths, managerPaths...)...)
signals := redfishprofile.CollectSignals(serviceRootDoc, systemDoc, chassisDoc, managerDoc, resourceHints)
hintDocs := c.collectProfileHintDocs(discoveryCtx, hintClient, req, baseURL, primarySystem, primaryChassis)
signals := redfishprofile.CollectSignals(serviceRootDoc, systemDoc, chassisDoc, managerDoc, resourceHints, hintDocs...)
matchResult := redfishprofile.MatchProfiles(signals)
acquisitionPlan := redfishprofile.BuildAcquisitionPlan(signals)
telemetrySummary := telemetry.Snapshot()
@@ -1557,6 +1559,33 @@ func (c *RedfishConnector) discoverMemberPaths(ctx context.Context, client *http
return nil
}
func (c *RedfishConnector) collectProfileHintDocs(ctx context.Context, client *http.Client, req Request, baseURL, systemPath, chassisPath string) []map[string]interface{} {
paths := []string{
"/redfish/v1/UpdateService/FirmwareInventory",
joinPath(systemPath, "/NetworkInterfaces"),
joinPath(chassisPath, "/Drives"),
joinPath(chassisPath, "/NetworkAdapters"),
}
seen := make(map[string]struct{}, len(paths))
docs := make([]map[string]interface{}, 0, len(paths))
for _, path := range paths {
path = normalizeRedfishPath(path)
if path == "" {
continue
}
if _, ok := seen[path]; ok {
continue
}
seen[path] = struct{}{}
doc, err := c.getJSON(ctx, client, req, baseURL, path)
if err != nil {
continue
}
docs = append(docs, doc)
}
return docs
}
func (c *RedfishConnector) collectRawRedfishTree(ctx context.Context, client *http.Client, req Request, baseURL string, seedPaths []string, tuning redfishprofile.AcquisitionTuning, emit ProgressFn) (map[string]interface{}, []map[string]interface{}, redfishPostProbeMetrics, string) {
maxDocuments := redfishSnapshotMaxDocuments(tuning)
workers := redfishSnapshotWorkers(tuning)
@@ -3476,14 +3505,20 @@ func parseCPUs(docs []map[string]interface{}) []models.CPU {
}
}
l1, l2, l3 := parseCPUCachesFromProcessorMemory(doc)
publicSerial := redfishCPUPublicSerial(doc)
serial := normalizeRedfishIdentityField(asString(doc["SerialNumber"]))
if serial == "" && publicSerial == "" {
serial = findFirstNormalizedStringByKeys(doc, "SerialNumber")
}
cpus = append(cpus, models.CPU{
Socket: socket,
Model: firstNonEmpty(asString(doc["Model"]), asString(doc["Name"])),
Cores: asInt(doc["TotalCores"]),
Threads: asInt(doc["TotalThreads"]),
FrequencyMHz: asInt(doc["OperatingSpeedMHz"]),
MaxFreqMHz: asInt(doc["MaxSpeedMHz"]),
SerialNumber: findFirstNormalizedStringByKeys(doc, "SerialNumber"),
FrequencyMHz: int(redfishFirstNumeric(doc, "OperatingSpeedMHz", "CurrentSpeedMHz", "FrequencyMHz")),
MaxFreqMHz: int(redfishFirstNumeric(doc, "MaxSpeedMHz", "TurboEnableMaxSpeedMHz", "TurboDisableMaxSpeedMHz")),
PPIN: firstNonEmpty(findFirstNormalizedStringByKeys(doc, "PPIN", "ProtectedIdentificationNumber"), publicSerial),
SerialNumber: serial,
L1CacheKB: l1,
L2CacheKB: l2,
L3CacheKB: l3,
@@ -3494,6 +3529,12 @@ func parseCPUs(docs []map[string]interface{}) []models.CPU {
return cpus
}
func redfishCPUPublicSerial(doc map[string]interface{}) string {
oem, _ := doc["Oem"].(map[string]interface{})
public, _ := oem["Public"].(map[string]interface{})
return normalizeRedfishIdentityField(asString(public["SerialNumber"]))
}
// parseCPUCachesFromProcessorMemory reads L1/L2/L3 cache sizes from the
// Redfish ProcessorMemory array (Processor.v1_x spec).
func parseCPUCachesFromProcessorMemory(doc map[string]interface{}) (l1, l2, l3 int) {
@@ -3942,7 +3983,7 @@ func parsePSUWithSupplementalDocs(doc map[string]interface{}, idx int, supplemen
Present: present,
Model: firstNonEmpty(asString(doc["Model"]), asString(doc["Name"])),
Vendor: asString(doc["Manufacturer"]),
WattageW: asInt(doc["PowerCapacityWatts"]),
WattageW: redfishPSUNominalWattage(doc),
SerialNumber: findFirstNormalizedStringByKeys(doc, "SerialNumber"),
PartNumber: asString(doc["PartNumber"]),
Firmware: asString(doc["FirmwareVersion"]),
@@ -3955,6 +3996,25 @@ func parsePSUWithSupplementalDocs(doc map[string]interface{}, idx int, supplemen
}
}
func redfishPSUNominalWattage(doc map[string]interface{}) int {
if ranges, ok := doc["InputRanges"].([]interface{}); ok {
best := 0
for _, rawRange := range ranges {
rangeDoc, ok := rawRange.(map[string]interface{})
if !ok {
continue
}
if wattage := asInt(rangeDoc["OutputWattage"]); wattage > best {
best = wattage
}
}
if best > 0 {
return best
}
}
return asInt(doc["PowerCapacityWatts"])
}
func redfishDriveDetails(doc map[string]interface{}) map[string]any {
return redfishDriveDetailsWithSupplementalDocs(doc)
}
@@ -5781,7 +5841,6 @@ func parseFirmware(system, bios, manager, networkProtocol map[string]interface{}
return out
}
func mapStatus(statusAny interface{}) string {
if statusAny == nil {
return ""