SSH接続にparamikoを使用するテストケースを設計しています。テストケースには通常、ラッパーを持っているparamiko.exec_command()
呼び出しが含まれています(run_command()
と呼ばれます)。ここで、_self.ssh
_はparamiko.SSHClient()
のインスタンスです。各呼び出しの前に、デコレータを使用してssh接続を確認します。 (self.get_ssh()
は接続をネゴシエートします)
_def check_connections(function):
''' A decorator to check SSH connections. '''
def deco(self, *args, **kwargs):
if self.ssh is None:
self.ssh = self.get_ssh()
else:
ret = getattr(self.ssh.get_transport(), 'is_active', None)
if ret is None or (ret is not None and not ret()):
self.ssh = self.get_ssh()
return function(self, *args, **kwargs)
return deco
_
_@check_connections
def run_command(self, command):
''' Executes command via SSH. '''
stdin, stdout, stderr = self.ssh.exec_command(command)
stdin.flush()
stdin.channel.shutdown_write()
ret = stdout.read()
err = stderr.read()
if ret:
return ret
Elif err:
return err
else:
return None
_
リモートノードが再起動するまでは完全に機能しますが、これは時々発生する可能性があります。それが発生すると、次のrun_command()
呼び出しは_socket.error
_例外を生成します。問題は、例外がスローされるまで_paramiko.Transport
_オブジェクトがアクティブ状態のままであるように見えることです。
_Python 2.7.3 (default, Mar 7 2013, 14:03:36)
[GCC 4.3.4 [gcc-4_3-branch revision 152973]] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> import paramiko
>>> ssh = paramiko.SSHClient()
>>> print ssh
<paramiko.SSHClient object at 0x7f2397b96d50>
>>> print ssh.get_transport()
None
>>> ssh.set_missing_Host_key_policy(paramiko.AutoAddPolicy())
>>> ssh.load_Host_keys(os.path.expanduser('~') + '/.ssh/known_hosts')
>>> ssh.connect(hostname = '172.31.77.57', username = 'root', password = 'rootroot', timeout = 5.0)
>>> print ssh
<paramiko.SSHClient object at 0x7f2397b96d50>
>>> print ssh.get_transport()
<paramiko.Transport at 0x97537550L (cipher aes128-ctr, 128 bits) (active; 0 open channel(s))>
>>> print ssh.get_transport().is_active()
True
>>> ssh.exec_command('ls')
(<paramiko.ChannelFile from <paramiko.Channel 1 (open) window=2097152 -> <paramiko.Transport at 0x97537550L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>, <paramiko.ChannelFile from <paramiko.Channel 1 (open) window=2097152 -> <paramiko.Transport at 0x97537550L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>, <paramiko.ChannelFile from <paramiko.Channel 1 (open) window=2097152 -> <paramiko.Transport at 0x97537550L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>)
>>> print ssh
<paramiko.SSHClient object at 0x7f2397b96d50>
>>> print ssh.get_transport()
<paramiko.Transport at 0x97537550L (cipher aes128-ctr, 128 bits) (active; 0 open channel(s))>
>>> print ssh.get_transport().is_active()
True
>>> ssh.exec_command('reboot')
(<paramiko.ChannelFile from <paramiko.Channel 2 (open) window=2097152 -> <paramiko.Transport at 0x97537550L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>, <paramiko.ChannelFile from <paramiko.Channel 2 (open) window=2097152 -> <paramiko.Transport at 0x97537550L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>, <paramiko.ChannelFile from <paramiko.Channel 2 (open) window=2097152 -> <paramiko.Transport at 0x97537550L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>)
>>> print ssh
<paramiko.SSHClient object at 0x7f2397b96d50>
>>> print ssh.get_transport()
<paramiko.Transport at 0x97537550L (cipher aes128-ctr, 128 bits) (active; 0 open channel(s))>
>>> print ssh.get_transport().is_active()
True
>>> ssh.exec_command('ls')
No handlers could be found for logger "paramiko.transport"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/pytest/lib/python2.7/site-packages/paramiko/client.py", line 370, in exec_command
chan = self._transport.open_session()
File "/home/pytest/lib/python2.7/site-packages/paramiko/transport.py", line 662, in open_session
return self.open_channel('session')
File "/home/pytest/lib/python2.7/site-packages/paramiko/transport.py", line 764, in open_channel
raise e
socket.error: [Errno 104] Connection reset by peer
>>> print ssh
<paramiko.SSHClient object at 0x7f2397b96d50>
>>> print ssh.get_transport()
<paramiko.Transport at 0x97537550L (unconnected)>
>>> print ssh.get_transport().is_active()
False
>>>
_
質問:接続が本当にアクティブであるかどうかを確認するにはどうすればよいですか?
Pythonでは 許可よりも許しを求める方が簡単です 。
ssh.exec_command
への各呼び出しを次のようにラップします。
try:
ssh.exec_command('ls')
except socket.error as e:
# Crap, it's closed. Perhaps reopen and retry?
私のソリューションは基本的にあなたのソリューションと同じですが、編成が異なります。
def connection(self):
if not self.is_connected():
self._ssh = paramiko.SSHClient()
self._ssh.connect(self.server, self.port,
username = self.username, password = self.password)
return self._ssh
def is_connected(self):
transport = self._ssh.get_transport() if self._ssh else None
return transport and transport.is_active()
def do_something(self):
self.connection().exec_command('ls')
誰かがそれを役に立つと思うかもしれないので、私はこれをここに投げます。これらの方法のいくつかには1つの落とし穴があります。 Paramiko
は内部的にsockets
を使用します。新しい接続はすべてsocket
を呼び出し、新しいファイル記述子を開きます。プロセスは一定数の開いているファイル記述子に制限されているため、しばらくすると不足し、次のような結果になります。
_socket.error: [Errno 24] Too many open files
_。
したがって、SSHClient.close()
メソッドを使用して新しい接続を確立する前に、明示的に接続を閉じようとすることをお勧めします。
これは機能します:
import paramiko
client = paramiko.SSHClient()
client.set_missing_Host_key_policy(paramiko.AutoAddPolicy()) # Setting the missing Host policy to auto add it
client.connect('192.168.1.16', port=22, username='admin', password='admin', timeout=3, banner_timeout=2)
channel = client.invoke_Shell() # Request an interactive Shell session on this channel. If the server allows it, the channel will then be directly connected to the stdin, stdout, and stderr of the Shell.
print channel.closed # False
command = 'reboot'
channel.send(command + '\n')
# wait a while
print channel.closed # True