Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
joelrebel committed Nov 6, 2023
1 parent 71e9ae8 commit 3748abb
Show file tree
Hide file tree
Showing 14 changed files with 271 additions and 198 deletions.
139 changes: 109 additions & 30 deletions bmc/firmware.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import (
"io"

"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
//
Expand Down Expand Up @@ -90,6 +91,8 @@ func FirmwareInstallFromInterfaces(ctx context.Context, component, operationAppl
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 @@ -167,87 +170,81 @@ func FirmwareInstallStatusFromInterfaces(ctx context.Context, installVersion, co
return firmwareInstallStatus(ctx, installVersion, component, taskID, implementations)
}

type FirmwareInstallOptions struct {
// The firmware upload task ID if any.
UploadTaskID string
// operationsApplyTime - one of the OperationApplyTime constants
OperationApplyTime constants.OperationApplyTime
}

// FirmwareInstallerWithOpts defines an interface to install firmware updates with the given install parameters
type FirmwareInstallerWithOptions interface {
// FirmwareInstallWithOptions uploads firmware update payload to the BMC returning the task ID
// 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.
// reader - the io.reader to the firmware update file.
// uploadTaskID - the taskID for the firmware upload verify task (returned by FirmwareUpload)
//
// return values:
// taskID - A taskID is returned if the update process on the BMC returns an identifier for the update process.
FirmwareInstallWithOptions(ctx context.Context, component string, reader io.Reader, opts *FirmwareInstallOptions) (taskID string, err error)
// 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
FirmwareInstallerWithOptions
FirmwareInstallerUploaded
}

// firmwareInstallWithOptions uploads and initiates firmware update for the component
func firmwareInstallWithOptions(ctx context.Context, component string, reader io.Reader, opts *FirmwareInstallOptions, generic []firmwareInstallerWithOptionsProvider) (taskID string, metadata Metadata, err error) {
// 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.FirmwareInstallerWithOptions == nil {
if elem.FirmwareInstallerUploaded == nil {
continue
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())

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

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

// FirmwareInstallWithOptionsFromInterfaces identifies implementations of the FirmwareInstallerWithOptions interface and passes the found implementations to the firmwareInstallWithOptions() wrapper
func FirmwareInstallWithOptionsFromInterfaces(ctx context.Context, component string, reader io.Reader, opts *FirmwareInstallOptions, generic []interface{}) (taskID string, metadata Metadata, err error) {
// 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 FirmwareInstallerWithOptions:
temp.FirmwareInstallerWithOptions = p
case FirmwareInstallerUploaded:
temp.FirmwareInstallerUploaded = p
implementations = append(implementations, temp)
default:
e := fmt.Sprintf("not a FirmwareInstallerWithOptions implementation: %T", p)
e := fmt.Sprintf("not a FirmwareInstallerUploaded implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(implementations) == 0 {
return taskID, metadata, multierror.Append(
return installTaskID, metadata, multierror.Append(
err,
errors.Wrap(
bmclibErrs.ErrProviderImplementation,
("no FirmwareInstallerWithOptions implementations found"),
("no FirmwareInstallerUploaded implementations found"),
),
)
}

return firmwareInstallWithOptions(ctx, component, reader, opts, implementations)
return firmwareInstallUploaded(ctx, component, uploadTaskID, implementations)
}

type FirmwareInstallStepsGetter interface {
Expand Down Expand Up @@ -381,3 +378,85 @@ func firmwareUpload(ctx context.Context, component string, reader io.Reader, gen

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
}
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))
}
}
if len(implementations) == 0 {
return state, status, metadata, multierror.Append(
err,
errors.Wrap(
bmclibErrs.ErrProviderImplementation,
("no FirmwareTaskVerifier implementations found"),
),
)
}

return firmwareTaskStatus(ctx, kind, component, taskID, installVersion, implementations)
}
23 changes: 16 additions & 7 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ func (c *Client) FirmwareInstall(ctx context.Context, component string, operatio
return taskID, err
}

// Note: this interface is to be deprecated in favour of a more generic FirmwareTaskStatus.
//
// FirmwareInstallStatus pass through library function to check firmware install status
func (c *Client) FirmwareInstallStatus(ctx context.Context, installVersion, component, taskID string) (status string, err error) {
status, metadata, err := bmc.FirmwareInstallStatusFromInterfaces(ctx, installVersion, component, taskID, c.registry().GetDriverInterfaces())
Expand Down Expand Up @@ -461,16 +463,23 @@ func (c *Client) FirmwareInstallSteps(ctx context.Context, component string) (ac
return status, err
}

// FirmwareInstallWithOptions kicks off firmware install, it is to deprecate the existing FirmwareInstall interface method.
func (c *Client) FirmwareInstallWithOptions(ctx context.Context, component string, reader io.Reader, opts *bmc.FirmwareInstallOptions) (installTaskID string, err error) {
installTaskID, metadata, err := bmc.FirmwareInstallWithOptionsFromInterfaces(ctx, component, reader, opts, c.registry().GetDriverInterfaces())
c.setMetadata(metadata)
return installTaskID, err
}

// FirmwareUpload just uploads the firmware for install, it returns a task ID to verify the upload status.
func (c *Client) FirmwareUpload(ctx context.Context, component string, reader io.Reader) (uploadVerifyTaskID string, err error) {
uploadVerifyTaskID, metadata, err := bmc.FirmwareUploadFromInterfaces(ctx, component, reader, c.Registry.GetDriverInterfaces())
c.setMetadata(metadata)
return uploadVerifyTaskID, err
}

// FirmwareTaskStatus pass through library function to check firmware task statuses
func (c *Client) FirmwareTaskStatus(ctx context.Context, kind constants.FirmwareInstallStep, component, taskID, installVersion string) (state, status string, err error) {
state, status, metadata, err := bmc.FirmwareTaskStatusFromInterfaces(ctx, kind, component, taskID, installVersion, c.registry().GetDriverInterfaces())
c.setMetadata(metadata)
return state, status, err
}

// FirmwareInstallUploaded kicks off firmware install for a firmware uploaded with FirmwareUpload.
func (c *Client) FirmwareInstallUploaded(ctx context.Context, component, uploadVerifyTaskID string) (installTaskID string, err error) {
installTaskID, metadata, err := bmc.FirmwareInstallerUploadedFromInterfaces(ctx, component, uploadVerifyTaskID, c.registry().GetDriverInterfaces())
c.setMetadata(metadata)
return installTaskID, err
}
14 changes: 10 additions & 4 deletions constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const (
// FirmwareOnStartUpdateRequest sets the firmware install to begin after the start request has been sent.
OnStartUpdateRequest OperationApplyTime = "OnStartUpdateRequest"

// TODO: rename FirmwareInstall* task status names to FirmwareTaskState and declare a type.

// Firmware install states returned by bmclib provider FirmwareInstallStatus implementations
//
// The redfish from the redfish spec are exposed as a smaller set of bmclib states for callers
Expand Down Expand Up @@ -67,20 +69,24 @@ const (
FirmwareInstallFailed = "failed"

// FirmwareInstallPowerCycleHost indicates the firmware install requires a host power cycle
FirmwareInstallPowerCyleHost = "powercycle-host"
FirmwareInstallPowerCycleHost = "powercycle-host"

// FirmwareInstallPowerCycleBMC indicates the firmware install requires a BMC power cycle
FirmwareInstallPowerCycleBMC = "powercycle-bmc"

FirmwareInstallUnknown = "unknown"

// FirmwareInstallStepUploadInitiateInstall identifies the firmware
FirmwareInstallStepUploadInitiateInstall FirmwareInstallStep = "upload-initiate-install"
FirmwareInstallStepInstallStatus FirmwareInstallStep = "install-status"

// FirmwareInstallStepUpload is an identifier for the firmware upload step in the firmware install process.
FirmwareInstallStepUpload FirmwareInstallStep = "upload"
FirmwareInstallStepUploadStatus FirmwareInstallStep = "upload-status"

// FirmwareInstallStepInstall is an identifier for the firmware install step after a firmware has been uploaded.
FirmwareInstallStepInstall FirmwareInstallStep = "install"
FirmwareInstallStepInstallStatus FirmwareInstallStep = "install-status"
FirmwareInstallStepInstallUploaded FirmwareInstallStep = "install-uploaded"

FirmwareInstallStepPowerOffHost FirmwareInstallStep = "power-off-host"

// device BIOS/UEFI POST code bmclib identifiers
POSTStateBootINIT = "boot-init/pxe"
Expand Down
8 changes: 4 additions & 4 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ var (
// ErrFirmwareInstallStatus is returned for firmware install status read
ErrFirmwareInstallStatus = errors.New("error querying firmware install status")

// ErrFirmwareVerifyTaskRunning indicates a firmware verify task is in progress
// the caller is expected to check the verify task is completed successfully before proceeding.
ErrFirmwareVerifyTaskRunning = errors.New("firmware uploaded and is currently being verified")
// ErrFirmwareTaskStatus is returned when a query for the firmware upload status fails
ErrFirmwareTaskStatus = errors.New("error querying firmware upload status")

ErrFirmwareVerifyTaskFailed = errors.New("firmware verify task failed")
// ErrFirmwareVerifyTask indicates a firmware verify task is in progress or did not complete successfully,
ErrFirmwareVerifyTask = errors.New("error firmware upload verify task")

// ErrRedfishUpdateService is returned on redfish update service errors
ErrRedfishUpdateService = errors.New("redfish update service error")
Expand Down
27 changes: 0 additions & 27 deletions internal/redfishwrapper/firmware_install_status.go

This file was deleted.

2 changes: 0 additions & 2 deletions internal/redfishwrapper/firmware_upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ func (c *Client) FirmwareUpload(ctx context.Context, reader io.Reader, params *R
return "", errors.Wrap(err, "error redfish UpdateParameters payload")
}

fmt.Println(string(parameters))

installMethod, installURI, err := c.firmwareInstallMethodURI(ctx)
if err != nil {
return "", errors.Wrap(bmclibErrs.ErrFirmwareUpload, err.Error())
Expand Down
Loading

0 comments on commit 3748abb

Please sign in to comment.