有很多主题涉及标题的一部分,但没有什么能完全满足整个事情。我在远程服务器上推送一个命令,经过很长的执行时间后需要完整的输出,比如5分钟左右。使用通道我能够设置超时,但是当我读回stdout时,我只得到了一小部分输出。解决方案似乎是等待channel.exit_status_ready()。这对成功的呼叫起作用,但是失败的呼叫永远不会触发信道超时。在查看了文档之后,我推测这是因为超时仅适用于读取操作,并且等待退出状态不符合条件。这是尝试:
channel = ssh.get_transport().open_session()
channel.settimeout(timeout)
channel.exec_command(cmd) # return on this is not reliable
while True:
try:
if channel.exit_status_ready():
if channel.recv_ready(): # so use recv instead...
output = channel.recv(1048576)
break
if channel.recv_stderr_ready(): # then check error
error = channel.recv_stderr(1048576)
break
except socket.timeout:
print("SSH channel timeout exceeded.")
break
except Exception:
traceback.print_exc()
break
漂亮,不是吗?希望它有效。
我首次尝试解决方案是使用time.time()来开始,然后检查start - time.time()>超时。这看起来很简单,但是在我现在的版本中,我输出start-time.time(),其中一个固定的超时应该触发一个中断...并且看到差异使得超时加倍并且不会发生中断。为了节省空间,我会提到我的第三次尝试,我已经用这个尝试了。我在这里读到关于使用select.select等待输出的问题,并在文档中注意到那里也有超时。正如您将从下面的代码中看到的,我混合了所有三种方法 - 通道超时,time.time超时和选择超时 - 但仍然必须终止进程。这是frankencode:
channel = ssh.get_transport().open_session()
channel.settimeout(timeout)
channel.exec_command(cmd) # return on this is not reliable
print("{0}".format(cmd))
start = time.time()
while True:
try:
rlist, wlist, elist = select([channel], [], [],
float(timeout))
print("{0}, {1}, {2}".format(rlist, wlist, elist))
if rlist is not None and len(rlist) > 0:
if channel.exit_status_ready():
if channel.recv_ready(): # so use recv instead...
output = channel.recv(1048576)
break
elif elist is not None and len(elist) > 0:
if channel.recv_stderr_ready(): # then check error
error = channel.recv_stderr(1048576)
break
print("{0} - {1} = {2}".format(
time.time(), start, time.time() - start))
if time.time() - start > timeout:
break
except socket.timeout:
print("SSH channel timeout exceeded.")
break
except Exception:
traceback.print_exc()
break
这是一些典型的输出:
[<paramiko.Channel 3 (open) window=515488 -> <paramiko.Transport at 0x888414cL (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>], [], []
1352494558.42 - 1352494554.69 = 3.73274183273
顶行是select的[rlist,wlist,elist],底行是time.time() - start =(time.time() - start)。通过计算迭代次数并在循环1000次后尝试在底部打破,我得到了这个运行。样本运行时超时设置为3。这证明我们通过了尝试,但很明显,三种方式都不应该超时。
如果我从根本上误解了某些内容,请随意翻录代码。我希望这是超级 - Pythonic,我还在学习。
答案 0 :(得分:3)
虽然我还在测试中,但这可能会有所帮助。在经历了各种类型的超时(包括Python的全部超时),并意识到真正的问题是服务器无法被信任终止进程之后,我做到了这一点:
chan = ssh.get_transport().open_session()
cmd = "timeout {0} {1}\n".format(timeouttime, cmd)
chan.exec_command(cmd)
如果timeouttime
cmd
没有尽快退出,服务器将在{{1}}之后超时,完全按照我的意愿,并且终止的命令会终止该频道。唯一的问题是GNU coreutils必须存在于服务器上。没有替代品。
答案 1 :(得分:2)
我遇到了同样的问题。我认为我们可以通过信号处理它。 http://docs.python.org/2/library/signal.html
这是一个简单明了的例子来说明它是如何运作的。
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)
所以在这里,闹钟设置为2秒。 Time.sleep用10秒调用。当然,警报将在睡眠结束前触发。如果你在time.sleep之后输入一些输出,你会看到程序执行在那里重新开始。
如果您希望控件继续在其他位置继续,请在try / except中包装挂起的调用,并让处理函数引发异常。
虽然我很确定它会起作用,但我还没有通过paramiko调用测试它。
答案 2 :(得分:0)
我从通道调用exec_command时遇到了很多问题,而是直接使用ssh连接中的exec_command并调用std输出的通道,对我有用的代码就像 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
我使用Debian 8和Python 2.7.13,祝你好运。