Skip to content

Commit

Permalink
FS-1729 (#8)
Browse files Browse the repository at this point in the history
* FS-1729; Set/Upload BiosCfg skeleton

- move actions to their own file for cleanliness
- Update Rivets, add new biosControl action
- add new biosControl action to handler
- add new functions to store/bmc to support SetBiosConfig
- Update bmclib
- Add proper call to bmclib for SetBiosConfigurationFromFile

* PR changes
  • Loading branch information
jakeschuurmans authored Oct 11, 2024
1 parent c5a94cc commit fba9711
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 64 deletions.
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/metal-toolbox/bioscfg
go 1.22.1

require (
github.com/bmc-toolbox/bmclib/v2 v2.2.4
github.com/bmc-toolbox/bmclib/v2 v2.3.1
github.com/bombsimon/logrusr/v2 v2.0.1
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/equinix-labs/otel-init-go v0.0.9
Expand All @@ -12,7 +12,7 @@ require (
github.com/jeremywohl/flatten v1.0.1
github.com/metal-toolbox/ctrl v0.2.9
github.com/metal-toolbox/fleetdb v1.19.5
github.com/metal-toolbox/rivets v1.3.8
github.com/metal-toolbox/rivets v1.3.10
github.com/mitchellh/copystructure v1.2.0
github.com/mitchellh/mapstructure v1.5.0
github.com/pkg/errors v0.9.1
Expand All @@ -36,7 +36,7 @@ require (
github.com/VictorLowther/soap v0.0.0-20150314151524-8e36fca84b22 // indirect
github.com/banzaicloud/logrus-runtime-formatter v0.0.0-20190729070250-5ae5475bae5e // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bmc-toolbox/common v0.0.0-20240723142833-87832458b53b // indirect
github.com/bmc-toolbox/common v0.0.0-20240806132831-ba8adc6a35e3 // indirect
github.com/bytedance/sonic v1.12.1 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
Expand Down Expand Up @@ -112,7 +112,7 @@ require (
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91 // indirect
github.com/stmcginnis/gofish v0.19.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
Expand Down Expand Up @@ -144,6 +144,7 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
Expand Down
18 changes: 10 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bmc-toolbox/bmclib/v2 v2.2.4 h1:agAQuDLI/NNpKkxU+c+NfPZAu8ENBDE+kcTfz7WTCrw=
github.com/bmc-toolbox/bmclib/v2 v2.2.4/go.mod h1:V2XVg0Scpm16+0gE7WnI+5bU/M0c/o/nPZKHKzyVjAo=
github.com/bmc-toolbox/common v0.0.0-20240723142833-87832458b53b h1:0LHjikaGWlqEMczrCEZ6w1N/ZqcYlx6WRHkhabRUQEk=
github.com/bmc-toolbox/common v0.0.0-20240723142833-87832458b53b/go.mod h1:Cdnkm+edb6C0pVkyCrwh3JTXAe0iUF9diDG/DztPI9I=
github.com/bmc-toolbox/bmclib/v2 v2.3.1 h1:cXUfQFlQpDeUHruwXBYOdEkWXmW/nKdXpOIqbH8fz/M=
github.com/bmc-toolbox/bmclib/v2 v2.3.1/go.mod h1:t8If/0fHQTRIK/yKDk2H3SgthDNNj+7z2aeftDFRFrU=
github.com/bmc-toolbox/common v0.0.0-20240806132831-ba8adc6a35e3 h1:/BjZSX/sphptIdxpYo4wxAQkgMLyMMgfdl48J9DKNeE=
github.com/bmc-toolbox/common v0.0.0-20240806132831-ba8adc6a35e3/go.mod h1:Cdnkm+edb6C0pVkyCrwh3JTXAe0iUF9diDG/DztPI9I=
github.com/bombsimon/logrusr/v2 v2.0.1 h1:1VgxVNQMCvjirZIYaT9JYn6sAVGVEcNtRE0y4mvaOAM=
github.com/bombsimon/logrusr/v2 v2.0.1/go.mod h1:ByVAX+vHdLGAfdroiMg6q0zgq2FODY2lc5YJvzmOJio=
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
Expand Down Expand Up @@ -556,8 +556,8 @@ github.com/metal-toolbox/ctrl v0.2.9 h1:Q1Hqpqyb71/gg2PcX/qrfoDE8FlydJt4rPQb7/Z8
github.com/metal-toolbox/ctrl v0.2.9/go.mod h1:QVATUIWFx3dbjOoEX0EnJHtRvypRlXZ9HUGaPLRyTG8=
github.com/metal-toolbox/fleetdb v1.19.5 h1:ERgdFAUtWnT/AeVhCGclsENmwPhU88JUcgOZAdxWKYI=
github.com/metal-toolbox/fleetdb v1.19.5/go.mod h1:k9MZXQsJX4NfBoANst6g1468papSs0tzsSyzN3gGWuQ=
github.com/metal-toolbox/rivets v1.3.8 h1:BxzBPBYPMGBwJurIe+8Xji2YL7vHZUHbOmMpszWfPYw=
github.com/metal-toolbox/rivets v1.3.8/go.mod h1:8irU6eXgOa3QkjdcGi/aY4vqoMqCkbwVz7iVTYYPCX8=
github.com/metal-toolbox/rivets v1.3.10 h1:UgYQSx/QJF6Yuzf+YSRF/t9soL6QjMY8sLugf/aMmII=
github.com/metal-toolbox/rivets v1.3.10/go.mod h1:HkF9k8yw3MZqrIkxsi7w7EkTP3h2/t08WBpm+WK/Dsk=
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
Expand Down Expand Up @@ -701,8 +701,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91 h1:WmABtU8y6kTgzoVUn3FWCQGAfyodve3uz3xno28BrRs=
github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91/go.mod h1:BLDSFTp8pDlf/xDbLZa+F7f7eW0E/CHCboggsu8CznI=
github.com/stmcginnis/gofish v0.19.0 h1:fmxdRZ5WHfs+4ExArMYoeRfoh+SAxLELKtmoVplBkU4=
github.com/stmcginnis/gofish v0.19.0/go.mod h1:lq2jHj2t8Krg0Gx02ABk8MbK7Dz9jvWpO/TGnVksn00=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
Expand Down Expand Up @@ -1349,6 +1349,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
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=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs=
gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
Expand Down
105 changes: 105 additions & 0 deletions internal/bioscfg/action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package bioscfg

import (
"context"
"io"
"net/http"

"github.com/metal-toolbox/bioscfg/internal/model"
rctypes "github.com/metal-toolbox/rivets/condition"
)

// handleAction completes the condition task based on the condition action
func (th *TaskHandler) handleAction(ctx context.Context) error {
switch th.task.Parameters.Action {
case rctypes.ResetConfig:
return th.resetBiosConfig(ctx)
case rctypes.SetConfig:
return th.setBiosConfig(ctx)
default:
return th.failedWithError(ctx, string(th.task.Parameters.Action), errUnsupportedAction)
}
}

// resetBiosConfig resets the bios of the server
func (th *TaskHandler) resetBiosConfig(ctx context.Context) error {
// Get Power State
state, err := th.bmcClient.GetPowerState(ctx)
if err != nil {
return th.failedWithError(ctx, "error getting power state", err)
}

err = th.publishActivef(ctx, "current power state: %s", state)
if err != nil {
return err
}

// Reset Bios
err = th.bmcClient.ResetBiosConfig(ctx)
if err != nil {
return th.failedWithError(ctx, "error reseting bios", err)
}

err = th.publishActive(ctx, "BIOS settings reset")
if err != nil {
return err
}

// Reboot (if ON)
if state == model.PowerStateOn {
err = th.bmcClient.SetPowerState(ctx, model.PowerStateReset)
if err != nil {
return th.failedWithError(ctx, "failed to reboot server", err)
}

return th.successful(ctx, "rebooting server")
}

return th.successful(ctx, "skipping server reboot, not on")
}

// setBiosConfig sets BIOS Config
func (th *TaskHandler) setBiosConfig(ctx context.Context) error {
var configURL = ""
if th.task.Parameters.BiosConfigURL != nil {
configURL = th.task.Parameters.BiosConfigURL.String()
}

if configURL == "" {
return th.failed(ctx, "no Bios Configu URL was found")
}

req, err := http.NewRequest(http.MethodGet, configURL, http.NoBody)
if err != nil {
return th.failedWithError(ctx, "failed to create http request", err)
}
req = req.WithContext(ctx)

client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
return th.failedWithError(ctx, "failed to get bios config from url", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return th.failed(ctx, resp.Status)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return th.failedWithError(ctx, "failed to read file from response body", err)
}

err = th.publishActive(ctx, "got bios config from url")
if err != nil {
return err
}

err = th.bmcClient.SetBiosConfigFromFile(ctx, string(body))
if err != nil {
return th.failedWithError(ctx, "failed to set bios config through the bmc", err)
}

return th.successful(ctx, "bios set")
}
50 changes: 4 additions & 46 deletions internal/bioscfg/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (th *TaskHandler) HandleTask(ctx context.Context, genTask *rctypes.Task[any
th.publisher = publisher

// Ungeneric the task
th.task, err = NewTask(genTask)
th.task, err = newTask(genTask)
if err != nil {
th.logger.WithFields(logrus.Fields{
"conditionID": genTask.ID,
Expand Down Expand Up @@ -91,10 +91,10 @@ func (th *TaskHandler) HandleTask(ctx context.Context, genTask *rctypes.Task[any
}
}()

return th.Run(ctx)
return th.run(ctx)
}

func (th *TaskHandler) Run(ctx context.Context) error {
func (th *TaskHandler) run(ctx context.Context) error {
ctx, span := otel.Tracer(pkgName).Start(
ctx,
"TaskHandler.Run",
Expand All @@ -108,47 +108,5 @@ func (th *TaskHandler) Run(ctx context.Context) error {
return err
}

switch th.task.Parameters.Action {
case rctypes.ResetSettings:
return th.ResetBios(ctx)
default:
return th.failedWithError(ctx, string(th.task.Parameters.Action), errUnsupportedAction)
}
}

// ResetBios reset the bios of the server
func (th *TaskHandler) ResetBios(ctx context.Context) error {
// Get Power State
state, err := th.bmcClient.GetPowerState(ctx)
if err != nil {
return th.failedWithError(ctx, "error getting power state", err)
}

err = th.publishActivef(ctx, "current power state: %s", state)
if err != nil {
return err
}

// Reset Bios
err = th.bmcClient.ResetBios(ctx)
if err != nil {
return th.failedWithError(ctx, "error reseting bios", err)
}

err = th.publishActive(ctx, "BIOS settings reset")
if err != nil {
return err
}

// Reboot (if ON)
if state == model.PowerStateOn {
err = th.bmcClient.SetPowerState(ctx, model.PowerStateReset)
if err != nil {
return th.failedWithError(ctx, "failed to reboot server", err)
}

return th.successful(ctx, "rebooting server")
}

return th.successful(ctx, "skipping server reboot, not on")
return th.handleAction(ctx)
}
2 changes: 1 addition & 1 deletion internal/bioscfg/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func (th *TaskHandler) publish(ctx context.Context, status string, state rctypes
th.task.State = state
th.task.Status.Append(status)

genTask, err := th.task.ToGeneric()
genTask, err := th.task.toGeneric()
if err != nil {
th.logger.WithError(errTaskConv).Error()
return err
Expand Down
6 changes: 4 additions & 2 deletions internal/bioscfg/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (

type Task rctypes.Task[*rctypes.BiosControlTaskParameters, json.RawMessage]

func NewTask(task *rctypes.Task[any, any]) (*Task, error) {
// newTask converts a Generic Condition Task to a BiosControl Task
func newTask(task *rctypes.Task[any, any]) (*Task, error) {
paramsJSON, ok := task.Parameters.(json.RawMessage)
if !ok {
return nil, errInvalidConditionParams
Expand Down Expand Up @@ -52,7 +53,8 @@ func NewTask(task *rctypes.Task[any, any]) (*Task, error) {
}, nil
}

func (task *Task) ToGeneric() (*rctypes.Task[any, any], error) {
// toGeneric converts a BiosControl Task to a Generic Condition Task
func (task *Task) toGeneric() (*rctypes.Task[any, any], error) {
paramsJSON, err := task.Parameters.Marshal()
if err != nil {
return nil, errors.Wrap(errTaskConv, err.Error()+": Task.Parameters")
Expand Down
11 changes: 10 additions & 1 deletion internal/store/bmc/bmc.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func (b *Client) Close(traceCtx context.Context) error {
// GetPowerState returns the device power status
func (b *Client) GetPowerState(ctx context.Context) (string, error) {
defer b.tracelog()

return b.client.GetPowerState(ctx)
}

Expand Down Expand Up @@ -148,11 +149,16 @@ func (b *Client) HostBooted(ctx context.Context) (bool, error) {
return status == constants.POSTStateOS, nil
}

func (b *Client) ResetBios(ctx context.Context) error {
func (b *Client) ResetBiosConfig(ctx context.Context) error {
defer b.tracelog()
return b.client.ResetBiosConfiguration(ctx)
}

func (b *Client) SetBiosConfigFromFile(ctx context.Context, cfg string) error {
defer b.tracelog()
return b.client.SetBiosConfigurationFromFile(ctx, cfg)
}

func (b *Client) tracelog() {
pc, _, _, _ := runtime.Caller(1)
funcName := path.Base(runtime.FuncForPC(pc).Name())
Expand Down Expand Up @@ -240,6 +246,9 @@ func newBmclibClient(asset *model.Asset, l *logrus.Entry) *bmclib.Client {
providers.FeatureBootDeviceSet,
providers.FeaturePowerSet,
providers.FeaturePowerState,
providers.FeatureResetBiosConfiguration,
providers.FeatureSetBiosConfiguration,
providers.FeatureSetBiosConfigurationFromFile,
)

// NOTE: remove the .Using("redfish") before this ends up in prod
Expand Down
6 changes: 5 additions & 1 deletion internal/store/bmc/dryRun.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (b *DryRunBMCClient) HostBooted(_ context.Context) (bool, error) {
return true, nil
}

func (b *DryRunBMCClient) ResetBios(ctx context.Context) error {
func (b *DryRunBMCClient) ResetBiosConfig(ctx context.Context) error {
_, ok := serverStates[b.id]
if !ok {
return errBmcCantFindServer
Expand All @@ -130,6 +130,10 @@ func (b *DryRunBMCClient) ResetBios(ctx context.Context) error {
return b.SetPowerState(ctx, "cycle")
}

func (b *DryRunBMCClient) SetBiosConfigFromFile(_ context.Context, _ string) error {
return nil
}

// getServer gets a simulateed server state, and update power status and boot device if required
func (b *DryRunBMCClient) getServer() (*server, error) {
state, ok := serverStates[b.id]
Expand Down
3 changes: 2 additions & 1 deletion internal/store/bmc/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ type BMC interface {
GetBootDevice(ctx context.Context) (device string, persistent, efiBoot bool, err error)
PowerCycleBMC(ctx context.Context) error
HostBooted(ctx context.Context) (bool, error)
ResetBios(ctx context.Context) error
ResetBiosConfig(ctx context.Context) error
SetBiosConfigFromFile(ctx context.Context, cfg string) error
}

0 comments on commit fba9711

Please sign in to comment.