From d607bc13424285ca463ff6be812e8a19be251d28 Mon Sep 17 00:00:00 2001 From: Matt Vandermeulen Date: Wed, 9 Oct 2024 14:01:37 -0300 Subject: [PATCH 1/2] redfish/drive: expose oem data and actions Signed-off-by: Matt Vandermeulen --- redfish/drive.go | 21 ++++++++++++--------- redfish/drive_test.go | 4 ++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/redfish/drive.go b/redfish/drive.go index 79eace6a..c6e248bb 100644 --- a/redfish/drive.go +++ b/redfish/drive.go @@ -299,6 +299,10 @@ type Drive struct { // WriteCacheEnabled shall indicate whether the drive // write cache is enabled. WriteCacheEnabled bool + // Actions are all the listed actions under the drive + Actions DriveActions + // Oem is all the available OEM information for the drive + Oem json.RawMessage // ActiveSoftwareImage shall contain a link a resource of type SoftwareInventory that represents the active drive // firmware image. @@ -329,12 +333,16 @@ type Drive struct { storagePools []string // storagePools []string StoragePoolsCount int - // secureEraseTarget is the URL for SecureErase actions. - secureEraseTarget string // rawData holds the original serialized JSON so we can compare updates. rawData []byte } +// DriveActions are a set of actions available under a drive +type DriveActions struct { + SecureErase common.ActionTarget `json:"#Drive.SecureErase"` + Oem json.RawMessage +} + // UnmarshalJSON unmarshals a Drive object from the raw JSON. func (drive *Drive) UnmarshalJSON(b []byte) error { type temp Drive @@ -364,13 +372,9 @@ func (drive *Drive) UnmarshalJSON(b []byte) error { Volumes common.Links VolumeCount int `json:"Volumes@odata.count"` } - type Actions struct { - SecureErase common.ActionTarget `json:"#Drive.SecureErase"` - } var t struct { temp Links links - Actions Actions Assembly common.Link EnvironmentMetrics common.Link } @@ -391,6 +395,7 @@ func (drive *Drive) UnmarshalJSON(b []byte) error { drive.EndpointsCount = t.Links.EndpointCount drive.networkDeviceFunctions = t.Links.NetworkDeviceFunctions.ToStrings() drive.NetworkDeviceFunctionsCount = t.Links.NetworkDeviceFunctionsCount + drive.Oem = t.Oem drive.pcieFunctions = t.Links.PCIeFunctions.ToStrings() drive.PCIeFunctionCount = t.Links.PCIeFunctionsCount drive.softwareImages = t.Links.SoftwareImages.ToStrings() @@ -401,8 +406,6 @@ func (drive *Drive) UnmarshalJSON(b []byte) error { drive.volumes = t.Links.Volumes.ToStrings() drive.VolumesCount = t.Links.VolumeCount - drive.secureEraseTarget = t.Actions.SecureErase.Target - // This is a read/write object, so we need to save the raw object data for later drive.rawData = b @@ -486,5 +489,5 @@ func (drive *Drive) PCIeFunctions() ([]*PCIeFunction, error) { // SecureErase shall perform a secure erase of the drive. func (drive *Drive) SecureErase() error { - return drive.Post(drive.secureEraseTarget, nil) + return drive.Post(drive.Actions.SecureErase.Target, nil) } diff --git a/redfish/drive_test.go b/redfish/drive_test.go index ed3d6f68..2532751a 100644 --- a/redfish/drive_test.go +++ b/redfish/drive_test.go @@ -138,8 +138,8 @@ func TestDrive(t *testing.T) { t.Errorf("Invalid chassis link: %s", result.chassis) } - if result.secureEraseTarget != "/redfish/v1/Chassis/NVMeChassis/Disk.Bay.0/Actions/Drive.SecureErase" { - t.Errorf("Invalid SecureErase target: %s", result.secureEraseTarget) + if result.Actions.SecureErase.Target != "/redfish/v1/Chassis/NVMeChassis/Disk.Bay.0/Actions/Drive.SecureErase" { + t.Errorf("Invalid SecureErase target: %s", result.Actions.SecureErase.Target) } } From 8bb070cc4e63832ca3dc116efcd3e65f92bac90c Mon Sep 17 00:00:00 2001 From: Matt Vandermeulen Date: Wed, 9 Oct 2024 15:58:03 -0300 Subject: [PATCH 2/2] oem/smc: introduce drive and drive action oem fields --- oem/smc/drive.go | 62 +++++++++++++++++++++++++++++++++ oem/smc/drive_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 oem/smc/drive.go create mode 100644 oem/smc/drive_test.go diff --git a/oem/smc/drive.go b/oem/smc/drive.go new file mode 100644 index 00000000..a69d51fd --- /dev/null +++ b/oem/smc/drive.go @@ -0,0 +1,62 @@ +package smc + +import ( + "encoding/json" + + "github.com/stmcginnis/gofish/redfish" +) + +type Drive struct { + redfish.Drive + Oem DriveOem `json:"Oem"` +} + +type DriveOem struct { + Supermicro struct { + Temperature int + PercentageDriveLifeUsed int + DriveFunctional bool + } `json:"Supermicro"` +} + +type DriveTarget struct { + Target string `json:"target"` + ActionInfo string `json:"@Redfish.ActionInfo"` +} + +type DriveActions struct { + redfish.DriveActions + Oem struct { + DriveIndicate DriveTarget `json:"#Drive.Indicate"` + SmcDriveIndicate DriveTarget `json:"#SmcDrive.Indicate"` + } `json:"Oem"` +} + +func FromDrive(drive *redfish.Drive) (Drive, error) { + var oem DriveOem + err := json.Unmarshal(drive.Oem, &oem) + + return Drive{ + Drive: *drive, + Oem: oem, + }, err +} + +func FromDriveActions(da *redfish.DriveActions) (DriveActions, error) { + oemActions := DriveActions{ + DriveActions: *da, + } + + err := json.Unmarshal(da.Oem, &oemActions.Oem) + return oemActions, err +} + +// DriveIndicateTarget checks both the SmcDriveIndicate and DriveIndicateTarget +// Oem entries and returns the first populated target, due to key inconsistencies +func (da DriveActions) DriveIndicateTarget() string { + if len(da.Oem.SmcDriveIndicate.Target) > 0 { + return da.Oem.SmcDriveIndicate.Target + } + + return da.Oem.DriveIndicate.Target +} diff --git a/oem/smc/drive_test.go b/oem/smc/drive_test.go new file mode 100644 index 00000000..d8045935 --- /dev/null +++ b/oem/smc/drive_test.go @@ -0,0 +1,80 @@ +package smc + +import ( + "encoding/json" + "testing" + + "github.com/stmcginnis/gofish/redfish" +) + +var smcDriveBody = `{ + "@odata.type": "#Drive.v1_6_2.Drive", + "@odata.id": "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives/Disk.Bay.22", + "Name": "Disk.Bay.22", + "Id": "22", + "Manufacturer": "INTEL", + "SerialNumber": "PHLWOOFMEOWIAMCATDOG", + "Model": "INTEL SSDPE2KX080T8O", + "StatusIndicator": "OK", + "FailurePredicted": false, + "CapacityBytes": 8001563222016, + "CapableSpeedGbs": 31.5, + "Oem": { + "Supermicro": { + "@odata.type": "#SmcDriveExtensions.v1_0_0.Drive", + "Temperature": 33, + "PercentageDriveLifeUsed": 3, + "DriveFunctional": true + } + }, + "IndicatorLED": "Off", + "Status": { + "State": "Enabled", + "Health": "OK" + }, + "Links": { + "Volumes": [] + }, + "Actions": { + "Oem": { + "#Drive.Indicate": { + "target": "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives/Disk.Bay.22/Actions/Oem/Drive.Indicate", + "@Redfish.ActionInfo": "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives/Disk.Bay.22/IndicateActionInfo" + } + } + } +}` + +// TestSmcDriveOem tests the parsing of the Drive oem field +func TestSmcDriveOem(t *testing.T) { + drive := &redfish.Drive{} + if err := json.Unmarshal([]byte(smcDriveBody), drive); err != nil { + t.Fatalf("error decoding json: %v", err) + } + + smcDrive, err := FromDrive(drive) + if err != nil { + t.Fatalf("error getting oem info from drive: %v", err) + } + + if smcDrive.Oem.Supermicro.Temperature != 33 { + t.Errorf("unexpected oem drive temerature: %d", smcDrive.Oem.Supermicro.Temperature) + } +} + +// TestSmcDriveActionOem tests the parsing of the Drive Actions field +func TestSmcDriveActionOem(t *testing.T) { + drive := &redfish.Drive{} + if err := json.Unmarshal([]byte(smcDriveBody), drive); err != nil { + t.Fatalf("error decoding json: %v", err) + } + + smcDriveActions, err := FromDriveActions(&drive.Actions) + if err != nil { + t.Fatalf("error getting oem info from drive: %v", err) + } + + if smcDriveActions.DriveIndicateTarget() != "/redfish/v1/Chassis/NVMeSSD.0.Group.0.StorageBackplane/Drives/Disk.Bay.22/Actions/Oem/Drive.Indicate" { + t.Errorf("unexpected oem drive indicator target: %s", smcDriveActions.DriveIndicateTarget()) + } +}