Skip to content

Commit

Permalink
FS-1123: Supermicro Update Manager (SUM) provider
Browse files Browse the repository at this point in the history
  • Loading branch information
splaspood committed Jun 5, 2024
1 parent a704223 commit 471edba
Showing 1 changed file with 280 additions and 0 deletions.
280 changes: 280 additions & 0 deletions providers/sum/sum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
package sum

Check failure on line 1 in providers/sum/sum.go

View workflow job for this annotation

GitHub Actions / lint

: # github.com/bmc-toolbox/bmclib/v2/providers/sum

import (
"context"
"os"
"os/exec"
"strings"

"github.com/bmc-toolbox/bmclib/v2/providers"
"github.com/bmc-toolbox/common"
"github.com/bmc-toolbox/common/config"
"github.com/go-logr/logr"
"github.com/jacobweinstock/registrar"
)

const (
// ProviderName for the provider implementation
ProviderName = "sum"
// ProviderProtocol for the provider implementation
ProviderProtocol = "sum"
)

var (
// Features implemented by SUM
Features = registrar.Features{
providers.FeatureGetBiosConfiguration,
providers.FeatureSetBiosConfiguration,
providers.FeatureSetBiosConfigurationFromFile,
providers.FeatureResetBiosConfiguration,
}
)

// Conn for Sum connection details
type Exec struct {
SumPath string
Log logr.Logger
Host string
Username string
Password string
}

// Option for setting optional Client values
type Option func(*Exec)

func WithSumPath(sumPath string) Option {
return func(c *Exec) {
c.SumPath = sumPath
}
}

func WithLogger(log logr.Logger) Option {
return func(c *Exec) {
c.Log = log
}
}

func New(host, user, pass string, opts ...Option) (*Exec, error) {
sum := &Exec{
Host: host,
Username: user,
Password: pass,
Log: logr.Discard(),
}

for _, opt := range opts {
opt(sum)
}

var err error

if sum.SumPath == "" {
sum.SumPath, err = exec.LookPath("sum")
if err != nil {
return nil, err
}
} else {
if _, err = os.Stat(sum.SumPath); err != nil {
return nil, err
}
}

return sum, nil
}

// Open a connection to a BMC
func (c *Exec) Open(ctx context.Context) (err error) {
return nil
}

// Close a connection to a BMC
func (c *Exec) Close(ctx context.Context) (err error) {
return nil
}

func (c *Exec) Name() string {
return ProviderName
}

func (s *Exec) run(ctx context.Context, command string, additionalArgs ...string) (output string, err error) {
// TODO(splaspood) use a tmp file here (as sum supports) to read the password
sumArgs := []string{"-i", s.Host, "-u", s.Username, "-p", s.Password, "-c", command}
sumArgs = append(sumArgs, additionalArgs...)

s.Log.V(9).WithValues(
"sumArgs",
sumArgs,
).Info("Calling sum")

cmd := exec.CommandContext(ctx, s.SumPath, sumArgs...)
b_out, err := cmd.CombinedOutput()
if err != nil || ctx.Err() != nil {
if err != nil {
return
}

if ctx.Err() != nil {
return output, ctx.Err()
}
}

return string(b_out), err
}

func (s *Exec) GetCurrentBiosCfg(ctx context.Context) (output string, err error) {
return s.run(ctx, "GetCurrentBiosCfg")
}

func (s *Exec) LoadDefaultBiosCfg(ctx context.Context) (err error) {
_, err = s.run(ctx, "LoadDefaultBiosCfg")
return
}

func (s *Exec) ChangeBiosCfg(ctx context.Context, cfgFile string, reboot bool) (err error) {
args := []string{"--file", cfgFile}

if reboot {
args = append(args, "--reboot")
}

_, err = s.run(ctx, "ChangeBiosCfg", args...)

return
}

// GetBiosConfiguration return bios configuration
func (s *Exec) GetBiosConfiguration(ctx context.Context) (biosConfig map[string]string, err error) {
biosText, err := s.GetCurrentBiosCfg(ctx)
if err != nil {
return
}

// We need to call vcm here to take the XML returned by SUM and convert it into a simple map
vcm, err := config.NewVendorConfigManager("xml", common.VendorSupermicro, map[string]string{})
if err != nil {
return
}

err = vcm.Unmarshal(biosText)
if err != nil {
return nil, err
}

biosConfig, err = vcm.StandardConfig()
if err != nil {
return nil, err
}

return biosConfig, nil
}

// SetBiosConfiguration set bios configuration
func (s *Exec) SetBiosConfiguration(ctx context.Context, biosConfig map[string]string) (err error) {
vcm, err := config.NewVendorConfigManager("xml", common.VendorSupermicro, map[string]string{})
if err != nil {
return
}

for k, v := range biosConfig {
switch {
case k == "boot_mode":
if err = vcm.BootMode(v); err != nil {

Check failure on line 181 in providers/sum/sum.go

View workflow job for this annotation

GitHub Actions / lint

vcm.BootMode undefined (type config.VendorConfigManager has no field or method BootMode)
return err
}
case k == "boot_order":
if err = vcm.BootOrder(v); err != nil {

Check failure on line 185 in providers/sum/sum.go

View workflow job for this annotation

GitHub Actions / lint

vcm.BootOrder undefined (type config.VendorConfigManager has no field or method BootOrder)
return err
}
case k == "intel_sgx":
if err = vcm.IntelSGX(v); err != nil {

Check failure on line 189 in providers/sum/sum.go

View workflow job for this annotation

GitHub Actions / lint

vcm.IntelSGX undefined (type config.VendorConfigManager has no field or method IntelSGX)
return err
}
case k == "secure_boot":
switch v {
case "Enabled":
if err = vcm.SecureBoot(true); err != nil {

Check failure on line 195 in providers/sum/sum.go

View workflow job for this annotation

GitHub Actions / lint

vcm.SecureBoot undefined (type config.VendorConfigManager has no field or method SecureBoot)
return err
}
case "Disabled":
if err = vcm.SecureBoot(false); err != nil {

Check failure on line 199 in providers/sum/sum.go

View workflow job for this annotation

GitHub Actions / lint

vcm.SecureBoot undefined (type config.VendorConfigManager has no field or method SecureBoot)
return err
}
}
case k == "tpm":
switch v {
case "Enabled":
if err = vcm.TPM(true); err != nil {

Check failure on line 206 in providers/sum/sum.go

View workflow job for this annotation

GitHub Actions / lint

vcm.TPM undefined (type config.VendorConfigManager has no field or method TPM)
return err
}
case "Disabled":
if err = vcm.TPM(false); err != nil {

Check failure on line 210 in providers/sum/sum.go

View workflow job for this annotation

GitHub Actions / lint

vcm.TPM undefined (type config.VendorConfigManager has no field or method TPM)
return err
}
}
case k == "smt":
switch v {
case "Enabled":
if err = vcm.SMT(true); err != nil {

Check failure on line 217 in providers/sum/sum.go

View workflow job for this annotation

GitHub Actions / lint

vcm.SMT undefined (type config.VendorConfigManager has no field or method SMT)
return err
}
case "Disabled":
if err = vcm.SMT(false); err != nil {

Check failure on line 221 in providers/sum/sum.go

View workflow job for this annotation

GitHub Actions / lint

vcm.SMT undefined (type config.VendorConfigManager has no field or method SMT)
return err
}
}
case k == "sr_iov":
switch v {
case "Enabled":
if err = vcm.SRIOV(true); err != nil {
return err
}
case "Disabled":
if err = vcm.SRIOV(false); err != nil {
return err
}
}
case strings.HasPrefix(k, "raw:"):
// k = raw:Menu1,SubMenu1,SubMenuMenu1,SettingName
pathStr := strings.TrimPrefix(k, "raw:")
path := strings.Split(pathStr, ",")
name := path[len(path)-1]
path = path[:len(path)-1]

vcm.Raw(name, v, path)
}
}

xmlData, err := vcm.Marshal()
if err != nil {
return
}

return s.SetBIOSConfigurationFromFile(ctx, xmlData)
}

func (s *Exec) SetBIOSConfigurationFromFile(ctx context.Context, cfg string) (err error) {
// Open tmp file to hold cfg
inputConfigTmpFile, err := os.CreateTemp("", "bmclib")
if err != nil {
return
}

defer os.Remove(inputConfigTmpFile.Name())

_, err = inputConfigTmpFile.WriteString(cfg)
if err != nil {
return
}

err = inputConfigTmpFile.Close()
if err != nil {
return
}

return s.ChangeBiosCfg(ctx, inputConfigTmpFile.Name(), false)
}

// ResetBiosConfiguration reset bios configuration
func (s *Exec) ResetBiosConfiguration(ctx context.Context) (err error) {
return s.LoadDefaultBiosCfg(ctx)
}

0 comments on commit 471edba

Please sign in to comment.