From 4f0b2dc1e41d60d686ee6a04918afa40a12b082e Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 1 Mar 2023 19:04:24 +0300 Subject: [PATCH] Fix system settings (#239) * feat: add settings to system * fix: test for bios * fix: linter for settings --- redfish/bios.go | 45 ++------------------------ redfish/bios_test.go | 4 +-- redfish/computersystem.go | 67 +++++++++++++++++++++++++++++++++++++++ redfish/settings.go | 49 ++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 44 deletions(-) create mode 100644 redfish/settings.go diff --git a/redfish/bios.go b/redfish/bios.go index 3111af55..772c83db 100644 --- a/redfish/bios.go +++ b/redfish/bios.go @@ -12,45 +12,6 @@ import ( "github.com/stmcginnis/gofish/common" ) -// BiosAttributes handles the Bios attribute values that may be any of several -// types and adds some basic helper methods to make accessing values easier. -type BiosAttributes map[string]interface{} - -// String gets the string representation of the attribute value. -func (ba BiosAttributes) String(name string) string { - if val, ok := ba[name]; ok { - return fmt.Sprintf("%v", val) - } - - return "" -} - -// Float64 gets the value as a float64 or 0 if that is not possible. -func (ba BiosAttributes) Float64(name string) float64 { - if val, ok := ba[name]; ok { - return val.(float64) - } - - return 0 -} - -// Int gets the value as an integer or 0 if that is not possible. -func (ba BiosAttributes) Int(name string) int { - // Integer values may be interpeted as float64, so get it as that first, - // then coerce down to int. - floatVal := int(ba.Float64(name)) - return (floatVal) -} - -// Bool gets the value as a boolean or returns false. -func (ba BiosAttributes) Bool(name string) bool { - maybeBool := ba.String(name) - maybeBool = strings.ToLower(maybeBool) - return (maybeBool == "true" || - maybeBool == "1" || - maybeBool == "enabled") -} - // Bios is used to represent BIOS attributes. type Bios struct { common.Entity @@ -69,7 +30,7 @@ type Bios struct { // that Attribute Registry by their attribute name. Attributes in this // Attribute Registry with the AttributeType of Enumeration shall use valid // ValueName values in this object, as listed in that Attribute Registry. - Attributes BiosAttributes + Attributes SettingsAttributes // Attributes are additional properties in this object, and can be looked up // in the Attribute Registry by their AttributeName. // Attributes string @@ -236,7 +197,7 @@ func (bios *Bios) AllowedAttributeUpdateApplyTimes() []common.ApplyTime { } // UpdateBiosAttributesApplyAt is used to update attribute values and set apply time together -func (bios *Bios) UpdateBiosAttributesApplyAt(attrs BiosAttributes, applyTime common.ApplyTime) error { +func (bios *Bios) UpdateBiosAttributesApplyAt(attrs SettingsAttributes, applyTime common.ApplyTime) error { //nolint:dupl payload := make(map[string]interface{}) // Get a representation of the object's original state so we can find what @@ -284,7 +245,7 @@ func (bios *Bios) UpdateBiosAttributesApplyAt(attrs BiosAttributes, applyTime co } // UpdateBiosAttributes is used to update attribute values. -func (bios *Bios) UpdateBiosAttributes(attrs BiosAttributes) error { +func (bios *Bios) UpdateBiosAttributes(attrs SettingsAttributes) error { return bios.UpdateBiosAttributesApplyAt(attrs, "") } diff --git a/redfish/bios_test.go b/redfish/bios_test.go index bc03da27..f1d2d93d 100644 --- a/redfish/bios_test.go +++ b/redfish/bios_test.go @@ -197,7 +197,7 @@ func TestUpdateBiosAttributes(t *testing.T) { testClient := &common.TestClient{} result.SetClient(testClient) - update := BiosAttributes{"AssetTag": "test"} + update := SettingsAttributes{"AssetTag": "test"} err = result.UpdateBiosAttributes(update) if err != nil { @@ -231,7 +231,7 @@ func TestUpdateBiosAttributesApplyAt(t *testing.T) { testClient := &common.TestClient{} result.SetClient(testClient) - update := BiosAttributes{"AssetTag": "test"} + update := SettingsAttributes{"AssetTag": "test"} err = result.UpdateBiosAttributesApplyAt(update, common.AtMaintenanceWindowStartApplyTime) if err != nil { diff --git a/redfish/computersystem.go b/redfish/computersystem.go index aee7af97..9bf988e7 100644 --- a/redfish/computersystem.go +++ b/redfish/computersystem.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "github.com/stmcginnis/gofish/common" ) @@ -541,6 +542,10 @@ type ComputerSystem struct { SupportedResetTypes []ResetType // setDefaultBootOrderTarget is the URL to send SetDefaultBootOrder actions to. setDefaultBootOrderTarget string + settingsTarget string + // settingsApplyTimes is a set of allowed settings update apply times. If none + // are specified, then the system does not provide that information. + settingsApplyTimes []common.ApplyTime // ManagedBy An array of references to the Managers responsible for this system. // This is temporary until a proper method can be implemented to actually // retrieve those objects directly. @@ -578,6 +583,7 @@ func (computersystem *ComputerSystem) UnmarshalJSON(b []byte) error { PCIeDevices common.Links PCIeFunctions common.Links Links CSLinks + Settings common.Settings `json:"@Redfish.Settings"` } err := json.Unmarshal(b, &t) @@ -605,6 +611,14 @@ func (computersystem *ComputerSystem) UnmarshalJSON(b []byte) error { computersystem.SupportedResetTypes = t.Actions.ComputerSystemReset.AllowedResetTypes computersystem.setDefaultBootOrderTarget = t.Actions.SetDefaultBootOrder.Target computersystem.ManagedBy = t.Links.ManagedBy.ToStrings() + computersystem.settingsApplyTimes = t.Settings.SupportedApplyTimes + + // Some implementations use a @Redfish.Settings object to direct settings updates to a + // different URL than the object being updated. Others don't, so handle both. + computersystem.settingsTarget = t.Settings.SettingsObject.String() + if computersystem.settingsTarget == "" { + computersystem.settingsTarget = computersystem.ODataID + } // This is a read/write object, so we need to save the raw object data for later computersystem.rawData = b @@ -860,6 +874,59 @@ func (computersystem *ComputerSystem) Reset(resetType ResetType) error { return computersystem.Post(computersystem.resetTarget, t) } +// UpdateBootAttributesApplyAt is used to update attribute values and set apply time together +func (computersystem *ComputerSystem) UpdateBootAttributesApplyAt(attrs SettingsAttributes, applyTime common.ApplyTime) error { //nolint:dupl + payload := make(map[string]interface{}) + + // Get a representation of the object's original state so we can find what + // to update. + original := new(Bios) + err := original.UnmarshalJSON(computersystem.rawData) + if err != nil { + return err + } + + for key := range attrs { + if strings.HasPrefix(key, "BootTypeOrder") || + original.Attributes[key] != attrs[key] { + payload[key] = attrs[key] + } + } + + resp, err := computersystem.Client.Get(computersystem.settingsTarget) + if err != nil { + return err + } + defer resp.Body.Close() + + // If there are any allowed updates, try to send updates to the system and + // return the result. + if len(payload) > 0 { + data := map[string]interface{}{"Boot": payload} + if applyTime != "" { + data["@Redfish.SettingsApplyTime"] = map[string]string{"ApplyTime": string(applyTime)} + } + + var header = make(map[string]string) + if resp.Header["Etag"] != nil { + header["If-Match"] = resp.Header["Etag"][0] + } + + resp, err = computersystem.Client.PatchWithHeaders(computersystem.settingsTarget, data, header) + if err != nil { + return err + } + defer resp.Body.Close() + } + + return nil +} + +// UpdateBootAttributes is used to update attribute values. +func (computersystem *ComputerSystem) UpdateBootAttributes(attrs SettingsAttributes) error { + return computersystem.UpdateBootAttributesApplyAt(attrs, "") +} + // SetDefaultBootOrder shall set the BootOrder array to the default settings. func (computersystem *ComputerSystem) SetDefaultBootOrder() error { // This action wasn't added until 1.5.0, make sure this is supported. diff --git a/redfish/settings.go b/redfish/settings.go new file mode 100644 index 00000000..fafe7f59 --- /dev/null +++ b/redfish/settings.go @@ -0,0 +1,49 @@ +// +// SPDX-License-Identifier: BSD-3-Clause +// + +package redfish + +import ( + "fmt" + "strings" +) + +// SettingsAttributes handles the settings attribute values that may be any of several +// types and adds some basic helper methods to make accessing values easier. +type SettingsAttributes map[string]interface{} + +// String gets the string representation of the attribute value. +func (ba SettingsAttributes) String(name string) string { + if val, ok := ba[name]; ok { + return fmt.Sprintf("%v", val) + } + + return "" +} + +// Float64 gets the value as a float64 or 0 if that is not possible. +func (ba SettingsAttributes) Float64(name string) float64 { + if val, ok := ba[name]; ok { + return val.(float64) + } + + return 0 +} + +// Int gets the value as an integer or 0 if that is not possible. +func (ba SettingsAttributes) Int(name string) int { + // Integer values may be interpeted as float64, so get it as that first, + // then coerce down to int. + floatVal := int(ba.Float64(name)) + return (floatVal) +} + +// Bool gets the value as a boolean or returns false. +func (ba SettingsAttributes) Bool(name string) bool { + maybeBool := ba.String(name) + maybeBool = strings.ToLower(maybeBool) + return (maybeBool == "true" || + maybeBool == "1" || + maybeBool == "enabled") +}