-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Supermicro UpdateService object (#375)
This adds an OEM version of the UpdateService to expose some OEM-specific actions and properties. Signed-off-by: Sean McGinnis <[email protected]>
- Loading branch information
1 parent
7257532
commit 4c227db
Showing
3 changed files
with
280 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
// | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
// | ||
|
||
package smc | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
|
||
"github.com/stmcginnis/gofish/common" | ||
"github.com/stmcginnis/gofish/redfish" | ||
) | ||
|
||
type SSLCert struct { | ||
common.Entity | ||
|
||
ValidFrom string | ||
GoodThrough string `json:"GoodTHRU"` | ||
|
||
uploadTarget string | ||
} | ||
|
||
// UnmarshalJSON unmarshals a UpdateService object from the raw JSON. | ||
func (cert *SSLCert) UnmarshalJSON(b []byte) error { | ||
type temp SSLCert | ||
var t struct { | ||
temp | ||
Actions struct { | ||
Upload common.ActionTarget `json:"#SmcSSLCert.Upload"` | ||
} | ||
} | ||
|
||
err := json.Unmarshal(b, &t) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
*cert = SSLCert(t.temp) | ||
cert.uploadTarget = t.Actions.Upload.Target | ||
|
||
return nil | ||
} | ||
|
||
// GetSSLCert will get the SSLCert instance from the Redfish | ||
// service. | ||
func GetSSLCert(c common.Client, uri string) (*SSLCert, error) { | ||
return common.GetObject[SSLCert](c, uri) | ||
} | ||
|
||
// Upload installs an SSL cert. | ||
// NOTE: This is probably not correct. The jsonschema reported by SMC does not | ||
// include any parameters for this action. That seems very unlikely, so expect | ||
// this to fail. | ||
func (cert *SSLCert) Upload() error { | ||
if cert.uploadTarget == "" { | ||
return errors.New("upload is not supported by this system") | ||
} | ||
|
||
return cert.Post(cert.uploadTarget, nil) | ||
} | ||
|
||
type IPMIConfig struct { | ||
common.Entity | ||
|
||
uploadTarget string | ||
downloadTarget string | ||
} | ||
|
||
// UnmarshalJSON unmarshals a UpdateService object from the raw JSON. | ||
func (ipmi *IPMIConfig) UnmarshalJSON(b []byte) error { | ||
type temp IPMIConfig | ||
var t struct { | ||
temp | ||
Actions struct { | ||
Upload common.ActionTarget `json:"#SmcIPMIConfig.Upload"` | ||
Download common.ActionTarget `json:"#SmcIPMIConfig.Download"` | ||
} | ||
} | ||
|
||
err := json.Unmarshal(b, &t) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
*ipmi = IPMIConfig(t.temp) | ||
ipmi.uploadTarget = t.Actions.Upload.Target | ||
ipmi.downloadTarget = t.Actions.Download.Target | ||
|
||
return nil | ||
} | ||
|
||
// GetIPMIConfig will get the IPMIConfig instance from the Redfish | ||
// service. | ||
func GetIPMIConfig(c common.Client, uri string) (*IPMIConfig, error) { | ||
return common.GetObject[IPMIConfig](c, uri) | ||
} | ||
|
||
// Upload restores a saved IPMI configuration. | ||
// NOTE: This is probably not correct. The jsonschema reported by SMC does not | ||
// include any parameters for this action. That seems very unlikely, so expect | ||
// this to fail. | ||
func (ipmi *IPMIConfig) Upload() error { | ||
if ipmi.uploadTarget == "" { | ||
return errors.New("upload is not supported by this system") | ||
} | ||
|
||
return ipmi.Post(ipmi.uploadTarget, nil) | ||
} | ||
|
||
// Download saves the current IPMI configuration. | ||
// NOTE: This is probably not correct. The jsonschema reported by SMC does not | ||
// include any parameters for this action. That seems very unlikely, so expect | ||
// this to fail. | ||
func (ipmi *IPMIConfig) Download() error { | ||
if ipmi.downloadTarget == "" { | ||
return errors.New("download is not supported by this system") | ||
} | ||
|
||
return ipmi.Post(ipmi.downloadTarget, nil) | ||
} | ||
|
||
// UpdateService is the license manager instance associated with the system. | ||
type UpdateService struct { | ||
redfish.UpdateService | ||
|
||
sslCert string | ||
ipmiConfig string | ||
|
||
installTarget string | ||
} | ||
|
||
// FromUpdateService gets the OEM instance of the UpdateService. | ||
func FromUpdateService(updateService *redfish.UpdateService) (*UpdateService, error) { | ||
us := UpdateService{ | ||
UpdateService: *updateService, | ||
} | ||
|
||
var t struct { | ||
Actions struct { | ||
Oem struct { | ||
Install common.ActionTarget `json:"#SmcUpdateService.Install"` | ||
} | ||
} | ||
Oem struct { | ||
Supermicro struct { | ||
SSLCert common.Link | ||
IPMIConfig common.Link | ||
} | ||
} | ||
} | ||
|
||
err := json.Unmarshal(updateService.RawData, &t) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
us.sslCert = t.Oem.Supermicro.SSLCert.String() | ||
us.ipmiConfig = t.Oem.Supermicro.IPMIConfig.String() | ||
|
||
us.installTarget = t.Actions.Oem.Install.Target | ||
|
||
return &us, nil | ||
} | ||
|
||
// GetUpdateService will get a UpdateService instance from the service. | ||
func GetUpdateService(c common.Client, uri string) (*UpdateService, error) { | ||
return common.GetObject[UpdateService](c, uri) | ||
} | ||
|
||
// ActivateLicense performs the ActivateLicense action of the UpdateService. | ||
func (us *UpdateService) Install(targets, installOptions []string) error { | ||
if us.installTarget == "" { | ||
return errors.New("Install is not supported by this system") | ||
} | ||
|
||
return us.Post(us.installTarget, map[string]any{ | ||
"Targets": targets, | ||
"InstallOptions": installOptions, | ||
}) | ||
} | ||
|
||
// SSLCert will get the SSLCert information from the service. | ||
func (us *UpdateService) SSLCert() (*SSLCert, error) { | ||
return GetSSLCert(us.GetClient(), us.sslCert) | ||
} | ||
|
||
// IPMIConfig will get the IPMIConfig information from the service. | ||
func (us *UpdateService) IPMIConfig() (*IPMIConfig, error) { | ||
return GetIPMIConfig(us.GetClient(), us.ipmiConfig) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
// | ||
|
||
package smc | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/stmcginnis/gofish/redfish" | ||
) | ||
|
||
var updateServiceBody = `{ | ||
"@odata.type": "#UpdateService.v1_8_4.UpdateService", | ||
"@odata.id": "/redfish/v1/UpdateService", | ||
"Id": "UpdateService", | ||
"Name": "Update Service", | ||
"Description": "Service for updating firmware and includes inventory of firmware", | ||
"Status": { | ||
"State": "Enabled", | ||
"Health": "OK", | ||
"HealthRollup": "OK" | ||
}, | ||
"ServiceEnabled": true, | ||
"MultipartHttpPushUri": "/redfish/v1/UpdateService/upload", | ||
"FirmwareInventory": { | ||
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory" | ||
}, | ||
"Actions": { | ||
"Oem": { | ||
"#SmcUpdateService.Install": { | ||
"target": "/redfish/v1/UpdateService/Actions/Oem/SmcUpdateService.Install", | ||
"@Redfish.ActionInfo": "/redfish/v1/UpdateService/Oem/Supermicro/InstallActionInfo" | ||
} | ||
}, | ||
"#UpdateService.SimpleUpdate": { | ||
"target": "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate", | ||
"@Redfish.ActionInfo": "/redfish/v1/UpdateService/SimpleUpdateActionInfo" | ||
}, | ||
"#UpdateService.StartUpdate": { | ||
"target": "/redfish/v1/UpdateService/Actions/UpdateService.StartUpdate" | ||
} | ||
}, | ||
"Oem": { | ||
"Supermicro": { | ||
"@odata.type": "#SmcUpdateServiceExtensions.v1_0_0.UpdateService", | ||
"SSLCert": { | ||
"@odata.id": "/redfish/v1/UpdateService/Oem/Supermicro/SSLCert" | ||
}, | ||
"IPMIConfig": { | ||
"@odata.id": "/redfish/v1/UpdateService/Oem/Supermicro/IPMIConfig" | ||
} | ||
} | ||
}, | ||
"@odata.etag": "\"e9b94401dae9992fef2e71ef30cbcfdc\"" | ||
}` | ||
|
||
// TestSmcUpdateServiceOem tests the parsing of the UpdateService oem field | ||
func TestSmcUpdateServiceOem(t *testing.T) { | ||
us := &redfish.UpdateService{} | ||
if err := json.Unmarshal([]byte(updateServiceBody), us); err != nil { | ||
t.Fatalf("error decoding json: %v", err) | ||
} | ||
|
||
updateService, err := FromUpdateService(us) | ||
if err != nil { | ||
t.Fatalf("error getting oem object: %v", err) | ||
} | ||
|
||
if updateService.ID != "UpdateService" { | ||
t.Errorf("unexpected ID: %s", updateService.ID) | ||
} | ||
|
||
if updateService.installTarget != "/redfish/v1/UpdateService/Actions/Oem/SmcUpdateService.Install" { | ||
t.Errorf("unexpected install target: %s", updateService.installTarget) | ||
} | ||
|
||
if updateService.sslCert != "/redfish/v1/UpdateService/Oem/Supermicro/SSLCert" { | ||
t.Errorf("unexpected ssl cert link: %s", updateService.installTarget) | ||
} | ||
|
||
if updateService.ipmiConfig != "/redfish/v1/UpdateService/Oem/Supermicro/IPMIConfig" { | ||
t.Errorf("unexpected ipmi config link: %s", updateService.installTarget) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters