Python Paramiko timeout with long execution, need full output

被刻印的时光 ゝ 提交于 2019-12-01 17:49:28
user1772459

Here's something that might help, though I'm still in the midst of testing. After struggling with timeouts of various types including a catch-all timeout for Python, and realizing that the real problem is that the server can't be trusted to terminate the process, I did this:

chan = ssh.get_transport().open_session()

cmd = "timeout {0} {1}\n".format(timeouttime, cmd)

chan.exec_command(cmd)

The server times out after timeouttime if cmd doesn't exit sooner, exactly as I'd wish, and the terminated command kills the channel. The only catch is that GNU coreutils must exist on the server. Failing that there are alternatives.

I'm having the same kind of issue. I think we can handle it with signalling. http://docs.python.org/2/library/signal.html

Here is a plain dumb example to show how it works.

import signal, time                          

def handler(signum, frame):                  
    pass                                     

# Set the signal handler and a 2-second alarm
signal.signal(signal.SIGALRM, handler)       
signal.alarm(2)                              

# This is where your operation that might hang goes
time.sleep(10)                               

# Disable the alarm                          
signal.alarm(0)                              

So here, the alarm is set to 2 seconds. Time.sleep is called with 10 seconds. Of course, the alarm will be triggered before the sleep finishes. If you put some output after the time.sleep, you'll see that program execution resumes there.

If you want the control to continue somewhere else, wrap your hanging call in a try/except and have your handler function raise an exception.

Although I'm pretty sure it would work, I haven't tested it yet over paramiko calls.

I had a lot of problem calling the exec_command from the channel, instead I use directly the exec_command from the ssh connection and call the channel of the std output, the code that works for me is like myexec:

#!/usr/bin/env python
import paramiko
import select

def myexec(ssh, cmd, timeout):
  stdin, stdout, stderr = ssh.exec_command(cmd)
  channel = stdout.channel
  stdin.close() #As I don't need stdin
  channel.shutdown_write() #As I will not write to this channel

  stdout_chunks = []
  stdout_chunks.append(stdout.channel.recv(len(stdout.channel.in_buffer)))

  # chunked read to prevent stalls
  while not channel.closed or channel.recv_ready()
        or channel.recv_stderr_ready():

    # stop if channel was closed prematurely,
    # and there is no data in the buffers.
    got_chunk = False
    readq, _, _ = select.select([stdout.channel], [], [], timeout)
    for c in readq:
      if c.recv_ready():
        stdout_chunks.append(stdout.channel.recv(len(c.in_buffer)))
        got_chunk = True
      if c.recv_stderr_ready():
        # make sure to read stderr to prevent stall
        stderr.channel.recv_stderr(len(c.in_stderr_buffer))
        got_chunk = True
    if not got_chunk \
           and stdout.channel.exit_status_ready() \
           and not stderr.channel.recv_stderr_ready() \
           and not stdout.channel.recv_ready():
      # indicate that we're not going to read from this channel anymore
      stdout.channel.shutdown_read() # close the channel
      stdout.channel.close()
      break    # exit as remote side is finished and our bufferes are empty

  # close all the pseudofiles
  stdout.close()
  stderr.close()
  return (''.join(stdout_chunks), stdout.channel.recv_exit_status())

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('remotehost', username='remoteuser', password='remotepassword')
rtrval = myexec(ssh, 'remotecomand', 5*60)
ssh.close()
print rtrval

I use Debian 8 and Python 2.7.13, good luck.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!