IPMIシミュレータ ipmisim を使ってみる

# はじめに
HAクラスタや仮想基盤ソフトの中にはスプリットブレイン対策でIPMIによる強制電源断機能を搭載しているものがあります。しかしながら自宅環境や検証環境にはリモートコンソール機能を持つサーバが転がっていなかったため、IPMIを擬似できるソフトがないか探していたところ、[ipmisim](https://github.com/rhtyd/ipmisim)というソフトを発見しました。本ソフトに微修正を施したところipmitoolの動作を擬似できましたので紹介したいと思います。

# ipmisimのインストール
CentOS7.3で試しています。

```
[root@cent1 ~]# yum install epel-release
[root@cent1 ~]# yum install python-pip
[root@cent1 ~]# yum install python-devel
[root@cent1 ~]# yum install git gcc
[root@cent1 ~]# git config --global user.name "tester"
[root@cent1 ~]# git config --global user.email "tester@example.com"
[root@cent1 ~]# git clone https://github.com/rhtyd/ipmisim.git
[root@cent1 ~]# cd ipmisim/
[root@cent1 ipmisim]# pip install .
```

ipmisimの以下のファイルに色々と付け足すことでipmitoolのオプションに応じた動きを擬似できます。

```
[root@cent1 ~]# vi ipmisim/ipmisim/fakebmc.py
(省略)
    def cold_reset(self):
        logger.info('IPMI BMC Cold_Reset request.')
        self.powerstate = 'off'
        self.bootdevice = 'default'

    def get_power_state(self):
        logger.info('IPMI BMC Get_Power_State request.')
        return self.powerstate

    def power_off(self):
        logger.info('IPMI BMC Power_Off request.')
        self.powerstate = 'off'

    def power_reset(self):
        logger.info('IPMI BMC Power_Reset request.')
        # warm boot
        self.powerstate = 'on'

    def power_shutdown(self):
        logger.info('IPMI BMC Power_Shutdown request.')
        self.powerstate = 'off'
```

# デフォルト動作の確認

まず、ipmitoolで落とされるサーバでipmisimを起動します。

```
[root@cent1 ipmisim]# firewall-cmd --zone=public --remove-port=9001/udp --permanent
success
[root@cent1 ipmisim]# firewall-cmd --reload
success
[root@cent1 ipmisim]# ipmisim 9001
2017-02-18 00:23:51,571 - ipmisim - INFO - IPMI BMC initialized.
2017-02-18 00:23:51,572 - ipmisim - INFO - CloudStack IPMI Sim BMC initialized
2017-02-18 00:23:51,572 - ipmisim - INFO - Started IPMI Server on 0.0.0.0:9001
```

次に別のマシン(cent2)からipmitoolを叩いてデフォルトの動作を確認します。

```
[root@cent2 ~]# yum install OpenIPMI OpenIPMI-tools
→ipmitoolを実行できるようにインストールを実施

[root@cent2 ~]# ipmitool -I lanplus -H <cent1のIPアドレス> -p 9001 -R1 -U admin -P password chassis power off
Chassis Power Control: Down/Off
→cent2からのpower offをリクエストしてみます。上記を実行するとcent1側で以下のログが出力されます(power offはされません)

[root@cent1 ipmisim]# ipmisim
2017-02-XX 00:23:51,571 - ipmisim - INFO - IPMI BMC initialized.
2017-02-XX 00:23:51,572 - ipmisim - INFO - CloudStack IPMI Sim BMC initialized
2017-02-XX 00:23:51,572 - ipmisim - INFO - Started IPMI Server on 0.0.0.0:9001
2017-02-XX 00:23:53,700 - ipmisim - INFO - New IPMI traffic from ('<Cent2のIP>', 36389)
2017-02-XX 00:23:53,704 - ipmisim - INFO - New IPMI session initialized for client (('<Cent2のIP>', 36389))
2017-02-XX 00:23:53,766 - ipmisim - INFO - IPMI BMC Power_Off request.
```

# ipmitool power offの擬似

ログを残すだけではなく、パニックを起こすように修正してみます。

```
[root@cent1 ipmisim]# vi ipmisim/fakebmc.py
...
    def power_off(self):
        logger.info('IPMI BMC Power_Off request.')
        self.powerstate = 'off'
        import os
        cmd = "echo c > /proc/sysrq-trigger"
        os.system(cmd)

→self.powerstate = 'off'の下に3行足しただけです。

[root@cent1 ipmisim]# pip install --upgrade .
[root@cent1 ipmisim]# ipmisim
2017-02-18 00:41:36,452 - ipmisim - INFO - IPMI BMC initialized.
2017-02-18 00:41:36,453 - ipmisim - INFO - CloudStack IPMI Sim BMC initialized
2017-02-18 00:41:36,453 - ipmisim - INFO - Started IPMI Server on 0.0.0.0:9001
```

準備が整いましたのでいざ実施！

```
[root@cent2 ~]# ipmitool -I lanplus -H <cent1のIPアドレス> -p 9001 -R1 -U admin -P password chassis power off
```

cent1がpanic状態になりました。power offではありませんが、とりあえず強制電源断を擬似できました。

# ipmitool power resetの擬似

カーネルパニック時に自動起動する設定を行うことで、ipmitool <遠隔ノード> power resetを実行したら、遠隔ノードでpanicを起こして再起動させてみます。

```
[root@cent1 ~]# vi /etc/sysctl.conf
kernel.panic = 6

[root@cent1 ~]# sysctl -p
kernel.panic = 6
```

これで`echo c > /proc/sysrq-trigger`でパニックを起こせば6秒後に再起動(またはpanic状態の維持)するようになりました。

```
[root@cent1 ~]# vi ipmisim/ipmisim/fakebmc.py

    def power_reset(self):
        logger.info('IPMI BMC Power_Reset request.')
        # warm boot
        self.powerstate = 'on'
        import os
        cmd = "echo c > /proc/sysrq-trigger"
        os.system(cmd)
→self.powerstate = 'on'の下に3行足しただけです。

[root@cent1 ~]# cd ipmisim/
[root@cent1 ipmisim]# pip install --upgrade .
[root@cent1 ipmisim]# ipmisim
2017-03-01 12:25:04,045 - ipmisim - INFO - IPMI BMC initialized.
2017-03-01 12:25:04,045 - ipmisim - INFO - CloudStack IPMI Sim BMC initialized
2017-03-01 12:25:04,046 - ipmisim - INFO - Started IPMI Server on 0.0.0.0:9001
```

先ほど同様、別ノードからpower resetをしてみます。

```
[root@cent2 ~]# ipmitool -I lanplus -H <cent1のIPアドレス> -p 9001 -R1 -U admin -P password chassis power reset
```

cent1が再起動しました。


# まとめ
ipmisimを修正してipmitoolで遠隔マシンを再起動することに成功しました。

もちろん今回のケースはOSに対して実行しているので可用性の観点ではほとんど効果がありません。
IPMIを使っている基盤系のソフトの動作確認等、あくまで検証での利用に留めて有効活用して頂ければと思います。