From ca1021194ee8c33bc2893f85e4c6501310aab3db Mon Sep 17 00:00:00 2001 From: Sean McGinnis Date: Tue, 15 Oct 2024 17:23:29 -0500 Subject: [PATCH] Add Supermicro OEM Manager object (#369) Signed-off-by: Sean McGinnis --- oem/smc/manager.go | 115 +++++++++++++++++++++++++++++++++++ oem/smc/manager_test.go | 130 ++++++++++++++++++++++++++++++++++++++++ redfish/manager.go | 8 +-- 3 files changed, 249 insertions(+), 4 deletions(-) create mode 100644 oem/smc/manager.go create mode 100644 oem/smc/manager_test.go diff --git a/oem/smc/manager.go b/oem/smc/manager.go new file mode 100644 index 00000000..9896c015 --- /dev/null +++ b/oem/smc/manager.go @@ -0,0 +1,115 @@ +// +// SPDX-License-Identifier: BSD-3-Clause +// + +package smc + +import ( + "encoding/json" + "errors" + + "github.com/stmcginnis/gofish/common" + "github.com/stmcginnis/gofish/redfish" +) + +type ManagerConfigResetOption string + +const ( + PreserveUserManagerConfigResetOption ManagerConfigResetOption = "PreserveUser" + ClearConfigManagerConfigResetOption ManagerConfigResetOption = "ClearConfig" + ResetToAdminManagerConfigResetOption ManagerConfigResetOption = "ResetToADMIN" +) + +// Manager is a Supermicro OEM instance of a Manager. +type Manager struct { + redfish.Manager + + radius string + mouseMode string + ntp string + ipAccessControl string + smcRAKP string + syslog string + sysLockdown string + memoryPFA string + memoryHealthComp string + snooping string + fanMode string + iKVM string + kcsInterface string + lldp string + licenseManager string + + managerConfigResetTarget string +} + +// FromManager converts a standard Manager object to the OEM implementation. +func FromManager(manager *redfish.Manager) (*Manager, error) { + m := Manager{ + Manager: *manager, + } + + var t struct { + Oem struct { + Supermicro struct { + RADIUS common.Link `json:"RADIUS"` + MouseMode common.Link `json:"MouseMode"` + NTP common.Link `json:"NTP"` + IPAccessControl common.Link `json:"IPAccessControl"` + SMCRAKP common.Link `json:"SMCRAKP"` + Syslog common.Link `json:"Syslog"` + SysLockdown common.Link `json:"SysLockdown"` + MemoryPFA common.Link `json:"MemoryPFA"` + MemoryHealthComp common.Link `json:"MemoryHealthComp"` + Snooping common.Link `json:"Snooping"` + FanMode common.Link `json:"FanMode"` + IKVM common.Link `json:"IKVM"` + KCSInterface common.Link `json:"KCSInterface"` + LLDP common.Link `json:"LLDP"` + LicenseManager common.Link `json:"LicenseManager"` + } `json:"Supermicro"` + } `json:"Oem"` + Actions struct { + Oem struct { + ManagerConfigReset common.ActionTarget `json:"#SmcManagerConfig.Reset"` + } `json:"Oem"` + } `json:"Actions"` + } + + err := json.Unmarshal(manager.RawData, &t) + if err != nil { + return nil, err + } + + m.radius = t.Oem.Supermicro.RADIUS.String() + m.mouseMode = t.Oem.Supermicro.MouseMode.String() + m.ntp = t.Oem.Supermicro.NTP.String() + m.ipAccessControl = t.Oem.Supermicro.IPAccessControl.String() + m.smcRAKP = t.Oem.Supermicro.SMCRAKP.String() + m.syslog = t.Oem.Supermicro.Syslog.String() + m.sysLockdown = t.Oem.Supermicro.SysLockdown.String() + m.memoryPFA = t.Oem.Supermicro.MemoryPFA.String() + m.memoryHealthComp = t.Oem.Supermicro.MemoryHealthComp.String() + m.snooping = t.Oem.Supermicro.Snooping.String() + m.fanMode = t.Oem.Supermicro.FanMode.String() + m.iKVM = t.Oem.Supermicro.IKVM.String() + m.kcsInterface = t.Oem.Supermicro.KCSInterface.String() + m.lldp = t.Oem.Supermicro.LLDP.String() + m.licenseManager = t.Oem.Supermicro.LicenseManager.String() + + m.managerConfigResetTarget = t.Actions.Oem.ManagerConfigReset.Target + + m.SetClient(manager.GetClient()) + return &m, nil +} + +// TODO: Add linked objects + +// ManagerConfigReset resets the BMC to factory defaults. +func (m *Manager) ManagerConfigReset(option ManagerConfigResetOption) error { + if m.managerConfigResetTarget == "" { + return errors.New("manager config reset not supported by this system") + } + + return m.Post(m.managerConfigResetTarget, map[string]interface{}{"Option": option}) +} diff --git a/oem/smc/manager_test.go b/oem/smc/manager_test.go new file mode 100644 index 00000000..06884d48 --- /dev/null +++ b/oem/smc/manager_test.go @@ -0,0 +1,130 @@ +// +// SPDX-License-Identifier: BSD-3-Clause +// + +package smc + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/stmcginnis/gofish/redfish" +) + +var managerBody = `{ + "@odata.type": "#Manager.v1_11_0.Manager", + "@odata.id": "/redfish/v1/Managers/1", + "Id": "1", + "Name": "Manager", + "Description": "BMC", + "ManagerType": "BMC", + "UUID": "00000000-0000-0000-0000-7CC25586E000", + "Model": "ASPEED", + "FirmwareVersion": "01.01.06", + "DateTime": "2024-10-15T21:35:01Z", + "DateTimeLocalOffset": "+00:00", + "Actions": { + "Oem": { + "#SmcManagerConfig.Reset": { + "target": "/redfish/v1/Managers/1/Actions/Oem/SmcManagerConfig.Reset", + "@Redfish.ActionInfo": "/redfish/v1/Managers/1/Oem/Supermicro/ResetActionInfo" + } + }, + "#Manager.ResetToDefaults": { + "target": "/redfish/v1/Managers/1/Actions/Manager.ResetToDefaults", + "@Redfish.ActionInfo": "/redfish/v1/Managers/1/ResetToDefaultsActionInfo" + }, + "#Manager.Reset": { + "target": "/redfish/v1/Managers/1/Actions/Manager.Reset", + "ResetType@Redfish.AllowableValues": [ + "GracefulRestart" + ] + } + }, + "Oem": { + "Supermicro": { + "@odata.type": "#SmcManagerExtensions.v1_0_0.Manager", + "RADIUS": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/RADIUS" + }, + "MouseMode": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/MouseMode" + }, + "NTP": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/NTP" + }, + "IPAccessControl": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/IPAccessControl" + }, + "SMCRAKP": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/SMCRAKP" + }, + "Syslog": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/Syslog" + }, + "SysLockdown": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/SysLockdown" + }, + "MemoryPFA": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/MemoryPFA" + }, + "MemoryHealthComp": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/MemoryHealthComp" + }, + "Snooping": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/Snooping" + }, + "FanMode": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/FanMode" + }, + "IKVM": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/IKVM" + }, + "KCSInterface": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/KCSInterface" + }, + "LLDP": { + "@odata.id": "/redfish/v1/Managers/1/Oem/Supermicro/LLDP" + }, + "LicenseManager": { + "@odata.id": "/redfish/v1/Managers/1/LicenseManager" + } + } + }, + "@odata.etag": "\"70889f859f8f0399a6f71ffb167c1dc1\"" +}` + +// TestManager tests the parsing of Manager objects. +func TestManager(t *testing.T) { + var m redfish.Manager + err := json.NewDecoder(strings.NewReader(managerBody)).Decode(&m) + if err != nil { + t.Errorf("Error decoding JSON: %s", err) + } + + result, err := FromManager(&m) + if err != nil { + t.Errorf("Error converting Redfish Manager to SMC Manager: %s", err) + } + + if result.ID != "1" { + t.Errorf("Received invalid ID: %s", result.ID) + } + + if result.Name != "Manager" { + t.Errorf("Received invalid name: %s", result.Name) + } + + if result.radius != "/redfish/v1/Managers/1/Oem/Supermicro/RADIUS" { + t.Errorf("Invalid RADIUS link: %s", result.radius) + } + + if result.mouseMode != "/redfish/v1/Managers/1/Oem/Supermicro/MouseMode" { + t.Errorf("Invalid MouseMode link: %s", result.mouseMode) + } + + if result.managerConfigResetTarget != "/redfish/v1/Managers/1/Actions/Oem/SmcManagerConfig.Reset" { + t.Errorf("Invalid ManagerConfigResetTarget link: %s", result.managerConfigResetTarget) + } +} diff --git a/redfish/manager.go b/redfish/manager.go index 13929601..7c38b77f 100644 --- a/redfish/manager.go +++ b/redfish/manager.go @@ -384,8 +384,8 @@ type Manager struct { // SupportedResetTypes, if provided, is the reset types this system supports. SupportedResetTypes []ResetType resetToDefaultsTarget string - // rawData holds the original serialized JSON so we can compare updates. - rawData []byte + // RawData holds the original serialized JSON so we can compare updates. + RawData []byte } // UnmarshalJSON unmarshals a Manager object from the raw JSON. @@ -485,7 +485,7 @@ func (manager *Manager) UnmarshalJSON(b []byte) error { manager.actionInfo = t.Actions.Reset.ActionInfo // This is a read/write object, so we need to save the raw object data for later - manager.rawData = b + manager.RawData = b return nil } @@ -495,7 +495,7 @@ func (manager *Manager) Update() error { // Get a representation of the object's original state so we can find what // to update. original := new(Manager) - err := original.UnmarshalJSON(manager.rawData) + err := original.UnmarshalJSON(manager.RawData) if err != nil { return err }