optimize redfish post-probe and add eta progress
This commit is contained in:
@@ -689,9 +689,21 @@ func (c *RedfishConnector) collectRawRedfishTree(ctx context.Context, client *ht
|
||||
|
||||
// Some Supermicro BMCs expose NVMe disks at direct Disk.Bay endpoints even when the
|
||||
// Drives collection returns Members: []. Probe those paths so raw export can be replayed.
|
||||
driveCollections := make([]string, 0)
|
||||
for path := range out {
|
||||
if !strings.HasSuffix(normalizeRedfishPath(path), "/Drives") {
|
||||
continue
|
||||
if strings.HasSuffix(normalizeRedfishPath(path), "/Drives") {
|
||||
driveCollections = append(driveCollections, normalizeRedfishPath(path))
|
||||
}
|
||||
}
|
||||
sort.Strings(driveCollections)
|
||||
nvmeProbeStart := time.Now()
|
||||
for i, path := range driveCollections {
|
||||
if emit != nil && len(driveCollections) > 0 && (i == 0 || i%4 == 0 || i == len(driveCollections)-1) {
|
||||
emit(Progress{
|
||||
Status: "running",
|
||||
Progress: 97,
|
||||
Message: fmt.Sprintf("Redfish snapshot: post-probe NVMe (%d/%d, ETA≈%s), коллекция=%s", i+1, len(driveCollections), formatETA(estimateProgressETA(nvmeProbeStart, i, len(driveCollections), 2*time.Second)), compactProgressPath(path)),
|
||||
})
|
||||
}
|
||||
for _, bayPath := range directDiskBayCandidates(path) {
|
||||
doc, err := c.getJSON(ctx, client, req, baseURL, bayPath)
|
||||
@@ -707,14 +719,38 @@ func (c *RedfishConnector) collectRawRedfishTree(ctx context.Context, client *ht
|
||||
}
|
||||
// Some BMCs under-report collection Members for sensors/PSU subresources but still serve
|
||||
// direct numeric child endpoints. Probe common collections to maximize raw snapshot fidelity.
|
||||
postProbeCollections := make([]string, 0)
|
||||
for path := range out {
|
||||
if shouldPostProbeCollectionPath(path) {
|
||||
postProbeCollections = append(postProbeCollections, normalizeRedfishPath(path))
|
||||
}
|
||||
}
|
||||
sort.Strings(postProbeCollections)
|
||||
postProbeStart := time.Now()
|
||||
addedPostProbe := 0
|
||||
for i, path := range postProbeCollections {
|
||||
if emit != nil && len(postProbeCollections) > 0 && (i == 0 || i%8 == 0 || i == len(postProbeCollections)-1) {
|
||||
emit(Progress{
|
||||
Status: "running",
|
||||
Progress: 98,
|
||||
Message: fmt.Sprintf("Redfish snapshot: post-probe коллекций (%d/%d, ETA≈%s), текущая=%s", i+1, len(postProbeCollections), formatETA(estimateProgressETA(postProbeStart, i, len(postProbeCollections), 3*time.Second)), compactProgressPath(path)),
|
||||
})
|
||||
}
|
||||
for childPath, doc := range c.probeDirectRedfishCollectionChildren(ctx, client, req, baseURL, path) {
|
||||
if _, exists := out[childPath]; exists {
|
||||
continue
|
||||
}
|
||||
out[childPath] = doc
|
||||
addedPostProbe++
|
||||
}
|
||||
}
|
||||
if emit != nil && addedPostProbe > 0 {
|
||||
emit(Progress{
|
||||
Status: "running",
|
||||
Progress: 98,
|
||||
Message: fmt.Sprintf("Redfish snapshot: post-probe добавлено %d документов", addedPostProbe),
|
||||
})
|
||||
}
|
||||
|
||||
if emit != nil {
|
||||
emit(Progress{
|
||||
@@ -887,6 +923,31 @@ func directNumericProbePlan(collectionPath string) (maxItems, startIndex, missBu
|
||||
}
|
||||
}
|
||||
|
||||
func shouldPostProbeCollectionPath(path string) bool {
|
||||
path = normalizeRedfishPath(path)
|
||||
// Restrict expensive post-probe to collections that historically recover
|
||||
// missing inventory/telemetry on partially implemented BMCs.
|
||||
switch {
|
||||
case strings.HasSuffix(path, "/Sensors"),
|
||||
strings.HasSuffix(path, "/ThresholdSensors"),
|
||||
strings.HasSuffix(path, "/DiscreteSensors"),
|
||||
strings.HasSuffix(path, "/Temperatures"),
|
||||
strings.HasSuffix(path, "/Fans"),
|
||||
strings.HasSuffix(path, "/Voltages"),
|
||||
strings.HasSuffix(path, "/PowerSupplies"),
|
||||
strings.HasSuffix(path, "/EthernetInterfaces"),
|
||||
strings.HasSuffix(path, "/NetworkPorts"),
|
||||
strings.HasSuffix(path, "/Ports"),
|
||||
strings.HasSuffix(path, "/PCIeDevices"),
|
||||
strings.HasSuffix(path, "/PCIeFunctions"),
|
||||
strings.HasSuffix(path, "/Drives"),
|
||||
strings.HasSuffix(path, "/Volumes"):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func looksLikeRedfishResource(doc map[string]interface{}) bool {
|
||||
if len(doc) == 0 {
|
||||
return false
|
||||
@@ -1145,6 +1206,13 @@ func shouldCrawlPath(path string) bool {
|
||||
return false
|
||||
}
|
||||
normalized := normalizeRedfishPath(path)
|
||||
if strings.Contains(normalized, "/Chassis/") &&
|
||||
strings.Contains(normalized, "/PCIeDevices/") &&
|
||||
strings.Contains(normalized, "/PCIeFunctions/") {
|
||||
// Chassis-level PCIeFunctions links are frequently noisy/slow on some BMCs
|
||||
// and duplicate data we already collect from PCIe devices/functions elsewhere.
|
||||
return false
|
||||
}
|
||||
if strings.Contains(normalized, "/Memory/") {
|
||||
after := strings.SplitN(normalized, "/Memory/", 2)
|
||||
if len(after) == 2 && strings.Count(after[1], "/") >= 1 {
|
||||
@@ -2753,6 +2821,24 @@ func estimatePlanBETA(targets int) time.Duration {
|
||||
return time.Duration(targets) * perTarget
|
||||
}
|
||||
|
||||
func estimateProgressETA(start time.Time, processed, total int, fallbackPerItem time.Duration) time.Duration {
|
||||
if total <= 0 || processed >= total {
|
||||
return 0
|
||||
}
|
||||
remaining := total - processed
|
||||
if processed <= 0 {
|
||||
if fallbackPerItem <= 0 {
|
||||
fallbackPerItem = time.Second
|
||||
}
|
||||
return time.Duration(remaining) * fallbackPerItem
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
if elapsed <= 0 {
|
||||
return 0
|
||||
}
|
||||
return time.Duration(float64(elapsed) * float64(remaining) / float64(processed))
|
||||
}
|
||||
|
||||
func formatETA(d time.Duration) string {
|
||||
if d <= 0 {
|
||||
return "<1s"
|
||||
|
||||
@@ -779,4 +779,22 @@ func TestShouldCrawlPath_MemorySubresourcesAreSkipped(t *testing.T) {
|
||||
if shouldCrawlPath("/redfish/v1/Systems/1/Memory/CPU0_C0D0/MemoryMetrics") {
|
||||
t.Fatalf("expected DIMM metrics subresource to be skipped")
|
||||
}
|
||||
if shouldCrawlPath("/redfish/v1/Chassis/1/PCIeDevices/0/PCIeFunctions/1") {
|
||||
t.Fatalf("expected noisy chassis pciefunctions branch to be skipped")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldPostProbeCollectionPath(t *testing.T) {
|
||||
if !shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Sensors") {
|
||||
t.Fatalf("expected sensors collection to be post-probed")
|
||||
}
|
||||
if !shouldPostProbeCollectionPath("/redfish/v1/Systems/1/Storage/RAID/Drives") {
|
||||
t.Fatalf("expected drives collection to be post-probed")
|
||||
}
|
||||
if shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Boards/BOARD1") {
|
||||
t.Fatalf("expected board member resource to be skipped from post-probe")
|
||||
}
|
||||
if shouldPostProbeCollectionPath("/redfish/v1/Chassis/1/Assembly/Oem/COMMONb/COMMONbAssembly/1") {
|
||||
t.Fatalf("expected assembly member resource to be skipped from post-probe")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user