diff --git a/internal/collector/redfishprofile/profile_inspur_group_oem_platforms.go b/internal/collector/redfishprofile/profile_inspur_group_oem_platforms.go index 9cee549..6c78dbc 100644 --- a/internal/collector/redfishprofile/profile_inspur_group_oem_platforms.go +++ b/internal/collector/redfishprofile/profile_inspur_group_oem_platforms.go @@ -29,6 +29,7 @@ func inspurGroupOEMPlatformsProfile() Profile { matchFn: func(s MatchSignals) int { topologyScore := 0 boardScore := 0 + manufacturerScore := 0 chassisOutboard := matchedPathTokens(s.ResourceHints, "/redfish/v1/Chassis/", outboardCardHintRe) systemOutboard := matchedPathTokens(s.ResourceHints, "/redfish/v1/Systems/", outboardCardHintRe) obDrives := matchedPathTokens(s.ResourceHints, "", obDriveHintRe) @@ -62,10 +63,17 @@ func inspurGroupOEMPlatformsProfile() Profile { if anySignalContains(s, "GetServerAllUSBStatus") { boardScore += 8 } - if topologyScore == 0 || boardScore == 0 { + // Manufacturer alone is sufficient for standard Inspur servers (e.g. NF-series + // storage servers) that lack GPU/outboard-PCIe topology signals. Score 60 is + // the minimum to enter matched mode; topology+board can push it higher. + if containsFold(s.SystemManufacturer, "inspur") || containsFold(s.ChassisManufacturer, "inspur") { + manufacturerScore = 60 + } + total := manufacturerScore + topologyScore + boardScore + if total < 60 { return 0 } - return min(topologyScore+boardScore, 100) + return min(total, 100) }, extendAcquisition: func(plan *AcquisitionPlan, _ MatchSignals) { addPlanNote(plan, "Inspur Group OEM platform fingerprint matched") diff --git a/internal/collector/redfishprofile/profile_inspur_group_oem_platforms_test.go b/internal/collector/redfishprofile/profile_inspur_group_oem_platforms_test.go index 156a4ef..70ee38c 100644 --- a/internal/collector/redfishprofile/profile_inspur_group_oem_platforms_test.go +++ b/internal/collector/redfishprofile/profile_inspur_group_oem_platforms_test.go @@ -118,6 +118,52 @@ func TestCollectSignalsFromTree_InspurGroupOEMPlatformsSelectsMatchedMode(t *tes assertProfileSelected(t, match, "inspur-group-oem-platforms") } +// TestCollectSignalsFromTree_InspurGroupOEMPlatformsMatchesViaManufacturer covers standard +// Inspur storage servers (e.g. NF5280M6) that have no outboard PCIe / GPU topology but +// do expose Manufacturer="Inspur" in their System document. +func TestCollectSignalsFromTree_InspurGroupOEMPlatformsMatchesViaManufacturer(t *testing.T) { + // Minimal tree: no GPU cards, no OEM firmware hints — only System Manufacturer. + tree := map[string]interface{}{ + "/redfish/v1": map[string]interface{}{ + "@odata.id": "/redfish/v1", + }, + "/redfish/v1/Systems": map[string]interface{}{ + "Members": []interface{}{ + map[string]interface{}{"@odata.id": "/redfish/v1/Systems/1"}, + }, + }, + "/redfish/v1/Systems/1": map[string]interface{}{ + "@odata.id": "/redfish/v1/Systems/1", + "Manufacturer": "Inspur", + "Model": "NF5280M6", + }, + "/redfish/v1/Chassis": map[string]interface{}{ + "Members": []interface{}{ + map[string]interface{}{"@odata.id": "/redfish/v1/Chassis/1"}, + }, + }, + "/redfish/v1/Chassis/1": map[string]interface{}{ + "@odata.id": "/redfish/v1/Chassis/1", + }, + "/redfish/v1/Managers": map[string]interface{}{ + "Members": []interface{}{ + map[string]interface{}{"@odata.id": "/redfish/v1/Managers/1"}, + }, + }, + "/redfish/v1/Managers/1": map[string]interface{}{ + "@odata.id": "/redfish/v1/Managers/1", + }, + } + + signals := CollectSignalsFromTree(tree) + match := MatchProfiles(signals) + + if match.Mode != ModeMatched { + t.Fatalf("expected matched mode for Inspur NF-series, got %q (scores: %v)", match.Mode, match.Scores) + } + assertProfileSelected(t, match, "inspur-group-oem-platforms") +} + func TestCollectSignalsFromTree_InspurGroupOEMPlatformsDoesNotFalsePositiveOnExampleRawExports(t *testing.T) { examples := []string{ "2026-03-18 (G5500 V7) - 210619KUGGXGS2000015.zip",