import subprocess
+import logging
import sys
import os
-import Queue
-import threading
import wizard as _wizard
from wizard import util
+"""This is the path to the wizard executable as specified
+by the caller; it lets us recursively invoke wizard"""
wizard = sys.argv[0]
-class CallError(_wizard.Error):
- def __init__(self, code, args, stdout, stderr):
- self.code = code
- self.args = args
- self.stdout = stdout
- self.stderr = stderr
- def __str__(self):
- return "CallError [%d]" % self.code
-
-class PythonCallError(CallError):
- def __init__(self, code, args, stdout, stderr):
- self.name = util.get_exception_name(stderr)
- CallError.__init__(self, code, args, stdout, stderr)
- def __str__(self):
- return "PythonCallError [%s]" % self.name
-
def is_python(args):
return args[0] == "python" or args[0] == wizard
class Shell(object):
"""An advanced shell, with the ability to do dry-run and log commands"""
- def __init__(self, logger = False, dry = False):
- """ `logger` The logger
- `dry` Don't run any commands, just print them"""
- self.logger = logger
+ def __init__(self, dry = False):
+ """ `dry` Don't run any commands, just print them"""
self.dry = dry
def call(self, *args, **kwargs):
kwargs.setdefault("python", None)
- if self.dry or self.logger:
- self.logger.info("Running `" + ' '.join(args) + "`")
+ logging.info("Running `" + ' '.join(args) + "`")
if self.dry:
return
if kwargs["python"] is None and is_python(args):
raise eclass(proc.returncode, args, stdout, stderr)
return (stdout, stderr)
def log(self, stdout, stderr):
- if self.logger and stdout:
- self.logger.debug("STDOUT: " + stdout)
- if self.logger and stderr:
- self.logger.debug("STDERR: " + stderr)
+ if stdout:
+ logging.debug("STDOUT:\n" + stdout)
+ if stderr:
+ logging.debug("STDERR:\n" + stderr)
def callAsUser(self, *args, **kwargs):
user = kwargs.pop("user", None)
+ uid = kwargs.pop("uid", None)
kwargs.setdefault("python", is_python(args))
- if not user: return self.call(*args, **kwargs)
- return self.call("sudo", "-u", user, *args, **kwargs)
+ if not user and not uid: return self.call(*args, **kwargs)
+ if util.get_operator_name():
+ # This might be generalized as "preserve some environment"
+ args.insert(0, "SSH_GSSAPI_NAME=" + util.get_operator_name())
+ if uid: return self.call("sudo", "-u", "#" + str(uid), *args, **kwargs)
+ if user: return self.call("sudo", "-u", user, *args, **kwargs)
class ParallelShell(Shell):
"""Commands are queued here, and executed in parallel (with
threading) in accordance with the maximum number of allowed
subprocesses, and result in callback execution when they finish."""
- def __init__(self, logger = False, dry = False, max = 10):
- super(ParallelShell, self).__init__(logger=logger,dry=dry)
+ def __init__(self, dry = False, max = 10):
+ super(ParallelShell, self).__init__(dry=dry)
self.running = {}
self.max = max # maximum of commands to run in parallel
def async(self, proc, args, python, on_success, on_error):
class DummyParallelShell(ParallelShell):
"""Same API as ParallelShell, but doesn't actually parallelize (by
using only one thread)"""
- def __init__(self, logger = False, dry = False):
- super(DummyParallelShell, self).__init__(logger, dry, max=1)
+ def __init__(self, dry = False):
+ super(DummyParallelShell, self).__init__(dry=dry, max=1)
+
+class CallError(_wizard.Error):
+ def __init__(self, code, args, stdout, stderr):
+ self.code = code
+ self.args = args
+ self.stdout = stdout
+ self.stderr = stderr
+ def __str__(self):
+ return "CallError [%d]" % self.code
+
+class PythonCallError(CallError):
+ def __init__(self, code, args, stdout, stderr):
+ self.name = util.get_exception_name(stderr)
+ CallError.__init__(self, code, args, stdout, stderr)
+ def __str__(self):
+ return "PythonCallError [%s]" % self.name