refactor: unified ingest pipeline + modular Redfish profile framework

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>
This commit is contained in:
Mikhail Chusavitin
2026-03-18 08:48:58 +03:00
parent d8d3d8c524
commit d650a6ba1c
45 changed files with 5231 additions and 1011 deletions
@@ -0,0 +1,163 @@
package redfishprofile
import "strings"
func ResolveAcquisitionPlan(match MatchResult, plan AcquisitionPlan, discovered DiscoveredResources, signals MatchSignals) ResolvedAcquisitionPlan {
seedGroups := [][]string{
baselineSeedPaths(discovered),
expandScopedSuffixes(discovered.SystemPaths, plan.ScopedPaths.SystemSeedSuffixes),
expandScopedSuffixes(discovered.ChassisPaths, plan.ScopedPaths.ChassisSeedSuffixes),
expandScopedSuffixes(discovered.ManagerPaths, plan.ScopedPaths.ManagerSeedSuffixes),
plan.SeedPaths,
}
if plan.Mode == ModeFallback {
seedGroups = append(seedGroups, plan.PlanBPaths)
}
criticalGroups := [][]string{
baselineCriticalPaths(discovered),
expandScopedSuffixes(discovered.SystemPaths, plan.ScopedPaths.SystemCriticalSuffixes),
expandScopedSuffixes(discovered.ChassisPaths, plan.ScopedPaths.ChassisCriticalSuffixes),
expandScopedSuffixes(discovered.ManagerPaths, plan.ScopedPaths.ManagerCriticalSuffixes),
plan.CriticalPaths,
}
resolved := ResolvedAcquisitionPlan{
Plan: plan,
SeedPaths: mergeResolvedPaths(seedGroups...),
CriticalPaths: mergeResolvedPaths(criticalGroups...),
}
for _, profile := range match.Profiles {
profile.RefineAcquisitionPlan(&resolved, discovered, signals)
}
resolved.SeedPaths = mergeResolvedPaths(resolved.SeedPaths)
resolved.CriticalPaths = mergeResolvedPaths(resolved.CriticalPaths, resolved.Plan.CriticalPaths)
resolved.Plan.SeedPaths = mergeResolvedPaths(resolved.Plan.SeedPaths)
resolved.Plan.CriticalPaths = mergeResolvedPaths(resolved.Plan.CriticalPaths)
resolved.Plan.PlanBPaths = mergeResolvedPaths(resolved.Plan.PlanBPaths)
return resolved
}
func baselineSeedPaths(discovered DiscoveredResources) []string {
var out []string
add := func(p string) {
if p = normalizePath(p); p != "" {
out = append(out, p)
}
}
add("/redfish/v1/UpdateService")
add("/redfish/v1/UpdateService/FirmwareInventory")
for _, p := range discovered.SystemPaths {
add(p)
add(joinPath(p, "/Bios"))
add(joinPath(p, "/SecureBoot"))
add(joinPath(p, "/Oem/Public"))
add(joinPath(p, "/Oem/Public/FRU"))
add(joinPath(p, "/Processors"))
add(joinPath(p, "/Memory"))
add(joinPath(p, "/EthernetInterfaces"))
add(joinPath(p, "/NetworkInterfaces"))
add(joinPath(p, "/PCIeDevices"))
add(joinPath(p, "/PCIeFunctions"))
add(joinPath(p, "/Accelerators"))
add(joinPath(p, "/GraphicsControllers"))
add(joinPath(p, "/Storage"))
}
for _, p := range discovered.ChassisPaths {
add(p)
add(joinPath(p, "/Oem/Public"))
add(joinPath(p, "/Oem/Public/FRU"))
add(joinPath(p, "/PCIeDevices"))
add(joinPath(p, "/PCIeSlots"))
add(joinPath(p, "/NetworkAdapters"))
add(joinPath(p, "/Drives"))
add(joinPath(p, "/Power"))
}
for _, p := range discovered.ManagerPaths {
add(p)
add(joinPath(p, "/EthernetInterfaces"))
add(joinPath(p, "/NetworkProtocol"))
}
return mergeResolvedPaths(out)
}
func baselineCriticalPaths(discovered DiscoveredResources) []string {
var out []string
for _, group := range [][]string{
{"/redfish/v1"},
discovered.SystemPaths,
discovered.ChassisPaths,
discovered.ManagerPaths,
} {
out = append(out, group...)
}
return mergeResolvedPaths(out)
}
func expandScopedSuffixes(basePaths, suffixes []string) []string {
if len(basePaths) == 0 || len(suffixes) == 0 {
return nil
}
out := make([]string, 0, len(basePaths)*len(suffixes))
for _, basePath := range basePaths {
basePath = normalizePath(basePath)
if basePath == "" {
continue
}
for _, suffix := range suffixes {
suffix = strings.TrimSpace(suffix)
if suffix == "" {
continue
}
out = append(out, joinPath(basePath, suffix))
}
}
return mergeResolvedPaths(out)
}
func mergeResolvedPaths(groups ...[]string) []string {
seen := make(map[string]struct{})
out := make([]string, 0)
for _, group := range groups {
for _, path := range group {
path = normalizePath(path)
if path == "" {
continue
}
if _, ok := seen[path]; ok {
continue
}
seen[path] = struct{}{}
out = append(out, path)
}
}
return out
}
func normalizePath(path string) string {
path = strings.TrimSpace(path)
if path == "" {
return ""
}
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
return strings.TrimRight(path, "/")
}
func joinPath(base, rel string) string {
base = normalizePath(base)
rel = strings.TrimSpace(rel)
if base == "" {
return normalizePath(rel)
}
if rel == "" {
return base
}
if !strings.HasPrefix(rel, "/") {
rel = "/" + rel
}
return normalizePath(base + rel)
}
@@ -0,0 +1,100 @@
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 ""
}
}
@@ -0,0 +1,405 @@
package redfishprofile
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
func TestBuildAcquisitionPlan_Fixture_MSI_CG480(t *testing.T) {
signals := loadProfileFixtureSignals(t, "msi-cg480.json")
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, discoveredResourcesFromSignals(signals), signals)
if match.Mode != ModeMatched {
t.Fatalf("expected matched mode, got %q", match.Mode)
}
assertProfileSelected(t, match, "msi")
assertProfileSelected(t, match, "ami-family")
assertProfileNotSelected(t, match, "hgx-topology")
if plan.Tuning.PrefetchWorkers < 6 {
t.Fatalf("expected msi prefetch worker tuning, got %d", plan.Tuning.PrefetchWorkers)
}
if !containsString(resolved.SeedPaths, "/redfish/v1/Chassis/GPU1") {
t.Fatalf("expected MSI chassis GPU seed path")
}
if !containsString(resolved.CriticalPaths, "/redfish/v1/Chassis/GPU1/Sensors") {
t.Fatal("expected MSI GPU sensor critical path")
}
if !containsString(resolved.Plan.PlanBPaths, "/redfish/v1/Chassis/GPU1/Sensors") {
t.Fatal("expected MSI GPU sensor plan-b path")
}
if plan.Tuning.ETABaseline.SnapshotSeconds <= 0 {
t.Fatal("expected MSI snapshot eta baseline")
}
if !plan.Tuning.PostProbePolicy.EnableNumericCollectionProbe {
t.Fatal("expected MSI fixture to inherit generic numeric post-probe policy")
}
if !containsString(plan.ScopedPaths.SystemSeedSuffixes, "/SimpleStorage") {
t.Fatal("expected MSI fixture to inherit generic SimpleStorage scoped seed suffix")
}
if !containsString(plan.ScopedPaths.SystemCriticalSuffixes, "/Memory") {
t.Fatal("expected MSI fixture to inherit generic system critical suffixes")
}
if !containsString(plan.Tuning.PrefetchPolicy.IncludeSuffixes, "/Storage") {
t.Fatal("expected MSI fixture to inherit generic storage prefetch policy")
}
if !containsString(plan.CriticalPaths, "/redfish/v1/UpdateService") {
t.Fatal("expected MSI fixture to inherit generic top-level critical path")
}
if !plan.Tuning.RecoveryPolicy.EnableProfilePlanB {
t.Fatal("expected MSI fixture to enable profile plan-b")
}
}
func TestBuildAcquisitionPlan_Fixture_MSI_CG480_CopyMatchesSameProfiles(t *testing.T) {
originalSignals := loadProfileFixtureSignals(t, "msi-cg480.json")
copySignals := loadProfileFixtureSignals(t, "msi-cg480-copy.json")
originalMatch := MatchProfiles(originalSignals)
copyMatch := MatchProfiles(copySignals)
originalPlan := BuildAcquisitionPlan(originalSignals)
copyPlan := BuildAcquisitionPlan(copySignals)
originalResolved := ResolveAcquisitionPlan(originalMatch, originalPlan, discoveredResourcesFromSignals(originalSignals), originalSignals)
copyResolved := ResolveAcquisitionPlan(copyMatch, copyPlan, discoveredResourcesFromSignals(copySignals), copySignals)
assertSameProfileNames(t, originalMatch, copyMatch)
if originalPlan.Tuning.PrefetchWorkers != copyPlan.Tuning.PrefetchWorkers {
t.Fatalf("expected same MSI prefetch worker tuning, got %d vs %d", originalPlan.Tuning.PrefetchWorkers, copyPlan.Tuning.PrefetchWorkers)
}
if containsString(originalResolved.SeedPaths, "/redfish/v1/Chassis/GPU1") != containsString(copyResolved.SeedPaths, "/redfish/v1/Chassis/GPU1") {
t.Fatal("expected same MSI GPU chassis seed presence in both fixtures")
}
}
func TestBuildAcquisitionPlan_Fixture_MSI_CG290(t *testing.T) {
signals := loadProfileFixtureSignals(t, "msi-cg290.json")
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, discoveredResourcesFromSignals(signals), signals)
if match.Mode != ModeMatched {
t.Fatalf("expected matched mode, got %q", match.Mode)
}
assertProfileSelected(t, match, "msi")
assertProfileSelected(t, match, "ami-family")
assertProfileNotSelected(t, match, "hgx-topology")
if plan.Tuning.PrefetchWorkers < 6 {
t.Fatalf("expected MSI prefetch worker tuning, got %d", plan.Tuning.PrefetchWorkers)
}
if !containsString(resolved.SeedPaths, "/redfish/v1/Chassis/GPU1") {
t.Fatalf("expected MSI chassis GPU seed path")
}
}
func TestBuildAcquisitionPlan_Fixture_Supermicro_HGX(t *testing.T) {
signals := loadProfileFixtureSignals(t, "supermicro-hgx.json")
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
discovered := discoveredResourcesFromSignals(signals)
discovered.SystemPaths = dedupeSorted(append(discovered.SystemPaths, "/redfish/v1/Systems/HGX_Baseboard_0"))
resolved := ResolveAcquisitionPlan(match, plan, discovered, signals)
if match.Mode != ModeMatched {
t.Fatalf("expected matched mode, got %q", match.Mode)
}
assertProfileSelected(t, match, "supermicro")
assertProfileSelected(t, match, "hgx-topology")
assertProfileNotSelected(t, match, "msi")
if plan.Tuning.SnapshotMaxDocuments < 180000 {
t.Fatalf("expected widened HGX snapshot cap, got %d", plan.Tuning.SnapshotMaxDocuments)
}
if plan.Tuning.NVMePostProbeEnabled == nil || *plan.Tuning.NVMePostProbeEnabled {
t.Fatal("expected HGX fixture to disable NVMe post-probe")
}
if !containsString(resolved.SeedPaths, "/redfish/v1/Systems/HGX_Baseboard_0/Processors") {
t.Fatal("expected HGX baseboard processors seed path")
}
if !containsString(resolved.CriticalPaths, "/redfish/v1/Systems/HGX_Baseboard_0/Processors") {
t.Fatal("expected HGX baseboard processors critical path")
}
if !containsString(resolved.Plan.PlanBPaths, "/redfish/v1/Systems/HGX_Baseboard_0/Processors") {
t.Fatal("expected HGX baseboard processors plan-b path")
}
if plan.Tuning.ETABaseline.SnapshotSeconds < 300 {
t.Fatalf("expected HGX snapshot eta baseline, got %d", plan.Tuning.ETABaseline.SnapshotSeconds)
}
if !plan.Tuning.PostProbePolicy.EnableDirectNVMEDiskBayProbe {
t.Fatal("expected HGX fixture to retain Supermicro direct NVMe disk bay probe policy")
}
if !containsString(plan.ScopedPaths.SystemCriticalSuffixes, "/Storage/IntelVROC/Drives") {
t.Fatal("expected HGX fixture to inherit generic IntelVROC scoped critical suffix")
}
if !containsString(plan.ScopedPaths.ChassisCriticalSuffixes, "/Assembly") {
t.Fatal("expected HGX fixture to inherit generic chassis critical suffixes")
}
if !containsString(plan.Tuning.PrefetchPolicy.ExcludeContains, "/Assembly") {
t.Fatal("expected HGX fixture to inherit generic assembly prefetch exclusion")
}
if !plan.Tuning.RecoveryPolicy.EnableProfilePlanB {
t.Fatal("expected HGX fixture to enable profile plan-b")
}
}
func TestBuildAcquisitionPlan_Fixture_Supermicro_OAM_NoHGX(t *testing.T) {
signals := loadProfileFixtureSignals(t, "supermicro-oam-amd.json")
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, discoveredResourcesFromSignals(signals), signals)
if match.Mode != ModeMatched {
t.Fatalf("expected matched mode, got %q", match.Mode)
}
assertProfileSelected(t, match, "supermicro")
assertProfileNotSelected(t, match, "hgx-topology")
assertProfileNotSelected(t, match, "msi")
if containsString(resolved.SeedPaths, "/redfish/v1/Systems/HGX_Baseboard_0/Processors") {
t.Fatal("did not expect HGX baseboard processors seed path for OAM fixture")
}
if containsString(resolved.CriticalPaths, "/redfish/v1/Systems/HGX_Baseboard_0/Processors") {
t.Fatal("did not expect HGX baseboard processors critical path for OAM fixture")
}
if !containsString(resolved.CriticalPaths, "/redfish/v1/UpdateService/Oem/Supermicro/FirmwareInventory") {
t.Fatal("expected Supermicro firmware critical path")
}
if !containsString(resolved.Plan.PlanBPaths, "/redfish/v1/UpdateService/Oem/Supermicro/FirmwareInventory") {
t.Fatal("expected Supermicro firmware plan-b path")
}
if plan.Tuning.SnapshotMaxDocuments != 150000 {
t.Fatalf("expected generic supermicro snapshot cap, got %d", plan.Tuning.SnapshotMaxDocuments)
}
if plan.Tuning.NVMePostProbeEnabled != nil {
t.Fatal("did not expect HGX NVMe tuning for OAM fixture")
}
if plan.Tuning.ETABaseline.SnapshotSeconds < 180 {
t.Fatalf("expected Supermicro snapshot eta baseline, got %d", plan.Tuning.ETABaseline.SnapshotSeconds)
}
if !plan.Tuning.PostProbePolicy.EnableDirectNVMEDiskBayProbe {
t.Fatal("expected Supermicro OAM fixture to use direct NVMe disk bay probe policy")
}
if !plan.Tuning.PostProbePolicy.EnableNumericCollectionProbe {
t.Fatal("expected Supermicro OAM fixture to inherit generic numeric post-probe policy")
}
if !containsString(plan.ScopedPaths.SystemSeedSuffixes, "/Storage/IntelVROC") {
t.Fatal("expected Supermicro OAM fixture to inherit generic IntelVROC scoped seed suffix")
}
if !plan.Tuning.RecoveryPolicy.EnableProfilePlanB {
t.Fatal("expected Supermicro OAM fixture to enable profile plan-b")
}
}
func TestBuildAcquisitionPlan_Fixture_Dell_R750(t *testing.T) {
signals := loadProfileFixtureSignals(t, "dell-r750.json")
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
SystemPaths: []string{"/redfish/v1/Systems/System.Embedded.1"},
ChassisPaths: []string{"/redfish/v1/Chassis/System.Embedded.1"},
ManagerPaths: []string{"/redfish/v1/Managers/1", "/redfish/v1/Managers/iDRAC.Embedded.1"},
}, signals)
if match.Mode != ModeMatched {
t.Fatalf("expected matched mode, got %q", match.Mode)
}
assertProfileSelected(t, match, "dell")
assertProfileNotSelected(t, match, "supermicro")
assertProfileNotSelected(t, match, "hgx-topology")
assertProfileNotSelected(t, match, "msi")
if !plan.Tuning.RecoveryPolicy.EnableProfilePlanB {
t.Fatal("expected dell fixture to enable profile plan-b")
}
if !containsString(resolved.SeedPaths, "/redfish/v1/Managers/iDRAC.Embedded.1") {
t.Fatal("expected Dell refinement to add iDRAC manager seed path")
}
if !containsString(resolved.CriticalPaths, "/redfish/v1/Managers/iDRAC.Embedded.1") {
t.Fatal("expected Dell refinement to add iDRAC manager critical path")
}
directives := ResolveAnalysisPlan(match, nil, DiscoveredResources{}, signals).Directives
if !directives.EnableGenericGraphicsControllerDedup {
t.Fatal("expected dell fixture to enable graphics controller dedup")
}
}
func TestBuildAcquisitionPlan_Fixture_AMI_Generic(t *testing.T) {
signals := loadProfileFixtureSignals(t, "ami-generic.json")
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
if match.Mode != ModeMatched {
t.Fatalf("expected matched mode, got %q", match.Mode)
}
assertProfileSelected(t, match, "ami-family")
assertProfileNotSelected(t, match, "msi")
assertProfileNotSelected(t, match, "supermicro")
assertProfileNotSelected(t, match, "dell")
assertProfileNotSelected(t, match, "hgx-topology")
if plan.Tuning.PrefetchEnabled == nil || !*plan.Tuning.PrefetchEnabled {
t.Fatal("expected ami-family fixture to force prefetch enabled")
}
if !containsString(plan.SeedPaths, "/redfish/v1/Oem/Ami") {
t.Fatal("expected ami-family fixture seed path /redfish/v1/Oem/Ami")
}
if !containsString(plan.SeedPaths, "/redfish/v1/Oem/Ami/InventoryData/Status") {
t.Fatal("expected ami-family fixture seed path /redfish/v1/Oem/Ami/InventoryData/Status")
}
if !containsString(plan.CriticalPaths, "/redfish/v1/UpdateService") {
t.Fatal("expected ami-family fixture to inherit generic critical path")
}
directives := ResolveAnalysisPlan(match, nil, DiscoveredResources{}, signals).Directives
if !directives.EnableGenericGraphicsControllerDedup {
t.Fatal("expected ami-family fixture to enable graphics controller dedup")
}
}
func TestBuildAcquisitionPlan_Fixture_UnknownVendor(t *testing.T) {
signals := loadProfileFixtureSignals(t, "unknown-vendor.json")
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
SystemPaths: []string{"/redfish/v1/Systems/1"},
ChassisPaths: []string{"/redfish/v1/Chassis/1"},
ManagerPaths: []string{"/redfish/v1/Managers/1"},
}, signals)
if match.Mode != ModeFallback {
t.Fatalf("expected fallback mode for unknown vendor, got %q", match.Mode)
}
if len(match.Profiles) == 0 {
t.Fatal("expected fallback to aggregate profiles")
}
for _, profile := range match.Profiles {
if !profile.SafeForFallback() {
t.Fatalf("fallback mode included non-safe profile %q", profile.Name())
}
}
if plan.Tuning.SnapshotMaxDocuments < 180000 {
t.Fatalf("expected fallback to widen snapshot cap, got %d", plan.Tuning.SnapshotMaxDocuments)
}
if plan.Tuning.PrefetchEnabled == nil || !*plan.Tuning.PrefetchEnabled {
t.Fatal("expected fallback fixture to force prefetch enabled")
}
if !containsString(resolved.CriticalPaths, "/redfish/v1/Systems/1") {
t.Fatal("expected fallback resolved critical paths to include discovered system")
}
analysisPlan := ResolveAnalysisPlan(match, nil, DiscoveredResources{}, signals)
if !analysisPlan.Directives.EnableProcessorGPUFallback {
t.Fatal("expected fallback fixture to enable processor GPU fallback")
}
if !analysisPlan.Directives.EnableStorageEnclosureRecovery {
t.Fatal("expected fallback fixture to enable storage enclosure recovery")
}
if !analysisPlan.Directives.EnableGenericGraphicsControllerDedup {
t.Fatal("expected fallback fixture to enable graphics controller dedup")
}
}
func loadProfileFixtureSignals(t *testing.T, fixtureName string) MatchSignals {
t.Helper()
path := filepath.Join("testdata", fixtureName)
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read fixture %s: %v", path, err)
}
var signals MatchSignals
if err := json.Unmarshal(data, &signals); err != nil {
t.Fatalf("decode fixture %s: %v", path, err)
}
return normalizeSignals(signals)
}
func assertProfileSelected(t *testing.T, match MatchResult, want string) {
t.Helper()
for _, profile := range match.Profiles {
if profile.Name() == want {
return
}
}
t.Fatalf("expected profile %q in %v", want, profileNames(match))
}
func assertProfileNotSelected(t *testing.T, match MatchResult, want string) {
t.Helper()
for _, profile := range match.Profiles {
if profile.Name() == want {
t.Fatalf("did not expect profile %q in %v", want, profileNames(match))
}
}
}
func profileNames(match MatchResult) []string {
out := make([]string, 0, len(match.Profiles))
for _, profile := range match.Profiles {
out = append(out, profile.Name())
}
return out
}
func assertSameProfileNames(t *testing.T, left, right MatchResult) {
t.Helper()
leftNames := profileNames(left)
rightNames := profileNames(right)
if len(leftNames) != len(rightNames) {
t.Fatalf("profile stack size differs: %v vs %v", leftNames, rightNames)
}
for i := range leftNames {
if leftNames[i] != rightNames[i] {
t.Fatalf("profile stack differs: %v vs %v", leftNames, rightNames)
}
}
}
func containsString(items []string, want string) bool {
for _, item := range items {
if item == want {
return true
}
}
return false
}
func discoveredResourcesFromSignals(signals MatchSignals) DiscoveredResources {
var discovered DiscoveredResources
for _, hint := range signals.ResourceHints {
memberPath := discoveredMemberPath(hint)
switch {
case strings.HasPrefix(memberPath, "/redfish/v1/Systems/"):
discovered.SystemPaths = append(discovered.SystemPaths, memberPath)
case strings.HasPrefix(memberPath, "/redfish/v1/Chassis/"):
discovered.ChassisPaths = append(discovered.ChassisPaths, memberPath)
case strings.HasPrefix(memberPath, "/redfish/v1/Managers/"):
discovered.ManagerPaths = append(discovered.ManagerPaths, memberPath)
}
}
discovered.SystemPaths = dedupeSorted(discovered.SystemPaths)
discovered.ChassisPaths = dedupeSorted(discovered.ChassisPaths)
discovered.ManagerPaths = dedupeSorted(discovered.ManagerPaths)
return discovered
}
func discoveredMemberPath(path string) string {
path = strings.TrimSpace(path)
if path == "" {
return ""
}
parts := strings.Split(strings.Trim(path, "/"), "/")
if len(parts) < 4 || parts[0] != "redfish" || parts[1] != "v1" {
return ""
}
switch parts[2] {
case "Systems", "Chassis", "Managers":
return "/" + strings.Join(parts[:4], "/")
default:
return ""
}
}
@@ -0,0 +1,122 @@
package redfishprofile
import (
"sort"
"git.mchus.pro/mchus/logpile/internal/models"
)
const (
ModeMatched = "matched"
ModeFallback = "fallback"
)
func MatchProfiles(signals MatchSignals) MatchResult {
type scored struct {
profile Profile
score int
}
builtins := BuiltinProfiles()
candidates := make([]scored, 0, len(builtins))
allScores := make([]ProfileScore, 0, len(builtins))
for _, profile := range builtins {
score := profile.Match(signals)
allScores = append(allScores, ProfileScore{
Name: profile.Name(),
Score: score,
Priority: profile.Priority(),
})
if score <= 0 {
continue
}
candidates = append(candidates, scored{profile: profile, score: score})
}
sort.Slice(allScores, func(i, j int) bool {
if allScores[i].Score == allScores[j].Score {
if allScores[i].Priority == allScores[j].Priority {
return allScores[i].Name < allScores[j].Name
}
return allScores[i].Priority < allScores[j].Priority
}
return allScores[i].Score > allScores[j].Score
})
sort.Slice(candidates, func(i, j int) bool {
if candidates[i].score == candidates[j].score {
return candidates[i].profile.Priority() < candidates[j].profile.Priority()
}
return candidates[i].score > candidates[j].score
})
if len(candidates) == 0 || candidates[0].score < 60 {
profiles := make([]Profile, 0, len(builtins))
active := make(map[string]struct{}, len(builtins))
for _, profile := range builtins {
if profile.SafeForFallback() {
profiles = append(profiles, profile)
active[profile.Name()] = struct{}{}
}
}
sortProfiles(profiles)
for i := range allScores {
_, ok := active[allScores[i].Name]
allScores[i].Active = ok
}
return MatchResult{Mode: ModeFallback, Profiles: profiles, Scores: allScores}
}
profiles := make([]Profile, 0, len(candidates))
seen := make(map[string]struct{}, len(candidates))
for _, candidate := range candidates {
name := candidate.profile.Name()
if _, ok := seen[name]; ok {
continue
}
seen[name] = struct{}{}
profiles = append(profiles, candidate.profile)
}
sortProfiles(profiles)
for i := range allScores {
_, ok := seen[allScores[i].Name]
allScores[i].Active = ok
}
return MatchResult{Mode: ModeMatched, Profiles: profiles, Scores: allScores}
}
func BuildAcquisitionPlan(signals MatchSignals) AcquisitionPlan {
match := MatchProfiles(signals)
plan := AcquisitionPlan{Mode: match.Mode}
for _, profile := range match.Profiles {
plan.Profiles = append(plan.Profiles, profile.Name())
profile.ExtendAcquisitionPlan(&plan, signals)
}
plan.Profiles = dedupeSorted(plan.Profiles)
plan.SeedPaths = dedupeSorted(plan.SeedPaths)
plan.CriticalPaths = dedupeSorted(plan.CriticalPaths)
plan.PlanBPaths = dedupeSorted(plan.PlanBPaths)
plan.Notes = dedupeSorted(plan.Notes)
if plan.Mode == ModeFallback {
ensureSnapshotMaxDocuments(&plan, 180000)
ensurePrefetchEnabled(&plan, true)
addPlanNote(&plan, "fallback acquisition expands safe profile probes")
}
return plan
}
func ApplyAnalysisProfiles(result *models.AnalysisResult, snapshot map[string]interface{}, signals MatchSignals) MatchResult {
match := MatchProfiles(signals)
for _, profile := range match.Profiles {
profile.PostAnalyze(result, snapshot, signals)
}
return match
}
func BuildAnalysisDirectives(match MatchResult) AnalysisDirectives {
return ResolveAnalysisPlan(match, nil, DiscoveredResources{}, MatchSignals{}).Directives
}
func sortProfiles(profiles []Profile) {
sort.Slice(profiles, func(i, j int) bool {
if profiles[i].Priority() == profiles[j].Priority() {
return profiles[i].Name() < profiles[j].Name()
}
return profiles[i].Priority() < profiles[j].Priority()
})
}
@@ -0,0 +1,390 @@
package redfishprofile
import (
"strings"
"testing"
)
func TestMatchProfiles_UnknownVendorFallsBackToAggregateProfiles(t *testing.T) {
match := MatchProfiles(MatchSignals{
ServiceRootProduct: "Redfish Server",
})
if match.Mode != ModeFallback {
t.Fatalf("expected fallback mode, got %q", match.Mode)
}
if len(match.Profiles) < 2 {
t.Fatalf("expected aggregated fallback profiles, got %d", len(match.Profiles))
}
}
func TestMatchProfiles_MSISelectsMatchedMode(t *testing.T) {
match := MatchProfiles(MatchSignals{
SystemManufacturer: "Micro-Star International Co., Ltd.",
ResourceHints: []string{"/redfish/v1/Chassis/GPU1"},
})
if match.Mode != ModeMatched {
t.Fatalf("expected matched mode, got %q", match.Mode)
}
found := false
for _, profile := range match.Profiles {
if profile.Name() == "msi" {
found = true
break
}
}
if !found {
t.Fatal("expected msi profile to be selected")
}
}
func TestBuildAcquisitionPlan_FallbackIncludesProfileNotes(t *testing.T) {
plan := BuildAcquisitionPlan(MatchSignals{
ServiceRootVendor: "AMI",
})
if len(plan.Profiles) == 0 {
t.Fatal("expected acquisition plan profiles")
}
if len(plan.Notes) == 0 {
t.Fatal("expected acquisition plan notes")
}
}
func TestBuildAcquisitionPlan_FallbackAddsBroadCrawlTuning(t *testing.T) {
plan := BuildAcquisitionPlan(MatchSignals{
ServiceRootProduct: "Unknown Redfish",
})
if plan.Mode != ModeFallback {
t.Fatalf("expected fallback mode, got %q", plan.Mode)
}
if plan.Tuning.SnapshotMaxDocuments < 180000 {
t.Fatalf("expected widened snapshot cap, got %d", plan.Tuning.SnapshotMaxDocuments)
}
if plan.Tuning.PrefetchEnabled == nil || !*plan.Tuning.PrefetchEnabled {
t.Fatal("expected fallback to force prefetch enabled")
}
if !plan.Tuning.RecoveryPolicy.EnableCriticalCollectionMemberRetry {
t.Fatal("expected fallback to inherit critical member retry recovery")
}
if !plan.Tuning.RecoveryPolicy.EnableCriticalSlowProbe {
t.Fatal("expected fallback to inherit critical slow probe recovery")
}
}
func TestBuildAcquisitionPlan_HGXDisablesNVMePostProbe(t *testing.T) {
plan := BuildAcquisitionPlan(MatchSignals{
SystemModel: "HGX B200",
ResourceHints: []string{"/redfish/v1/Systems/HGX_Baseboard_0"},
})
if plan.Mode != ModeMatched {
t.Fatalf("expected matched mode, got %q", plan.Mode)
}
if plan.Tuning.NVMePostProbeEnabled == nil || *plan.Tuning.NVMePostProbeEnabled {
t.Fatal("expected hgx profile to disable NVMe post-probe")
}
}
func TestResolveAcquisitionPlan_ExpandsScopedPaths(t *testing.T) {
signals := MatchSignals{}
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
SystemPaths: []string{"/redfish/v1/Systems/1", "/redfish/v1/Systems/2"},
}, signals)
joined := joinResolvedPaths(resolved.SeedPaths)
for _, wanted := range []string{
"/redfish/v1/Systems/1/SimpleStorage",
"/redfish/v1/Systems/1/Storage/IntelVROC",
"/redfish/v1/Systems/2/SimpleStorage",
"/redfish/v1/Systems/2/Storage/IntelVROC",
} {
if !containsJoinedPath(joined, wanted) {
t.Fatalf("expected resolved seed path %q", wanted)
}
}
}
func TestResolveAcquisitionPlan_CriticalBaselineIsShapedByProfiles(t *testing.T) {
signals := MatchSignals{}
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
SystemPaths: []string{"/redfish/v1/Systems/1"},
ChassisPaths: []string{"/redfish/v1/Chassis/1"},
ManagerPaths: []string{"/redfish/v1/Managers/1"},
}, signals)
joined := joinResolvedPaths(resolved.CriticalPaths)
for _, wanted := range []string{
"/redfish/v1",
"/redfish/v1/Systems/1",
"/redfish/v1/Systems/1/Memory",
"/redfish/v1/Chassis/1/Assembly",
"/redfish/v1/Managers/1/NetworkProtocol",
"/redfish/v1/UpdateService",
} {
if !containsJoinedPath(joined, wanted) {
t.Fatalf("expected resolved critical path %q", wanted)
}
}
}
func TestResolveAcquisitionPlan_FallbackAppendsPlanBToSeeds(t *testing.T) {
signals := MatchSignals{ServiceRootProduct: "Unknown Redfish"}
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
if plan.Mode != ModeFallback {
t.Fatalf("expected fallback mode, got %q", plan.Mode)
}
plan.PlanBPaths = append(plan.PlanBPaths, "/redfish/v1/Systems/1/Oem/TestPlanB")
resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
SystemPaths: []string{"/redfish/v1/Systems/1"},
}, signals)
if !containsJoinedPath(joinResolvedPaths(resolved.SeedPaths), "/redfish/v1/Systems/1/Oem/TestPlanB") {
t.Fatal("expected fallback resolved seeds to include plan-b path")
}
}
func TestResolveAcquisitionPlan_MSIRefinesDiscoveredGPUChassis(t *testing.T) {
signals := MatchSignals{
SystemManufacturer: "Micro-Star International Co., Ltd.",
ResourceHints: []string{"/redfish/v1/Chassis/GPU1", "/redfish/v1/Chassis/GPU4/Sensors"},
}
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
ChassisPaths: []string{"/redfish/v1/Chassis/1", "/redfish/v1/Chassis/GPU1", "/redfish/v1/Chassis/GPU4"},
}, signals)
joinedSeeds := joinResolvedPaths(resolved.SeedPaths)
joinedCritical := joinResolvedPaths(resolved.CriticalPaths)
if !containsJoinedPath(joinedSeeds, "/redfish/v1/Chassis/GPU1") || !containsJoinedPath(joinedSeeds, "/redfish/v1/Chassis/GPU4") {
t.Fatal("expected MSI refinement to add discovered GPU chassis seed paths")
}
if containsJoinedPath(joinedSeeds, "/redfish/v1/Chassis/GPU2") {
t.Fatal("did not expect undiscovered MSI GPU chassis in resolved seeds")
}
if !containsJoinedPath(joinedCritical, "/redfish/v1/Chassis/GPU1/Sensors") || !containsJoinedPath(joinedCritical, "/redfish/v1/Chassis/GPU4/Sensors") {
t.Fatal("expected MSI refinement to add discovered GPU sensor critical paths")
}
if containsJoinedPath(joinedCritical, "/redfish/v1/Chassis/GPU3/Sensors") {
t.Fatal("did not expect undiscovered MSI GPU sensor critical path")
}
}
func TestResolveAcquisitionPlan_HGXRefinesDiscoveredBaseboardSystems(t *testing.T) {
signals := MatchSignals{
SystemManufacturer: "Supermicro",
SystemModel: "SYS-821GE-TNHR",
ChassisModel: "HGX B200",
ResourceHints: []string{
"/redfish/v1/Systems/HGX_Baseboard_0",
"/redfish/v1/Systems/HGX_Baseboard_0/Processors",
"/redfish/v1/Systems/1",
},
}
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
SystemPaths: []string{"/redfish/v1/Systems/1", "/redfish/v1/Systems/HGX_Baseboard_0"},
}, signals)
joinedSeeds := joinResolvedPaths(resolved.SeedPaths)
joinedCritical := joinResolvedPaths(resolved.CriticalPaths)
if !containsJoinedPath(joinedSeeds, "/redfish/v1/Systems/HGX_Baseboard_0") || !containsJoinedPath(joinedSeeds, "/redfish/v1/Systems/HGX_Baseboard_0/Processors") {
t.Fatal("expected HGX refinement to add discovered baseboard system paths")
}
if !containsJoinedPath(joinedCritical, "/redfish/v1/Systems/HGX_Baseboard_0") || !containsJoinedPath(joinedCritical, "/redfish/v1/Systems/HGX_Baseboard_0/Processors") {
t.Fatal("expected HGX refinement to add discovered baseboard critical paths")
}
if containsJoinedPath(joinedSeeds, "/redfish/v1/Systems/HGX_Baseboard_1") {
t.Fatal("did not expect undiscovered HGX baseboard system path")
}
}
func TestResolveAcquisitionPlan_SupermicroRefinesFirmwareInventoryFromHint(t *testing.T) {
signals := MatchSignals{
SystemManufacturer: "Supermicro",
ResourceHints: []string{
"/redfish/v1/UpdateService/Oem/Supermicro/FirmwareInventory",
"/redfish/v1/Managers/1/Oem/Supermicro/FanMode",
},
}
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
ManagerPaths: []string{"/redfish/v1/Managers/1"},
}, signals)
joinedCritical := joinResolvedPaths(resolved.CriticalPaths)
if !containsJoinedPath(joinedCritical, "/redfish/v1/UpdateService/Oem/Supermicro/FirmwareInventory") {
t.Fatal("expected Supermicro refinement to add firmware inventory critical path")
}
if !containsJoinedPath(joinResolvedPaths(resolved.Plan.PlanBPaths), "/redfish/v1/UpdateService/Oem/Supermicro/FirmwareInventory") {
t.Fatal("expected Supermicro refinement to add firmware inventory plan-b path")
}
}
func TestResolveAcquisitionPlan_DellRefinesDiscoveredIDRACManager(t *testing.T) {
signals := MatchSignals{
SystemManufacturer: "Dell Inc.",
ServiceRootProduct: "iDRAC Redfish Service",
}
match := MatchProfiles(signals)
plan := BuildAcquisitionPlan(signals)
resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
ManagerPaths: []string{"/redfish/v1/Managers/1", "/redfish/v1/Managers/iDRAC.Embedded.1"},
}, signals)
joinedSeeds := joinResolvedPaths(resolved.SeedPaths)
joinedCritical := joinResolvedPaths(resolved.CriticalPaths)
if !containsJoinedPath(joinedSeeds, "/redfish/v1/Managers/iDRAC.Embedded.1") {
t.Fatal("expected Dell refinement to add discovered iDRAC manager seed path")
}
if !containsJoinedPath(joinedCritical, "/redfish/v1/Managers/iDRAC.Embedded.1") {
t.Fatal("expected Dell refinement to add discovered iDRAC manager critical path")
}
}
func TestBuildAnalysisDirectives_SupermicroEnablesVendorStorageFallbacks(t *testing.T) {
signals := MatchSignals{
SystemManufacturer: "Supermicro",
SystemModel: "SYS-821GE",
}
match := MatchProfiles(signals)
plan := ResolveAnalysisPlan(match, map[string]interface{}{
"/redfish/v1/Chassis/NVMeSSD.1.StorageBackplane/Drives": map[string]interface{}{},
}, DiscoveredResources{}, signals)
directives := plan.Directives
if !directives.EnableSupermicroNVMeBackplane {
t.Fatal("expected supermicro nvme backplane fallback")
}
}
func joinResolvedPaths(paths []string) string {
return "\n" + strings.Join(paths, "\n") + "\n"
}
func containsJoinedPath(joined, want string) bool {
return strings.Contains(joined, "\n"+want+"\n")
}
func TestBuildAnalysisDirectives_HGXEnablesGPUFallbacks(t *testing.T) {
signals := MatchSignals{
SystemManufacturer: "Supermicro",
SystemModel: "SYS-821GE-TNHR",
ChassisModel: "HGX B200",
ResourceHints: []string{"/redfish/v1/Systems/HGX_Baseboard_0", "/redfish/v1/Chassis/HGX_Chassis_0/PCIeDevices/GPU_SXM_1"},
}
match := MatchProfiles(signals)
plan := ResolveAnalysisPlan(match, map[string]interface{}{
"/redfish/v1/Systems/HGX_Baseboard_0/Processors/GPU_SXM_1": map[string]interface{}{"ProcessorType": "GPU"},
"/redfish/v1/Chassis/HGX_Chassis_0/PCIeDevices/GPU_SXM_1": map[string]interface{}{},
}, DiscoveredResources{
SystemPaths: []string{"/redfish/v1/Systems/HGX_Baseboard_0"},
}, signals)
directives := plan.Directives
if !directives.EnableProcessorGPUFallback {
t.Fatal("expected processor GPU fallback for hgx profile")
}
if !directives.EnableProcessorGPUChassisAlias {
t.Fatal("expected processor GPU chassis alias resolution for hgx profile")
}
if !directives.EnableGenericGraphicsControllerDedup {
t.Fatal("expected graphics-controller dedup for hgx profile")
}
}
func TestBuildAnalysisDirectives_MSIEnablesMSIChassisLookup(t *testing.T) {
signals := MatchSignals{
SystemManufacturer: "Micro-Star International Co., Ltd.",
}
match := MatchProfiles(signals)
plan := ResolveAnalysisPlan(match, map[string]interface{}{
"/redfish/v1/Systems/1/Processors/GPU1": map[string]interface{}{"ProcessorType": "GPU"},
"/redfish/v1/Chassis/GPU1": map[string]interface{}{},
}, DiscoveredResources{
SystemPaths: []string{"/redfish/v1/Systems/1"},
ChassisPaths: []string{"/redfish/v1/Chassis/GPU1"},
}, signals)
directives := plan.Directives
if !directives.EnableMSIProcessorGPUChassisLookup {
t.Fatal("expected MSI processor GPU chassis lookup")
}
}
func TestBuildAnalysisDirectives_SupermicroEnablesStorageRecovery(t *testing.T) {
signals := MatchSignals{
SystemManufacturer: "Supermicro",
}
match := MatchProfiles(signals)
plan := ResolveAnalysisPlan(match, map[string]interface{}{
"/redfish/v1/Chassis/1/Drives": map[string]interface{}{},
"/redfish/v1/Systems/1/Storage/IntelVROC": map[string]interface{}{},
"/redfish/v1/Systems/1/Storage/IntelVROC/Drives": map[string]interface{}{},
}, DiscoveredResources{}, signals)
directives := plan.Directives
if !directives.EnableStorageEnclosureRecovery {
t.Fatal("expected storage enclosure recovery for supermicro")
}
if !directives.EnableKnownStorageControllerRecovery {
t.Fatal("expected known storage controller recovery for supermicro")
}
}
func TestMatchProfiles_OrderingIsDeterministic(t *testing.T) {
signals := MatchSignals{
SystemManufacturer: "Micro-Star International Co., Ltd.",
ResourceHints: []string{"/redfish/v1/Chassis/GPU1"},
}
first := MatchProfiles(signals)
second := MatchProfiles(signals)
if len(first.Profiles) != len(second.Profiles) {
t.Fatalf("profile stack size differs across calls: %d vs %d", len(first.Profiles), len(second.Profiles))
}
for i := range first.Profiles {
if first.Profiles[i].Name() != second.Profiles[i].Name() {
t.Fatalf("profile ordering differs at index %d: %q vs %q", i, first.Profiles[i].Name(), second.Profiles[i].Name())
}
}
}
func TestMatchProfiles_FallbackOrderingIsDeterministic(t *testing.T) {
signals := MatchSignals{ServiceRootProduct: "Unknown Redfish"}
first := MatchProfiles(signals)
second := MatchProfiles(signals)
if first.Mode != ModeFallback || second.Mode != ModeFallback {
t.Fatalf("expected fallback mode in both calls")
}
if len(first.Profiles) != len(second.Profiles) {
t.Fatalf("fallback profile stack size differs: %d vs %d", len(first.Profiles), len(second.Profiles))
}
for i := range first.Profiles {
if first.Profiles[i].Name() != second.Profiles[i].Name() {
t.Fatalf("fallback profile ordering differs at index %d: %q vs %q", i, first.Profiles[i].Name(), second.Profiles[i].Name())
}
}
}
func TestMatchProfiles_FallbackOnlySelectsSafeProfiles(t *testing.T) {
match := MatchProfiles(MatchSignals{ServiceRootProduct: "Unknown Generic Redfish Server"})
if match.Mode != ModeFallback {
t.Fatalf("expected fallback mode, got %q", match.Mode)
}
for _, profile := range match.Profiles {
if !profile.SafeForFallback() {
t.Fatalf("fallback mode included non-safe profile %q", profile.Name())
}
}
}
func TestBuildAnalysisDirectives_GenericMatchedKeepsFallbacksDisabled(t *testing.T) {
match := MatchResult{
Mode: ModeMatched,
Profiles: []Profile{genericProfile()},
}
directives := ResolveAnalysisPlan(match, nil, DiscoveredResources{}, MatchSignals{}).Directives
if directives.EnableProcessorGPUFallback {
t.Fatal("did not expect processor GPU fallback for generic matched profile")
}
if directives.EnableSupermicroNVMeBackplane {
t.Fatal("did not expect supermicro nvme fallback for generic matched profile")
}
if directives.EnableGenericGraphicsControllerDedup {
t.Fatal("did not expect generic graphics-controller dedup for generic matched profile")
}
}
@@ -0,0 +1,33 @@
package redfishprofile
func amiProfile() Profile {
return staticProfile{
name: "ami-family",
priority: 10,
safeForFallback: true,
matchFn: func(s MatchSignals) int {
score := 0
if containsFold(s.ServiceRootVendor, "ami") || containsFold(s.ServiceRootProduct, "ami") {
score += 70
}
for _, ns := range s.OEMNamespaces {
if containsFold(ns, "ami") {
score += 30
break
}
}
return min(score, 100)
},
extendAcquisition: func(plan *AcquisitionPlan, _ MatchSignals) {
addPlanPaths(&plan.SeedPaths,
"/redfish/v1/Oem/Ami",
"/redfish/v1/Oem/Ami/InventoryData/Status",
)
ensurePrefetchEnabled(plan, true)
addPlanNote(plan, "ami-family acquisition extensions enabled")
},
applyAnalysisDirectives: func(d *AnalysisDirectives, _ MatchSignals) {
d.EnableGenericGraphicsControllerDedup = true
},
}
}
@@ -0,0 +1,45 @@
package redfishprofile
func dellProfile() Profile {
return staticProfile{
name: "dell",
priority: 20,
safeForFallback: true,
matchFn: func(s MatchSignals) int {
score := 0
if containsFold(s.SystemManufacturer, "dell") || containsFold(s.ChassisManufacturer, "dell") {
score += 80
}
for _, ns := range s.OEMNamespaces {
if containsFold(ns, "dell") {
score += 30
break
}
}
if containsFold(s.ServiceRootProduct, "idrac") {
score += 30
}
return min(score, 100)
},
extendAcquisition: func(plan *AcquisitionPlan, _ MatchSignals) {
ensureRecoveryPolicy(plan, AcquisitionRecoveryPolicy{
EnableProfilePlanB: true,
})
addPlanNote(plan, "dell iDRAC acquisition extensions enabled")
},
refineAcquisition: func(resolved *ResolvedAcquisitionPlan, discovered DiscoveredResources, _ MatchSignals) {
for _, managerPath := range discovered.ManagerPaths {
if !containsFold(managerPath, "idrac") {
continue
}
addPlanPaths(&resolved.SeedPaths, managerPath)
addPlanPaths(&resolved.Plan.SeedPaths, managerPath)
addPlanPaths(&resolved.CriticalPaths, managerPath)
addPlanPaths(&resolved.Plan.CriticalPaths, managerPath)
}
},
applyAnalysisDirectives: func(d *AnalysisDirectives, _ MatchSignals) {
d.EnableGenericGraphicsControllerDedup = true
},
}
}
@@ -0,0 +1,117 @@
package redfishprofile
func genericProfile() Profile {
return staticProfile{
name: "generic",
priority: 100,
safeForFallback: true,
matchFn: func(MatchSignals) int { return 10 },
extendAcquisition: func(plan *AcquisitionPlan, _ MatchSignals) {
ensurePrefetchPolicy(plan, AcquisitionPrefetchPolicy{
IncludeSuffixes: []string{
"/Bios",
"/SecureBoot",
"/Processors",
"/Memory",
"/Storage",
"/SimpleStorage",
"/PCIeDevices",
"/PCIeFunctions",
"/Accelerators",
"/GraphicsControllers",
"/EthernetInterfaces",
"/NetworkInterfaces",
"/NetworkAdapters",
"/Drives",
"/Power",
"/PowerSubsystem/PowerSupplies",
"/NetworkProtocol",
"/UpdateService",
"/UpdateService/FirmwareInventory",
},
ExcludeContains: []string{
"/Fabrics",
"/Backplanes",
"/Boards",
"/Assembly",
"/Sensors",
"/ThresholdSensors",
"/DiscreteSensors",
"/ThermalConfig",
"/ThermalSubsystem",
"/EnvironmentMetrics",
"/Certificates",
"/LogServices",
},
})
ensureScopedPathPolicy(plan, AcquisitionScopedPathPolicy{
SystemCriticalSuffixes: []string{
"/Bios",
"/SecureBoot",
"/Oem/Public",
"/Oem/Public/FRU",
"/Processors",
"/Memory",
"/Storage",
"/PCIeDevices",
"/PCIeFunctions",
"/Accelerators",
"/GraphicsControllers",
"/EthernetInterfaces",
"/NetworkInterfaces",
"/SimpleStorage",
"/Storage/IntelVROC",
"/Storage/IntelVROC/Drives",
"/Storage/IntelVROC/Volumes",
},
ChassisCriticalSuffixes: []string{
"/Oem/Public",
"/Oem/Public/FRU",
"/Power",
"/NetworkAdapters",
"/PCIeDevices",
"/Accelerators",
"/Drives",
"/Assembly",
},
ManagerCriticalSuffixes: []string{
"/NetworkProtocol",
},
SystemSeedSuffixes: []string{
"/SimpleStorage",
"/Storage/IntelVROC",
"/Storage/IntelVROC/Drives",
"/Storage/IntelVROC/Volumes",
},
})
addPlanPaths(&plan.CriticalPaths,
"/redfish/v1/UpdateService",
"/redfish/v1/UpdateService/FirmwareInventory",
)
ensureSnapshotMaxDocuments(plan, 100000)
ensureSnapshotWorkers(plan, 6)
ensurePrefetchWorkers(plan, 4)
ensureETABaseline(plan, AcquisitionETABaseline{
DiscoverySeconds: 8,
SnapshotSeconds: 90,
PrefetchSeconds: 20,
CriticalPlanBSeconds: 20,
ProfilePlanBSeconds: 15,
})
ensurePostProbePolicy(plan, AcquisitionPostProbePolicy{
EnableNumericCollectionProbe: true,
})
ensureRecoveryPolicy(plan, AcquisitionRecoveryPolicy{
EnableCriticalCollectionMemberRetry: true,
EnableCriticalSlowProbe: true,
})
ensureRatePolicy(plan, AcquisitionRatePolicy{
TargetP95LatencyMS: 900,
ThrottleP95LatencyMS: 1800,
MinSnapshotWorkers: 2,
MinPrefetchWorkers: 1,
DisablePrefetchOnErrors: true,
})
},
}
}
@@ -0,0 +1,85 @@
package redfishprofile
func hgxProfile() Profile {
return staticProfile{
name: "hgx-topology",
priority: 30,
safeForFallback: true,
matchFn: func(s MatchSignals) int {
score := 0
if containsFold(s.SystemModel, "hgx") || containsFold(s.ChassisModel, "hgx") {
score += 70
}
for _, hint := range s.ResourceHints {
if containsFold(hint, "hgx_") || containsFold(hint, "gpu_sxm") {
score += 20
break
}
}
return min(score, 100)
},
extendAcquisition: func(plan *AcquisitionPlan, _ MatchSignals) {
ensureSnapshotMaxDocuments(plan, 180000)
ensureSnapshotWorkers(plan, 4)
ensurePrefetchWorkers(plan, 4)
ensureNVMePostProbeEnabled(plan, false)
ensureRecoveryPolicy(plan, AcquisitionRecoveryPolicy{
EnableProfilePlanB: true,
})
ensureETABaseline(plan, AcquisitionETABaseline{
DiscoverySeconds: 20,
SnapshotSeconds: 300,
PrefetchSeconds: 50,
CriticalPlanBSeconds: 90,
ProfilePlanBSeconds: 40,
})
ensureRatePolicy(plan, AcquisitionRatePolicy{
TargetP95LatencyMS: 1500,
ThrottleP95LatencyMS: 3000,
MinSnapshotWorkers: 1,
MinPrefetchWorkers: 1,
DisablePrefetchOnErrors: true,
})
addPlanNote(plan, "hgx topology acquisition extensions enabled")
},
refineAcquisition: func(resolved *ResolvedAcquisitionPlan, discovered DiscoveredResources, _ MatchSignals) {
for _, systemPath := range discovered.SystemPaths {
if !containsFold(systemPath, "hgx_baseboard_") {
continue
}
addPlanPaths(&resolved.SeedPaths, systemPath, joinPath(systemPath, "/Processors"))
addPlanPaths(&resolved.Plan.SeedPaths, systemPath, joinPath(systemPath, "/Processors"))
addPlanPaths(&resolved.CriticalPaths, systemPath, joinPath(systemPath, "/Processors"))
addPlanPaths(&resolved.Plan.CriticalPaths, systemPath, joinPath(systemPath, "/Processors"))
addPlanPaths(&resolved.Plan.PlanBPaths, systemPath, joinPath(systemPath, "/Processors"))
}
},
applyAnalysisDirectives: func(d *AnalysisDirectives, _ MatchSignals) {
d.EnableGenericGraphicsControllerDedup = true
d.EnableStorageEnclosureRecovery = true
},
refineAnalysis: func(plan *ResolvedAnalysisPlan, snapshot map[string]interface{}, discovered DiscoveredResources, _ MatchSignals) {
if snapshotHasGPUProcessor(snapshot, discovered.SystemPaths) && (snapshotHasPathContaining(snapshot, "gpu_sxm") || snapshotHasPathContaining(snapshot, "hgx_")) {
plan.Directives.EnableProcessorGPUFallback = true
plan.Directives.EnableProcessorGPUChassisAlias = true
addAnalysisLookupMode(plan, "hgx-alias")
addAnalysisNote(plan, "hgx analysis enables processor-gpu alias fallback from snapshot topology")
}
if snapshotHasStorageControllerHint(snapshot, "/storage/intelvroc", "/storage/ha-raid", "/storage/mrvl.ha-raid") {
plan.Directives.EnableKnownStorageControllerRecovery = true
addAnalysisStorageDriveCollections(plan,
"/Storage/IntelVROC/Drives",
"/Storage/IntelVROC/Controllers/1/Drives",
)
addAnalysisStorageVolumeCollections(plan,
"/Storage/IntelVROC/Volumes",
"/Storage/HA-RAID/Volumes",
"/Storage/MRVL.HA-RAID/Volumes",
)
}
if snapshotHasPathContaining(snapshot, "/chassis/nvmessd.") && snapshotHasPathContaining(snapshot, ".storagebackplane") {
plan.Directives.EnableSupermicroNVMeBackplane = true
}
},
}
}
@@ -0,0 +1,72 @@
package redfishprofile
import "strings"
func msiProfile() Profile {
return staticProfile{
name: "msi",
priority: 20,
safeForFallback: true,
matchFn: func(s MatchSignals) int {
score := 0
if containsFold(s.SystemManufacturer, "micro-star") || containsFold(s.ChassisManufacturer, "micro-star") {
score += 80
}
if containsFold(s.SystemManufacturer, "msi") || containsFold(s.ChassisManufacturer, "msi") {
score += 40
}
for _, hint := range s.ResourceHints {
if strings.HasPrefix(hint, "/redfish/v1/Chassis/GPU") {
score += 10
break
}
}
return min(score, 100)
},
extendAcquisition: func(plan *AcquisitionPlan, _ MatchSignals) {
ensureSnapshotWorkers(plan, 6)
ensurePrefetchWorkers(plan, 8)
ensureETABaseline(plan, AcquisitionETABaseline{
DiscoverySeconds: 12,
SnapshotSeconds: 120,
PrefetchSeconds: 25,
CriticalPlanBSeconds: 35,
ProfilePlanBSeconds: 25,
})
ensureRatePolicy(plan, AcquisitionRatePolicy{
TargetP95LatencyMS: 1000,
ThrottleP95LatencyMS: 2200,
MinSnapshotWorkers: 2,
MinPrefetchWorkers: 2,
DisablePrefetchOnErrors: true,
})
ensureRecoveryPolicy(plan, AcquisitionRecoveryPolicy{
EnableProfilePlanB: true,
})
addPlanNote(plan, "msi gpu chassis probes enabled")
},
refineAcquisition: func(resolved *ResolvedAcquisitionPlan, discovered DiscoveredResources, _ MatchSignals) {
for _, chassisPath := range discovered.ChassisPaths {
if !strings.HasPrefix(chassisPath, "/redfish/v1/Chassis/GPU") {
continue
}
addPlanPaths(&resolved.SeedPaths, chassisPath)
addPlanPaths(&resolved.Plan.SeedPaths, chassisPath)
addPlanPaths(&resolved.CriticalPaths, joinPath(chassisPath, "/Sensors"))
addPlanPaths(&resolved.Plan.CriticalPaths, joinPath(chassisPath, "/Sensors"))
addPlanPaths(&resolved.Plan.PlanBPaths, joinPath(chassisPath, "/Sensors"))
}
},
applyAnalysisDirectives: func(d *AnalysisDirectives, _ MatchSignals) {
d.EnableGenericGraphicsControllerDedup = true
},
refineAnalysis: func(plan *ResolvedAnalysisPlan, snapshot map[string]interface{}, discovered DiscoveredResources, _ MatchSignals) {
if snapshotHasGPUProcessor(snapshot, discovered.SystemPaths) && snapshotHasPathPrefix(snapshot, "/redfish/v1/Chassis/GPU") {
plan.Directives.EnableProcessorGPUFallback = true
plan.Directives.EnableMSIProcessorGPUChassisLookup = true
addAnalysisLookupMode(plan, "msi-index")
addAnalysisNote(plan, "msi analysis enables processor-gpu fallback from discovered GPU chassis")
}
},
}
}
@@ -0,0 +1,81 @@
package redfishprofile
func supermicroProfile() Profile {
return staticProfile{
name: "supermicro",
priority: 20,
safeForFallback: true,
matchFn: func(s MatchSignals) int {
score := 0
if containsFold(s.SystemManufacturer, "supermicro") || containsFold(s.ChassisManufacturer, "supermicro") {
score += 80
}
for _, hint := range s.ResourceHints {
if containsFold(hint, "hgx_baseboard") || containsFold(hint, "hgx_gpu_sxm") {
score += 20
break
}
}
return min(score, 100)
},
extendAcquisition: func(plan *AcquisitionPlan, _ MatchSignals) {
ensureSnapshotMaxDocuments(plan, 150000)
ensureSnapshotWorkers(plan, 6)
ensurePrefetchWorkers(plan, 4)
ensureETABaseline(plan, AcquisitionETABaseline{
DiscoverySeconds: 15,
SnapshotSeconds: 180,
PrefetchSeconds: 35,
CriticalPlanBSeconds: 45,
ProfilePlanBSeconds: 30,
})
ensurePostProbePolicy(plan, AcquisitionPostProbePolicy{
EnableDirectNVMEDiskBayProbe: true,
})
ensureRecoveryPolicy(plan, AcquisitionRecoveryPolicy{
EnableProfilePlanB: true,
})
ensureRatePolicy(plan, AcquisitionRatePolicy{
TargetP95LatencyMS: 1200,
ThrottleP95LatencyMS: 2400,
MinSnapshotWorkers: 2,
MinPrefetchWorkers: 1,
DisablePrefetchOnErrors: true,
})
addPlanNote(plan, "supermicro acquisition extensions enabled")
},
refineAcquisition: func(resolved *ResolvedAcquisitionPlan, _ DiscoveredResources, signals MatchSignals) {
for _, hint := range signals.ResourceHints {
if normalizePath(hint) != "/redfish/v1/UpdateService/Oem/Supermicro/FirmwareInventory" {
continue
}
addPlanPaths(&resolved.CriticalPaths, hint)
addPlanPaths(&resolved.Plan.CriticalPaths, hint)
addPlanPaths(&resolved.Plan.PlanBPaths, hint)
break
}
},
applyAnalysisDirectives: func(d *AnalysisDirectives, _ MatchSignals) {
d.EnableStorageEnclosureRecovery = true
},
refineAnalysis: func(plan *ResolvedAnalysisPlan, snapshot map[string]interface{}, _ DiscoveredResources, _ MatchSignals) {
if snapshotHasPathContaining(snapshot, "/chassis/nvmessd.") && snapshotHasPathContaining(snapshot, ".storagebackplane") {
plan.Directives.EnableSupermicroNVMeBackplane = true
addAnalysisNote(plan, "supermicro analysis enables NVMe backplane recovery from snapshot paths")
}
if snapshotHasStorageControllerHint(snapshot, "/storage/intelvroc", "/storage/ha-raid", "/storage/mrvl.ha-raid") {
plan.Directives.EnableKnownStorageControllerRecovery = true
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, "supermicro analysis enables known storage-controller recovery from snapshot paths")
}
},
}
}
@@ -0,0 +1,228 @@
package redfishprofile
import (
"strings"
"git.mchus.pro/mchus/logpile/internal/models"
)
type staticProfile struct {
name string
priority int
safeForFallback bool
matchFn func(MatchSignals) int
extendAcquisition func(*AcquisitionPlan, MatchSignals)
refineAcquisition func(*ResolvedAcquisitionPlan, DiscoveredResources, MatchSignals)
applyAnalysisDirectives func(*AnalysisDirectives, MatchSignals)
refineAnalysis func(*ResolvedAnalysisPlan, map[string]interface{}, DiscoveredResources, MatchSignals)
postAnalyze func(*models.AnalysisResult, map[string]interface{}, MatchSignals)
}
func (p staticProfile) Name() string { return p.name }
func (p staticProfile) Priority() int { return p.priority }
func (p staticProfile) Match(signals MatchSignals) int { return p.matchFn(normalizeSignals(signals)) }
func (p staticProfile) SafeForFallback() bool { return p.safeForFallback }
func (p staticProfile) ExtendAcquisitionPlan(plan *AcquisitionPlan, signals MatchSignals) {
if p.extendAcquisition != nil {
p.extendAcquisition(plan, normalizeSignals(signals))
}
}
func (p staticProfile) RefineAcquisitionPlan(resolved *ResolvedAcquisitionPlan, discovered DiscoveredResources, signals MatchSignals) {
if p.refineAcquisition != nil {
p.refineAcquisition(resolved, discovered, normalizeSignals(signals))
}
}
func (p staticProfile) ApplyAnalysisDirectives(directives *AnalysisDirectives, signals MatchSignals) {
if p.applyAnalysisDirectives != nil {
p.applyAnalysisDirectives(directives, normalizeSignals(signals))
}
}
func (p staticProfile) RefineAnalysisPlan(plan *ResolvedAnalysisPlan, snapshot map[string]interface{}, discovered DiscoveredResources, signals MatchSignals) {
if p.refineAnalysis != nil {
p.refineAnalysis(plan, snapshot, discovered, normalizeSignals(signals))
}
}
func (p staticProfile) PostAnalyze(result *models.AnalysisResult, snapshot map[string]interface{}, signals MatchSignals) {
if p.postAnalyze != nil {
p.postAnalyze(result, snapshot, normalizeSignals(signals))
}
}
func BuiltinProfiles() []Profile {
return []Profile{
genericProfile(),
amiProfile(),
msiProfile(),
supermicroProfile(),
dellProfile(),
hgxProfile(),
}
}
func containsFold(v, sub string) bool {
return strings.Contains(strings.ToLower(strings.TrimSpace(v)), strings.ToLower(strings.TrimSpace(sub)))
}
func addPlanPaths(dst *[]string, paths ...string) {
*dst = append(*dst, paths...)
*dst = dedupeSorted(*dst)
}
func addPlanNote(plan *AcquisitionPlan, note string) {
if strings.TrimSpace(note) == "" {
return
}
plan.Notes = append(plan.Notes, note)
plan.Notes = dedupeSorted(plan.Notes)
}
func addAnalysisNote(plan *ResolvedAnalysisPlan, note string) {
if plan == nil || strings.TrimSpace(note) == "" {
return
}
plan.Notes = append(plan.Notes, note)
plan.Notes = dedupeSorted(plan.Notes)
}
func addAnalysisLookupMode(plan *ResolvedAnalysisPlan, mode string) {
if plan == nil || strings.TrimSpace(mode) == "" {
return
}
plan.ProcessorGPUChassisLookupModes = dedupeSorted(append(plan.ProcessorGPUChassisLookupModes, mode))
}
func addAnalysisStorageDriveCollections(plan *ResolvedAnalysisPlan, rels ...string) {
if plan == nil {
return
}
plan.KnownStorageDriveCollections = dedupeSorted(append(plan.KnownStorageDriveCollections, rels...))
}
func addAnalysisStorageVolumeCollections(plan *ResolvedAnalysisPlan, rels ...string) {
if plan == nil {
return
}
plan.KnownStorageVolumeCollections = dedupeSorted(append(plan.KnownStorageVolumeCollections, rels...))
}
func ensureSnapshotMaxDocuments(plan *AcquisitionPlan, n int) {
if n <= 0 {
return
}
if plan.Tuning.SnapshotMaxDocuments < n {
plan.Tuning.SnapshotMaxDocuments = n
}
}
func ensureSnapshotWorkers(plan *AcquisitionPlan, n int) {
if n <= 0 {
return
}
if plan.Tuning.SnapshotWorkers < n {
plan.Tuning.SnapshotWorkers = n
}
}
func ensurePrefetchEnabled(plan *AcquisitionPlan, enabled bool) {
if plan.Tuning.PrefetchEnabled == nil {
plan.Tuning.PrefetchEnabled = new(bool)
}
*plan.Tuning.PrefetchEnabled = enabled
}
func ensurePrefetchWorkers(plan *AcquisitionPlan, n int) {
if n <= 0 {
return
}
if plan.Tuning.PrefetchWorkers < n {
plan.Tuning.PrefetchWorkers = n
}
}
func ensureNVMePostProbeEnabled(plan *AcquisitionPlan, enabled bool) {
if plan.Tuning.NVMePostProbeEnabled == nil {
plan.Tuning.NVMePostProbeEnabled = new(bool)
}
*plan.Tuning.NVMePostProbeEnabled = enabled
}
func ensureRatePolicy(plan *AcquisitionPlan, policy AcquisitionRatePolicy) {
if policy.TargetP95LatencyMS > plan.Tuning.RatePolicy.TargetP95LatencyMS {
plan.Tuning.RatePolicy.TargetP95LatencyMS = policy.TargetP95LatencyMS
}
if policy.ThrottleP95LatencyMS > plan.Tuning.RatePolicy.ThrottleP95LatencyMS {
plan.Tuning.RatePolicy.ThrottleP95LatencyMS = policy.ThrottleP95LatencyMS
}
if policy.MinSnapshotWorkers > plan.Tuning.RatePolicy.MinSnapshotWorkers {
plan.Tuning.RatePolicy.MinSnapshotWorkers = policy.MinSnapshotWorkers
}
if policy.MinPrefetchWorkers > plan.Tuning.RatePolicy.MinPrefetchWorkers {
plan.Tuning.RatePolicy.MinPrefetchWorkers = policy.MinPrefetchWorkers
}
if policy.DisablePrefetchOnErrors {
plan.Tuning.RatePolicy.DisablePrefetchOnErrors = true
}
}
func ensureETABaseline(plan *AcquisitionPlan, baseline AcquisitionETABaseline) {
if baseline.DiscoverySeconds > plan.Tuning.ETABaseline.DiscoverySeconds {
plan.Tuning.ETABaseline.DiscoverySeconds = baseline.DiscoverySeconds
}
if baseline.SnapshotSeconds > plan.Tuning.ETABaseline.SnapshotSeconds {
plan.Tuning.ETABaseline.SnapshotSeconds = baseline.SnapshotSeconds
}
if baseline.PrefetchSeconds > plan.Tuning.ETABaseline.PrefetchSeconds {
plan.Tuning.ETABaseline.PrefetchSeconds = baseline.PrefetchSeconds
}
if baseline.CriticalPlanBSeconds > plan.Tuning.ETABaseline.CriticalPlanBSeconds {
plan.Tuning.ETABaseline.CriticalPlanBSeconds = baseline.CriticalPlanBSeconds
}
if baseline.ProfilePlanBSeconds > plan.Tuning.ETABaseline.ProfilePlanBSeconds {
plan.Tuning.ETABaseline.ProfilePlanBSeconds = baseline.ProfilePlanBSeconds
}
}
func ensurePostProbePolicy(plan *AcquisitionPlan, policy AcquisitionPostProbePolicy) {
if policy.EnableDirectNVMEDiskBayProbe {
plan.Tuning.PostProbePolicy.EnableDirectNVMEDiskBayProbe = true
}
if policy.EnableNumericCollectionProbe {
plan.Tuning.PostProbePolicy.EnableNumericCollectionProbe = true
}
if policy.EnableSensorCollectionProbe {
plan.Tuning.PostProbePolicy.EnableSensorCollectionProbe = true
}
}
func ensureRecoveryPolicy(plan *AcquisitionPlan, policy AcquisitionRecoveryPolicy) {
if policy.EnableCriticalCollectionMemberRetry {
plan.Tuning.RecoveryPolicy.EnableCriticalCollectionMemberRetry = true
}
if policy.EnableCriticalSlowProbe {
plan.Tuning.RecoveryPolicy.EnableCriticalSlowProbe = true
}
if policy.EnableProfilePlanB {
plan.Tuning.RecoveryPolicy.EnableProfilePlanB = true
}
}
func ensureScopedPathPolicy(plan *AcquisitionPlan, policy AcquisitionScopedPathPolicy) {
addPlanPaths(&plan.ScopedPaths.SystemSeedSuffixes, policy.SystemSeedSuffixes...)
addPlanPaths(&plan.ScopedPaths.SystemCriticalSuffixes, policy.SystemCriticalSuffixes...)
addPlanPaths(&plan.ScopedPaths.ChassisSeedSuffixes, policy.ChassisSeedSuffixes...)
addPlanPaths(&plan.ScopedPaths.ChassisCriticalSuffixes, policy.ChassisCriticalSuffixes...)
addPlanPaths(&plan.ScopedPaths.ManagerSeedSuffixes, policy.ManagerSeedSuffixes...)
addPlanPaths(&plan.ScopedPaths.ManagerCriticalSuffixes, policy.ManagerCriticalSuffixes...)
}
func ensurePrefetchPolicy(plan *AcquisitionPlan, policy AcquisitionPrefetchPolicy) {
addPlanPaths(&plan.Tuning.PrefetchPolicy.IncludeSuffixes, policy.IncludeSuffixes...)
addPlanPaths(&plan.Tuning.PrefetchPolicy.ExcludeContains, policy.ExcludeContains...)
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
@@ -0,0 +1,98 @@
package redfishprofile
import "strings"
func CollectSignals(serviceRootDoc, systemDoc, chassisDoc, managerDoc map[string]interface{}, resourceHints []string) MatchSignals {
signals := MatchSignals{
ServiceRootVendor: lookupString(serviceRootDoc, "Vendor"),
ServiceRootProduct: lookupString(serviceRootDoc, "Product"),
SystemManufacturer: lookupString(systemDoc, "Manufacturer"),
SystemModel: lookupString(systemDoc, "Model"),
SystemSKU: lookupString(systemDoc, "SKU"),
ChassisManufacturer: lookupString(chassisDoc, "Manufacturer"),
ChassisModel: lookupString(chassisDoc, "Model"),
ManagerManufacturer: lookupString(managerDoc, "Manufacturer"),
ResourceHints: resourceHints,
}
signals.OEMNamespaces = dedupeSorted(append(
oemNamespaces(serviceRootDoc),
append(oemNamespaces(systemDoc), append(oemNamespaces(chassisDoc), oemNamespaces(managerDoc)...)...)...,
))
return normalizeSignals(signals)
}
func CollectSignalsFromTree(tree map[string]interface{}) MatchSignals {
getDoc := func(path string) map[string]interface{} {
if v, ok := tree[path]; ok {
if doc, ok := v.(map[string]interface{}); ok {
return doc
}
}
return nil
}
memberPath := func(collectionPath, fallbackPath string) string {
collection := getDoc(collectionPath)
if len(collection) != 0 {
if members, ok := collection["Members"].([]interface{}); ok && len(members) > 0 {
if ref, ok := members[0].(map[string]interface{}); ok {
if path := lookupString(ref, "@odata.id"); path != "" {
return path
}
}
}
}
return fallbackPath
}
systemPath := memberPath("/redfish/v1/Systems", "/redfish/v1/Systems/1")
chassisPath := memberPath("/redfish/v1/Chassis", "/redfish/v1/Chassis/1")
managerPath := memberPath("/redfish/v1/Managers", "/redfish/v1/Managers/1")
resourceHints := make([]string, 0, len(tree))
for path := range tree {
path = strings.TrimSpace(path)
if path == "" {
continue
}
resourceHints = append(resourceHints, path)
}
return CollectSignals(
getDoc("/redfish/v1"),
getDoc(systemPath),
getDoc(chassisPath),
getDoc(managerPath),
resourceHints,
)
}
func lookupString(doc map[string]interface{}, key string) string {
if len(doc) == 0 {
return ""
}
value, _ := doc[key]
if s, ok := value.(string); ok {
return strings.TrimSpace(s)
}
return ""
}
func oemNamespaces(doc map[string]interface{}) []string {
if len(doc) == 0 {
return nil
}
oem, ok := doc["Oem"].(map[string]interface{})
if !ok {
return nil
}
out := make([]string, 0, len(oem))
for key := range oem {
key = strings.TrimSpace(key)
if key == "" {
continue
}
out = append(out, key)
}
return out
}
@@ -0,0 +1,17 @@
{
"ServiceRootVendor": "AMI",
"ServiceRootProduct": "AMI Redfish Server",
"SystemManufacturer": "Gigabyte",
"SystemModel": "G292-Z42",
"SystemSKU": "",
"ChassisManufacturer": "",
"ChassisModel": "",
"ManagerManufacturer": "",
"OEMNamespaces": ["Ami"],
"ResourceHints": [
"/redfish/v1/Chassis/Self",
"/redfish/v1/Managers/Self",
"/redfish/v1/Oem/Ami",
"/redfish/v1/Systems/Self"
]
}
@@ -0,0 +1,18 @@
{
"ServiceRootVendor": "",
"ServiceRootProduct": "iDRAC Redfish Service",
"SystemManufacturer": "Dell Inc.",
"SystemModel": "PowerEdge R750",
"SystemSKU": "0A42H9",
"ChassisManufacturer": "Dell Inc.",
"ChassisModel": "PowerEdge R750",
"ManagerManufacturer": "Dell Inc.",
"OEMNamespaces": ["Dell"],
"ResourceHints": [
"/redfish/v1/Chassis/System.Embedded.1",
"/redfish/v1/Managers/iDRAC.Embedded.1",
"/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell",
"/redfish/v1/Systems/System.Embedded.1",
"/redfish/v1/Systems/System.Embedded.1/Storage"
]
}
@@ -0,0 +1,33 @@
{
"ServiceRootVendor": "AMI",
"ServiceRootProduct": "AMI Redfish Server",
"SystemManufacturer": "Micro-Star International Co., Ltd.",
"SystemModel": "CG290-S3063",
"SystemSKU": "S3063G290RAU4",
"ChassisManufacturer": "NVIDIA",
"ChassisModel": "",
"ManagerManufacturer": "",
"OEMNamespaces": ["Ami"],
"ResourceHints": [
"/redfish/v1/Chassis/GPU1",
"/redfish/v1/Chassis/GPU1/NetworkAdapters",
"/redfish/v1/Chassis/GPU1/Sensors",
"/redfish/v1/Chassis/GPU1/Sensors/GPU1_Power",
"/redfish/v1/Chassis/GPU1/Sensors/GPU1_TLimit",
"/redfish/v1/Chassis/GPU1/Sensors/GPU1_Temperature",
"/redfish/v1/Chassis/GPU2",
"/redfish/v1/Chassis/GPU2/NetworkAdapters",
"/redfish/v1/Chassis/GPU2/Sensors",
"/redfish/v1/Chassis/GPU2/Sensors/GPU2_Power",
"/redfish/v1/Chassis/GPU2/Sensors/GPU2_TLimit",
"/redfish/v1/Chassis/GPU2/Sensors/GPU2_Temperature",
"/redfish/v1/Chassis/GPU3",
"/redfish/v1/Chassis/GPU3/NetworkAdapters",
"/redfish/v1/Chassis/GPU3/Sensors",
"/redfish/v1/Chassis/GPU3/Sensors/GPU3_Power",
"/redfish/v1/Chassis/GPU3/Sensors/GPU3_TLimit",
"/redfish/v1/Chassis/GPU3/Sensors/GPU3_Temperature",
"/redfish/v1/Chassis/GPU4",
"/redfish/v1/Chassis/GPU4/NetworkAdapters"
]
}
@@ -0,0 +1,33 @@
{
"ServiceRootVendor": "AMI",
"ServiceRootProduct": "AMI Redfish Server",
"SystemManufacturer": "Micro-Star International Co., Ltd.",
"SystemModel": "CG480-S5063",
"SystemSKU": "5063G480RAE20",
"ChassisManufacturer": "NVIDIA",
"ChassisModel": "",
"ManagerManufacturer": "",
"OEMNamespaces": ["Ami"],
"ResourceHints": [
"/redfish/v1/Chassis/GPU1",
"/redfish/v1/Chassis/GPU1/NetworkAdapters",
"/redfish/v1/Chassis/GPU1/Sensors",
"/redfish/v1/Chassis/GPU1/Sensors/GPU1_Power",
"/redfish/v1/Chassis/GPU1/Sensors/GPU1_TLimit",
"/redfish/v1/Chassis/GPU1/Sensors/GPU1_Temperature",
"/redfish/v1/Chassis/GPU2",
"/redfish/v1/Chassis/GPU2/NetworkAdapters",
"/redfish/v1/Chassis/GPU2/Sensors",
"/redfish/v1/Chassis/GPU2/Sensors/GPU2_Power",
"/redfish/v1/Chassis/GPU2/Sensors/GPU2_TLimit",
"/redfish/v1/Chassis/GPU2/Sensors/GPU2_Temperature",
"/redfish/v1/Chassis/GPU3",
"/redfish/v1/Chassis/GPU3/NetworkAdapters",
"/redfish/v1/Chassis/GPU3/Sensors",
"/redfish/v1/Chassis/GPU3/Sensors/GPU3_Power",
"/redfish/v1/Chassis/GPU3/Sensors/GPU3_TLimit",
"/redfish/v1/Chassis/GPU3/Sensors/GPU3_Temperature",
"/redfish/v1/Chassis/GPU4",
"/redfish/v1/Chassis/GPU4/NetworkAdapters"
]
}
@@ -0,0 +1,33 @@
{
"ServiceRootVendor": "AMI",
"ServiceRootProduct": "AMI Redfish Server",
"SystemManufacturer": "Micro-Star International Co., Ltd.",
"SystemModel": "CG480-S5063",
"SystemSKU": "5063G480RAE20",
"ChassisManufacturer": "NVIDIA",
"ChassisModel": "",
"ManagerManufacturer": "",
"OEMNamespaces": ["Ami"],
"ResourceHints": [
"/redfish/v1/Chassis/GPU1",
"/redfish/v1/Chassis/GPU1/NetworkAdapters",
"/redfish/v1/Chassis/GPU1/Sensors",
"/redfish/v1/Chassis/GPU1/Sensors/GPU1_Power",
"/redfish/v1/Chassis/GPU1/Sensors/GPU1_TLimit",
"/redfish/v1/Chassis/GPU1/Sensors/GPU1_Temperature",
"/redfish/v1/Chassis/GPU2",
"/redfish/v1/Chassis/GPU2/NetworkAdapters",
"/redfish/v1/Chassis/GPU2/Sensors",
"/redfish/v1/Chassis/GPU2/Sensors/GPU2_Power",
"/redfish/v1/Chassis/GPU2/Sensors/GPU2_TLimit",
"/redfish/v1/Chassis/GPU2/Sensors/GPU2_Temperature",
"/redfish/v1/Chassis/GPU3",
"/redfish/v1/Chassis/GPU3/NetworkAdapters",
"/redfish/v1/Chassis/GPU3/Sensors",
"/redfish/v1/Chassis/GPU3/Sensors/GPU3_Power",
"/redfish/v1/Chassis/GPU3/Sensors/GPU3_TLimit",
"/redfish/v1/Chassis/GPU3/Sensors/GPU3_Temperature",
"/redfish/v1/Chassis/GPU4",
"/redfish/v1/Chassis/GPU4/NetworkAdapters"
]
}
@@ -0,0 +1,33 @@
{
"ServiceRootVendor": "Supermicro",
"ServiceRootProduct": "",
"SystemManufacturer": "Supermicro",
"SystemModel": "SYS-821GE-TNHR",
"SystemSKU": "0x1D1415D9",
"ChassisManufacturer": "Supermicro",
"ChassisModel": "X13DEG-OAD",
"ManagerManufacturer": "",
"OEMNamespaces": ["Supermicro"],
"ResourceHints": [
"/redfish/v1/Chassis/HGX_BMC_0",
"/redfish/v1/Chassis/HGX_BMC_0/Assembly",
"/redfish/v1/Chassis/HGX_BMC_0/Controls",
"/redfish/v1/Chassis/HGX_BMC_0/Drives",
"/redfish/v1/Chassis/HGX_BMC_0/EnvironmentMetrics",
"/redfish/v1/Chassis/HGX_BMC_0/LogServices",
"/redfish/v1/Chassis/HGX_BMC_0/PCIeDevices",
"/redfish/v1/Chassis/HGX_BMC_0/PCIeSlots",
"/redfish/v1/Chassis/HGX_BMC_0/PowerSubsystem",
"/redfish/v1/Chassis/HGX_BMC_0/PowerSubsystem/PowerSupplies",
"/redfish/v1/Chassis/HGX_BMC_0/Sensors",
"/redfish/v1/Chassis/HGX_BMC_0/Sensors/HGX_BMC_0_Temp_0",
"/redfish/v1/Chassis/HGX_BMC_0/ThermalSubsystem",
"/redfish/v1/Chassis/HGX_BMC_0/ThermalSubsystem/ThermalMetrics",
"/redfish/v1/Chassis/HGX_Chassis_0",
"/redfish/v1/Chassis/HGX_Chassis_0/Assembly",
"/redfish/v1/Chassis/HGX_Chassis_0/Controls",
"/redfish/v1/Chassis/HGX_Chassis_0/Controls/TotalGPU_Power_0",
"/redfish/v1/Chassis/HGX_Chassis_0/Drives",
"/redfish/v1/Chassis/HGX_Chassis_0/EnvironmentMetrics"
]
}
@@ -0,0 +1,51 @@
{
"ServiceRootVendor": "",
"ServiceRootProduct": "H12DGQ-NT6",
"SystemManufacturer": "Supermicro",
"SystemModel": "AS -4124GQ-TNMI",
"SystemSKU": "091715D9",
"ChassisManufacturer": "Supermicro",
"ChassisModel": "H12DGQ-NT6",
"ManagerManufacturer": "",
"OEMNamespaces": [
"Supermicro"
],
"ResourceHints": [
"/redfish/v1/Chassis/1/PCIeDevices",
"/redfish/v1/Chassis/1/PCIeDevices/GPU1",
"/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions",
"/redfish/v1/Chassis/1/PCIeDevices/GPU1/PCIeFunctions/1",
"/redfish/v1/Chassis/1/PCIeDevices/GPU2",
"/redfish/v1/Chassis/1/PCIeDevices/GPU2/PCIeFunctions",
"/redfish/v1/Chassis/1/PCIeDevices/GPU2/PCIeFunctions/1",
"/redfish/v1/Chassis/1/PCIeDevices/GPU3",
"/redfish/v1/Chassis/1/PCIeDevices/GPU3/PCIeFunctions",
"/redfish/v1/Chassis/1/PCIeDevices/GPU3/PCIeFunctions/1",
"/redfish/v1/Chassis/1/PCIeDevices/GPU4",
"/redfish/v1/Chassis/1/PCIeDevices/GPU4/PCIeFunctions",
"/redfish/v1/Chassis/1/PCIeDevices/GPU4/PCIeFunctions/1",
"/redfish/v1/Chassis/1/PCIeDevices/GPU5",
"/redfish/v1/Chassis/1/PCIeDevices/GPU5/PCIeFunctions",
"/redfish/v1/Chassis/1/PCIeDevices/GPU5/PCIeFunctions/1",
"/redfish/v1/Chassis/1/PCIeDevices/GPU6",
"/redfish/v1/Chassis/1/PCIeDevices/GPU6/PCIeFunctions",
"/redfish/v1/Chassis/1/PCIeDevices/GPU6/PCIeFunctions/1",
"/redfish/v1/Chassis/1/PCIeDevices/GPU7",
"/redfish/v1/Chassis/1/PCIeDevices/GPU7/PCIeFunctions",
"/redfish/v1/Chassis/1/PCIeDevices/GPU7/PCIeFunctions/1",
"/redfish/v1/Chassis/1/PCIeDevices/GPU8",
"/redfish/v1/Chassis/1/PCIeDevices/GPU8/PCIeFunctions",
"/redfish/v1/Chassis/1/PCIeDevices/GPU8/PCIeFunctions/1",
"/redfish/v1/Managers/1/Oem/Supermicro/FanMode",
"/redfish/v1/Oem/Supermicro/DumpService",
"/redfish/v1/UpdateService/FirmwareInventory/GPU1",
"/redfish/v1/UpdateService/FirmwareInventory/GPU2",
"/redfish/v1/UpdateService/FirmwareInventory/GPU3",
"/redfish/v1/UpdateService/FirmwareInventory/GPU4",
"/redfish/v1/UpdateService/FirmwareInventory/GPU5",
"/redfish/v1/UpdateService/FirmwareInventory/GPU6",
"/redfish/v1/UpdateService/FirmwareInventory/GPU7",
"/redfish/v1/UpdateService/FirmwareInventory/GPU8",
"/redfish/v1/UpdateService/Oem/Supermicro/FirmwareInventory"
]
}
@@ -0,0 +1,16 @@
{
"ServiceRootVendor": "",
"ServiceRootProduct": "Redfish Service",
"SystemManufacturer": "",
"SystemModel": "",
"SystemSKU": "",
"ChassisManufacturer": "",
"ChassisModel": "",
"ManagerManufacturer": "",
"OEMNamespaces": [],
"ResourceHints": [
"/redfish/v1/Chassis/1",
"/redfish/v1/Managers/1",
"/redfish/v1/Systems/1"
]
}
+167
View File
@@ -0,0 +1,167 @@
package redfishprofile
import (
"sort"
"git.mchus.pro/mchus/logpile/internal/models"
)
type MatchSignals struct {
ServiceRootVendor string
ServiceRootProduct string
SystemManufacturer string
SystemModel string
SystemSKU string
ChassisManufacturer string
ChassisModel string
ManagerManufacturer string
OEMNamespaces []string
ResourceHints []string
}
type AcquisitionPlan struct {
Mode string
Profiles []string
SeedPaths []string
CriticalPaths []string
PlanBPaths []string
Notes []string
ScopedPaths AcquisitionScopedPathPolicy
Tuning AcquisitionTuning
}
type DiscoveredResources struct {
SystemPaths []string
ChassisPaths []string
ManagerPaths []string
}
type ResolvedAcquisitionPlan struct {
Plan AcquisitionPlan
SeedPaths []string
CriticalPaths []string
}
type AcquisitionScopedPathPolicy struct {
SystemSeedSuffixes []string
SystemCriticalSuffixes []string
ChassisSeedSuffixes []string
ChassisCriticalSuffixes []string
ManagerSeedSuffixes []string
ManagerCriticalSuffixes []string
}
type AcquisitionTuning struct {
SnapshotMaxDocuments int
SnapshotWorkers int
PrefetchEnabled *bool
PrefetchWorkers int
NVMePostProbeEnabled *bool
RatePolicy AcquisitionRatePolicy
ETABaseline AcquisitionETABaseline
PostProbePolicy AcquisitionPostProbePolicy
RecoveryPolicy AcquisitionRecoveryPolicy
PrefetchPolicy AcquisitionPrefetchPolicy
}
type AcquisitionRatePolicy struct {
TargetP95LatencyMS int
ThrottleP95LatencyMS int
MinSnapshotWorkers int
MinPrefetchWorkers int
DisablePrefetchOnErrors bool
}
type AcquisitionETABaseline struct {
DiscoverySeconds int
SnapshotSeconds int
PrefetchSeconds int
CriticalPlanBSeconds int
ProfilePlanBSeconds int
}
type AcquisitionPostProbePolicy struct {
EnableDirectNVMEDiskBayProbe bool
EnableNumericCollectionProbe bool
EnableSensorCollectionProbe bool
}
type AcquisitionRecoveryPolicy struct {
EnableCriticalCollectionMemberRetry bool
EnableCriticalSlowProbe bool
EnableProfilePlanB bool
}
type AcquisitionPrefetchPolicy struct {
IncludeSuffixes []string
ExcludeContains []string
}
type AnalysisDirectives struct {
EnableProcessorGPUFallback bool
EnableSupermicroNVMeBackplane bool
EnableProcessorGPUChassisAlias bool
EnableGenericGraphicsControllerDedup bool
EnableMSIProcessorGPUChassisLookup bool
EnableStorageEnclosureRecovery bool
EnableKnownStorageControllerRecovery bool
}
type ResolvedAnalysisPlan struct {
Match MatchResult
Directives AnalysisDirectives
Notes []string
ProcessorGPUChassisLookupModes []string
KnownStorageDriveCollections []string
KnownStorageVolumeCollections []string
}
type Profile interface {
Name() string
Priority() int
Match(signals MatchSignals) int
SafeForFallback() bool
ExtendAcquisitionPlan(plan *AcquisitionPlan, signals MatchSignals)
RefineAcquisitionPlan(resolved *ResolvedAcquisitionPlan, discovered DiscoveredResources, signals MatchSignals)
ApplyAnalysisDirectives(directives *AnalysisDirectives, signals MatchSignals)
RefineAnalysisPlan(plan *ResolvedAnalysisPlan, snapshot map[string]interface{}, discovered DiscoveredResources, signals MatchSignals)
PostAnalyze(result *models.AnalysisResult, snapshot map[string]interface{}, signals MatchSignals)
}
type MatchResult struct {
Mode string
Profiles []Profile
Scores []ProfileScore
}
type ProfileScore struct {
Name string
Score int
Active bool
Priority int
}
func normalizeSignals(signals MatchSignals) MatchSignals {
signals.OEMNamespaces = dedupeSorted(signals.OEMNamespaces)
signals.ResourceHints = dedupeSorted(signals.ResourceHints)
return signals
}
func dedupeSorted(items []string) []string {
if len(items) == 0 {
return nil
}
set := make(map[string]struct{}, len(items))
for _, item := range items {
if item == "" {
continue
}
set[item] = struct{}{}
}
out := make([]string, 0, len(set))
for item := range set {
out = append(out, item)
}
sort.Strings(out)
return out
}