Expand Redfish storage fallback for enclosure Disk.Bay paths
This commit is contained in:
@@ -186,6 +186,11 @@ func (c *RedfishConnector) collectStorage(ctx context.Context, client *http.Clie
|
|||||||
for _, driveDoc := range driveDocs {
|
for _, driveDoc := range driveDocs {
|
||||||
out = append(out, parseDrive(driveDoc))
|
out = append(out, parseDrive(driveDoc))
|
||||||
}
|
}
|
||||||
|
if len(driveDocs) == 0 {
|
||||||
|
for _, driveDoc := range c.probeDirectDiskBayChildren(ctx, client, req, baseURL, driveCollectionPath) {
|
||||||
|
out = append(out, parseDrive(driveDoc))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -213,6 +218,24 @@ func (c *RedfishConnector) collectStorage(ctx context.Context, client *http.Clie
|
|||||||
if looksLikeDrive(member) {
|
if looksLikeDrive(member) {
|
||||||
out = append(out, parseDrive(member))
|
out = append(out, parseDrive(member))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Supermicro/RAID implementations can expose physical disks under chassis enclosures
|
||||||
|
// linked from Storage.Links.Enclosures, while Storage.Drives stays empty.
|
||||||
|
for _, enclosurePath := range redfishLinkRefs(member, "Links", "Enclosures") {
|
||||||
|
driveDocs, err := c.getCollectionMembers(ctx, client, req, baseURL, joinPath(enclosurePath, "/Drives"))
|
||||||
|
if err == nil {
|
||||||
|
for _, driveDoc := range driveDocs {
|
||||||
|
if looksLikeDrive(driveDoc) {
|
||||||
|
out = append(out, parseDrive(driveDoc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(driveDocs) == 0 {
|
||||||
|
for _, driveDoc := range c.probeDirectDiskBayChildren(ctx, client, req, baseURL, joinPath(enclosurePath, "/Drives")) {
|
||||||
|
out = append(out, parseDrive(driveDoc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback for platforms that expose disks in SimpleStorage.
|
// Fallback for platforms that expose disks in SimpleStorage.
|
||||||
@@ -615,10 +638,10 @@ func (c *RedfishConnector) collectRawRedfishTree(ctx context.Context, client *ht
|
|||||||
// Some Supermicro BMCs expose NVMe disks at direct Disk.Bay endpoints even when the
|
// Some Supermicro BMCs expose NVMe disks at direct Disk.Bay endpoints even when the
|
||||||
// Drives collection returns Members: []. Probe those paths so raw export can be replayed.
|
// Drives collection returns Members: []. Probe those paths so raw export can be replayed.
|
||||||
for path := range out {
|
for path := range out {
|
||||||
if !isSupermicroNVMeBackplanePath(path) {
|
if !strings.HasSuffix(normalizeRedfishPath(path), "/Drives") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, bayPath := range supermicroNVMeDiskBayCandidates(path) {
|
for _, bayPath := range directDiskBayCandidates(path) {
|
||||||
doc, err := c.getJSON(ctx, client, req, baseURL, bayPath)
|
doc, err := c.getJSON(ctx, client, req, baseURL, bayPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
@@ -643,8 +666,21 @@ func (c *RedfishConnector) collectRawRedfishTree(ctx context.Context, client *ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *RedfishConnector) probeSupermicroNVMeDiskBays(ctx context.Context, client *http.Client, req Request, baseURL, backplanePath string) []map[string]interface{} {
|
func (c *RedfishConnector) probeSupermicroNVMeDiskBays(ctx context.Context, client *http.Client, req Request, baseURL, backplanePath string) []map[string]interface{} {
|
||||||
|
return c.probeDirectDiskBayChildren(ctx, client, req, baseURL, joinPath(backplanePath, "/Drives"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSupermicroNVMeBackplanePath(path string) bool {
|
||||||
|
path = normalizeRedfishPath(path)
|
||||||
|
return strings.Contains(path, "/Chassis/NVMeSSD.") && strings.Contains(path, ".StorageBackplane")
|
||||||
|
}
|
||||||
|
|
||||||
|
func supermicroNVMeDiskBayCandidates(backplanePath string) []string {
|
||||||
|
return directDiskBayCandidates(joinPath(backplanePath, "/Drives"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RedfishConnector) probeDirectDiskBayChildren(ctx context.Context, client *http.Client, req Request, baseURL, drivesCollectionPath string) []map[string]interface{} {
|
||||||
var out []map[string]interface{}
|
var out []map[string]interface{}
|
||||||
for _, path := range supermicroNVMeDiskBayCandidates(backplanePath) {
|
for _, path := range directDiskBayCandidates(drivesCollectionPath) {
|
||||||
doc, err := c.getJSON(ctx, client, req, baseURL, path)
|
doc, err := c.getJSON(ctx, client, req, baseURL, path)
|
||||||
if err != nil || !looksLikeDrive(doc) {
|
if err != nil || !looksLikeDrive(doc) {
|
||||||
continue
|
continue
|
||||||
@@ -654,18 +690,36 @@ func (c *RedfishConnector) probeSupermicroNVMeDiskBays(ctx context.Context, clie
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSupermicroNVMeBackplanePath(path string) bool {
|
func directDiskBayCandidates(drivesCollectionPath string) []string {
|
||||||
path = normalizeRedfishPath(path)
|
const maxBays = 128
|
||||||
return strings.Contains(path, "/Chassis/NVMeSSD.") && strings.Contains(path, ".StorageBackplane")
|
prefix := normalizeRedfishPath(drivesCollectionPath)
|
||||||
}
|
out := make([]string, 0, maxBays*3)
|
||||||
|
|
||||||
func supermicroNVMeDiskBayCandidates(backplanePath string) []string {
|
|
||||||
const maxBays = 64
|
|
||||||
prefix := joinPath(backplanePath, "/Drives")
|
|
||||||
out := make([]string, 0, maxBays*2)
|
|
||||||
for i := 0; i < maxBays; i++ {
|
for i := 0; i < maxBays; i++ {
|
||||||
out = append(out, fmt.Sprintf("%s/Disk.Bay.%d", prefix, i))
|
out = append(out, fmt.Sprintf("%s/Disk.Bay.%d", prefix, i))
|
||||||
out = append(out, fmt.Sprintf("%s/Disk.Bay%d", prefix, i))
|
out = append(out, fmt.Sprintf("%s/Disk.Bay%d", prefix, i))
|
||||||
|
out = append(out, fmt.Sprintf("%s/%d", prefix, i))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func redfishLinkRefs(doc map[string]interface{}, topKey, nestedKey string) []string {
|
||||||
|
top, ok := doc[topKey].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
items, ok := top[nestedKey].([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]string, 0, len(items))
|
||||||
|
for _, itemAny := range items {
|
||||||
|
item, ok := itemAny.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p := asString(item["@odata.id"]); p != "" {
|
||||||
|
out = append(out, p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,6 +208,11 @@ func (r redfishSnapshotReader) collectStorage(systemPath string) []models.Storag
|
|||||||
for _, driveDoc := range driveDocs {
|
for _, driveDoc := range driveDocs {
|
||||||
out = append(out, parseDrive(driveDoc))
|
out = append(out, parseDrive(driveDoc))
|
||||||
}
|
}
|
||||||
|
if len(driveDocs) == 0 {
|
||||||
|
for _, driveDoc := range r.probeDirectDiskBayChildren(driveCollectionPath) {
|
||||||
|
out = append(out, parseDrive(driveDoc))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -233,6 +238,22 @@ func (r redfishSnapshotReader) collectStorage(systemPath string) []models.Storag
|
|||||||
if looksLikeDrive(member) {
|
if looksLikeDrive(member) {
|
||||||
out = append(out, parseDrive(member))
|
out = append(out, parseDrive(member))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, enclosurePath := range redfishLinkRefs(member, "Links", "Enclosures") {
|
||||||
|
driveDocs, err := r.getCollectionMembers(joinPath(enclosurePath, "/Drives"))
|
||||||
|
if err == nil {
|
||||||
|
for _, driveDoc := range driveDocs {
|
||||||
|
if looksLikeDrive(driveDoc) {
|
||||||
|
out = append(out, parseDrive(driveDoc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(driveDocs) == 0 {
|
||||||
|
for _, driveDoc := range r.probeDirectDiskBayChildren(joinPath(enclosurePath, "/Drives")) {
|
||||||
|
out = append(out, parseDrive(driveDoc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
simpleStorageMembers, _ := r.getCollectionMembers(joinPath(systemPath, "/SimpleStorage"))
|
simpleStorageMembers, _ := r.getCollectionMembers(joinPath(systemPath, "/SimpleStorage"))
|
||||||
@@ -278,8 +299,12 @@ func (r redfishSnapshotReader) collectStorage(systemPath string) []models.Storag
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r redfishSnapshotReader) probeSupermicroNVMeDiskBays(backplanePath string) []map[string]interface{} {
|
func (r redfishSnapshotReader) probeSupermicroNVMeDiskBays(backplanePath string) []map[string]interface{} {
|
||||||
|
return r.probeDirectDiskBayChildren(joinPath(backplanePath, "/Drives"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r redfishSnapshotReader) probeDirectDiskBayChildren(drivesCollectionPath string) []map[string]interface{} {
|
||||||
var out []map[string]interface{}
|
var out []map[string]interface{}
|
||||||
for _, path := range supermicroNVMeDiskBayCandidates(backplanePath) {
|
for _, path := range directDiskBayCandidates(drivesCollectionPath) {
|
||||||
doc, err := r.getJSON(path)
|
doc, err := r.getJSON(path)
|
||||||
if err != nil || !looksLikeDrive(doc) {
|
if err != nil || !looksLikeDrive(doc) {
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user