Example #1
0
    def test_sanitize_output_use_pyt_false(self):
        # pty is not used, \r\n shouldn't be replaced with \n
        input_strs = [
            'foo',
            'foo\n',
            'foo\r\n',
            'foo\nbar\nbaz\n',
            'foo\r\nbar\r\nbaz\r\n',
        ]
        expected = [
            'foo',
            'foo',
            'foo',
            'foo\nbar\nbaz',
            'foo\r\nbar\r\nbaz',
        ]

        for input_str, expected_output in zip(input_strs, expected):
            output = sanitize_output(input_str, uses_pty=False)
            self.assertEqual(expected_output, output)
Example #2
0
    def test_sanitize_output_use_pyt_true(self):
        # pty is used, \r\n should be replaced with \n
        input_strs = [
            "foo",
            "foo\n",
            "foo\r\n",
            "foo\nbar\nbaz\n",
            "foo\r\nbar\r\nbaz\r\n",
        ]
        expected = [
            "foo",
            "foo",
            "foo",
            "foo\nbar\nbaz",
            "foo\nbar\nbaz",
        ]

        for input_str, expected_output in zip(input_strs, expected):
            output = sanitize_output(input_str, uses_pty=True)
            self.assertEqual(expected_output, output)
Example #3
0
    def run(self,
            cmd,
            timeout=None,
            quote=False,
            call_line_handler_func=False):
        """
        Note: This function is based on paramiko's exec_command()
        method.

        :param timeout: How long to wait (in seconds) for the command to finish (optional).
        :type timeout: ``float``

        :param call_line_handler_func: True to call handle_stdout_line_func function for each line
                                       of received stdout and handle_stderr_line_func for each
                                       line of stderr.
        :type call_line_handler_func: ``bool``
        """

        if quote:
            cmd = quote_unix(cmd)

        extra = {'_cmd': cmd}
        self.logger.info('Executing command', extra=extra)

        # Use the system default buffer size
        bufsize = -1

        transport = self.client.get_transport()
        chan = transport.open_session()

        start_time = time.time()
        if cmd.startswith('sudo'):
            # Note that fabric does this as well. If you set pty, stdout and stderr
            # streams will be combined into one.
            # NOTE: If pty is used, every new line character \n will be converted to \r\n which
            # isn't desired. Because of that we sanitize the output and replace \r\n with \n at the
            # bottom of this method
            uses_pty = True
            chan.get_pty()
        else:
            uses_pty = False
        chan.exec_command(cmd)

        stdout = StringIO()
        stderr = StringIO()

        # Create a stdin file and immediately close it to prevent any
        # interactive script from hanging the process.
        stdin = chan.makefile('wb', bufsize)
        stdin.close()

        # Receive all the output
        # Note #1: This is used instead of chan.makefile approach to prevent
        # buffering issues and hanging if the executed command produces a lot
        # of output.
        #
        # Note #2: If you are going to remove "ready" checks inside the loop
        # you are going to have a bad time. Trying to consume from a channel
        # which is not ready will block for indefinitely.
        exit_status_ready = chan.exit_status_ready()

        if exit_status_ready:
            stdout_data = self._consume_stdout(
                chan=chan, call_line_handler_func=call_line_handler_func)
            stdout_data = stdout_data.getvalue()

            stderr_data = self._consume_stderr(
                chan=chan, call_line_handler_func=call_line_handler_func)
            stderr_data = stderr_data.getvalue()

            stdout.write(stdout_data)
            stderr.write(stderr_data)

        while not exit_status_ready:
            current_time = time.time()
            elapsed_time = (current_time - start_time)

            if timeout and (elapsed_time > timeout):
                # TODO: Is this the right way to clean up?
                chan.close()

                stdout = sanitize_output(stdout.getvalue(), uses_pty=uses_pty)
                stderr = sanitize_output(stderr.getvalue(), uses_pty=uses_pty)
                raise SSHCommandTimeoutError(cmd=cmd,
                                             timeout=timeout,
                                             stdout=stdout,
                                             stderr=stderr)

            stdout_data = self._consume_stdout(
                chan=chan, call_line_handler_func=call_line_handler_func)
            stdout_data = stdout_data.getvalue()

            stderr_data = self._consume_stderr(
                chan=chan, call_line_handler_func=call_line_handler_func)
            stderr_data = stderr_data.getvalue()

            stdout.write(stdout_data)
            stderr.write(stderr_data)

            # We need to check the exit status here, because the command could
            # print some output and exit during this sleep below.
            exit_status_ready = chan.exit_status_ready()

            if exit_status_ready:
                break

            # Short sleep to prevent busy waiting
            concurrency.sleep(self.SLEEP_DELAY)
        # print('Wait over. Channel must be ready for host: %s' % self.hostname)

        # Receive the exit status code of the command we ran.
        status = chan.recv_exit_status()

        stdout = sanitize_output(stdout.getvalue(), uses_pty=uses_pty)
        stderr = sanitize_output(stderr.getvalue(), uses_pty=uses_pty)

        extra = {'_status': status, '_stdout': stdout, '_stderr': stderr}
        self.logger.debug('Command finished', extra=extra)

        return [stdout, stderr, status]
Example #4
0
    def run(self, cmd, timeout=None, quote=False, call_line_handler_func=False):
        """
        Note: This function is based on paramiko's exec_command()
        method.

        :param timeout: How long to wait (in seconds) for the command to finish (optional).
        :type timeout: ``float``

        :param call_line_handler_func: True to call handle_stdout_line_func function for each line
                                       of received stdout and handle_stderr_line_func for each
                                       line of stderr.
        :type call_line_handler_func: ``bool``
        """

        if quote:
            cmd = quote_unix(cmd)

        extra = {'_cmd': cmd}
        self.logger.info('Executing command', extra=extra)

        # Use the system default buffer size
        bufsize = -1

        transport = self.client.get_transport()
        chan = transport.open_session()

        start_time = time.time()
        if cmd.startswith('sudo'):
            # Note that fabric does this as well. If you set pty, stdout and stderr
            # streams will be combined into one.
            # NOTE: If pty is used, every new line character \n will be converted to \r\n which
            # isn't desired. Because of that we sanitize the output and replace \r\n with \n at the
            # bottom of this method
            uses_pty = True
            chan.get_pty()
        else:
            uses_pty = False
        chan.exec_command(cmd)

        stdout = StringIO()
        stderr = StringIO()

        # Create a stdin file and immediately close it to prevent any
        # interactive script from hanging the process.
        stdin = chan.makefile('wb', bufsize)
        stdin.close()

        # Receive all the output
        # Note #1: This is used instead of chan.makefile approach to prevent
        # buffering issues and hanging if the executed command produces a lot
        # of output.
        #
        # Note #2: If you are going to remove "ready" checks inside the loop
        # you are going to have a bad time. Trying to consume from a channel
        # which is not ready will block for indefinitely.
        exit_status_ready = chan.exit_status_ready()

        if exit_status_ready:
            stdout_data = self._consume_stdout(chan=chan,
                                               call_line_handler_func=call_line_handler_func)
            stdout_data = stdout_data.getvalue()

            stderr_data = self._consume_stderr(chan=chan,
                                               call_line_handler_func=call_line_handler_func)
            stderr_data = stderr_data.getvalue()

            stdout.write(stdout_data)
            stderr.write(stderr_data)

        while not exit_status_ready:
            current_time = time.time()
            elapsed_time = (current_time - start_time)

            if timeout and (elapsed_time > timeout):
                # TODO: Is this the right way to clean up?
                chan.close()

                stdout = sanitize_output(stdout.getvalue(), uses_pty=uses_pty)
                stderr = sanitize_output(stderr.getvalue(), uses_pty=uses_pty)
                raise SSHCommandTimeoutError(cmd=cmd, timeout=timeout, stdout=stdout,
                                             stderr=stderr)

            stdout_data = self._consume_stdout(chan=chan,
                                               call_line_handler_func=call_line_handler_func)
            stdout_data = stdout_data.getvalue()

            stderr_data = self._consume_stderr(chan=chan,
                                               call_line_handler_func=call_line_handler_func)
            stderr_data = stderr_data.getvalue()

            stdout.write(stdout_data)
            stderr.write(stderr_data)

            # We need to check the exit status here, because the command could
            # print some output and exit during this sleep below.
            exit_status_ready = chan.exit_status_ready()

            if exit_status_ready:
                break

            # Short sleep to prevent busy waiting
            eventlet.sleep(self.SLEEP_DELAY)
        # print('Wait over. Channel must be ready for host: %s' % self.hostname)

        # Receive the exit status code of the command we ran.
        status = chan.recv_exit_status()

        stdout = sanitize_output(stdout.getvalue(), uses_pty=uses_pty)
        stderr = sanitize_output(stderr.getvalue(), uses_pty=uses_pty)

        extra = {'_status': status, '_stdout': stdout, '_stderr': stderr}
        self.logger.debug('Command finished', extra=extra)

        return [stdout, stderr, status]