intent

intent的blog

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

UPS/NUT(網絡UPS工具)

UPS/NUT (網絡 UPS 工具)#

NUT#

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

Nut 主要包含三個核心服務:

  • nut-driver: 這個服務負責通過特定驅動來與 UPS 進行通信
  • nut-server: 該服務利用 nut-driver 溝通 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

參考#

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。