Trim noisy Lenovo Redfish collection paths
This commit is contained in:
@@ -352,7 +352,15 @@ func TestMatchProfiles_LenovoXCCSelectsMatchedModeAndExcludesSensors(t *testing.
|
|||||||
ChassisManufacturer: "Lenovo",
|
ChassisManufacturer: "Lenovo",
|
||||||
OEMNamespaces: []string{"Lenovo"},
|
OEMNamespaces: []string{"Lenovo"},
|
||||||
})
|
})
|
||||||
wantExcluded := []string{"/Sensors/", "/Oem/Lenovo/LEDs/", "/Oem/Lenovo/Slots/"}
|
wantExcluded := []string{
|
||||||
|
"/Sensors/",
|
||||||
|
"/Oem/Lenovo/LEDs/",
|
||||||
|
"/Oem/Lenovo/Slots/",
|
||||||
|
"/Oem/Lenovo/Configuration",
|
||||||
|
"/NetworkProtocol/Oem/Lenovo/",
|
||||||
|
"/VirtualMedia/",
|
||||||
|
"/ThermalSubsystem/Fans/",
|
||||||
|
}
|
||||||
for _, want := range wantExcluded {
|
for _, want := range wantExcluded {
|
||||||
found := false
|
found := false
|
||||||
for _, ex := range plan.Tuning.SnapshotExcludeContains {
|
for _, ex := range plan.Tuning.SnapshotExcludeContains {
|
||||||
@@ -367,6 +375,46 @@ func TestMatchProfiles_LenovoXCCSelectsMatchedModeAndExcludesSensors(t *testing.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveAcquisitionPlan_LenovoFiltersNonInventoryChassisBranches(t *testing.T) {
|
||||||
|
signals := MatchSignals{
|
||||||
|
SystemManufacturer: "Lenovo",
|
||||||
|
ChassisManufacturer: "Lenovo",
|
||||||
|
OEMNamespaces: []string{"Lenovo"},
|
||||||
|
ResourceHints: []string{
|
||||||
|
"/redfish/v1/Chassis/1/Power",
|
||||||
|
"/redfish/v1/Chassis/1/Thermal",
|
||||||
|
"/redfish/v1/Chassis/1/NetworkAdapters",
|
||||||
|
"/redfish/v1/Chassis/3",
|
||||||
|
"/redfish/v1/Chassis/IO_Board",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
match := MatchProfiles(signals)
|
||||||
|
plan := BuildAcquisitionPlan(signals)
|
||||||
|
resolved := ResolveAcquisitionPlan(match, plan, DiscoveredResources{
|
||||||
|
ChassisPaths: []string{
|
||||||
|
"/redfish/v1/Chassis/1",
|
||||||
|
"/redfish/v1/Chassis/3",
|
||||||
|
"/redfish/v1/Chassis/IO_Board",
|
||||||
|
},
|
||||||
|
}, signals)
|
||||||
|
|
||||||
|
if !containsString(resolved.CriticalPaths, "/redfish/v1/Chassis/1/Power") {
|
||||||
|
t.Fatal("expected primary Lenovo chassis power path to remain critical")
|
||||||
|
}
|
||||||
|
if containsString(resolved.CriticalPaths, "/redfish/v1/Chassis/3/Power") {
|
||||||
|
t.Fatal("did not expect non-inventory Lenovo backplane chassis power path")
|
||||||
|
}
|
||||||
|
if containsString(resolved.CriticalPaths, "/redfish/v1/Chassis/IO_Board/Assembly") {
|
||||||
|
t.Fatal("did not expect IO board assembly path without inventory hints")
|
||||||
|
}
|
||||||
|
if containsString(resolved.Plan.PlanBPaths, "/redfish/v1/Chassis/3/Assembly") {
|
||||||
|
t.Fatal("did not expect non-inventory Lenovo chassis plan-b target")
|
||||||
|
}
|
||||||
|
if !containsString(resolved.CriticalPaths, "/redfish/v1/Chassis/3") {
|
||||||
|
t.Fatal("expected chassis root to remain discoverable even when suffixes are filtered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMatchProfiles_OrderingIsDeterministic(t *testing.T) {
|
func TestMatchProfiles_OrderingIsDeterministic(t *testing.T) {
|
||||||
signals := MatchSignals{
|
signals := MatchSignals{
|
||||||
SystemManufacturer: "Micro-Star International Co., Ltd.",
|
SystemManufacturer: "Micro-Star International Co., Ltd.",
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package redfishprofile
|
package redfishprofile
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
func lenovoProfile() Profile {
|
func lenovoProfile() Profile {
|
||||||
return staticProfile{
|
return staticProfile{
|
||||||
name: "lenovo",
|
name: "lenovo",
|
||||||
@@ -33,14 +35,30 @@ func lenovoProfile() Profile {
|
|||||||
// Lenovo OEM subtrees under Oem/Lenovo/LEDs and Oem/Lenovo/Slots also
|
// Lenovo OEM subtrees under Oem/Lenovo/LEDs and Oem/Lenovo/Slots also
|
||||||
// enumerate dozens of individual documents not relevant to inventory.
|
// enumerate dozens of individual documents not relevant to inventory.
|
||||||
ensureSnapshotExcludeContains(plan,
|
ensureSnapshotExcludeContains(plan,
|
||||||
"/Sensors/", // individual sensor docs (Chassis/1/Sensors/NNN)
|
"/Sensors/", // individual sensor docs (Chassis/1/Sensors/NNN)
|
||||||
"/Oem/Lenovo/LEDs/", // individual LED status entries (~47 per server)
|
"/Oem/Lenovo/LEDs/", // individual LED status entries (~47 per server)
|
||||||
"/Oem/Lenovo/Slots/", // individual slot detail entries (~26 per server)
|
"/Oem/Lenovo/Slots/", // individual slot detail entries (~26 per server)
|
||||||
"/Oem/Lenovo/Metrics/", // operational metrics, not inventory
|
"/Oem/Lenovo/Metrics/", // operational metrics, not inventory
|
||||||
"/Oem/Lenovo/History", // historical telemetry
|
"/Oem/Lenovo/History", // historical telemetry
|
||||||
"/Oem/Lenovo/ScheduledPower", // power scheduling config
|
"/Oem/Lenovo/Configuration", // BMC config service, not inventory
|
||||||
"/Oem/Lenovo/BootSettings/BootOrder", // individual boot order lists
|
"/Oem/Lenovo/DateTimeService", // BMC time service config
|
||||||
"/PortForwardingMap/", // network port forwarding config
|
"/Oem/Lenovo/GroupService", // XCC fleet/group management state
|
||||||
|
"/Oem/Lenovo/Recipients", // alert recipient config
|
||||||
|
"/Oem/Lenovo/RemoteControl", // remote-media/session management
|
||||||
|
"/Oem/Lenovo/RemoteMap", // remote-media mapping config
|
||||||
|
"/Oem/Lenovo/SecureKeyLifecycleService", // key lifecycle/cert config
|
||||||
|
"/Oem/Lenovo/ServerProfile", // profile export/import config
|
||||||
|
"/Oem/Lenovo/ServiceData", // support/service metadata
|
||||||
|
"/Oem/Lenovo/SsoCertificates", // SSO certificate config
|
||||||
|
"/Oem/Lenovo/SystemGuard", // snapshot/history service
|
||||||
|
"/Oem/Lenovo/Watchdogs", // watchdog config
|
||||||
|
"/Oem/Lenovo/ScheduledPower", // power scheduling config
|
||||||
|
"/Oem/Lenovo/BootSettings/BootOrder", // individual boot order lists
|
||||||
|
"/NetworkProtocol/Oem/Lenovo/", // DNS/LDAP/SMTP/SNMP manager config
|
||||||
|
"/PortForwardingMap/", // network port forwarding config
|
||||||
|
"/VirtualMedia/", // virtual media inventory/config, not hardware
|
||||||
|
"/Boot/Certificates", // secure boot certificate stores, not inventory
|
||||||
|
"/ThermalSubsystem/Fans/", // per-fan member docs; replay uses aggregate Thermal only
|
||||||
)
|
)
|
||||||
// Lenovo XCC BMC is typically slow (p95 latency often 3-5s even under
|
// Lenovo XCC BMC is typically slow (p95 latency often 3-5s even under
|
||||||
// normal load). Set rate thresholds that don't over-throttle on the
|
// normal load). Set rate thresholds that don't over-throttle on the
|
||||||
@@ -61,5 +79,97 @@ func lenovoProfile() Profile {
|
|||||||
})
|
})
|
||||||
addPlanNote(plan, "lenovo xcc acquisition extensions enabled: noisy sensor/oem paths excluded from snapshot")
|
addPlanNote(plan, "lenovo xcc acquisition extensions enabled: noisy sensor/oem paths excluded from snapshot")
|
||||||
},
|
},
|
||||||
|
refineAcquisition: func(resolved *ResolvedAcquisitionPlan, discovered DiscoveredResources, signals MatchSignals) {
|
||||||
|
allowedChassis := lenovoAllowedInventoryChassis(discovered.ChassisPaths, signals.ResourceHints)
|
||||||
|
resolved.SeedPaths = filterLenovoChassisInventoryPaths(resolved.SeedPaths, allowedChassis)
|
||||||
|
resolved.CriticalPaths = filterLenovoChassisInventoryPaths(resolved.CriticalPaths, allowedChassis)
|
||||||
|
resolved.Plan.SeedPaths = filterLenovoChassisInventoryPaths(resolved.Plan.SeedPaths, allowedChassis)
|
||||||
|
resolved.Plan.CriticalPaths = filterLenovoChassisInventoryPaths(resolved.Plan.CriticalPaths, allowedChassis)
|
||||||
|
resolved.Plan.PlanBPaths = filterLenovoChassisInventoryPaths(resolved.Plan.PlanBPaths, allowedChassis)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lenovoAllowedInventoryChassis(chassisPaths, resourceHints []string) map[string]struct{} {
|
||||||
|
allowed := make(map[string]struct{}, len(chassisPaths))
|
||||||
|
for _, chassisPath := range chassisPaths {
|
||||||
|
normalized := normalizePath(chassisPath)
|
||||||
|
if normalized == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if normalized == "/redfish/v1/Chassis/1" {
|
||||||
|
allowed[normalized] = struct{}{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, hint := range resourceHints {
|
||||||
|
hint = normalizePath(hint)
|
||||||
|
if !strings.HasPrefix(hint, normalized+"/") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if lenovoHintLooksLikeChassisInventory(hint) {
|
||||||
|
allowed[normalized] = struct{}{}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
func lenovoHintLooksLikeChassisInventory(path string) bool {
|
||||||
|
for _, suffix := range []string{
|
||||||
|
"/Power",
|
||||||
|
"/PowerSubsystem",
|
||||||
|
"/PowerSubsystem/PowerSupplies",
|
||||||
|
"/Thermal",
|
||||||
|
"/ThresholdSensors",
|
||||||
|
"/DiscreteSensors",
|
||||||
|
"/SensorsList",
|
||||||
|
"/NetworkAdapters",
|
||||||
|
"/PCIeDevices",
|
||||||
|
"/Drives",
|
||||||
|
"/Assembly",
|
||||||
|
} {
|
||||||
|
if strings.HasSuffix(path, suffix) || strings.Contains(path, suffix+"/") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterLenovoChassisInventoryPaths(paths []string, allowedChassis map[string]struct{}) []string {
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]string, 0, len(paths))
|
||||||
|
for _, path := range paths {
|
||||||
|
normalized := normalizePath(path)
|
||||||
|
chassis := lenovoPathChassisRoot(normalized)
|
||||||
|
if chassis == "" {
|
||||||
|
out = append(out, normalized)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if normalized == chassis {
|
||||||
|
out = append(out, normalized)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := allowedChassis[chassis]; ok {
|
||||||
|
out = append(out, normalized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dedupeSorted(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lenovoPathChassisRoot(path string) string {
|
||||||
|
const prefix = "/redfish/v1/Chassis/"
|
||||||
|
if !strings.HasPrefix(path, prefix) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
rest := strings.TrimPrefix(path, prefix)
|
||||||
|
if rest == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if idx := strings.IndexByte(rest, '/'); idx >= 0 {
|
||||||
|
return prefix + rest[:idx]
|
||||||
|
}
|
||||||
|
return prefix + rest
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user