- Mon 19 December 2022
- How-to
- vivi_
- #how-to, #Cyber Power, #0764:0501, #UPS, #Grafana, #Prometheus, #node_exporter, #Python
Whilst wrangling some kettle cords on this very busy desk I was reminded of that unused USB port on the back of my UPS and thought "these are a lot of devices for one 15 amp circuit, I should probably measure my consumption".
I have two Cyber Power CP1500 connected to mains power; one for my three workstations and bench gear, the other powers my three servers. I have exactly one power outlet in my office (ikr!) which I'm hoping to remediate in 2023.
Proof-of-concept
I connected the UPS USB cable to my laptop - unfortunately there is no officially supported PowerPanel® for Arch but a simple yay -yS powerpanel
was a quick and easy fix.
I enabled and started the service with
sudo systemctl enable pwrstatd.service
sudo systemctl start pwrstatd.service
Then to make sure it's working
sudo pwrstat -status
It worked, neat!
My next step is to see if there are any pre-built solutions for monitoring power consumption over time. Unfortunately I couldn't find anything in nut
and the only other officially supported solution was to connect the UPS to my Apple computer and use the GUI - we can do better.
The pwrstat
help text does not contain any means to produce machine-friendly output so it's scripting time.
I chose Python for the PoC because 1) it's ubiquitous and 2) it's less ugly than bash - perhaps I'll riig
in the near future.
Anyway, here's what I came up with
#!/usr/bin/env python3
import re
import subprocess
import time
# we will configure node_exporter to include this file in it's output
promfile = "/var/lib/node_exporter/ups.prom"
while True :
# run the pwrstat command, capture output, decode to utf-8
output = subprocess.check_output(["pwrstat", "-status"]).decode('utf-8')
# split command output by line into a list, ignore empty lines
lines = [x for x in output.split("\n") if x]
# we can re-use this variable, not sure if this is `pythonic` though
output = ""
for line in lines :
# we don't care about lines with colons
if ":" in line : continue
# replace all periods (.) with a single colon, lowercase everything
line = re.sub('\.+', ':', line).lower()
# split keys and values, strip padding whitespace
parts = [x.strip() for x in line.split(":")]
# EAFP : skip non-numeric values
try :
key = "ups_metrics_%s" % parts[0].replace(" ", "_")
value = float(parts[1].split()[0])
output += "# TYPE %s gauge\n%s %f\n" % (key, key, value)
# we don't care about text values (for now?)
except :
pass
""" not sure if opening and closing the file every second is a good idea or not,
should probably refactor this to limit i/o ¯\_(ツ)_/¯
""""
with open(promfile, "w") as fh :
fh.write(output)
fh.close()
time.sleep(1)
Place in /usr/local/bin/ups_metrics
, set execution bit and run it (nb: there wont be any terminal output)
chmod +x /usr/local/bin/ups_metrics
sudo /usr/local/bin/ups_metrics
Time to test
curl -sSL http://localhost:9100/metrics | grep "ups_metrics"
# HELP ups_metrics_battery_capacity Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_battery_capacity gauge
ups_metrics_battery_capacity 100
# HELP ups_metrics_load Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_load gauge
ups_metrics_load 261
# HELP ups_metrics_output_voltage Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_output_voltage gauge
ups_metrics_output_voltage 122
# HELP ups_metrics_rating_power Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_rating_power gauge
ups_metrics_rating_power 900
# HELP ups_metrics_rating_voltage Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_rating_voltage gauge
ups_metrics_rating_voltage 120
# HELP ups_metrics_remaining_runtime Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_remaining_runtime gauge
ups_metrics_remaining_runtime 22
# HELP ups_metrics_utility_voltage Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_utility_voltage gauge
ups_metrics_utility_voltage 122
Success!
Installing as a system service
I created the file /etc/systemd/system/upsmetrics.service
with the following content
[Unit]
Description=upsmetrics
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/ups_metrics
Restart=on-failure
RestartSec=30
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
Then, reload systemd and enable the upsmetrics service
sudo systemctl daemon-reload
sudo systemctl enable upsmetrics
sudo systemctl start upsmetrics
I followed these instructions to install node_exporter
(if you installed using a package manager your service file and paths may differ) -
here's my /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter --collector.textfile.directory=/var/lib/node_exporter
[Install]
WantedBy=multi-user.target
The significant line here is
ExecStart=/usr/local/bin/node_exporter --collector.textfile.directory=/var/lib/node_exporter
Which looks for /var/lib/node_exporter/*.prom
files - once I verified the service was running and could see my UPS metrics in the /metrics
endpoint I created this simple dashboard (I will write about the temperature metric in a future article).
Hope someone finds this helpful :)
:wq