summaryrefslogtreecommitdiffstats
path: root/openstack
diff options
context:
space:
mode:
Diffstat (limited to 'openstack')
-rw-r--r--openstack/common/lockutils.py29
-rw-r--r--openstack/common/processutils.py66
-rw-r--r--openstack/common/rootwrap/filters.py20
-rw-r--r--openstack/common/rootwrap/wrapper.py8
-rw-r--r--openstack/common/rpc/impl_qpid.py2
-rw-r--r--openstack/common/strutils.py15
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'):