raid: add storcli2 support for Tri-Mode controllers (SAS3808-iMR/9500 series)

storcli64 enumerates these controllers but reports zero drives; storcli2
is the tool Broadcom ships for Tri-Mode/MegaRAID8 hardware and uses a
compatible JSON schema for drive listing. Wires storcli2 into both the
collector (structured drive data) and the webui RAID Management page
(dedup so a controller isn't double-listed if storcli64 already sees it
with zero drives), plus techdump raw collection and the ISO vendor-tool
build step.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-07-07 15:25:06 +03:00
co-authored by Claude Sonnet 5
parent a3377083aa
commit b7f015c713
7 changed files with 307 additions and 2 deletions
+60
View File
@@ -41,6 +41,9 @@ func collectRAIDStorage(pcie []schema.HardwarePCIeDevice) []schema.HardwareStora
if drives := collectStorcliDrives(); len(drives) > 0 {
out = append(out, drives...)
}
if drives := collectStorcli2Drives(); len(drives) > 0 {
out = append(out, drives...)
}
if drives := collectSASIrcuDrives("sas3ircu"); len(drives) > 0 {
out = append(out, drives...)
}
@@ -99,6 +102,63 @@ func collectStorcliDrives() []schema.HardwareStorage {
return drives
}
// collectStorcli2Drives covers Broadcom Tri-Mode controllers (e.g.
// SAS3808-iMR/9500 series), which storcli64 can enumerate at a basic level
// but whose drives it never finds — a separate tool/JSON schema (storcli2)
// is required. storcli2's "Drive Information" array shape matches storcli64's
// (confirmed against Broadcom's published drive-info-schema.json), so
// parseStorcliDrivesJSON is reused as-is; only the controller enumeration
// and per-controller invocation syntax differ (storcli2 wants an explicit
// /cN target rather than storcli64's /call wildcard).
func collectStorcli2Drives() []schema.HardwareStorage {
sysOut, err := raidToolQuery("storcli2", "show", "all", "J")
if err != nil {
slog.Info("raid: storcli2 unavailable", "err", err)
return nil
}
indices := parseStorcli2ControllerIndices(sysOut)
if len(indices) == 0 {
return nil
}
var drives []schema.HardwareStorage
for _, idx := range indices {
out, err := raidToolQuery("storcli2", "/c"+strconv.Itoa(idx)+"/eall/sall", "show", "all", "J")
if err != nil {
continue
}
drives = append(drives, parseStorcliDrivesJSON(out)...)
}
if len(drives) == 0 {
slog.Info("raid: storcli2 returned no drives")
}
return drives
}
// parseStorcli2ControllerIndices parses "storcli2 show all J" (Broadcom's
// system-schema.json: Controllers[0].Response Data.System Overview[].Ctrl).
func parseStorcli2ControllerIndices(raw []byte) []int {
var doc struct {
Controllers []struct {
ResponseData struct {
SystemOverview []struct {
Ctrl int `json:"Ctrl"`
} `json:"System Overview"`
} `json:"Response Data"`
} `json:"Controllers"`
}
if err := json.Unmarshal(raw, &doc); err != nil {
return nil
}
var indices []int
for _, c := range doc.Controllers {
for _, ov := range c.ResponseData.SystemOverview {
indices = append(indices, ov.Ctrl)
}
}
return indices
}
func collectSASIrcuDrives(tool string) []schema.HardwareStorage {
out, err := raidToolQuery(tool, "list")
if err != nil {