def read_buffers_into_queue( stdout_buffer, stderr_buffer, timeout, print_output, print_prefix, ): output_queue = Queue() # Iterate through outputs to get an exit status and generate desired list # output, done in two greenlets so stdout isn't printed before stderr. Not # attached to state.pool to avoid blocking it with 2x n-hosts greenlets. stdout_reader = gevent.spawn( read_buffer, 'stdout', stdout_buffer, output_queue, print_output=print_output, print_func=lambda line: '{0}{1}'.format(print_prefix, line), ) stderr_reader = gevent.spawn( read_buffer, 'stderr', stderr_buffer, output_queue, print_output=print_output, print_func=lambda line: '{0}{1}'.format( print_prefix, click.style(line, 'red'), ), ) # Wait on output, with our timeout (or None) greenlets = gevent.wait((stdout_reader, stderr_reader), timeout=timeout) # Timeout doesn't raise an exception, but gevent.wait returns the greenlets # which did complete. So if both haven't completed, we kill them and fail # with a timeout. if len(greenlets) != 2: stdout_reader.kill() stderr_reader.kill() raise timeout_error() return list(output_queue.queue)
def run_shell_command( state, host, command, sudo=False, sudo_user=None, su_user=None, preserve_sudo_env=False, get_pty=False, env=None, timeout=None, print_output=False, ): ''' Execute a command on the specified host. Args: state (``pyinfra.api.State`` obj): state object for this command hostname (string): hostname of the target command (string): actual command to execute sudo (boolean): whether to wrap the command with sudo sudo_user (string): user to sudo to get_pty (boolean): whether to get a PTY before executing the command env (dict): envrionment variables to set timeout (int): timeout for this command to complete before erroring Returns: tuple: (exit_code, stdout, stderr) stdout and stderr are both lists of strings from each buffer. ''' command = make_command( command, env=env, sudo=sudo, sudo_user=sudo_user, su_user=su_user, preserve_sudo_env=preserve_sudo_env, ) logger.debug('Running command on {0}: {1}'.format(host.name, command)) if print_output: print('{0}>>> {1}'.format(host.print_prefix, command)) # Run it! Get stdout, stderr & the underlying channel _, stdout_buffer, stderr_buffer = host.connection.exec_command( command, get_pty=get_pty, ) channel = stdout_buffer.channel # Iterate through outputs to get an exit status and generate desired list # output, done in two greenlets so stdout isn't printed before stderr. Not # attached to state.pool to avoid blocking it with 2x n-hosts greenlets. stdout_reader = gevent.spawn( read_buffer, stdout_buffer, print_output=print_output, print_func=lambda line: '{0}{1}'.format(host.print_prefix, line), ) stderr_reader = gevent.spawn( read_buffer, stderr_buffer, print_output=print_output, print_func=lambda line: '{0}{1}'.format( host.print_prefix, click.style(line, 'red'), ), ) # Wait on output, with our timeout (or None) greenlets = gevent.wait((stdout_reader, stderr_reader), timeout=timeout) # Timeout doesn't raise an exception, but gevent.wait returns the greenlets # which did complete. So if both haven't completed, we kill them and fail # with a timeout. if len(greenlets) != 2: stdout_reader.kill() stderr_reader.kill() raise timeout_error() # Read the buffers into a list of lines stdout = stdout_reader.get() stderr = stderr_reader.get() logger.debug('Waiting for exit status...') exit_status = channel.recv_exit_status() logger.debug('Command exit status: {0}'.format(exit_status)) return exit_status == 0, stdout, stderr
def run_shell_command( state, hostname, command, sudo=False, sudo_user=None, su_user=None, env=None, timeout=None, print_output=False ): ''' Execute a command on the specified host. Args: state (``pyinfra.api.State`` obj): state object for this command hostname (string): hostname of the target command (string): actual command to execute sudo (boolean): whether to wrap the command with sudo sudo_user (string): user to sudo to env (dict): envrionment variables to set timeout (int): timeout for this command to complete before erroring Returns: tuple: (channel, stdout, stderr) Channel is a Paramiko channel object, mainly used for it's ``.exit_code`` attribute. stdout and stderr are both lists of strings from each buffer. ''' print_prefix = '[{0}] '.format(colored(hostname, attrs=['bold'])) if env is None: env = {} logger.debug('Building command on {0}: {1}'.format(hostname, command)) debug_meta = {} for key, value in six.iteritems({ 'sudo': sudo, 'sudo_user': sudo_user, 'su_user': su_user, 'env': env }): if value: debug_meta[key] = value logger.debug('Command meta ({0})'.format(' '.join( '{0}: {1}'.format(key, value) for key, value in six.iteritems(debug_meta) ))) command = make_command(command, env=env, sudo=sudo, sudo_user=sudo_user, su_user=su_user) logger.debug('--> Running command on {0}: {1}'.format(hostname, command)) if print_output: print('{0}>>> {1}'.format(print_prefix, command)) # Get the connection for this hostname connection = state.ssh_connections[hostname] # Run it! Get stdout, stderr & the underlying channel _, stdout_buffer, stderr_buffer = connection.exec_command(command) channel = stdout_buffer.channel # Iterate through outputs to get an exit status and generate desired list output, # done in two greenlets so stdout isn't printed before stderr. Not attached to # state.pool to avoid blocking it with 2x n-hosts greenlets. stdout_reader = gevent.spawn( read_buffer, stdout_buffer, print_output=print_output, print_func=lambda line: '{0}{1}'.format(print_prefix, line) ) stderr_reader = gevent.spawn( read_buffer, stderr_buffer, print_output=print_output, print_func=lambda line: '{0}{1}'.format(print_prefix, colored(line, 'red')) ) # Wait on output, with our timeout (or None) greenlets = gevent.wait((stdout_reader, stderr_reader), timeout=timeout) # Timeout doesn't raise an exception, but gevent.wait returns the greenlets which did # complete. So if both haven't completed, we kill them and fail with a timeout. if len(greenlets) != 2: stdout_reader.kill() stderr_reader.kill() raise timeout_error() # Read the buffers into a list of lines stdout = stdout_reader.get() stderr = stderr_reader.get() # Wait until the exit code is ready. We have to sleep between checks to allow gevent # to jump to the paramiko networking stack to receive the final exit code. Normally # this is ready after reading out the buffers, but it's possible the exit code didn't # make it into the final read. ready = channel.exit_status_ready() while ready is False: logger.debug('--> Waiting for exit status...') sleep(0.001) ready = channel.exit_status_ready() logger.debug('--> Command exit status: {0}'.format(channel.exit_status)) return channel, stdout, stderr
def run_shell_command(state, hostname, command, sudo=False, sudo_user=None, env=None, timeout=None, print_output=False): ''' Execute a command on the specified host. Args: state (``pyinfra.api.State`` obj): state object for this command hostname (string): hostname of the target command (string): actual command to execute sudo (boolean): whether to wrap the command with sudo sudo_user (string): user to sudo to env (dict): envrionment variables to set timeout (int): timeout for this command to complete before erroring Returns: tuple: (channel, stdout, stderr) Channel is a Paramiko channel object, mainly used for it's ``.exit_code`` attribute. stdout and stderr are both lists of strings from each buffer. ''' print_prefix = '[{0}] '.format(colored(hostname, attrs=['bold'])) if env is None: env = {} logger.debug('Running command on {0}: "{1}"'.format(hostname, command)) logger.debug('Command sudo?: {0}, sudo user: {1}, env: {2}'.format( sudo, sudo_user, env)) command = make_command(command, env=env, sudo=sudo, sudo_user=sudo_user) if print_output: print('{0}>>> {1}'.format(print_prefix, command)) # Get the connection for this hostname connection = state.ssh_connections[hostname] # Run it! Get stdout, stderr & the underlying channel _, stdout_buffer, stderr_buffer = connection.exec_command(command) channel = stdout_buffer.channel # Iterate through outputs to get an exit status and generate desired list output, # done in two greenlets so stdout isn't printed before stderr. Not attached to # state.pool to avoid blocking it with 2x n-hosts greenlets. stdout_reader = gevent.spawn( read_buffer, stdout_buffer, print_output=print_output, print_func=lambda line: '{0}{1}'.format(print_prefix, line)) stderr_reader = gevent.spawn(read_buffer, stderr_buffer, print_output=print_output, print_func=lambda line: '{0}{1}'.format( print_prefix, colored(line, 'red'))) # Wait on output, with our timeout (or None) greenlets = gevent.wait((stdout_reader, stderr_reader), timeout=timeout) # Timeout doesn't raise an exception, but gevent.wait returns the greenlets which did # complete. So if both haven't completed, we kill them and fail with a timeout. if len(greenlets) != 2: stdout_reader.kill() stderr_reader.kill() raise timeout_error() stdout = stdout_reader.get() stderr = stderr_reader.get() return channel, stdout, stderr
def run_shell_command(state, host, command, get_pty=False, timeout=None, print_output=False, **command_kwargs): ''' Execute a command on the local machine. Args: state (``pyinfra.api.State`` obj): state object for this command hostname (string): hostname of the target command (string): actual command to execute sudo (boolean): whether to wrap the command with sudo sudo_user (string): user to sudo to get_pty (boolean): whether to get a PTY before executing the command env (dict): envrionment variables to set timeout (int): timeout for this command to complete before erroring Returns: tuple: (exit_code, stdout, stderr) stdout and stderr are both lists of strings from each buffer. ''' command = make_command(command, **command_kwargs) logger.debug('--> Running command on localhost: {0}'.format(command)) if print_output: print('{0}>>> {1}'.format(host.print_prefix, command)) process = Popen(command, shell=True, stdout=PIPE, stderr=PIPE) # Iterate through outputs to get an exit status and generate desired list # output, done in two greenlets so stdout isn't printed before stderr. Not # attached to state.pool to avoid blocking it with 2x n-hosts greenlets. stdout_reader = gevent.spawn( read_buffer, process.stdout, print_output=print_output, print_func=lambda line: '{0}{1}'.format(host.print_prefix, line), ) stderr_reader = gevent.spawn( read_buffer, process.stderr, print_output=print_output, print_func=lambda line: '{0}{1}'.format( host.print_prefix, click.style(line, 'red'), ), ) # Wait on output, with our timeout (or None) greenlets = gevent.wait((stdout_reader, stderr_reader), timeout=timeout) # Timeout doesn't raise an exception, but gevent.wait returns the greenlets # which did complete. So if both haven't completed, we kill them and fail # with a timeout. if len(greenlets) != 2: stdout_reader.kill() stderr_reader.kill() raise timeout_error() # Read the buffers into a list of lines stdout = stdout_reader.get() stderr = stderr_reader.get() logger.debug('--> Waiting for exit status...') process.wait() # Close any open file descriptor process.stdout.close() logger.debug('--> Command exit status: {0}'.format(process.returncode)) return process.returncode == 0, stdout, stderr