intent

intent的blog

🚴自行车骑的最远的全栈工程师
github

UPS/NUT(Network UPS Tool)

UPS/NUT(Network UPS Tool)#

NUT#

NUT (Network UPS Tool),集成了一系列方便操作、通知的 UPS 工具。

Nut 主要包含三个核心服务:

  • nut-driver: 这个服务负责通过特定放驱动来与 UPS 进行通信
  • nut-server: 该服务利用 nut-dirver 沟通 UPS, 并将 UPS 状态通过网络服务发布
  • nut-monitor (nut-client): 该服务连接 nut-server, 根据 UPS 状态做出特定响应

注意: nut-client 实际上是一个 systemd 软连接文件,本质上还是 nut-monitor.

             ┌─────────────┐                  ┌────────────┐
       ┌──── nut-monitor ───────────────► nut-server
     └─────────────┘                  └────────────┘




 ┌─────────────┐                              ┌────────────┐
  upssched nut-driver
 └─────────────┘                              └────────────┘





┌────────────────┐                            ┌─────────────┐
  user scripts     UPS
└────────────────┘                            └─────────────┘

安装 NUT#

pve 控制台直接安装

apt install -y nut

连接 UPS,通过命令:

# nut-scanner -U
SNMP library not found. SNMP search disabled.
Neon library not found. XML search disabled.
IPMI library not found. IPMI search disabled.
Scanning USB bus.
[nutdev1]
	driver = "blazer_usb"
	port = "auto"
	vendorid = "0665"
	productid = "5161"
	product = "USB to Serial"
	vendor = "INNO TECH"
	bus = "001" 

or

/bin/upsc nutdev1@localhost
Init SSL without certificate database
battery.charge: 100
battery.voltage: 13.50
battery.voltage.high: 13.00
battery.voltage.low: 10.40
battery.voltage.nominal: 12.0
...

查询 UPS 的状态。

NUT Server 配置#

在 Nut 安装完成后会自动在 /etc/nut 目录生成配置文件,接下来的配置工作主要是调整该目录下的各种配置文件.

nut.conf#

该配置主要定义 nut 的运行模式,只有一个配置字段 MODE=xxxx, 该配置可选值及含义如下:

  • none: Nut 未配置或使用外部系统启动 Nut 服务,可以理解为 "啥也不干".
  • standalone: 独立模式,一般在只有一个 UPS 且只负责本地系统 (不提供网络服务) 的情况下使用.
  • netserver: 跟独立模式类似,会启动 driver、upsd 和 upsmon 服务,不同之处是可以提供网络服务,其他机器上的 nut-monitor 可以通过网络来连接 Nut Server.
  • netclient: 仅客户端模式,只启动 nut-monitor, 用于连接远程的 Nut 服务.

这里我使用的 SANTAK TG-BOX 850 通过 USB 连接我的 pve,pve 安装了 NUT 工具,所以 pve 当作 server,其他的查询 server 的状态就行。

# cat /etc/nut/nut.conf
MODE=netserver

ups.conf#

该配置用于定义 nut-driver 如何连接到物理 UPS, 该配置文件的饿配置格式如下:

# 需要注意的是, 如果想要群晖或者 QNAP 能通过网络连接 UPS,
# 那么 UPS 的名称是有特殊要求的(群晖必须起名叫 ups, QNAP 没有测试过可能叫 qnapups);
[ups]
	driver = usbhid-ups
	port = auto
	desc = "TG-BOX 850"

upsd.conf#

该配置文件用于控制 nut-server 的网络服务,例如监听端口、最大连接数、证书配置等;由于我是在内网使用,所以只需要配置网络监听,其他参数保持默认即可:

LISTEN 0.0.0.0 3493

upsd.users#

upsd.users 配置文件用于定义通过网络连接到 nut-server 的用户名和密码,该配置样例如下:

[monuser]
    password = secret
    upsmon master

关于 群晖 和 QNAP 用户:如果期望这两个 NAS 可以直接连接到 Nut Server, 可以确认的是群晖需要保证存在用户名为 monuser 密码为 secret 的用户;QNAP 我没有验证过,网上查询到的结果是需要保证存在用户名为 admin 密码为 123456 的用户.

NUT 监控配置#

相较于基本配置来说,Nut 核心处理在于如何做好监控配置;Nut 对于监控策略支持大致有两种:

  • 1、直接由upsmon.conf配置,任何事件直接由用户指定的脚本负责处理,没有定时器等高级特性
  • 2、在 upsmon.conf中配置执行脚本为upssched, 任何事件先由upssched处理,借助于 upssched 可以实现一些高级功能,比如选择性触发关机等

upsmon.conf#

该配置主要用于配置 nut-monitor 如何监控 UPS, 同时定义 UPS 出现哪些事件要进行怎样的处理动作,下面详细解释一下核心配置.

MONITOR 指令#

MONITOR 指令用于定义要监控 UPS 的连接地址, 其格式如下:

MONITOR <system> <powervalue> <username> <password> ("master"|"slave")
  • <system>: nut-server 链接地址,格式为 "UPS 名称" + "@" + "nut-server 地址", 例如myups@192.168.1.2
  • <powervalue>: UPS 数量,大多数情况你只有一个 UPS 电源,所以写 1 就行
  • <username>/<password>: 在upsd.users中定义的用户名和密码
  • `master/slave:master表示该系统将最后关闭,让从属系统先关闭;slave表示该系统立即关闭

以下为我的配置样例:

MONITOR ups@localhost 1 monuser secret master

SHUTDOWNCMD#

SHUTDOWNCMD 指令用于定义在 UPS 电量不足或者需要主动关机时的关机命令,建议填写完整路径.

SHUTDOWNCMD "/usr/sbin/poweroff"

NOTIFYCMD#

NOTIFYCMD 指令是一个非常重要的指令,该指令用于配置在发生特定事件 (如市电中断、UPS 处于低电量等) 时, nut-monitor 所执行的命令. 简单的说就是 NOTIFYCMD 定义了具体执行命令,可以直接在此处配置一个自己编写的脚本,当 UPS 有事件发生时此脚本都会被调用.

NOTIFYCMD 指令大致有两种配置方式,一种是配置成自己的脚本,脚本需要有可执行权限,脚本内可以通过 NOTIFY 环境变量获取事件类型,然后自己进行处理. 这种方式有点 "简单粗暴" 的意思,可定制化程度完全依赖于你的脚本怎么写。具体可以使用哪些变量请自行测试,因为版本不同可能环境变量名称也不同.

还有一种方式是将 NOTIFYCMD 配置为执行内置的 upssched 命令;upssched 是 Nut 提供的一个带有特定策略的调度程序;简而言之就是基于常用的功能进行了抽象,upssched 有自己单独配置,可以实现 "如果市电在 180s 内恢复则不进行关机" 的这种高级调度策略.

这里我选择使用第二种方式,

NOTIFYCMD /usr/sbin/upssched

NOTIFYFLAG#

NOTIFYFLAG 同样是关键配置,需要与 NOTIFYCMD 配合使用;NOTIFYFLAG 指令负责指定一系列的 UPS 事件应该触发何种操作,该指令格式如下:

NOTIFYFLAG <notify type> <flag>[+<flag>][+<flag>] ...

其中 表示事件类型,可选类型如下:

  • ONLINE: UPS 在线,即市电恢复时会触发
  • ONBATT: UPS 使用电池供电,即市电中断时会触发
  • LOWBATT: UPS 低电量时会触发
  • FSD: UPS 正在被关闭 (Forced Shutdown)
  • COMMOK: 与 nut-server 成功建立连接时触发
  • COMMBAD: 与 nut-server 建立连接失败 (连接丢失) 时触发
  • SHUTDOWN: UPS 发出关机指令触发
  • REPLBATT: UPS 需要更换电池时触发
  • NOCOMM: 无法与 UPS 建立连接 (UPS 未就绪) 时触发

对于 flag 标志通常有四种,多种组合时用加号 (+) 连接:

  • SYSLOG: 只打印 syslog
  • WALL: 在终端上弹出消息 (/bin/wall)
  • EXEC: 调用 NOTIFYCMD指定的命令,并传递相关事件
  • IGNORE: 啥也不干,忽略该事件

如果 NOTIFYCMD 使用了自定义脚本,则此处请根据实际需要来配置需要脚本处理的事件;如果 NOTIFYCMD 配置为使用 upssched, 可以将所有事件配置为 EXEC, 然后具体过滤在 upssched 处理.

例如如果只想让 NOTIFYCMD 配置的脚本只处理市电中断和恢复事件,同时打印 syslog, 可以这样配置:

NOTIFYFLAG ONLINE SYSLOG+EXEC
NOTIFYFLAG ONBATT SYSLOG+EXEC

由于我 NOTIFYCMD 配置的是 upssched, 所以我这里将所有事件全部传递给 upssched:

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 NOPARENT SYSLOG+EXEC

其他配置#

除了以上的核心配置,其他配置如果没特殊情况一般保持默认即可;具体的配置可以参考官方文档: UPSMON.CONF(5).

upssched.conf#

使用该配置的前提是 upsmon.conf 配置中的 NOTIFYCMD 指向了 upssched, 并且 NOTIFYFLAG 为相关事件配置了 EXEC; 该配置主要的作用是使用一些 upssched 内置的高级语法来控制特定事件的处理方式. upssched.conf 配置主要包含两部分:头部的选项配置和尾部的规则配置.

4.2.1、选项配置#

头部选项配置只包含三个配置:

  • CMDSCRIPT: 该配置应该位于首行 (推荐这样,实际上是 AT 指令之前就行), 该指令用于定义事件的处理脚本;脚本一般由用户自行编写,upssched会根据规则将指定参数传递到此脚本并执行.
  • PIPEFN: 用于进程间通信的管道文件,需要位于 AT 指令之前
  • LOCKFN: 互斥锁文件,用于防止 upsmon 同时调度多个文件,需要位于 AT 指令之前

关于 CMDSCRIPT 配置的脚本,下面是一个样例:

#!/usr/bin/env bash

set -ex

exec >> /var/log/upssched-cmd.log 2>&1

# 主要负责关闭系统的函数
function xpoweroff(){
    logger -t upssched-cmd '准备关闭 XPEnology...'
    logger -t upssched-cmd '准备关闭 TProxy Gateway...'
    logger -t upssched-cmd '所有必要系统已成功关闭...'
}

# 判断 upssched 触发的事件
case $1 in
    onbattwarn)
        logger -t upssched-cmd 'UPS 已经切换到电池供电, 准备安全关闭系统...'
        xpoweroff
        ;;
    ups-back-on-line)
        logger -t upssched-cmd '市电已恢复...'
        ;;
    lowbatt)
        logger -t upssched-cmd 'UPS 电量不足, 立即关闭系统...'
        xpoweroff
        ;;
    *)
        logger -t upssched-cmd "Unrecognized command: $1"
        ;;
esac

有一点需要注意,CMDSCRIPT 配置的脚本默认是以 nut 用户运行的,所以要处理好 nut 用户权限问题.

对于 PIPEFN 和 LOCKFN 官方推荐单独创建叫 upssched 目录,然后文件放在这个目录里;但是有些系统例如 Ubuntu Server 22.04, /run/nut/ 是 tmpfs, 重启会丢失目录导致出现权限问题;所以推荐直接不创建独立目录直接配置:

CMDSCRIPT /opt/scripts/upssched-cmd.sh
PIPEFN /run/nut/upssched.pipe
LOCKFN /run/nut/upssched.lock

4.2.2、规则配置#

使用 upssched 的好处就是内置了一个规则引擎,我们可以通过一些简单的语法来配置复杂规则;upssched 的规则语法如下:

AT notifytype upsname command

规则以 AT 开头,notifytype 用于指定需要关注的事件类型;upsname 指定 UPS 名称,只有一个的情况下或者不想区分时可以无脑写 *; command 部分用于指定需要执行的动作,command 大致有三种类型:

  • START-TIMER: 启动一个定时器
  • CANCEL-TIMER: 取消一个定时器
  • EXECUTE: 立即执行

这部分看起来复杂实际很简单,例如以下规则实现了: 当市电断开 (UPS 使用电池供电时) 启动一个名字叫 onbattwarn 的定时器,这个定时器在 180s 后会执行 CMDSCRIPT 定义的脚本并将 onbattwarn 作为第一个参数传递给脚本;同时如果市电在计时器的 180s 之内恢复 (临时闪断), 则取消执行脚本.

AT ONBATT * START-TIMER onbattwarn 180
AT ONLINE * CANCEL-TIMER onbattwarn

当然也有些事件是需要立即执行的,例如: 市电中断立即发送通知

# 立即执行 `CMDSCRIPT` 指定的脚本, 并将 `onbattnoti` 作为参数传递给脚本
AT ONBATT * EXECUTE onbattnoti

NUT Server 配置文件#

完整配置为:

nut.conf

MODE=netserver

ups.conf

# 名称是为了一开始兼容群晖, 所以就叫 UPS
[ups]
    driver = "usbhid-ups"
    port = "auto"

upsd.conf

LISTEN 0.0.0.0 3493

upsd.users

[monuser]
    password = secret
    upsmon master
[qnapups]
    password = 123456
    upsmon master

upsmon.conf

MONITOR ups@localhost 1 monuser secret master
MINSUPPLIES 1
SHUTDOWNCMD "/usr/sbin/poweroff"
NOTIFYCMD /usr/sbin/upssched
POLLFREQ 5
POLLFREQALERT 5
HOSTSYNC 30
DEADTIME 15
POWERDOWNFLAG /etc/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 NOPARENT SYSLOG+EXEC
RBWARNTIME 43200
NOCOMMWARNTIME 300
FINALDELAY 5

upssched.conf

CMDSCRIPT /opt/scripts/upssched-cmd.sh
PIPEFN /run/nut/upssched.pipe
LOCKFN /run/nut/upssched.lock
AT ONBATT * START-TIMER onbattwarn 180
AT ONLINE * CANCEL-TIMER onbattwarn
AT ONLINE * EXECUTE ups-back-on-line
AT LOWBATT * EXECUTE lowbatt

upssched-cmd.sh

#!/usr/bin/env bash

set -ex

exec >> /var/log/upssched-cmd.log 2>&1

echo "执行 ID: $(id)"

SSH_CMD='ssh -o StrictHostKeyChecking=no'

case $1 in
    onbattwarn)
        logger -t upssched-cmd 'UPS 已经切换到电池供电, 准备安全关闭系统...'
        ;;
    ups-back-on-line)
        logger -t upssched-cmd '市电已恢复...'
        ;;
    lowbatt)
        logger -t upssched-cmd 'UPS 电量不足, 立即关闭系统...'
        ;;
    *)
        logger -t upssched-cmd "Unrecognized command: $1"
        ;;
esac

重启 NUT Server#

systemctl restart nut-server.service
systemctl restart nut-driver.service
systemctl restart nut-monitor.service

NUT Client 配置#

配置 nut 客户端#

编辑 /etc/nut/nut.conf 文件,将 MODE 设置为 netclient 模式

MODE=netclient

配置 upsmon#

编辑 /etc/nut/upsmon.conf 文件,添加以下配置项:

MONITOR ups的名称@ups的ip地址 1 用户名 密码 slave
MINSUPPLIES 1
SHUTDOWNCMD "/usr/sbin/poweroff"
NOTIFYCMD /usr/sbin/upssched
POLLFREQ 5
POLLFREQALERT 5
HOSTSYNC 30
DEADTIME 15
POWERDOWNFLAG /etc/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 NOPARENT SYSLOG+EXEC
RBWARNTIME 43200
NOCOMMWARNTIME 300
FINALDELAY 5
RUN_AS_USER root

这里注意 RUN_AS_USER,在 upsmon.conf 文件中,通常会定义 upsmon 运行的用户。在某些系统中,upsmon 默认以普通用户运行,这可能导致无法执行关机命令,所以需要 root。

配置 upssched#

wall 命令在系统中用于向所有已登录用户广播消息。upsmon 默认配置会使用 wall 命令来发送通知消息给所有用户。

这里需要安装 wall 工具,Gentoo 安装过程为:

echo "sys-apps/util-linux tty-helpers" >> /etc/portage/package.use
sudo emerge -v sys-apps/util-linux
# 验证是否安装
which wall

upssched 工具允许你在特定条件下执行脚本。编辑 /etc/nut/upssched.conf 文件,并添加以下内容:

CMDSCRIPT /etc/nut/upssched-cmd.sh
PIPEFN /run/nut/upssched.pipe
LOCKFN /run/nut/upssched.lock
AT ONBATT * START-TIMER onbattwarn 600
AT ONLINE * CANCEL-TIMER onbattwarn
AT ONLINE * EXECUTE ups-back-on-line
AT LOWBATT * EXECUTE lowbatt

这将会在 ups 供电后 600 秒后发送 onbattwarn 事件到脚本,然后在脚本定义执行的命令。

/etc/nut/upssched-cmd.sh 内容为

#!/usr/bin/env bash

set -ex

exec >> /var/log/upssched-cmd.log 2>&1

echo "执行 ID: $(id)"

function xpoweroff(){
        /sbin/shutdown -h +0 "UPS has been on battery for 1 minute, shutting down."
}

case $1 in
    onbattwarn)
        logger -t upssched-cmd 'UPS 已经切换到电池供电, 准备安全关闭系统...'
        xpoweroff
        ;;
    ups-back-on-line)
        logger -t upssched-cmd '市电已恢复...'
        ;;
    lowbatt)
        logger -t upssched-cmd 'UPS 电量不足, 立即关闭系统...'
        xpoweroff
        ;;
    *)
        logger -t upssched-cmd "Unrecognized command: $1"
        ;;
esac

赋予可执行权限

sudo chmod +x /etc/nut/upssched-cmd.sh

重启 NUT Client#

systemctl enable nut-monitor.service
systemctl restart nut-monitor.service
systemctl status nut-monitor.service

验证配置#

你可以通过以下命令测试 UPS 状态:

upsc <ups_name>@IP

参考#

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。