diff --git a/exporter/exporter.go b/exporter/exporter.go index 9fd0cb5..6f4d058 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -102,8 +102,9 @@ type SystemEndpoints struct { } type DriveEndpoints struct { - logicalDriveURLs []string - physicalDriveURLs []string + arrayControllerURLs []string + logicalDriveURLs []string + physicalDriveURLs []string } type Excludes map[string]interface{} @@ -305,11 +306,24 @@ func NewExporter(ctx context.Context, target, uri, profile, model string, exclud } } - log.Debug("drive endpoints response", zap.Strings("logical_drive_endpoints", driveEndpointsResp.logicalDriveURLs), + if len(sysEndpoints.storageController) == 0 && ss == "" { + driveEndpointsResp, err = getAllDriveEndpoints(ctx, exp.url, exp.url+sysEndpoints.systems[0]+"Storage/", target, retryClient) + if err != nil { + log.Error("error when getting drive endpoints", zap.Error(err), zap.Any("trace_id", ctx.Value("traceID"))) + return nil, err + } + } + + log.Debug("drive endpoints response", zap.Strings("array_controller_endpoints", driveEndpointsResp.arrayControllerURLs), + zap.Strings("logical_drive_endpoints", driveEndpointsResp.logicalDriveURLs), zap.Strings("physical_drive_endpoints", driveEndpointsResp.physicalDriveURLs), zap.Any("trace_id", ctx.Value("traceID"))) - // Loop through logicalDriveURLs, physicalDriveURLs, and nvmeDriveURLs and append each URL to the tasks pool + // Loop through arrayControllerURLs, logicalDriveURLs, physicalDriveURLs, and nvmeDriveURLs and append each URL to the tasks pool + for _, url := range driveEndpointsResp.arrayControllerURLs { + tasks = append(tasks, pool.NewTask(common.Fetch(exp.url+url, target, profile, retryClient), exp.url+url, handle(&exp, STORAGE_CONTROLLER))) + } + for _, url := range driveEndpointsResp.logicalDriveURLs { tasks = append(tasks, pool.NewTask(common.Fetch(exp.url+url, target, profile, retryClient), exp.url+url, handle(&exp, LOGICALDRIVE))) } diff --git a/exporter/handlers.go b/exporter/handlers.go index 36cff69..916cb1c 100644 --- a/exporter/handlers.go +++ b/exporter/handlers.go @@ -291,10 +291,22 @@ func (e *Exporter) exportLogicalDriveMetrics(body []byte) error { var state float64 var dllogical oem.LogicalDriveMetrics var dllogicaldrive = (*e.DeviceMetrics)["logicalDriveMetrics"] + var ldName string + var raidType string + var volIdentifier string err := json.Unmarshal(body, &dllogical) if err != nil { return fmt.Errorf("Error Unmarshalling LogicalDriveMetrics - " + err.Error()) } + if dllogical.Raid == "" { + ldName = dllogical.DisplayName + raidType = dllogical.RaidType + volIdentifier = dllogical.Identifiers[0].DurableName + } else { + ldName = dllogical.LogicalDriveName + raidType = dllogical.Raid + volIdentifier = dllogical.VolumeUniqueIdentifier + } // Check physical drive is enabled then check status and convert string to numeric values if dllogical.Status.State == "Enabled" { if dllogical.Status.Health == "OK" { @@ -306,7 +318,7 @@ func (e *Exporter) exportLogicalDriveMetrics(body []byte) error { state = DISABLED } - (*dllogicaldrive)["raidStatus"].WithLabelValues(dllogical.Name, e.ChassisSerialNumber, e.Model, dllogical.LogicalDriveName, dllogical.VolumeUniqueIdentifier, dllogical.Raid).Set(state) + (*dllogicaldrive)["raidStatus"].WithLabelValues(dllogical.Name, e.ChassisSerialNumber, e.Model, ldName, volIdentifier, raidType).Set(state) return nil } @@ -381,6 +393,17 @@ func (e *Exporter) exportStorageControllerMetrics(body []byte) error { } } + if len(scm.StorageController.StorageController) == 0 { + if scm.Status.State == "Enabled" { + if scm.Status.Health == "OK" { + state = OK + } else { + state = BAD + } + (*drv)["storageControllerStatus"].WithLabelValues(scm.Name, e.ChassisSerialNumber, e.Model, scm.ControllerFirmware.FirmwareVersion, scm.Model).Set(state) + } + } + return nil } @@ -395,7 +418,10 @@ func (e *Exporter) exportMemorySummaryMetrics(body []byte) error { return fmt.Errorf("Error Unmarshalling MemorySummaryMetrics - " + err.Error()) } // Check memory status and convert string to numeric values - if dlm.MemorySummary.Status.HealthRollup == "OK" { + // Ignore memory summary if status is not present + if dlm.MemorySummary.Status.HealthRollup == "" { + return nil + } else if dlm.MemorySummary.Status.HealthRollup == "OK" { state = OK } else { state = BAD @@ -566,6 +592,12 @@ func (e *Exporter) exportProcessorMetrics(body []byte) error { case int: totCores = strconv.Itoa(pm.TotalCores.(int)) } + + // Ignore metrics if processor is absent + if pm.Status.State == "Absent" { + return nil + } + if pm.Status.Health == "OK" { state = OK } else { diff --git a/exporter/helpers.go b/exporter/helpers.go index c2b2002..7fb8146 100644 --- a/exporter/helpers.go +++ b/exporter/helpers.go @@ -334,6 +334,43 @@ func getAllDriveEndpoints(ctx context.Context, fqdn, initialUrl, host string, cl return driveEndpoints, err } + // This if condition is for servers with iLO6. Gather metrics only from controllers with drives + // /redfish/v1/Systems/XXXX/Storage/XXXXX/ + if len(arrayCtrlResp.StorageDrives) > 0 { + for _, member := range arrayCtrlResp.StorageDrives { + driveEndpoints.physicalDriveURLs = append(driveEndpoints.physicalDriveURLs, appendSlash(member.URL)) + } + + // If Volumes are present, parse volumes endpoint until all urls are found + if arrayCtrlResp.Volumes.URL != "" { + volumeOutput, err := getDriveEndpoint(fqdn+arrayCtrlResp.Volumes.URL, host, client) + if err != nil { + log.Error("api call "+fqdn+arrayCtrlResp.Volumes.URL+" failed", zap.Error(err), zap.Any("trace_id", ctx.Value("traceID"))) + return driveEndpoints, err + } + + for _, member := range volumeOutput.Members { + driveEndpoints.logicalDriveURLs = append(driveEndpoints.logicalDriveURLs, appendSlash(member.URL)) + } + } + + if arrayCtrlResp.Controllers.URL != "" { + controllerOutput, err := getDriveEndpoint(fqdn+arrayCtrlResp.Controllers.URL, host, client) + if err != nil { + log.Error("api call "+fqdn+arrayCtrlResp.Controllers.URL+" failed", zap.Error(err), zap.Any("trace_id", ctx.Value("traceID"))) + return driveEndpoints, err + } + + for _, member := range controllerOutput.Members { + driveEndpoints.arrayControllerURLs = append(driveEndpoints.arrayControllerURLs, appendSlash(member.URL)) + } + } + } else if arrayCtrlResp.LinksUpper.PhysicalDrives.URL != "" || arrayCtrlResp.LinksLower.PhysicalDrives.URL != "" { + // /redfish/v1/Systems/XXXX/SmartStorage/ArrayControllers/X/ + driveEndpoints.arrayControllerURLs = append(driveEndpoints.arrayControllerURLs, appendSlash(member.URL)) + } + + // all other servers apart from iLO6 // If LogicalDrives is present, parse logical drive endpoint until all urls are found if arrayCtrlResp.LinksUpper.LogicalDrives.URL != "" { logicalDriveOutput, err := getDriveEndpoint(fqdn+arrayCtrlResp.LinksUpper.LogicalDrives.URL, host, client) diff --git a/oem/drive.go b/oem/drive.go index 05a1579..88814bc 100644 --- a/oem/drive.go +++ b/oem/drive.go @@ -44,17 +44,24 @@ type NVMeDriveMetrics struct { // Logical Drives // /redfish/v1/Systems/X/SmartStorage/ArrayControllers/X/LogicalDrives/X/ type LogicalDriveMetrics struct { - Id string `json:"Id"` - CapacityMiB int `json:"CapacityMiB"` - Description string `json:"Description"` - InterfaceType string `json:"InterfaceType"` - LogicalDriveName string `json:"LogicalDriveName"` - LogicalDriveNumber int `json:"LogicalDriveNumber"` - Name string `json:"Name"` - Raid string `json:"Raid"` - Status Status `json:"Status"` - StripeSizebytes int `json:"StripeSizebytes"` - VolumeUniqueIdentifier string `json:"VolumeUniqueIdentifier"` + Id string `json:"Id"` + CapacityMiB int `json:"CapacityMiB"` + Description string `json:"Description"` + DisplayName string `json:"DisplayName"` + InterfaceType string `json:"InterfaceType"` + Identifiers []Identifiers `json:"Identifiers"` + LogicalDriveName string `json:"LogicalDriveName"` + LogicalDriveNumber int `json:"LogicalDriveNumber"` + Name string `json:"Name"` + Raid string `json:"Raid"` + RaidType string `json:"RAIDType"` + Status Status `json:"Status"` + StripeSizebytes int `json:"StripeSizebytes"` + VolumeUniqueIdentifier string `json:"VolumeUniqueIdentifier"` +} + +type Identifiers struct { + DurableName string `json:"DurableName"` } // Disk Drives @@ -116,10 +123,14 @@ func (w *LocationWrapper) UnmarshalJSON(data []byte) error { // /redfish/v1/Systems/X/SmartStorage/ArrayControllers/ for Logical and Physical Drives // /redfish/v1/Chassis/X/Drives/ for NVMe Drive(s) type GenericDrive struct { - Members []Members `json:"Members,omitempty"` - LinksUpper LinksUpper `json:"Links,omitempty"` - LinksLower LinksLower `json:"links,omitempty"` - MembersCount int `json:"Members@odata.count,omitempty"` + Members []Members `json:"Members,omitempty"` + LinksUpper LinksUpper `json:"Links,omitempty"` + LinksLower LinksLower `json:"links,omitempty"` + MembersCount int `json:"Members@odata.count,omitempty"` + DriveCount int `json:"Drives@odata.count,omitempty"` + StorageDrives []Link `json:"Drives,omitempty"` + Volumes Link `json:"Volumes,omitempty"` + Controllers Link `json:"Controllers,omitempty"` } type Members struct { diff --git a/oem/storage_ctrl.go b/oem/storage_ctrl.go index 7fc922a..1ee908c 100644 --- a/oem/storage_ctrl.go +++ b/oem/storage_ctrl.go @@ -23,9 +23,12 @@ import ( // /redfish/v1/Systems/WZPXXXXX/Storage/MRAID type StorageControllerMetrics struct { - Name string `json:"Name"` - StorageController StorageControllerWrapper `json:"StorageControllers"` - Drives []Drive `json:"Drives"` + Name string `json:"Name"` + StorageController StorageControllerWrapper `json:"StorageControllers"` + Drives []Drive `json:"Drives"` + Status Status `json:"Status"` + Model string `json:"Model"` + ControllerFirmware FirmwareVersionWrapper `json:"FirmwareVersion"` } // StorageController contains status metadata of the C220 chassis storage controller @@ -62,6 +65,28 @@ func (w *StorageControllerWrapper) UnmarshalJSON(data []byte) error { return nil } +type FirmwareVersionWrapper struct { + FirmwareVersion string +} + +func (w *FirmwareVersionWrapper) UnmarshalJSON(data []byte) error { + // because of a change in output between firmware versions we need to account for this + // firmware version can either be a string or a json object + var CurrentFirmware struct { + Current struct { + VersionString string `json:"VersionString"` + } `json:"Current"` + } + err := json.Unmarshal(data, &CurrentFirmware) + if err == nil { + w.FirmwareVersion = CurrentFirmware.Current.VersionString + } else { + return json.Unmarshal(data, &w.FirmwareVersion) + } + + return nil +} + type Drive struct { Url string `json:"@odata.id"` }