huawei-ups2000 驱动接进 upsd、upsmon、upssched 的事件链路。0. 结论和使用边界#
文中的 UT / UNT server 指 NUT: Network UPS Tools。这台机器承担 NUT Server 角色:通过 huawei-ups2000 驱动读取 UPS2000G,再把状态暴露给本机和远端 NUT 客户端。
当前部署状态:
| 项 | 状态 |
|---|---|
| 操作系统 | Ubuntu 22.04.4 LTS,Linux 5.15 |
| 发行版 NUT 包 | nut / nut-server / nut-client / nut-cgi 为 2.7.4-14ubuntu2 |
| 实际 UPS 驱动 | /lib/nut/huawei-ups2000,从 NUT 源码编译安装 |
| 源码版本 | NUT 2.8.2.1,本地源码目录保留在 /root/nut |
| 驱动版本 | huawei-ups2000 internal version 0.06 |
| UPS 型号 | Huawei UPS2000G,额定功率 3000 VA |
| 连接方式 | UPS USB 口识别为 /dev/ttyUSB0,USB UART 为 04e2:1410 Exar XR21V1410 |
| NUT 模式 | MODE=netserver,upsd 监听 0.0.0.0:3493 |
| Web 状态入口 | Apache 暴露 nut-cgi,状态页为 /cgi-bin/nut/upsstats.cgi |
| systemd 服务 | nut-driver、nut-server、nut-monitor 均为 active |
| 当前 UPS 状态 | OL CHRG,在线供电并充电,电量 100%,负载约 38% |
主要覆盖:
- 如何从一台空 Ubuntu 机器准备 NUT Server 的编译和运行环境。
- 如何确认 Huawei UPS2000G 的 USB/串口链路可用。
- 如何从 NUT 源码编译
huawei-ups2000驱动并放入 Ubuntu 的 NUT 服务目录。 - 如何配置
ups.conf、upsd、upsmon、upssched,让 UPS 与 NUT Server 关联起来。 - 如何用
upsc、upscmd、upsrw、nut-cgi和 systemd 日志验证接入结果。
操作边界:
- 生产 NUT 密码、UPS 序列号、客户端 IP 和主机地址只写示例值。
- 不在生产环境演示
load.off、driver.killpower、shutdown.return等会影响供电的命令。 - 不把当前现场策略包装成唯一方案。尤其是
upssched-cmd里的主节点不关机逻辑,需要结合业务保护目标再确认。 - 不建议手工改 NUT 源码。当前只处理“编译已有驱动并接入服务”,不维护 fork。
1. 架构关系:UPS 如何接入 NUT Server#
NUT 接入要先看链路。真正需要对齐的是驱动、服务端、监控端和调度脚本,而不是某一个单独命令。
flowchart LR
ups["UPS
后备电源
停电后通知服务器执行关机"]
subgraph vm_zone["虚拟化节点"]
nutvm["NUT Server VM
Ubuntu
示例地址:192.0.2.10/24"]
driver["huawei-ups2000 driver
/dev/ttyUSB0"]
upsd["upsd
TCP 3493"]
monitor["local upsmon
master"]
cgi["nut-cgi
/cgi-bin/nut/upsstats.cgi"]
end
subgraph phy_zone["受 UPS 供电的物理机"]
nas["nas-node
400W"]
node01["compute-node-01
400W"]
node02["compute-node-02
400W"]
node03["compute-node-03
500W"]
node04["compute-node-04
450W"]
node05["compute-node-05
500W"]
node06["compute-node-06
300W"]
backup["vm-backup-node
150W"]
end
ups -->|USB 直通到 VM| nutvm
nutvm --> driver --> upsd
upsd --> monitor
upsd --> cgi
upsd -. Network .-> nas
upsd -. Network .-> node01
upsd -. Network .-> node02
upsd -. Network .-> node03
upsd -. Network .-> node04
upsd -. Network .-> node05
upsd -. Network .-> node06
upsd -. Network .-> backup
ups -. 提供电源 .-> phy_zone
现场的三层关联是:
| 层级 | 配置入口 | 作用 |
|---|---|---|
| UPS 到驱动 | /etc/nut/ups.conf 中的 [huawei] | 指定 driver = huawei-ups2000 和 port = /dev/ttyUSB0 |
| 驱动到 NUT Server | nut-driver.service + upsd | upsdrvctl 启动 /lib/nut/huawei-ups2000 -a huawei,upsd 对外发布状态 |
| 事件到动作 | upsmon.conf + upssched.conf | 断电、恢复供电、低电量等事件进入调度脚本 |
1.1 为什么需要单独编译驱动#
Ubuntu 22.04 的 NUT 包提供了稳定的 systemd 服务和 upsd / upsmon 运行框架,但现场需要的 huawei-ups2000 驱动来自 NUT 较新的源码树。最终做法是:
- 保留发行版 NUT 包作为服务框架。
- 从 NUT 源码启用
libmodbus编译huawei-ups2000。 - 只把编译出的驱动二进制安装到
/lib/nut/huawei-ups2000。 - 让发行版自带的
nut-driver.service继续管理驱动进程。
这样改动范围小:不用替换整套 NUT 服务,也不用改 systemd unit。代价也明确:驱动二进制来自源码编译,升级 NUT 包或重装机器时,要重新确认 /lib/nut/huawei-ups2000 是否还在。
1.2 UPS2000G 的连接约束#
现场 UPS 通过 /dev/ttyUSB0 接入,设备信息显示为:
ID_VENDOR_ID=04e2
ID_MODEL_ID=1410
ID_MODEL=1410
这对应 Huawei UPS2000 常见的 Exar XR21V1410 USB-to-serial 芯片。NUT 源码里的 huawei-ups2000 文档说明了一个关键点:UPS2000 的 USB 和 RS-232 虽然都能作为串口通道使用,但同一时间只应该使用一个端口;如果切换线缆或端口类型,UPS 端口可能需要断电冷启动后才恢复响应。
部署时按顺序检查:
dmesg | grep -Ei 'ttyUSB|xr_serial|ch341|cdc_acm'
ls -l /dev/ttyUSB0
udevadm info -q property -n /dev/ttyUSB0 | grep -E 'ID_VENDOR_ID|ID_MODEL_ID|ID_MODEL'
通过标准:
- 能看到
/dev/ttyUSB0或明确的串口设备。 nut运行用户对串口设备有读写权限。- 如果设备被识别成不兼容的
cdc_acm,不要继续调 NUT 配置,应先处理内核驱动或改用 RS-232。
2. 从零部署:准备系统和依赖#
从空机器开始,最小路径如下。已部署机器上的 history、包列表和源码目录能还原出完整步骤。
2.1 安装基础包#
Ubuntu 22.04 上先安装发行版 NUT 和编译依赖:
apt update
apt install -y \
git \
build-essential \
pkgconf \
autoconf \
automake \
libtool \
libmodbus-dev \
nut \
nut-server \
nut-client \
nut-cgi
验证依赖:
dpkg -l | awk '/^(ii)/ && ($2 ~ /^(nut|nut-client|nut-server|libmodbus|build-essential|pkgconf|autoconf|automake|libtool)/) {print $2, $3}'
pkgconf --libs libmodbus
现场能看到的关键包版本:
| 包 | 版本 |
|---|---|
nut | 2.7.4-14ubuntu2 |
nut-server | 2.7.4-14ubuntu2 |
nut-client | 2.7.4-14ubuntu2 |
libmodbus-dev | 3.1.6-2 |
build-essential | 12.9ubuntu3 |
2.2 准备串口权限#
现场 /dev/ttyUSB0 权限是:
crw-rw---- 1 root dialout ... /dev/ttyUSB0
nut 用户加入了 dialout 组:
uid=110(nut) gid=115(nut) groups=115(nut),20(dialout)
配置命令如下:
usermod -aG dialout nut
chown root:dialout /dev/ttyUSB0
chmod 660 /dev/ttyUSB0
chown 和 chmod 只对当前设备节点生效,重启或重新插拔后可能丢失。生产环境建议增加 udev 规则,把设备稳定映射为 /dev/ups2000:
cat >/etc/udev/rules.d/60-ups2000.rules <<'EOF'
SUBSYSTEM=="tty", ATTRS{idVendor}=="04e2", ATTRS{idProduct}=="1410", GROUP="dialout", MODE="0660", SYMLINK+="ups2000"
EOF
udevadm control --reload-rules
udevadm trigger
ls -l /dev/ups2000
如果使用 udev symlink,后续 ups.conf 里的 port 建议改成 /dev/ups2000。当前部署仍使用 /dev/ttyUSB0,后面会把现状和推荐做法分别说明。
3. 先用原型程序确认 UPS 通信#
现场 /root/ups2000_test 保留了一个早期 UPS2000 测试程序,里面有 Makefile、README 和 ups2000.c。它不是最终服务的一部分,作用是把硬件链路和 Modbus 通信先单独验掉。
3.1 编译原型程序#
原型程序依赖 libmodbus:
cd /root/ups2000_test
make
Makefile 的核心逻辑是编译 serial.c、ups2000.c、stub.c、main.c,并链接 libmodbus:
MODBUS_CFLAGS=`pkgconf --cflags libmodbus`
MODBUS_LIBS=`pkgconf --libs libmodbus`
all: serial.c serial.h ups2000.c stub.c stub.h main.c
cc -c serial.c -o serial.o $(CFLAGS)
cc -c ups2000.c -o ups2000.o $(CFLAGS) $(MODBUS_CFLAGS)
cc -c stub.c -o stub.o $(CFLAGS)
cc -c main.c -o main.o $(CFLAGS)
cc -o ups2000 main.o stub.o serial.o ups2000.o $(MODBUS_LIBS)
3.2 运行通信测试#
测试命令形如:
./ups2000 /dev/ttyUSB0
通过标准:
- 程序能读到 UPS 型号、固件和实时状态。
- 失败时能明确区分权限问题、串口不可读、Modbus 超时或 UPS 端口无响应。
- 如果切换过 USB / RS-232 连接方式,先按 UPS 侧流程冷启动,再重新测试。
先用单一程序确认串口通信,再接入 NUT。排障时可以避免把“硬件不通”和“NUT 配置错误”混在一起。
4. 编译并安装 huawei-ups2000 驱动#
NUT 源码目录保留在 /root/nut。现场源码的 Git HEAD 是 3e31b39,版本宏为 2.8.2.1,远端为 https://github.com/networkupstools/nut.git。
4.1 拉取源码#
git clone https://github.com/networkupstools/nut.git --depth=1 /root/nut
cd /root/nut
4.2 生成 configure 脚本#
./autogen.sh
如果缺少 automake、autoconf 或 libtool,autogen.sh 会失败。不要跳过报错继续 make,先补齐依赖。
4.3 按 Ubuntu NUT 路径配置编译参数#
现场 config.log 记录的配置命令如下:
./configure \
--with-modbus \
--with-user=nut \
--with-group=nut \
--datadir=/usr/share/nut \
--sysconfdir=/etc/nut \
--includedir=/usr/include \
--with-statepath=/run/nut \
--with-pidpath=/run/nut \
--with-altpidpath=/run/nut \
--with-drvpath=/lib/nut \
--with-udev-dir=/lib/udev \
--with-systemdsystemunitdir=/lib/systemd/system \
--with-systemdshutdowndir=/lib/systemd/system-shutdown
关键不在参数数量,而在两点:
--with-modbus让huawei-ups2000进入 Modbus 驱动列表,并链接libmodbus。--with-drvpath=/lib/nut对齐 Ubuntu 发行版 NUT 的驱动目录,避免 systemd 仍去旧路径找驱动。
4.4 编译并安装驱动#
可以只编译目标驱动:
cd /root/nut
make -j"$(nproc)" huawei-ups2000
install -m 0755 drivers/huawei-ups2000 /lib/nut/huawei-ups2000
验证二进制:
/lib/nut/huawei-ups2000 -V
ldd /lib/nut/huawei-ups2000 | grep modbus
现场验证结果:
Network UPS Tools - NUT Huawei UPS2000 (1kVA-3kVA) RS-232 Modbus driver 0.06 (2.8.2.1)
libmodbus.so.5 => /lib/x86_64-linux-gnu/libmodbus.so.5
huawei-ups2000 -h 会提示这是 experimental driver。生产使用时不需要回避这个事实,应该把它写进风险说明:驱动可用,但升级 NUT、切换内核或更换 UPS 型号时都要重新验证。
5. 配置 NUT Server#
NUT Server 的配置目标是:upsdrvctl 能启动 huawei-ups2000 驱动,upsd 能发布 huawei@localhost,upsmon 能监控这个 UPS 并把事件交给 upssched。
5.1 /etc/nut/nut.conf#
现场使用 netserver 模式:
MODE=netserver
这个模式适合一台机器连接 UPS,多台机器通过网络读取 UPS 状态的场景。
5.2 /etc/nut/ups.conf#
现场配置:
maxretry = 3
[huawei]
driver = huawei-ups2000
port = /dev/ttyUSB0
desc = "Huawei UPS2000"
user = nut
如果采用 udev symlink,推荐改成:
[huawei]
driver = huawei-ups2000
port = /dev/ups2000
desc = "Huawei UPS2000"
user = nut
验证命令:
upsdrvctl -t start
systemctl restart nut-driver
systemctl status nut-driver --no-pager
验收时要能看到 /lib/nut/huawei-ups2000 -a huawei 进程,日志里包含 Startup successful。
5.3 /etc/nut/upsd.conf#
现场配置为:
LISTEN 0.0.0.0 3493
这让远端客户端可以访问 NUT Server。生产环境需要配合防火墙或安全组,只允许可信网段访问 3493/tcp。如果只给本机使用,可以改为监听 127.0.0.1。
验证:
ss -lntp | grep ':3493'
现场结果是 upsd 监听在 0.0.0.0:3493。
5.4 /etc/nut/upsd.users#
示例配置如下,真实密码不要写进文档或仓库:
[monuser]
password = "<UPS_MONITOR_PASSWORD>"
upsmon = master
[remoteuser]
password = "<UPS_REMOTE_MONITOR_PASSWORD>"
upsmon = secondary
[admin]
password = "<UPS_ADMIN_PASSWORD>"
upsmon = master
NUT 2.8 的文档把监控角色称为 primary / secondary。现场老配置里的 master / slave 仍能兼容,但新增客户端建议按新名称写。NUT Server 本机使用 monuser 作为 primary 监控端;远端物理机使用单独的 remoteuser,不要把 primary 凭据分发到每台客户端。
文件权限建议:
chown root:nut /etc/nut/upsd.users
chmod 640 /etc/nut/upsd.users
NUT 的用户密码通常以明文形式保存在配置文件里,因此重点不是“把它写进 Git”,而是限制文件权限、避免日志输出、按需轮换。
5.5 /etc/nut/upsmon.conf#
现场有效配置可以整理为:
RUN_AS_USER root
MONITOR huawei@localhost 1 monuser <UPS_MONITOR_PASSWORD> 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
这个配置要盯住三点:
MONITOR huawei@localhost ... master把本机设为 UPS 的 master 监控端。NOTIFYCMD /usr/sbin/upssched让状态变化进入调度器。RUN_AS_USER root是现场现状,目的是允许执行关机命令;如果改成非 root,需要重新设计关机权限。
5.6 /etc/nut/upssched.conf#
现场事件策略:
CMDSCRIPT /etc/nut/upssched-cmd
PIPEFN /var/run/nut/upssched.pipe
LOCKFN /var/run/nut/upssched.lock
AT ONBATT * START-TIMER power-off 60
AT ONLINE * CANCEL-TIMER power-off
AT ONLINE * EXECUTE power-on
事件含义:
- UPS 转电池后,启动一个 60 秒的
power-off计时器。 - 如果 60 秒内恢复市电,取消这个计时器。
- 恢复市电时执行
power-on事件,记录恢复日志。
5.7 /etc/nut/upssched-cmd#
现场脚本逻辑如下,已去掉无关细节:
#!/bin/bash
is_master() {
grep -q "master" /etc/nut/upsmon.conf
return $?
}
case "$1" in
power-off)
if is_master; then
logger "NUT: Master UPS monitor - Not shutting down."
else
logger "NUT: Slave system - Initiating shutdown due to prolonged power outage."
/usr/sbin/poweroff
fi
;;
power-on)
logger "NUT: Power has been restored."
;;
*)
logger "NUT: Unknown command $1"
;;
esac
这段逻辑需要单独 review:当前机器作为 master 时不会自动关机,只记录日志;只有非 master 角色会执行 poweroff。这可能是有意为之,也可能是早期调试留下来的保护逻辑。发布到生产前,需要明确 UPS 保护目标:
- 如果 NUT Server 本身也需要受保护,应设计 master 的关机策略。
- 如果 NUT Server 必须保持在线,负责通知下游主机,则要确认它的供电容量和外部告警链路。
- 如果下游机器依赖远端
upsmon自行关机,要逐台验证客户端配置,而不是只看 Server。
脚本权限:
chmod 755 /etc/nut/upssched-cmd
5.8 日志分流#
rsyslog 额外把 NUT 日志写到独立文件:
if $programname == 'upsd' then /var/log/nut/upsd.log
if $programname == 'upsmon' then /var/log/nut/upsmon.log
对应目录权限建议:
mkdir -p /var/log/nut
chown syslog:adm /var/log/nut
chmod 750 /var/log/nut
systemctl restart rsyslog
5.9 远端物理机接入#
UPS 不只保护 NUT Server 本机。其他物理机可以不直连 UPS,只运行 upsmon,通过网络订阅 NUT Server 的 huawei UPS 状态。这样断电事件由 NUT Server 统一发布,各物理机按自己的策略关机。
远端物理机只需要客户端组件:
apt install -y nut-client
/etc/nut/nut.conf 使用 netclient 模式:
MODE=netclient
/etc/nut/upsmon.conf 的核心配置如下。示例里的 nut.example.internal 替换为 NUT Server 地址,用户名和密码保持和 Server 端 /etc/nut/upsd.users 里的 [remoteuser] 一致。最后一列 secondary 表示这台机器跟随 NUT Server,不负责发起 FSD:
RUN_AS_USER root
MONITOR huawei@nut.example.internal 1 remoteuser <UPS_REMOTE_MONITOR_PASSWORD> secondary
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
如果远端物理机也使用 60 秒延迟关机策略,/etc/nut/upssched.conf 可以和 Server 保持一致:
CMDSCRIPT /etc/nut/upssched-cmd
PIPEFN /var/run/nut/upssched.pipe
LOCKFN /var/run/nut/upssched.lock
AT ONBATT * START-TIMER power-off 60
AT ONLINE * CANCEL-TIMER power-off
AT ONLINE * EXECUTE power-on
客户端侧的 upssched-cmd 不要复用 Server 上“master 不关机”的判断。远端物理机是 secondary,计时器到期后应按维护策略执行本机关机:
#!/bin/bash
case "$1" in
power-off)
logger "NUT: Secondary system - Initiating shutdown due to prolonged power outage."
/usr/sbin/poweroff
;;
power-on)
logger "NUT: Power has been restored."
;;
*)
logger "NUT: Unknown command $1"
;;
esac
启动客户端监控服务,并按主机启动策略决定是否设置开机自启。现场见过 nut-monitor 处于 active 但 disabled 的状态;如果这台物理机重启后仍需要自动受 UPS 保护,应显式 enable:
systemctl restart nut-monitor
systemctl status nut-monitor --no-pager
# 确认需要随系统启动后再执行
systemctl enable nut-monitor
某台远端物理机上,nut-monitor 已作为客户端长期运行。一次断电后很快恢复的日志链路如下,主机名和 NUT Server 地址已替换为示例值:
nut-client-01 upssched: Timer daemon started
nut-client-01 upssched: New timer: power-off (60 seconds)
nut-client-01 nut-monitor: Network UPS Tools upsmon 2.8.0
nut-client-01 nut-monitor: UPS huawei@nut.example.internal on line power
nut-client-01 upssched: Cancelling timer: power-off
nut-client-01 nut-monitor: Executing command: power-on
nut-client-01 upssched: Executing command: power-on
nut-client-01 upssched: Timer queue empty, exiting
nut-client-01 root: NUT: Power has been restored.
这条链路说明远端物理机已经收到 NUT Server 发布的 UPS 状态,并能在恢复供电时取消本地关机计时器。它仍然不能替代完整断电演练:如果要验证 60 秒后真的关机,应在维护窗口单台灰度,不要直接对所有物理机同时演练。
6. 启动顺序和服务关联#
配置完成后,按驱动、服务端、监控端的顺序启动,排障路径更短。
systemctl restart nut-driver
systemctl restart nut-server
systemctl restart nut-monitor
systemctl enable nut-server
systemctl enable nut-monitor
服务状态:
| 服务 | 状态 | 作用 |
|---|---|---|
nut-driver.service | active running | 启动 /lib/nut/huawei-ups2000 -a huawei |
nut-server.service | active running | 启动 /lib/nut/upsd,监听 3493 |
nut-monitor.service | active running | 启动 /lib/nut/upsmon,处理 UPS 事件 |
ups-monitor.service | masked | 现场未使用这个旧服务名 |
进程关系:
nut /lib/nut/huawei-ups2000 -a huawei
nut /lib/nut/upsd
root /lib/nut/upsmon
root /lib/nut/upsmon
验证命令:
systemctl --no-pager --plain status nut-driver nut-server nut-monitor
ps -eo user,pid,cmd | grep -E '[h]uawei-ups2000|[u]psd|[u]psmon'
ss -lntp | grep ':3493'
如果 nut-driver 失败,优先看串口权限和驱动路径;如果 nut-server 失败,优先看 upsd.users 和 ups.conf;如果 nut-monitor 失败,优先看 MONITOR 行里的用户名、密码和 UPS 名称是否一致。
6.1 Web 状态入口:nut-cgi#
nut-cgi 由 Apache 通过 CGI 目录暴露状态页。有效入口是:
http://<nut-server>/cgi-bin/nut/upsstats.cgi
这个入口只用于浏览 UPS 状态,值班时可以快速确认 ups.status、电池电量、输入输出电压和负载。它不是远程控制入口。
状态总览页展示每个 UPS 的核心字段:

点击 All data 后可以进入单台 UPS 的详情视图:

本机验证命令:
curl -I http://127.0.0.1/cgi-bin/nut/upsstats.cgi
返回 HTTP/1.1 200 OK 即可。下面两个路径不是现场有效入口,返回 404 属于预期结果:
http://<nut-server>/nut/upsstats.cgi
http://<nut-server>/upsstats.cgi
upsset.cgi 虽然随 nut-cgi 安装,但现场没有启用控制入口。这个 CGI 涉及修改 UPS 变量和执行控制动作,只有在 CGI 目录已经做访问控制、审计和网络隔离后,才应该考虑在 /etc/nut/upsset.conf 中打开安全确认项。普通状态查看场景保持关闭即可。
7. 功能验证矩阵#
上线验收按下面的矩阵跑。服务 active 只能说明进程在,不能证明 UPS 数据正常刷新。
| 验证项 | 命令 | 通过标准 |
|---|---|---|
| 驱动启动 | systemctl status nut-driver --no-pager | 看到 huawei-ups2000 进程和 Startup successful |
| NUT Server 监听 | `ss -lntp | grep ‘:3493’` |
| UPS 数据读取 | upsc huawei@localhost | 返回 driver.name、ups.status、电压、负载、电量等字段 |
| 即时命令列表 | upscmd -l huawei@localhost | 能列出 beeper、test、shutdown 等命令 |
| 可写变量 | upsrw huawei@localhost | 能看到 ups.delay.*、ups.beeper.status 等变量 |
| Web 状态页 | curl -I http://127.0.0.1/cgi-bin/nut/upsstats.cgi | 返回 HTTP/1.1 200 OK |
| 远端物理机 | systemctl status nut-monitor --no-pager | secondary 物理机的 nut-monitor 为 active |
| 远端订阅状态 | 查看远端 journalctl -u nut-monitor | 出现 UPS huawei@nut.example.internal on line power 或对应状态事件 |
| 断电事件 | 查看 journalctl -u nut-monitor | ONBATT 触发 upssched 计时器 |
| 恢复供电 | 查看 journalctl -u nut-monitor | ONLINE 取消计时器并记录恢复日志 |
upsc huawei@localhost 的关键字段如下:
| 字段 | 值 |
|---|---|
device.mfr | Huawei |
device.model | UPS2000G |
driver.name | huawei-ups2000 |
driver.version | 2.8.2.1 |
driver.version.internal | 0.06 |
driver.parameter.port | /dev/ttyUSB0 |
battery.charge | 100.0 |
battery.runtime | 约 1400 秒 |
input.voltage | 约 231V |
output.voltage | 约 220V |
output.realpower | 约 900W |
ups.load | 38.0 |
ups.power.nominal | 3000 |
ups.status | OL CHRG |
ups.temperature | 约 22.6 |
ups.type | online |
断电后又恢复供电时,nut-monitor 日志应出现类似链路:
UPS huawei@localhost on battery
New timer: power-off (60 seconds)
UPS huawei@localhost on line power
Cancelling timer: power-off
Executing command: power-on
NUT: Power has been restored.
这类日志能说明 upsmon -> upssched -> upssched-cmd 通知链路已经跑通,但不能当作完整的生产关机验收。市电在 60 秒内恢复,没有进入最终关机动作。
8. 继续和停止条件#
上线或迁移时,用条件判断替代“看起来没问题”。
可以继续的条件:
/dev/ttyUSB0或/dev/ups2000存在,且nut用户可读写。/lib/nut/huawei-ups2000 -V返回预期版本。nut-driver、nut-server、nut-monitor都为 active。upsc huawei@localhost能稳定返回数据,且ups.status符合现场供电状态。nut-cgi的状态页只在受控网络内可访问,并且/cgi-bin/nut/upsstats.cgi返回 200。- 远端物理机能登录
upsd,secondarynut-monitor日志能收到huawei@<server>的状态事件。 - 断电恢复事件能触发
upssched,并按策略取消或执行计时器。
必须停止的条件:
- 串口设备被识别为不兼容驱动,或者
upsc数据长时间不刷新。 upsd.users或upsmon.conf中的真实密码被写入文档、Git、工单或聊天记录。LISTEN 0.0.0.0 3493暴露到非可信网络,且没有防火墙限制。- Apache CGI 入口暴露到非可信网络,或者
upsset.cgi在没有访问控制的情况下被启用。 upssched-cmd的关机策略没有经过业务确认。- 需要执行
load.off、driver.killpower、shutdown.return这类会影响供电的命令,但没有维护窗口和现场人员。
9. 回滚和 fallback#
如果驱动或配置变更导致 NUT 不稳定,回滚要优先保证 UPS 本体供电不受影响。NUT 是监控和联动层,不应该让回滚动作影响 UPS 输出。
9.1 变更前备份#
BACKUP_DIR="/root/nut-backup-$(date +%Y%m%d%H%M%S)"
mkdir -p "${BACKUP_DIR}"
cp -a /etc/nut "${BACKUP_DIR}/etc-nut"
cp -a /lib/nut/huawei-ups2000 "${BACKUP_DIR}/huawei-ups2000" 2>/dev/null || true
systemctl status nut-driver nut-server nut-monitor --no-pager >"${BACKUP_DIR}/systemd-status.txt" || true
9.2 回滚驱动和配置#
systemctl stop nut-monitor
systemctl stop nut-server
systemctl stop nut-driver
cp -a "${BACKUP_DIR}/etc-nut/." /etc/nut/
cp -a "${BACKUP_DIR}/huawei-ups2000" /lib/nut/huawei-ups2000
systemctl start nut-driver
systemctl start nut-server
systemctl start nut-monitor
回滚后验证:
upsc huawei@localhost
journalctl -u nut-driver -u nut-server -u nut-monitor -n 80 --no-pager
9.3 fallback 策略#
如果 huawei-ups2000 驱动无法恢复,fallback 不是立刻尝试高风险关机,而是退回到更保守的保护方式:
- UPS 继续作为独立供电设备运行。
- 暂停依赖 NUT 的自动关机策略。
- 用 UPS 面板、厂商管理卡或现场检查确认电池状态。
- 保留
/etc/nut和串口日志,离线复现驱动问题。 - 在测试环境验证新驱动后再恢复生产 NUT 联动。
10. 常见问题#
10.1 Driver not connected 怎么排查#
按顺序检查:
ls -l /dev/ttyUSB0
id nut
systemctl status nut-driver --no-pager
journalctl -u nut-driver -n 80 --no-pager
常见原因是 nut 用户没有串口权限、ups.conf 里的 port 写错、驱动二进制不在 /lib/nut,或者 UPS 端口在切换 USB / RS-232 后没有恢复响应。
10.2 upsc huawei@localhost 连接失败#
检查 upsd:
systemctl status nut-server --no-pager
ss -lntp | grep ':3493'
grep -vE '^#|^$' /etc/nut/upsd.conf
如果本机能访问、远端不能访问,再检查防火墙和 LISTEN 地址。不要为了临时排障把 3493 暴露给全网。
10.3 远端客户端认证失败#
核对三处名称和值:
ups.conf里的 UPS 名称是[huawei]。upsd.users里的用户名和密码与客户端一致。- 客户端
upsmon.conf的MONITOR huawei@<server> ...指向正确 NUT Server。
真实密码只保存在配置文件和密钥管理系统里,不进入文章、Git 或截图。
10.4 upssched 没有触发#
检查这些点:
grep -E 'NOTIFYCMD|NOTIFYFLAG' /etc/nut/upsmon.conf
grep -vE '^#|^$' /etc/nut/upssched.conf
test -x /etc/nut/upssched-cmd && echo ok
journalctl -u nut-monitor -n 120 --no-pager
NOTIFYFLAG 必须包含 EXEC,否则 upsmon 只写日志,不会调用 upssched。
10.5 能不能直接测试 upsmon -c fsd#
只能在受控维护窗口测试。upsmon -c fsd 会触发 forced shutdown 语义,可能导致关联主机执行关机流程。生产环境验证应先用日志、模拟环境和单台客户端灰度确认,再做全链路演练。
11. 遗留项和加固建议#
待确认项:
| 项 | 当前状态 | 建议 |
|---|---|---|
| NUT 监听地址 | upsd 监听 0.0.0.0:3493 | 配合防火墙限制可信客户端;如果只本机使用则改为 127.0.0.1 |
| CGI 状态页 | Apache 暴露 /cgi-bin/nut/upsstats.cgi | 只放在受控网络;需要公网访问时先加反向代理认证和访问日志 |
| CGI 控制页 | upsset.cgi 已安装但未启用 | 保持关闭;确需启用时先完成目录级认证、审计和控制命令评审 |
| 串口设备名 | 使用 /dev/ttyUSB0 | 增加 udev symlink,避免插拔后设备名变化 |
| NUT 明文密码 | NUT 配置文件需要保存认证密码 | 限制文件权限,发布文档必须过滤敏感值,必要时轮换 |
| master 关机策略 | 当前 master 收到 power-off 只记录日志 | 结合业务目标确认是否应该保护 NUT Server 自身 |
| 驱动来源 | 源码编译的 experimental driver | 升级内核、升级 NUT 或更换 UPS 型号后重新验证 |
| 破坏性命令 | upscmd 暴露 shutdown / load 控制能力 | 只给可信管理员开放,生产禁止随意执行 |
12. 参考资料#
参考资料分两类:运行配置和服务状态用于还原部署方式;驱动与厂商文档用于确认硬件约束和驱动参数。
| 资料 | 用途 |
|---|---|
| NUT 官方 huawei-ups2000 驱动文档 | 确认 UPS2000 支持范围、USB/RS-232 约束、libmodbus 编译参数、offdelay / ondelay 等驱动参数 |
| NUT Huawei 兼容性列表 | 确认 NUT 设备列表中包含 Huawei UPS2000-G-3KRTS |
| huawei-ups2000 当前 man page | 补充较新版本中对 UPS2000G、XR21V1410、CH341 和已知问题的说明 |
| 华为 UPS2000 产品文档入口 | 查询厂商手册、验收、维护、通信矩阵等资料 |
| 知乎上的同类实践文章 | 同类部署思路参考 |
13. 可复用原则#
硬件接入不要从最终服务配置开始调。更稳的顺序是:
- 先确认物理链路:线缆、串口设备、内核驱动和权限先过关。
- 再做最小通信测试:用独立程序或驱动 debug 模式确认能读到 UPS 数据。
- 接入服务框架:让
upsd、upsmon、upssched分层验证。 - 保留路径一致性:如果使用发行版 systemd unit,就让自编译驱动落在发行版期望的
/lib/nut。 - 把关机策略当生产变更:任何会影响负载供电的命令都需要维护窗口、回滚方案和现场确认。
- 文档只沉淀方法,不沉淀秘密:NUT 密码、UPS 序列号、客户端地址和生产 IP 都不应该出现在公开文章里。
服务 active 只是开始。只有 upsc 持续返回正确状态、远端客户端按权限登录、断电恢复事件按策略进入 upssched,这套 NUT Server 才算真正接入完成。
