From d87156335ac2ffb5d3b949ab8290d8585825f3bf Mon Sep 17 00:00:00 2001 From: "James W. Brinkerhoff" Date: Tue, 9 Apr 2024 16:33:43 -0400 Subject: [PATCH] Add SetBiosConfiguration(ctx, biosConfig) as well as ResetBiosConfiguration(ctx) to redfishwrapper, redfish, etc. --- bmc/bios.go | 126 ++++++++++++++++++++++++++++++++ client.go | 24 +++++- internal/redfishwrapper/bios.go | 38 ++++++++++ providers/dell/idrac.go | 18 +++++ providers/providers.go | 9 +++ providers/redfish/redfish.go | 5 ++ 6 files changed, 219 insertions(+), 1 deletion(-) diff --git a/bmc/bios.go b/bmc/bios.go index e1230fca..cdc0db00 100644 --- a/bmc/bios.go +++ b/bmc/bios.go @@ -18,6 +18,24 @@ type biosConfigurationGetterProvider struct { BiosConfigurationGetter } +type BiosConfigurationSetter interface { + SetBiosConfiguration(ctx context.Context, biosConfig map[string]string) (err error) +} + +type biosConfigurationSetterProvider struct { + name string + BiosConfigurationSetter +} + +type BiosConfigurationResetter interface { + ResetBiosConfiguration(ctx context.Context) (err error) +} + +type biosConfigurationResetterProvider struct { + name string + BiosConfigurationResetter +} + func biosConfiguration(ctx context.Context, generic []biosConfigurationGetterProvider) (biosConfig map[string]string, metadata Metadata, err error) { var metadataLocal Metadata Loop: @@ -46,6 +64,62 @@ Loop: return biosConfig, metadataLocal, multierror.Append(err, errors.New("failure to get bios configuration")) } +func setBiosConfiguration(ctx context.Context, generic []biosConfigurationSetterProvider, biosConfig map[string]string) (metadata Metadata, err error) { + var metadataLocal Metadata +Loop: + for _, elem := range generic { + if elem.BiosConfigurationSetter == nil { + continue + } + select { + case <-ctx.Done(): + err = multierror.Append(err, ctx.Err()) + break Loop + default: + metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name) + vErr := elem.SetBiosConfiguration(ctx, biosConfig) + if vErr != nil { + err = multierror.Append(err, errors.WithMessagef(vErr, "provider: %v", elem.name)) + err = multierror.Append(err, vErr) + continue + + } + metadataLocal.SuccessfulProvider = elem.name + return metadataLocal, nil + } + } + + return metadataLocal, multierror.Append(err, errors.New("failure to set bios configuration")) +} + +func resetBiosConfiguration(ctx context.Context, generic []biosConfigurationResetterProvider) (metadata Metadata, err error) { + var metadataLocal Metadata +Loop: + for _, elem := range generic { + if elem.BiosConfigurationResetter == nil { + continue + } + select { + case <-ctx.Done(): + err = multierror.Append(err, ctx.Err()) + break Loop + default: + metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name) + vErr := elem.ResetBiosConfiguration(ctx) + if vErr != nil { + err = multierror.Append(err, errors.WithMessagef(vErr, "provider: %v", elem.name)) + err = multierror.Append(err, vErr) + continue + + } + metadataLocal.SuccessfulProvider = elem.name + return metadataLocal, nil + } + } + + return metadataLocal, multierror.Append(err, errors.New("failure to reset bios configuration")) +} + func GetBiosConfigurationInterfaces(ctx context.Context, generic []interface{}) (biosConfig map[string]string, metadata Metadata, err error) { implementations := make([]biosConfigurationGetterProvider, 0) for _, elem := range generic { @@ -71,3 +145,55 @@ func GetBiosConfigurationInterfaces(ctx context.Context, generic []interface{}) return biosConfiguration(ctx, implementations) } + +func SetBiosConfigurationInterfaces(ctx context.Context, generic []interface{}, biosConfig map[string]string) (metadata Metadata, err error) { + implementations := make([]biosConfigurationSetterProvider, 0) + for _, elem := range generic { + temp := biosConfigurationSetterProvider{name: getProviderName(elem)} + switch p := elem.(type) { + case BiosConfigurationSetter: + temp.BiosConfigurationSetter = p + implementations = append(implementations, temp) + default: + e := fmt.Sprintf("not a BiosConfigurationSetter implementation: %T", p) + err = multierror.Append(err, errors.New(e)) + } + } + if len(implementations) == 0 { + return metadata, multierror.Append( + err, + errors.Wrap( + bmclibErrs.ErrProviderImplementation, + ("no BiosConfigurationSetter implementations found"), + ), + ) + } + + return setBiosConfiguration(ctx, implementations, biosConfig) +} + +func ResetBiosConfigurationInterfaces(ctx context.Context, generic []interface{}) (metadata Metadata, err error) { + implementations := make([]biosConfigurationResetterProvider, 0) + for _, elem := range generic { + temp := biosConfigurationResetterProvider{name: getProviderName(elem)} + switch p := elem.(type) { + case BiosConfigurationResetter: + temp.BiosConfigurationResetter = p + implementations = append(implementations, temp) + default: + e := fmt.Sprintf("not a BiosConfigurationResetter implementation: %T", p) + err = multierror.Append(err, errors.New(e)) + } + } + if len(implementations) == 0 { + return metadata, multierror.Append( + err, + errors.Wrap( + bmclibErrs.ErrProviderImplementation, + ("no BiosConfigurationResetter implementations found"), + ), + ) + } + + return resetBiosConfiguration(ctx, implementations) +} diff --git a/client.go b/client.go index a4444b3b..c4fcf33d 100644 --- a/client.go +++ b/client.go @@ -27,8 +27,8 @@ import ( "github.com/bmc-toolbox/common" "github.com/go-logr/logr" "github.com/jacobweinstock/registrar" + "go.opencensus.io/trace" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" oteltrace "go.opentelemetry.io/otel/trace" tracenoop "go.opentelemetry.io/otel/trace/noop" ) @@ -556,6 +556,28 @@ func (c *Client) GetBiosConfiguration(ctx context.Context) (biosConfig map[strin return biosConfig, err } +func (c *Client) SetBiosConfiguration(ctx context.Context, biosConfig map[string]string) (err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "SetBiosConfiguration") + defer span.End() + + metadata, err := bmc.SetBiosConfigurationInterfaces(ctx, c.registry().GetDriverInterfaces(), biosConfig) + c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + + return err +} + +func (c *Client) ResetBiosConfiguration(ctx context.Context) (err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "ResetBiosConfiguration") + defer span.End() + + metadata, err := bmc.ResetBiosConfigurationInterfaces(ctx, c.registry().GetDriverInterfaces()) + c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + + return err +} + // FirmwareInstall pass through library function to upload firmware and install firmware func (c *Client) FirmwareInstall(ctx context.Context, component string, operationApplyTime string, forceInstall bool, reader io.Reader) (taskID string, err error) { ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "FirmwareInstall") diff --git a/internal/redfishwrapper/bios.go b/internal/redfishwrapper/bios.go index a08be5a9..f5b1dcf8 100644 --- a/internal/redfishwrapper/bios.go +++ b/internal/redfishwrapper/bios.go @@ -4,6 +4,7 @@ import ( "context" bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors" + "github.com/stmcginnis/gofish/redfish" ) func (c *Client) GetBiosConfiguration(ctx context.Context) (biosConfig map[string]string, err error) { @@ -34,3 +35,40 @@ func (c *Client) GetBiosConfiguration(ctx context.Context) (biosConfig map[strin return biosConfig, nil } + +func (c *Client) SetBiosConfiguration(ctx context.Context, biosConfig map[string]string) (err error) { + systems, err := c.Systems() + if err != nil { + return err + } + + settingsAttributes := make(redfish.SettingsAttributes) + + for attr, value := range biosConfig { + settingsAttributes[attr] = value + } + + for _, sys := range systems { + if !c.compatibleOdataID(sys.ODataID, knownSystemsOdataIDs) { + continue + } + + bios, err := sys.Bios() + if err != nil { + return err + } + + err = bios.UpdateBiosAttributes(settingsAttributes) + + if err != nil { + return err + } + } + + return nil +} + +func (c *Client) ResetBiosConfiguration(ctx context.Context) (err error) { + // TODO(jwb) do the reset :P + return nil +} diff --git a/providers/dell/idrac.go b/providers/dell/idrac.go index 73c457ef..7ea6e724 100644 --- a/providers/dell/idrac.go +++ b/providers/dell/idrac.go @@ -43,6 +43,9 @@ var ( providers.FeatureFirmwareTaskStatus, providers.FeatureInventoryRead, providers.FeatureBmcReset, + providers.FeatureGetBiosConfiguration, + providers.FeatureSetBiosConfiguration, + providers.FeatureResetBiosConfiguration, } errManufacturerUnknown = errors.New("error identifying device manufacturer") @@ -219,6 +222,21 @@ func (c *Conn) BmcReset(ctx context.Context, resetType string) (ok bool, err err return c.redfishwrapper.BMCReset(ctx, resetType) } +// GetBiosConfiguration returns the BIOS configuration settings via the BMC +func (c *Conn) GetBiosConfiguration(ctx context.Context) (biosConfig map[string]string, err error) { + return c.redfishwrapper.GetBiosConfiguration(ctx) +} + +// SetBiosConfiguration sets the BIOS configuration settings via the BMC +func (c *Conn) SetBiosConfiguration(ctx context.Context, biosConfig map[string]string) (err error) { + return c.redfishwrapper.SetBiosConfiguration(ctx, biosConfig) +} + +// ResetBiosConfiguration resets the BIOS configuration settings back to 'factory defaults' via the BMC +func (c *Conn) ResetBiosConfiguration(ctx context.Context) (err error) { + return c.redfishwrapper.ResetBiosConfiguration(ctx) +} + // SendNMI tells the BMC to issue an NMI to the device func (c *Conn) SendNMI(ctx context.Context) error { return c.redfishwrapper.SendNMI(ctx) diff --git a/providers/providers.go b/providers/providers.go index 96eb97b6..0699e7cb 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -63,4 +63,13 @@ const ( // FeatureDeactivateSOL means an implementation that can deactivate active SOL sessions FeatureDeactivateSOL registrar.Feature = "deactivatesol" + + // FeatureResetBiosConfiguration means an implementation that can reset bios configuration back to 'factory' defaults + FeatureResetBiosConfiguration registrar.Feature = "resetbiosconfig" + + // FeatureSetBiosConfiguration means an implementation that can set bios configuration from an input k/v map + FeatureSetBiosConfiguration registrar.Feature = "setbiosconfig" + + // FeatureGetBiosConfiguration means an implementation that can get bios configuration in a simple k/v map + FeatureGetBiosConfiguration registrar.Feature = "getbiosconfig" ) diff --git a/providers/redfish/redfish.go b/providers/redfish/redfish.go index 9bfacc5a..1541feb2 100644 --- a/providers/redfish/redfish.go +++ b/providers/redfish/redfish.go @@ -218,6 +218,11 @@ func (c *Conn) GetBiosConfiguration(ctx context.Context) (biosConfig map[string] return c.redfishwrapper.GetBiosConfiguration(ctx) } +// SetBiosConfiguration set bios configuration +func (c *Conn) SetBiosConfiguration(ctx context.Context, biosConfig map[string]string) (err error) { + return c.redfishwrapper.SetBiosConfiguration(ctx, biosConfig) +} + // SendNMI tells the BMC to issue an NMI to the device func (c *Conn) SendNMI(ctx context.Context) error { return c.redfishwrapper.SendNMI(ctx)