Python Paramiko timeout z długim wykonaniem, wymaga pełnego wyjścia

Jest wiele tematów dotykających części tytułu, ale nic, co całkiem nie satysfakcjonuje całej sprawy. Naciskam polecenie na zdalnym serwerze i potrzebuję pełnego wyjścia po długim czasie wykonania, powiedzmy około 5 minut. Korzystając z kanału udało mi się ustawić limit czasu, ale gdy przeczytałem stdout, otrzymałem tylko niewielką część wyjścia. Rozwiązaniem wydawało się czekać na channel.exit_status_ready (). To działało na udanym wywołaniu, ale nieudane wywołanie nigdy nie wyzwoli limitu czasu kanału. Po zapoznaniu się z dokumentami, teoretycznie twierdzę, że limit czasu działa tylko w przypadku operacji odczytu, a czekanie na status wyjścia nie kwalifikuje się. Oto ta próba:

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

Ładnie, prawda? Chciałbym, żeby to zadziałało.

Moją pierwszą próbą rozwiązania było użycie time.time (), aby uzyskać start, a następnie sprawdź start - time.time ()> timeout. Wydaje się to proste, ale w mojej obecnej wersji wypuszczam start - time.time () ze stałym limitem czasu, który powinien wywołać przerwę ... i zobaczyć różnice, które podwoją i potroją limit czasu bez wystąpienia przerwy. Aby zaoszczędzić miejsce, wspomnę o mojej trzeciej próbie, którą zawarłem z tym. Czytałem tutaj o używaniu select.select, aby poczekać na wyjście, i odnotowałem w dokumentacji, że jest tam także limit czasu. Jak zobaczysz w poniższym kodzie, zmieszałem wszystkie trzy metody - limit czasu kanału, limit czasu time.time i wybrałem limit czasu - a mimo to muszę zabić proces. Oto 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

Oto typowe dane wyjściowe:

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

Górny wiersz to [rlist, wlist, elist] od select, dolny wiersz to time.time () - start = (time.time () - start). Rozerwałem ten bieg, licząc iteracje i łamanie na dole próby po zapętleniu 1000 razy. Limit czasu został ustawiony na 3 w przebiegu próbnym. Co dowodzi, że przechodzimy przez próbę, ale oczywiście żaden z trzech sposobów, które powinny ograniczać czas pracy.

Zapraszam do zgrania do kodu, jeśli coś źle zrozumiałem. Chciałbym, żeby to było uber-Pythonic i wciąż się uczę.

questionAnswers(3)

yourAnswerToTheQuestion