From 1e1d53ecd86d696c7b903dd2eeefd0ff4d12c391 Mon Sep 17 00:00:00 2001 From: Bastian de Byl Date: Sat, 25 Jul 2026 12:01:16 -0400 Subject: [PATCH 1/6] feat(gitea-actions): install jq in the ESP-IDF CI image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit esp32-stm32-vcu's scripts/release.sh uses jq to rewrite the protocol manifest before publishing it to S3. The image did not have it, so the release aborted at: ./scripts/release.sh: line 61: jq: command not found The failure mode is nastier than a red job. jq is only reached *after* the firmware .bin and version.json have already been uploaded, so clients were served the new build while the git tag, the Gitea release and the protocol manifest were never written — the repo still showed the previous release as latest while production served a newer one. Only jq was missing; aws and sha256sum are already present (verified in the rebuilt image: jq-1.7, aws-ok, sha256sum-ok). This surfaced when the skudak firmware jobs were moved into this image (they previously ran on the default gitea-ci image, which has jq). --- .../gitea-actions/templates/Containerfile.espidf.j2 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ansible/roles/gitea-actions/templates/Containerfile.espidf.j2 b/ansible/roles/gitea-actions/templates/Containerfile.espidf.j2 index b849d61..6a64850 100644 --- a/ansible/roles/gitea-actions/templates/Containerfile.espidf.j2 +++ b/ansible/roles/gitea-actions/templates/Containerfile.espidf.j2 @@ -1,16 +1,23 @@ # ESP-IDF firmware job image (managed by ansible: roles/gitea-actions). # Adds node (required by actions/checkout and other JS actions), the AWS CLI -# (firmware artifacts ship to S3), and the common-yaml header generator's Python -# deps on top of the official Espressif toolchain. +# (firmware artifacts ship to S3), jq (the release script rewrites the protocol +# manifest with it), and the common-yaml header generator's Python deps on top +# of the official Espressif toolchain. # IDF lives at /opt/esp/idf — firmware jobs source /opt/esp/idf/export.sh. # python3-yaml + python3-jinja2 are installed as distro packages so the # common-yaml generator runs with a plain `python3 generate.py` — no pip at job # time (the base image's system Python is PEP 668 externally-managed) and no # need to source the IDF venv just to generate headers. +# +# jq is required by esp32-stm32-vcu scripts/release.sh, which publishes the +# protocol manifest and BLE_API.md to S3 after the firmware upload. Without it +# the release aborts *after* the firmware and version.json are already live — +# clients get the new build while the tag, Gitea release and protocol manifest +# are never written. Keep it installed. FROM espressif/idf:{{ esp_idf_version }} RUN apt-get update && apt-get install -y --no-install-recommends \ - curl ca-certificates unzip python3-yaml python3-jinja2 \ + curl ca-certificates unzip jq python3-yaml python3-jinja2 \ && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ && apt-get install -y --no-install-recommends nodejs \ && rm -rf /var/lib/apt/lists/* From 0ab423ca550cd6a290495e2596d41bab5d0b1474 Mon Sep 17 00:00:00 2001 From: Bastian de Byl Date: Sun, 26 Jul 2026 16:03:18 -0400 Subject: [PATCH 2/6] harden nextcloud backups: db dumps, alerting, drift fix The data-only rsync left no way to restore a working instance: mysql/ and config/ were never backed up, so a recovery would have files but no shares, users or metadata. Dump the database before syncing files (a DB older than the files is repairable with occ files:scan; a newer one references blobs that never made it into the backup) and ship config/ alongside it. Capture the --chmod=Du=rwx,Dgo=rx flag that had been hand-added to the deployed skudak-cloud script. It was outside git, so every deploy silently reverted it. It now lives in backup_rsync_extra_args. Add OnFailure= alerting. The units failed silently before, which is how an iDrive sync failure sat unnoticed since May. msmtp rather than the esmtp already installed: the OpenSRS relay is port 465 (implicit TLS) and libesmtp only speaks STARTTLS. Exclude nextcloud.log* from the sync and cap log_rotate_size. skudak-cloud was running at loglevel 0 and had written a 64 GB log that was being rsynced and pushed to S3; set it to 2 to match the home instance. Stagger the timers (04:00 / 04:30) so both finish before the 05:00 TrueNAS snapshot task, and bound TimeoutStartSec so a wedged rsync cannot leave the unit activating forever and skip every subsequent trigger. Co-Authored-By: Claude Opus 5 (1M context) --- ansible/roles/common/tasks/mail.yml | 34 ++++++ ansible/roles/common/tasks/main.yml | 3 + .../roles/common/templates/msmtp/msmtprc.j2 | 17 +++ ansible/roles/podman/defaults/main.yml | 2 + .../podman/tasks/containers/cloud-backup.yml | 21 ++++ .../podman/tasks/containers/home/cloud.yml | 14 +++ .../podman/tasks/containers/skudak/cloud.yml | 32 +++++ .../nextcloud/cloud-backup.service.j2 | 8 ++ .../templates/nextcloud/cloud-backup.sh.j2 | 111 +++++++++++++++++- .../templates/nextcloud/cloud-backup.timer.j2 | 3 +- .../nextcloud/nextcloud-backup-alert.sh.j2 | 40 +++++++ .../nextcloud-backup-failed@.service.j2 | 6 + 12 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 ansible/roles/common/tasks/mail.yml create mode 100644 ansible/roles/common/templates/msmtp/msmtprc.j2 create mode 100644 ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 create mode 100644 ansible/roles/podman/templates/nextcloud/nextcloud-backup-failed@.service.j2 diff --git a/ansible/roles/common/tasks/mail.yml b/ansible/roles/common/tasks/mail.yml new file mode 100644 index 0000000..5b14498 --- /dev/null +++ b/ansible/roles/common/tasks/mail.yml @@ -0,0 +1,34 @@ +--- +# Outbound mail for system notifications (backup failures, cron output). +# +# msmtp rather than the esmtp already on the box: the OpenSRS relay uses port +# 465, which is IMPLICIT TLS, and libesmtp/esmtp only speaks STARTTLS. msmtp +# handles implicit TLS via `tls_starttls off` and mirrors the settings the +# TrueNAS box already uses. +# On Fedora the single `msmtp` package already ships /usr/bin/sendmail and +# /usr/lib/sendmail; there is no separate msmtp-sendmail package (Debian split). +- name: install msmtp + become: true + ansible.builtin.package: + name: msmtp + state: present + tags: mail + +- name: configure msmtp + become: true + ansible.builtin.template: + src: msmtp/msmtprc.j2 + dest: /etc/msmtprc + owner: root + group: root + mode: 0600 + no_log: true + tags: mail + +- name: point the mta alternative at msmtp + become: true + community.general.alternatives: + name: mta + path: /usr/bin/msmtp-sendmail + failed_when: false + tags: mail diff --git a/ansible/roles/common/tasks/main.yml b/ansible/roles/common/tasks/main.yml index db08c36..8a7ba73 100644 --- a/ansible/roles/common/tasks/main.yml +++ b/ansible/roles/common/tasks/main.yml @@ -3,6 +3,9 @@ - import_tasks: security.yml - import_tasks: service.yml +- import_tasks: mail.yml + tags: mail + - import_tasks: fluent-bit.yml tags: fluent-bit, graylog diff --git a/ansible/roles/common/templates/msmtp/msmtprc.j2 b/ansible/roles/common/templates/msmtp/msmtprc.j2 new file mode 100644 index 0000000..20f29db --- /dev/null +++ b/ansible/roles/common/templates/msmtp/msmtprc.j2 @@ -0,0 +1,17 @@ +# {{ ansible_managed }} +defaults +auth on +tls on +tls_trust_file /etc/pki/tls/certs/ca-bundle.crt +logfile /var/log/msmtp.log + +account home +host {{ system_smtp_host | default('mail.b.hostedemail.com') }} +port {{ system_smtp_port | default(465) }} +# Port 465 is implicit TLS (SMTPS), not STARTTLS. +tls_starttls off +from {{ system_smtp_from | default('home@bdebyl.net') }} +user {{ system_smtp_user | default('home@bdebyl.net') }} +password {{ home_smtp }} + +account default : home diff --git a/ansible/roles/podman/defaults/main.yml b/ansible/roles/podman/defaults/main.yml index 70a8b88..7204e2d 100644 --- a/ansible/roles/podman/defaults/main.yml +++ b/ansible/roles/podman/defaults/main.yml @@ -1,4 +1,6 @@ --- +# Where Nextcloud backup failure alerts are mailed (see containers/cloud-backup.yml). +backup_alert_email: bastian@debyl.io bookstack_path: "{{ podman_volumes }}/bookstack" cam2ip_path: "{{ podman_volumes }}/cam2ip" cloud_path: "{{ podman_volumes }}/cloud" diff --git a/ansible/roles/podman/tasks/containers/cloud-backup.yml b/ansible/roles/podman/tasks/containers/cloud-backup.yml index 00bd37d..40fb012 100644 --- a/ansible/roles/podman/tasks/containers/cloud-backup.yml +++ b/ansible/roles/podman/tasks/containers/cloud-backup.yml @@ -28,6 +28,27 @@ mode: 0755 setype: bin_t +# Shared by every backup instance. Rendered once per include; the second and +# later renders are no-ops. +- name: template nextcloud backup alert script + become: true + ansible.builtin.template: + src: nextcloud/nextcloud-backup-alert.sh.j2 + dest: /usr/local/bin/nextcloud-backup-alert.sh + owner: root + group: root + mode: 0755 + setype: bin_t + +- name: template nextcloud backup failure handler unit + become: true + ansible.builtin.template: + src: nextcloud/nextcloud-backup-failed@.service.j2 + dest: /etc/systemd/system/nextcloud-backup-failed@.service + owner: root + group: root + mode: 0644 + - name: template {{ backup_name }} backup systemd service become: true ansible.builtin.template: diff --git a/ansible/roles/podman/tasks/containers/home/cloud.yml b/ansible/roles/podman/tasks/containers/home/cloud.yml index 0dddc81..23a2824 100644 --- a/ansible/roles/podman/tasks/containers/home/cloud.yml +++ b/ansible/roles/podman/tasks/containers/home/cloud.yml @@ -84,10 +84,24 @@ vars: container_name: cloud +# Unbounded by default: nextcloud.log.1 had reached 1.12 GB and was being +# rsynced to TrueNAS and pushed to S3 on every run. Cap at 10 MiB. +- name: cap nextcloud log rotation size for cloud + become: true + become_user: "{{ podman_user }}" + ansible.builtin.command: > + podman exec -u www-data cloud + php occ config:system:set log_rotate_size --value 10485760 --type integer + register: cloud_log_rotate + changed_when: "'System config value log_rotate_size' in cloud_log_rotate.stdout" + failed_when: false + - include_tasks: containers/cloud-backup.yml vars: backup_name: cloud data_path: "{{ cloud_path }}/data" + config_path: "{{ cloud_path }}/config" + db_container: cloud-db ssh_key_path: /etc/ssh/backup_keys/cloud ssh_key_content: "{{ cloud_backup_ssh_key }}" ssh_user: cloud diff --git a/ansible/roles/podman/tasks/containers/skudak/cloud.yml b/ansible/roles/podman/tasks/containers/skudak/cloud.yml index ab2df47..1175948 100644 --- a/ansible/roles/podman/tasks/containers/skudak/cloud.yml +++ b/ansible/roles/podman/tasks/containers/skudak/cloud.yml @@ -131,12 +131,44 @@ changed_when: "'System config value trusted_domains' in trusted_domain_result.stdout" failed_when: false +# This instance was left at loglevel 0 (DEBUG) and had written a 64 GB +# nextcloud.log, almost entirely repeated deprecation notices. 2 = Warning, +# which is both the Nextcloud default and what the home instance already uses. +- name: set nextcloud loglevel for skudak-cloud + become: true + become_user: "{{ podman_user }}" + ansible.builtin.command: > + podman exec -u www-data skudak-cloud + php occ config:system:set loglevel --value 2 --type integer + register: skudak_loglevel + changed_when: "'System config value loglevel' in skudak_loglevel.stdout" + failed_when: false + +# Unbounded by default; see the equivalent task in containers/home/cloud.yml. +- name: cap nextcloud log rotation size for skudak-cloud + become: true + become_user: "{{ podman_user }}" + ansible.builtin.command: > + podman exec -u www-data skudak-cloud + php occ config:system:set log_rotate_size --value 10485760 --type integer + register: skudak_log_rotate + changed_when: "'System config value log_rotate_size' in skudak_log_rotate.stdout" + failed_when: false + - include_tasks: containers/cloud-backup.yml vars: backup_name: skudak-cloud data_path: "{{ cloud_skudak_path }}/data" + config_path: "{{ cloud_skudak_path }}/config" + db_container: skudak-cloud-db ssh_key_path: /etc/ssh/backup_keys/skudak-cloud ssh_key_content: "{{ cloud_skudak_backup_ssh_key }}" ssh_user: skucloud remote_path: /mnt/glacier/skudakcloud script_path: /usr/local/bin/skudak-cloud-backup.sh + # skudakcloud/data is mode 770, so the receiving side needs traversable + # dirs. This flag was hand-added on the host and was being silently + # reverted by every `make deploy TAGS=skudak-cloud`; it now lives in git. + backup_rsync_extra_args: "--chmod=Du=rwx,Dgo=rx" + # Staggered so both instances finish before the 05:00 TrueNAS snapshot. + backup_oncalendar: "*-*-* 04:30:00" diff --git a/ansible/roles/podman/templates/nextcloud/cloud-backup.service.j2 b/ansible/roles/podman/templates/nextcloud/cloud-backup.service.j2 index 626c635..4183855 100644 --- a/ansible/roles/podman/templates/nextcloud/cloud-backup.service.j2 +++ b/ansible/roles/podman/templates/nextcloud/cloud-backup.service.j2 @@ -1,6 +1,14 @@ [Unit] Description=Nextcloud {{ instance_name }} backup to TrueNAS +After=network-online.target +Wants=network-online.target +OnFailure=nextcloud-backup-failed@%n.service [Service] Type=oneshot ExecStart={{ script_path }} +# Type=oneshot disables the start timeout by default, so a wedged rsync would +# leave the unit "activating" forever and every subsequent daily trigger would +# be silently skipped. Bound it. +TimeoutStartSec={{ backup_timeout | default('4h') }} +Nice=10 diff --git a/ansible/roles/podman/templates/nextcloud/cloud-backup.sh.j2 b/ansible/roles/podman/templates/nextcloud/cloud-backup.sh.j2 index 50a656f..8153153 100644 --- a/ansible/roles/podman/templates/nextcloud/cloud-backup.sh.j2 +++ b/ansible/roles/podman/templates/nextcloud/cloud-backup.sh.j2 @@ -1,4 +1,111 @@ #!/bin/bash +# {{ ansible_managed }} +# Nextcloud "{{ backup_name }}" -> truenas.localdomain. +# +# Ordering is deliberate: the database is dumped BEFORE the file tree is +# synced. A DB snapshot slightly OLDER than the files degrades to "files +# Nextcloud has not indexed yet" and is repaired with `occ files:scan`. A DB +# snapshot NEWER than the files references blobs that never made it into the +# backup, which surfaces as broken shares and dead file entries on restore. set -euo pipefail -rsync -az --exclude .ssh -e "ssh -i {{ ssh_key_path }} -o StrictHostKeyChecking=accept-new" \ - {{ data_path }}/ {{ ssh_user }}@truenas.localdomain:{{ remote_path }}/ + +TAG=nextcloud-backup +INSTANCE={{ backup_name }} +STAGE={{ backup_stage_path | default('/var/backups/nextcloud/' ~ backup_name) }} +KEEP={{ backup_db_keep | default(7) }} +DUMP="$STAGE/db/${INSTANCE}-$(date +%Y%m%d).sql.gz" + +log() { logger -t "$TAG" -p daemon.info -- "instance=$INSTANCE $*"; echo "$TAG: $*"; } +fail() { logger -t "$TAG" -p daemon.err -- "instance=$INSTANCE status=failed $*" + echo "$TAG: FAILED: $*" >&2; exit 1; } + +SSH="ssh -i {{ ssh_key_path }} -o StrictHostKeyChecking=accept-new -o ServerAliveInterval=30 -o ServerAliveCountMax=6" +DEST={{ ssh_user }}@truenas.localdomain + +log "status=start" + +{% if db_container | default('') %} +# ------------------------------------------------------------ 1. database +# The Nextcloud containers are ROOTLESS podman owned by "{{ podman_user }}", +# but this script runs as root under systemd. Every podman call therefore +# goes through sudo: +# -H HOME becomes the podman user's home, so podman finds its rootless +# graph root under ~/.local/share/containers +# cd; required preamble (see CLAUDE.md) so the shell starts in that home +# XDG_RUNTIME_DIR the podman user's runtime dir. Lingering is enabled by +# roles/podman/tasks/podman/podman.yml so /run/user/ exists; +# guarded anyway so podman falls back cleanly if it ever does not. +# +# No credential is stored in this file or placed on a host command line: +# $MYSQL_ROOT_PASSWORD and $MYSQL_DATABASE are expanded by the shell INSIDE +# the database container, which already carries them in its environment. +# +# Flags are deliberately minimal. --events and --routines were both tried and +# both abort the dump on these instances: the event scheduler is disabled +# (error 1577) and mysql.proc reads as corrupted (error 1728, an artefact of +# an image bump without mariadb-upgrade). Nextcloud uses neither events nor +# stored routines, so dropping them loses nothing. +pexec() { + sudo -H -u {{ podman_user }} bash -c \ + 'cd; d=/run/user/$(id -u); [ -d "$d" ] && export XDG_RUNTIME_DIR="$d" + exec podman "$@"' _ "$@" +} + +install -d -m 0700 "$STAGE" "$STAGE/db" +tmp="$DUMP.tmp" +rm -f "$tmp" + +log "dumping {{ db_container }}" +set +e +pexec exec {{ db_container }} sh -c ' + exec env MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mariadb-dump -u root \ + --single-transaction --quick --triggers \ + --no-tablespaces --default-character-set=utf8mb4 "$MYSQL_DATABASE" +' | gzip -6 > "$tmp" +dump_rc=${PIPESTATUS[0]} +set -e +[ "$dump_rc" -eq 0 ] || fail "mariadb-dump {{ db_container }} exited $dump_rc" + +# A new dump is promoted over yesterday's only after it proves complete: +# a valid gzip stream AND the "-- Dump completed" trailer that mariadb-dump +# writes only on a clean finish. `mv` is atomic within the staging +# filesystem, so a failed or truncated run can never replace a good dump. +gzip -t "$tmp" || fail "dump is not a valid gzip stream" +gunzip -c "$tmp" | tail -c 512 | grep -q 'Dump completed' \ + || fail "dump is truncated (no completion trailer)" +mv -f "$tmp" "$DUMP" +log "db_dump=ok bytes=$(stat -c %s "$DUMP")" + +# Local retention. The staging sync below mirrors with --delete, so remote +# retention follows the same window; deeper history comes from the TrueNAS +# periodic ZFS snapshots (see roles/podman/README.md). +ls -1t "$STAGE"/db/"$INSTANCE"-*.sql.gz | tail -n +$((KEEP + 1)) | xargs -r rm -f +{% endif %} + +# ----------------------------------------------------------- 2. file tree +# --exclude .ssh is load-bearing: {{ remote_path }} IS {{ ssh_user }}'s home +# on TrueNAS and its authorized_keys lives there, so a `.ssh` directory +# appearing in the data tree must never be shipped. No --delete here: the +# data tree is append-mostly and a source-side mishap must not propagate. +log "syncing data" +rsync -az --timeout=1800 --exclude .ssh \ + {{ backup_rsync_excludes | default("--exclude '/nextcloud.log*' --exclude '/updater.log'") }} \ + {{ backup_rsync_extra_args | default('') }} \ + -e "$SSH" {{ data_path }}/ "$DEST:{{ remote_path }}/" + +# ------------------------------------------- 3. config/ and database dumps +{# --mkpath creates the nested _backup// destination; rsync will not build + more than one missing level on its own. Requires rsync >= 3.2.3 on both + ends (galactica 3.4.1, truenas 3.2.7). #} +{% if config_path | default('') %} +log "syncing config" +rsync -az --timeout=600 --delete --mkpath {{ backup_rsync_extra_args | default('') }} \ + -e "$SSH" {{ config_path }}/ "$DEST:{{ remote_path }}/_backup/config/" +{% endif %} +{% if db_container | default('') %} +log "syncing db dumps" +rsync -az --timeout=600 --delete --mkpath {{ backup_rsync_extra_args | default('') }} \ + -e "$SSH" "$STAGE/db/" "$DEST:{{ remote_path }}/_backup/db/" +{% endif %} + +log "status=ok" diff --git a/ansible/roles/podman/templates/nextcloud/cloud-backup.timer.j2 b/ansible/roles/podman/templates/nextcloud/cloud-backup.timer.j2 index ca27957..0982d9f 100644 --- a/ansible/roles/podman/templates/nextcloud/cloud-backup.timer.j2 +++ b/ansible/roles/podman/templates/nextcloud/cloud-backup.timer.j2 @@ -2,7 +2,8 @@ Description=Daily Nextcloud {{ instance_name }} backup [Timer] -OnCalendar=*-*-* 04:00:00 +OnCalendar={{ backup_oncalendar | default('*-*-* 04:00:00') }} +RandomizedDelaySec={{ backup_randomized_delay | default('5m') }} Persistent=true [Install] diff --git a/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 b/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 new file mode 100644 index 0000000..c461bbb --- /dev/null +++ b/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 @@ -0,0 +1,40 @@ +#!/bin/bash +# {{ ansible_managed }} +# OnFailure= handler for the Nextcloud backup units. Invoked as: +# nextcloud-backup-alert.sh +# +# Deliberately NOT `set -e`: an alert handler that dies partway through +# reports nothing, which is worse than a partial report. Same reasoning as +# roles/ups/templates/ups-restore.sh.j2. +set -uo pipefail + +TAG=nextcloud-backup +UNIT="${1:-unknown}" +TO="{{ backup_alert_email | default('root') }}" +HOST="$(hostname -f 2>/dev/null || hostname)" + +result="$(systemctl show -p Result --value "$UNIT" 2>/dev/null)" +code="$(systemctl show -p ExecMainStatus --value "$UNIT" 2>/dev/null)" + +# One machine-parseable line for Graylog, then the context. +logger -t "$TAG" -p daemon.err -- \ + "status=failed unit=$UNIT result=${result:-unknown} exit=${code:-unknown}" + +body="$(printf 'Nextcloud backup FAILED on %s\n\nunit: %s\nresult: %s\nexit: %s\n\n--- last 40 journal lines ---\n' \ + "$HOST" "$UNIT" "${result:-unknown}" "${code:-unknown}") +$(journalctl -u "$UNIT" -n 40 --no-pager -o cat 2>/dev/null)" + +echo "$body" | logger -t "$TAG" -p daemon.err + +# Mail is best-effort: if the MTA is not configured the journald record above +# is still the authoritative signal, so never fail the handler on this. +if command -v sendmail >/dev/null 2>&1; then + printf 'To: %s\nSubject: [%s] Nextcloud backup FAILED: %s\nContent-Type: text/plain; charset=UTF-8\n\n%s\n' \ + "$TO" "$HOST" "$UNIT" "$body" | sendmail -t \ + && logger -t "$TAG" -p daemon.info -- "alert_mail=sent to=$TO" \ + || logger -t "$TAG" -p daemon.err -- "alert_mail=failed to=$TO" +else + logger -t "$TAG" -p daemon.err -- "alert_mail=skipped reason=no-sendmail" +fi + +exit 0 diff --git a/ansible/roles/podman/templates/nextcloud/nextcloud-backup-failed@.service.j2 b/ansible/roles/podman/templates/nextcloud/nextcloud-backup-failed@.service.j2 new file mode 100644 index 0000000..5da3b87 --- /dev/null +++ b/ansible/roles/podman/templates/nextcloud/nextcloud-backup-failed@.service.j2 @@ -0,0 +1,6 @@ +[Unit] +Description=Report failure of %i + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/nextcloud-backup-alert.sh %i From 7c72aee4f959abd139490a92d4e345e501b2f018 Mon Sep 17 00:00:00 2001 From: Bastian de Byl Date: Sun, 26 Jul 2026 17:22:30 -0400 Subject: [PATCH 3/6] add home_smtp vault key for system mail Referenced by roles/common/templates/msmtp/msmtprc.j2 to authenticate outbound alert mail against the OpenSRS relay. Without this committed a fresh checkout cannot render /etc/msmtprc. Co-Authored-By: Claude Opus 5 (1M context) --- ansible/vars/vault.yml | Bin 27852 -> 28629 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ansible/vars/vault.yml b/ansible/vars/vault.yml index 83294d18312b0f9bcc73839b737dff710c76bd44..371e48f8cf878eabddbece4babe961b308787ef4 100644 GIT binary patch literal 28629 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

7MP-gqcHWn}j=^KG3TuZD^SbE~*|^^3AZF^eLw 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 vmEQdigshbwy Date: Sun, 26 Jul 2026 20:04:32 -0400 Subject: [PATCH 4/6] restore full dump flags; stop spurious alerts claiming FAILED mariadb-upgrade --force has now been run against both instances and repaired the system tables, so --routines and --events no longer abort the dump. Restore them for completeness. Both verified: rc=0 with a clean completion trailer, and both Nextcloud instances report installed with unchanged table counts afterwards. The upgrade still exits non-zero on these containers because it cannot create the `sys` schema: /var/lib/mysql is owned by daemon rather than mysql, so mysqld may not create top-level databases. `sys` is diagnostic only and unused by Nextcloud, but the same permission would block creating any new database, so it is recorded in the template comment. The alert handler claimed FAILED in its subject line regardless of what the unit actually reported, so starting it by hand mailed out a failure notice for a run that succeeded. Derive the subject and log line from the real Result, and emit status=spurious rather than status=failed so such triggers cannot match a Graylog alert rule keyed on genuine failures. Co-Authored-By: Claude Opus 5 (1M context) --- .../templates/nextcloud/cloud-backup.sh.j2 | 18 +++++++---- .../nextcloud/nextcloud-backup-alert.sh.j2 | 32 ++++++++++++++----- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/ansible/roles/podman/templates/nextcloud/cloud-backup.sh.j2 b/ansible/roles/podman/templates/nextcloud/cloud-backup.sh.j2 index 8153153..7ad1ed5 100644 --- a/ansible/roles/podman/templates/nextcloud/cloud-backup.sh.j2 +++ b/ansible/roles/podman/templates/nextcloud/cloud-backup.sh.j2 @@ -40,11 +40,17 @@ log "status=start" # $MYSQL_ROOT_PASSWORD and $MYSQL_DATABASE are expanded by the shell INSIDE # the database container, which already carries them in its environment. # -# Flags are deliberately minimal. --events and --routines were both tried and -# both abort the dump on these instances: the event scheduler is disabled -# (error 1577) and mysql.proc reads as corrupted (error 1728, an artefact of -# an image bump without mariadb-upgrade). Nextcloud uses neither events nor -# stored routines, so dropping them loses nothing. +# --routines and --events initially aborted the dump here: mysql.proc read as +# corrupted (error 1728) and the event scheduler reported disabled (1577), +# both artefacts of an image bump without mariadb-upgrade. `mariadb-upgrade +# --force` has since been run against both instances and repaired the system +# tables, so the full flag set works and is kept for completeness. +# +# Note that upgrade still exits non-zero on these containers: it cannot +# install the `sys` schema because the datadir root (/var/lib/mysql) is owned +# by daemon rather than mysql, so mysqld may not create new top-level +# databases. `sys` is purely diagnostic and unused by Nextcloud, so this is +# cosmetic -- but it does mean creating a NEW database would fail too. pexec() { sudo -H -u {{ podman_user }} bash -c \ 'cd; d=/run/user/$(id -u); [ -d "$d" ] && export XDG_RUNTIME_DIR="$d" @@ -59,7 +65,7 @@ log "dumping {{ db_container }}" set +e pexec exec {{ db_container }} sh -c ' exec env MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mariadb-dump -u root \ - --single-transaction --quick --triggers \ + --single-transaction --quick --routines --events --triggers \ --no-tablespaces --default-character-set=utf8mb4 "$MYSQL_DATABASE" ' | gzip -6 > "$tmp" dump_rc=${PIPESTATUS[0]} diff --git a/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 b/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 index c461bbb..1e72623 100644 --- a/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 +++ b/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 @@ -16,21 +16,37 @@ HOST="$(hostname -f 2>/dev/null || hostname)" result="$(systemctl show -p Result --value "$UNIT" 2>/dev/null)" code="$(systemctl show -p ExecMainStatus --value "$UNIT" 2>/dev/null)" -# One machine-parseable line for Graylog, then the context. -logger -t "$TAG" -p daemon.err -- \ - "status=failed unit=$UNIT result=${result:-unknown} exit=${code:-unknown}" +# Only claim a failure when the unit actually reports one. Starting this +# handler by hand (or any other spurious trigger) would otherwise mail out a +# subject line saying FAILED about a run that succeeded. Keeping status=failed +# exact also stops such triggers matching the Graylog alert rule. +if [ "${result:-success}" = "success" ]; then + state=spurious + prio=daemon.warning + headline="$(printf 'Nextcloud backup alert handler was invoked on %s, but %s reports SUCCESS.\nThis is not a backup failure -- most likely the handler was started manually.' "$HOST" "$UNIT")" + subject="[$HOST] Nextcloud backup alert (spurious, unit OK): $UNIT" +else + state=failed + prio=daemon.err + headline="Nextcloud backup FAILED on $HOST" + subject="[$HOST] Nextcloud backup FAILED: $UNIT" +fi -body="$(printf 'Nextcloud backup FAILED on %s\n\nunit: %s\nresult: %s\nexit: %s\n\n--- last 40 journal lines ---\n' \ - "$HOST" "$UNIT" "${result:-unknown}" "${code:-unknown}") +# One machine-parseable line for Graylog, then the context. +logger -t "$TAG" -p "$prio" -- \ + "status=$state unit=$UNIT result=${result:-unknown} exit=${code:-unknown}" + +body="$(printf '%s\n\nunit: %s\nresult: %s\nexit: %s\n\n--- last 40 journal lines ---\n' \ + "$headline" "$UNIT" "${result:-unknown}" "${code:-unknown}") $(journalctl -u "$UNIT" -n 40 --no-pager -o cat 2>/dev/null)" -echo "$body" | logger -t "$TAG" -p daemon.err +echo "$body" | logger -t "$TAG" -p "$prio" # Mail is best-effort: if the MTA is not configured the journald record above # is still the authoritative signal, so never fail the handler on this. if command -v sendmail >/dev/null 2>&1; then - printf 'To: %s\nSubject: [%s] Nextcloud backup FAILED: %s\nContent-Type: text/plain; charset=UTF-8\n\n%s\n' \ - "$TO" "$HOST" "$UNIT" "$body" | sendmail -t \ + printf 'To: %s\nSubject: %s\nContent-Type: text/plain; charset=UTF-8\n\n%s\n' \ + "$TO" "$subject" "$body" | sendmail -t \ && logger -t "$TAG" -p daemon.info -- "alert_mail=sent to=$TO" \ || logger -t "$TAG" -p daemon.err -- "alert_mail=failed to=$TO" else From 16145fb6bdde55a93b45e4c617fdab053cf56736 Mon Sep 17 00:00:00 2001 From: Bastian de Byl Date: Sun, 26 Jul 2026 20:34:45 -0400 Subject: [PATCH 5/6] only email genuine backup failures A spurious invocation carries no action for a reader, so mailing it just trains them to skip past the subject line -- which defeats the point of the alert. Send mail only when the unit actually reports failure; spurious triggers still leave their journald record, so they stay greppable and can still feed a Graylog rule. Verified both paths: a spurious trigger leaves /var/log/msmtp.log untouched and logs alert_mail=skipped reason=spurious, while a genuinely failed unit still composes mail with the FAILED subject. Co-Authored-By: Claude Opus 5 (1M context) --- .../templates/nextcloud/nextcloud-backup-alert.sh.j2 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 b/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 index 1e72623..9598b8e 100644 --- a/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 +++ b/ansible/roles/podman/templates/nextcloud/nextcloud-backup-alert.sh.j2 @@ -42,6 +42,15 @@ $(journalctl -u "$UNIT" -n 40 --no-pager -o cat 2>/dev/null)" echo "$body" | logger -t "$TAG" -p "$prio" +# Only genuine failures are worth an email. A spurious invocation carries no +# action for a human, and mailing it trains the reader to ignore the subject +# line -- which defeats the point of having the alert at all. The journald +# record above is kept either way, so spurious triggers stay greppable. +if [ "$state" != "failed" ]; then + logger -t "$TAG" -p daemon.info -- "alert_mail=skipped reason=$state" + exit 0 +fi + # Mail is best-effort: if the MTA is not configured the journald record above # is still the authoritative signal, so never fail the handler on this. if command -v sendmail >/dev/null 2>&1; then From 5776dbe1bf2f0e493901dd72f2853472e065d68a Mon Sep 17 00:00:00 2001 From: Bastian de Byl Date: Thu, 30 Jul 2026 13:00:51 -0400 Subject: [PATCH 6/6] UPS monitoring, Nextcloud cron, container image bumps ups role: NUT server on home.debyl.io for the CyberPower PR1500RT2U that backs both it and truenas.localdomain, with staged shutdown (TrueNAS sheds at t+2min, host at 10% charge) and best-effort IPMI power-on when mains returns. The 10% threshold leans on ignorelb + override.battery.charge.low rather than a custom poller, because CyberPower asserts its own low-battery flag far too early. Credentials come from vault vars; nothing sensitive is templated in the clear. Nextcloud background jobs: both instances have backgroundjobs_mode "cron", which expects an external caller every ~5 minutes, and nothing was calling. The personal instance had not run a background job since 2026-05-14 and skudak since 2024-11-20. Consequently trash and file versions never expired, stale chunked uploads accumulated, calendar reminders never fired, and nextcloud.log was never rotated -- which quietly made the existing log_rotate_size cap inert. Added a systemd timer per instance, skipping cleanly when the container is down or in maintenance so deploy windows don't show up as failed units. Trash retention on the personal instance: the default "auto" only expires when disk space demands it, so 66 GB of >30-day deletions sat on a host with 1.3 TB free -- effectively unbounded. "auto, 30" makes the 30-day expiry unconditional while still purging early under pressure. Image bumps: nextcloud 33.0.0 -> 34.0.2 (both cloud and skudak-cloud) greg-time-bot 3.9.25 -> 3.10.0 fulfillr 20260723.2044 -> 20260728.2155 (prod and dev) The fulfillr bump records what is already deployed: both containers were rolled to that image on 2026-07-29 for SCRUM-156 (digital product releases + customer update campaign). Committing it keeps the repo from claiming an older tag than the host is actually running, which would otherwise roll fulfillr backwards on the next clean-checkout deploy. Co-Authored-By: Claude Opus 5 (1M context) --- ansible/deploy_home.yml | 2 + .../podman/tasks/containers/cloud-cron.yml | 40 ++++++ .../podman/tasks/containers/home/cloud.yml | 20 +++ .../podman/tasks/containers/skudak/cloud.yml | 6 + ansible/roles/podman/tasks/main.yml | 10 +- .../templates/nextcloud/cloud-cron.service.j2 | 13 ++ .../templates/nextcloud/cloud-cron.sh.j2 | 51 +++++++ .../templates/nextcloud/cloud-cron.timer.j2 | 13 ++ ansible/roles/ups/README.md | 114 ++++++++++++++++ ansible/roles/ups/defaults/main.yml | 36 +++++ ansible/roles/ups/handlers/main.yml | 35 +++++ ansible/roles/ups/tasks/deps.yml | 7 + ansible/roles/ups/tasks/firewall.yml | 12 ++ ansible/roles/ups/tasks/ipmi.yml | 68 +++++++++ ansible/roles/ups/tasks/main.yml | 5 + ansible/roles/ups/tasks/nut.yml | 129 ++++++++++++++++++ ansible/roles/ups/templates/nut.conf.j2 | 3 + .../roles/ups/templates/truenas-power.sh.j2 | 34 +++++ .../ups/templates/ups-restore.service.j2 | 13 ++ ansible/roles/ups/templates/ups-restore.sh.j2 | 63 +++++++++ .../roles/ups/templates/ups-sched-cmd.sh.j2 | 14 ++ ansible/roles/ups/templates/ups.conf.j2 | 18 +++ ansible/roles/ups/templates/upsd.conf.j2 | 3 + ansible/roles/ups/templates/upsd.users.j2 | 11 ++ ansible/roles/ups/templates/upsmon.conf.j2 | 40 ++++++ ansible/roles/ups/templates/upssched.conf.j2 | 13 ++ ansible/vars/vault.yml | Bin 28629 -> 28629 bytes 27 files changed, 768 insertions(+), 5 deletions(-) create mode 100644 ansible/roles/podman/tasks/containers/cloud-cron.yml create mode 100644 ansible/roles/podman/templates/nextcloud/cloud-cron.service.j2 create mode 100644 ansible/roles/podman/templates/nextcloud/cloud-cron.sh.j2 create mode 100644 ansible/roles/podman/templates/nextcloud/cloud-cron.timer.j2 create mode 100644 ansible/roles/ups/README.md create mode 100644 ansible/roles/ups/defaults/main.yml create mode 100644 ansible/roles/ups/handlers/main.yml create mode 100644 ansible/roles/ups/tasks/deps.yml create mode 100644 ansible/roles/ups/tasks/firewall.yml create mode 100644 ansible/roles/ups/tasks/ipmi.yml create mode 100644 ansible/roles/ups/tasks/main.yml create mode 100644 ansible/roles/ups/tasks/nut.yml create mode 100644 ansible/roles/ups/templates/nut.conf.j2 create mode 100644 ansible/roles/ups/templates/truenas-power.sh.j2 create mode 100644 ansible/roles/ups/templates/ups-restore.service.j2 create mode 100644 ansible/roles/ups/templates/ups-restore.sh.j2 create mode 100644 ansible/roles/ups/templates/ups-sched-cmd.sh.j2 create mode 100644 ansible/roles/ups/templates/ups.conf.j2 create mode 100644 ansible/roles/ups/templates/upsd.conf.j2 create mode 100644 ansible/roles/ups/templates/upsd.users.j2 create mode 100644 ansible/roles/ups/templates/upsmon.conf.j2 create mode 100644 ansible/roles/ups/templates/upssched.conf.j2 diff --git a/ansible/deploy_home.yml b/ansible/deploy_home.yml index a40b77e..cc2064d 100644 --- a/ansible/deploy_home.yml +++ b/ansible/deploy_home.yml @@ -11,3 +11,5 @@ - role: github-actions - role: graylog-config tags: graylog-config + - role: ups + tags: ups diff --git a/ansible/roles/podman/tasks/containers/cloud-cron.yml b/ansible/roles/podman/tasks/containers/cloud-cron.yml new file mode 100644 index 0000000..1502453 --- /dev/null +++ b/ansible/roles/podman/tasks/containers/cloud-cron.yml @@ -0,0 +1,40 @@ +--- +- name: template {{ cron_name }} cron script + become: true + ansible.builtin.template: + src: nextcloud/cloud-cron.sh.j2 + dest: "{{ cron_script_path }}" + owner: root + group: root + mode: 0755 + setype: bin_t + +- name: template {{ cron_name }} cron systemd service + become: true + ansible.builtin.template: + src: nextcloud/cloud-cron.service.j2 + dest: "/etc/systemd/system/{{ cron_name }}-cron.service" + owner: root + group: root + mode: 0644 + vars: + instance_name: "{{ cron_name }}" + +- name: template {{ cron_name }} cron systemd timer + become: true + ansible.builtin.template: + src: nextcloud/cloud-cron.timer.j2 + dest: "/etc/systemd/system/{{ cron_name }}-cron.timer" + owner: root + group: root + mode: 0644 + vars: + instance_name: "{{ cron_name }}" + +- name: enable and start {{ cron_name }} cron timer + become: true + ansible.builtin.systemd: + name: "{{ cron_name }}-cron.timer" + enabled: true + state: started + daemon_reload: true diff --git a/ansible/roles/podman/tasks/containers/home/cloud.yml b/ansible/roles/podman/tasks/containers/home/cloud.yml index 23a2824..f67062e 100644 --- a/ansible/roles/podman/tasks/containers/home/cloud.yml +++ b/ansible/roles/podman/tasks/containers/home/cloud.yml @@ -96,6 +96,26 @@ changed_when: "'System config value log_rotate_size' in cloud_log_rotate.stdout" failed_when: false +# Nextcloud's default ('auto') only expires trash when disk space is needed, +# so 66 GB of >30-day deletions sat untouched on a host with 1.3 TB free -- +# the retention was effectively unbounded. 'auto, 30' makes the 30-day +# expiry unconditional while still purging early under space pressure. +- name: set nextcloud trashbin retention for cloud + become: true + become_user: "{{ podman_user }}" + ansible.builtin.command: > + podman exec -u www-data cloud + php occ config:system:set trashbin_retention_obligation --value "auto, 30" + register: cloud_trashbin_retention + changed_when: "'System config value trashbin_retention_obligation' in cloud_trashbin_retention.stdout" + failed_when: false + +- include_tasks: containers/cloud-cron.yml + vars: + cron_name: cloud + cron_container: cloud + cron_script_path: /usr/local/bin/cloud-cron.sh + - include_tasks: containers/cloud-backup.yml vars: backup_name: cloud diff --git a/ansible/roles/podman/tasks/containers/skudak/cloud.yml b/ansible/roles/podman/tasks/containers/skudak/cloud.yml index 1175948..9fe2d2f 100644 --- a/ansible/roles/podman/tasks/containers/skudak/cloud.yml +++ b/ansible/roles/podman/tasks/containers/skudak/cloud.yml @@ -155,6 +155,12 @@ changed_when: "'System config value log_rotate_size' in skudak_log_rotate.stdout" failed_when: false +- include_tasks: containers/cloud-cron.yml + vars: + cron_name: skudak-cloud + cron_container: skudak-cloud + cron_script_path: /usr/local/bin/skudak-cloud-cron.sh + - include_tasks: containers/cloud-backup.yml vars: backup_name: skudak-cloud diff --git a/ansible/roles/podman/tasks/main.yml b/ansible/roles/podman/tasks/main.yml index a981a85..cca4d9b 100644 --- a/ansible/roles/podman/tasks/main.yml +++ b/ansible/roles/podman/tasks/main.yml @@ -67,24 +67,24 @@ - import_tasks: containers/home/cloud.yml vars: db_image: docker.io/library/mariadb:10.6 - image: docker.io/library/nextcloud:33.0.0-apache + image: docker.io/library/nextcloud:34.0.2-apache tags: cloud - import_tasks: containers/skudak/cloud.yml vars: db_image: docker.io/library/mariadb:10.6 - image: docker.io/library/nextcloud:33.0.0-apache + image: docker.io/library/nextcloud:34.0.2-apache tags: skudak, skudak-cloud - import_tasks: containers/debyltech/fulfillr.yml vars: - image: git.debyl.io/debyltech/fulfillr:20260723.2044 + image: git.debyl.io/debyltech/fulfillr:20260728.2155 tags: debyltech, fulfillr # Staging back-office (fulfillr-dev.debyltech.com) — same image, staging Turso config. - import_tasks: containers/debyltech/fulfillr-dev.yml vars: - image: git.debyl.io/debyltech/fulfillr:20260723.2044 + image: git.debyl.io/debyltech/fulfillr:20260728.2155 tags: debyltech, fulfillr-dev - import_tasks: containers/debyltech/uptime-kuma.yml @@ -109,7 +109,7 @@ - import_tasks: containers/home/gregtime.yml vars: - image: localhost/greg-time-bot:3.9.25 + image: localhost/greg-time-bot:3.10.0 tags: gregtime - import_tasks: containers/home/zomboid.yml diff --git a/ansible/roles/podman/templates/nextcloud/cloud-cron.service.j2 b/ansible/roles/podman/templates/nextcloud/cloud-cron.service.j2 new file mode 100644 index 0000000..27bb1f0 --- /dev/null +++ b/ansible/roles/podman/templates/nextcloud/cloud-cron.service.j2 @@ -0,0 +1,13 @@ +[Unit] +Description=Nextcloud {{ instance_name }} background jobs +After=network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +ExecStart={{ cron_script_path }} +# Type=oneshot disables the start timeout by default, so a wedged cron.php +# would leave the unit "activating" forever and every subsequent 5-minute +# trigger would be silently skipped. Bound it. +TimeoutStartSec={{ cron_timeout | default('30m') }} +Nice=10 diff --git a/ansible/roles/podman/templates/nextcloud/cloud-cron.sh.j2 b/ansible/roles/podman/templates/nextcloud/cloud-cron.sh.j2 new file mode 100644 index 0000000..672dbd1 --- /dev/null +++ b/ansible/roles/podman/templates/nextcloud/cloud-cron.sh.j2 @@ -0,0 +1,51 @@ +#!/bin/bash +# {{ ansible_managed }} +# Nextcloud "{{ cron_name }}" background jobs (cron.php). +# +# backgroundjobs_mode is "cron" on both instances, which means Nextcloud +# expects an external caller to run cron.php every ~5 minutes. Nothing was: +# the personal instance had not run a background job since 2026-05-14 and +# skudak since 2024-11-20. Without it Nextcloud never expires trash or file +# versions, never cleans stale chunked uploads, never sends calendar +# reminders, and -- easy to miss -- never rotates nextcloud.log, which makes +# the log_rotate_size cap set in containers/*/cloud.yml inert. +set -euo pipefail + +TAG=nextcloud-cron +INSTANCE={{ cron_name }} + +log() { logger -t "$TAG" -p daemon.info -- "instance=$INSTANCE $*"; } +fail() { logger -t "$TAG" -p daemon.err -- "instance=$INSTANCE status=failed $*" + echo "$TAG: FAILED: $*" >&2; exit 1; } + +# The Nextcloud containers are ROOTLESS podman owned by "{{ podman_user }}", +# but this script runs as root under systemd. Same sudo/cd/XDG_RUNTIME_DIR +# preamble as cloud-backup.sh -- see CLAUDE.md for why `cd;` is required. +pexec() { + sudo -H -u {{ podman_user }} bash -c \ + 'cd; d=/run/user/$(id -u); [ -d "$d" ] && export XDG_RUNTIME_DIR="$d" + exec podman "$@"' _ "$@" +} + +# A container that is down (deploy, image bump, host reboot) is not a failure +# worth flagging -- the next tick picks it up five minutes later. +if ! pexec container exists {{ cron_container }} 2>/dev/null; then + log "status=skipped reason=container-absent" + exit 0 +fi + +# occ and cron.php both refuse to do anything useful mid-upgrade. Skipping +# keeps a deploy window from parading as a run of failed units. +if pexec exec -u www-data {{ cron_container }} php occ status 2>/dev/null \ + | grep -q 'maintenance: true'; then + log "status=skipped reason=maintenance" + exit 0 +fi + +set +e +pexec exec -u www-data {{ cron_container }} php -f /var/www/html/cron.php +rc=$? +set -e +[ "$rc" -eq 0 ] || fail "cron.php exited $rc" + +log "status=ok" diff --git a/ansible/roles/podman/templates/nextcloud/cloud-cron.timer.j2 b/ansible/roles/podman/templates/nextcloud/cloud-cron.timer.j2 new file mode 100644 index 0000000..cf1d9a5 --- /dev/null +++ b/ansible/roles/podman/templates/nextcloud/cloud-cron.timer.j2 @@ -0,0 +1,13 @@ +[Unit] +Description=Nextcloud {{ instance_name }} background jobs every 5 minutes + +[Timer] +OnBootSec={{ cron_onbootsec | default('5m') }} +OnUnitActiveSec={{ cron_interval | default('5m') }} +RandomizedDelaySec={{ cron_randomized_delay | default('30s') }} +# Deliberately NOT Persistent: this runs every 5 minutes, so replaying runs +# missed while the host was off buys nothing and just stampedes at boot. +Persistent=false + +[Install] +WantedBy=timers.target diff --git a/ansible/roles/ups/README.md b/ansible/roles/ups/README.md new file mode 100644 index 0000000..0b402ad --- /dev/null +++ b/ansible/roles/ups/README.md @@ -0,0 +1,114 @@ +# ups + +UPS monitoring and staged shutdown for the home rack. + +A CyberPower PR1500RT2U (`0764:0601`) is cabled by USB to `home.debyl.io` and +backs both that host and `truenas.localdomain` (Dell PowerEdge R415). This role +makes `home.debyl.io` the NUT server and gives it the ability to power TrueNAS +back on over IPMI. + +## Outage sequence + +| When | What happens | Driven by | +| --- | --- | --- | +| t+0 | UPS goes on battery, `ONBATT` logged to journald → Graylog | `upsmon` | +| t+2min | TrueNAS shuts itself down cleanly, shedding ~200 W | TrueNAS UPS service, Slave mode, `Shutdown Timer 120` | +| 10% charge | `home.debyl.io` shuts itself down and tells the UPS to cut its output | `upsmon` `SHUTDOWNCMD` + `/lib/systemd/system-shutdown/nutshutdown` | +| mains returns | UPS re-energizes; both machines power themselves back up | R415 `always-on` restore policy; `home.debyl.io` BIOS *After Power Loss → Power On* | +| mains back +5min | Best-effort IPMI power-on, if a dedicated iDRAC is ever fitted | `upssched` → `ups-restore.sh` | +| host boot | Same restore check, for the deep-drain case | `ups-restore.service` | + +The 10% threshold is not a custom poller. CyberPower asserts its own low-battery +flag around 20–35%, so `ups.conf` sets `ignorelb` plus +`override.battery.charge.low`, and stock `upsmon` fires at exactly the +configured percentage. + +Likewise, the 2-minute TrueNAS shed is TrueNAS's own native "shutdown timer" +setting — no SSH key and no shutdown script from this side. + +## Powering TrueNAS back on + +Neither Wake-on-LAN nor IPMI works on this box today, so restore is done with +the chassis power restore policy instead. + +**Wake-on-LAN is out.** `bce0` advertises no WOL capability (`ifconfig -m bce0` +has no `WOL_MAGIC`) and both NICs are bonded into an LACP `lagg0`. + +**IPMI is out too, for now.** The iDRAC is in `shared / LOM1` mode and the +Enterprise card that would provide a dedicated management port is not fitted: + +``` +$ ipmitool sdr elist | grep -i idrac +iDRAC6 Ent Pres | 70h | ok | 7.1 | Absent +``` + +A shared-LOM iDRAC6 Express has no standby power. Measured directly: with the +chassis powered off the BMC does not even answer ARP, and it only reappears +~200s into POST, at the moment the host brings the NIC link up. That is a +hardware limitation, not a switch or BIOS problem. + +**So restore works like this instead.** `ipmitool chassis policy always-on` is +set on the R415. On a deep outage `home.debyl.io` halts at 10% and NUT's +shutdown hook tells the UPS to cut its output; when mains returns the UPS +re-energizes, the R415 sees AC and boots itself. + +The gap is the medium outage — mains returns after TrueNAS has shed but before +the battery reaches 10%. The UPS never cuts power, so TrueNAS stays off and +needs a manual power button press. Fitting a used iDRAC6 Enterprise card and +running `ipmitool delloem lan set dedicated` (plus a cable to the dedicated +port) closes that gap, and `ups-restore.sh` starts working with no code +changes — it is already deployed and simply logs and exits while the BMC is +unreachable. + +## One-time setup outside Ansible + +These are not managed by this role. + +**iDRAC (already done, via `ipmitool` on TrueNAS):** + +``` +ipmitool lan set 1 ipsrc static +ipmitool lan set 1 ipaddr 192.168.1.12 +ipmitool lan set 1 netmask 255.255.255.0 +ipmitool lan set 1 defgw ipaddr 192.168.1.1 +ipmitool lan set 1 access on # was disabled - nothing answers without this +ipmitool channel setaccess 1 2 callin=on ipmi=on link=on privilege=4 +ipmitool user set password 2 '' +ipmitool chassis policy always-on # this is what restores power after an outage +``` + +`idrac_password` is also the iDRAC web UI password for `root` - they share a +user database. + +**TrueNAS UI → Services → UPS** (enable + start automatically): + +| Field | Value | +| --- | --- | +| UPS Mode | Slave | +| Remote Host | `192.168.1.10` | +| Remote Port | `3493` | +| Identifier | `cyberpower` | +| Monitor User | `truenas` | +| Monitor Password | vault `nut_truenas_password` | +| Shutdown Mode | UPS goes on battery | +| Shutdown Timer | `120` | +| Shutdown Command | `/sbin/shutdown -p now` | +| Power Off UPS | unchecked | + +**BIOS on `home.debyl.io`** (Lenovo 10MR0004US): Power → *After Power Loss* → +**Power On**. On a full drain the NUT shutdown hook +(`/lib/systemd/system-shutdown/nutshutdown`) tells the UPS to cut its own +output; this BIOS setting is what brings the host back when mains returns. + +## Vault keys + +`nut_upsmon_password`, `nut_truenas_password`, `idrac_password` + +## Operating + +``` +upsc cyberpower # full UPS status +upsc cyberpower battery.charge +sudo /usr/local/bin/truenas-power.sh status|on|soft|off +journalctl -t ups-restore -t ups-sched +``` diff --git a/ansible/roles/ups/defaults/main.yml b/ansible/roles/ups/defaults/main.yml new file mode 100644 index 0000000..b8d8408 --- /dev/null +++ b/ansible/roles/ups/defaults/main.yml @@ -0,0 +1,36 @@ +--- +# CyberPower PR1500LCDRT2U cabled by USB to this host (0764:0601). +# This host is the NUT server; truenas.localdomain is a NUT slave. +ups_name: cyberpower +# lsusb reports the product string as PR1500LCDRT2U, but the device itself +# reports device.model CP1500PFCRM2U. The latter is what it actually is. +ups_desc: CyberPower CP1500PFCRM2U +ups_vendorid: "0764" +ups_productid: "0601" + +ups_listen_addr: 192.168.1.10 +ups_listen_port: 3493 + +# truenas.localdomain - the only host allowed to reach upsd +ups_slave_ip: 192.168.1.11 + +# Battery charge at which THIS host shuts itself down. TrueNAS sheds much +# earlier via its own "shutdown timer" setting (see roles/ups/README.md). +ups_low_charge_pct: 10 + +# Seconds of stable mains after ONLINE before TrueNAS is powered back on. +ups_restore_stable_secs: 300 + +# R415 iDRAC6. Shares LOM1 with bce0. +idrac_host: 192.168.1.12 +idrac_user: root + +ups_deps: + [ + ipmitool, + nut, + nut-client, + ] + +# Secrets live in ansible/vars/vault.yml (no vault_ prefix, per repo +# convention): nut_upsmon_password, nut_truenas_password, idrac_password diff --git a/ansible/roles/ups/handlers/main.yml b/ansible/roles/ups/handlers/main.yml new file mode 100644 index 0000000..e2af69c --- /dev/null +++ b/ansible/roles/ups/handlers/main.yml @@ -0,0 +1,35 @@ +--- +# Regenerates the nut-driver@ unit instances from ups.conf. Oneshot, +# so "restarted" just means "run it again". +- name: reload nut driver units + become: true + ansible.builtin.systemd: + name: nut-driver-enumerator.service + state: restarted + daemon_reload: true + +# The enumerator only writes unit definitions; it will not pick up changed +# driver options in an already-running driver. Restart the instance itself. +- name: restart nut driver + become: true + ansible.builtin.systemd: + name: "nut-driver@{{ ups_name }}.service" + state: restarted + +- name: restart nut server + become: true + ansible.builtin.systemd: + name: nut-server.service + state: restarted + +- name: restart nut monitor + become: true + ansible.builtin.systemd: + name: nut-monitor.service + state: restarted + +- name: restart firewalld + become: true + ansible.builtin.systemd: + name: firewalld + state: restarted diff --git a/ansible/roles/ups/tasks/deps.yml b/ansible/roles/ups/tasks/deps.yml new file mode 100644 index 0000000..2c640ce --- /dev/null +++ b/ansible/roles/ups/tasks/deps.yml @@ -0,0 +1,7 @@ +--- +- name: install NUT and ipmitool + become: true + ansible.builtin.dnf: + name: "{{ ups_deps }}" + state: present + tags: ups diff --git a/ansible/roles/ups/tasks/firewall.yml b/ansible/roles/ups/tasks/firewall.yml new file mode 100644 index 0000000..1004ef0 --- /dev/null +++ b/ansible/roles/ups/tasks/firewall.yml @@ -0,0 +1,12 @@ +--- +- name: allow NUT from the truenas slave only + become: true + ansible.posix.firewalld: + rich_rule: >- + rule family="ipv4" source address="{{ ups_slave_ip }}/32" + port port="{{ ups_listen_port }}" protocol="tcp" accept + permanent: true + immediate: true + state: enabled + notify: restart firewalld + tags: [ups, firewall] diff --git a/ansible/roles/ups/tasks/ipmi.yml b/ansible/roles/ups/tasks/ipmi.yml new file mode 100644 index 0000000..0a1518b --- /dev/null +++ b/ansible/roles/ups/tasks/ipmi.yml @@ -0,0 +1,68 @@ +--- +# IPMI power control for truenas.localdomain (Dell R415, iDRAC6). +# WOL is not an option there: bce0 advertises no WOL capability and is an +# LACP lagg member, so IPMI is the only remote power-on path. + +- name: deploy iDRAC credential + become: true + ansible.builtin.copy: + content: "{{ idrac_password }}\n" + dest: /etc/ups/idrac.pw + owner: root + group: nut + mode: '0640' + no_log: true + tags: ups + +- name: deploy truenas power helper + become: true + ansible.builtin.template: + src: truenas-power.sh.j2 + dest: /usr/local/bin/truenas-power.sh + owner: root + group: root + mode: '0755' + setype: bin_t + tags: ups + +- name: deploy truenas restore script + become: true + ansible.builtin.template: + src: ups-restore.sh.j2 + dest: /usr/local/bin/ups-restore.sh + owner: root + group: root + mode: '0755' + setype: bin_t + tags: ups + +- name: deploy upssched command dispatcher + become: true + ansible.builtin.template: + src: ups-sched-cmd.sh.j2 + dest: /usr/local/bin/ups-sched-cmd.sh + owner: root + group: root + mode: '0755' + setype: bin_t + tags: ups + +# Covers the deep-drain case: if the battery ran out, this host was itself +# powered off when mains returned, so nothing was running to restore TrueNAS. +- name: deploy boot-time truenas restore unit + become: true + ansible.builtin.template: + src: ups-restore.service.j2 + dest: /etc/systemd/system/ups-restore.service + owner: root + group: root + mode: '0644' + tags: ups + +- name: enable boot-time truenas restore unit + become: true + ansible.builtin.systemd: + name: ups-restore.service + enabled: true + daemon_reload: true + tags: ups diff --git a/ansible/roles/ups/tasks/main.yml b/ansible/roles/ups/tasks/main.yml new file mode 100644 index 0000000..bf19bd2 --- /dev/null +++ b/ansible/roles/ups/tasks/main.yml @@ -0,0 +1,5 @@ +--- +- import_tasks: deps.yml +- import_tasks: nut.yml +- import_tasks: ipmi.yml +- import_tasks: firewall.yml diff --git a/ansible/roles/ups/tasks/nut.yml b/ansible/roles/ups/tasks/nut.yml new file mode 100644 index 0000000..3f86819 --- /dev/null +++ b/ansible/roles/ups/tasks/nut.yml @@ -0,0 +1,129 @@ +--- +# NUT server. The UPS is on USB here, so this host drives it and serves +# status to truenas.localdomain over the network. + +- name: set NUT run mode to netserver + become: true + ansible.builtin.template: + src: nut.conf.j2 + dest: /etc/ups/nut.conf + owner: root + group: nut + mode: '0640' + notify: + - reload nut driver units + - restart nut driver + - restart nut server + - restart nut monitor + tags: ups + +- name: deploy NUT driver configuration + become: true + ansible.builtin.template: + src: ups.conf.j2 + dest: /etc/ups/ups.conf + owner: root + group: nut + mode: '0640' + notify: + - reload nut driver units + - restart nut driver + - restart nut server + tags: ups + +- name: deploy upsd listener configuration + become: true + ansible.builtin.template: + src: upsd.conf.j2 + dest: /etc/ups/upsd.conf + owner: root + group: nut + mode: '0640' + notify: restart nut server + tags: ups + +- name: deploy upsd users + become: true + ansible.builtin.template: + src: upsd.users.j2 + dest: /etc/ups/upsd.users + owner: root + group: nut + mode: '0640' + notify: + - restart nut server + - restart nut monitor + tags: ups + +- name: deploy upsmon configuration + become: true + ansible.builtin.template: + src: upsmon.conf.j2 + dest: /etc/ups/upsmon.conf + owner: root + group: nut + mode: '0640' + notify: restart nut monitor + tags: ups + +- name: deploy upssched configuration + become: true + ansible.builtin.template: + src: upssched.conf.j2 + dest: /etc/ups/upssched.conf + owner: root + group: nut + mode: '0640' + notify: restart nut monitor + tags: ups + +# The nut package ships /usr/lib/udev/rules.d/62-nut-usbups.rules, which +# hands the UPS USB device to the nut user. Reload and retrigger so the +# driver can claim it without physically replugging the UPS. +- name: reload udev rules for NUT USB access + become: true + ansible.builtin.command: + cmd: udevadm control --reload-rules + changed_when: false + tags: ups + +- name: retrigger UPS USB device + become: true + ansible.builtin.command: + cmd: >- + udevadm trigger --subsystem-match=usb + --attr-match=idVendor={{ ups_vendorid }} + changed_when: false + tags: ups + +# nut-driver-enumerator reads ups.conf and generates nut-driver@{{ ups_name }}. +# The .service is oneshot (so never "started" for long) and is triggered by +# the .path unit watching ups.conf - enable both, but only start the .path. +- name: enable NUT driver enumerator + become: true + ansible.builtin.systemd: + name: nut-driver-enumerator.service + enabled: true + daemon_reload: true + tags: ups + +- name: enable and start NUT driver enumerator path trigger + become: true + ansible.builtin.systemd: + name: nut-driver-enumerator.path + enabled: true + state: started + tags: ups + +- name: enable and start NUT services + become: true + ansible.builtin.systemd: + name: "{{ item }}" + enabled: true + state: started + loop: + - "nut-driver@{{ ups_name }}.service" + - nut-server.service + - nut-monitor.service + - nut.target + tags: ups diff --git a/ansible/roles/ups/templates/nut.conf.j2 b/ansible/roles/ups/templates/nut.conf.j2 new file mode 100644 index 0000000..d916a4c --- /dev/null +++ b/ansible/roles/ups/templates/nut.conf.j2 @@ -0,0 +1,3 @@ +# {{ ansible_managed }} +# netserver: run the driver + upsd locally and serve slaves over the network. +MODE=netserver diff --git a/ansible/roles/ups/templates/truenas-power.sh.j2 b/ansible/roles/ups/templates/truenas-power.sh.j2 new file mode 100644 index 0000000..310a189 --- /dev/null +++ b/ansible/roles/ups/templates/truenas-power.sh.j2 @@ -0,0 +1,34 @@ +#!/bin/bash +# {{ ansible_managed }} +# Remote power control for truenas.localdomain via the R415 iDRAC6. +# Usage: truenas-power.sh {status|on|soft|off|cycle} +set -euo pipefail + +PW_FILE=/etc/ups/idrac.pw + +if [ ! -r "$PW_FILE" ]; then + echo "cannot read $PW_FILE" >&2 + exit 1 +fi + +# -f keeps the password out of the process argument list. +ipmi() { + ipmitool -I lanplus -H {{ idrac_host }} -U {{ idrac_user }} \ + -f "$PW_FILE" "$@" +} + +case "${1:-status}" in + status) ipmi chassis power status ;; + on) ipmi chassis power on ;; + # ACPI soft-off. FreeBSD has hw.acpi.power_button_state=S5, so this is + # a clean TrueNAS shutdown. Normally unused: TrueNAS shuts itself down + # as a NUT slave. This is the manual escape hatch. + soft) ipmi chassis power soft ;; + # Hard cut, last resort only. + off) ipmi chassis power off ;; + cycle) ipmi chassis power cycle ;; + *) + echo "usage: $0 {status|on|soft|off|cycle}" >&2 + exit 2 + ;; +esac diff --git a/ansible/roles/ups/templates/ups-restore.service.j2 b/ansible/roles/ups/templates/ups-restore.service.j2 new file mode 100644 index 0000000..f8aeffd --- /dev/null +++ b/ansible/roles/ups/templates/ups-restore.service.j2 @@ -0,0 +1,13 @@ +# {{ ansible_managed }} +[Unit] +Description=Restore TrueNAS power after an outage +After=network-online.target nut-server.service nut-monitor.service +Wants=network-online.target +Requires=nut-server.service + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/ups-restore.sh + +[Install] +WantedBy=multi-user.target diff --git a/ansible/roles/ups/templates/ups-restore.sh.j2 b/ansible/roles/ups/templates/ups-restore.sh.j2 new file mode 100644 index 0000000..666550a --- /dev/null +++ b/ansible/roles/ups/templates/ups-restore.sh.j2 @@ -0,0 +1,63 @@ +#!/bin/bash +# {{ ansible_managed }} +# Power truenas.localdomain back on after an outage, but only when it is +# genuinely safe to do so. Called by upssched once mains has been stable, +# and once at boot by ups-restore.service (deep-drain recovery). +# +# Deliberately stateless: we ask the iDRAC whether the chassis is off +# rather than tracking whether we were the ones who shut it down. A NAS +# powered off by hand while on mains is safe, because no ONLINE event +# fires in that case and the boot path checks UPS status first. +# +# NOTE: as of now this is a best-effort secondary path. The R415 has no +# iDRAC6 Enterprise card ("iDRAC6 Ent Pres ... Absent"), so its shared-LOM +# BMC has no standby power and goes unreachable whenever the chassis is +# off - exactly when we would want it. An unreachable iDRAC is therefore +# the EXPECTED case here, and we exit quietly rather than alarming. +# +# The primary restore path needs no IPMI: the R415 power restore policy is +# set to always-on, so when the UPS cuts and then restores its output the +# server powers itself back up. Fit an iDRAC6 Enterprise card and switch it +# to dedicated mode and this script starts working with no changes. +set -uo pipefail + +TAG=ups-restore +UPS={{ ups_name }}@localhost + +log() { logger -t "$TAG" -- "$*"; echo "$TAG: $*"; } + +# At boot, upsd may not be serving yet. Wait a bounded amount of time. +for _ in $(seq 1 30); do + if upsc "$UPS" ups.status >/dev/null 2>&1; then + break + fi + sleep 2 +done + +status=$(upsc "$UPS" ups.status 2>/dev/null || echo UNKNOWN) +if [[ "$status" != *OL* ]]; then + log "UPS status is '$status', not on line - refusing to power TrueNAS on" + exit 0 +fi + +power=$(/usr/local/bin/truenas-power.sh status 2>&1 || true) +case "$power" in + *"is off"*) + log "mains stable and chassis off - powering TrueNAS on" + if /usr/local/bin/truenas-power.sh on; then + log "power-on command accepted" + else + log "power-on command FAILED - check iDRAC at {{ idrac_host }}" + exit 1 + fi + ;; + *"is on"*) + log "TrueNAS already on, nothing to do" + ;; + *) + # Expected while the box has no iDRAC6 Enterprise card: the BMC is + # simply not on the network with the chassis powered down. + log "iDRAC at {{ idrac_host }} unreachable - relying on the" \ + "always-on power restore policy instead ($power)" + ;; +esac diff --git a/ansible/roles/ups/templates/ups-sched-cmd.sh.j2 b/ansible/roles/ups/templates/ups-sched-cmd.sh.j2 new file mode 100644 index 0000000..bf0cb90 --- /dev/null +++ b/ansible/roles/ups/templates/ups-sched-cmd.sh.j2 @@ -0,0 +1,14 @@ +#!/bin/bash +# {{ ansible_managed }} +# upssched CMDSCRIPT. Runs as the unprivileged nut user. +set -uo pipefail + +case "${1:-}" in + truenas-restore) + exec /usr/local/bin/ups-restore.sh + ;; + *) + logger -t ups-sched -- "unknown timer '${1:-}'" + exit 1 + ;; +esac diff --git a/ansible/roles/ups/templates/ups.conf.j2 b/ansible/roles/ups/templates/ups.conf.j2 new file mode 100644 index 0000000..b817d02 --- /dev/null +++ b/ansible/roles/ups/templates/ups.conf.j2 @@ -0,0 +1,18 @@ +# {{ ansible_managed }} + +[{{ ups_name }}] + driver = usbhid-ups + port = auto + vendorid = {{ ups_vendorid }} + productid = {{ ups_productid }} + desc = "{{ ups_desc }}" + # CyberPower asserts its own low-battery flag around 20-35%, far too + # early for us. Ignore it and derive LB from our own threshold so + # upsmon fires SHUTDOWNCMD at exactly {{ ups_low_charge_pct }}%. + ignorelb + override.battery.charge.low = {{ ups_low_charge_pct }} + # Unlock driver.killpower. Without this the driver refuses to cut UPS + # output at shutdown, and the R415's always-on power restore policy + # would never see AC drop and return - i.e. nothing comes back after a + # deep outage. See README.md. + allow_killpower diff --git a/ansible/roles/ups/templates/upsd.conf.j2 b/ansible/roles/ups/templates/upsd.conf.j2 new file mode 100644 index 0000000..2ce034e --- /dev/null +++ b/ansible/roles/ups/templates/upsd.conf.j2 @@ -0,0 +1,3 @@ +# {{ ansible_managed }} +LISTEN 127.0.0.1 {{ ups_listen_port }} +LISTEN {{ ups_listen_addr }} {{ ups_listen_port }} diff --git a/ansible/roles/ups/templates/upsd.users.j2 b/ansible/roles/ups/templates/upsd.users.j2 new file mode 100644 index 0000000..f070473 --- /dev/null +++ b/ansible/roles/ups/templates/upsd.users.j2 @@ -0,0 +1,11 @@ +# {{ ansible_managed }} + +# Local upsmon on this host. +[upsmon] + password = {{ nut_upsmon_password }} + upsmon master + +# truenas.localdomain, running the TrueNAS UPS service in Slave mode. +[truenas] + password = {{ nut_truenas_password }} + upsmon slave diff --git a/ansible/roles/ups/templates/upsmon.conf.j2 b/ansible/roles/ups/templates/upsmon.conf.j2 new file mode 100644 index 0000000..1fe98a7 --- /dev/null +++ b/ansible/roles/ups/templates/upsmon.conf.j2 @@ -0,0 +1,40 @@ +# {{ ansible_managed }} + +MONITOR {{ ups_name }}@localhost 1 upsmon {{ nut_upsmon_password }} master + +MINSUPPLIES 1 +SHUTDOWNCMD "/usr/bin/systemctl poweroff" +NOTIFYCMD /usr/bin/upssched + +# upsmon drops this file before halting; /lib/systemd/system-shutdown/nutshutdown +# reads it late in shutdown and, if present, tells the UPS to cut its output. +# That AC drop-and-return is what triggers the R415's always-on restore policy +# and this host's BIOS "After Power Loss: Power On". upsmon has NO compiled-in +# default for this - leave it unset and the UPS never powers down. +# Must be on tmpfs: a persistent path can go stale and make every ordinary +# reboot look like a forced shutdown. +POWERDOWNFLAG /run/nut/killpower + +POLLFREQ 5 +POLLFREQALERT 5 + +# Wait up to 30s for the truenas slave to disconnect before we halt. +HOSTSYNC 30 +DEADTIME 15 +RBWARNTIME 43200 +NOCOMMWARNTIME 300 +FINALDELAY 5 + +# SYSLOG puts every UPS event in the journal, which fluent-bit already +# forwards to Graylog (see roles/common/tasks/fluent-bit.yml). +# EXEC runs NOTIFYCMD, i.e. upssched, which drives the TrueNAS restore. +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 +NOTIFYFLAG NOCOMM SYSLOG +NOTIFYFLAG NOPARENT SYSLOG diff --git a/ansible/roles/ups/templates/upssched.conf.j2 b/ansible/roles/ups/templates/upssched.conf.j2 new file mode 100644 index 0000000..b668731 --- /dev/null +++ b/ansible/roles/ups/templates/upssched.conf.j2 @@ -0,0 +1,13 @@ +# {{ ansible_managed }} + +CMDSCRIPT /usr/local/bin/ups-sched-cmd.sh +PIPEFN /run/nut/upssched.pipe +LOCKFN /run/nut/upssched.lock + +# Mains is back: wait for it to hold for {{ ups_restore_stable_secs }}s +# before powering TrueNAS back on, so we do not flap on unstable power. +AT ONLINE * CANCEL-TIMER truenas-restore +AT ONLINE * START-TIMER truenas-restore {{ ups_restore_stable_secs }} + +# Power dropped again while the restore timer was pending - stand down. +AT ONBATT * CANCEL-TIMER truenas-restore diff --git a/ansible/vars/vault.yml b/ansible/vars/vault.yml index 371e48f8cf878eabddbece4babe961b308787ef4..9c9d8746da439a228717b78c34810be77a1a57ef 100644 GIT binary patch literal 28629 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 literal 28629 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