タイトルの一部に触れるトピックはたくさんありますが、全体を完全に満足させるものはありません。リモート サーバーでコマンドをプッシュしていて、長い実行時間 (たとえば 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() > timeout を確認することでした。これは簡単に思えますが、私の現在のバージョンでは、ブレークをトリガーする固定タイムアウトで start - time.time() を出力し、ブレークが発生しないでタイムアウトを 2 倍および 3 倍にする違いを確認します。スペースを節約するために、3 回目の試みについて言及します。ここで select.select を使用して出力を待機する方法について読み、ドキュメントにタイムアウトがあることにも注意しました。以下のコードからわかるように、チャネル タイムアウト、time.time タイムアウト、および選択タイムアウトの 3 つの方法をすべて組み合わせましたが、それでもプロセスを強制終了する必要があります。フランケンコードは次のとおりです。
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 に設定されました。これは、試行をやり遂げたことを証明していますが、明らかに、タイムアウトになるはずの 3 つの方法のいずれも機能しません。
基本的に何かを誤解している場合は、遠慮なくコードを読み込んでください。これを uber-Pythonic にしたいのですが、まだ学習中です。