Fabricで多段ssh
Fabricで多段sshした上でコマンド実行させることができたので、書いておきます。
やり方は.ssh/configに書かれたProxyCommand設定を読み込む方法とFabricのenvをいじる方法の2つがあります。
ここではFabricのenvをいじる方法について解説します。
環境
+---------------+ |192.168.200.99 | +---------------+ ↓ +---------------+ |192.168.200.100| +---------------+ ↓ +---------------+ |192.168.200.101| +---------------+ ↓ +---------------+ |192.168.200.102| +---------------+
- 192.168.200.99
プログラムを動かすところ。
- 192.168.200.100
踏み台役。アクセス制限や鍵などの設定は何もしていない。
- 192.168.200.101
TCP Wrapperで、sshでの接続は192.168.200.100からのみに制限している。
- 192.168.200.102
コード
# fabric_test.py from fabric.api import run, env from fabric.state import connections, output from fabric.network import denormalize from fabric.exceptions import NetworkError via = [('user1@192.168.200.100:22', 'password1'), ('user2@192.168.200.101:22'. 'password2'), ('user3@192.168.200.102:22', 'password3')] try: for host, passwd in via: env.gateway = env.host_string env.host_string = host env.password = passwd run('', quiet=True) run('hostname') except NetworkError as e: print(e) finally: for key in connections.keys(): if output.status: print("Disconnection from %s" $ denormalize(key)) connections[key].close()
実行結果
% python fabric_test.py [user3@192.168.200.102] run: hostname [user3@192.168.200.102] out: server3 [user3@192.168.200.102] out: Disconnection from user3@192.168.200.102... Disconnection from user2@192.168.200.101... Disconnection from user1@192.168.200.100...
鍵情報が必要な場合は、env.key_filenameにファイルパスを渡してあげるといいです。
(ただし、ファイルパスは当然プログラムが動いている環境のパスになります。)
基本的にはtryの部分の処理のみでOKなのですが、プログラムが終了するときにconnectionをcloseしてあげないと、以下の様なエラーが飛んできます。
Exception in thread Thread-2 (most likely raised during interpreter shutdown): Traceback (most recent call last): File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner File "/usr/lib/python2.6/site-packages/paramiko/transport.py", line 1613, in run <type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'error' Exception in thread Thread-3 (most likely raised during interpreter shutdown): Traceback (most recent call last): File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner File "/usr/lib/python2.6/site-packages/paramiko/transport.py", line 1613, in run <type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'error'
初めはFabric1.4.3の状態で「うーん、できない・・・」と悩んでいたのですが、Fabric1.5でenv.gatewayと.ssh/configのProxyCommandを読み込む機能が追加されたのを知り、上記のような感じになりました。
参考
Gateway solutions in Paramiko & Fabric - bitprophet.org
多段 ssh / rsync するために ProxyCommand を使ってみる (2) - daily dayflower
Intermittent "'NoneType' object has no attribute 'error'" · Issue #513 · fabric/fabric · GitHub
Exception in thread · Issue #17 · paramiko/paramiko · GitHub
Disconnecting from host with Python Fabric when using the API - Stack Overflow
-
-
- -
-
追記(2014/10/07)
上記コードではFabricの現行のバージョンで動きませんので、修正しました。
Fabricの1.6.4/1.7.5/1.8.5/1.9.1/1.10.0で動作確認済みです。
1.5.5では動きませんでした。
# fabric_test.py from fabric.api import run, env from fabric.state import connections, output from fabric.network import denormalize from fabric.exceptions import NetworkError via = [('user1@192.168.200.100:22', 'password1'), ('user2@192.168.200.101:22'. 'password2'), ('user3@192.168.200.102:22', 'password3')] try: for host, passwd in via: env.gateway = env.host_string env.host_string = host env.password = passwd env.passwords[host] = passwd connections.connect(host) run('hostname') except NetworkError as e: print(e) finally: for key in connections.keys(): if output.status: print("Disconnection from %s" % denormalize(key)) connections[key].close()