Skip to content

Commit

Permalink
feat(metric): Added HPE SmartStorage specific drive metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
BirknerAlex committed Apr 2, 2024
1 parent 551eeda commit 06312b0
Show file tree
Hide file tree
Showing 9 changed files with 397 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
run:
tests: true

issues:
exclude-dirs:
- pkg/oem/

linters:
enable-all: true
disable:
Expand Down
2 changes: 2 additions & 0 deletions pkg/drivers/redfish/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ func (m *Metrics) Collect() error {
}

for _, system := range systems {
m.vendorSpecifics(system)

m.WithRedfishHealthMetric(convertHealthStatus(system.Status.Health, false), map[string]string{
"system_id": system.ID,
})
Expand Down
52 changes: 52 additions & 0 deletions pkg/drivers/redfish/metrics/vendor_specifics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package metrics

import (
"log"

"github.com/g-portal/redfish_exporter/pkg/oem/hpe"
"github.com/stmcginnis/gofish/common"
"github.com/stmcginnis/gofish/redfish"
)

// vendorSpecifics is a helper function to collect vendor specific metrics.
func (m *Metrics) vendorSpecifics(system *redfish.ComputerSystem) {
switch system.Manufacturer {
case hpe.Manufacturer:
//nolint: nestif
if hpeStorage, err := hpe.GetSmartStorage(system); err == nil {
if arrayControllers, err := hpeStorage.ArrayControllers(); err == nil {
for _, controller := range arrayControllers {
m.WithRedfishStorageHealthMetric(convertHealthStatus(hpeStorage.Status.Health,
hpeStorage.Status.State == common.EnabledState), map[string]string{
"system_id": system.ID,
"storage_id": controller.ID,
})

if drives, err := controller.PhysicalDrives(); err == nil {
for _, drive := range drives {
m.WithRedfishDriveHealthMetric(convertHealthStatus(drive.Status.Health,
drive.Status.State == common.EnabledState), map[string]string{
"system_id": system.ID,
"storage_id": controller.ID,
"drive_id": drive.ID,
})
m.WithRedfishDriveCapacityMetric(float64(drive.CapacityMiB), map[string]string{
"system_id": system.ID,
"storage_id": controller.ID,
"drive_id": drive.ID,
})
}
} else {
log.Printf("error getting drives: %s", err)
}
}
} else {
log.Printf("error getting array controllers: %s", err)
}
} else {
log.Printf("error getting SmartStorage: %s", err)
}
default:
// no vendor specific metrics to collect
}
}
116 changes: 116 additions & 0 deletions pkg/oem/hpe/array_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package hpe

import (
"encoding/json"
"fmt"
"github.com/stmcginnis/gofish/common"
"io"
)

type ArrayController struct {
common.Entity

OdataContext string `json:"@odata.context"`
OdataID string `json:"@odata.id"`
OdataType string `json:"@odata.type"`
AdapterType string `json:"AdapterType"`
BackupPowerSourceStatus string `json:"BackupPowerSourceStatus"`
CurrentOperatingMode string `json:"CurrentOperatingMode"`
Description string `json:"Description"`
EncryptionCryptoOfficerPasswordSet bool `json:"EncryptionCryptoOfficerPasswordSet"`
EncryptionEnabled bool `json:"EncryptionEnabled"`
EncryptionFwLocked bool `json:"EncryptionFwLocked"`
EncryptionHasLockedVolumesMissingBootPassword bool `json:"EncryptionHasLockedVolumesMissingBootPassword"`
EncryptionMixedVolumesEnabled bool `json:"EncryptionMixedVolumesEnabled"`
EncryptionStandaloneModeEnabled bool `json:"EncryptionStandaloneModeEnabled"`
ExternalPortCount int `json:"ExternalPortCount"`
FirmwareVersion struct {
Current struct {
VersionString string `json:"VersionString"`
} `json:"Current"`
} `json:"FirmwareVersion"`
HardwareRevision string `json:"HardwareRevision"`
ID string `json:"Id"`
InternalPortCount int `json:"InternalPortCount"`
Location string `json:"Location"`
LocationFormat string `json:"LocationFormat"`
Model string `json:"Model"`
Name string `json:"Name"`
SerialNumber string `json:"SerialNumber"`
Status common.Status `json:"Status"`
Type string `json:"Type"`
Links struct {
LogicalDrives struct {
Href string `json:"href"`
} `json:"LogicalDrives"`
PhysicalDrives struct {
Href string `json:"href"`
} `json:"PhysicalDrives"`
StorageEnclosures struct {
Href string `json:"href"`
} `json:"StorageEnclosures"`
UnconfiguredDrives struct {
Href string `json:"href"`
} `json:"UnconfiguredDrives"`
Self struct {
Href string `json:"href"`
} `json:"self"`
} `json:"links"`
}

func (s *ArrayController) PhysicalDrives() ([]*Drive, error) {
result := make([]*Drive, 0)

drives, err := s.diskDrivesList()
if err != nil {
return nil, fmt.Errorf("error getting ArrayControllers: %w", err)
}

for _, member := range drives.Members {
data, err := s.GetClient().Get(member.OdataID)
if err != nil {
return nil, fmt.Errorf("error getting ArrayController data: %w", err)
}

var drive *Drive
jsonPayload, err := io.ReadAll(data.Body)
if err != nil {
return nil, fmt.Errorf("error reading SmartStorage data: %w", err)
}

err = json.Unmarshal(jsonPayload, &drive)
if err != nil {
return nil, fmt.Errorf("error unmarshalling SmartStorage data: %w", err)
}

drive.SetClient(s.GetClient())

result = append(result, drive)
}

return result, nil

}

func (s *ArrayController) diskDrivesList() (*DiskDrives, error) {
var result *DiskDrives

data, err := s.GetClient().Get(s.Links.PhysicalDrives.Href)
if err != nil {
return nil, fmt.Errorf("error getting physical drives: %w", err)
}

jsonPayload, err := io.ReadAll(data.Body)
if err != nil {
return nil, fmt.Errorf("error reading SmartStorage data: %w", err)
}

err = json.Unmarshal(jsonPayload, &result)
if err != nil {
return nil, fmt.Errorf("error unmarshalling SmartStorage data: %w", err)
}

result.SetClient(s.GetClient())

return result, nil
}
89 changes: 89 additions & 0 deletions pkg/oem/hpe/array_controllers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package hpe

import (
"encoding/json"
"fmt"
"github.com/stmcginnis/gofish/common"
"io"
)

type ArrayControllers struct {
common.Entity

OdataContext string `json:"@odata.context"`
OdataID string `json:"@odata.id"`
OdataType string `json:"@odata.type"`
Description string `json:"Description"`
MemberType string `json:"MemberType"`
Members []struct {
OdataID string `json:"@odata.id"`
} `json:"Members"`
MembersOdataCount int `json:"[email protected]"`
Name string `json:"Name"`
Total int `json:"Total"`
Type string `json:"Type"`
Links struct {
Member []struct {
Href string `json:"href"`
} `json:"Member"`
Self struct {
Href string `json:"href"`
} `json:"self"`
} `json:"links"`
}

func (s *SmartStorage) ArrayControllers() ([]*ArrayController, error) {
result := make([]*ArrayController, 0)

controllers, err := s.arrayControllerList()
if err != nil {
return nil, fmt.Errorf("error getting ArrayControllers: %w", err)
}

for _, member := range controllers.Members {
data, err := s.GetClient().Get(member.OdataID)
if err != nil {
return nil, fmt.Errorf("error getting ArrayController data: %w", err)
}

var controller *ArrayController
jsonPayload, err := io.ReadAll(data.Body)
if err != nil {
return nil, fmt.Errorf("error reading SmartStorage data: %w", err)
}

err = json.Unmarshal(jsonPayload, &controller)
if err != nil {
return nil, fmt.Errorf("error unmarshalling SmartStorage data: %w", err)
}

controller.SetClient(s.GetClient())

result = append(result, controller)
}

return result, nil
}

func (s *SmartStorage) arrayControllerList() (*ArrayControllers, error) {
var result *ArrayControllers

data, err := s.GetClient().Get(s.Links.ArrayControllers.Href)
if err != nil {
return nil, fmt.Errorf("error getting ArrayControllers: %w", err)
}

jsonPayload, err := io.ReadAll(data.Body)
if err != nil {
return nil, fmt.Errorf("error reading SmartStorage data: %w", err)
}

err = json.Unmarshal(jsonPayload, &result)
if err != nil {
return nil, fmt.Errorf("error unmarshalling SmartStorage data: %w", err)
}

result.SetClient(s.GetClient())

return result, nil
}
46 changes: 46 additions & 0 deletions pkg/oem/hpe/drive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package hpe

import "github.com/stmcginnis/gofish/common"

type Drive struct {
common.Entity

OdataContext string `json:"@odata.context"`
OdataID string `json:"@odata.id"`
OdataType string `json:"@odata.type"`
BlockSizeBytes int `json:"BlockSizeBytes"`
CapacityGB int `json:"CapacityGB"`
CapacityLogicalBlocks int64 `json:"CapacityLogicalBlocks"`
CapacityMiB int `json:"CapacityMiB"`
CarrierApplicationVersion string `json:"CarrierApplicationVersion"`
CarrierAuthenticationStatus string `json:"CarrierAuthenticationStatus"`
CurrentTemperatureCelsius int `json:"CurrentTemperatureCelsius"`
Description string `json:"Description"`
DiskDriveStatusReasons []string `json:"DiskDriveStatusReasons"`
EncryptedDrive bool `json:"EncryptedDrive"`
FirmwareVersion struct {
Current struct {
VersionString string `json:"VersionString"`
} `json:"Current"`
} `json:"FirmwareVersion"`
ID string `json:"Id"`
InterfaceSpeedMbps int `json:"InterfaceSpeedMbps"`
InterfaceType string `json:"InterfaceType"`
Location string `json:"Location"`
LocationFormat string `json:"LocationFormat"`
MaximumTemperatureCelsius int `json:"MaximumTemperatureCelsius"`
MediaType string `json:"MediaType"`
Model string `json:"Model"`
Name string `json:"Name"`
PowerOnHours interface{} `json:"PowerOnHours"`
RotationalSpeedRpm int `json:"RotationalSpeedRpm"`
SSDEnduranceUtilizationPercentage interface{} `json:"SSDEnduranceUtilizationPercentage"`
SerialNumber string `json:"SerialNumber"`
Status common.Status `json:"Status"`
Type string `json:"Type"`
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
} `json:"links"`
}
28 changes: 28 additions & 0 deletions pkg/oem/hpe/drives.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package hpe

import "github.com/stmcginnis/gofish/common"

type DiskDrives struct {
common.Entity

OdataContext string `json:"@odata.context"`
OdataID string `json:"@odata.id"`
OdataType string `json:"@odata.type"`
Description string `json:"Description"`
MemberType string `json:"MemberType"`
Members []struct {
OdataID string `json:"@odata.id"`
} `json:"Members"`
MembersOdataCount int `json:"[email protected]"`
Name string `json:"Name"`
Total int `json:"Total"`
Type string `json:"Type"`
Links struct {
Member []struct {
Href string `json:"href"`
} `json:"Member"`
Self struct {
Href string `json:"href"`
} `json:"self"`
} `json:"links"`
}
3 changes: 3 additions & 0 deletions pkg/oem/hpe/generic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package hpe

const Manufacturer = "HPE"
Loading

0 comments on commit 06312b0

Please sign in to comment.