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

Adding support for the collection of Component Firmware Version and other details #82

Merged
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
69b1cdc
Update exporter.go to include new FirmwareInventory logic
jenniferKaiser21 Jun 11, 2024
ebb72d1
Update handlers.go to include new functions for Firmware Inventory Me…
jenniferKaiser21 Jun 11, 2024
04952ef
Update helpers.go to include new functions for Firmware Inventory Met…
jenniferKaiser21 Jun 11, 2024
cbabe05
Update metrics.go to include new metrics for Firmware Inventory export
jenniferKaiser21 Jun 11, 2024
98db506
Create firmware.go to include new Firmware Component structs
jenniferKaiser21 Jun 11, 2024
f6bb712
Update exporter.go to reuse existing functions and structs
jenniferKaiser21 Jun 13, 2024
945f80f
Update handlers.go to include health status in firmware componoent ex…
jenniferKaiser21 Jun 13, 2024
3b12e96
Update metrics.go to include id in firmware component status
jenniferKaiser21 Jun 13, 2024
ee4411f
Update chassis.go to incorporate firmware component
jenniferKaiser21 Jun 13, 2024
f931a72
Delete oem/firmware.go, incorporated FirmwareComponent in chassis.go
jenniferKaiser21 Jun 13, 2024
900f4e5
Update exporter.go to include graceful fail for iLO4 c220 missing fir…
jenniferKaiser21 Jun 14, 2024
e8c5a2e
Update helpers.go to reuse function instead of redundant function
jenniferKaiser21 Jun 14, 2024
7e55385
Update exporter.go to include work in progress loop for iLO4 firmware
jenniferKaiser21 Jun 18, 2024
a7b2489
Update handlers.go to include work in progress loop for ilo4 firmware
jenniferKaiser21 Jun 18, 2024
a80b873
Update helpers.go to include work in progress getFirmwareComponents f…
jenniferKaiser21 Jun 18, 2024
e721c44
Update system.go to include SystemFirmwareInventory struct
jenniferKaiser21 Jun 18, 2024
0cce139
exporter.go 6/24 changes
jenniferKaiser21 Jun 25, 2024
a39a8f5
handlers.go 6/24 changes
jenniferKaiser21 Jun 25, 2024
6224043
metrics.go 6/24 changes
jenniferKaiser21 Jun 25, 2024
94745ad
chassis.go 6/24 changes
jenniferKaiser21 Jun 25, 2024
e92b2dc
Create firmware.go 6/24 changes
jenniferKaiser21 Jun 25, 2024
d17e60a
Clean up debug prints and comments
jenniferKaiser21 Jun 27, 2024
dccfcea
Update handlers.go to include refactored exportFirmwareInventoryMetri…
jenniferKaiser21 Jun 27, 2024
78d034a
Update metrics.go with new export firmware metric fields
jenniferKaiser21 Jun 27, 2024
c8f0ff6
Update firmware.go to include finalized universal structs
jenniferKaiser21 Jun 27, 2024
a623743
Remove comments from exporter.go
jenniferKaiser21 Jun 28, 2024
6e6b9d7
Remove comment from exporter.go
jenniferKaiser21 Jun 28, 2024
6804991
Merge branch 'main' into firmwareInventoryMetrics
jenniferKaiser21 Jun 30, 2024
672da07
Remove unused function from helpers.go
jenniferKaiser21 Jul 1, 2024
942eab1
Remove unused struct from system.go
jenniferKaiser21 Jul 1, 2024
6358911
Add firmware modules exclude flag
jenniferKaiser21 Jul 5, 2024
173294e
Update exporter.go to include use of firmware.modules-exclude flag
jenniferKaiser21 Jul 5, 2024
26ad359
Update README.md to include firmware.modules-exclude flag
jenniferKaiser21 Jul 5, 2024
b57eb09
Update CHANGELOG.md for firmware metrics gathering addition
jenniferKaiser21 Jul 5, 2024
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
25 changes: 25 additions & 0 deletions exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ const (
STORAGEBATTERY = "StorBatteryMetrics"
// ILOSELFTEST represents the processor metric endpoints
ILOSELFTEST = "iloSelfTestMetrics"
// FIRMWAREINVENTORY represents the component firmware metric endpoints
FIRMWAREINVENTORY = "FirmwareInventoryMetrics"
// OK is a string representation of the float 1.0 for device status
OK = 1.0
// BAD is a string representation of the float 0.0 for device status
Expand Down Expand Up @@ -360,6 +362,24 @@ func NewExporter(ctx context.Context, target, uri, profile, model string, exclud
zap.Strings("physical_drive_endpoints", driveEndpointsResp.physicalDriveURLs),
zap.Any("trace_id", ctx.Value("traceID")))

// Call /redfish/v1/Managers/XXXX/UpdateService/FirmwareInventory/ for firmware inventory
firmwareInventoryEndpoints, err := getMemberUrls(exp.url+uri+"/UpdateService/FirmwareInventory/", target, retryClient)
if err != nil {
// Try the iLo 4 firmware inventory endpoint
// Use the collected sysEndpoints.systems to build url(s)
if len(sysEndpoints.systems) > 0 {
// call /redfish/v1/Systems/XXXX/FirmwareInventory/
for _, system := range sysEndpoints.systems {
firmwareInventoryEndpoints = append(firmwareInventoryEndpoints, system+"FirmwareInventory/")
}
// Ensure we have at least one firmware inventory endpoint
if len(firmwareInventoryEndpoints) == 0 {
log.Error("error when getting FirmwareInventory url", zap.Error(err), zap.Any("trace_id", ctx.Value("traceID")))
return nil, err
}
}
}

// 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)))
Expand Down Expand Up @@ -404,6 +424,11 @@ func NewExporter(ctx context.Context, target, uri, profile, model string, exclud
pool.NewTask(common.Fetch(exp.url+dimm.URL, target, profile, retryClient), exp.url+dimm.URL, handle(&exp, MEMORY)))
}

// Firmware Inventory
for _, url := range firmwareInventoryEndpoints {
tasks = append(tasks, pool.NewTask(common.Fetch(exp.url+url, target, profile, retryClient), exp.url+url, handle(&exp, FIRMWAREINVENTORY)))
}

// call /redfish/v1/Managers/XXX/ for firmware version and ilo self test metrics
tasks = append(tasks,
pool.NewTask(common.Fetch(exp.url+mgrEndpointFinal, target, profile, retryClient),
Expand Down
30 changes: 30 additions & 0 deletions exporter/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ func handle(exp *Exporter, metricType ...string) []common.Handler {
handlers = append(handlers, exp.exportStorageBattery)
} else if m == ILOSELFTEST {
handlers = append(handlers, exp.exportIloSelfTest)
} else if m == FIRMWAREINVENTORY {
handlers = append(handlers, exp.exportFirmwareInventoryMetrics)
}

}

return handlers
Expand Down Expand Up @@ -635,6 +638,33 @@ func (e *Exporter) exportProcessorMetrics(body []byte) error {
return nil
}

// exportFirmwareInventoryMetrics collects the inventory component's firmware metrics in json format and sets the prometheus guages
func (e *Exporter) exportFirmwareInventoryMetrics(body []byte) error {
var fwcomponent oem.ILO4Firmware
var component = (*e.DeviceMetrics)["firmwareInventoryMetrics"]

err := json.Unmarshal(body, &fwcomponent)
if err != nil {
return fmt.Errorf("Error Unmarshalling FirmwareInventoryMetrics - " + err.Error())
}
// Export for iLO4 since it has a different structure
if len(fwcomponent.Current.Firmware) > 0 {
for _, firmware := range fwcomponent.Current.Firmware {
(*component)["componentFirmware"].WithLabelValues(firmware.Id, firmware.Name, firmware.Location, firmware.VersionString).Set(1.0)
}
} else {
// Export for iLO5, since it's structure matches the GenericFirmware struct
var fwcomponent oem.GenericFirmware
err := json.Unmarshal(body, &fwcomponent)
if err != nil {
return fmt.Errorf("Error Unmarshalling FirmwareInventoryMetrics - " + err.Error())
}

(*component)["componentFirmware"].WithLabelValues(fwcomponent.Id, fwcomponent.Name, fwcomponent.Description, fwcomponent.Version).Set(1.0)
}
return nil
}

// exportIloSelfTest collects the iLO Self Test Results metrics in json format and sets the prometheus guage
func (e *Exporter) exportIloSelfTest(body []byte) error {

Expand Down
45 changes: 45 additions & 0 deletions exporter/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,51 @@ func getProcessorEndpoints(url, host string, client *retryablehttp.Client) (oem.
return processors, nil
}

// Getting firmware components for iLO 4 hosts
func getFirmwareComponents(url, host string, client *retryablehttp.Client) (oem.SystemFirmwareInventory, error) {
var fc oem.SystemFirmwareInventory
var resp *http.Response
var err error
retryCount := 0
req := common.BuildRequest(url, host)

resp, err = common.DoRequest(client, req)
if err != nil {
return fc, err
}
defer resp.Body.Close()
if !(resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices) {
if resp.StatusCode == http.StatusNotFound {
for retryCount < 3 && resp.StatusCode == http.StatusNotFound {
time.Sleep(client.RetryWaitMin)
resp, err = common.DoRequest(client, req)
retryCount = retryCount + 1
}
if err != nil {
return fc, err
} else if !(resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices) {
return fc, fmt.Errorf("HTTP status %d", resp.StatusCode)
}
} else if resp.StatusCode == http.StatusUnauthorized {
return fc, common.ErrInvalidCredential
} else {
return fc, fmt.Errorf("HTTP status %d", resp.StatusCode)
}
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return fc, fmt.Errorf("Error reading Response Body - " + err.Error())
}

err = json.Unmarshal(body, &fc)
if err != nil {
return fc, fmt.Errorf("Error Unmarshalling SystemFirmwareInventory Collection struct - " + err.Error())
}

return fc, nil
}
jenniferKaiser21 marked this conversation as resolved.
Show resolved Hide resolved

// appendSlash appends a slash to the end of a URL if it does not already have one
func appendSlash(url string) string {
if url[len(url)-1] != '/' {
Expand Down
32 changes: 19 additions & 13 deletions exporter/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func NewDeviceMetrics() *map[string]*metrics {
"nvmeDriveStatus": newServerMetric("redfish_nvme_drive_status", "Current NVME status 1 = OK, 0 = BAD, -1 = DISABLED", nil, []string{"chassisSerialNumber", "chassisModel", "protocol", "id", "serviceLabel"}),
}

// Phyiscal Storage Disk Drive Metrics
// Physical Storage Disk Drive Metrics
DiskDriveMetrics = &metrics{
"driveStatus": newServerMetric("redfish_disk_drive_status", "Current Disk Drive status 1 = OK, 0 = BAD, -1 = DISABLED", nil, []string{"name", "chassisSerialNumber", "chassisModel", "id", "location", "serialnumber", "capacityMiB"}),
}
Expand All @@ -92,23 +92,29 @@ func NewDeviceMetrics() *map[string]*metrics {
"memoryDimmStatus": newServerMetric("redfish_memory_dimm_status", "Current dimm status 1 = OK, 0 = BAD", nil, []string{"name", "chassisSerialNumber", "chassisModel", "capacityMiB", "manufacturer", "partNumber"}),
}

// Component Firmware Metrics
FirmwareInventoryMetrics = &metrics{
"componentFirmware": newServerMetric("redfish_component_firmware", "Current firmware component status 1 = OK, 0 = BAD", nil, []string{"id", "name", "description", "version"}),
}

DeviceMetrics = &metrics{
"deviceInfo": newServerMetric("redfish_device_info", "Current snapshot of device firmware information", nil, []string{"name", "chassisSerialNumber", "chassisModel", "firmwareVersion", "biosVersion"}),
}

Metrics = &map[string]*metrics{
"up": UpMetric,
"thermalMetrics": ThermalMetrics,
"powerMetrics": PowerMetrics,
"processorMetrics": ProcessorMetrics,
"nvmeMetrics": NVMeDriveMetrics,
"diskDriveMetrics": DiskDriveMetrics,
"logicalDriveMetrics": LogicalDriveMetrics,
"storBatteryMetrics": StorageBatteryMetrics,
"storageCtrlMetrics": StorageControllerMetrics,
"iloSelfTestMetrics": IloSelfTestMetrics,
"memoryMetrics": MemoryMetrics,
"deviceInfo": DeviceMetrics,
"up": UpMetric,
"thermalMetrics": ThermalMetrics,
"powerMetrics": PowerMetrics,
"processorMetrics": ProcessorMetrics,
"nvmeMetrics": NVMeDriveMetrics,
"diskDriveMetrics": DiskDriveMetrics,
"logicalDriveMetrics": LogicalDriveMetrics,
"storBatteryMetrics": StorageBatteryMetrics,
"storageCtrlMetrics": StorageControllerMetrics,
"iloSelfTestMetrics": IloSelfTestMetrics,
"firmwareInventoryMetrics": FirmwareInventoryMetrics,
"memoryMetrics": MemoryMetrics,
"deviceInfo": DeviceMetrics,
}
)

Expand Down
66 changes: 66 additions & 0 deletions oem/firmware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2024 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package oem

import "encoding/json"

// /redfish/v1/UpdateService/FirmwareInventory/XXXX/
type GenericFirmware struct {
Id string `json:"Id,omitempty"`
Name string `json:"Name"`
Version string `json:"Version,omitempty"`
VersionString string `json:"VersionString,omitempty"`
Location string `json:"Location,omitempty"`
Description string `json:"Description,omitempty"`
Status Status `json:"Status,omitempty"`
}

// /redfish/v1/Systems/1/FirmwareInventory/
type ILO4Firmware struct {
Current FirmwareWrapper `json:"Current"`
}

type FirmwareWrapper struct {
FirmwareSlice
}

type FirmwareSlice struct {
Firmware []GenericFirmware
}

// Function to unmarshal the FirmwareWrapper struct
func (w *FirmwareWrapper) UnmarshalJSON(data []byte) error {
var fw GenericFirmware
var jsonData map[string]interface{}
err := json.Unmarshal(data, &jsonData)

if err == nil {
for _, items := range jsonData {
for _, item := range items.([]interface{}) {
component, _ := json.Marshal(item)
err = json.Unmarshal(component, &fw)
if err == nil {
w.Firmware = append(w.Firmware, fw)
}
}
}
} else {
return json.Unmarshal(data, &w.Firmware)
}

return nil
}
16 changes: 16 additions & 0 deletions oem/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,19 @@ type MemorySummary struct {
type StatusMemory struct {
HealthRollup string `json:"HealthRollup"`
}

// SystemFirmwareInventory is the json object for SystemFirwmareInventory metadata
type SystemFirmwareInventory struct {
Current []struct {
Component []struct {
Details []struct {
Item []struct {
Name string `json:"Name,omitempty"`
Key string `json:"Key,omitempty"`
Location string `json:"Location,omitempty"`
}
VersionString string `json:"VersionString,omitempty"`
}
}
}
}
jenniferKaiser21 marked this conversation as resolved.
Show resolved Hide resolved