From ccb9d620bcbf44cd98ea3644a3e9979e7e6dfa7e Mon Sep 17 00:00:00 2001 From: Joel Rebello Date: Fri, 22 Apr 2022 11:14:48 +0200 Subject: [PATCH 1/9] providers/asrockrack: empty version string from BMC returned as state FirmwareInstallUnknown this is so that the caller is aware that the BMC is not aware of the component version and can decide if it wants to power cycle or take other actions. instead of assuming the device needs a powercycle --- providers/asrockrack/firmware.go | 132 +++++++++++++++---------------- 1 file changed, 63 insertions(+), 69 deletions(-) diff --git a/providers/asrockrack/firmware.go b/providers/asrockrack/firmware.go index 0ac74860..374d3c29 100644 --- a/providers/asrockrack/firmware.go +++ b/providers/asrockrack/firmware.go @@ -14,6 +14,13 @@ import ( bmclibErrs "github.com/bmc-toolbox/bmclib/errors" ) +const ( + versionStrError = -1 + versionStrMatch = 0 + versionStrMismatch = 1 + versionStrEmpty = 2 +) + // FirmwareInstall uploads and initiates firmware update for the component func (a *ASRockRack) FirmwareInstall(ctx context.Context, component, applyAt string, forceInstall bool, reader io.Reader) (jobID string, err error) { var size int64 @@ -42,12 +49,10 @@ func (a *ASRockRack) FirmwareInstall(ctx context.Context, component, applyAt str // FirmwareInstallStatus returns the status of the firmware install process, a bool value indicating if the component requires a reset func (a *ASRockRack) FirmwareInstallStatus(ctx context.Context, component, installVersion, taskID string) (status string, err error) { switch component { - case devices.SlugBIOS: - return a.firmwareUpdateBIOSStatus(ctx, installVersion) - case devices.SlugBMC: - return a.firmwareUpdateBMCStatus(ctx, installVersion) + case devices.SlugBIOS, devices.SlugBMC: + return a.firmwareUpdateStatus(ctx, component, installVersion) default: - return "", errors.Wrap(bmclibErrs.ErrFirmwareInstall, "component unsupported: "+component) + return "", errors.Wrap(bmclibErrs.ErrFirmwareInstallStatus, "component unsupported: "+component) } } @@ -114,92 +119,74 @@ func (a *ASRockRack) firmwareInstallBIOS(ctx context.Context, reader io.Reader, return nil } -// firmwareUpdateBMCStatus returns the BMC firmware install status -func (a *ASRockRack) firmwareUpdateBMCStatus(ctx context.Context, installVersion string) (status string, err error) { - p, progressErr := a.flashProgress(ctx, "api/maintenance/firmware/flash-progress") - if progressErr != nil { - installed, versionErr := a.versionInstalled(ctx, devices.SlugBMC, installVersion) - if err != nil { - return "", versionErr - } - - if installed { - return devices.FirmwareInstallComplete, nil - } - - return "", progressErr - } +// firmwareUpdateBIOSStatus returns the BIOS firmware install status +func (a *ASRockRack) firmwareUpdateStatus(ctx context.Context, component string, installVersion string) (status string, err error) { + // TODO: purge debug logging + os.Setenv("BMCLIB_LOG_LEVEL", "trace") + defer os.Unsetenv("BMCLIB_LOG_LEVEL") - switch p.State { - case 0: - return devices.FirmwareInstallRunning, nil - case 2: - return devices.FirmwareInstallComplete, nil + var endpoint string + switch component { + case devices.SlugBIOS: + endpoint = "api/asrr/maintenance/BIOS/flash-progress" + case devices.SlugBMC: + endpoint = "api/maintenance/firmware/flash-progress" default: - a.log.V(3).Info("warn", "bmc returned unknown flash progress state: "+strconv.Itoa(p.State)) + return "", errors.Wrap(bmclibErrs.ErrFirmwareInstallStatus, "component unsupported: "+component) } - // at this point the flash-progress endpoint isn't returning useful information - // query the fimware info endpoint to identify the installed version - installed, err := a.versionInstalled(ctx, devices.SlugBMC, installVersion) + // 1. query the flash progress endpoint + // + // once an update completes/fails this endpoint will return 500 + progress, err := a.flashProgress(ctx, endpoint) if err != nil { - return "", err + a.log.V(3).Info("warn", "bmc query for install progress returned error: "+err.Error()) } - if installed { - return devices.FirmwareInstallComplete, nil - } - - return devices.FirmwareInstallUnknown, nil -} - -// firmwareUpdateBIOSStatus returns the BIOS firmware install status -func (a *ASRockRack) firmwareUpdateBIOSStatus(ctx context.Context, installVersion string) (status string, err error) { - p, progressErr := a.flashProgress(ctx, "api/asrr/maintenance/BIOS/flash-progress") - if progressErr != nil { - installed, versionErr := a.versionInstalled(ctx, devices.SlugBIOS, installVersion) - if versionErr != nil { - return "", versionErr - } - - if installed { + if progress != nil { + switch progress.State { + case 0: + return devices.FirmwareInstallRunning, nil + case 2: return devices.FirmwareInstallComplete, nil + default: + a.log.V(3).Info("warn", "bmc returned unknown flash progress state: "+strconv.Itoa(progress.State)) } - - return "", progressErr - } - - switch p.State { - // Note: we're ignoring case 1 here, should it just be part of case 0 - case 0: - return devices.FirmwareInstallRunning, nil - case 2: - return devices.FirmwareInstallComplete, nil - default: - a.log.V(3).Info("warn", "bmc returned unknown flash progress state: "+strconv.Itoa(p.State)) } + // 2. query the firmware info endpoint to determine the update status + // // at this point the flash-progress endpoint isn't returning useful information - // query the fimware info endpoint to identify the installed version - installed, err := a.versionInstalled(ctx, devices.SlugBIOS, installVersion) + var installStatus int + + installStatus, err = a.versionInstalled(ctx, component, installVersion) if err != nil { - return "", err + return "", errors.Wrap(bmclibErrs.ErrFirmwareInstallStatus, err.Error()) } - if installed { + switch installStatus { + case versionStrMatch: return devices.FirmwareInstallComplete, nil + case versionStrEmpty: + return devices.FirmwareInstallUnknown, nil + case versionStrMismatch: + return devices.FirmwareInstallRunning, nil } return devices.FirmwareInstallUnknown, nil } -// versionInstalled returns a -func (a *ASRockRack) versionInstalled(ctx context.Context, component, version string) (bool, error) { +// versionInstalled returns int values on the status of the firmware version install +// +// - 0 indicates the given version parameter matches the version installed +// - 1 indicates the given version parameter does not match the version installed +// - 2 the version parameter returned from the BMC is empty (which means the BMC needs a reset) +func (a *ASRockRack) versionInstalled(ctx context.Context, component, version string) (status int, err error) { fwInfo, err := a.firmwareInfo(ctx) if err != nil { err = errors.Wrap(err, "error querying for firmware info: ") a.log.V(3).Info("warn", err.Error()) - return false, err + return versionStrError, err } var installed string @@ -210,12 +197,19 @@ func (a *ASRockRack) versionInstalled(ctx context.Context, component, version st case devices.SlugBMC: installed = fwInfo.BMCVersion default: - return false, errors.Wrap(bmclibErrs.ErrFirmwareInstall, "component unsupported: "+component) + return versionStrError, errors.Wrap(bmclibErrs.ErrFirmwareInstall, "component unsupported: "+component) } + // version match if strings.EqualFold(installed, version) { - return true, nil + return versionStrMatch, nil + } + + // fwinfo returned an empty string for firmware revision + // this indicates the BMC is out of sync with the firmware versions installed + if strings.TrimSpace(installed) == "" { + return versionStrEmpty, nil } - return false, nil + return 1, nil } From de3f25a41280018894f067d0967f8beb0065e2b3 Mon Sep 17 00:00:00 2001 From: Joel Rebello Date: Fri, 22 Apr 2022 11:21:48 +0200 Subject: [PATCH 2/9] providers/asrockrack: include various other fields in the BMC update payload this is an attempt to have the BMC preserve the User, Network BMC configuration after a flash --- providers/asrockrack/helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/asrockrack/helpers.go b/providers/asrockrack/helpers.go index 4f00a5a5..f64cb572 100644 --- a/providers/asrockrack/helpers.go +++ b/providers/asrockrack/helpers.go @@ -275,7 +275,7 @@ func (a *ASRockRack) upgradeBMC(ctx context.Context) error { endpoint := "api/maintenance/firmware/upgrade" // preserve all configuration during upgrade, full flash - pConfig := &preserveConfig{PreserveConfig: 1, FlashStatus: 1} + pConfig := &preserveConfig{FlashStatus: 1, PreserveConfig: 1, PreserveNetwork: 1, PreserveUser: 1} payload, err := json.Marshal(pConfig) if err != nil { return err From 5a1cc2f151c9f2a3338648a8c257fc1b9348df9f Mon Sep 17 00:00:00 2001 From: Joel Rebello Date: Fri, 22 Apr 2022 11:23:16 +0200 Subject: [PATCH 3/9] consts, errors: firmware install status returned when a BMC needs a reset error returned when the install status lookup fails --- devices/constants.go | 4 +++- errors/errors.go | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/devices/constants.go b/devices/constants.go index c5820a75..f0fc4aae 100644 --- a/devices/constants.go +++ b/devices/constants.go @@ -78,9 +78,11 @@ const ( FirmwareInstallFailed = "failed" // FirmwareInstallPowerCycleHost indicates the firmware install requires a host power cycle - // this covers the dell redfish state - 'scheduled' FirmwareInstallPowerCyleHost = "powercycle-host" + // FirmwareInstallPowerCycleBMC indicates the firmware install requires a BMC power cycle + FirmwareInstallPowerCycleBMC = "powercycle-bmc" + FirmwareInstallUnknown = "unknown" // device BIOS/UEFI POST code bmclib identifiers diff --git a/errors/errors.go b/errors/errors.go index fe4af1f1..f6546eb1 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -93,9 +93,12 @@ var ( // ErrFirmwareUpload is returned when a firmware upload method fails ErrFirmwareUpload = errors.New("error uploading firmware") - // ErrFirmwareInstall is returned for firmware update failures + // ErrFirmwareInstall is returned for firmware install failures ErrFirmwareInstall = errors.New("error updating firmware") + // ErrFirmwareInstallStatus is returned for firmware install status read + ErrFirmwareInstallStatus = errors.New("error querying firmware install status") + // ErrRedfishUpdateService is returned on redfish update service errors ErrRedfishUpdateService = errors.New("redfish update service error") From 970dfcffa611733d951ddb277ba49c79279e0ece Mon Sep 17 00:00:00 2001 From: Joel Rebello Date: Fri, 22 Apr 2022 11:24:58 +0200 Subject: [PATCH 4/9] providers/asrr, redfish: implement BmcReset interface This enables the ASRR and Redfish providers to reset the BMC --- providers/asrockrack/asrockrack.go | 1 + providers/asrockrack/power.go | 13 +++++++++++-- providers/redfish/redfish.go | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/providers/asrockrack/asrockrack.go b/providers/asrockrack/asrockrack.go index 8e2331b0..4f4b3d23 100644 --- a/providers/asrockrack/asrockrack.go +++ b/providers/asrockrack/asrockrack.go @@ -26,6 +26,7 @@ var ( providers.FeatureFirmwareInstall, providers.FeatureFirmwareInstallStatus, providers.FeaturePostCodeRead, + providers.FeatureBmcReset, } ) diff --git a/providers/asrockrack/power.go b/providers/asrockrack/power.go index 7f64de5d..f4deae18 100644 --- a/providers/asrockrack/power.go +++ b/providers/asrockrack/power.go @@ -86,8 +86,17 @@ func (a *ASRockRack) powerAction(ctx context.Context, action int) (ok bool, err return true, nil } -// 4. reset BMC -// nolint: unused // left here for reference +// BmcReset will reset the BMC - ASRR BMCs only support a cold reset. +func (a *ASRockRack) BmcReset(ctx context.Context, resetType string) (ok bool, err error) { + err = a.resetBMC(ctx) + if err != nil { + return false, err + } + + return true, nil +} + +// 4. reset BMC - performs a cold reset func (a *ASRockRack) resetBMC(ctx context.Context) error { endpoint := "api/maintenance/reset" diff --git a/providers/redfish/redfish.go b/providers/redfish/redfish.go index 17cab76d..750f7f53 100644 --- a/providers/redfish/redfish.go +++ b/providers/redfish/redfish.go @@ -33,6 +33,7 @@ var ( providers.FeatureInventoryRead, providers.FeatureFirmwareInstall, providers.FeatureFirmwareInstallStatus, + providers.FeatureBmcReset, } ) @@ -96,6 +97,23 @@ func (c *Conn) Compatible(ctx context.Context) bool { return err == nil } +// BmcReset powercycles the BMC +func (c *Conn) BmcReset(ctx context.Context, resetType string) (ok bool, err error) { + managers, err := c.conn.Service.Managers() + if err != nil { + return false, err + } + + for _, manager := range managers { + err = manager.Reset(rf.ResetType(resetType)) + if err != nil { + return false, err + } + } + + return true, nil +} + // PowerStateGet gets the power state of a BMC machine func (c *Conn) PowerStateGet(ctx context.Context) (state string, err error) { return c.status(ctx) From 592e418db234bbe3bfbe8112b8852a4bbe063ccc Mon Sep 17 00:00:00 2001 From: Joel Rebello Date: Fri, 22 Apr 2022 16:05:32 +0200 Subject: [PATCH 5/9] redfish: fix implementation, rename GetInventory() -> Inventory() --- providers/redfish/inventory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/redfish/inventory.go b/providers/redfish/inventory.go index 6e3593d8..d12a44bb 100644 --- a/providers/redfish/inventory.go +++ b/providers/redfish/inventory.go @@ -62,7 +62,7 @@ func (c *Conn) DeviceVendorModel(ctx context.Context) (vendor, model string, err return vendor, model, bmclibErrs.ErrRedfishSystemOdataID } -func (c *Conn) GetInventory(ctx context.Context) (device *devices.Device, err error) { +func (c *Conn) Inventory(ctx context.Context) (device *devices.Device, err error) { // initialize inventory object inv := &inventory{conn: c.conn} // TODO: this can soft fail From aa2539090556e1566231293846e85651fd4d682b Mon Sep 17 00:00:00 2001 From: Joel Rebello Date: Mon, 25 Apr 2022 16:05:24 +0200 Subject: [PATCH 6/9] bmc/firmware: update FirmwareInstallStatus() method signature - moves the optional component parameter at the end - providers/asrockrack/firmware: wrap FirmwareInstall errors --- bmc/firmware.go | 12 +- providers/asrockrack/firmware.go | 20 +- providers/redfish/firmware.go | 2 +- providers/redfish/inventory_collect.go | 344 +++++++++++++++++++++++++ 4 files changed, 362 insertions(+), 16 deletions(-) create mode 100644 providers/redfish/inventory_collect.go diff --git a/bmc/firmware.go b/bmc/firmware.go index 82fecd85..a20f28c1 100644 --- a/bmc/firmware.go +++ b/bmc/firmware.go @@ -93,13 +93,13 @@ type FirmwareInstallVerifier interface { // FirmwareInstallStatus returns the status of the firmware install process. // // parameters: - // component (optional) - the component slug for the component update being installed. // 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, component, installVersion, taskID string) (status string, err error) + FirmwareInstallStatus(ctx context.Context, installVersion, component, taskID string) (status string, err error) } // firmwareInstallVerifierProvider is an internal struct to correlate an implementation/provider and its name @@ -109,7 +109,7 @@ type firmwareInstallVerifierProvider struct { } // firmwareInstallStatus returns the status of the firmware install process -func firmwareInstallStatus(ctx context.Context, component, installVersion, taskID string, generic []firmwareInstallVerifierProvider) (status string, metadata Metadata, err error) { +func firmwareInstallStatus(ctx context.Context, installVersion, component, taskID string, generic []firmwareInstallVerifierProvider) (status string, metadata Metadata, err error) { var metadataLocal Metadata Loop: for _, elem := range generic { @@ -122,7 +122,7 @@ Loop: break Loop default: metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name) - status, vErr := elem.FirmwareInstallStatus(ctx, component, installVersion, taskID) + 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) @@ -138,7 +138,7 @@ Loop: } // FirmwareInstallStatusFromInterfaces pass through to library function -func FirmwareInstallStatusFromInterfaces(ctx context.Context, component, installVersion, taskID string, generic []interface{}) (status string, metadata Metadata, err error) { +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)} @@ -161,5 +161,5 @@ func FirmwareInstallStatusFromInterfaces(ctx context.Context, component, install ) } - return firmwareInstallStatus(ctx, component, installVersion, taskID, implementations) + return firmwareInstallStatus(ctx, installVersion, component, taskID, implementations) } diff --git a/providers/asrockrack/firmware.go b/providers/asrockrack/firmware.go index 374d3c29..1020b765 100644 --- a/providers/asrockrack/firmware.go +++ b/providers/asrockrack/firmware.go @@ -2,7 +2,6 @@ package asrockrack import ( "context" - "fmt" "io" "os" "strconv" @@ -40,14 +39,17 @@ func (a *ASRockRack) FirmwareInstall(ctx context.Context, component, applyAt str err = a.firmwareInstallBMC(ctx, reader, size) default: return "", errors.Wrap(bmclibErrs.ErrFirmwareInstall, "component unsupported: "+component) + } + if err != nil { + err = errors.Wrap(bmclibErrs.ErrFirmwareInstall, err.Error()) } return jobID, err } // FirmwareInstallStatus returns the status of the firmware install process, a bool value indicating if the component requires a reset -func (a *ASRockRack) FirmwareInstallStatus(ctx context.Context, component, installVersion, taskID string) (status string, err error) { +func (a *ASRockRack) FirmwareInstallStatus(ctx context.Context, installVersion, component, taskID string) (status string, err error) { switch component { case devices.SlugBIOS, devices.SlugBMC: return a.firmwareUpdateStatus(ctx, component, installVersion) @@ -64,28 +66,28 @@ func (a *ASRockRack) firmwareInstallBMC(ctx context.Context, reader io.Reader, f a.log.V(2).Info("info", "action", "set device to flash mode, takes a minute...", "step", "1/4") err = a.setFlashMode(ctx) if err != nil { - return fmt.Errorf("failed in step 1/4 - set device to flash mode: " + err.Error()) + return errors.Wrap(err, "failed in step 1/4 - set device to flash mode") } // 2. upload firmware image file a.log.V(2).Info("info", "action", "upload BMC firmware image", "step", "2/4") err = a.uploadFirmware(ctx, "api/maintenance/firmware", reader, fileSize) if err != nil { - return fmt.Errorf("failed in step 2/4 - upload BMC firmware image: " + err.Error()) + return errors.Wrap(err, "failed in step 2/4 - upload BMC firmware image") } // 3. BMC to verify the uploaded file err = a.verifyUploadedFirmware(ctx) a.log.V(2).Info("info", "action", "BMC verify uploaded firmware", "step", "3/4") if err != nil { - return fmt.Errorf("failed in step 3/4 - BMC verify uploaded firmware: " + err.Error()) + return errors.Wrap(err, "failed in step 3/4 - BMC verify uploaded firmware") } // 4. Run the upgrade - preserving current config a.log.V(2).Info("info", "action", "proceed with upgrade, preserve current configuration", "step", "4/4") err = a.upgradeBMC(ctx) if err != nil { - return fmt.Errorf("failed in step 4/4 - proceed with upgrade: " + err.Error()) + return errors.Wrap(err, "failed in step 4/4 - proceed with upgrade") } return nil @@ -99,21 +101,21 @@ func (a *ASRockRack) firmwareInstallBIOS(ctx context.Context, reader io.Reader, a.log.V(2).Info("info", "action", "upload BIOS firmware image", "step", "1/3") err = a.uploadFirmware(ctx, "api/asrr/maintenance/BIOS/firmware", reader, fileSize) if err != nil { - return fmt.Errorf("failed in step 1/3 - upload firmware image: " + err.Error()) + return errors.Wrap(err, "failed in step 1/3 - upload firmware image") } // 2. set update parameters to preserve configurations a.log.V(2).Info("info", "action", "set flash configuration", "step", "2/3") err = a.biosUpgradeConfiguration(ctx) if err != nil { - return fmt.Errorf("failed in step 2/3 - set flash configuration: " + err.Error()) + return errors.Wrap(err, "failed in step 2/3 - set flash configuration") } // 3. run upgrade a.log.V(2).Info("info", "action", "proceed with upgrade", "step", "3/3") err = a.upgradeBIOS(ctx) if err != nil { - return fmt.Errorf("failed in step 3/3 - proceed with upgrade: " + err.Error()) + return errors.Wrap(err, "failed in step 3/3 - proceed with upgrade") } return nil diff --git a/providers/redfish/firmware.go b/providers/redfish/firmware.go index e1098040..f7dfb6d2 100644 --- a/providers/redfish/firmware.go +++ b/providers/redfish/firmware.go @@ -103,7 +103,7 @@ func (c *Conn) FirmwareInstall(ctx context.Context, component, applyAt string, f } // FirmwareInstallStatus returns the status of the firmware install task queued -func (c *Conn) FirmwareInstallStatus(ctx context.Context, component, installVersion, taskID string) (state string, err error) { +func (c *Conn) FirmwareInstallStatus(ctx context.Context, installVersion, component, taskID string) (state string, err error) { vendor, _, err := c.DeviceVendorModel(ctx) if err != nil { return state, errors.Wrap(err, "unable to determine device vendor, model attributes") diff --git a/providers/redfish/inventory_collect.go b/providers/redfish/inventory_collect.go new file mode 100644 index 00000000..04349373 --- /dev/null +++ b/providers/redfish/inventory_collect.go @@ -0,0 +1,344 @@ +package redfish + +// defines various inventory collection helper methods + +// collectEnclosure collects Enclosure information +func (i *inventory) collectEnclosure(ch *redfish.Chassis, device *devices.Device) (err error) { + + e := &devices.Enclosure{ + ID: ch.ID, + Description: ch.Description, + Vendor: ch.Manufacturer, + Model: ch.Model, + ChassisType: string(ch.ChassisType), + Status: &devices.Status{ + Health: string(ch.Status.Health), + State: string(ch.Status.State), + }, + Firmware: &devices.Firmware{}, + } + + // include additional firmware attributes from redfish firmware inventory + i.firmwareAttributes(devices.SlugEnclosure, e.ID, e.Firmware) + + device.Enclosures = append(device.Enclosures, e) + + return nil +} + +// collectPSUs collects Power Supply Unit component information +func (i *inventory) collectPSUs(ch *redfish.Chassis, device *devices.Device) (err error) { + power, err := ch.Power() + if err != nil { + return err + } + + if power == nil { + return nil + } + + for _, psu := range power.PowerSupplies { + p := &devices.PSU{ + ID: psu.ID, + Description: psu.Name, + Vendor: psu.Manufacturer, + Model: psu.Model, + Serial: psu.SerialNumber, + PowerCapacityWatts: int64(psu.PowerCapacityWatts), + Status: &devices.Status{ + Health: string(psu.Status.Health), + State: string(psu.Status.State), + }, + Firmware: &devices.Firmware{ + Installed: psu.FirmwareVersion, + }, + } + + // include additional firmware attributes from redfish firmware inventory + i.firmwareAttributes(devices.SlugPSU, psu.ID, p.Firmware) + + device.PSUs = append(device.PSUs, p) + + } + return nil +} + +// collectTPMs collects Trusted Platform Module component information +func (i *inventory) collectTPMs(sys *redfish.ComputerSystem, device *devices.Device) (err error) { + for _, module := range sys.TrustedModules { + + tpm := &devices.TPM{ + InterfaceType: string(module.InterfaceType), + Firmware: &devices.Firmware{ + Installed: module.FirmwareVersion, + }, + Status: &devices.Status{ + State: string(module.Status.State), + Health: string(module.Status.Health), + }, + } + + // include additional firmware attributes from redfish firmware inventory + i.firmwareAttributes(devices.SlugTPM, "TPM", tpm.Firmware) + + device.TPMs = append(device.TPMs, tpm) + } + + return nil +} + +// collectNICs collects network interface component information +func (i *inventory) collectNICs(sys *redfish.ComputerSystem, device *devices.Device) (err error) { + // collect network interface information + nics, err := sys.NetworkInterfaces() + if err != nil { + return err + } + + // collect network ethernet interface information, these attributes are not available in NetworkAdapter, NetworkInterfaces + ethernetInterfaces, err := sys.EthernetInterfaces() + if err != nil { + return err + } + + for _, nic := range nics { + + // collect network interface adaptor information + adapter, err := nic.NetworkAdapter() + if err != nil { + return err + } + + n := &devices.NIC{ + ID: nic.ID, // "Id": "NIC.Slot.3", + Vendor: adapter.Manufacturer, + Model: adapter.Model, + Serial: adapter.SerialNumber, + Status: &devices.Status{ + State: string(nic.Status.State), + Health: string(nic.Status.Health), + }, + } + + if len(adapter.Controllers) > 0 { + n.Firmware = &devices.Firmware{ + Installed: adapter.Controllers[0].FirmwarePackageVersion, + } + } + + // populate mac addresses from ethernet interfaces + for _, ethInterface := range ethernetInterfaces { + // the ethernet interface includes the port and position number NIC.Slot.3-1-1 + if !strings.HasPrefix(ethInterface.ID, adapter.ID) { + continue + } + + // The ethernet interface description is + n.Description = ethInterface.Description + n.MacAddress = ethInterface.MACAddress + n.SpeedBits = int64(ethInterface.SpeedMbps*10 ^ 6) + } + + // include additional firmware attributes from redfish firmware inventory + i.firmwareAttributes(devices.SlugNIC, n.ID, n.Firmware) + + device.NICs = append(device.NICs, n) + } + + return nil +} + +func (i *inventory) collectBIOS(sys *redfish.ComputerSystem, device *devices.Device) (err error) { + bios, err := sys.Bios() + if err != nil { + return err + } + + device.BIOS = &devices.BIOS{ + Description: bios.Description, + Firmware: &devices.Firmware{ + Installed: sys.BIOSVersion, + }, + } + + // include additional firmware attributes from redfish firmware inventory + i.firmwareAttributes(devices.SlugBIOS, "BIOS", device.BIOS.Firmware) + + return nil +} + +// collectDrives collects drive component information +func (i *inventory) collectDrives(sys *redfish.ComputerSystem, device *devices.Device) (err error) { + storage, err := sys.Storage() + if err != nil { + return err + } + + for _, member := range storage { + if member.DrivesCount == 0 { + continue + } + + drives, err := member.Drives() + if err != nil { + return err + } + + for _, drive := range drives { + d := &devices.Drive{ + ID: drive.ID, + ProductName: drive.Model, + Type: string(drive.MediaType), + Description: drive.Description, + Serial: drive.SerialNumber, + StorageController: member.ID, + Vendor: drive.Manufacturer, + Model: drive.Model, + Protocol: string(drive.Protocol), + CapacityBytes: drive.CapacityBytes, + CapableSpeedGbps: int64(drive.CapableSpeedGbs), + NegotiatedSpeedGbps: int64(drive.NegotiatedSpeedGbs), + BlockSizeBytes: int64(drive.BlockSizeBytes), + Firmware: &devices.Firmware{ + Installed: drive.Revision, + }, + Status: &devices.Status{ + Health: string(drive.Status.Health), + State: string(drive.Status.State), + }, + } + + // include additional firmware attributes from redfish firmware inventory + i.firmwareAttributes("Disk", drive.ID, d.Firmware) + + device.Drives = append(device.Drives, d) + + } + + } + + return nil +} + +// collectStorageControllers populates the device with Storage controller component attributes +func (i *inventory) collectStorageControllers(sys *redfish.ComputerSystem, device *devices.Device) (err error) { + storage, err := sys.Storage() + if err != nil { + return err + } + + for _, member := range storage { + for _, controller := range member.StorageControllers { + + c := &devices.StorageController{ + ID: controller.ID, + Description: controller.Name, + Vendor: controller.Manufacturer, + Model: controller.PartNumber, + Serial: controller.SerialNumber, + SpeedGbps: int64(controller.SpeedGbps), + Status: &devices.Status{ + Health: string(controller.Status.Health), + State: string(controller.Status.State), + }, + Firmware: &devices.Firmware{ + Installed: controller.FirmwareVersion, + }, + } + + // include additional firmware attributes from redfish firmware inventory + i.firmwareAttributes(c.Description, c.ID, c.Firmware) + + device.StorageControllers = append(device.StorageControllers, c) + } + + } + + return nil +} + +// collectCPUs populates the device with CPU component attributes +func (i *inventory) collectCPUs(sys *redfish.ComputerSystem, device *devices.Device) (err error) { + procs, err := sys.Processors() + if err != nil { + return err + } + + for _, proc := range procs { + if proc.ProcessorType != "CPU" { + // TODO: handle this case + continue + } + + device.CPUs = append(device.CPUs, &devices.CPU{ + ID: proc.ID, + Description: proc.Description, + Vendor: proc.Manufacturer, + Model: proc.Model, + Architecture: string(proc.ProcessorArchitecture), + Serial: "", + Slot: proc.Socket, + ClockSpeedHz: int64(proc.MaxSpeedMHz), + Cores: proc.TotalCores, + Threads: proc.TotalThreads, + Status: &devices.Status{ + Health: string(proc.Status.Health), + State: string(proc.Status.State), + }, + Firmware: &devices.Firmware{ + Installed: proc.ProcessorID.MicrocodeInfo, + }, + }) + } + + return nil +} + +// collectDIMMs populates the device with memory component attributes +func (i *inventory) collectDIMMs(sys *redfish.ComputerSystem, device *devices.Device) (err error) { + dimms, err := sys.Memory() + if err != nil { + return err + } + + for _, dimm := range dimms { + device.Memory = append(device.Memory, &devices.Memory{ + Description: dimm.Description, + Slot: dimm.ID, + Type: string(dimm.MemoryType), + Vendor: dimm.Manufacturer, + Model: "", + Serial: dimm.SerialNumber, + SizeBytes: int64(dimm.VolatileSizeMiB), + FormFactor: "", + PartNumber: dimm.PartNumber, + ClockSpeedHz: int64(dimm.OperatingSpeedMhz), + Status: &devices.Status{ + Health: string(dimm.Status.Health), + State: string(dimm.Status.State), + }, + }) + } + + return nil +} + +// collecCPLDs populates the device with CPLD component attributes +func (i *inventory) collectCPLDs(device *devices.Device) (err error) { + + cpld := &devices.CPLD{ + Vendor: device.Vendor, + Model: device.Model, + Firmware: &devices.Firmware{Metadata: make(map[string]string)}, + } + + i.firmwareAttributes(devices.SlugCPLD, "", cpld.Firmware) + name, exists := cpld.Firmware.Metadata["name"] + if exists { + cpld.Description = name + } + + device.CPLDs = []*devices.CPLD{cpld} + + return nil +} From da0793ac66a6bd8dec55ecf696e376dd58bb90bc Mon Sep 17 00:00:00 2001 From: Joel Rebello Date: Mon, 25 Apr 2022 16:08:51 +0200 Subject: [PATCH 7/9] redfish/inventory: split out helper methods --- go.mod | 7 +- go.sum | 19 +- providers/redfish/inventory.go | 341 ------------------------- providers/redfish/inventory_collect.go | 7 + 4 files changed, 22 insertions(+), 352 deletions(-) diff --git a/go.mod b/go.mod index f71eac81..6ba2f3ce 100644 --- a/go.mod +++ b/go.mod @@ -25,9 +25,10 @@ require ( github.com/spf13/viper v1.7.1 github.com/stmcginnis/gofish v0.12.1-0.20220311113027-6072260f4c8d github.com/stretchr/testify v1.7.0 - golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670 - golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 - golang.org/x/text v0.3.5 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 + golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f + golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect + golang.org/x/text v0.3.7 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 gopkg.in/go-playground/validator.v9 v9.31.0 diff --git a/go.sum b/go.sum index 3e9ab8f5..56bf5d6c 100644 --- a/go.sum +++ b/go.sum @@ -227,8 +227,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670 h1:gzMM0EjIYiRmJI3+jBdFuoynZlpxa2JQZsolKu09BXo= -golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -262,8 +262,8 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -288,17 +288,20 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 h1:C+AwYEtBp/VQwoLntUmQ/yx3MS9vmZaKNdw5eOpoQe8= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/providers/redfish/inventory.go b/providers/redfish/inventory.go index d12a44bb..c324fa3c 100644 --- a/providers/redfish/inventory.go +++ b/providers/redfish/inventory.go @@ -256,347 +256,6 @@ func (i *inventory) systemAttributes(device *devices.Device) (err error) { return nil } -// collectEnclosure collects Enclosure information -func (i *inventory) collectEnclosure(ch *redfish.Chassis, device *devices.Device) (err error) { - - e := &devices.Enclosure{ - ID: ch.ID, - Description: ch.Description, - Vendor: ch.Manufacturer, - Model: ch.Model, - ChassisType: string(ch.ChassisType), - Status: &devices.Status{ - Health: string(ch.Status.Health), - State: string(ch.Status.State), - }, - Firmware: &devices.Firmware{}, - } - - // include additional firmware attributes from redfish firmware inventory - i.firmwareAttributes(devices.SlugEnclosure, e.ID, e.Firmware) - - device.Enclosures = append(device.Enclosures, e) - - return nil -} - -// collectPSUs collects Power Supply Unit component information -func (i *inventory) collectPSUs(ch *redfish.Chassis, device *devices.Device) (err error) { - power, err := ch.Power() - if err != nil { - return err - } - - if power == nil { - return nil - } - - for _, psu := range power.PowerSupplies { - p := &devices.PSU{ - ID: psu.ID, - Description: psu.Name, - Vendor: psu.Manufacturer, - Model: psu.Model, - Serial: psu.SerialNumber, - PowerCapacityWatts: int64(psu.PowerCapacityWatts), - Status: &devices.Status{ - Health: string(psu.Status.Health), - State: string(psu.Status.State), - }, - Firmware: &devices.Firmware{ - Installed: psu.FirmwareVersion, - }, - } - - // include additional firmware attributes from redfish firmware inventory - i.firmwareAttributes(devices.SlugPSU, psu.ID, p.Firmware) - - device.PSUs = append(device.PSUs, p) - - } - return nil -} - -// collectTPMs collects Trusted Platform Module component information -func (i *inventory) collectTPMs(sys *redfish.ComputerSystem, device *devices.Device) (err error) { - for _, module := range sys.TrustedModules { - - tpm := &devices.TPM{ - InterfaceType: string(module.InterfaceType), - Firmware: &devices.Firmware{ - Installed: module.FirmwareVersion, - }, - Status: &devices.Status{ - State: string(module.Status.State), - Health: string(module.Status.Health), - }, - } - - // include additional firmware attributes from redfish firmware inventory - i.firmwareAttributes(devices.SlugTPM, "TPM", tpm.Firmware) - - device.TPMs = append(device.TPMs, tpm) - } - - return nil -} - -// collectNICs collects network interface component information -func (i *inventory) collectNICs(sys *redfish.ComputerSystem, device *devices.Device) (err error) { - // collect network interface information - nics, err := sys.NetworkInterfaces() - if err != nil { - return err - } - - // collect network ethernet interface information, these attributes are not available in NetworkAdapter, NetworkInterfaces - ethernetInterfaces, err := sys.EthernetInterfaces() - if err != nil { - return err - } - - for _, nic := range nics { - - // collect network interface adaptor information - adapter, err := nic.NetworkAdapter() - if err != nil { - return err - } - - n := &devices.NIC{ - ID: nic.ID, // "Id": "NIC.Slot.3", - Vendor: adapter.Manufacturer, - Model: adapter.Model, - Serial: adapter.SerialNumber, - Status: &devices.Status{ - State: string(nic.Status.State), - Health: string(nic.Status.Health), - }, - } - - if len(adapter.Controllers) > 0 { - n.Firmware = &devices.Firmware{ - Installed: adapter.Controllers[0].FirmwarePackageVersion, - } - } - - // populate mac addresses from ethernet interfaces - for _, ethInterface := range ethernetInterfaces { - // the ethernet interface includes the port and position number NIC.Slot.3-1-1 - if !strings.HasPrefix(ethInterface.ID, adapter.ID) { - continue - } - - // The ethernet interface description is - n.Description = ethInterface.Description - n.MacAddress = ethInterface.MACAddress - n.SpeedBits = int64(ethInterface.SpeedMbps*10 ^ 6) - } - - // include additional firmware attributes from redfish firmware inventory - i.firmwareAttributes(devices.SlugNIC, n.ID, n.Firmware) - - device.NICs = append(device.NICs, n) - } - - return nil -} - -func (i *inventory) collectBIOS(sys *redfish.ComputerSystem, device *devices.Device) (err error) { - bios, err := sys.Bios() - if err != nil { - return err - } - - device.BIOS = &devices.BIOS{ - Description: bios.Description, - Firmware: &devices.Firmware{ - Installed: sys.BIOSVersion, - }, - } - - // include additional firmware attributes from redfish firmware inventory - i.firmwareAttributes(devices.SlugBIOS, "BIOS", device.BIOS.Firmware) - - return nil -} - -// collectDrives collects drive component information -func (i *inventory) collectDrives(sys *redfish.ComputerSystem, device *devices.Device) (err error) { - storage, err := sys.Storage() - if err != nil { - return err - } - - for _, member := range storage { - if member.DrivesCount == 0 { - continue - } - - drives, err := member.Drives() - if err != nil { - return err - } - - for _, drive := range drives { - d := &devices.Drive{ - ID: drive.ID, - ProductName: drive.Model, - Type: string(drive.MediaType), - Description: drive.Description, - Serial: drive.SerialNumber, - StorageController: member.ID, - Vendor: drive.Manufacturer, - Model: drive.Model, - Protocol: string(drive.Protocol), - CapacityBytes: drive.CapacityBytes, - CapableSpeedGbps: int64(drive.CapableSpeedGbs), - NegotiatedSpeedGbps: int64(drive.NegotiatedSpeedGbs), - BlockSizeBytes: int64(drive.BlockSizeBytes), - Firmware: &devices.Firmware{ - Installed: drive.Revision, - }, - Status: &devices.Status{ - Health: string(drive.Status.Health), - State: string(drive.Status.State), - }, - } - - // include additional firmware attributes from redfish firmware inventory - i.firmwareAttributes("Disk", drive.ID, d.Firmware) - - device.Drives = append(device.Drives, d) - - } - - } - - return nil -} - -// collectStorageControllers populates the device with Storage controller component attributes -func (i *inventory) collectStorageControllers(sys *redfish.ComputerSystem, device *devices.Device) (err error) { - storage, err := sys.Storage() - if err != nil { - return err - } - - for _, member := range storage { - for _, controller := range member.StorageControllers { - - c := &devices.StorageController{ - ID: controller.ID, - Description: controller.Name, - Vendor: controller.Manufacturer, - Model: controller.PartNumber, - Serial: controller.SerialNumber, - SpeedGbps: int64(controller.SpeedGbps), - Status: &devices.Status{ - Health: string(controller.Status.Health), - State: string(controller.Status.State), - }, - Firmware: &devices.Firmware{ - Installed: controller.FirmwareVersion, - }, - } - - // include additional firmware attributes from redfish firmware inventory - i.firmwareAttributes(c.Description, c.ID, c.Firmware) - - device.StorageControllers = append(device.StorageControllers, c) - } - - } - - return nil -} - -// collectCPUs populates the device with CPU component attributes -func (i *inventory) collectCPUs(sys *redfish.ComputerSystem, device *devices.Device) (err error) { - procs, err := sys.Processors() - if err != nil { - return err - } - - for _, proc := range procs { - if proc.ProcessorType != "CPU" { - // TODO: handle this case - continue - } - - device.CPUs = append(device.CPUs, &devices.CPU{ - ID: proc.ID, - Description: proc.Description, - Vendor: proc.Manufacturer, - Model: proc.Model, - Architecture: string(proc.ProcessorArchitecture), - Serial: "", - Slot: proc.Socket, - ClockSpeedHz: int64(proc.MaxSpeedMHz), - Cores: proc.TotalCores, - Threads: proc.TotalThreads, - Status: &devices.Status{ - Health: string(proc.Status.Health), - State: string(proc.Status.State), - }, - Firmware: &devices.Firmware{ - Installed: proc.ProcessorID.MicrocodeInfo, - }, - }) - } - - return nil -} - -// collectDIMMs populates the device with memory component attributes -func (i *inventory) collectDIMMs(sys *redfish.ComputerSystem, device *devices.Device) (err error) { - dimms, err := sys.Memory() - if err != nil { - return err - } - - for _, dimm := range dimms { - device.Memory = append(device.Memory, &devices.Memory{ - Description: dimm.Description, - Slot: dimm.ID, - Type: string(dimm.MemoryType), - Vendor: dimm.Manufacturer, - Model: "", - Serial: dimm.SerialNumber, - SizeBytes: int64(dimm.VolatileSizeMiB), - FormFactor: "", - PartNumber: dimm.PartNumber, - ClockSpeedHz: int64(dimm.OperatingSpeedMhz), - Status: &devices.Status{ - Health: string(dimm.Status.Health), - State: string(dimm.Status.State), - }, - }) - } - - return nil -} - -// collecCPLDs populates the device with CPLD component attributes -func (i *inventory) collectCPLDs(device *devices.Device) (err error) { - - cpld := &devices.CPLD{ - Vendor: device.Vendor, - Model: device.Model, - Firmware: &devices.Firmware{Metadata: make(map[string]string)}, - } - - i.firmwareAttributes(devices.SlugCPLD, "", cpld.Firmware) - name, exists := cpld.Firmware.Metadata["name"] - if exists { - cpld.Description = name - } - - device.CPLDs = []*devices.CPLD{cpld} - - return nil -} - // firmwareInventory looks up the redfish inventory for objects that // match - 1. slug, 2. id // and returns the intalled or previous firmware for objects that matched diff --git a/providers/redfish/inventory_collect.go b/providers/redfish/inventory_collect.go index 04349373..678d05c5 100644 --- a/providers/redfish/inventory_collect.go +++ b/providers/redfish/inventory_collect.go @@ -1,5 +1,12 @@ package redfish +import ( + "strings" + + "github.com/bmc-toolbox/bmclib/devices" + "github.com/stmcginnis/gofish/redfish" +) + // defines various inventory collection helper methods // collectEnclosure collects Enclosure information From a940d428578aedebd40fa1149c0b748a072906bd Mon Sep 17 00:00:00 2001 From: Joel Rebello Date: Tue, 26 Apr 2022 11:54:50 +0200 Subject: [PATCH 8/9] providers/asrockrack: use structured logging, purge debug statements --- examples/v1/firmware/firmware.go | 19 ++++++------------ providers/asrockrack/firmware.go | 33 ++++++++++++++------------------ 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/examples/v1/firmware/firmware.go b/examples/v1/firmware/firmware.go index 70551673..dbe19f32 100644 --- a/examples/v1/firmware/firmware.go +++ b/examples/v1/firmware/firmware.go @@ -2,7 +2,6 @@ package main import ( "context" - "fmt" "log" "os" "time" @@ -18,13 +17,13 @@ func main() { defer cancel() // set BMC parameters here - host := "" + host := "10.247.150.161" port := "" - user := "root" - pass := "" + user := "admin" + pass := "RmrJ56BFUarn6g" l := logrus.New() - l.Level = logrus.DebugLevel + l.Level = logrus.TraceLevel logger := logrusr.New(l) if host == "" || user == "" || pass == "" { @@ -40,23 +39,17 @@ func main() { defer cl.Close(ctx) - for _, update := range []string{"/tmp/iDRAC-with-Lifecycle-Controller_Firmware_F87RP_WN64_5.00.00.00_A00.EXE"} { + for _, update := range []string{"/tmp/E6D4INL2.09C.ima"} { fh, err := os.Open(update) if err != nil { log.Fatal(err) } - taskID, err := cl.FirmwareInstall(ctx, devices.SlugBMC, devices.FirmwareApplyOnReset, true, fh) + _, err = cl.FirmwareInstall(ctx, devices.SlugBMC, devices.FirmwareApplyOnReset, true, fh) if err != nil { l.Error(err) } - state, err := cl.FirmwareInstallStatus(ctx, "", taskID, "5.00.00.00") - if err != nil { - log.Fatal(err) - } - - fmt.Printf("state: %s\n", state) } } diff --git a/providers/asrockrack/firmware.go b/providers/asrockrack/firmware.go index 1020b765..74badd9a 100644 --- a/providers/asrockrack/firmware.go +++ b/providers/asrockrack/firmware.go @@ -4,7 +4,6 @@ import ( "context" "io" "os" - "strconv" "strings" "github.com/pkg/errors" @@ -26,7 +25,7 @@ func (a *ASRockRack) FirmwareInstall(ctx context.Context, component, applyAt str if file, ok := reader.(*os.File); ok { finfo, err := file.Stat() if err != nil { - a.log.V(2).Info("warn", "unable to determine file size: "+err.Error()) + a.log.V(2).Error(err, "unable to determine file size") } size = finfo.Size() @@ -63,14 +62,14 @@ func (a *ASRockRack) firmwareInstallBMC(ctx context.Context, reader io.Reader, f var err error // 1. set the device to flash mode - prepares the flash - a.log.V(2).Info("info", "action", "set device to flash mode, takes a minute...", "step", "1/4") + a.log.V(2).WithValues("step", "1/4").Info("set device to flash mode, takes a minute...") err = a.setFlashMode(ctx) if err != nil { return errors.Wrap(err, "failed in step 1/4 - set device to flash mode") } // 2. upload firmware image file - a.log.V(2).Info("info", "action", "upload BMC firmware image", "step", "2/4") + a.log.V(2).WithValues("step", "2/4").Info("upload BMC firmware image") err = a.uploadFirmware(ctx, "api/maintenance/firmware", reader, fileSize) if err != nil { return errors.Wrap(err, "failed in step 2/4 - upload BMC firmware image") @@ -78,16 +77,16 @@ func (a *ASRockRack) firmwareInstallBMC(ctx context.Context, reader io.Reader, f // 3. BMC to verify the uploaded file err = a.verifyUploadedFirmware(ctx) - a.log.V(2).Info("info", "action", "BMC verify uploaded firmware", "step", "3/4") + a.log.V(2).WithValues("step", "3/4").Info("verify uploaded BMC firmware") if err != nil { - return errors.Wrap(err, "failed in step 3/4 - BMC verify uploaded firmware") + return errors.Wrap(err, "failed in step 3/4 - verify uploaded BMC firmware") } // 4. Run the upgrade - preserving current config - a.log.V(2).Info("info", "action", "proceed with upgrade, preserve current configuration", "step", "4/4") + a.log.V(2).WithValues("step", "4/4").Info("proceed with BMC firmware install, preserve current configuration") err = a.upgradeBMC(ctx) if err != nil { - return errors.Wrap(err, "failed in step 4/4 - proceed with upgrade") + return errors.Wrap(err, "failed in step 4/4 - proceed with BMC firmware install") } return nil @@ -98,24 +97,24 @@ func (a *ASRockRack) firmwareInstallBIOS(ctx context.Context, reader io.Reader, var err error // 1. upload firmware image file - a.log.V(2).Info("info", "action", "upload BIOS firmware image", "step", "1/3") + a.log.V(2).WithValues("step", "1/3").Info("upload BIOS firmware image") err = a.uploadFirmware(ctx, "api/asrr/maintenance/BIOS/firmware", reader, fileSize) if err != nil { - return errors.Wrap(err, "failed in step 1/3 - upload firmware image") + return errors.Wrap(err, "failed in step 1/3 - upload BIOS firmware image") } // 2. set update parameters to preserve configurations - a.log.V(2).Info("info", "action", "set flash configuration", "step", "2/3") + a.log.V(2).WithValues("step", "2/3").Info("set BIOS preserve flash configuration") err = a.biosUpgradeConfiguration(ctx) if err != nil { return errors.Wrap(err, "failed in step 2/3 - set flash configuration") } // 3. run upgrade - a.log.V(2).Info("info", "action", "proceed with upgrade", "step", "3/3") + a.log.V(2).WithValues("step", "3/3").Info("proceed with BIOS firmware install") err = a.upgradeBIOS(ctx) if err != nil { - return errors.Wrap(err, "failed in step 3/3 - proceed with upgrade") + return errors.Wrap(err, "failed in step 3/3 - proceed with BIOS firmware install") } return nil @@ -123,10 +122,6 @@ func (a *ASRockRack) firmwareInstallBIOS(ctx context.Context, reader io.Reader, // firmwareUpdateBIOSStatus returns the BIOS firmware install status func (a *ASRockRack) firmwareUpdateStatus(ctx context.Context, component string, installVersion string) (status string, err error) { - // TODO: purge debug logging - os.Setenv("BMCLIB_LOG_LEVEL", "trace") - defer os.Unsetenv("BMCLIB_LOG_LEVEL") - var endpoint string switch component { case devices.SlugBIOS: @@ -142,7 +137,7 @@ func (a *ASRockRack) firmwareUpdateStatus(ctx context.Context, component string, // once an update completes/fails this endpoint will return 500 progress, err := a.flashProgress(ctx, endpoint) if err != nil { - a.log.V(3).Info("warn", "bmc query for install progress returned error: "+err.Error()) + a.log.V(3).Error(err, "bmc query for install progress returned error: "+err.Error()) } if progress != nil { @@ -152,7 +147,7 @@ func (a *ASRockRack) firmwareUpdateStatus(ctx context.Context, component string, case 2: return devices.FirmwareInstallComplete, nil default: - a.log.V(3).Info("warn", "bmc returned unknown flash progress state: "+strconv.Itoa(progress.State)) + a.log.V(3).WithValues("state", progress.State).Info("warn", "bmc returned unknown flash progress state") } } From 37fcdfd7433d6aba5ff41a02fd9c4e4a6f09bbb1 Mon Sep 17 00:00:00 2001 From: Joel Rebello Date: Tue, 26 Apr 2022 15:34:05 +0200 Subject: [PATCH 9/9] examples/v1/firmware: cleanup --- examples/v1/firmware/firmware.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/v1/firmware/firmware.go b/examples/v1/firmware/firmware.go index dbe19f32..f46070ab 100644 --- a/examples/v1/firmware/firmware.go +++ b/examples/v1/firmware/firmware.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "log" "os" "time" @@ -17,10 +18,10 @@ func main() { defer cancel() // set BMC parameters here - host := "10.247.150.161" + host := "" port := "" - user := "admin" - pass := "RmrJ56BFUarn6g" + user := "" + pass := "" l := logrus.New() l.Level = logrus.TraceLevel @@ -42,14 +43,20 @@ func main() { for _, update := range []string{"/tmp/E6D4INL2.09C.ima"} { fh, err := os.Open(update) if err != nil { - log.Fatal(err) + l.Fatal(err) } - _, err = cl.FirmwareInstall(ctx, devices.SlugBMC, devices.FirmwareApplyOnReset, true, fh) + taskID, err := cl.FirmwareInstall(ctx, devices.SlugBMC, devices.FirmwareApplyOnReset, true, fh) if err != nil { - l.Error(err) + l.Fatal(err) } + state, err := cl.FirmwareInstallStatus(ctx, "", taskID, "5.00.00.00") + if err != nil { + l.Fatal(err) + } + + fmt.Printf("taskID: %s, state: %s\n", taskID, state) } }