log = logging.getLogger(__name__)
+
class RemoteProcess(object):
+
"""
Remote process object used to keep track of attributes of a process.
"""
# for orchestra.remote.Remote to place a backreference
'remote',
]
+
def __init__(self, command, stdin, stdout, stderr, exitstatus, exited):
self.command = command
self.stdin = stdin
self.exitstatus = exitstatus
self.exited = exited
+
class Raw(object):
+
"""
Raw objects are passed to remote objects and are not processed locally.
"""
value=self.value,
)
+
def quote(args):
"""
Internal quote wrapper.
)
return r
+
def copy_to_log(f, logger, host, loglevel=logging.INFO):
"""
Interface to older xreadlines api.
line = unicode(line, 'utf-8', 'replace').encode('utf-8')
logger.log(loglevel, '[' + host + ']: ' + line)
+
def copy_and_close(src, fdst):
"""
copyfileobj call wrapper.
shutil.copyfileobj(src, fdst)
fdst.close()
+
def copy_file_to(f, dst, host):
"""
Copy file
class CommandFailedError(Exception):
+
"""
Exception thrown on command failure
"""
self.node = node
def __str__(self):
- return "Command failed on {node} with status {status}: {command!r}".format(
+ return "Command failed on {node} with status {status}: {cmd!r}".format(
node=self.node,
status=self.exitstatus,
- command=self.command,
+ cmd=self.command,
)
class CommandCrashedError(Exception):
+
"""
Exception thrown on crash
"""
class ConnectionLostError(Exception):
+
"""
Exception thrown when the connection is lost
"""
command=self.command,
)
+
def spawn_asyncresult(fn, *args, **kwargs):
"""
Spawn a Greenlet and pass it's results to an AsyncResult.
AsyncResult avoids this.
"""
r = gevent.event.AsyncResult()
+
def wrapper():
"""
Internal wrapper.
return r
+
class Sentinel(object):
+
"""
Sentinel -- used to define PIPE file-like object.
"""
PIPE = Sentinel('PIPE')
+
class KludgeFile(object):
+
"""
Wrap Paramiko's ChannelFile in a way that lets ``f.close()``
actually cause an EOF for the remote command.
check_status=True,
wait=True,
name=None
- ):
+):
"""
Run a command remotely.
:param client: SSHConnection to run the command with
:param args: command to run
:type args: list of string
- :param stdin: Standard input to send; either a string, a file-like object, None, or `PIPE`. `PIPE` means caller is responsible for closing stdin, or command may never exit.
- :param stdout: What to do with standard output. Either a file-like object, a `logging.Logger`, `PIPE`, or `None` for copying to default log. `PIPE` means caller is responsible for reading, or command may never exit.
+ :param stdin: Standard input to send; either a string, a file-like object,
+ None, or `PIPE`. `PIPE` means caller is responsible for
+ closing stdin, or command may never exit.
+ :param stdout: What to do with standard output. Either a file-like object,
+ a `logging.Logger`, `PIPE`, or `None` for copying to default
+ log. `PIPE` means caller is responsible for reading, or
+ command may never exit.
:param stderr: What to do with standard error. See `stdout`.
- :param logger: If logging, write stdout/stderr to "out" and "err" children of this logger. Defaults to logger named after this module.
- :param check_status: Whether to raise CommandFailedError on non-zero exit status, and . Defaults to True. All signals and connection loss are made to look like SIGHUP.
- :param wait: Whether to wait for process to exit. If False, returned ``r.exitstatus`` s a `gevent.event.AsyncResult`, and the actual status is available via ``.get()``.
- :param name: Human readable name (probably hostname) of the destination host
+ :param logger: If logging, write stdout/stderr to "out" and "err" children
+ of this logger. Defaults to logger named after this module.
+ :param check_status: Whether to raise CommandFailedError on non-zero exit
+ status, and . Defaults to True. All signals and
+ connection loss are made to look like SIGHUP.
+ :param wait: Whether to wait for process to exit. If False, returned
+ ``r.exitstatus`` s a `gevent.event.AsyncResult`, and the
+ actual status is available via ``.get()``.
+ :param name: Human readable name (probably hostname) of the destination
+ host
"""
(host, port) = client.get_transport().getpeername()
g_in = gevent.spawn(copy_and_close, stdin, r.stdin)
r.stdin = None
else:
- assert not wait, "Using PIPE for stdin without wait=False would deadlock."
+ assert not wait, \
+ "Using PIPE for stdin without wait=False would deadlock."
if logger is None:
logger = log
g_err = gevent.spawn(copy_file_to, r.stderr, stderr, name)
r.stderr = stderr
else:
- assert not wait, "Using PIPE for stderr without wait=False would deadlock."
+ assert not wait, \
+ "Using PIPE for stderr without wait=False would deadlock."
g_out = None
if stdout is not PIPE:
g_out = gevent.spawn(copy_file_to, r.stdout, stdout, name)
r.stdout = stdout
else:
- assert not wait, "Using PIPE for stdout without wait=False would deadlock."
+ assert not wait, \
+ "Using PIPE for stdout without wait=False would deadlock."
def _check_status(status):
"""
# signal; sadly SSH does not tell us which signal
raise CommandCrashedError(command=r.command)
if status != 0:
- raise CommandFailedError(command=r.command, exitstatus=status, node=name)
+ raise CommandFailedError(
+ command=r.command, exitstatus=status, node=name)
return status
if wait: