Skip to content

Commit 3616db3

Browse files
Merge pull request jenningsloy318#50 from dylngg/proper_fan_percentages
Collect fan low/hi RPMs thresholds and properly compute fan percentage
2 parents 9a7e865 + 431889d commit 3616db3

File tree

2 files changed

+110
-7
lines changed

2 files changed

+110
-7
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ Note that port 9610 has been [reserved][4] for the redfish_exporter.
100100
- Enginetech EG920A-G20 (Huawei iBMC 6.22)
101101
- Lenovo ThinkSystem SR850 (BMC 2.1/2.42)
102102
- Lenovo ThinkSystem SR650 (BMC 2.50)
103-
- PowerEdge R440
103+
- Dell PowerEdge R440, R640, R650, R6515, C6420
104+
- GIGABYTE G292-Z20, G292-Z40, G482-Z54
105+
104106
## Acknowledgement
105107

106108
- [gofish][5] provides the underlying library to interact servers

collector/chassis_collector.go

Lines changed: 107 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package collector
22

33
import (
44
"fmt"
5+
"math"
6+
"strings"
57
"sync"
68

79
"github.com/apex/log"
@@ -15,7 +17,7 @@ var (
1517
ChassisSubsystem = "chassis"
1618
ChassisLabelNames = []string{"resource", "chassis_id"}
1719
ChassisTemperatureLabelNames = []string{"resource", "chassis_id", "sensor", "sensor_id"}
18-
ChassisFanLabelNames = []string{"resource", "chassis_id", "fan", "fan_id"}
20+
ChassisFanLabelNames = []string{"resource", "chassis_id", "fan", "fan_id", "fan_unit"}
1921
ChassisPowerVoltageLabelNames = []string{"resource", "chassis_id", "power_voltage", "power_voltage_id"}
2022
ChassisPowerSupplyLabelNames = []string{"resource", "chassis_id", "power_supply", "power_supply_id"}
2123
ChassisNetworkAdapterLabelNames = []string{"resource", "chassis_id", "network_adapter", "network_adapter_id"}
@@ -72,9 +74,81 @@ var (
7274
),
7375
},
7476
"chassis_fan_rpm": {
77+
desc: prometheus.NewDesc(
78+
prometheus.BuildFQName(namespace, ChassisSubsystem, "fan_rpm"),
79+
"fan RPM or percentage on this chassis component",
80+
ChassisFanLabelNames,
81+
nil,
82+
),
83+
},
84+
"chassis_fan_rpm_percentage": {
7585
desc: prometheus.NewDesc(
7686
prometheus.BuildFQName(namespace, ChassisSubsystem, "fan_rpm_percentage"),
77-
"fan rpm percentage on this chassis component",
87+
"fan RPM, as a percentage of the min-max RPMs possible, on this chassis component",
88+
ChassisFanLabelNames,
89+
nil,
90+
),
91+
},
92+
"chassis_fan_rpm_min": {
93+
desc: prometheus.NewDesc(
94+
prometheus.BuildFQName(namespace, ChassisSubsystem, "fan_rpm_min"),
95+
"lowest possible fan RPM or percentage, on this chassis component",
96+
ChassisFanLabelNames,
97+
nil,
98+
),
99+
},
100+
"chassis_fan_rpm_max": {
101+
desc: prometheus.NewDesc(
102+
prometheus.BuildFQName(namespace, ChassisSubsystem, "fan_rpm_max"),
103+
"highest possible fan RPM or percentage, on this chassis component",
104+
ChassisFanLabelNames,
105+
nil,
106+
),
107+
},
108+
"chassis_fan_rpm_lower_threshold_critical": {
109+
desc: prometheus.NewDesc(
110+
prometheus.BuildFQName(namespace, ChassisSubsystem, "fan_rpm_lower_threshold_critical"),
111+
"threshold below the normal range fan RPM or percentage, but not fatal, on this chassis component",
112+
ChassisFanLabelNames,
113+
nil,
114+
),
115+
},
116+
"chassis_fan_rpm_lower_threshold_non_critical": {
117+
desc: prometheus.NewDesc(
118+
prometheus.BuildFQName(namespace, ChassisSubsystem, "fan_rpm_lower_threshold_non_critical"),
119+
"threshold below the normal range fan RPM or percentage, but not critical, on this chassis component",
120+
ChassisFanLabelNames,
121+
nil,
122+
),
123+
},
124+
"chassis_fan_rpm_lower_threshold_fatal": {
125+
desc: prometheus.NewDesc(
126+
prometheus.BuildFQName(namespace, ChassisSubsystem, "fan_rpm_lower_threshold_fatal"),
127+
"threshold below the normal range fan RPM or percentage, and is fatal, on this chassis component",
128+
ChassisFanLabelNames,
129+
nil,
130+
),
131+
},
132+
"chassis_fan_rpm_upper_threshold_critical": {
133+
desc: prometheus.NewDesc(
134+
prometheus.BuildFQName(namespace, ChassisSubsystem, "fan_rpm_upper_threshold_critical"),
135+
"threshold above the normal range fan RPM or percentage, but not fatal, on this chassis component",
136+
ChassisFanLabelNames,
137+
nil,
138+
),
139+
},
140+
"chassis_fan_rpm_upper_threshold_non_critical": {
141+
desc: prometheus.NewDesc(
142+
prometheus.BuildFQName(namespace, ChassisSubsystem, "fan_rpm_upper_threshold_non_critical"),
143+
"threshold above the normal range fan RPM or percentage, but not critical, on this chassis component",
144+
ChassisFanLabelNames,
145+
nil,
146+
),
147+
},
148+
"chassis_fan_rpm_upper_threshold_fatal": {
149+
desc: prometheus.NewDesc(
150+
prometheus.BuildFQName(namespace, ChassisSubsystem, "fan_rpm_upper_threshold_fatal"),
151+
"threshold above the normal range fan RPM or percentage, and is fatal, on this chassis component",
78152
ChassisFanLabelNames,
79153
nil,
80154
),
@@ -365,10 +439,31 @@ func parseChassisFan(ch chan<- prometheus.Metric, chassisID string, chassisFan r
365439
chassisFanStaus := chassisFan.Status
366440
chassisFanStausHealth := chassisFanStaus.Health
367441
chassisFanStausState := chassisFanStaus.State
368-
chassisFanRPM := chassisFan.Reading
442+
chassisFanRPM := float64(chassisFan.Reading)
443+
chassisFanUnit := chassisFan.ReadingUnits
444+
chassisFanRPMLowerCriticalThreshold := float64(chassisFan.LowerThresholdCritical)
445+
chassisFanRPMUpperCriticalThreshold := float64(chassisFan.UpperThresholdCritical)
446+
chassisFanRPMLowerFatalThreshold := float64(chassisFan.LowerThresholdFatal)
447+
chassisFanRPMUpperFatalThreshold := float64(chassisFan.UpperThresholdFatal)
448+
chassisFanRPMMin := float64(chassisFan.MinReadingRange)
449+
chassisFanRPMMax := float64(chassisFan.MaxReadingRange)
450+
451+
chassisFanPercentage := chassisFanRPM
452+
if chassisFanUnit != redfish.PercentReadingUnits {
453+
// Some vendors (e.g. PowerEdge C6420) report null RPMs for Min/Max, as well as Lower/UpperFatal,
454+
// but provide Lower/UpperCritical, so use largest non-null for max. However, we can't know if
455+
// min is null (reported as zero by gofish) or just zero, so we'll have to assume a min of zero
456+
// if Min is not reported...
457+
min := chassisFanRPMMin
458+
max := math.Max(math.Max(chassisFanRPMMax, chassisFanRPMUpperFatalThreshold), chassisFanRPMUpperCriticalThreshold)
459+
chassisFanPercentage = 0
460+
if max != 0 {
461+
chassisFanPercentage = float64((chassisFanRPM+min)/max) * 100
462+
}
463+
}
369464

370465
// chassisFanStatusLabelNames :=[]string{BaseLabelNames,"fan_name","fan_member_id")
371-
chassisFanLabelvalues := []string{"fan", chassisID, chassisFanName, chassisFanID}
466+
chassisFanLabelvalues := []string{"fan", chassisID, chassisFanName, chassisFanID, strings.ToLower(string(chassisFanUnit))} // e.g. RPM -> rpm, Percentage -> percentage
372467

373468
if chassisFanStausHealthValue, ok := parseCommonStatusHealth(chassisFanStausHealth); ok {
374469
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_health"].desc, prometheus.GaugeValue, chassisFanStausHealthValue, chassisFanLabelvalues...)
@@ -377,8 +472,14 @@ func parseChassisFan(ch chan<- prometheus.Metric, chassisID string, chassisFan r
377472
if chassisFanStausStateValue, ok := parseCommonStatusState(chassisFanStausState); ok {
378473
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_state"].desc, prometheus.GaugeValue, chassisFanStausStateValue, chassisFanLabelvalues...)
379474
}
380-
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_rpm"].desc, prometheus.GaugeValue, float64(chassisFanRPM), chassisFanLabelvalues...)
381-
475+
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_rpm"].desc, prometheus.GaugeValue, chassisFanRPM, chassisFanLabelvalues...)
476+
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_rpm_min"].desc, prometheus.GaugeValue, chassisFanRPMMin, chassisFanLabelvalues...)
477+
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_rpm_max"].desc, prometheus.GaugeValue, chassisFanRPMMax, chassisFanLabelvalues...)
478+
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_rpm_percentage"].desc, prometheus.GaugeValue, chassisFanPercentage, chassisFanLabelvalues...)
479+
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_rpm_lower_threshold_critical"].desc, prometheus.GaugeValue, chassisFanRPMLowerCriticalThreshold, chassisFanLabelvalues...)
480+
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_rpm_upper_threshold_critical"].desc, prometheus.GaugeValue, chassisFanRPMUpperCriticalThreshold, chassisFanLabelvalues...)
481+
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_rpm_lower_threshold_fatal"].desc, prometheus.GaugeValue, chassisFanRPMLowerFatalThreshold, chassisFanLabelvalues...)
482+
ch <- prometheus.MustNewConstMetric(chassisMetrics["chassis_fan_rpm_upper_threshold_fatal"].desc, prometheus.GaugeValue, chassisFanRPMUpperFatalThreshold, chassisFanLabelvalues...)
382483
}
383484

384485
func parseChassisPowerInfoVoltage(ch chan<- prometheus.Metric, chassisID string, chassisPowerInfoVoltage redfish.Voltage, wg *sync.WaitGroup) {

0 commit comments

Comments
 (0)