2013-11-22 17 views
9

Sto esaminando casi di test in cui utilizzo paramiko per le connessioni SSH. I test case di solito contengono le chiamate paramiko.exec_command() per le quali ho un wrapper (chiamato run_command()). Qui self.ssh è un'intestazione di paramiko.SSHClient(). Uso un decoratore per verificare la connessione ssh prima di ogni chiamata. (self.get_ssh() negozia la connessione)Come sapere se un canale SSH paramiko è disconnesso?

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 

Funziona perfettamente fino miei riavvio nodo remoto, che può avvenire a volte. Quando si verifica la successiva chiamata run_command(), viene generata un'eccezione socket.error. Il problema è, che l'oggetto paramiko.Transport sembra rimanere in stato attivo fino a quando viene generata un'eccezione:

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 
>>> 

Domanda: come posso essere sicuro che la connessione è in realtà attiva o no?

risposta

8

In python, è easier to ask for forgiveness than permission.

Wrap ogni chiamata a ssh.exec_command in questo modo:

try: 
    ssh.exec_command('ls') 
except socket.error as e: 
    # Crap, it's closed. Perhaps reopen and retry? 
+0

+1 tuttavia è piuttosto una soluzione alternativa che una soluzione per me ... Immagino che il paramiko manchi di quello che mi serve ... – Milo

+1

@milo - hai trovato una soluzione migliore per Paramiko? Sono nella stessa identica posizione di te. –

+0

No, ma non ho fatto ricerche su di esso di recente. Penso che debba essere fatto in questo modo. (Ci scusiamo per la risposta tardiva.) – Milo

2

La mia soluzione praticamente uguali ai suoi, appena organizzato in modo diverso:

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

Questo funziona:

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 
Problemi correlati