diff --git a/bmc/bios.go b/bmc/bios.go index e1230fca..13c9d3a8 100644 --- a/bmc/bios.go +++ b/bmc/bios.go @@ -18,8 +18,26 @@ 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 + metadata = newMetadata() Loop: for _, elem := range generic { if elem.BiosConfigurationGetter == nil { @@ -30,7 +48,7 @@ Loop: err = multierror.Append(err, ctx.Err()) break Loop default: - metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name) + metadata.ProvidersAttempted = append(metadata.ProvidersAttempted, elem.name) biosConfig, vErr := elem.GetBiosConfiguration(ctx) if vErr != nil { err = multierror.Append(err, errors.WithMessagef(vErr, "provider: %v", elem.name)) @@ -38,12 +56,68 @@ Loop: continue } - metadataLocal.SuccessfulProvider = elem.name - return biosConfig, metadataLocal, nil + metadata.SuccessfulProvider = elem.name + return biosConfig, metadata, nil + } + } + + return biosConfig, metadata, 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) { + metadata = newMetadata() +Loop: + for _, elem := range generic { + if elem.BiosConfigurationSetter == nil { + continue + } + select { + case <-ctx.Done(): + err = multierror.Append(err, ctx.Err()) + break Loop + default: + metadata.ProvidersAttempted = append(metadata.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 + + } + metadata.SuccessfulProvider = elem.name + return metadata, nil + } + } + + return metadata, multierror.Append(err, errors.New("failure to set bios configuration")) +} + +func resetBiosConfiguration(ctx context.Context, generic []biosConfigurationResetterProvider) (metadata Metadata, err error) { + metadata = newMetadata() +Loop: + for _, elem := range generic { + if elem.BiosConfigurationResetter == nil { + continue + } + select { + case <-ctx.Done(): + err = multierror.Append(err, ctx.Err()) + break Loop + default: + metadata.ProvidersAttempted = append(metadata.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 + + } + metadata.SuccessfulProvider = elem.name + return metadata, nil } } - return biosConfig, metadataLocal, multierror.Append(err, errors.New("failure to get bios configuration")) + return metadata, 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) { @@ -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..e9bd76ca 100644 --- a/client.go +++ b/client.go @@ -28,7 +28,6 @@ import ( "github.com/go-logr/logr" "github.com/jacobweinstock/registrar" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" oteltrace "go.opentelemetry.io/otel/trace" tracenoop "go.opentelemetry.io/otel/trace/noop" ) @@ -319,7 +318,7 @@ func (c *Client) registry() *registrar.Registry { return c.Registry } -func (c *Client) RegisterSpanAttributes(m bmc.Metadata, span trace.Span) { +func (c *Client) RegisterSpanAttributes(m bmc.Metadata, span oteltrace.Span) { span.SetAttributes(attribute.String("host", c.Auth.Host)) span.SetAttributes(attribute.String("successful-provider", m.SuccessfulProvider)) @@ -556,6 +555,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/examples/bios/main.go b/examples/bios/main.go index c8a515ae..16def4aa 100644 --- a/examples/bios/main.go +++ b/examples/bios/main.go @@ -2,30 +2,45 @@ package main import ( "context" + "encoding/json" + "flag" "fmt" + "io" "os" + "strings" + "time" bmclib "github.com/bmc-toolbox/bmclib/v2" + "github.com/bmc-toolbox/bmclib/v2/providers" logrusr "github.com/bombsimon/logrusr/v2" + "github.com/sirupsen/logrus" ) func main() { - // setup logger + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) + defer cancel() + + // Command line option flag parsing + user := flag.String("user", "", "Username to login with") + pass := flag.String("password", "", "Username to login with") + host := flag.String("host", "", "BMC hostname to connect to") + mode := flag.String("mode", "get", "Mode [get,set,reset]") + dfile := flag.String("file", "", "Read data from file") + + flag.Parse() + + // Logger configuration l := logrus.New() - l.Level = logrus.TraceLevel + l.Level = logrus.DebugLevel logger := logrusr.New(l) + // bmclib client abstraction clientOpts := []bmclib.Option{bmclib.WithLogger(logger)} - host := os.Getenv("BMC_HOST") - bmcPass := os.Getenv("BMC_PASSWORD") - bmcUser := os.Getenv("BMC_USERNAME") - // init client - client := bmclib.NewClient(host, bmcUser, bmcPass, clientOpts...) + client := bmclib.NewClient(*host, *user, *pass, clientOpts...) + client.Registry.Drivers = client.Registry.Supports(providers.FeatureGetBiosConfiguration, providers.FeatureSetBiosConfiguration, providers.FeatureResetBiosConfiguration) - ctx := context.TODO() - // open BMC session err := client.Open(ctx) if err != nil { l.Fatal(err, "bmc login failed") @@ -33,11 +48,51 @@ func main() { defer client.Close(ctx) - // retrieve bios configuration - biosConfig, err := client.GetBiosConfiguration(ctx) - if err != nil { - l.Error(err) - } + // Operating mode selection + switch strings.ToLower(*mode) { + case "get": + // retrieve bios configuration + biosConfig, err := client.GetBiosConfiguration(ctx) + if err != nil { + l.Fatal(err) + } + + fmt.Printf("biosConfig: %+v\n", biosConfig) + case "set": + exampleConfig := make(map[string]string) - fmt.Println(biosConfig) + if *dfile != "" { + jsonFile, err := os.Open(*dfile) + if err != nil { + l.Fatal(err) + } + + defer jsonFile.Close() + + jsonData, _ := io.ReadAll(jsonFile) + + err = json.Unmarshal(jsonData, &exampleConfig) + if err != nil { + l.Fatal(err) + } + } else { + exampleConfig["TpmSecurity"] = "Off" + } + + fmt.Println("Attempting to set BIOS configuration:") + fmt.Printf("exampleConfig: %+v\n", exampleConfig) + + err := client.SetBiosConfiguration(ctx, exampleConfig) + if err != nil { + l.Error(err) + } + case "reset": + err := client.ResetBiosConfiguration(ctx) + if err != nil { + l.Error(err) + } + default: + l.Fatal("Unknown mode: " + *mode) + + } } diff --git a/go.mod b/go.mod index 1112b4c5..ed7b5cf2 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,8 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/otel v1.25.0 - go.opentelemetry.io/otel/trace v1.25.0 + go.opentelemetry.io/otel v1.24.0 + go.opentelemetry.io/otel/trace v1.24.0 go.uber.org/goleak v1.2.1 golang.org/x/crypto v0.22.0 golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 diff --git a/go.sum b/go.sum index 27c7a6c8..d16a2664 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,6 @@ github.com/VictorLowther/simplexml v0.0.0-20180716164440-0bff93621230 h1:t95Grn2 github.com/VictorLowther/simplexml v0.0.0-20180716164440-0bff93621230/go.mod h1:t2EzW1qybnPDQ3LR/GgeF0GOzHUXT5IVMLP2gkW1cmc= github.com/VictorLowther/soap v0.0.0-20150314151524-8e36fca84b22 h1:a0MBqYm44o0NcthLKCljZHe1mxlN6oahCQHHThnSwB4= github.com/VictorLowther/soap v0.0.0-20150314151524-8e36fca84b22/go.mod h1:/B7V22rcz4860iDqstGvia/2+IYWXf3/JdQCVd/1D2A= -github.com/bmc-toolbox/common v0.0.0-20230717121556-5eb9915a8a5a h1:SjtoU9dE3bYfYnPXODCunMztjoDgnE3DVJCPLBqwz6Q= -github.com/bmc-toolbox/common v0.0.0-20230717121556-5eb9915a8a5a/go.mod h1:SY//n1PJjZfbFbmAsB6GvEKbc7UXz3d30s3kWxfJQ/c= github.com/bmc-toolbox/common v0.0.0-20231204194243-7bcbccab7116 h1:gqWn/cMjryKoUfITx2vRHrRHTvd9fQ+zKPwWsmMIrK4= github.com/bmc-toolbox/common v0.0.0-20231204194243-7bcbccab7116/go.mod h1:SY//n1PJjZfbFbmAsB6GvEKbc7UXz3d30s3kWxfJQ/c= github.com/bombsimon/logrusr/v2 v2.0.1 h1:1VgxVNQMCvjirZIYaT9JYn6sAVGVEcNtRE0y4mvaOAM= @@ -20,8 +18,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-logr/logr v1.0.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs= @@ -69,30 +65,18 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc= -go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs= -go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= -go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= -go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ= -go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= -go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= -go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -101,11 +85,10 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/internal/redfishwrapper/bios.go b/internal/redfishwrapper/bios.go index a08be5a9..b6c5f8d5 100644 --- a/internal/redfishwrapper/bios.go +++ b/internal/redfishwrapper/bios.go @@ -4,6 +4,8 @@ import ( "context" bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors" + "github.com/stmcginnis/gofish/common" + "github.com/stmcginnis/gofish/redfish" ) func (c *Client) GetBiosConfiguration(ctx context.Context) (biosConfig map[string]string, err error) { @@ -34,3 +36,62 @@ 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 + } + + // TODO(jwb) We should handle passing different apply times here + err = bios.UpdateBiosAttributesApplyAt(settingsAttributes, common.OnResetApplyTime) + + if err != nil { + return err + } + } + + return nil +} + +func (c *Client) ResetBiosConfiguration(ctx context.Context) (err error) { + systems, err := c.Systems() + if err != nil { + return err + } + + for _, sys := range systems { + if !c.compatibleOdataID(sys.ODataID, knownSystemsOdataIDs) { + continue + } + + bios, err := sys.Bios() + if err != nil { + return err + } + + err = bios.ResetBios() + + if err != nil { + return err + } + } + + return nil +} diff --git a/internal/redfishwrapper/firmware.go b/internal/redfishwrapper/firmware.go index 9fdf8c7d..08ac4976 100644 --- a/internal/redfishwrapper/firmware.go +++ b/internal/redfishwrapper/firmware.go @@ -47,7 +47,7 @@ func (c *Client) FirmwareUpload(ctx context.Context, updateFile *os.File, params return "", errors.Wrap(errUpdateParams, err.Error()) } - installMethod, installURI, err := c.firmwareInstallMethodURI(ctx) + installMethod, installURI, err := c.firmwareInstallMethodURI() if err != nil { return "", errors.Wrap(bmclibErrs.ErrFirmwareUpload, err.Error()) } @@ -69,14 +69,14 @@ func (c *Client) FirmwareUpload(ctx context.Context, updateFile *os.File, params switch installMethod { case multipartHttpUpload: var uploadErr error - resp, uploadErr = c.multipartHTTPUpload(ctx, installURI, updateFile, parameters) + resp, uploadErr = c.multipartHTTPUpload(installURI, updateFile, parameters) if uploadErr != nil { return "", errors.Wrap(bmclibErrs.ErrFirmwareUpload, uploadErr.Error()) } case unstructuredHttpPush: var uploadErr error - resp, uploadErr = c.unstructuredHttpUpload(ctx, installURI, updateFile, parameters) + resp, uploadErr = c.unstructuredHttpUpload(installURI, updateFile) if uploadErr != nil { return "", errors.Wrap(bmclibErrs.ErrFirmwareUpload, uploadErr.Error()) } @@ -212,7 +212,7 @@ type multipartPayload struct { updateFile *os.File } -func (c *Client) multipartHTTPUpload(ctx context.Context, url string, update *os.File, params []byte) (*http.Response, error) { +func (c *Client) multipartHTTPUpload(url string, update *os.File, params []byte) (*http.Response, error) { if url == "" { return nil, fmt.Errorf("unable to execute request, no target provided") } @@ -226,7 +226,7 @@ func (c *Client) multipartHTTPUpload(ctx context.Context, url string, update *os return c.runRequestWithMultipartPayload(url, payload) } -func (c *Client) unstructuredHttpUpload(ctx context.Context, url string, update io.Reader, params []byte) (*http.Response, error) { +func (c *Client) unstructuredHttpUpload(url string, update io.Reader) (*http.Response, error) { if url == "" { return nil, fmt.Errorf("unable to execute request, no target provided") } @@ -240,7 +240,7 @@ func (c *Client) unstructuredHttpUpload(ctx context.Context, url string, update } // firmwareUpdateMethodURI returns the updateMethod and URI -func (c *Client) firmwareInstallMethodURI(ctx context.Context) (method installMethod, updateURI string, err error) { +func (c *Client) firmwareInstallMethodURI() (method installMethod, updateURI string, err error) { updateService, err := c.UpdateService() if err != nil { return "", "", errors.Wrap(bmclibErrs.ErrRedfishUpdateService, err.Error()) diff --git a/internal/redfishwrapper/firmware_test.go b/internal/redfishwrapper/firmware_test.go index 0929d0a7..5d8c7033 100644 --- a/internal/redfishwrapper/firmware_test.go +++ b/internal/redfishwrapper/firmware_test.go @@ -197,7 +197,7 @@ func TestFirmwareInstallMethodURI(t *testing.T) { t.Fatal(err) } - gotMethod, gotURI, err := client.firmwareInstallMethodURI(ctx) + gotMethod, gotURI, err := client.firmwareInstallMethodURI() if tc.err != nil { assert.ErrorContains(t, err, tc.err.Error()) return diff --git a/internal/redfishwrapper/inventory.go b/internal/redfishwrapper/inventory.go index 05046eb2..7d4076aa 100644 --- a/internal/redfishwrapper/inventory.go +++ b/internal/redfishwrapper/inventory.go @@ -90,13 +90,13 @@ func (c *Client) Inventory(ctx context.Context, failOnError bool) (device *commo } // populate device System components attributes - err = c.systemAttributes(ctx, device, failOnError, softwareInventory) + err = c.systemAttributes(device, failOnError, softwareInventory) if err != nil && failOnError { return nil, err } // populate device BMC component attributes - err = c.bmcAttributes(ctx, device, failOnError, softwareInventory) + err = c.bmcAttributes(ctx, device, softwareInventory) if err != nil && failOnError { return nil, err } @@ -107,7 +107,7 @@ func (c *Client) Inventory(ctx context.Context, failOnError bool) (device *commo // DeviceVendorModel returns the device vendor and model attributes // bmcAttributes collects BMC component attributes -func (c *Client) bmcAttributes(ctx context.Context, device *common.Device, failOnError bool, softwareInventory []*redfish.SoftwareInventory) (err error) { +func (c *Client) bmcAttributes(ctx context.Context, device *common.Device, softwareInventory []*redfish.SoftwareInventory) (err error) { managers, err := c.Managers(ctx) if err != nil { return err @@ -193,7 +193,7 @@ func (c *Client) chassisAttributes(ctx context.Context, device *common.Device, f } -func (c *Client) systemAttributes(ctx context.Context, device *common.Device, failOnError bool, softwareInventory []*redfish.SoftwareInventory) (err error) { +func (c *Client) systemAttributes(device *common.Device, failOnError bool, softwareInventory []*redfish.SoftwareInventory) (err error) { systems, err := c.Systems() if err != nil { return err 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)