After the host rebooted, truenas began mailing NOCOMM continuously. The UPS driver was fine throughout -- nut-driver@cyberpower stayed running and the CyberPower is still on USB here. What died was upsd, which publishes its state to truenas over the network: upsd: not listening on 192.168.1.10 port 3493 upsd: Fatal error: some listening interfaces were not available nut-server.service: Start request repeated too quickly upsd binds an explicit address, but the packaged unit only orders itself After=network.target, which is satisfied when networking STARTS rather than when an address exists. It tried to bind 11 seconds into boot, before NetworkManager had assigned the address, then burned all five default restart attempts inside one second, tripped the start limit and stayed dead. The shipped unit carries these two lines commented out, because upstream knows the case. network-online.target is the correct ordering; NetworkManager-wait-online is enabled here so it genuinely waits for addresses. StartLimitIntervalSec=0 and RestartSec are belt and braces: a slow address can no longer exhaust the attempts, and retries are spaced instead of hammered. truenas needs no change -- it is a correctly configured SLAVE that reconnected on its own, and its NUT config is regenerated from the middleware database anyway. Verified: upsd listening on both addresses, UPS OL at 100%, and truenas querying it again with zero failures since. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
153 lines
3.7 KiB
YAML
153 lines
3.7 KiB
YAML
---
|
|
# 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
|
|
|
|
# upsd binds an explicit address, so it must not start before that address
|
|
# exists. See the template for the boot race this fixes.
|
|
- name: ensure nut-server drop-in directory exists
|
|
become: true
|
|
ansible.builtin.file:
|
|
path: /etc/systemd/system/nut-server.service.d
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: '0755'
|
|
tags: ups
|
|
|
|
- name: order upsd after the network is actually online
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: nut-server-network-online.conf.j2
|
|
dest: /etc/systemd/system/nut-server.service.d/10-network-online.conf
|
|
owner: root
|
|
group: root
|
|
mode: '0644'
|
|
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
|