Beispiel #1
0
def sudo(command, shell=True, user=None, pty=False):
    """
    Run a shell command on a remote host, with superuser privileges.
    
    As with ``run()``, ``sudo()`` executes within a shell command defaulting to
    the value of ``env.shell``, although it goes one step further and wraps the
    command with ``sudo`` as well. Also similar to ``run()``, the shell

    You may specify a ``user`` keyword argument, which is passed to ``sudo``
    and allows you to run as some user other than root (which is the default).
    On most systems, the ``sudo`` program can take a string username or an
    integer userid (uid); ``user`` may likewise be a string or an int.

    Some remote systems may be configured to disallow sudo access unless a
    terminal or pseudoterminal is being used (e.g. when ``Defaults
    requiretty`` exists in ``/etc/sudoers``.) If updating the remote system's
    ``sudoers`` configuration is not possible or desired, you may pass
    ``pty=True`` to `sudo` to force allocation of a pseudo tty on the remote
    end.
       
    `sudo` will return the result of the remote program's stdout as a
    single (likely multiline) string. This string will exhibit a ``failed``
    boolean attribute specifying whether the command failed or succeeded, and
    will also include the return code as the ``return_code`` attribute.

    Examples::
    
        sudo("~/install_script.py")
        sudo("mkdir /var/www/new_docroot", user="******")
        sudo("ls /home/jdoe", user=1001)
        result = sudo("ls /tmp/")
    
    """
    # Construct sudo command, with user if necessary
    if user is not None:
        if str(user).isdigit():
            user = "******" % user
        sudo_prefix = "sudo -S -p '%%s' -u \"%s\" " % user
    else:
        sudo_prefix = "sudo -S -p '%s' "
    # Put in explicit sudo prompt string (so we know what to look for when
    # detecting prompts)
    sudo_prefix = sudo_prefix % env.sudo_prompt
    # Without using a shell, we just do 'sudo -u blah my_command'
    if (not env.use_shell) or (not shell):
        real_command = "%s %s" % (sudo_prefix, _shell_escape(command))
    # With a shell, we do 'sudo -u blah /bin/bash -l -c "my_command"'
    else:
        # With a shell, we can also honor cwd
        cwd = env.get('cwd', '')
        if cwd:
            # TODO: see if there is any nice way to quote this, given that it
            # ends up inside double quotes down below...
            cwd = 'cd %s && ' % _shell_escape(cwd)
        real_command = '%s %s "%s"' % (sudo_prefix, env.shell,
            _shell_escape(cwd + command))
    # TODO: handle confirm_proceed behavior, as in run()
    if output.debug:
        print("[%s] sudo: %s" % (env.host_string, real_command))
    elif output.running:
        print("[%s] sudo: %s" % (env.host_string, command))
    channel = connections[env.host_string]._transport.open_session()
    # Create pty if necessary (using Paramiko default options, which as of
    # 1.7.4 is vt100 $TERM @ 80x24 characters)
    if pty:
        channel.get_pty()
    # Execute
    channel.exec_command(real_command)
    capture = []

    out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture)
    err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True)

    # Close channel when done
    status = channel.recv_exit_status()

    # Wait for threads to exit before returning (otherwise we will occasionally
    # end up returning before the threads have fully wrapped up)
    out_thread.join()
    err_thread.join()

    # Close channel
    channel.close()

    # Assemble stdout string
    out = _AttributeString("".join(capture).strip())

    # Error handling
    out.failed = False
    if status != 0:
        out.failed = True
        msg = "sudo() encountered an error (return code %s) while executing '%s'" % (status, command)
        _handle_failure(message=msg)

    # Attach return code for convenience
    out.return_code = status
    return out
Beispiel #2
0
def sudo(command, shell=True, user=None):
    """
    Run a shell command on a remote host, with superuser privileges.
    
    As with ``run()``, ``sudo()`` executes within a shell command defaulting to
    the value of ``env.shell``, although it goes one step further and wraps the
    command with ``sudo`` as well. Also similar to ``run()``, the shell

    You may specify a ``user`` keyword argument, which is passed to ``sudo``
    and allows you to run as some user other than root (which is the default).
    On most systems, the ``sudo`` program can take a string username or an
    integer userid (uid); ``user`` may likewise be a string or an int.
       
    `sudo` will return the result of the remote program's stdout as a
    single (likely multiline) string. This string will exhibit a ``failed``
    boolean attribute specifying whether the command failed or succeeded, and
    will also include the return code as the ``return_code`` attribute.

    Examples::
    
        sudo("~/install_script.py")
        sudo("mkdir /var/www/new_docroot", user="******")
        sudo("ls /home/jdoe", user=1001)
        result = sudo("ls /tmp/")
    
    """
    # Construct sudo command, with user if necessary
    if user is not None:
        if str(user).isint():
            user = "******" % user
        sudo_prefix = "sudo -S -p '%%s' -u \"%s\" " % user
    else:
        sudo_prefix = "sudo -S -p '%s' "
    # Put in explicit sudo prompt string (so we know what to look for when
    # detecting prompts)
    sudo_prefix = sudo_prefix % env.sudo_prompt
    # Without using a shell, we just do 'sudo -u blah my_command'
    if (not env.use_shell) or (not shell):
        real_command = "%s %s" % (sudo_prefix, _shell_escape(command))
    # With a shell, we do 'sudo -u blah /bin/bash -l -c "my_command"'
    else:
        real_command = '%s %s "%s"' % (sudo_prefix, env.shell, _shell_escape(command))
    # TODO: tie this into global output controls; both in terms of showing the
    # shell itself, AND showing the sudo prefix. Not 100% sure it's worth being
    # so granular as to allow one on and one off, but think about it.
    # TODO: handle confirm_proceed behavior, as in run()
    if env.debug:
        print("[%s] sudo: %s" % (env.host_string, real_command))
    else:
        print("[%s] sudo: %s" % (env.host_string, command))
    channel = connections[env.host_string]._transport.open_session()
    channel.exec_command(real_command)
    capture = []

    out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture)
    err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True)

    # Close channel when done
    status = channel.recv_exit_status()
    channel.close()

    # Wait for threads to exit before returning (otherwise we will occasionally
    # end up returning before the threads have fully wrapped up)
    out_thread.join()
    err_thread.join()

    # Assemble stdout string
    output = _AttributeString("".join(capture).strip())

    # Error handling
    output.failed = False
    if status != 0:
        output.failed = True
        msg = "sudo() encountered an error (return code %s) while executing '%s'" % (status, command)
        _handle_failure(message=msg)

    # Attach return code for convenience
    output.return_code = status
    return output
Beispiel #3
0
def run(command, shell=True, pty=False):
    """
    Run a shell command on a remote host.

    If ``shell`` is True (the default), ``run()`` will execute the given
    command string via a shell interpreter, the value of which may be
    controlled by setting ``env.shell`` (defaulting to something similar to
    ``/bin/bash -l -c "<command>"``.) Any double-quote (``"``) characters in
    ``command`` will be automatically escaped when ``shell`` is True.

    `run` will return the result of the remote program's stdout as a
    single (likely multiline) string. This string will exhibit a ``failed``
    boolean attribute specifying whether the command failed or succeeded, and
    will also include the return code as the ``return_code`` attribute.

    You may pass ``pty=True`` to force allocation of a pseudo tty on
    the remote end. This is not normally required, but some programs may
    complain (or, even more rarely, refuse to run) if a tty is not present.

    Examples::
    
        run("ls /var/www/")
        run("ls /home/myuser", shell=False)
        output = run('ls /var/www/site1')
    
    """
    # Set up new var so original argument can be displayed verbatim later.
    real_command = command
    if shell:
        # Handle cwd munging via 'cd' context manager
        cwd = env.get('cwd', '')
        if cwd:
            # TODO: see if there is any nice way to quote this, given that it
            # ends up inside double quotes down below...
            cwd = 'cd %s && ' % _shell_escape(cwd)
        # Construct final real, full command
        real_command = '%s "%s"' % (env.shell,
            _shell_escape(cwd + real_command))
    # TODO: possibly put back in previously undocumented 'confirm_proceed'
    # functionality, i.e. users may set an option to be prompted before each
    # execution. Pretty sure this should be a global option applying to ALL
    # remote operations! And, of course -- documented.
    if output.debug:
        print("[%s] run: %s" % (env.host_string, real_command))
    elif output.running:
        print("[%s] run: %s" % (env.host_string, command))
    channel = connections[env.host_string]._transport.open_session()
    # Create pty if necessary (using Paramiko default options, which as of
    # 1.7.4 is vt100 $TERM @ 80x24 characters)
    if pty:
        channel.get_pty()
    channel.exec_command(real_command)
    capture = []

    out_thread = output_thread("[%s] out" % env.host_string, channel,
        capture=capture)
    err_thread = output_thread("[%s] err" % env.host_string, channel,
        stderr=True)
    
    # Close when done
    status = channel.recv_exit_status()
    
    # Wait for threads to exit so we aren't left with stale threads
    out_thread.join()
    err_thread.join()

    # Close channel
    channel.close()

    # Assemble output string
    out = _AttributeString("".join(capture).strip())

    # Error handling
    out.failed = False
    if status != 0:
        out.failed = True
        msg = "run() encountered an error (return code %s) while executing '%s'" % (status, command)
        _handle_failure(message=msg)

    # Attach return code to output string so users who have set things to warn
    # only, can inspect the error code.
    out.return_code = status
    return out
Beispiel #4
0
def run(command, shell=True):
    """
    Run a shell command on a remote host.

    If ``shell`` is True (the default), ``run()`` will execute the given
    command string via a shell interpreter, the value of which may be
    controlled by setting ``env.shell`` (defaulting to something similar to
    ``/bin/bash -l -c "<command>"``.) Any double-quote (``"``) characters in
    ``command`` will be automatically escaped when ``shell`` is True.

    `run` will return the result of the remote program's stdout as a
    single (likely multiline) string. This string will exhibit a ``failed``
    boolean attribute specifying whether the command failed or succeeded, and
    will also include the return code as the ``return_code`` attribute.

    Examples::
    
        run("ls /var/www/")
        run("ls /home/myuser", shell=False)
        output = run('ls /var/www/site1')
    
    """
    real_command = command
    if shell:
        real_command = '%s "%s"' % (env.shell, _shell_escape(command))
    # TODO: possibly put back in previously undocumented 'confirm_proceed'
    # functionality, i.e. users may set an option to be prompted before each
    # execution. Pretty sure this should be a global option applying to ALL
    # remote operations! And, of course -- documented.
    # TODO: tie this into global output controls
    if env.debug:
        print("[%s] run: %s" % (env.host_string, real_command))
    else:
        print("[%s] run: %s" % (env.host_string, command))
    channel = connections[env.host_string]._transport.open_session()
    channel.exec_command(real_command)
    capture = []

    # TODO: tie into global output controls
    out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture)
    err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True)

    # Close when done
    status = channel.recv_exit_status()
    channel.close()

    # Wait for threads to exit so we aren't left with stale threads
    out_thread.join()
    err_thread.join()

    # Assemble output string
    output = _AttributeString("".join(capture).strip())

    # Error handling
    output.failed = False
    if status != 0:
        output.failed = True
        msg = "run() encountered an error (return code %s) while executing '%s'" % (status, command)
        _handle_failure(message=msg)

    # Attach return code to output string so users who have set things to warn
    # only, can inspect the error code.
    output.return_code = status
    return output