Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
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