def copy_file_to_host(file_from, dest, host, username, pkey): dest = "%s@%s:%s" % (username, host, dest) cmd = "scp -v -o UserKnownHostsFile=/dev/null " \ "-o StrictHostKeyChecking=no " \ "-i %(pkey)s %(file1)s %(dest)s" % {'pkey': pkey, 'file1': file_from, 'dest': dest} args = shlex.split(cmd.encode('utf-8')) subprocess_args = {'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT} proc = subprocess.Popen(args, **subprocess_args) stdout, stderr = proc.communicate() if proc.returncode != 0: raise exceptions.SSHExecCommandFailed(cmd, proc.returncode, stdout, stderr) return stdout
def exec_command(self, cmd, encoding="utf-8"): """Execute the specified command on the server Note that this method is reading whole command outputs to memory, thus shouldn't be used for large outputs. :param str cmd: Command to run at remote server. :param str encoding: Encoding for result from paramiko. Result will not be decoded if None. :returns: data read from standard output of the command. :raises: SSHExecCommandFailed if command returns nonzero status. The exception contains command status stderr content. :raises: TimeoutException if cmd doesn't end when timeout expires. """ ssh = self._get_ssh_connection() transport = ssh.get_transport() with transport.open_session() as channel: channel.fileno() # Register event pipe channel.exec_command(cmd) channel.shutdown_write() # If the executing host is linux-based, poll the channel if self._can_system_poll(): out_data_chunks = [] err_data_chunks = [] poll = select.poll() poll.register(channel, select.POLLIN) start_time = time.time() while True: ready = poll.poll(self.channel_timeout) if not any(ready): if not self._is_timed_out(start_time): continue raise exceptions.TimeoutException( "Command: '{0}' executed on host '{1}'.".format( cmd, self.host)) if not ready[0]: # If there is nothing to read. continue out_chunk = err_chunk = None if channel.recv_ready(): out_chunk = channel.recv(self.buf_size) out_data_chunks += out_chunk, if channel.recv_stderr_ready(): err_chunk = channel.recv_stderr(self.buf_size) err_data_chunks += err_chunk, if not err_chunk and not out_chunk: break out_data = b''.join(out_data_chunks) err_data = b''.join(err_data_chunks) # Just read from the channels else: out_file = channel.makefile('rb', self.buf_size) err_file = channel.makefile_stderr('rb', self.buf_size) out_data = out_file.read() err_data = err_file.read() if encoding: out_data = out_data.decode(encoding) err_data = err_data.decode(encoding) exit_status = channel.recv_exit_status() ssh.close() if 0 != exit_status: raise exceptions.SSHExecCommandFailed(command=cmd, exit_status=exit_status, stderr=err_data, stdout=out_data) return out_data
def execute_script(self, script, become_root=False, combine_stderr=True, shell='sh -eux'): """Connect to remote machine and executes script. Implementation note: it passes script lines to shell interpreter via STDIN. Therefore script line number could be not available to some script interpreters for debugging porposes. :param script: script lines to be executed. :param become_root: executes interpreter as root with sudo. :param combine_stderr (bool): whenever to redirect STDERR to STDOUT so that output from both streams are returned together. True by default. :param shell: command line used to launch script interpreter. By default it executes Bash with -eux options enabled. This means that any command returning non-zero exist status or any any undefined variable would interrupt script execution with an error and every command executed by the script is going to be traced to STDERR. :returns output written by script to STDOUT. :raises tempest.lib.exceptions.SSHTimeout: in case it fails to connect to remote server or it fails to open a channel. :raises tempest.lib.exceptions.SSHExecCommandFailed: in case command script exits with non zero exit status. """ channel = self.open_session() with channel: # Combine STOUT and STDERR to have to handle with only one stream channel.set_combine_stderr(combine_stderr) # Set default environment channel.update_environment({ # Language and encoding 'LC_ALL': os.environ.get('LC_ALL') or self.default_ssh_lang, 'LANG': os.environ.get('LANG') or self.default_ssh_lang }) if become_root: shell = 'sudo ' + shell # Spawn a Bash channel.exec_command(shell) lines_iterator = iter(script.splitlines()) output_data = b'' error_data = b'' while not channel.exit_status_ready(): # Drain incoming data buffers while channel.recv_ready(): output_data += channel.recv(self.buf_size) while channel.recv_stderr_ready(): error_data += channel.recv_stderr(self.buf_size) if channel.send_ready(): try: line = next(lines_iterator) except StopIteration: # Finalize Bash script execution channel.shutdown_write() else: # Send script to Bash STDIN line by line channel.send((line + '\n').encode('utf-8')) else: time.sleep(.1) # Get exit status and drain incoming data buffers exit_status = channel.recv_exit_status() while channel.recv_ready(): output_data += channel.recv(self.buf_size) while channel.recv_stderr_ready(): error_data += channel.recv_stderr(self.buf_size) if exit_status != 0: raise exceptions.SSHExecCommandFailed( command='bash', exit_status=exit_status, stderr=error_data.decode('utf-8'), stdout=output_data.decode('utf-8')) return output_data.decode('utf-8')