diff --git a/bmc/floppy.go b/bmc/floppy.go new file mode 100644 index 00000000..0965de37 --- /dev/null +++ b/bmc/floppy.go @@ -0,0 +1,136 @@ +package bmc + +import ( + "context" + "fmt" + "io" + + "github.com/hashicorp/go-multierror" + "github.com/pkg/errors" +) + +// FloppyImageUploader defines methods to upload a floppy image +type FloppyImageUploader interface { + UploadFloppyImage(ctx context.Context, image io.Reader) (err error) +} + +// floppyImageUploaderProvider is an internal struct to correlate an implementation/provider and its name +type floppyImageUploaderProvider struct { + name string + impl FloppyImageUploader +} + +// uploadFloppyImage is a wrapper method to invoke methods for the FloppyImageUploader interface +func uploadFloppyImage(ctx context.Context, image io.Reader, p []floppyImageUploaderProvider) (metadata Metadata, err error) { + var metadataLocal Metadata + + for _, elem := range p { + if elem.impl == nil { + continue + } + + select { + case <-ctx.Done(): + err = multierror.Append(err, ctx.Err()) + + return metadata, err + default: + metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name) + uploadErr := elem.impl.UploadFloppyImage(ctx, image) + if uploadErr != nil { + err = multierror.Append(err, errors.WithMessagef(uploadErr, "provider: %v", elem.name)) + continue + } + + metadataLocal.SuccessfulProvider = elem.name + return metadataLocal, nil + } + } + + return metadataLocal, multierror.Append(err, errors.New("failed to upload floppy image")) +} + +// UploadFloppyImageFromInterfaces identifies implementations of the FloppyImageUploader interface and passes the found implementations to the uploadFloppyImage() wrapper +func UploadFloppyImageFromInterfaces(ctx context.Context, image io.Reader, p []interface{}) (metadata Metadata, err error) { + providers := make([]floppyImageUploaderProvider, 0) + for _, elem := range p { + temp := floppyImageUploaderProvider{name: getProviderName(elem)} + switch p := elem.(type) { + case FloppyImageUploader: + temp.impl = p + providers = append(providers, temp) + default: + e := fmt.Sprintf("not a FloppyImageUploader implementation: %T", p) + err = multierror.Append(err, errors.New(e)) + } + } + + if len(providers) == 0 { + return metadata, multierror.Append(err, errors.New("no FloppyImageUploader implementations found")) + } + + return uploadFloppyImage(ctx, image, providers) +} + +// FloppyImageUploader defines methods to unmount a floppy image +type FloppyImageUnmounter interface { + UnmountFloppyImage(ctx context.Context) (err error) +} + +// floppyImageUnmounterProvider is an internal struct to correlate an implementation/provider and its name +type floppyImageUnmounterProvider struct { + name string + impl FloppyImageUnmounter +} + +// unmountFloppyImage is a wrapper method to invoke methods for the FloppyImageUnmounter interface +func unmountFloppyImage(ctx context.Context, p []floppyImageUnmounterProvider) (metadata Metadata, err error) { + var metadataLocal Metadata + + for _, elem := range p { + if elem.impl == nil { + continue + } + + select { + case <-ctx.Done(): + err = multierror.Append(err, ctx.Err()) + + return metadata, err + default: + metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name) + uploadErr := elem.impl.UnmountFloppyImage(ctx) + if uploadErr != nil { + err = multierror.Append(err, errors.WithMessagef(uploadErr, "provider: %v", elem.name)) + continue + } + + metadataLocal.SuccessfulProvider = elem.name + return metadataLocal, nil + } + } + + return metadataLocal, multierror.Append(err, errors.New("failed to unmount floppy image")) +} + +// UploadFloppyImageFromInterfaces identifies implementations of the FloppyImageUnmounter interface and passes the found implementations to the unmountFloppyImage() wrapper +func UnmountFloppyImageFromInterfaces(ctx context.Context, p []interface{}) (metadata Metadata, err error) { + providers := make([]floppyImageUnmounterProvider, 0) + for _, elem := range p { + temp := floppyImageUnmounterProvider{name: getProviderName(elem)} + switch p := elem.(type) { + case FloppyImageUnmounter: + temp.impl = p + providers = append(providers, temp) + default: + e := fmt.Sprintf("not a FloppyImageUnmounter implementation: %T", p) + err = multierror.Append(err, errors.New(e)) + } + } + + if len(providers) == 0 { + return metadata, multierror.Append(err, errors.New("no FloppyImageUnmounter implementations found")) + } + + return unmountFloppyImage(ctx, providers) +} diff --git a/client.go b/client.go index c5ecadde..15c6d470 100644 --- a/client.go +++ b/client.go @@ -378,3 +378,17 @@ func (c *Client) Screenshot(ctx context.Context) (image []byte, fileType string, return image, fileType, err } + +func (c *Client) UploadFloppyImage(ctx context.Context, image io.Reader) (err error) { + metadata, err := bmc.UploadFloppyImageFromInterfaces(ctx, image, c.registry().GetDriverInterfaces()) + c.setMetadata(metadata) + + return err +} + +func (c *Client) UnmountFloppyImage(ctx context.Context) (err error) { + metadata, err := bmc.UnmountFloppyImageFromInterfaces(ctx, c.registry().GetDriverInterfaces()) + c.setMetadata(metadata) + + return err +} diff --git a/providers/providers.go b/providers/providers.go index 64c587a4..3fe85b64 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -23,6 +23,12 @@ const ( FeatureBootDeviceSet registrar.Feature = "bootdeviceset" // FeaturesVirtualMedia means an implementation can manage virtual media devices FeatureVirtualMedia registrar.Feature = "virtualmedia" + // FeatureUploadFloppyImage means an implementation uploads a floppy image for mounting as virtual media. + // + // note: This is differs from FeatureVirtualMedia which is limited to accepting a URL to download the image from. + FeatureUploadFloppyImage registrar.Feature = "uploadFloppyImage" + // FeatureUnmountFloppyImage means an implementation removes a floppy image that was previously uploaded. + FeatureUnmountFloppyImage registrar.Feature = "unmountFloppyImage" // FeatureFirmwareInstall means an implementation that initiates the firmware install process FeatureFirmwareInstall registrar.Feature = "firmwareinstall" // FeatureFirmwareInstallSatus means an implementation that returns the firmware install status