openvstorage / framework

The Framework is a set of components and tools which brings the user an interface (GUI / API) to setup, extend and manage an Open vStorage platform.
Other
27 stars 23 forks source link

SSHClient can't handle large output from executed command #846

Closed JeffreyDevloo closed 8 years ago

JeffreyDevloo commented 8 years ago

Problem description

The SSHClient.run can and will hang forever on large outputs.

Possible root of the problem

The run method in SSHClient reads the whole stdout in a single go. The issue is, is that this buffer can overflow if a large output from a command is expected.

stdout = ''.join(stdout.readlines()).replace(u'\u2018', u'"').replace(u'\u2019', u'"')

Possible solution

Read stdout partially.

stdin, stdout, stderr = ssh_client.client.exec_command('python {0}'.format(LogFileTimeParser.TEMP_FILE_PATH))
                channel = stdout.channel

                stdin.close()  # we do not need stdin.
                channel.shutdown_write()  # indicate that we're not going to write to that channel anymore
                # read stdout/stderr in order to prevent read block hangs
                stdout_chunks = []
                # chunked read to prevent stalls
                while not channel.closed:  # stop if channel was closed prematurely
                    readq, _, _ = select.select([stdout.channel], [], [], LogFileTimeParser.TIME_OUT)
                    for c in readq:
                        if c.recv_ready():
                            stdout_chunks.append(stdout.channel.recv(len(c.in_buffer)))                          
                        if c.recv_stderr_ready():
                            # make sure to read stderr to prevent stall
                            stderr.channel.recv_stderr(len(c.in_stderr_buffer))
                    if 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 buffers are empty

                # close all the pseudofiles
                stdout.close()
                stderr.close()
                ssh_client.client.close()

Additional information

Setup

Hyperconverged setup

wimpers commented 8 years ago

Can your create a PR for this?

JeffreyDevloo commented 8 years ago

Steps

from ovs.extensions.generic.sshclient import SSHClient
client = SSHClient('10.100.199.152', 'root')
client.run('lsof')

The lsof was one of the commands that always hung when started.

Output

Got the output from command as expected.

u"COMMAND     PID   TID       USER   FD      TYPE             DEVICE SIZE/OFF       NODE NAME\ninit          1             root  cwd       DIR              252,0     4096          2 /\ninit          1             root  rtd       DIR              252,0     4096          2 /\ninit          1             root  txt       REG              252,0   265848     260233 /sbin/init\ninit          1             root  mem       REG              252,0    43616     390375 /lib/x86_64-linux-gnu/libnss_files-2.19.so\ninit          1             root  mem       REG              252,0    47760     390379 /lib/x86_64-linux-gnu/libnss_nis-2.19.so\ninit          1             root  mem       REG              252,0    97296     390369 /lib/x86_64-linux-gnu/libnsl-2.19.so\ninit          1             root  mem       REG              252,0    39824     390371 /lib/x86_64-linux-gnu/libnss_compat-2.19.so\ninit          1             root  mem       REG              252,0    14664     390332 /lib/x86_64-linux-gnu/libdl-2.19.so\ninit          1             root  mem       REG              252,0   252032     390391 /lib/x86_64-linux-gnu/libpcre.so.3.13.1\ninit          1             root  mem       REG              252,0   141574     390406 /lib/x86_64-linux-gnu/libpthread-2.19.so\ninit          1             root  mem       REG              252,0  1840928     390316 /lib/x86_64-linux-gnu/libc-2.19.so\ninit          1             root  mem       REG

Test result

Test passed.