Initial commit

master
Dustin 2024-01-11 20:53:17 -06:00
commit 778c6d440d
19 changed files with 269 additions and 0 deletions

17
.editorconfig Normal file
View File

@ -0,0 +1,17 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[**.k]
max_line_length = 79
indent_style = space
indent_size = 4
[**.sh]
max_line_length = 79
indent_style = space
indent_size = 4

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.kclvm
/keys.txt

7
app/nut/base/base.k Normal file
View File

@ -0,0 +1,7 @@
import pkg.nut
nut: nut.Nut {
users.upsmon = {
upsmon = "primary"
}
}

13
app/nut/config.sh Normal file
View File

@ -0,0 +1,13 @@
#!/bin/sh
set -e
: "${DESTDIR=/host}"
env="$1"
kcl nut/base nut/"${env}" -o nut.yaml
mkdir -p "${DESTDIR}"/etc/ups
tera --template nut/templates/ups.conf nut.yaml \
> "${DESTDIR}"/etc/ups/ups.conf
tera --template nut/templates/upsd.users nut.yaml \
> "${DESTDIR}"/etc/ups/upsd.users

24
app/nut/prod/main.k Normal file
View File

@ -0,0 +1,24 @@
import pkg.nut
nut: nut.Nut {
ups = {
apc1500 = {
driver = "usbhid-ups"
port = "auto"
desc = "Back-UPS XS 1500G"
vendorid = "051d"
product = ".*1500M.*"
pollonly = "enabled"
pollinterval = 1
}
apc1300 = {
driver = "usbhid-ups"
port = "auto"
desc = "Back-UPS XS 1300G"
vendorid = "051d"
product = ".*1300G.*"
pollonly = "enabled"
pollinterval = 1
}
}
}

23
app/nut/prod/secrets.k Normal file
View File

@ -0,0 +1,23 @@
import pkg.nut
nut: nut.Nut {
users.homeassistant.password = """\
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxZUZleGt3emxXdDFtcEtN
Wll3K0hrS2c2M1oyMWh2VlBnMER0bkZOb1VVCnU3aTI2eVJoV1dKNWxEd0VnbVNa
NlMxVWRuZWpNbTJRVUhWR2w3bUlwaEUKLS0tIGZLcGNQRy9LNUF1Y0JzZEZGdXBn
bUJjYlBSSEYwRUpwemlMZ0xCZnpTS2cKUFke27YDeTME9OBgEcQdbJ3jsDZS43km
tK61kLMcexq3lXQb30gx4fzMuYa0MXFygawscTnxTrOrXUd36Iga4A==
-----END AGE ENCRYPTED FILE-----
"""
users.upsmon.password = """\
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzdnZMZ1F1d05RRi84ZENT
SmdRNkFLWDRPMTBFTkdOdFRBMC8xK0gvUVV3CjRhWVljR3ZiU1hwWEJBN2hCcTM1
NWFQWWdmVm1XK1pKUHFnRjJjYXdDNjgKLS0tIERvOU44ellHdGZYVXRDMHN4NkpV
QkhtVlVQUS96UStlQWo2QWJISUlGL2cKc8AC3UujJMIafbV31pjAzniqSHBNwYDw
zhh094auKibUcg6Tbyc=
-----END AGE ENCRYPTED FILE-----
"""
}

23
app/nut/test/secrets.k Normal file
View File

@ -0,0 +1,23 @@
import pkg.nut
nut: nut.Nut {
users.dustin.password = """\
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsdWVBOXZpb3Fxclk4SGJv
N0JyeEthbzFOTzQySkw3cWZhSEk2Y1poWm0wCnhEVkJhWWtIdUxnVUFSa2xCSDNU
ak1rbzhaUzN1WUxKajdvOTluVGJwaGsKLS0tIG9NL3ZrMDY5Z2prMnNWL003cDRP
T3NySlREeXZoaVhGcUdTVmxrYXNyUDQKXFvRM9INd7E334OOYlFgp9pu1w4b3szL
yVmqMxpy98iHA6HKvuw=
-----END AGE ENCRYPTED FILE-----
"""
users.upsmon.password = """\
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzdnZMZ1F1d05RRi84ZENT
SmdRNkFLWDRPMTBFTkdOdFRBMC8xK0gvUVV3CjRhWVljR3ZiU1hwWEJBN2hCcTM1
NWFQWWdmVm1XK1pKUHFnRjJjYXdDNjgKLS0tIERvOU44ellHdGZYVXRDMHN4NkpV
QkhtVlVQUS96UStlQWo2QWJISUlGL2cKc8AC3UujJMIafbV31pjAzniqSHBNwYDw
zhh094auKibUcg6Tbyc=
-----END AGE ENCRYPTED FILE-----
"""
}

View File

@ -0,0 +1,4 @@
import pkg
import pkg.nut
render: [pkg.RenderInstruction] = nut.templates

View File

@ -0,0 +1,4 @@
kcl_cli_configs:
files:
- ../app/nut/base
- ../app/nut/test

5
kcl.mod Normal file
View File

@ -0,0 +1,5 @@
[package]
name = "dch"
edition = "0.0.1"
version = "0.0.1"

0
kcl.mod.lock Normal file
View File

17
pkg/instructions.k Normal file
View File

@ -0,0 +1,17 @@
schema Hook:
run: str
immediate?: bool
schema Hooks:
changed?: [Hook]
schema RenderInstruction:
template: str
dest: str
owner?: str
group?: str
mode?: str
hooks?: Hooks
schema Instructions:
render: [RenderInstruction]

35
pkg/nut/main.k Normal file
View File

@ -0,0 +1,35 @@
schema Ups:
"""Schema for ups.conf sections"""
driver: str
port?: str
desc?: str
vendorid?: str
productid?: str
product?: str
serial?: str
vendor?: str
bus?: str
pollonly?: "enabled" | "disabled"
pollinterval?: int
schema User:
"""Schema for upsd.users sections"""
password: str
actions?: [str]
instcmds?: [str]
upsmon?: "primary" | "secondary"
schema Listen:
"""Schema for listen socket address"""
address: str = "::"
port: int = 3493
schema Nut:
"""Schema for NUT configuration"""
listen = Listen()
ups: {str:Ups} = {}
users: {str:User} = {}

46
pkg/nut/templates.k Normal file
View File

@ -0,0 +1,46 @@
templates = [
{
template = "nut/nut.sysusers"
dest = "/etc/sysusers.d/nut.conf"
hooks = {
changed = [{
run = "systemd-sysusers %s"
immediate = True
}]
}
}
{
template = "nut/ups.conf"
dest = "/etc/ups/ups.conf"
hooks = {
changed = [{run = "systemctl try-restart nut-server"}]
}
}
{
template = "nut/upsd.conf"
dest = "/etc/ups/upsd.conf"
owner = "root"
group = "nut"
mode = "u=rw,g=r,o="
hooks = {
changed = [{run = "systemctl try-reload-or-restart nut-server"}]
}
}
{
template = "nut/upsd.users"
dest = "/etc/ups/upsd.users"
owner = "root"
group = "nut"
mode = "u=rw,g=r,o="
hooks = {
changed = [{run = "systemctl try-reload-or-restart nut-server"}]
}
}
{
template = "nut/nut-server.container"
dest = "/etc/containers/systemd/nut-server.container"
hooks = {
changed = [{run = "systemctl restart nut-server"}]
}
}
]

View File

@ -0,0 +1,23 @@
# vim: set ft=systemd :
[Unit]
Description=Network UPS Tools - power devices information server
Wants=network-online.target
After=network-online.target
Before=nut-monitor.service
[Container]
Image=git.pyrocufflink.net/containerimages/nut:latest
Pull=newer
RunInit=true
Volume=/etc/ups:/etc/ups:ro
Volume=/dev:/dev:rw
ReadOnly=true
VolatileTmp=true
PodmanArgs=--privileged
Network=host
[Service]
ExecReload=podman exec systemd-%N upsd -c reload
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,4 @@
g nut 57
u nut 57:57 "Network UPS Tools" /var/lib/ups /bin/false
m nut tty
m nut dialout

7
templates/nut/ups.conf Normal file
View File

@ -0,0 +1,7 @@
{% for name, ups in nut.ups %}{% if not loop.first %}
{% endif -%}
[{{ name }}]
{% for key, value in ups -%}
{{ key }} = "{{ value }}"
{% endfor -%}
{% endfor -%}

1
templates/nut/upsd.conf Normal file
View File

@ -0,0 +1 @@
LISTEN {{ nut.listen.address }} {{ nut.listen.port }}

14
templates/nut/upsd.users Normal file
View File

@ -0,0 +1,14 @@
{% for username, settings in nut.users %}{% if not loop.first %}
{% endif -%}
[{{ username }}]
password = {{ settings.password | decrypt }}
{% for action in settings.actions|default(value=[]) -%}
actions = {{ action }}
{% endfor -%}
{% for instcmds in settings.instcmds|default(value=[]) -%}
instcmds = {{ instcmds }}
{% endfor -%}
{% if settings.upsmon is defined -%}
upsmon {{ settings.upsmon }}
{% endif -%}
{% endfor -%}