From fb74f0e81c6358a4e4c54aa121361cfab6921bf9 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Fri, 19 Jan 2024 08:52:14 -0600 Subject: [PATCH] nut: Configure upsmon `upsmon` is the component of NUT that tracks the status of UPSs and reacts to their changing by sending notifications and/or shutting down the system. It is a networked application that can run on any system; it can run on a different system than `upsd`, and indeed can run on multiple systems simultaneously. Each system that runs `upsmon` will need a username and password for each UPS it will monitor. Using the CUE [function pattern][0], I've made it pretty simple to declare the necessary values under `nut.monitor`. [0]: https://cuetorials.com/patterns/functions/ --- app/nut/schema/schema.cue | 17 +++++++++++++++ app/nut/templates.cue | 23 ++++++++++++++++++++ env/prod/nut.cue | 21 +++++++++++++++++++ host/nut0.pyrocufflink.blue.cue | 6 ++++++ instructions/nut0.pyrocufflink.blue.cue | 1 + templates/nut/nut-monitor.container | 24 +++++++++++++++++++++ templates/nut/upsmon.conf | 28 +++++++++++++++++++++++++ 7 files changed, 120 insertions(+) create mode 100644 templates/nut/nut-monitor.container create mode 100644 templates/nut/upsmon.conf diff --git a/app/nut/schema/schema.cue b/app/nut/schema/schema.cue index 0b52123..39e8e92 100644 --- a/app/nut/schema/schema.cue +++ b/app/nut/schema/schema.cue @@ -26,8 +26,25 @@ package schema port: int } +#NutMonitorSystem: { + system: string + powervalue: int + username: string + password: string + role: "primary" | "secondary" +} + +#NutMonitor: { + monitor: [...#NutMonitorSystem] + minsupplies: int | *1 + shutdowncmd?: string + notifycmd?: string +} + #Nut: { listen: #Listen ups: [string]: #Ups users: [string]: #User + + monitor?: #NutMonitor } diff --git a/app/nut/templates.cue b/app/nut/templates.cue index 95d761d..e68f02d 100644 --- a/app/nut/templates.cue +++ b/app/nut/templates.cue @@ -51,3 +51,26 @@ templates: containerudev.templates + [ } }, ] + +monitor: templates: [ + { + template: "nut/upsmon.conf" + dest: "/etc/ups/upsmon.conf" + owner: "root" + group: "nut" + mode: "u=rw,g=r,o=" + hooks: { + changed: [{run: "systemctl try-reload-or-restart nut-server"}] + } + }, + { + template: "nut/nut-monitor.container" + dest: "/etc/containers/systemd/nut-monitor.container" + hooks: { + changed: [ + {run: "systemctl daemon-reload", immediate: true}, + {run: "systemctl restart nut-monitor"}, + ] + } + }, +] diff --git a/env/prod/nut.cue b/env/prod/nut.cue index c13318d..9b2e167 100644 --- a/env/prod/nut.cue +++ b/env/prod/nut.cue @@ -2,6 +2,8 @@ package prod import "du5t1n.me/cfg/app/nut/schema" +ups_server: "nut.pyrocufflink.blue" + nut: schema.#Nut & { listen: { address: "::" @@ -56,6 +58,25 @@ nut: schema.#Nut & { } } +#nut_monitor: { + #server: string | *ups_server + #username: string + #role: "primary" | *"secondary" + + monitor: [ + for k, v in nut.ups { + { + system: k + "@" + #server + powervalue: 1 + username: #username + password: nut.users[#username].password + role: #role + } + }, + ] + minsupplies: 1 +} + let _nut = nut collectd: nut: schema.#CollectdNut & { diff --git a/host/nut0.pyrocufflink.blue.cue b/host/nut0.pyrocufflink.blue.cue index b8543f1..abeeb04 100644 --- a/host/nut0.pyrocufflink.blue.cue +++ b/host/nut0.pyrocufflink.blue.cue @@ -6,4 +6,10 @@ import ( nut: prod.nut +nut: monitor: prod.#nut_monitor & { + #server: "localhost" + #username: "upsmon" + #role: "primary" +} + collectd: prod.collectd diff --git a/instructions/nut0.pyrocufflink.blue.cue b/instructions/nut0.pyrocufflink.blue.cue index 5dc00b3..aed1c12 100644 --- a/instructions/nut0.pyrocufflink.blue.cue +++ b/instructions/nut0.pyrocufflink.blue.cue @@ -10,5 +10,6 @@ import ( render: list.Concat([ collectd.templates, nut.templates, + nut.monitor.templates, nut.collectd.templates, ]) diff --git a/templates/nut/nut-monitor.container b/templates/nut/nut-monitor.container new file mode 100644 index 0000000..b82f43f --- /dev/null +++ b/templates/nut/nut-monitor.container @@ -0,0 +1,24 @@ +# vim: set ft=systemd : +[Unit] +Description=Network UPS Tools - power device monitor and shutdown controller +Wants=network-online.target +After=network-online.target +After=nut-server.service + +[Container] +Image=git.pyrocufflink.net/containerimages/nut:latest +Pull=newer +Exec=upsmon -F +User=nut +RunInit=true +Mount=type=tmpfs,dest=/run/nut,chown=true +Volume=/etc/ups:/etc/ups:ro +ReadOnly=true +VolatileTmp=true +Network=host + +[Service] +ExecReload=podman exec systemd-%N upsmon -c reload + +[Install] +WantedBy=multi-user.target diff --git a/templates/nut/upsmon.conf b/templates/nut/upsmon.conf new file mode 100644 index 0000000..3255591 --- /dev/null +++ b/templates/nut/upsmon.conf @@ -0,0 +1,28 @@ +{% for monitor in nut.monitor.monitor -%} +MONITOR {{ monitor.system }} {{ monitor.powervalue }} {{ monitor.username }} {{ monitor.password | decrypt }} {{ monitor.role }} +{% endfor %} +MINSUPPLIES {{ nut.monitor.minsupplies }} +{% if nut.monitor.shutdowncmd is defined -%} +SHUTDOWNCMD "{{ nut.monitor.shutdowncmd }}" +{% endif -%} +{% if nut.monitor.notifycmd is defined -%} +NOTIFYCMD {{ nut.monitor.notifycmd }} +{% endif %} +POLLFREQ 5 +POLLFREQALERT 5 +HOSTSYNC 15 +DEADTIME 15 +POWERDOWNFLAG /run/killpower +NOTIFYFLAG ONLINE SYSLOG+EXEC +NOTIFYFLAG ONBATT SYSLOG+EXEC +NOTIFYFLAG LOWBATT SYSLOG+EXEC +NOTIFYFLAG FSD SYSLOG+EXEC +NOTIFYFLAG COMMOK SYSLOG+EXEC +NOTIFYFLAG COMMBAD SYSLOG+EXEC +NOTIFYFLAG SHUTDOWN SYSLOG+EXEC +NOTIFYFLAG REPLBATT SYSLOG+EXEC +NOTIFYFLAG NOCOMM SYSLOG+EXEC +NOTIFYFLAG NOPARENT SYSLOG+EXEC +RBWARNTIME 43200 +NOCOMMWARNTIME 300 +FINALDELAY 5