Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

paramiko python module hangs at stdout.read()

I am using the below code:

import paramiko

def runSshCmd(hostname, username, password, cmd, timeout=None):          
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname, username=username, password=password,
            allow_agent=False, look_for_keys=False, timeout=timeout) 
    stdin, stdout, stderr = client.exec_command(cmd)
    stdin.flush()
    data = stdout.read()
    print (data)
    client.close()

runSshCmd("10.128.12.32", "root", "C0mput3Gr!d", "ts_menu")

when it comes to stdout.read() , it hangs... sometimes it prints the output after long time.

Can you please suggest if anything can be done about this issue??

I see this issue has been reported in :

https://bugs.python.org/issue24026

Is there any better module in python for ssh connection and run commands ??

like image 899
user3378508 Avatar asked Feb 08 '16 09:02

user3378508


4 Answers

Could be related to https://github.com/paramiko/paramiko/issues/109

Below is explanation of what i am facing and how i worked around it.

I also experienced this issue it is due to stdout.channel.eof_received == 0

import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect("1.1.1.1", username="root", password="pass")
stdin, stdout, stderr = client.exec_command("service XXX start")

stdin, stdout and stderr are staying open...

>>> print stdin
<paramiko.ChannelFile from <paramiko.Channel 3 (open) window=2097152 in-buffer=50 -> <paramiko.Transport at 0x17eff90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>
>>> print stdout
<paramiko.ChannelFile from <paramiko.Channel 3 (open) window=2097152 in-buffer=50 -> <paramiko.Transport at 0x17eff90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>
>>> print stderr
<paramiko.ChannelFile from <paramiko.Channel 3 (open) window=2097152 in-buffer=50 -> <paramiko.Transport at 0x17eff90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>

So EOF was not received...

>>> print stdin.channel.eof_received
0

Usually I receive True and can just stdout.read(), but to be safe i use this workaround (which works!): Wait for a timeout, force stdout.channel.close() and then stdout.read():

>>> timeout = 30
>>> import time
>>> endtime = time.time() + timeout
>>> while not stdout.channel.eof_received:
...     sleep(1)
...     if time.time() > endtime:
...         stdout.channel.close()
...         break
>>> stdout.read()
'Starting XXX: \n[  OK  ]\rProgram started . . .\n'
>>>

BTW i use:

Python 2.6.6
paramiko (1.15.2)

Hope this helps...

like image 145
Dmitry Tokarev Avatar answered Oct 24 '22 07:10

Dmitry Tokarev


I happen to come across this issue. But I kinda work around it by using "readline" instead "readlines".

For example:

client = paramiko.SSHClient()
client.connect(addr, port, username, password)
stdin, stdout, stderr = client.exec_command(cmd)

while True:
    print(stdout.readline())
    if stdout.channel.exit_status_ready():
        break

So it will print every line immediately and no more hanging, also exit_status_ready() will make sure the loop breaks when stdout has stopped/exited.

like image 43
Carl Cheung Avatar answered Oct 24 '22 05:10

Carl Cheung


It use to happen when there is no data in stdout or there is a line without eol (i.e. in a read statement inside a sh script). Try setting 'get_pty=True', then reading only the bytes in stdout. To avoid infinite loops, it'd be a good idea setting a timeout and a sleep in spite of the continue statement:

stdin, stdout, stderr = ssh.exec_command("your-command",get_pty=True)
stdout.flush()
nbytes = 0
while (len(stdout.channel.in_buffer)==0):
     continue

nbytes=len(stdout.channel.in_buffer)
print(nbytes)
stdout.read(nbytes)
like image 3
nachopol Avatar answered Oct 24 '22 05:10

nachopol


try to close stdin before you use stdout.

env:
python3.8
paramiko-2.7.2

stdin, stdout, stderr = client.exec_command(cmd)
stdin.close()
for line in iter(stout.readline,""):
    print(line, end='')
like image 2
user16013904 Avatar answered Oct 24 '22 07:10

user16013904