UPS と NAS, Linux サーバを連携させる

# やりたいこと
電力供給が途絶えたら、UPS に繋いだ NAS と Linux サーバを自動シャットダウンさせる。

# 環境
#### UPS
APC Smart-UPS 500

#### NAS
QNAP TS-451+ (IP Address: 192.168.1.10)

#### Linux サーバ
OS: Ubuntu 18.04 LTS (IP Address: 192.168.1.20)

# 前提として
UPS と何かのマシン 1 台だけ繋いで連携させるのはそれほど難しくない。しかし繋ぐマシンが 2 台以上になると設定が面倒になる。

そもそも、UPS からの電源供給に関する通知(停電したとか)は、USB によるシリアル通信で行われる。しかし UPS と直接シリアル接続できるマシンは、(少なくとも自分の所有する UPS については) 1 台のみである。

では 2 台目以降のマシンはどうやって通知を受け取るのかというと、UPS と 直接シリアル接続しているマシンから受け取る。ここで、UPS と直接シリアル接続しているマシンを **master**、master から通知を受け取るマシンを **slave** と呼ぶ。

今回は、NAS を master、Linux サーバを slave とした。

# 設定手順
## master (NAS) 側
やることは
1. UPS と NAS を USB 接続する
2. NAS の設定で UPS との接続方法を USB とし、ネットワーク内の他のマシンへの電源供給に関する通知を有効にする
3. slave が電源供給情報を取得するための nut ユーザ作成
4. NAS の UPS name の確認

以下は QNAP における手順です。他の製品の場合はそれのドキュメントを参照してください。

UPS と QNAP を USB 接続したら QNAP のコントロールパネルを開き、システム → 外部デバイス → UPS と進む。

繋ぎ方は「USB 接続」とする。また「ネットワーク UPS サポート」を有効にし、通知を有効にする IP アドレスには slave のもの (ここでは 192.168.1.20) を入力する。

次に slave が電源供給情報を取得するための nut ユーザを作成する。admin として QNAP に SSH 接続し、`/etc/config/ups/upsd.users` に以下を追記する。

```
[monslave]
    password=[好きなパスワード]
    upsmon slave
```

また `/etc/config/ups/ups.conf` の中身を参照し、QNAP の UPS name を確認しておく。以下の例では `qnapups` が UPS name である。

```
$ less /etc/config/ups/ups.conf
[qnapups] ← []の中身が UPS name
	driver = usbhid-ups
	port = /dev/ttyS1
	desc = "Workstation"
	pollinterval=2
```

設定が終わったら QNAP を再起動。

## slave (Linux サーバ)側
### nut の入手
OS が Ubuntu なので、必要なソフトウェアは `apt` で入手する。使うのは `nut` というパッケージである。以下のコマンドでインストールする。

```shell
$ sudo apt install nut
```

### nut 設定ファイルの編集
Ubuntu の場合設定ファイルは `/etc/nut/` にある。ここにあるファイルは以下の通り。

```
$ ls /etc/nut/
nut.conf  ups.conf  upsd.conf  upsd.users  upsmon.conf  upssched.conf
```

このうち、slave として機能させるためにいじる必要があるのは `nut.conf` `upsmon.conf` `upssched.conf` の 3 つである。編集する前に必ずコピーをとっておきましょう。

またこれらのファイルの所有グループが `nut` になっていないと動作しないため、確認しておきましょう。

#### nut.conf
以下を追記する。(競合する設定はコメントアウトする)

```
MODE=netclient
```

#### upsmon.conf
このファイルは、`upsmon` というデーモンの設定ファイルである。`upsmon` は UPS の状態通知を受け取り、その状態によってログを吐いたり wall でブロードキャストしたりする。`upsmon.conf` に以下を追記する。(競合する設定はコメントアウトする)

```
MONITOR qnapups@192.168.1.10:3493 1 monslave [monslaveのパスワード] slave
NOTIFYCMD /sbin/upssched
NOTIFYFLAG ONLINE       SYSLOG+WALL+EXEC
NOTIFYFLAG ONBATT       SYSLOG+WALL+EXEC
```

一行目の `MONITOR` で始まる行には、master の情報を記載する。

二行目の `NOTIFYCMD` で始まる行には、状態通知が来た際に実行する `upssched` のパスを記載する。このパスは環境によって変わる可能性がある。

四行目の `NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC` は、電源供給が途絶えてバッテリー駆動になった時の挙動を設定している。最後の `EXEC`　がないと、二行目の `upssched` が実行されない。

三行目の `NOTIFYFLAG ONLINE SYSLOG+WALL+EXEC` は同様に、電源が復旧した際の挙動を設定している。

#### upssched.conf
上の `upsmon.conf` 設定により、バッテリー駆動 or 電源復旧の通知が来たら、`upssched` が実行されるようになった。 今度はその `upssched` の挙動の設定を行う。

`upssched.conf` に以下を追記する。(競合する設定はコメントアウトする)

```
CMDSCRIPT /bin/upssched-cmd
PIPEFN /var/run/nut/upssched.pipe
LOCKFN /var/run/nut/upssched.lock
AT ONBATT * START-TIMER upsgone 120
AT ONLINE * CANCEL-TIMER upsgone
```

一行目の `CMDSCRIPT /bin/upssched-cmd` は、`upssched` が実行するスクリプト `upssched-cmd` のパスである。これも環境により変わる可能性がある。

二、三行目には `upssched` の実行の際に作られる一時ファイルのパスである。ディレクトリ `/var/run/nut/` は、ユーザ `nut` が書き込めるような権限にしておく必要がある。`/var/run/nut/` の所有グループを確認しておき、`nut` になっていなければ `nut` に変更すること。

四行目は、バッテリー駆動になったら 120 秒後に `upssched-cmd` を実行する設定をしている。`upsgone` は `upssched-cmd` に渡される引数である。

五行目では、電源復旧したら四行目のタイマーをキャンセルするよう設定している。

#### /bin/upssched-cmd
最後に　`upssched-cmd` にシャットダウン処理を加える。

```
case $1 in
        upsgone)
                logger -t upssched-cmd "The UPS has been gone for awhile"
                upsmon -c fsd ←これを追記
                ;;
        *)
                logger -t upssched-cmd "Unrecognized command: $1"
                ;;
esac
```

追記した `upsmon -c fsd` により、シャットダウンが行われる。

ここまで設定が終わったら、`nut-client` を再起動する。

```
$ sudo service nut-client restart
```

これで、停電時に NAS と Linux サーバの両方のシャットダウンが行えるようになった。

# 参考
- https://ameblo.jp/p630/entry-12333636345.html
- http://rogerprice.org/NUT/ConfigExamples.A5.pdf (英語だけどとても詳しい)