def test_trailing_newline_line_drop(): """ Trailing newlines shouldn't cause last line to be dropped. """ # Multiline output with trailing newline output_string = """AUTHORS FAQ Fabric.egg-info INSTALL LICENSE MANIFEST README build docs fabfile.py fabfile.pyc fabric requirements.txt setup.py tests """ # Setup for calling output_thread capture = [] prefix = "[localhost]" # TODO: fix below two lines, duplicates inner workings of output_thread full_prefix = prefix + ": " expected_output = ( full_prefix + ('\n' + full_prefix).join(output_string.splitlines())) + '\n' # Create, tie off thread thread = output_thread(prefix, FakeChannel(output_string), capture=capture) thread.join() # Test equivalence of expected, received output eq_(expected_output, sys.stdout.getvalue())
def test_trailing_newline_line_drop(): """ Trailing newlines shouldn't cause last line to be dropped. """ # Multiline output with trailing newline output_string = """AUTHORS FAQ Fabric.egg-info INSTALL LICENSE MANIFEST README build docs fabfile.py fabfile.pyc fabric requirements.txt setup.py tests """ # Setup for calling output_thread capture = [] prefix = "[localhost]" # TODO: fix below two lines, duplicates inner workings of output_thread full_prefix = prefix + ": " expected_output = (full_prefix + ('\n' + full_prefix).join(output_string.splitlines()) ) + '\n' # Create, tie off thread thread = output_thread(prefix, FakeChannel(output_string), capture=capture) thread.join() # Test equivalence of expected, received output eq_( expected_output, sys.stdout.getvalue() )
def _run_command(command, shell=True, pty=False, sudo=False, user=None): """ Underpinnings of `run` and `sudo`. See their docstrings for more info. """ # Set up new var so original argument can be displayed verbatim later. given_command = command # Handle context manager modifications, and shell wrapping wrapped_command = _shell_wrap( _prefix_commands(_prefix_env_vars(command)), shell, _sudo_prefix(user) if sudo else None ) which = 'sudo' if sudo else 'run' if output.debug: print("[%s] %s: %s" % (env.host_string, which, wrapped_command)) elif output.running: print("[%s] %s: %s" % (env.host_string, which, given_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 or env.always_use_pty: channel.get_pty() channel.exec_command(wrapped_command) capture_stdout = [] capture_stderr = [] out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture_stdout) err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True, capture=capture_stderr) # 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_stdout).strip()) err = _AttributeString("".join(capture_stderr).strip()) # Error handling out.failed = False if status != 0: out.failed = True msg = "%s() encountered an error (return code %s) while executing '%s'" % (which, 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 # Convenience mirror of .failed out.succeeded = not out.failed # Attach stderr for anyone interested in that. out.stderr = err return out
def _run_command(command, shell=True, pty=False, sudo=False, user=None): """ Underpinnings of `run` and `sudo`. See their docstrings for more info. """ # Set up new var so original argument can be displayed verbatim later. given_command = command # Handle context manager modifications, and shell wrapping wrapped_command = _shell_wrap(_prefix_commands(_prefix_env_vars(command)), shell, _sudo_prefix(user) if sudo else None) which = 'sudo' if sudo else 'run' if output.debug: print("[%s] %s: %s" % (env.host_string, which, wrapped_command)) elif output.running: print("[%s] %s: %s" % (env.host_string, which, given_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 or env.always_use_pty: channel.get_pty() channel.exec_command(wrapped_command) capture_stdout = [] capture_stderr = [] out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture_stdout) err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True, capture=capture_stderr) # 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_stdout).strip()) err = _AttributeString("".join(capture_stderr).strip()) # Error handling out.failed = False if status != 0: out.failed = True msg = "%s() encountered an error (return code %s) while executing '%s'" % ( which, 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 # Convenience mirror of .failed out.succeeded = not out.failed # Attach stderr for anyone interested in that. out.stderr = err return out
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. Like `run`, this behavior may be disabled by specifying ``shell=False``. 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. Standard error will also be attached, as a string, to this return value as the ``stderr`` 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: cwd = 'cd \"%s\" && ' % _shell_escape(cwd) real_command = '%s %s "%s"' % (sudo_prefix, env.shell, _shell_escape(cwd + command)) 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_stdout = [] capture_stderr = [] out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture_stdout) err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True, capture=capture_stderr) # 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_stdout).strip()) err = _AttributeString("".join(capture_stderr).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 # Attach stderr for those who need it. out.stderr = err return out
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. Standard error will also be attached, as a string, to this return value as the ``stderr`` 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: cwd = 'cd \"%s\" && ' % _shell_escape(cwd) # Construct final real, full command real_command = '%s "%s"' % (env.shell, _shell_escape(cwd + real_command)) 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_stdout = [] capture_stderr = [] out_thread = output_thread("[%s] out" % env.host_string, channel, capture=capture_stdout) err_thread = output_thread("[%s] err" % env.host_string, channel, stderr=True, capture=capture_stderr) # 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_stdout).strip()) err = _AttributeString("".join(capture_stderr).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 # Attach stderr for anyone interested in that. out.stderr = err return out