Linux マシンをリモートから電源オンオフするアプリ

HP の小型サーバー "HP Proliant MicroServer N54L" が激安だったので買って Ubuntu ServerPlex Media Server をインストールして動画サーバーにしました。 消費電力は少なめですが、やはり電源入れっぱなしは電気がもったいないので動画を見ない時は電源を切っています。タブレット(Windows 8.1)でこのサーバーに入れた動画を見る時に電源を入れたり見終わった時に電源を切りためにサーバーの近くまで行くのが面倒なのでリモートで電源オンオフするアプリを作ってみました。

開発言語

お手軽なのとデスクトップパソコン(iMac)からも使いたいので Python で作ることにしました。また、GUI もつけたいと思ったのですが凝った UI は不要なので標準で入っている Tkinter を使います。

電源オン

HP Proliant MicroServer は Wake on LAN に対応しているので同一 LAN 内であれば他のパソコンから電源オンできそうです。emptypage.jp の方が wol.py を公開してくださっているのでそのまま使わせてもらうことにしました。

電源オフ

ssh でリモートログインして shutdown コマンドを実行するという方法をとることにしました。Paramiko という便利なモジュールがあるのでこれを使います。

ステータス表示

サーバーの電源が今オンなのかオフなのかはわかったほうが良いので定期的に ping を打って応答を確認するようにしました。Mac OSX では ping コマンドの終了コードが 0 であることを確認するだけで良いのですが、Windows の場合はサーバーの電源がオフで ping に応答しない場合でも終了コードが 0 になる場合がありました。そこで、Windows の場合は ping の出力に "unreachable" や "Received = 0" という文字列が含まれていたら電源オフ、含まれていなければ電源オンと判断することにしました。

Windowsping への対応

日本語 Windows で実行した場合 ping の応答も日本語で帰ってくるので、それぞれ "到達できません", "受信 = 0" で判断する必要がありました。なので、日本語/英語以外のロケールで実行すると正しく動作しません。英語モードに切り替えて ping を実行できればよいのですがやり方がわかりませんでした...。*1 Windows では ping コマンドの実行ではなく、Windows API (ICMPSendEcho) を使って ICMP パケットを投げたほうが良いのかもしれません。

ping の非同期実行

pingPython の subprocess モジュールで実行させました。subprocess.call() で実行するのが簡単なのですが、実行が完了するまで待つのでサーバーが起動しておらず pingタイムアウトになる場合はタイムアウトになるまで UI が応答しなくなってしまいます。マルチスレッドにしても良いのでしょうが、なんだか大袈裟さので proc = subprocess.Popen() で ping を実行し、proc.poll() で終了するまでポーリングすることにしました。

py2exe

実際に動かす Windows タブレットPython や Paramiko モジュールをインストールしなくても動作するよう py2exe を使って EXE 化することにしました。

というわけで

bitbucketソースコードWindows 用バイナリーを置いておきました。以下のコマンドラインオプションをセットする必要があります。

-h オンオフしたいマシンのIPアドレス
-m オンオフしたいマシンのMACアドレス
-u マシンにログインするためのユーザー名(sudo で shutdown コマンドを実行できる権限を持っている必要あり)
-p -u で指定するユーザーのパスワード
-g GUI モードを指定

-g の代わりに -w を指定すると GUI なしにマシン起動を実行します。同じく -s を指定するとマシンをシャットダウンします。

f:id:loopdeloop:20150201215313p:plain

f:id:loopdeloop:20150201215321p:plain

TODO

エラー処理をまともにする。

*1:cmd.exe で "chcp 437 & ping" を実行したところ、普通に Python インタプリタ経由では OK なのに、py2exe で EXE 化したら NG でした。