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

Add proliant xl420 #35

Merged
merged 7 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ log is based on the [Keep a CHANGELOG](http://keepachangelog.com/) project.
- Add ability to reference different vault paths for credential retrieval [#25](https://github.com/Comcast/fishymetrics/issues/25)
- Added HPE DL380 Gen10 support [#17](https://github.com/Comcast/fishymetrics/issues/17)
- Enhanced drive metrics collection for DL380 model servers to include NVME, Storage Disk Drives, and Logical Drives [#17](https://github.com/Comcast/fishymetrics/issues/17)
- add ability to send logs directly to elasticsearch endpoints [#10](https://github.com/Comcast/fishymetrics/issues/10)
- Add ability to send logs directly to elasticsearch endpoints [#10](https://github.com/Comcast/fishymetrics/issues/10)
- Add HPE Proliant DL560 Gen9 support [#23](https://github.com/Comcast/fishymetrics/issues/23)
- Add HPE Proliant XL420 Support [#33](https://github.com/Comcast/fishymetrics/issues/33)

## Fixed
- Cisco UCS C220 - add additional edge cases when collecting memory metrics [#2](https://github.com/Comcast/fishymetrics/issues/2)

## Updated
- Enhanced drive metrics collection for HPE DL360 model servers to include NVME, Storage Disk Drives, and Logical Drives. [#31](https://github.com/Comcast/fishymetrics/issues/31)
- removed references to internal URLs/FQDNs to opensource the project
- Removed references to internal URLs/FQDNs to opensource the project
- Cisco S3260M5 module to support FW Ver 4.2(xx) [#18](https://github.com/Comcast/fishymetrics/issues/18)
- HP DL360 module to support responses from iLO4 [#34](https://github.com/Comcast/fishymetrics/issues/34)

## [0.7.1]

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Current device models supported
- HP DL360
- HP DL560
- HP DL20
- HP XL420
- Cisco UCS C220 M5
- Cisco UCS S3260 M4
- Cisco UCS S3260 M5
Expand Down Expand Up @@ -142,7 +143,7 @@ comcast/fishymetrics:latest
## Prometheus Configuration

The fishymetrics exporter needs to be passed the address as a parameter, this can be
done with relabelling. available module options `["moonshot", "dl360", "dl20", "dl380", "dl560", "c220", "s3260m4", "s3260m5"]`
done with relabelling. available module options `["moonshot", "dl360", "dl20", "dl380", "dl560", "xl420", "c220", "s3260m4", "s3260m5"]`

Example config:
```YAML
Expand Down
5 changes: 4 additions & 1 deletion cmd/fishymetrics/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"github.com/comcast/fishymetrics/hpe/dl380"
"github.com/comcast/fishymetrics/hpe/dl560"
"github.com/comcast/fishymetrics/hpe/moonshot"
"github.com/comcast/fishymetrics/hpe/xl420"
"github.com/comcast/fishymetrics/logger"
"github.com/comcast/fishymetrics/middleware/muxprom"
fishy_vault "github.com/comcast/fishymetrics/vault"
Expand Down Expand Up @@ -157,6 +158,8 @@ func handler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
exporter = dl560.NewExporter(r.Context(), target, uri, credProf)
case "dl20":
exporter = dl20.NewExporter(r.Context(), target, uri, credProf)
case "xl420":
exporter = xl420.NewExporter(r.Context(), target, uri, credProf)
case "c220":
exporter, err = c220.NewExporter(r.Context(), target, uri, credProf)
case "s3260m4":
Expand All @@ -165,7 +168,7 @@ func handler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
exporter, err = s3260m5.NewExporter(r.Context(), target, uri, credProf)
default:
log.Error("'module' parameter does not match available options", zap.String("module", moduleName), zap.String("target", target), zap.Any("trace_id", r.Context().Value("traceID")))
http.Error(w, "'module' parameter does not match available options: [moonshot, dl360, dl380, dl560, dl20, c220, s3260m4, s3260m5]", http.StatusBadRequest)
http.Error(w, "'module' parameter does not match available options: [moonshot, dl360, dl380, dl560, dl20, xl420, c220, s3260m4, s3260m5]", http.StatusBadRequest)
return
}

Expand Down
3 changes: 2 additions & 1 deletion cmd/fishymetrics/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ const indexTmpl string = `<html>
<option vaule="moonshot">moonshot</option>
<option vaule="dl380">dl380</option>
<option vaule="dl360">dl360</option>
<option vaule="dl360">dl560</option>
<option vaule="dl560">dl560</option>
<option vaule="dl20">dl20</option>
<option vaule="xl420">xl420</option>
<option vaule="c220">c220</option>
<option vaule="s3260m4">s3260m4</option>
<option vaule="s3260m5">s3260m5</option>
Expand Down
17 changes: 14 additions & 3 deletions hpe/dl360/drive.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,28 @@ type GenericDrive struct {
Members []struct {
URL string `json:"@odata.id"`
} `json:"Members,omitempty"`
Links struct {
Links *struct {
Drives []struct {
URL string `json:"@odata.id"`
} `json:"Drives,omitempty"`
LogicalDrives struct {
LogicalDrives *struct {
URL string `json:"@odata.id"`
} `json:"LogicalDrives,omitempty"`
PhysicalDrives struct {
PhysicalDrives *struct {
URL string `json:"@odata.id"`
} `json:"PhysicalDrives,omitempty"`
} `json:"Links,omitempty"`
Link *struct {
Drives []struct {
URL string `json:"href"`
} `json:"Drives,omitempty"`
LogicalDrives *struct {
URL string `json:"href"`
} `json:"LogicalDrives,omitempty"`
PhysicalDrives *struct {
URL string `json:"href"`
} `json:"PhysicalDrives,omitempty"`
} `json:"links,omitempty"`
MembersCount int `json:"[email protected],omitempty"`
}

Expand Down
121 changes: 90 additions & 31 deletions hpe/dl360/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,34 +154,70 @@ func NewExporter(ctx context.Context, target, uri, profile string) *Exporter {
continue
}

// If LogicalDrives is present, parse logical drive endpoint until all urls are found
if newOutput.Links.LogicalDrives.URL != "" {
logicalDriveOutput, err := getDriveEndpoint(fqdn.String()+newOutput.Links.LogicalDrives.URL, target, retryClient)
if err != nil {
log.Error("api call "+fqdn.String()+newOutput.Links.LogicalDrives.URL+" failed - ", zap.Error(err), zap.Any("trace_id", ctx.Value("traceID")))
continue
if newOutput.Links != nil {
// If LogicalDrives is present, parse logical drive endpoint until all urls are found
if newOutput.Links.LogicalDrives != nil && newOutput.Links.LogicalDrives.URL != "" {
logicalDriveOutput, err := getDriveEndpoint(fqdn.String()+newOutput.Links.LogicalDrives.URL, target, retryClient)
if err != nil {
log.Error("api call "+fqdn.String()+newOutput.Links.LogicalDrives.URL+" failed - ", zap.Error(err), zap.Any("trace_id", ctx.Value("traceID")))
continue
}

if logicalDriveOutput.MembersCount > 0 {
// loop through each Member in the "LogicalDrive" field
for _, member := range logicalDriveOutput.Members {
// append each URL in the Members array to the logicalDriveURLs array.
logicalDriveURLs = append(logicalDriveURLs, member.URL)
}
}
}

if logicalDriveOutput.MembersCount > 0 {
// loop through each Member in the "LogicalDrive" field
for _, member := range logicalDriveOutput.Members {
// append each URL in the Members array to the logicalDriveURLs array.
logicalDriveURLs = append(logicalDriveURLs, member.URL)
// If PhysicalDrives is present, parse physical drive endpoint until all urls are found
if newOutput.Links.PhysicalDrives != nil && newOutput.Links.PhysicalDrives.URL != "" {
physicalDriveOutput, err := getDriveEndpoint(fqdn.String()+newOutput.Links.PhysicalDrives.URL, target, retryClient)
if err != nil {
log.Error("api call "+fqdn.String()+newOutput.Links.PhysicalDrives.URL+" failed - ", zap.Error(err), zap.Any("trace_id", ctx.Value("traceID")))
continue
}

if physicalDriveOutput.MembersCount > 0 {
for _, member := range physicalDriveOutput.Members {
physicalDriveURLs = append(physicalDriveURLs, member.URL)
}
}
}
}

// If PhysicalDrives is present, parse physical drive endpoint until all urls are found
if newOutput.Links.PhysicalDrives.URL != "" {
physicalDriveOutput, err := getDriveEndpoint(fqdn.String()+newOutput.Links.PhysicalDrives.URL, target, retryClient)
if newOutput.Link != nil {
// If LogicalDrives is present, parse logical drive endpoint until all urls are found
if newOutput.Link.LogicalDrives != nil && newOutput.Link.LogicalDrives.URL != "" {
logicalDriveOutput, err := getDriveEndpoint(fqdn.String()+newOutput.Link.LogicalDrives.URL, target, retryClient)
if err != nil {
log.Error("api call "+fqdn.String()+newOutput.Link.LogicalDrives.URL+" failed - ", zap.Error(err), zap.Any("trace_id", ctx.Value("traceID")))
continue
}

if err != nil {
log.Error("api call "+fqdn.String()+newOutput.Links.PhysicalDrives.URL+" failed - ", zap.Error(err), zap.Any("trace_id", ctx.Value("traceID")))
continue
if logicalDriveOutput.MembersCount > 0 {
// loop through each Member in the "LogicalDrive" field
for _, member := range logicalDriveOutput.Members {
// append each URL in the Members array to the logicalDriveURLs array.
logicalDriveURLs = append(logicalDriveURLs, member.URL)
}
}
}
if physicalDriveOutput.MembersCount > 0 {
for _, member := range physicalDriveOutput.Members {
physicalDriveURLs = append(physicalDriveURLs, member.URL)

// If PhysicalDrives is present, parse physical drive endpoint until all urls are found
if newOutput.Link.PhysicalDrives != nil && newOutput.Link.PhysicalDrives.URL != "" {
physicalDriveOutput, err := getDriveEndpoint(fqdn.String()+newOutput.Link.PhysicalDrives.URL, target, retryClient)
if err != nil {
log.Error("api call "+fqdn.String()+newOutput.Links.PhysicalDrives.URL+" failed - ", zap.Error(err), zap.Any("trace_id", ctx.Value("traceID")))
continue
}

if physicalDriveOutput.MembersCount > 0 {
for _, member := range physicalDriveOutput.Members {
physicalDriveURLs = append(physicalDriveURLs, member.URL)
}
}
}
}
Expand All @@ -196,10 +232,12 @@ func NewExporter(ctx context.Context, target, uri, profile string) *Exporter {
}

// parse through "Links" to find "Drives" array
if len(chassisOutput.Links.Drives) > 0 {
// loop through drives array and append each odata.id url to nvmeDriveURLs list
for _, drive := range chassisOutput.Links.Drives {
nvmeDriveURLs = append(nvmeDriveURLs, drive.URL)
if chassisOutput.Links != nil {
if len(chassisOutput.Links.Drives) > 0 {
// loop through drives array and append each odata.id url to nvmeDriveURLs list
for _, drive := range chassisOutput.Links.Drives {
nvmeDriveURLs = append(nvmeDriveURLs, drive.URL)
}
}
}

Expand Down Expand Up @@ -426,20 +464,33 @@ func (e *Exporter) exportPowerMetrics(body []byte) error {
return fmt.Errorf("Error Unmarshalling DL360 PowerMetrics - " + err.Error())
}

for _, pc := range pm.PowerControl {
(*dlPower)["supplyTotalConsumed"].WithLabelValues(pc.MemberID).Set(float64(pc.PowerConsumedWatts))
(*dlPower)["supplyTotalCapacity"].WithLabelValues(pc.MemberID).Set(float64(pc.PowerCapacityWatts))
for idx, pc := range pm.PowerControl {
if pc.MemberID != "" {
(*dlPower)["supplyTotalConsumed"].WithLabelValues(pc.MemberID).Set(float64(pc.PowerConsumedWatts))
(*dlPower)["supplyTotalCapacity"].WithLabelValues(pc.MemberID).Set(float64(pc.PowerCapacityWatts))
} else {
(*dlPower)["supplyTotalConsumed"].WithLabelValues(strconv.Itoa(idx)).Set(float64(pc.PowerConsumedWatts))
(*dlPower)["supplyTotalCapacity"].WithLabelValues(strconv.Itoa(idx)).Set(float64(pc.PowerCapacityWatts))
}
}

for _, ps := range pm.PowerSupplies {
if ps.Status.State == "Enabled" {
(*dlPower)["supplyOutput"].WithLabelValues(ps.MemberID, ps.SparePartNumber).Set(float64(ps.LastPowerOutputWatts))
if ps.MemberID != "" {
(*dlPower)["supplyOutput"].WithLabelValues(ps.MemberID, ps.SparePartNumber).Set(float64(ps.LastPowerOutputWatts))
} else {
(*dlPower)["supplyOutput"].WithLabelValues(strconv.Itoa(ps.Oem.Hp.BayNumber), ps.SparePartNumber).Set(float64(ps.LastPowerOutputWatts))
}
if ps.Status.Health == "OK" {
state = OK
} else {
state = BAD
}
(*dlPower)["supplyStatus"].WithLabelValues(ps.MemberID, ps.SparePartNumber).Set(state)
if ps.MemberID != "" {
(*dlPower)["supplyStatus"].WithLabelValues(ps.MemberID, ps.SparePartNumber).Set(state)
} else {
(*dlPower)["supplyStatus"].WithLabelValues(strconv.Itoa(ps.Oem.Hp.BayNumber), ps.SparePartNumber).Set(state)
}
}
}

Expand All @@ -461,13 +512,21 @@ func (e *Exporter) exportThermalMetrics(body []byte) error {
for _, fan := range tm.Fans {
// Check fan status and convert string to numeric values
if fan.Status.State == "Enabled" {
(*dlThermal)["fanSpeed"].WithLabelValues(fan.Name).Set(float64(fan.Reading))
if fan.FanName != "" {
(*dlThermal)["fanSpeed"].WithLabelValues(fan.FanName).Set(float64(fan.CurrentReading))
} else {
(*dlThermal)["fanSpeed"].WithLabelValues(fan.Name).Set(float64(fan.Reading))
}
if fan.Status.Health == "OK" {
state = OK
} else {
state = BAD
}
(*dlThermal)["fanStatus"].WithLabelValues(fan.Name).Set(state)
if fan.FanName != "" {
(*dlThermal)["fanStatus"].WithLabelValues(fan.FanName).Set(state)
} else {
(*dlThermal)["fanStatus"].WithLabelValues(fan.Name).Set(state)
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions hpe/dl360/power.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023 Comcast Cable Communications Management, LLC
* 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.
Expand Down Expand Up @@ -62,7 +62,8 @@ type PowerSupply struct {

// OemPower is the top level json object for historical data for wattage
type OemPower struct {
Hpe Hpe `json:"Hpe"`
Hpe Hpe `json:"Hpe,omitempty"`
Hp Hpe `json:"Hp,omitempty"`
}

// Hpe contains metadata on power supply product info
Expand Down
14 changes: 8 additions & 6 deletions hpe/dl360/thermal.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023 Comcast Cable Communications Management, LLC
* 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.
Expand Down Expand Up @@ -28,11 +28,13 @@ type ThermalMetrics struct {

// Fan is the json object for a DL360 fan module
type Fan struct {
MemberID string `json:"MemberId"`
Name string `json:"Name"`
Reading int `json:"Reading"`
ReadingUnits string `json:"ReadingUnits"`
Status StatusThermal `json:"Status"`
MemberID string `json:"MemberId"`
Name string `json:"Name"`
FanName string `json:"FanName"`
Reading int `json:"Reading"`
CurrentReading int `json:"CurrentReading"`
ReadingUnits string `json:"ReadingUnits"`
Status StatusThermal `json:"Status"`
}

// StatusThermal is the variable to determine if a fan or temperature sensor module is OK or not
Expand Down
8 changes: 4 additions & 4 deletions hpe/dl560/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net"
"net/http"
"net/url"
Expand All @@ -46,7 +46,7 @@ const (
THERMAL = "ThermalMetrics"
// POWER represents the power metric endpoint
POWER = "PowerMetrics"
// DRIVE represents the logical drive metric endpoints
// DRIVE represents the physical drive metric endpoints
DRIVE = "PhysicalDriveMetrics"
// LOGICALDRIVE represents the Logical drive metric endpoint
LOGICALDRIVE = "LogicalDriveMetrics"
Expand Down Expand Up @@ -478,14 +478,14 @@ func getDriveEndpoints(url, host string, client *retryablehttp.Client) (GenericD
}
}

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

err = json.Unmarshal(body, &drives)
if err != nil {
return drives, fmt.Errorf("Error Unmarshalling S3260M5 Memory Collection struct - " + err.Error())
return drives, fmt.Errorf("Error Unmarshalling DL560 Drive Collection struct - " + err.Error())
}

return drives, nil
Expand Down
Loading
Loading