diff options
Diffstat (limited to 'openstack')
-rw-r--r-- | openstack/common/lockutils.py | 29 | ||||
-rw-r--r-- | openstack/common/processutils.py | 66 | ||||
-rw-r--r-- | openstack/common/rootwrap/filters.py | 20 | ||||
-rw-r--r-- | openstack/common/rootwrap/wrapper.py | 8 | ||||
-rw-r--r-- | openstack/common/rpc/impl_qpid.py | 2 | ||||
-rw-r--r-- | openstack/common/strutils.py | 15 |
6 files changed, 118 insertions, 22 deletions
diff --git a/openstack/common/lockutils.py b/openstack/common/lockutils.py index 892d821..79d1905 100644 --- a/openstack/common/lockutils.py +++ b/openstack/common/lockutils.py @@ -49,6 +49,10 @@ CONF = cfg.CONF CONF.register_opts(util_opts) +def set_defaults(lock_path): + cfg.set_defaults(util_opts, lock_path=lock_path) + + class _InterProcessLock(object): """Lock implementation which allows multiple locks, working around issues like bugs.debian.org/cgi-bin/bugreport.cgi?bug=632857 and does @@ -247,3 +251,28 @@ def synchronized(name, lock_file_prefix, external=False, lock_path=None): return retval return inner return wrap + + +def synchronized_with_prefix(lock_file_prefix): + """Partial object generator for the synchronization decorator. + + Redefine @synchronized in each project like so:: + + (in nova/utils.py) + from nova.openstack.common import lockutils + + synchronized = lockutils.synchronized_with_prefix('nova-') + + + (in nova/foo.py) + from nova import utils + + @utils.synchronized('mylock') + def bar(self, *args): + ... + + The lock_file_prefix argument is used to provide lock files on disk with a + meaningful prefix. The prefix should end with a hyphen ('-') if specified. + """ + + return functools.partial(synchronized, lock_file_prefix=lock_file_prefix) diff --git a/openstack/common/processutils.py b/openstack/common/processutils.py index 560055e..09baea3 100644 --- a/openstack/common/processutils.py +++ b/openstack/common/processutils.py @@ -34,6 +34,11 @@ from openstack.common import log as logging LOG = logging.getLogger(__name__) +class InvalidArgumentError(Exception): + def __init__(self, message=None): + super(InvalidArgumentError, self).__init__(message) + + class UnknownArgumentError(Exception): def __init__(self, message=None): super(UnknownArgumentError, self).__init__(message) @@ -179,3 +184,64 @@ def execute(*cmd, **kwargs): # call clean something up in between calls, without # it two execute calls in a row hangs the second one greenthread.sleep(0) + + +def trycmd(*args, **kwargs): + """ + A wrapper around execute() to more easily handle warnings and errors. + + Returns an (out, err) tuple of strings containing the output of + the command's stdout and stderr. If 'err' is not empty then the + command can be considered to have failed. + + :discard_warnings True | False. Defaults to False. If set to True, + then for succeeding commands, stderr is cleared + + """ + discard_warnings = kwargs.pop('discard_warnings', False) + + try: + out, err = execute(*args, **kwargs) + failed = False + except ProcessExecutionError, exn: + out, err = '', str(exn) + failed = True + + if not failed and discard_warnings and err: + # Handle commands that output to stderr but otherwise succeed + err = '' + + return out, err + + +def ssh_execute(ssh, cmd, process_input=None, + addl_env=None, check_exit_code=True): + LOG.debug(_('Running cmd (SSH): %s'), cmd) + if addl_env: + raise InvalidArgumentError(_('Environment not supported over SSH')) + + if process_input: + # This is (probably) fixable if we need it... + raise InvalidArgumentError(_('process_input not supported over SSH')) + + stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd) + channel = stdout_stream.channel + + # NOTE(justinsb): This seems suspicious... + # ...other SSH clients have buffering issues with this approach + stdout = stdout_stream.read() + stderr = stderr_stream.read() + stdin_stream.close() + + exit_status = channel.recv_exit_status() + + # exit_status == -1 if no exit code was returned + if exit_status != -1: + LOG.debug(_('Result was %s') % exit_status) + if check_exit_code and exit_status != 0: + raise ProcessExecutionError(exit_code=exit_status, + stdout=stdout, + stderr=stderr, + cmd=cmd) + + return (stdout, stderr) diff --git a/openstack/common/rootwrap/filters.py b/openstack/common/rootwrap/filters.py index d9618af..58121cb 100644 --- a/openstack/common/rootwrap/filters.py +++ b/openstack/common/rootwrap/filters.py @@ -20,7 +20,7 @@ import re class CommandFilter(object): - """Command filter only checking that the 1st argument matches exec_path""" + """Command filter only checking that the 1st argument matches exec_path.""" def __init__(self, exec_path, run_as, *args): self.name = '' @@ -30,7 +30,7 @@ class CommandFilter(object): self.real_exec = None def get_exec(self, exec_dirs=[]): - """Returns existing executable, or empty string if none found""" + """Returns existing executable, or empty string if none found.""" if self.real_exec is not None: return self.real_exec self.real_exec = "" @@ -46,10 +46,8 @@ class CommandFilter(object): return self.real_exec def match(self, userargs): - """Only check that the first argument (command) matches exec_path""" - if (os.path.basename(self.exec_path) == userargs[0]): - return True - return False + """Only check that the first argument (command) matches exec_path.""" + return os.path.basename(self.exec_path) == userargs[0] def get_command(self, userargs, exec_dirs=[]): """Returns command to execute (with sudo -u if run_as != root).""" @@ -60,12 +58,12 @@ class CommandFilter(object): return [to_exec] + userargs[1:] def get_environment(self, userargs): - """Returns specific environment to set, None if none""" + """Returns specific environment to set, None if none.""" return None class RegExpFilter(CommandFilter): - """Command filter doing regexp matching for every argument""" + """Command filter doing regexp matching for every argument.""" def match(self, userargs): # Early skip if command or number of args don't match @@ -135,7 +133,7 @@ class PathFilter(CommandFilter): class DnsmasqFilter(CommandFilter): - """Specific filter for the dnsmasq call (which includes env)""" + """Specific filter for the dnsmasq call (which includes env).""" CONFIG_FILE_ARG = 'CONFIG_FILE' @@ -160,7 +158,7 @@ class DnsmasqFilter(CommandFilter): class DeprecatedDnsmasqFilter(DnsmasqFilter): - """Variant of dnsmasq filter to support old-style FLAGFILE""" + """Variant of dnsmasq filter to support old-style FLAGFILE.""" CONFIG_FILE_ARG = 'FLAGFILE' @@ -210,7 +208,7 @@ class KillFilter(CommandFilter): class ReadFileFilter(CommandFilter): - """Specific filter for the utils.read_file_as_root call""" + """Specific filter for the utils.read_file_as_root call.""" def __init__(self, file_path, *args): self.file_path = file_path diff --git a/openstack/common/rootwrap/wrapper.py b/openstack/common/rootwrap/wrapper.py index e0ac9df..d488ddd 100644 --- a/openstack/common/rootwrap/wrapper.py +++ b/openstack/common/rootwrap/wrapper.py @@ -93,7 +93,7 @@ def setup_syslog(execname, facility, level): def build_filter(class_name, *args): - """Returns a filter object of class class_name""" + """Returns a filter object of class class_name.""" if not hasattr(filters, class_name): logging.warning("Skipping unknown filter class (%s) specified " "in filter definitions" % class_name) @@ -103,7 +103,7 @@ def build_filter(class_name, *args): def load_filters(filters_path): - """Load filters from a list of directories""" + """Load filters from a list of directories.""" filterlist = [] for filterdir in filters_path: if not os.path.isdir(filterdir): @@ -121,7 +121,7 @@ def load_filters(filters_path): return filterlist -def match_filter(filters, userargs, exec_dirs=[]): +def match_filter(filter_list, userargs, exec_dirs=[]): """ Checks user command and arguments through command filters and returns the first matching filter. @@ -131,7 +131,7 @@ def match_filter(filters, userargs, exec_dirs=[]): """ first_not_executable_filter = None - for f in filters: + for f in filter_list: if f.match(userargs): # Try other filters if executable is absent if not f.get_exec(exec_dirs=exec_dirs): diff --git a/openstack/common/rpc/impl_qpid.py b/openstack/common/rpc/impl_qpid.py index a7b4707..a03ebb2 100644 --- a/openstack/common/rpc/impl_qpid.py +++ b/openstack/common/rpc/impl_qpid.py @@ -375,7 +375,7 @@ class Connection(object): try: return method(*args, **kwargs) except (qpid_exceptions.Empty, - qpid_exceptions.ConnectionError), e: + qpid_exceptions.ConnectionError) as e: if error_callback: error_callback(e) self.reconnect() diff --git a/openstack/common/strutils.py b/openstack/common/strutils.py index fe8418e..d6dfb13 100644 --- a/openstack/common/strutils.py +++ b/openstack/common/strutils.py @@ -49,12 +49,15 @@ def bool_from_string(subject): Useful for JSON-decoded stuff and config file parsing """ - if isinstance(subject, bool): - return subject - if isinstance(subject, basestring): - if subject.strip().lower() in ('true', 'on', 'yes', '1'): - return True - return False + try: + # True or 1 or '1' -> True + # False or < 1 or > 1 or '0' -> False + return int(subject) == 1 + except TypeError: + # None -> False + return False + except ValueError: + return subject.strip().lower() in ('true', 'on', 'yes') def safe_decode(text, incoming=None, errors='strict'): |