Tiempo de espera de Python Paramiko con larga ejecución, necesita salida completa

Hay muchos temas que tocan parte del título, pero nada que satisfaga todo el asunto. Estoy presionando un comando en un servidor remoto y necesito la salida completa después de un largo tiempo de ejecución, digamos 5 minutos aproximadamente. Usando el canal, pude establecer un tiempo de espera, pero cuando leí la salida estándar solo obtuve una pequeña parte de la salida. La solución parecía ser esperar a channel.exit_status_ready (). Esto funcionó en una llamada exitosa, pero una llamada fallida nunca activaría el tiempo de espera del canal. Habiendo revisado los documentos, teorizo ​​que es porque el tiempo de espera solo funciona en una operación de lectura, y esperar el estado de salida no califica. Aquí está ese intento:

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

Bonita, ¿no es así? Ojalá funcionara.

Mi primer intento de encontrar una solución fue usar time.time () para comenzar, luego verificar start - time.time ()> timeout. Esto parece sencillo, pero en mi versión actual, muestro start-time.time () con un tiempo de espera fijo que debería provocar un descanso ... y veo diferencias que duplican y triplican el tiempo de espera sin que se produzca un descanso. Para ahorrar espacio, mencionaré mi tercer intento, que he resumido con este. Leí aquí sobre el uso de select.select para esperar el resultado y noté en la documentación que también hay un tiempo de espera allí. Como verá en el código a continuación, mezclé los tres métodos: el tiempo de espera del canal, el tiempo de espera del tiempo de espera y el tiempo de espera de selección, pero aún tengo que terminar el proceso. Aquí está el código de franqueo:

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

Aquí hay algunos resultados típicos:

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

La línea superior es [rlist, wlist, elist] de select, la línea inferior es time.time () - start = (time.time () - start). Conseguí esta carrera para romper contando las iteraciones y rompiendo en la parte inferior de la prueba después del bucle 1000 veces. el tiempo de espera se estableció en 3 en la ejecución de la muestra. Lo que prueba que superamos el intento, pero obviamente, ninguna de las tres formas en que debería ser el tiempo de espera funciona.

Siéntase libre de copiar el código si, fundamentalmente, he entendido mal algo. Me gustaría que esto fuera súper-pitónico y todavía estoy aprendiendo.

Respuestas a la pregunta(3)

Su respuesta a la pregunta