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

[1/3] Redfishwrapper changes #359

Merged
merged 21 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ace4b65
redfishwrapper/fixtures: add mock redfish endpoint data
joelrebel Nov 7, 2023
4546643
redfishwrapper/client: add helper methods to return the manager, bios…
joelrebel Nov 7, 2023
704d562
redfishwrapper: add Task helper methods
joelrebel Nov 7, 2023
a048777
redfishwrapper: add firmware helper methods
joelrebel Nov 7, 2023
c4e67c0
providers/redfish: move DeviceVendorModel helper method into redfishw…
joelrebel Nov 7, 2023
21f7a10
constants: define the FirmwareInstallStep, OperationApplyTime consts
joelrebel Nov 7, 2023
7acebcf
asrockrack: use helper method from the common package
joelrebel Nov 7, 2023
6668d0d
redfishwrapper/client: simpler logger setup
joelrebel Nov 9, 2023
c43615b
redfishwrapper: clean up a few commented out bits
joelrebel Nov 9, 2023
08b9c7f
bmc/firmware: rename constant and skip validating its value
joelrebel Nov 7, 2023
90503ed
bmc/firmware: adds new interfaces for firmware upload, install upload…
joelrebel Nov 7, 2023
287ca19
providers: define the new features
joelrebel Nov 7, 2023
e1067c2
errors: define a few common errors
joelrebel Nov 7, 2023
6967a0a
examples: update based on changes
joelrebel Nov 7, 2023
435ebfd
supermicro/firmware: rework to support x12s and the newer firmware in…
joelrebel Nov 7, 2023
c027c04
providers/redfish: fix bad import
joelrebel Nov 7, 2023
1e685c0
go: update to current gofish
joelrebel Nov 7, 2023
4d7e2c0
FirmwareUpload interface to accept a *os.File instead
joelrebel Nov 9, 2023
958dbc2
providers/supermicro: define a bmcQueryor interface and implement for…
joelrebel Nov 10, 2023
d4992c5
supermicro: support SMC BMCs that don't implement CSRF tokens
joelrebel Nov 9, 2023
e6f756a
providers/smc: purge unused error
joelrebel Nov 13, 2023
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
310 changes: 303 additions & 7 deletions bmc/firmware.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,29 @@
"context"
"fmt"
"io"
"os"

"github.com/bmc-toolbox/bmclib/v2/constants"
bconsts "github.com/bmc-toolbox/bmclib/v2/constants"
bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors"

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

// FirmwareInstaller defines an interface to install firmware updates
// FirmwareInstaller defines an interface to upload and initiate a firmware install
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".
// operationsApplyTime - one of the OperationApplyTime constants
// 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)
FirmwareInstall(ctx context.Context, component string, operationApplyTime string, forceInstall bool, reader io.Reader) (taskID string, err error)
}

// firmwareInstallerProvider is an internal struct to correlate an implementation/provider and its name
Expand All @@ -33,7 +36,7 @@
}

// 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) {
func firmwareInstall(ctx context.Context, component, operationApplyTime string, forceInstall bool, reader io.Reader, generic []firmwareInstallerProvider) (taskID string, metadata Metadata, err error) {
var metadataLocal Metadata

for _, elem := range generic {
Expand All @@ -47,7 +50,7 @@
return taskID, metadata, err
default:
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
taskID, vErr := elem.FirmwareInstall(ctx, component, applyAt, forceInstall, reader)
taskID, vErr := elem.FirmwareInstall(ctx, component, operationApplyTime, forceInstall, reader)
if vErr != nil {
err = multierror.Append(err, errors.WithMessagef(vErr, "provider: %v", elem.name))
err = multierror.Append(err, vErr)
Expand All @@ -63,7 +66,7 @@
}

// FirmwareInstallFromInterfaces identifies implementations of the FirmwareInstaller interface and passes the found implementations to the firmwareInstall() wrapper
func FirmwareInstallFromInterfaces(ctx context.Context, component, applyAt string, forceInstall bool, reader io.Reader, generic []interface{}) (taskID string, metadata Metadata, err error) {
func FirmwareInstallFromInterfaces(ctx context.Context, component, operationApplyTime 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)}
Expand All @@ -86,9 +89,11 @@
)
}

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

// Note: this interface is to be deprecated in favour of a more generic FirmwareTaskVerifier.
//
// FirmwareInstallVerifier defines an interface to check firmware install status
type FirmwareInstallVerifier interface {
// FirmwareInstallStatus returns the status of the firmware install process.
Expand Down Expand Up @@ -165,3 +170,294 @@

return firmwareInstallStatus(ctx, installVersion, component, taskID, implementations)
}

// FirmwareInstallerWithOpts defines an interface to install firmware that was previously uploaded with FirmwareUpload
type FirmwareInstallerUploaded interface {
// FirmwareInstallUploaded uploads firmware update payload to the BMC returning the firmware install task ID
//
// parameters:
// component - the component slug for the component update being installed.
// uploadTaskID - the taskID for the firmware upload verify task (returned by FirmwareUpload)
//
// return values:
// installTaskID - A installTaskID is returned if the update process on the BMC returns an identifier for the firmware install process.
FirmwareInstallUploaded(ctx context.Context, component, uploadTaskID string) (taskID string, err error)
}

// firmwareInstallerProvider is an internal struct to correlate an implementation/provider and its name
type firmwareInstallerWithOptionsProvider struct {
name string
FirmwareInstallerUploaded
}

// firmwareInstallUploaded uploads and initiates firmware update for the component
func firmwareInstallUploaded(ctx context.Context, component, uploadTaskID string, generic []firmwareInstallerWithOptionsProvider) (installTaskID string, metadata Metadata, err error) {
var metadataLocal Metadata

for _, elem := range generic {
if elem.FirmwareInstallerUploaded == nil {
continue

Check warning on line 199 in bmc/firmware.go

View check run for this annotation

Codecov / codecov/patch

bmc/firmware.go#L199

Added line #L199 was not covered by tests
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())

return installTaskID, metadata, err
default:
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
var vErr error
installTaskID, vErr = elem.FirmwareInstallUploaded(ctx, component, uploadTaskID)
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 installTaskID, metadataLocal, nil
}
}

return installTaskID, metadataLocal, multierror.Append(err, errors.New("failure in FirmwareInstallUploaded"))
}

// FirmwareInstallerUploadedFromInterfaces identifies implementations of the FirmwareInstallUploaded interface and passes the found implementations to the firmwareInstallUploaded() wrapper
func FirmwareInstallerUploadedFromInterfaces(ctx context.Context, component, uploadTaskID string, generic []interface{}) (installTaskID string, metadata Metadata, err error) {
implementations := make([]firmwareInstallerWithOptionsProvider, 0)
for _, elem := range generic {
temp := firmwareInstallerWithOptionsProvider{name: getProviderName(elem)}
switch p := elem.(type) {
case FirmwareInstallerUploaded:
temp.FirmwareInstallerUploaded = p
implementations = append(implementations, temp)
default:
e := fmt.Sprintf("not a FirmwareInstallerUploaded implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(implementations) == 0 {
return installTaskID, metadata, multierror.Append(
err,
errors.Wrap(
bmclibErrs.ErrProviderImplementation,
("no FirmwareInstallerUploaded implementations found"),
),
)
}

return firmwareInstallUploaded(ctx, component, uploadTaskID, implementations)
}

type FirmwareInstallStepsGetter interface {
FirmwareInstallSteps(ctx context.Context, component string) ([]constants.FirmwareInstallStep, error)
}

// firmwareInstallStepsGetterProvider is an internal struct to correlate an implementation/provider and its name
type firmwareInstallStepsGetterProvider struct {
name string
FirmwareInstallStepsGetter
}

// FirmwareInstallStepsFromInterfaces identifies implementations of the FirmwareInstallStepsGetter interface and passes the found implementations to the firmwareInstallSteps() wrapper.
func FirmwareInstallStepsFromInterfaces(ctx context.Context, component string, generic []interface{}) (steps []constants.FirmwareInstallStep, metadata Metadata, err error) {
implementations := make([]firmwareInstallStepsGetterProvider, 0)
for _, elem := range generic {
temp := firmwareInstallStepsGetterProvider{name: getProviderName(elem)}
switch p := elem.(type) {
case FirmwareInstallStepsGetter:
temp.FirmwareInstallStepsGetter = p
implementations = append(implementations, temp)
default:
e := fmt.Sprintf("not a FirmwareInstallStepsGetter implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(implementations) == 0 {
return steps, metadata, multierror.Append(
err,
errors.Wrap(
bmclibErrs.ErrProviderImplementation,
("no FirmwareInstallStepsGetter implementations found"),
),
)
}

return firmwareInstallSteps(ctx, component, implementations)
}

func firmwareInstallSteps(ctx context.Context, component string, generic []firmwareInstallStepsGetterProvider) (steps []constants.FirmwareInstallStep, metadata Metadata, err error) {
var metadataLocal Metadata

for _, elem := range generic {
if elem.FirmwareInstallStepsGetter == nil {
continue

Check warning on line 293 in bmc/firmware.go

View check run for this annotation

Codecov / codecov/patch

bmc/firmware.go#L293

Added line #L293 was not covered by tests
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())

return steps, metadata, err
default:
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
steps, vErr := elem.FirmwareInstallSteps(ctx, component)
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 steps, metadataLocal, nil
}
}

return steps, metadataLocal, multierror.Append(err, errors.New("failure in FirmwareInstallSteps"))
}

type FirmwareUploader interface {
FirmwareUpload(ctx context.Context, component string, file *os.File) (uploadVerifyTaskID string, err error)
}

// firmwareUploaderProvider is an internal struct to correlate an implementation/provider and its name
type firmwareUploaderProvider struct {
name string
FirmwareUploader
}

// FirmwareUploaderFromInterfaces identifies implementations of the FirmwareUploader interface and passes the found implementations to the firmwareUpload() wrapper.
func FirmwareUploadFromInterfaces(ctx context.Context, component string, file *os.File, generic []interface{}) (taskID string, metadata Metadata, err error) {
implementations := make([]firmwareUploaderProvider, 0)
for _, elem := range generic {
temp := firmwareUploaderProvider{name: getProviderName(elem)}
switch p := elem.(type) {
case FirmwareUploader:
temp.FirmwareUploader = p
implementations = append(implementations, temp)
default:
e := fmt.Sprintf("not a FirmwareUploader implementation: %T", p)
err = multierror.Append(err, errors.New(e))

Check warning on line 338 in bmc/firmware.go

View check run for this annotation

Codecov / codecov/patch

bmc/firmware.go#L328-L338

Added lines #L328 - L338 were not covered by tests
}
}
if len(implementations) == 0 {
return taskID, metadata, multierror.Append(
err,
errors.Wrap(
bmclibErrs.ErrProviderImplementation,
("no FirmwareUploader implementations found"),
),
)

Check warning on line 348 in bmc/firmware.go

View check run for this annotation

Codecov / codecov/patch

bmc/firmware.go#L341-L348

Added lines #L341 - L348 were not covered by tests
}

return firmwareUpload(ctx, component, file, implementations)

Check warning on line 351 in bmc/firmware.go

View check run for this annotation

Codecov / codecov/patch

bmc/firmware.go#L351

Added line #L351 was not covered by tests
}

func firmwareUpload(ctx context.Context, component string, file *os.File, generic []firmwareUploaderProvider) (taskID string, metadata Metadata, err error) {
var metadataLocal Metadata

for _, elem := range generic {
if elem.FirmwareUploader == nil {
continue

Check warning on line 359 in bmc/firmware.go

View check run for this annotation

Codecov / codecov/patch

bmc/firmware.go#L359

Added line #L359 was not covered by tests
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())

return taskID, metadata, err
default:
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
taskID, vErr := elem.FirmwareUpload(ctx, component, file)
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 taskID, metadataLocal, nil
}
}

return taskID, metadataLocal, multierror.Append(err, errors.New("failure in FirmwareUpload"))
}

// FirmwareTaskVerifier defines an interface to check the status for firmware related tasks queued on the BMC.
// these could be a an firmware upload and verify task or a firmware install task.
//
// This is to replace the FirmwareInstallVerifier interface
type FirmwareTaskVerifier interface {
// FirmwareTaskStatus returns the status of the firmware upload process.
//
// parameters:
// kind (required) - The FirmwareInstallStep
// component (optional) - the component slug for the component that the firmware was uploaded for.
// taskID (required) - the task identifier.
// installVersion (optional) - the firmware version being installed as part of the task if applicable.
//
// return values:
// state - returns one of the FirmwareTask statuses (see devices/constants.go).
// status - returns firmware task progress or other arbitrary task information.
FirmwareTaskStatus(ctx context.Context, kind bconsts.FirmwareInstallStep, component, taskID, installVersion string) (state string, status string, err error)
}

// firmwareTaskVerifierProvider is an internal struct to correlate an implementation/provider and its name
type firmwareTaskVerifierProvider struct {
name string
FirmwareTaskVerifier
}

// firmwareTaskStatus returns the status of the firmware upload process.
func firmwareTaskStatus(ctx context.Context, kind bconsts.FirmwareInstallStep, component, taskID, installVersion string, generic []firmwareTaskVerifierProvider) (state, status string, metadata Metadata, err error) {
var metadataLocal Metadata

for _, elem := range generic {
if elem.FirmwareTaskVerifier == nil {
continue

Check warning on line 414 in bmc/firmware.go

View check run for this annotation

Codecov / codecov/patch

bmc/firmware.go#L414

Added line #L414 was not covered by tests
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())

return state, status, metadata, err
default:
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
state, status, vErr := elem.FirmwareTaskStatus(ctx, kind, component, taskID, installVersion)
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 state, status, metadataLocal, nil
}
}

return state, status, metadataLocal, multierror.Append(err, errors.New("failure in FirmwareTaskStatus"))
}

// FirmwareTaskStatusFromInterfaces identifies implementations of the FirmwareTaskVerifier interface and passes the found implementations to the firmwareTaskStatus() wrapper.
func FirmwareTaskStatusFromInterfaces(ctx context.Context, kind bconsts.FirmwareInstallStep, component, taskID, installVersion string, generic []interface{}) (state, status string, metadata Metadata, err error) {
implementations := make([]firmwareTaskVerifierProvider, 0)
for _, elem := range generic {
temp := firmwareTaskVerifierProvider{name: getProviderName(elem)}
switch p := elem.(type) {
case FirmwareTaskVerifier:
temp.FirmwareTaskVerifier = p
implementations = append(implementations, temp)
default:
e := fmt.Sprintf("not a FirmwareTaskVerifier implementation: %T", p)
err = multierror.Append(err, errors.New(e))

Check warning on line 449 in bmc/firmware.go

View check run for this annotation

Codecov / codecov/patch

bmc/firmware.go#L447-L449

Added lines #L447 - L449 were not covered by tests
}
}
if len(implementations) == 0 {
return state, status, metadata, multierror.Append(
err,
errors.Wrap(
bmclibErrs.ErrProviderImplementation,
("no FirmwareTaskVerifier implementations found"),
),
)

Check warning on line 459 in bmc/firmware.go

View check run for this annotation

Codecov / codecov/patch

bmc/firmware.go#L453-L459

Added lines #L453 - L459 were not covered by tests
}

return firmwareTaskStatus(ctx, kind, component, taskID, installVersion, implementations)
}
Loading