Wie Sie wissen, wenn ein paramiko SSH-Kanal getrennt ist?
Ich bin desinging test Fälle, in denen ich verwenden paramiko für SSH-verbindungen. Testfälle enthalten in der Regel paramiko.exec_command()
Anrufe, die ich über ein wrapper für (genannt run_command()
). Hier self.ssh
ist ein Herzen lag der paramiko.SSHClient()
. Ich benutze ein Dekorateur, um zu überprüfen die ssh-Verbindung vor jedem Anruf. (self.get_ssh()
verhandelt der Verbindung)
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
Funktioniert es tadellos, bis meine remote-Knoten neu gestartet wird, kann manchmal passieren. Wenn es vorkommt, wird die nächste run_command()
Aufruf erzeugt eine socket.error
Ausnahme. Das problem ist, dass die paramiko.Transport
Objekt zu sein scheint, bleibt im aktiven Zustand bis eine exception geworfen wird:
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
>>>
Frage: wie kann ich sicher sein, dass die Verbindung wirklich aktiv ist oder nicht?
Du musst angemeldet sein, um einen Kommentar abzugeben.
In python, es ist einfacher um Vergebung zu bitten, als die Erlaubnis.
Wickeln Sie jeden Anruf zu
ssh.exec_command
etwa so:socket.error
ist ein alias fürOSError
(docs.python.org/3/library/socket.html#socket.error), das bedeutet zum Beispiel, dass man versehentlich fangenFileNotFoundError
welche ausgelöst werden können durch dieparamiko.SFTPClient
.Meine Lösung ist im Grunde das gleiche wie deins, nur anders organisiert:
Dies funktioniert:
Krank, nur werfen diese hier, da könnte jemand finden es nützlich. Es gibt einen Haken, mit einigen dieser Methoden.
Paramiko
intern verwendetsockets
. Jede neue Verbindung ruftsocket
dem öffnet sich ein neuer file-Deskriptor. Da die Prozesse beschränkt auf bestimmte Anzahl offener Datei-Deskriptoren, nach einiger Zeit werden Sie laufen, die Folge:socket.error: [Errno 24] Too many open files
.So ist es besser, explizit zu versuchen, die Verbindung zu schließen, bevor er einen neuen mit
SSHClient.close()
Methode.