Implement the full architectural plan: unified ingest.Service entry point for archive and Redfish payloads, modular redfishprofile package with composable profiles (generic, ami-family, msi, supermicro, dell, hgx-topology), score-based profile matching with fallback expansion mode, and profile-driven acquisition/analysis plans. Vendor-specific logic moved out of common executors and into profile hooks. GPU chassis lookup strategies and known storage recovery collections (IntelVROC/HA-RAID/MRVL) now live in ResolvedAnalysisPlan, populated by profiles at analysis time. Replay helpers read from the plan; no hardcoded path lists remain in generic code. Also splits redfish_replay.go into domain modules (gpu, storage, inventory, fru, profiles) and adds full fixture/matcher/directive test coverage including Dell, AMI, unknown-vendor fallback, and deterministic ordering. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
101 lines
2.7 KiB
Go
101 lines
2.7 KiB
Go
package redfishprofile
|
|
|
|
import "strings"
|
|
|
|
func ResolveAnalysisPlan(match MatchResult, snapshot map[string]interface{}, discovered DiscoveredResources, signals MatchSignals) ResolvedAnalysisPlan {
|
|
plan := ResolvedAnalysisPlan{
|
|
Match: match,
|
|
Directives: AnalysisDirectives{},
|
|
}
|
|
if match.Mode == ModeFallback {
|
|
plan.Directives.EnableProcessorGPUFallback = true
|
|
plan.Directives.EnableSupermicroNVMeBackplane = true
|
|
plan.Directives.EnableProcessorGPUChassisAlias = true
|
|
plan.Directives.EnableGenericGraphicsControllerDedup = true
|
|
plan.Directives.EnableStorageEnclosureRecovery = true
|
|
plan.Directives.EnableKnownStorageControllerRecovery = true
|
|
addAnalysisLookupMode(&plan, "msi-index")
|
|
addAnalysisLookupMode(&plan, "hgx-alias")
|
|
addAnalysisStorageDriveCollections(&plan,
|
|
"/Storage/IntelVROC/Drives",
|
|
"/Storage/IntelVROC/Controllers/1/Drives",
|
|
)
|
|
addAnalysisStorageVolumeCollections(&plan,
|
|
"/Storage/IntelVROC/Volumes",
|
|
"/Storage/HA-RAID/Volumes",
|
|
"/Storage/MRVL.HA-RAID/Volumes",
|
|
)
|
|
addAnalysisNote(&plan, "fallback analysis enables broad recovery directives")
|
|
}
|
|
for _, profile := range match.Profiles {
|
|
profile.ApplyAnalysisDirectives(&plan.Directives, signals)
|
|
}
|
|
for _, profile := range match.Profiles {
|
|
profile.RefineAnalysisPlan(&plan, snapshot, discovered, signals)
|
|
}
|
|
return plan
|
|
}
|
|
|
|
func snapshotHasPathPrefix(snapshot map[string]interface{}, prefix string) bool {
|
|
prefix = normalizePath(prefix)
|
|
if prefix == "" {
|
|
return false
|
|
}
|
|
for path := range snapshot {
|
|
if strings.HasPrefix(normalizePath(path), prefix) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func snapshotHasPathContaining(snapshot map[string]interface{}, sub string) bool {
|
|
sub = strings.ToLower(strings.TrimSpace(sub))
|
|
if sub == "" {
|
|
return false
|
|
}
|
|
for path := range snapshot {
|
|
if strings.Contains(strings.ToLower(path), sub) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func snapshotHasGPUProcessor(snapshot map[string]interface{}, systemPaths []string) bool {
|
|
for _, systemPath := range systemPaths {
|
|
prefix := normalizePath(joinPath(systemPath, "/Processors")) + "/"
|
|
for path, docAny := range snapshot {
|
|
if !strings.HasPrefix(normalizePath(path), prefix) {
|
|
continue
|
|
}
|
|
doc, ok := docAny.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
if strings.EqualFold(strings.TrimSpace(asString(doc["ProcessorType"])), "GPU") {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func snapshotHasStorageControllerHint(snapshot map[string]interface{}, needles ...string) bool {
|
|
for _, needle := range needles {
|
|
if snapshotHasPathContaining(snapshot, needle) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func asString(v interface{}) string {
|
|
switch x := v.(type) {
|
|
case string:
|
|
return x
|
|
default:
|
|
return ""
|
|
}
|
|
}
|