metrics(power): export UPS battery charge and load percent
This commit is contained in:
parent
3dda295cd2
commit
e016b5d096
@ -15,6 +15,8 @@ type Sample struct {
|
|||||||
OnBattery bool
|
OnBattery bool
|
||||||
LowBattery bool
|
LowBattery bool
|
||||||
RuntimeSecond int
|
RuntimeSecond int
|
||||||
|
BatteryCharge float64
|
||||||
|
LoadPercent float64
|
||||||
ThresholdSec int
|
ThresholdSec int
|
||||||
Trigger bool
|
Trigger bool
|
||||||
BreachCount int
|
BreachCount int
|
||||||
@ -100,6 +102,10 @@ func (e *Exporter) serveMetrics(w http.ResponseWriter, _ *http.Request) {
|
|||||||
b.WriteString("# TYPE hecate_ups_low_battery gauge\n")
|
b.WriteString("# TYPE hecate_ups_low_battery gauge\n")
|
||||||
b.WriteString("# HELP hecate_ups_runtime_seconds Battery runtime remaining reported by UPS.\n")
|
b.WriteString("# HELP hecate_ups_runtime_seconds Battery runtime remaining reported by UPS.\n")
|
||||||
b.WriteString("# TYPE hecate_ups_runtime_seconds gauge\n")
|
b.WriteString("# TYPE hecate_ups_runtime_seconds gauge\n")
|
||||||
|
b.WriteString("# HELP hecate_ups_battery_charge_percent Battery charge percentage reported by UPS.\n")
|
||||||
|
b.WriteString("# TYPE hecate_ups_battery_charge_percent gauge\n")
|
||||||
|
b.WriteString("# HELP hecate_ups_load_percent UPS output load percentage.\n")
|
||||||
|
b.WriteString("# TYPE hecate_ups_load_percent gauge\n")
|
||||||
b.WriteString("# HELP hecate_ups_threshold_seconds Red-line threshold for runtime-based shutdown.\n")
|
b.WriteString("# HELP hecate_ups_threshold_seconds Red-line threshold for runtime-based shutdown.\n")
|
||||||
b.WriteString("# TYPE hecate_ups_threshold_seconds gauge\n")
|
b.WriteString("# TYPE hecate_ups_threshold_seconds gauge\n")
|
||||||
b.WriteString("# HELP hecate_ups_trigger_active Whether this UPS source currently breaches shutdown trigger conditions.\n")
|
b.WriteString("# HELP hecate_ups_trigger_active Whether this UPS source currently breaches shutdown trigger conditions.\n")
|
||||||
@ -123,6 +129,8 @@ func (e *Exporter) serveMetrics(w http.ResponseWriter, _ *http.Request) {
|
|||||||
b.WriteString(fmt.Sprintf("hecate_ups_on_battery%s %d\n", labels, boolNum(s.OnBattery)))
|
b.WriteString(fmt.Sprintf("hecate_ups_on_battery%s %d\n", labels, boolNum(s.OnBattery)))
|
||||||
b.WriteString(fmt.Sprintf("hecate_ups_low_battery%s %d\n", labels, boolNum(s.LowBattery)))
|
b.WriteString(fmt.Sprintf("hecate_ups_low_battery%s %d\n", labels, boolNum(s.LowBattery)))
|
||||||
b.WriteString(fmt.Sprintf("hecate_ups_runtime_seconds%s %d\n", labels, s.RuntimeSecond))
|
b.WriteString(fmt.Sprintf("hecate_ups_runtime_seconds%s %d\n", labels, s.RuntimeSecond))
|
||||||
|
b.WriteString(fmt.Sprintf("hecate_ups_battery_charge_percent%s %.2f\n", labels, s.BatteryCharge))
|
||||||
|
b.WriteString(fmt.Sprintf("hecate_ups_load_percent%s %.2f\n", labels, s.LoadPercent))
|
||||||
b.WriteString(fmt.Sprintf("hecate_ups_threshold_seconds%s %d\n", labels, s.ThresholdSec))
|
b.WriteString(fmt.Sprintf("hecate_ups_threshold_seconds%s %d\n", labels, s.ThresholdSec))
|
||||||
b.WriteString(fmt.Sprintf("hecate_ups_trigger_active%s %d\n", labels, boolNum(s.Trigger)))
|
b.WriteString(fmt.Sprintf("hecate_ups_trigger_active%s %d\n", labels, boolNum(s.Trigger)))
|
||||||
b.WriteString(fmt.Sprintf("hecate_ups_breach_count%s %d\n", labels, s.BreachCount))
|
b.WriteString(fmt.Sprintf("hecate_ups_breach_count%s %d\n", labels, s.BreachCount))
|
||||||
|
|||||||
@ -16,6 +16,8 @@ func TestExporterEmitsCoreMetrics(t *testing.T) {
|
|||||||
OnBattery: true,
|
OnBattery: true,
|
||||||
LowBattery: false,
|
LowBattery: false,
|
||||||
RuntimeSecond: 412,
|
RuntimeSecond: 412,
|
||||||
|
BatteryCharge: 81.5,
|
||||||
|
LoadPercent: 27.4,
|
||||||
ThresholdSec: 354,
|
ThresholdSec: 354,
|
||||||
Trigger: true,
|
Trigger: true,
|
||||||
BreachCount: 2,
|
BreachCount: 2,
|
||||||
@ -34,6 +36,8 @@ func TestExporterEmitsCoreMetrics(t *testing.T) {
|
|||||||
"hecate_shutdown_triggers_total 1",
|
"hecate_shutdown_triggers_total 1",
|
||||||
"hecate_ups_on_battery{source=\"db-ups\",target=\"atlasups@localhost\"",
|
"hecate_ups_on_battery{source=\"db-ups\",target=\"atlasups@localhost\"",
|
||||||
"hecate_ups_runtime_seconds{source=\"db-ups\",target=\"atlasups@localhost\"",
|
"hecate_ups_runtime_seconds{source=\"db-ups\",target=\"atlasups@localhost\"",
|
||||||
|
"hecate_ups_battery_charge_percent{source=\"db-ups\",target=\"atlasups@localhost\"",
|
||||||
|
"hecate_ups_load_percent{source=\"db-ups\",target=\"atlasups@localhost\"",
|
||||||
"hecate_ups_threshold_seconds{source=\"db-ups\",target=\"atlasups@localhost\"",
|
"hecate_ups_threshold_seconds{source=\"db-ups\",target=\"atlasups@localhost\"",
|
||||||
}
|
}
|
||||||
for _, m := range mustContain {
|
for _, m := range mustContain {
|
||||||
|
|||||||
@ -129,6 +129,8 @@ func (d *Daemon) Run(ctx context.Context) error {
|
|||||||
OnBattery: sample.OnBattery,
|
OnBattery: sample.OnBattery,
|
||||||
LowBattery: sample.LowBattery,
|
LowBattery: sample.LowBattery,
|
||||||
RuntimeSecond: sample.RuntimeSeconds,
|
RuntimeSecond: sample.RuntimeSeconds,
|
||||||
|
BatteryCharge: sample.BatteryCharge,
|
||||||
|
LoadPercent: sample.LoadPercent,
|
||||||
ThresholdSec: threshold,
|
ThresholdSec: threshold,
|
||||||
Trigger: trigger,
|
Trigger: trigger,
|
||||||
BreachCount: breachCount[target.Name],
|
BreachCount: breachCount[target.Name],
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -13,6 +14,8 @@ type Sample struct {
|
|||||||
OnBattery bool
|
OnBattery bool
|
||||||
LowBattery bool
|
LowBattery bool
|
||||||
RuntimeSeconds int
|
RuntimeSeconds int
|
||||||
|
BatteryCharge float64
|
||||||
|
LoadPercent float64
|
||||||
RawStatus string
|
RawStatus string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,5 +85,29 @@ func parseNUT(raw string) (Sample, error) {
|
|||||||
out.RuntimeSeconds = runtime
|
out.RuntimeSeconds = runtime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if chargeRaw := kv["battery.charge"]; chargeRaw != "" {
|
||||||
|
if charge, ok := parseNumber(chargeRaw); ok {
|
||||||
|
out.BatteryCharge = charge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if loadRaw := kv["ups.load"]; loadRaw != "" {
|
||||||
|
if load, ok := parseNumber(loadRaw); ok {
|
||||||
|
out.LoadPercent = load
|
||||||
|
}
|
||||||
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parseNumberCleaner = regexp.MustCompile(`[^0-9.+-]`)
|
||||||
|
|
||||||
|
func parseNumber(raw string) (float64, bool) {
|
||||||
|
cleaned := strings.TrimSpace(parseNumberCleaner.ReplaceAllString(raw, ""))
|
||||||
|
if cleaned == "" {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
v, err := strconv.ParseFloat(cleaned, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import "testing"
|
|||||||
|
|
||||||
func TestParseNUT(t *testing.T) {
|
func TestParseNUT(t *testing.T) {
|
||||||
raw := `battery.runtime: 384
|
raw := `battery.runtime: 384
|
||||||
|
battery.charge: 72
|
||||||
|
ups.load: 19
|
||||||
ups.status: OB LB
|
ups.status: OB LB
|
||||||
`
|
`
|
||||||
s, err := parseNUT(raw)
|
s, err := parseNUT(raw)
|
||||||
@ -19,4 +21,10 @@ ups.status: OB LB
|
|||||||
if s.RuntimeSeconds != 384 {
|
if s.RuntimeSeconds != 384 {
|
||||||
t.Fatalf("expected runtime 384, got %d", s.RuntimeSeconds)
|
t.Fatalf("expected runtime 384, got %d", s.RuntimeSeconds)
|
||||||
}
|
}
|
||||||
|
if s.BatteryCharge != 72 {
|
||||||
|
t.Fatalf("expected battery charge 72, got %.2f", s.BatteryCharge)
|
||||||
|
}
|
||||||
|
if s.LoadPercent != 19 {
|
||||||
|
t.Fatalf("expected load 19, got %.2f", s.LoadPercent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user