2013-11-22 140 views
9

我正在設計測試用例,其中我使用paramiko進行SSH連接。測試用例通常包含paramiko.exec_command()調用,我有一個包裝(稱爲run_command())。這裏的self.sshparamiko.SSHClient()的一個實例。在每次調用之前,我使用裝飾器檢查ssh連接。 (self.get_ssh()協商連接)如何知道paramiko 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 
>>> 

問:我怎麼能肯定的是,連接確實是活躍與否?

回答

8

在Python中,它是easier to ask for forgiveness than permission

總結每次調用ssh.exec_command像這樣:

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

然而+1這是一個解決方案,而不是我的解決方案...我猜paramiko缺乏我需要的... – Milo

+1

@milo - 您是否找到Paramiko的更好解決方案?我和你的位置完全一樣。 –

+0

不,但我最近沒有做過任何研究。我認爲必須這樣做。 (對不起,延遲迴復。) – Milo

2

我的解決方法基本上是一樣的你,只是組織形式不同:

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

這工作:

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 
相關問題