diff options
Diffstat (limited to 'openstack')
-rwxr-xr-x | openstack/common/config/generator.py | 5 | ||||
-rw-r--r-- | openstack/common/funcutils.py | 76 | ||||
-rw-r--r-- | openstack/common/rootwrap/filters.py | 35 | ||||
-rw-r--r-- | openstack/common/strutils.py | 44 |
4 files changed, 124 insertions, 36 deletions
diff --git a/openstack/common/config/generator.py b/openstack/common/config/generator.py index 3f66f74..09649e7 100755 --- a/openstack/common/config/generator.py +++ b/openstack/common/config/generator.py @@ -56,7 +56,8 @@ OPTION_REGEX = re.compile(r"(%s)" % "|".join([STROPT, BOOLOPT, INTOPT, MULTISTROPT])) PY_EXT = ".py" -BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../")) +BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), + "../../../../")) WORDWRAP_WIDTH = 60 @@ -193,7 +194,7 @@ def _sanitize_default(s): return s.replace(BASEDIR, '') elif s == _get_my_ip(): return '10.0.0.1' - elif s == socket.getfqdn(): + elif s == socket.gethostname(): return 'oslo' elif s.strip() != s: return '"%s"' % s diff --git a/openstack/common/funcutils.py b/openstack/common/funcutils.py new file mode 100644 index 0000000..b04d58f --- /dev/null +++ b/openstack/common/funcutils.py @@ -0,0 +1,76 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack Foundation. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Utility methods for working with functions/decorators.""" + +import inspect + + +def get_wrapped_function(function): + """Get the method at the bottom of a stack of decorators.""" + + if not hasattr(function, 'func_closure') or not function.func_closure: + return function + + def _get_wrapped_function(function): + if not hasattr(function, 'func_closure') or not function.func_closure: + return None + + for closure in function.func_closure: + func = closure.cell_contents + + deeper_func = _get_wrapped_function(func) + if deeper_func: + return deeper_func + elif hasattr(closure.cell_contents, '__call__'): + return closure.cell_contents + + return _get_wrapped_function(function) + + +def getcallargs(function, *args, **kwargs): + """This is a simplified inspect.getcallargs (2.7+). + + It should be replaced when python >= 2.7 is standard. + """ + + keyed_args = {} + argnames, varargs, keywords, defaults = inspect.getargspec(function) + + keyed_args.update(kwargs) + + # NOTE(alaski) the implicit 'self' or 'cls' argument shows up in + # argnames but not in args or kwargs. Uses 'in' rather than '==' because + # some tests use 'self2'. + if 'self' in argnames[0] or 'cls' == argnames[0]: + # The function may not actually be a method or have im_self. + # Typically seen when it's stubbed with mox. + if inspect.ismethod(function) and hasattr(function, 'im_self'): + keyed_args[argnames[0]] = function.im_self + else: + keyed_args[argnames[0]] = None + + remaining_argnames = filter(lambda x: x not in keyed_args, argnames) + keyed_args.update(dict(zip(remaining_argnames, args))) + + if defaults: + num_defaults = len(defaults) + for argname, value in zip(argnames[-num_defaults:], defaults): + if argname not in keyed_args: + keyed_args[argname] = value + + return keyed_args diff --git a/openstack/common/rootwrap/filters.py b/openstack/common/rootwrap/filters.py index ae7c62c..0cc55ce 100644 --- a/openstack/common/rootwrap/filters.py +++ b/openstack/common/rootwrap/filters.py @@ -34,7 +34,7 @@ class CommandFilter(object): if self.real_exec is not None: return self.real_exec self.real_exec = "" - if self.exec_path.startswith('/'): + if os.path.isabs(self.exec_path): if os.access(self.exec_path, os.X_OK): self.real_exec = self.exec_path else: @@ -164,8 +164,10 @@ class DeprecatedDnsmasqFilter(DnsmasqFilter): class KillFilter(CommandFilter): """Specific filter for the kill calls. + 1st argument is the user to run /bin/kill under 2nd argument is the location of the affected executable + if the argument is not absolute, it is checked against $PATH Subsequent arguments list the accepted signals (if any) This filter relies on /proc to accurately determine affected @@ -194,21 +196,28 @@ class KillFilter(CommandFilter): return False try: command = os.readlink("/proc/%d/exe" % int(args[1])) - # NOTE(yufang521247): /proc/PID/exe may have '\0' on the - # end, because python doen't stop at '\0' when read the - # target path. - command = command.split('\0')[0] - # NOTE(dprince): /proc/PID/exe may have ' (deleted)' on - # the end if an executable is updated or deleted - if command.endswith(" (deleted)"): - command = command[:command.rindex(" ")] - if command != self.args[0]: - # Affected executable does not match - return False except (ValueError, OSError): # Incorrect PID return False - return True + + # NOTE(yufang521247): /proc/PID/exe may have '\0' on the + # end, because python doen't stop at '\0' when read the + # target path. + command = command.partition('\0')[0] + + # NOTE(dprince): /proc/PID/exe may have ' (deleted)' on + # the end if an executable is updated or deleted + if command.endswith(" (deleted)"): + command = command[:-len(" (deleted)")] + + kill_command = self.args[0] + + if os.path.isabs(kill_command): + return kill_command == command + + return (os.path.isabs(command) and + kill_command == os.path.basename(command) and + os.path.dirname(command) in os.environ['PATH'].split(':')) class ReadFileFilter(CommandFilter): diff --git a/openstack/common/strutils.py b/openstack/common/strutils.py index 05c178c..a3fb53a 100644 --- a/openstack/common/strutils.py +++ b/openstack/common/strutils.py @@ -35,7 +35,7 @@ BYTE_MULTIPLIERS = { 'm': 1024 ** 2, 'k': 1024, } - +BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)') TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') @@ -162,31 +162,33 @@ def safe_encode(text, incoming=None, def to_bytes(text, default=0): - """Try to turn a string into a number of bytes. Looks at the last - characters of the text to determine what conversion is needed to - turn the input text into a byte number. + """Converts a string into an integer of bytes. + + Looks at the last characters of the text to determine + what conversion is needed to turn the input text into a byte number. + Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive) - Supports: B/b, K/k, M/m, G/g, T/t (or the same with b/B on the end) + :param text: String input for bytes size conversion. + :param default: Default return value when text is blank. """ - # Take off everything not number 'like' (which should leave - # only the byte 'identifier' left) - mult_key_org = text.lstrip('-1234567890') - mult_key = mult_key_org.lower() - mult_key_len = len(mult_key) - if mult_key.endswith("b"): - mult_key = mult_key[0:-1] - try: - multiplier = BYTE_MULTIPLIERS[mult_key] - if mult_key_len: - # Empty cases shouldn't cause text[0:-0] - text = text[0:-mult_key_len] - return int(text) * multiplier - except KeyError: - msg = _('Unknown byte multiplier: %s') % mult_key_org + match = BYTE_REGEX.search(text) + if match: + magnitude = int(match.group(1)) + mult_key_org = match.group(2) + if not mult_key_org: + return magnitude + elif text: + msg = _('Invalid string format: %s') % text raise TypeError(msg) - except ValueError: + else: return default + mult_key = mult_key_org.lower().replace('b', '', 1) + multiplier = BYTE_MULTIPLIERS.get(mult_key) + if multiplier is None: + msg = _('Unknown byte multiplier: %s') % mult_key_org + raise TypeError(msg) + return magnitude * multiplier def to_slug(value, incoming=None, errors="strict"): |