Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inventory firmware #261

Merged
merged 25 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
430ebdf
define various consts, errors, providers, device types
joelrebel Apr 7, 2022
683e135
bmc/inventory: define inventory getter interface and method
joelrebel Apr 7, 2022
7e7ce13
providers/asrockrack: implement InventoryGetter interface
joelrebel Apr 7, 2022
3de88c2
providers/redfish: implement InventoryGetter interface, update gofish…
joelrebel Apr 7, 2022
440f699
bmc/firmware: replace previous interface with FirmwareInstaller, Firm…
joelrebel Apr 7, 2022
7ab7659
providers/redfish: implement FirmwareInstallStatus, FirmwareInstallVe…
joelrebel Apr 7, 2022
b061360
providers/asrockrack: rework, implement FirmwareInstaller, FirmwareIn…
joelrebel Apr 7, 2022
3bddf1d
providers/asrockrack: implement PowerStateGetter, PowerStateSetter in…
joelrebel Apr 7, 2022
df48b12
bmc/postcode: define interfaces to collect BIOS/UEFI POST codes
joelrebel Apr 7, 2022
d2657e4
bmc/client: define methods for Inventory, Firmware, PostCode actions
joelrebel Apr 7, 2022
a73cdcc
rename getter methods
joelrebel Apr 13, 2022
34c2169
Implement changes based on feedback from GH PR#261
joelrebel Apr 22, 2022
3673df4
providers/asrockrack: empty version string from BMC returned as state…
joelrebel Apr 22, 2022
84306c7
providers/asrockrack: include various other fields in the BMC update …
joelrebel Apr 22, 2022
f09df96
consts, errors: firmware install status returned when a BMC needs a r…
joelrebel Apr 22, 2022
201a037
providers/asrr, redfish: implement BmcReset interface
joelrebel Apr 22, 2022
95d0197
redfish: fix implementation, rename GetInventory() -> Inventory()
joelrebel Apr 22, 2022
91fc904
bmc/firmware: update FirmwareInstallStatus() method signature
joelrebel Apr 25, 2022
e2543e3
redfish/inventory: split out helper methods
joelrebel Apr 25, 2022
a68ebce
providers/asrockrack: use structured logging, purge debug statements
joelrebel Apr 26, 2022
8e5a784
examples: merge changes from master, fixup
joelrebel Apr 26, 2022
0f7a4c7
providers: merge changes from master, a few suggested fixes
joelrebel Apr 26, 2022
92f6fc0
go.mod: deps updated after rebase
joelrebel Apr 26, 2022
b14936a
providers/asrockrack: pass context to Compatible()
joelrebel Apr 27, 2022
eb3610c
examples/v1/inventory: fix JSON indent
joelrebel Apr 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 93 additions & 130 deletions bmc/firmware.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,201 +2,164 @@ package bmc

import (
"context"
"errors"
"fmt"
"io"

bmclibErrs "github.com/bmc-toolbox/bmclib/errors"

"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
)

// BMCVersionGetter retrieves the current BMC firmware version information
type BMCVersionGetter interface {
GetBMCVersion(ctx context.Context) (version string, err error)
}

// BMCFirmwareUpdater upgrades the BMC firmware
type BMCFirmwareUpdater interface {
FirmwareUpdateBMC(ctx context.Context, fileReader io.Reader, fileSize int64) (err error)
// FirmwareInstaller defines an interface to install firmware updates
type FirmwareInstaller interface {
// FirmwareInstall uploads firmware update payload to the BMC returning the task ID
//
// parameters:
// component - the component slug for the component update being installed.
// applyAt - one of "Immediate", "OnReset".
// forceInstall - purge the install task queued/scheduled firmware install BMC task (if any).
// reader - the io.reader to the firmware update file.
//
// return values:
// taskID - A taskID is returned if the update process on the BMC returns an identifier for the update process.
FirmwareInstall(ctx context.Context, component, applyAt string, forceInstall bool, reader io.Reader) (taskID string, err error)
}

// BIOSVersionGetter retrieves the current BIOS firmware version information
type BIOSVersionGetter interface {
GetBIOSVersion(ctx context.Context) (version string, err error)
// firmwareInstallerProvider is an internal struct to correlate an implementation/provider and its name
type firmwareInstallerProvider struct {
name string
FirmwareInstaller
}

// BIOSFirmwareUpdater upgrades the BIOS firmware
type BIOSFirmwareUpdater interface {
FirmwareUpdateBIOS(ctx context.Context, fileReader io.Reader, fileSize int64) (err error)
}

// GetBMCVersion returns the BMC firmware version, trying all interface implementations passed in
func GetBMCVersion(ctx context.Context, p []BMCVersionGetter) (version string, err error) {
// firmwareInstall uploads and initiates firmware update for the component
func firmwareInstall(ctx context.Context, component, applyAt string, forceInstall bool, reader io.Reader, generic []firmwareInstallerProvider) (taskID string, metadata Metadata, err error) {
var metadataLocal Metadata
Loop:
for _, elem := range p {
if elem == nil {
for _, elem := range generic {
if elem.FirmwareInstaller == nil {
continue
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())
break Loop
default:
version, vErr := elem.GetBMCVersion(ctx)
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
taskID, vErr := elem.FirmwareInstall(ctx, component, applyAt, forceInstall, reader)
if vErr != nil {
err = multierror.Append(err, errors.WithMessagef(vErr, "provider: %v", elem.name))
err = multierror.Append(err, vErr)
continue

}
return version, nil
metadataLocal.SuccessfulProvider = elem.name
return taskID, metadataLocal, nil
}
}

return version, multierror.Append(err, errors.New("failed to get BMC version"))
return taskID, metadataLocal, multierror.Append(err, errors.New("failure in FirmwareInstall"))
}

// GetBMCVersionFromInterfaces pass through to library function
func GetBMCVersionFromInterfaces(ctx context.Context, generic []interface{}) (version string, err error) {
bmcVersionGetter := make([]BMCVersionGetter, 0)
// FirmwareInstallFromInterfaces pass through to library function
func FirmwareInstallFromInterfaces(ctx context.Context, component, applyAt string, forceInstall bool, reader io.Reader, generic []interface{}) (taskID string, metadata Metadata, err error) {
implementations := make([]firmwareInstallerProvider, 0)
for _, elem := range generic {
temp := firmwareInstallerProvider{name: getProviderName(elem)}
switch p := elem.(type) {
case BMCVersionGetter:
bmcVersionGetter = append(bmcVersionGetter, p)
case FirmwareInstaller:
temp.FirmwareInstaller = p
implementations = append(implementations, temp)
default:
e := fmt.Sprintf("not a BMCVersionGetter implementation: %T", p)
e := fmt.Sprintf("not a FirmwareInstaller implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(bmcVersionGetter) == 0 {
return version, multierror.Append(err, errors.New("no BMCVersionGetter implementations found"))
if len(implementations) == 0 {
return taskID, metadata, multierror.Append(
err,
errors.Wrap(
bmclibErrs.ErrProviderImplementation,
("no FirmwareInstaller implementations found"),
),
)
}

return GetBMCVersion(ctx, bmcVersionGetter)
return firmwareInstall(ctx, component, applyAt, forceInstall, reader, implementations)
}

// UpdateBMCFirmware upgrades the BMC firmware, trying all interface implementations passed ini
func UpdateBMCFirmware(ctx context.Context, fileReader io.Reader, fileSize int64, p []BMCFirmwareUpdater) (err error) {
Loop:
for _, elem := range p {
if elem == nil {
continue
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())
break Loop
default:
uErr := elem.FirmwareUpdateBMC(ctx, fileReader, fileSize)
if uErr != nil {
err = multierror.Append(err, uErr)
continue
}
return nil
}
}

return multierror.Append(err, errors.New("failed to update BMC firmware"))
// FirmwareInstallVerifier defines an interface to check firmware install status
type FirmwareInstallVerifier interface {
// FirmwareInstallStatus returns the status of the firmware install process.
//
// parameters:
// installVersion (required) - the version this method should check is installed.
// component (optional) - the component slug for the component update being installed.
// taskID (optional) - the task identifier.
//
// return values:
// status - returns one of the FirmwareInstall statuses (see devices/constants.go).
FirmwareInstallStatus(ctx context.Context, installVersion, component, taskID string) (status string, err error)
}

// UpdateBMCFirmwareFromInterfaces pass through to library function
func UpdateBMCFirmwareFromInterfaces(ctx context.Context, fileReader io.Reader, fileSize int64, generic []interface{}) (err error) {
bmcFirmwareUpdater := make([]BMCFirmwareUpdater, 0)
for _, elem := range generic {
switch p := elem.(type) {
case BMCFirmwareUpdater:
bmcFirmwareUpdater = append(bmcFirmwareUpdater, p)
default:
e := fmt.Sprintf("not a BMCFirmwareUpdater implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(bmcFirmwareUpdater) == 0 {
return multierror.Append(err, errors.New("no BMCFirmwareUpdater implementations found"))
}

return UpdateBMCFirmware(ctx, fileReader, fileSize, bmcFirmwareUpdater)
// firmwareInstallVerifierProvider is an internal struct to correlate an implementation/provider and its name
type firmwareInstallVerifierProvider struct {
name string
FirmwareInstallVerifier
}

// GetBIOSVersion returns the BMC firmware version, trying all interface implementations passed in
func GetBIOSVersion(ctx context.Context, p []BIOSVersionGetter) (version string, err error) {
// firmwareInstallStatus returns the status of the firmware install process
func firmwareInstallStatus(ctx context.Context, installVersion, component, taskID string, generic []firmwareInstallVerifierProvider) (status string, metadata Metadata, err error) {
var metadataLocal Metadata
Loop:
for _, elem := range p {
if elem == nil {
for _, elem := range generic {
if elem.FirmwareInstallVerifier == nil {
continue
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())
break Loop
default:
version, vErr := elem.GetBIOSVersion(ctx)
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
status, vErr := elem.FirmwareInstallStatus(ctx, installVersion, component, taskID)
if vErr != nil {
err = multierror.Append(err, errors.WithMessagef(vErr, "provider: %v", elem.name))
err = multierror.Append(err, vErr)
continue
}
return version, nil
}
}

return version, multierror.Append(err, errors.New("failed to get BIOS version"))
}

// GetBIOSVersionFromInterfaces pass through to library function
func GetBIOSVersionFromInterfaces(ctx context.Context, generic []interface{}) (version string, err error) {
biosVersionGetter := make([]BIOSVersionGetter, 0)
for _, elem := range generic {
switch p := elem.(type) {
case BIOSVersionGetter:
biosVersionGetter = append(biosVersionGetter, p)
default:
e := fmt.Sprintf("not a BIOSVersionGetter implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(biosVersionGetter) == 0 {
return version, multierror.Append(err, errors.New("no BIOSVersionGetter implementations found"))
}

return GetBIOSVersion(ctx, biosVersionGetter)
}

// UpdateBIOSFirmware upgrades the BIOS firmware, trying all interface implementations passed ini
func UpdateBIOSFirmware(ctx context.Context, fileReader io.Reader, fileSize int64, p []BIOSFirmwareUpdater) (err error) {
Loop:
for _, elem := range p {
if elem == nil {
continue
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())
break Loop
default:
uErr := elem.FirmwareUpdateBIOS(ctx, fileReader, fileSize)
if uErr != nil {
err = multierror.Append(err, uErr)
continue
}
return nil
metadataLocal.SuccessfulProvider = elem.name
return status, metadataLocal, nil
}
}

return multierror.Append(err, errors.New("failed to update BIOS firmware"))
return status, metadataLocal, multierror.Append(err, errors.New("failure in FirmwareInstallStatus"))
}

// GetBMCVersionFromInterfaces pass through to library function
func UpdateBIOSFirmwareFromInterfaces(ctx context.Context, fileReader io.Reader, fileSize int64, generic []interface{}) (err error) {
biosFirmwareUpdater := make([]BIOSFirmwareUpdater, 0)
// FirmwareInstallStatusFromInterfaces pass through to library function
func FirmwareInstallStatusFromInterfaces(ctx context.Context, installVersion, component, taskID string, generic []interface{}) (status string, metadata Metadata, err error) {
implementations := make([]firmwareInstallVerifierProvider, 0)
for _, elem := range generic {
temp := firmwareInstallVerifierProvider{name: getProviderName(elem)}
switch p := elem.(type) {
case BIOSFirmwareUpdater:
biosFirmwareUpdater = append(biosFirmwareUpdater, p)
case FirmwareInstallVerifier:
temp.FirmwareInstallVerifier = p
implementations = append(implementations, temp)
default:
e := fmt.Sprintf("not a BIOSFirmwareUpdater implementation: %T", p)
e := fmt.Sprintf("not a FirmwareInstallVerifier implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(biosFirmwareUpdater) == 0 {
return multierror.Append(err, errors.New("no BIOSFirmwareUpdater implementations found"))
if len(implementations) == 0 {
return taskID, metadata, multierror.Append(
err,
errors.Wrap(
bmclibErrs.ErrProviderImplementation,
("no FirmwareInstallVerifier implementations found"),
),
)
}

return UpdateBIOSFirmware(ctx, fileReader, fileSize, biosFirmwareUpdater)
return firmwareInstallStatus(ctx, installVersion, component, taskID, implementations)
}
Loading