1200 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1200 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
						|
 | 
						|
# Copyright 2010 United States Government as represented by the
 | 
						|
# Administrator of the National Aeronautics and Space Administration.
 | 
						|
# Copyright 2011 Justin Santa Barbara
 | 
						|
# 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.
 | 
						|
 | 
						|
"""Utilities and helper functions."""
 | 
						|
 | 
						|
import contextlib
 | 
						|
import datetime
 | 
						|
import functools
 | 
						|
import hashlib
 | 
						|
import inspect
 | 
						|
import os
 | 
						|
import pyclbr
 | 
						|
import random
 | 
						|
import re
 | 
						|
import shutil
 | 
						|
import socket
 | 
						|
import struct
 | 
						|
import sys
 | 
						|
import tempfile
 | 
						|
import time
 | 
						|
from xml.sax import saxutils
 | 
						|
 | 
						|
import eventlet
 | 
						|
import netaddr
 | 
						|
 | 
						|
from oslo.config import cfg
 | 
						|
 | 
						|
from nova import exception
 | 
						|
from nova.openstack.common import excutils
 | 
						|
from nova.openstack.common.gettextutils import _
 | 
						|
from nova.openstack.common import importutils
 | 
						|
from nova.openstack.common import lockutils
 | 
						|
from nova.openstack.common import log as logging
 | 
						|
from nova.openstack.common import processutils
 | 
						|
from nova.openstack.common.rpc import common as rpc_common
 | 
						|
from nova.openstack.common import timeutils
 | 
						|
 | 
						|
notify_decorator = 'nova.openstack.common.notifier.api.notify_decorator'
 | 
						|
 | 
						|
monkey_patch_opts = [
 | 
						|
    cfg.BoolOpt('monkey_patch',
 | 
						|
                default=False,
 | 
						|
                help='Whether to log monkey patching'),
 | 
						|
    cfg.ListOpt('monkey_patch_modules',
 | 
						|
                default=[
 | 
						|
                  'nova.api.ec2.cloud:%s' % (notify_decorator),
 | 
						|
                  'nova.compute.api:%s' % (notify_decorator)
 | 
						|
                  ],
 | 
						|
                help='List of modules/decorators to monkey patch'),
 | 
						|
]
 | 
						|
utils_opts = [
 | 
						|
    cfg.IntOpt('password_length',
 | 
						|
               default=12,
 | 
						|
               help='Length of generated instance admin passwords'),
 | 
						|
    cfg.StrOpt('instance_usage_audit_period',
 | 
						|
               default='month',
 | 
						|
               help='time period to generate instance usages for.  '
 | 
						|
                    'Time period must be hour, day, month or year'),
 | 
						|
    cfg.StrOpt('rootwrap_config',
 | 
						|
               default="/etc/nova/rootwrap.conf",
 | 
						|
               help='Path to the rootwrap configuration file to use for '
 | 
						|
                    'running commands as root'),
 | 
						|
    cfg.StrOpt('tempdir',
 | 
						|
               help='Explicitly specify the temporary working directory'),
 | 
						|
]
 | 
						|
CONF = cfg.CONF
 | 
						|
CONF.register_opts(monkey_patch_opts)
 | 
						|
CONF.register_opts(utils_opts)
 | 
						|
CONF.import_opt('network_api_class', 'nova.network')
 | 
						|
 | 
						|
LOG = logging.getLogger(__name__)
 | 
						|
 | 
						|
# Used for looking up extensions of text
 | 
						|
# to their 'multiplied' byte amount
 | 
						|
BYTE_MULTIPLIERS = {
 | 
						|
    '': 1,
 | 
						|
    't': 1024 ** 4,
 | 
						|
    'g': 1024 ** 3,
 | 
						|
    'm': 1024 ** 2,
 | 
						|
    'k': 1024,
 | 
						|
}
 | 
						|
 | 
						|
# used in limits
 | 
						|
TIME_UNITS = {
 | 
						|
    'SECOND': 1,
 | 
						|
    'MINUTE': 60,
 | 
						|
    'HOUR': 3600,
 | 
						|
    'DAY': 84400
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
_IS_NEUTRON_ATTEMPTED = False
 | 
						|
_IS_NEUTRON = False
 | 
						|
 | 
						|
synchronized = lockutils.synchronized_with_prefix('nova-')
 | 
						|
 | 
						|
 | 
						|
def vpn_ping(address, port, timeout=0.05, session_id=None):
 | 
						|
    """Sends a vpn negotiation packet and returns the server session.
 | 
						|
 | 
						|
    Returns False on a failure. Basic packet structure is below.
 | 
						|
 | 
						|
    Client packet (14 bytes)::
 | 
						|
 | 
						|
         0 1      8 9  13
 | 
						|
        +-+--------+-----+
 | 
						|
        |x| cli_id |?????|
 | 
						|
        +-+--------+-----+
 | 
						|
        x = packet identifier 0x38
 | 
						|
        cli_id = 64 bit identifier
 | 
						|
        ? = unknown, probably flags/padding
 | 
						|
 | 
						|
    Server packet (26 bytes)::
 | 
						|
 | 
						|
         0 1      8 9  13 14    21 2225
 | 
						|
        +-+--------+-----+--------+----+
 | 
						|
        |x| srv_id |?????| cli_id |????|
 | 
						|
        +-+--------+-----+--------+----+
 | 
						|
        x = packet identifier 0x40
 | 
						|
        cli_id = 64 bit identifier
 | 
						|
        ? = unknown, probably flags/padding
 | 
						|
        bit 9 was 1 and the rest were 0 in testing
 | 
						|
 | 
						|
    """
 | 
						|
    if session_id is None:
 | 
						|
        session_id = random.randint(0, 0xffffffffffffffff)
 | 
						|
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 | 
						|
    data = struct.pack('!BQxxxxx', 0x38, session_id)
 | 
						|
    sock.sendto(data, (address, port))
 | 
						|
    sock.settimeout(timeout)
 | 
						|
    try:
 | 
						|
        received = sock.recv(2048)
 | 
						|
    except socket.timeout:
 | 
						|
        return False
 | 
						|
    finally:
 | 
						|
        sock.close()
 | 
						|
    fmt = '!BQxxxxxQxxxx'
 | 
						|
    if len(received) != struct.calcsize(fmt):
 | 
						|
        LOG.warn(_('Expected to receive %(exp)s bytes, but actually %(act)s') %
 | 
						|
                 dict(exp=struct.calcsize(fmt), act=len(received)))
 | 
						|
        return False
 | 
						|
    (identifier, server_sess, client_sess) = struct.unpack(fmt, received)
 | 
						|
    if identifier == 0x40 and client_sess == session_id:
 | 
						|
        return server_sess
 | 
						|
 | 
						|
 | 
						|
def execute(*cmd, **kwargs):
 | 
						|
    """Convenience wrapper around oslo's execute() method."""
 | 
						|
    if 'run_as_root' in kwargs and not 'root_helper' in kwargs:
 | 
						|
        kwargs['root_helper'] = 'sudo nova-rootwrap %s' % CONF.rootwrap_config
 | 
						|
    return processutils.execute(*cmd, **kwargs)
 | 
						|
 | 
						|
 | 
						|
def trycmd(*args, **kwargs):
 | 
						|
    """Convenience wrapper around oslo's trycmd() method."""
 | 
						|
    if 'run_as_root' in kwargs and not 'root_helper' in kwargs:
 | 
						|
        kwargs['root_helper'] = 'sudo nova-rootwrap %s' % CONF.rootwrap_config
 | 
						|
    return processutils.trycmd(*args, **kwargs)
 | 
						|
 | 
						|
 | 
						|
def novadir():
 | 
						|
    import nova
 | 
						|
    return os.path.abspath(nova.__file__).split('nova/__init__.py')[0]
 | 
						|
 | 
						|
 | 
						|
def debug(arg):
 | 
						|
    LOG.debug(_('debug in callback: %s'), arg)
 | 
						|
    return arg
 | 
						|
 | 
						|
 | 
						|
def generate_uid(topic, size=8):
 | 
						|
    characters = '01234567890abcdefghijklmnopqrstuvwxyz'
 | 
						|
    choices = [random.choice(characters) for _x in xrange(size)]
 | 
						|
    return '%s-%s' % (topic, ''.join(choices))
 | 
						|
 | 
						|
 | 
						|
# Default symbols to use for passwords. Avoids visually confusing characters.
 | 
						|
# ~6 bits per symbol
 | 
						|
DEFAULT_PASSWORD_SYMBOLS = ('23456789',  # Removed: 0,1
 | 
						|
                            'ABCDEFGHJKLMNPQRSTUVWXYZ',   # Removed: I, O
 | 
						|
                            'abcdefghijkmnopqrstuvwxyz')  # Removed: l
 | 
						|
 | 
						|
 | 
						|
# ~5 bits per symbol
 | 
						|
EASIER_PASSWORD_SYMBOLS = ('23456789',  # Removed: 0, 1
 | 
						|
                           'ABCDEFGHJKLMNPQRSTUVWXYZ')  # Removed: I, O
 | 
						|
 | 
						|
 | 
						|
def last_completed_audit_period(unit=None, before=None):
 | 
						|
    """This method gives you the most recently *completed* audit period.
 | 
						|
 | 
						|
    arguments:
 | 
						|
            units: string, one of 'hour', 'day', 'month', 'year'
 | 
						|
                    Periods normally begin at the beginning (UTC) of the
 | 
						|
                    period unit (So a 'day' period begins at midnight UTC,
 | 
						|
                    a 'month' unit on the 1st, a 'year' on Jan, 1)
 | 
						|
                    unit string may be appended with an optional offset
 | 
						|
                    like so:  'day@18'  This will begin the period at 18:00
 | 
						|
                    UTC.  'month@15' starts a monthly period on the 15th,
 | 
						|
                    and year@3 begins a yearly one on March 1st.
 | 
						|
            before: Give the audit period most recently completed before
 | 
						|
                    <timestamp>. Defaults to now.
 | 
						|
 | 
						|
 | 
						|
    returns:  2 tuple of datetimes (begin, end)
 | 
						|
              The begin timestamp of this audit period is the same as the
 | 
						|
              end of the previous.
 | 
						|
    """
 | 
						|
    if not unit:
 | 
						|
        unit = CONF.instance_usage_audit_period
 | 
						|
 | 
						|
    offset = 0
 | 
						|
    if '@' in unit:
 | 
						|
        unit, offset = unit.split("@", 1)
 | 
						|
        offset = int(offset)
 | 
						|
 | 
						|
    if before is not None:
 | 
						|
        rightnow = before
 | 
						|
    else:
 | 
						|
        rightnow = timeutils.utcnow()
 | 
						|
    if unit not in ('month', 'day', 'year', 'hour'):
 | 
						|
        raise ValueError('Time period must be hour, day, month or year')
 | 
						|
    if unit == 'month':
 | 
						|
        if offset == 0:
 | 
						|
            offset = 1
 | 
						|
        end = datetime.datetime(day=offset,
 | 
						|
                                month=rightnow.month,
 | 
						|
                                year=rightnow.year)
 | 
						|
        if end >= rightnow:
 | 
						|
            year = rightnow.year
 | 
						|
            if 1 >= rightnow.month:
 | 
						|
                year -= 1
 | 
						|
                month = 12 + (rightnow.month - 1)
 | 
						|
            else:
 | 
						|
                month = rightnow.month - 1
 | 
						|
            end = datetime.datetime(day=offset,
 | 
						|
                                    month=month,
 | 
						|
                                    year=year)
 | 
						|
        year = end.year
 | 
						|
        if 1 >= end.month:
 | 
						|
            year -= 1
 | 
						|
            month = 12 + (end.month - 1)
 | 
						|
        else:
 | 
						|
            month = end.month - 1
 | 
						|
        begin = datetime.datetime(day=offset, month=month, year=year)
 | 
						|
 | 
						|
    elif unit == 'year':
 | 
						|
        if offset == 0:
 | 
						|
            offset = 1
 | 
						|
        end = datetime.datetime(day=1, month=offset, year=rightnow.year)
 | 
						|
        if end >= rightnow:
 | 
						|
            end = datetime.datetime(day=1,
 | 
						|
                                    month=offset,
 | 
						|
                                    year=rightnow.year - 1)
 | 
						|
            begin = datetime.datetime(day=1,
 | 
						|
                                      month=offset,
 | 
						|
                                      year=rightnow.year - 2)
 | 
						|
        else:
 | 
						|
            begin = datetime.datetime(day=1,
 | 
						|
                                      month=offset,
 | 
						|
                                      year=rightnow.year - 1)
 | 
						|
 | 
						|
    elif unit == 'day':
 | 
						|
        end = datetime.datetime(hour=offset,
 | 
						|
                               day=rightnow.day,
 | 
						|
                               month=rightnow.month,
 | 
						|
                               year=rightnow.year)
 | 
						|
        if end >= rightnow:
 | 
						|
            end = end - datetime.timedelta(days=1)
 | 
						|
        begin = end - datetime.timedelta(days=1)
 | 
						|
 | 
						|
    elif unit == 'hour':
 | 
						|
        end = rightnow.replace(minute=offset, second=0, microsecond=0)
 | 
						|
        if end >= rightnow:
 | 
						|
            end = end - datetime.timedelta(hours=1)
 | 
						|
        begin = end - datetime.timedelta(hours=1)
 | 
						|
 | 
						|
    return (begin, end)
 | 
						|
 | 
						|
 | 
						|
def generate_password(length=None, symbolgroups=DEFAULT_PASSWORD_SYMBOLS):
 | 
						|
    """Generate a random password from the supplied symbol groups.
 | 
						|
 | 
						|
    At least one symbol from each group will be included. Unpredictable
 | 
						|
    results if length is less than the number of symbol groups.
 | 
						|
 | 
						|
    Believed to be reasonably secure (with a reasonable password length!)
 | 
						|
 | 
						|
    """
 | 
						|
    if length is None:
 | 
						|
        length = CONF.password_length
 | 
						|
 | 
						|
    r = random.SystemRandom()
 | 
						|
 | 
						|
    # NOTE(jerdfelt): Some password policies require at least one character
 | 
						|
    # from each group of symbols, so start off with one random character
 | 
						|
    # from each symbol group
 | 
						|
    password = [r.choice(s) for s in symbolgroups]
 | 
						|
    # If length < len(symbolgroups), the leading characters will only
 | 
						|
    # be from the first length groups. Try our best to not be predictable
 | 
						|
    # by shuffling and then truncating.
 | 
						|
    r.shuffle(password)
 | 
						|
    password = password[:length]
 | 
						|
    length -= len(password)
 | 
						|
 | 
						|
    # then fill with random characters from all symbol groups
 | 
						|
    symbols = ''.join(symbolgroups)
 | 
						|
    password.extend([r.choice(symbols) for _i in xrange(length)])
 | 
						|
 | 
						|
    # finally shuffle to ensure first x characters aren't from a
 | 
						|
    # predictable group
 | 
						|
    r.shuffle(password)
 | 
						|
 | 
						|
    return ''.join(password)
 | 
						|
 | 
						|
 | 
						|
def last_octet(address):
 | 
						|
    return int(address.split('.')[-1])
 | 
						|
 | 
						|
 | 
						|
def get_my_ipv4_address():
 | 
						|
    """Run ip route/addr commands to figure out the best ipv4
 | 
						|
    """
 | 
						|
    LOCALHOST = '127.0.0.1'
 | 
						|
    try:
 | 
						|
        out = execute('ip', '-f', 'inet', '-o', 'route', 'show',
 | 
						|
                      run_as_root=True)
 | 
						|
 | 
						|
        # Find the default route
 | 
						|
        regex_default = ('default\s*via\s*'
 | 
						|
                         '(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
 | 
						|
                         '\s*dev\s*(\w*)\s*')
 | 
						|
        default_routes = re.findall(regex_default, out[0])
 | 
						|
        if not default_routes:
 | 
						|
            return LOCALHOST
 | 
						|
        gateway, iface = default_routes[0]
 | 
						|
 | 
						|
        # Find the right subnet for the gateway/interface for
 | 
						|
        # the default route
 | 
						|
        route = ('(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})'
 | 
						|
              '\s*dev\s*(\w*)\s*')
 | 
						|
        for match in re.finditer(route, out[0]):
 | 
						|
            subnet = netaddr.IPNetwork(match.group(1) + "/" + match.group(2))
 | 
						|
            if (match.group(3) == iface and
 | 
						|
                    netaddr.IPAddress(gateway) in subnet):
 | 
						|
                try:
 | 
						|
                    return _get_ipv4_address_for_interface(iface)
 | 
						|
                except exception.NovaException:
 | 
						|
                    pass
 | 
						|
    except Exception as ex:
 | 
						|
        LOG.error(_("Couldn't get IPv4 : %(ex)s") % {'ex': ex})
 | 
						|
    return LOCALHOST
 | 
						|
 | 
						|
 | 
						|
def _get_ipv4_address_for_interface(iface):
 | 
						|
    """Run ip addr show for an interface and grab its ipv4 addresses
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        out = execute('ip', '-f', 'inet', '-o', 'addr', 'show', iface,
 | 
						|
                      run_as_root=True)
 | 
						|
        regexp_address = re.compile('inet\s*'
 | 
						|
                                    '(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
 | 
						|
        address = [m.group(1) for m in regexp_address.finditer(out[0])
 | 
						|
                   if m.group(1) != '127.0.0.1']
 | 
						|
        if address:
 | 
						|
            return address[0]
 | 
						|
        else:
 | 
						|
            msg = _('IPv4 address is not found.: %s') % out[0]
 | 
						|
            raise exception.NovaException(msg)
 | 
						|
    except Exception as ex:
 | 
						|
        msg = _("Couldn't get IPv4 of %(interface)s"
 | 
						|
                " : %(ex)s") % {'interface': iface, 'ex': ex}
 | 
						|
        LOG.error(msg)
 | 
						|
        raise exception.NovaException(msg)
 | 
						|
 | 
						|
 | 
						|
def get_my_linklocal(interface):
 | 
						|
    try:
 | 
						|
        if_str = execute('ip', '-f', 'inet6', '-o', 'addr', 'show', interface)
 | 
						|
        condition = '\s+inet6\s+([0-9a-f:]+)/\d+\s+scope\s+link'
 | 
						|
        links = [re.search(condition, x) for x in if_str[0].split('\n')]
 | 
						|
        address = [w.group(1) for w in links if w is not None]
 | 
						|
        if address[0] is not None:
 | 
						|
            return address[0]
 | 
						|
        else:
 | 
						|
            msg = _('Link Local address is not found.:%s') % if_str
 | 
						|
            raise exception.NovaException(msg)
 | 
						|
    except Exception as ex:
 | 
						|
        msg = _("Couldn't get Link Local IP of %(interface)s"
 | 
						|
                " :%(ex)s") % {'interface': interface, 'ex': ex}
 | 
						|
        raise exception.NovaException(msg)
 | 
						|
 | 
						|
 | 
						|
def parse_mailmap(mailmap='.mailmap'):
 | 
						|
    mapping = {}
 | 
						|
    if os.path.exists(mailmap):
 | 
						|
        fp = open(mailmap, 'r')
 | 
						|
        for l in fp:
 | 
						|
            l = l.strip()
 | 
						|
            if not l.startswith('#') and ' ' in l:
 | 
						|
                canonical_email, alias = l.split(' ')
 | 
						|
                mapping[alias.lower()] = canonical_email.lower()
 | 
						|
    return mapping
 | 
						|
 | 
						|
 | 
						|
def str_dict_replace(s, mapping):
 | 
						|
    for s1, s2 in mapping.iteritems():
 | 
						|
        s = s.replace(s1, s2)
 | 
						|
    return s
 | 
						|
 | 
						|
 | 
						|
class LazyPluggable(object):
 | 
						|
    """A pluggable backend loaded lazily based on some value."""
 | 
						|
 | 
						|
    def __init__(self, pivot, config_group=None, **backends):
 | 
						|
        self.__backends = backends
 | 
						|
        self.__pivot = pivot
 | 
						|
        self.__backend = None
 | 
						|
        self.__config_group = config_group
 | 
						|
 | 
						|
    def __get_backend(self):
 | 
						|
        if not self.__backend:
 | 
						|
            if self.__config_group is None:
 | 
						|
                backend_name = CONF[self.__pivot]
 | 
						|
            else:
 | 
						|
                backend_name = CONF[self.__config_group][self.__pivot]
 | 
						|
            if backend_name not in self.__backends:
 | 
						|
                msg = _('Invalid backend: %s') % backend_name
 | 
						|
                raise exception.NovaException(msg)
 | 
						|
 | 
						|
            backend = self.__backends[backend_name]
 | 
						|
            if isinstance(backend, tuple):
 | 
						|
                name = backend[0]
 | 
						|
                fromlist = backend[1]
 | 
						|
            else:
 | 
						|
                name = backend
 | 
						|
                fromlist = backend
 | 
						|
 | 
						|
            self.__backend = __import__(name, None, None, fromlist)
 | 
						|
        return self.__backend
 | 
						|
 | 
						|
    def __getattr__(self, key):
 | 
						|
        backend = self.__get_backend()
 | 
						|
        return getattr(backend, key)
 | 
						|
 | 
						|
 | 
						|
def xhtml_escape(value):
 | 
						|
    """Escapes a string so it is valid within XML or XHTML.
 | 
						|
 | 
						|
    """
 | 
						|
    return saxutils.escape(value, {'"': '"', "'": '''})
 | 
						|
 | 
						|
 | 
						|
def utf8(value):
 | 
						|
    """Try to turn a string into utf-8 if possible.
 | 
						|
 | 
						|
    Code is directly from the utf8 function in
 | 
						|
    http://github.com/facebook/tornado/blob/master/tornado/escape.py
 | 
						|
 | 
						|
    """
 | 
						|
    if isinstance(value, unicode):
 | 
						|
        return value.encode('utf-8')
 | 
						|
    assert isinstance(value, str)
 | 
						|
    return value
 | 
						|
 | 
						|
 | 
						|
def get_from_path(items, path):
 | 
						|
    """Returns a list of items matching the specified path.
 | 
						|
 | 
						|
    Takes an XPath-like expression e.g. prop1/prop2/prop3, and for each item
 | 
						|
    in items, looks up items[prop1][prop2][prop3].  Like XPath, if any of the
 | 
						|
    intermediate results are lists it will treat each list item individually.
 | 
						|
    A 'None' in items or any child expressions will be ignored, this function
 | 
						|
    will not throw because of None (anywhere) in items.  The returned list
 | 
						|
    will contain no None values.
 | 
						|
 | 
						|
    """
 | 
						|
    if path is None:
 | 
						|
        raise exception.NovaException('Invalid mini_xpath')
 | 
						|
 | 
						|
    (first_token, sep, remainder) = path.partition('/')
 | 
						|
 | 
						|
    if first_token == '':
 | 
						|
        raise exception.NovaException('Invalid mini_xpath')
 | 
						|
 | 
						|
    results = []
 | 
						|
 | 
						|
    if items is None:
 | 
						|
        return results
 | 
						|
 | 
						|
    if not isinstance(items, list):
 | 
						|
        # Wrap single objects in a list
 | 
						|
        items = [items]
 | 
						|
 | 
						|
    for item in items:
 | 
						|
        if item is None:
 | 
						|
            continue
 | 
						|
        get_method = getattr(item, 'get', None)
 | 
						|
        if get_method is None:
 | 
						|
            continue
 | 
						|
        child = get_method(first_token)
 | 
						|
        if child is None:
 | 
						|
            continue
 | 
						|
        if isinstance(child, list):
 | 
						|
            # Flatten intermediate lists
 | 
						|
            for x in child:
 | 
						|
                results.append(x)
 | 
						|
        else:
 | 
						|
            results.append(child)
 | 
						|
 | 
						|
    if not sep:
 | 
						|
        # No more tokens
 | 
						|
        return results
 | 
						|
    else:
 | 
						|
        return get_from_path(results, remainder)
 | 
						|
 | 
						|
 | 
						|
def flatten_dict(dict_, flattened=None):
 | 
						|
    """Recursively flatten a nested dictionary."""
 | 
						|
    flattened = flattened or {}
 | 
						|
    for key, value in dict_.iteritems():
 | 
						|
        if hasattr(value, 'iteritems'):
 | 
						|
            flatten_dict(value, flattened)
 | 
						|
        else:
 | 
						|
            flattened[key] = value
 | 
						|
    return flattened
 | 
						|
 | 
						|
 | 
						|
def partition_dict(dict_, keys):
 | 
						|
    """Return two dicts, one with `keys` the other with everything else."""
 | 
						|
    intersection = {}
 | 
						|
    difference = {}
 | 
						|
    for key, value in dict_.iteritems():
 | 
						|
        if key in keys:
 | 
						|
            intersection[key] = value
 | 
						|
        else:
 | 
						|
            difference[key] = value
 | 
						|
    return intersection, difference
 | 
						|
 | 
						|
 | 
						|
def map_dict_keys(dict_, key_map):
 | 
						|
    """Return a dict in which the dictionaries keys are mapped to new keys."""
 | 
						|
    mapped = {}
 | 
						|
    for key, value in dict_.iteritems():
 | 
						|
        mapped_key = key_map[key] if key in key_map else key
 | 
						|
        mapped[mapped_key] = value
 | 
						|
    return mapped
 | 
						|
 | 
						|
 | 
						|
def subset_dict(dict_, keys):
 | 
						|
    """Return a dict that only contains a subset of keys."""
 | 
						|
    subset = partition_dict(dict_, keys)[0]
 | 
						|
    return subset
 | 
						|
 | 
						|
 | 
						|
def diff_dict(orig, new):
 | 
						|
    """
 | 
						|
    Return a dict describing how to change orig to new.  The keys
 | 
						|
    correspond to values that have changed; the value will be a list
 | 
						|
    of one or two elements.  The first element of the list will be
 | 
						|
    either '+' or '-', indicating whether the key was updated or
 | 
						|
    deleted; if the key was updated, the list will contain a second
 | 
						|
    element, giving the updated value.
 | 
						|
    """
 | 
						|
    # Figure out what keys went away
 | 
						|
    result = dict((k, ['-']) for k in set(orig.keys()) - set(new.keys()))
 | 
						|
    # Compute the updates
 | 
						|
    for key, value in new.items():
 | 
						|
        if key not in orig or value != orig[key]:
 | 
						|
            result[key] = ['+', value]
 | 
						|
    return result
 | 
						|
 | 
						|
 | 
						|
def check_isinstance(obj, cls):
 | 
						|
    """Checks that obj is of type cls, and lets PyLint infer types."""
 | 
						|
    if isinstance(obj, cls):
 | 
						|
        return obj
 | 
						|
    raise Exception(_('Expected object of type: %s') % (str(cls)))
 | 
						|
 | 
						|
 | 
						|
def parse_server_string(server_str):
 | 
						|
    """
 | 
						|
    Parses the given server_string and returns a list of host and port.
 | 
						|
    If it's not a combination of host part and port, the port element
 | 
						|
    is a null string. If the input is invalid expression, return a null
 | 
						|
    list.
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        # First of all, exclude pure IPv6 address (w/o port).
 | 
						|
        if netaddr.valid_ipv6(server_str):
 | 
						|
            return (server_str, '')
 | 
						|
 | 
						|
        # Next, check if this is IPv6 address with a port number combination.
 | 
						|
        if server_str.find("]:") != -1:
 | 
						|
            (address, port) = server_str.replace('[', '', 1).split(']:')
 | 
						|
            return (address, port)
 | 
						|
 | 
						|
        # Third, check if this is a combination of an address and a port
 | 
						|
        if server_str.find(':') == -1:
 | 
						|
            return (server_str, '')
 | 
						|
 | 
						|
        # This must be a combination of an address and a port
 | 
						|
        (address, port) = server_str.split(':')
 | 
						|
        return (address, port)
 | 
						|
 | 
						|
    except Exception:
 | 
						|
        LOG.error(_('Invalid server_string: %s'), server_str)
 | 
						|
        return ('', '')
 | 
						|
 | 
						|
 | 
						|
def is_int_like(val):
 | 
						|
    """Check if a value looks like an int."""
 | 
						|
    try:
 | 
						|
        return str(int(val)) == str(val)
 | 
						|
    except Exception:
 | 
						|
        return False
 | 
						|
 | 
						|
 | 
						|
def is_valid_ipv4(address):
 | 
						|
    """Verify that address represents a valid IPv4 address."""
 | 
						|
    try:
 | 
						|
        return netaddr.valid_ipv4(address)
 | 
						|
    except Exception:
 | 
						|
        return False
 | 
						|
 | 
						|
 | 
						|
def is_valid_ipv6(address):
 | 
						|
    try:
 | 
						|
        return netaddr.valid_ipv6(address)
 | 
						|
    except Exception:
 | 
						|
        return False
 | 
						|
 | 
						|
 | 
						|
def is_valid_ipv6_cidr(address):
 | 
						|
    try:
 | 
						|
        str(netaddr.IPNetwork(address, version=6).cidr)
 | 
						|
        return True
 | 
						|
    except Exception:
 | 
						|
        return False
 | 
						|
 | 
						|
 | 
						|
def get_shortened_ipv6(address):
 | 
						|
    addr = netaddr.IPAddress(address, version=6)
 | 
						|
    return str(addr.ipv6())
 | 
						|
 | 
						|
 | 
						|
def get_shortened_ipv6_cidr(address):
 | 
						|
    net = netaddr.IPNetwork(address, version=6)
 | 
						|
    return str(net.cidr)
 | 
						|
 | 
						|
 | 
						|
def is_valid_cidr(address):
 | 
						|
    """Check if address is valid
 | 
						|
 | 
						|
    The provided address can be a IPv6 or a IPv4
 | 
						|
    CIDR address.
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        # Validate the correct CIDR Address
 | 
						|
        netaddr.IPNetwork(address)
 | 
						|
    except netaddr.core.AddrFormatError:
 | 
						|
        return False
 | 
						|
    except UnboundLocalError:
 | 
						|
        # NOTE(MotoKen): work around bug in netaddr 0.7.5 (see detail in
 | 
						|
        # https://github.com/drkjam/netaddr/issues/2)
 | 
						|
        return False
 | 
						|
 | 
						|
    # Prior validation partially verify /xx part
 | 
						|
    # Verify it here
 | 
						|
    ip_segment = address.split('/')
 | 
						|
 | 
						|
    if (len(ip_segment) <= 1 or
 | 
						|
            ip_segment[1] == ''):
 | 
						|
        return False
 | 
						|
 | 
						|
    return True
 | 
						|
 | 
						|
 | 
						|
def get_ip_version(network):
 | 
						|
    """Returns the IP version of a network (IPv4 or IPv6).
 | 
						|
 | 
						|
    Raises AddrFormatError if invalid network.
 | 
						|
    """
 | 
						|
    if netaddr.IPNetwork(network).version == 6:
 | 
						|
        return "IPv6"
 | 
						|
    elif netaddr.IPNetwork(network).version == 4:
 | 
						|
        return "IPv4"
 | 
						|
 | 
						|
 | 
						|
def monkey_patch():
 | 
						|
    """If the Flags.monkey_patch set as True,
 | 
						|
    this function patches a decorator
 | 
						|
    for all functions in specified modules.
 | 
						|
    You can set decorators for each modules
 | 
						|
    using CONF.monkey_patch_modules.
 | 
						|
    The format is "Module path:Decorator function".
 | 
						|
    Example:
 | 
						|
      'nova.api.ec2.cloud:nova.openstack.common.notifier.api.notify_decorator'
 | 
						|
 | 
						|
    Parameters of the decorator is as follows.
 | 
						|
    (See nova.openstack.common.notifier.api.notify_decorator)
 | 
						|
 | 
						|
    name - name of the function
 | 
						|
    function - object of the function
 | 
						|
    """
 | 
						|
    # If CONF.monkey_patch is not True, this function do nothing.
 | 
						|
    if not CONF.monkey_patch:
 | 
						|
        return
 | 
						|
    # Get list of modules and decorators
 | 
						|
    for module_and_decorator in CONF.monkey_patch_modules:
 | 
						|
        module, decorator_name = module_and_decorator.split(':')
 | 
						|
        # import decorator function
 | 
						|
        decorator = importutils.import_class(decorator_name)
 | 
						|
        __import__(module)
 | 
						|
        # Retrieve module information using pyclbr
 | 
						|
        module_data = pyclbr.readmodule_ex(module)
 | 
						|
        for key in module_data.keys():
 | 
						|
            # set the decorator for the class methods
 | 
						|
            if isinstance(module_data[key], pyclbr.Class):
 | 
						|
                clz = importutils.import_class("%s.%s" % (module, key))
 | 
						|
                for method, func in inspect.getmembers(clz, inspect.ismethod):
 | 
						|
                    setattr(clz, method,
 | 
						|
                        decorator("%s.%s.%s" % (module, key, method), func))
 | 
						|
            # set the decorator for the function
 | 
						|
            if isinstance(module_data[key], pyclbr.Function):
 | 
						|
                func = importutils.import_class("%s.%s" % (module, key))
 | 
						|
                setattr(sys.modules[module], key,
 | 
						|
                    decorator("%s.%s" % (module, key), func))
 | 
						|
 | 
						|
 | 
						|
def convert_to_list_dict(lst, label):
 | 
						|
    """Convert a value or list into a list of dicts."""
 | 
						|
    if not lst:
 | 
						|
        return None
 | 
						|
    if not isinstance(lst, list):
 | 
						|
        lst = [lst]
 | 
						|
    return [{label: x} for x in lst]
 | 
						|
 | 
						|
 | 
						|
def timefunc(func):
 | 
						|
    """Decorator that logs how long a particular function took to execute."""
 | 
						|
    @functools.wraps(func)
 | 
						|
    def inner(*args, **kwargs):
 | 
						|
        start_time = time.time()
 | 
						|
        try:
 | 
						|
            return func(*args, **kwargs)
 | 
						|
        finally:
 | 
						|
            total_time = time.time() - start_time
 | 
						|
            LOG.debug(_("timefunc: '%(name)s' took %(total_time).2f secs") %
 | 
						|
                      dict(name=func.__name__, total_time=total_time))
 | 
						|
    return inner
 | 
						|
 | 
						|
 | 
						|
def make_dev_path(dev, partition=None, base='/dev'):
 | 
						|
    """Return a path to a particular device.
 | 
						|
 | 
						|
    >>> make_dev_path('xvdc')
 | 
						|
    /dev/xvdc
 | 
						|
 | 
						|
    >>> make_dev_path('xvdc', 1)
 | 
						|
    /dev/xvdc1
 | 
						|
    """
 | 
						|
    path = os.path.join(base, dev)
 | 
						|
    if partition:
 | 
						|
        path += str(partition)
 | 
						|
    return path
 | 
						|
 | 
						|
 | 
						|
def total_seconds(td):
 | 
						|
    """Local total_seconds implementation for compatibility with python 2.6."""
 | 
						|
    if hasattr(td, 'total_seconds'):
 | 
						|
        return td.total_seconds()
 | 
						|
    else:
 | 
						|
        return ((td.days * 86400 + td.seconds) * 10 ** 6 +
 | 
						|
                td.microseconds) / 10.0 ** 6
 | 
						|
 | 
						|
 | 
						|
def sanitize_hostname(hostname):
 | 
						|
    """Return a hostname which conforms to RFC-952 and RFC-1123 specs."""
 | 
						|
    if isinstance(hostname, unicode):
 | 
						|
        hostname = hostname.encode('latin-1', 'ignore')
 | 
						|
 | 
						|
    hostname = re.sub('[ _]', '-', hostname)
 | 
						|
    hostname = re.sub('[^\w.-]+', '', hostname)
 | 
						|
    hostname = hostname.lower()
 | 
						|
    hostname = hostname.strip('.-')
 | 
						|
 | 
						|
    return hostname
 | 
						|
 | 
						|
 | 
						|
def read_cached_file(filename, cache_info, reload_func=None):
 | 
						|
    """Read from a file if it has been modified.
 | 
						|
 | 
						|
    :param cache_info: dictionary to hold opaque cache.
 | 
						|
    :param reload_func: optional function to be called with data when
 | 
						|
                        file is reloaded due to a modification.
 | 
						|
 | 
						|
    :returns: data from file
 | 
						|
 | 
						|
    """
 | 
						|
    mtime = os.path.getmtime(filename)
 | 
						|
    if not cache_info or mtime != cache_info.get('mtime'):
 | 
						|
        LOG.debug(_("Reloading cached file %s") % filename)
 | 
						|
        with open(filename) as fap:
 | 
						|
            cache_info['data'] = fap.read()
 | 
						|
        cache_info['mtime'] = mtime
 | 
						|
        if reload_func:
 | 
						|
            reload_func(cache_info['data'])
 | 
						|
    return cache_info['data']
 | 
						|
 | 
						|
 | 
						|
def hash_file(file_like_object):
 | 
						|
    """Generate a hash for the contents of a file."""
 | 
						|
    checksum = hashlib.sha1()
 | 
						|
    for chunk in iter(lambda: file_like_object.read(32768), b''):
 | 
						|
        checksum.update(chunk)
 | 
						|
    return checksum.hexdigest()
 | 
						|
 | 
						|
 | 
						|
@contextlib.contextmanager
 | 
						|
def temporary_mutation(obj, **kwargs):
 | 
						|
    """Temporarily set the attr on a particular object to a given value then
 | 
						|
    revert when finished.
 | 
						|
 | 
						|
    One use of this is to temporarily set the read_deleted flag on a context
 | 
						|
    object:
 | 
						|
 | 
						|
        with temporary_mutation(context, read_deleted="yes"):
 | 
						|
            do_something_that_needed_deleted_objects()
 | 
						|
    """
 | 
						|
    def is_dict_like(thing):
 | 
						|
        return hasattr(thing, 'has_key')
 | 
						|
 | 
						|
    def get(thing, attr, default):
 | 
						|
        if is_dict_like(thing):
 | 
						|
            return thing.get(attr, default)
 | 
						|
        else:
 | 
						|
            return getattr(thing, attr, default)
 | 
						|
 | 
						|
    def set_value(thing, attr, val):
 | 
						|
        if is_dict_like(thing):
 | 
						|
            thing[attr] = val
 | 
						|
        else:
 | 
						|
            setattr(thing, attr, val)
 | 
						|
 | 
						|
    def delete(thing, attr):
 | 
						|
        if is_dict_like(thing):
 | 
						|
            del thing[attr]
 | 
						|
        else:
 | 
						|
            delattr(thing, attr)
 | 
						|
 | 
						|
    NOT_PRESENT = object()
 | 
						|
 | 
						|
    old_values = {}
 | 
						|
    for attr, new_value in kwargs.items():
 | 
						|
        old_values[attr] = get(obj, attr, NOT_PRESENT)
 | 
						|
        set_value(obj, attr, new_value)
 | 
						|
 | 
						|
    try:
 | 
						|
        yield
 | 
						|
    finally:
 | 
						|
        for attr, old_value in old_values.items():
 | 
						|
            if old_value is NOT_PRESENT:
 | 
						|
                delete(obj, attr)
 | 
						|
            else:
 | 
						|
                set_value(obj, attr, old_value)
 | 
						|
 | 
						|
 | 
						|
def generate_mac_address():
 | 
						|
    """Generate an Ethernet MAC address."""
 | 
						|
    # NOTE(vish): We would prefer to use 0xfe here to ensure that linux
 | 
						|
    #             bridge mac addresses don't change, but it appears to
 | 
						|
    #             conflict with libvirt, so we use the next highest octet
 | 
						|
    #             that has the unicast and locally administered bits set
 | 
						|
    #             properly: 0xfa.
 | 
						|
    #             Discussion: https://bugs.launchpad.net/nova/+bug/921838
 | 
						|
    mac = [0xfa, 0x16, 0x3e,
 | 
						|
           random.randint(0x00, 0xff),
 | 
						|
           random.randint(0x00, 0xff),
 | 
						|
           random.randint(0x00, 0xff)]
 | 
						|
    return ':'.join(map(lambda x: "%02x" % x, mac))
 | 
						|
 | 
						|
 | 
						|
def read_file_as_root(file_path):
 | 
						|
    """Secure helper to read file as root."""
 | 
						|
    try:
 | 
						|
        out, _err = execute('cat', file_path, run_as_root=True)
 | 
						|
        return out
 | 
						|
    except processutils.ProcessExecutionError:
 | 
						|
        raise exception.FileNotFound(file_path=file_path)
 | 
						|
 | 
						|
 | 
						|
@contextlib.contextmanager
 | 
						|
def temporary_chown(path, owner_uid=None):
 | 
						|
    """Temporarily chown a path.
 | 
						|
 | 
						|
    :params owner_uid: UID of temporary owner (defaults to current user)
 | 
						|
    """
 | 
						|
    if owner_uid is None:
 | 
						|
        owner_uid = os.getuid()
 | 
						|
 | 
						|
    orig_uid = os.stat(path).st_uid
 | 
						|
 | 
						|
    if orig_uid != owner_uid:
 | 
						|
        execute('chown', owner_uid, path, run_as_root=True)
 | 
						|
    try:
 | 
						|
        yield
 | 
						|
    finally:
 | 
						|
        if orig_uid != owner_uid:
 | 
						|
            execute('chown', orig_uid, path, run_as_root=True)
 | 
						|
 | 
						|
 | 
						|
@contextlib.contextmanager
 | 
						|
def tempdir(**kwargs):
 | 
						|
    argdict = kwargs.copy()
 | 
						|
    if 'dir' not in argdict:
 | 
						|
        argdict['dir'] = CONF.tempdir
 | 
						|
    tmpdir = tempfile.mkdtemp(**argdict)
 | 
						|
    try:
 | 
						|
        yield tmpdir
 | 
						|
    finally:
 | 
						|
        try:
 | 
						|
            shutil.rmtree(tmpdir)
 | 
						|
        except OSError as e:
 | 
						|
            LOG.error(_('Could not remove tmpdir: %s'), str(e))
 | 
						|
 | 
						|
 | 
						|
def walk_class_hierarchy(clazz, encountered=None):
 | 
						|
    """Walk class hierarchy, yielding most derived classes first."""
 | 
						|
    if not encountered:
 | 
						|
        encountered = []
 | 
						|
    for subclass in clazz.__subclasses__():
 | 
						|
        if subclass not in encountered:
 | 
						|
            encountered.append(subclass)
 | 
						|
            # drill down to leaves first
 | 
						|
            for subsubclass in walk_class_hierarchy(subclass, encountered):
 | 
						|
                yield subsubclass
 | 
						|
            yield subclass
 | 
						|
 | 
						|
 | 
						|
class UndoManager(object):
 | 
						|
    """Provides a mechanism to facilitate rolling back a series of actions
 | 
						|
    when an exception is raised.
 | 
						|
    """
 | 
						|
    def __init__(self):
 | 
						|
        self.undo_stack = []
 | 
						|
 | 
						|
    def undo_with(self, undo_func):
 | 
						|
        self.undo_stack.append(undo_func)
 | 
						|
 | 
						|
    def _rollback(self):
 | 
						|
        for undo_func in reversed(self.undo_stack):
 | 
						|
            undo_func()
 | 
						|
 | 
						|
    def rollback_and_reraise(self, msg=None, **kwargs):
 | 
						|
        """Rollback a series of actions then re-raise the exception.
 | 
						|
 | 
						|
        .. note:: (sirp) This should only be called within an
 | 
						|
                  exception handler.
 | 
						|
        """
 | 
						|
        with excutils.save_and_reraise_exception():
 | 
						|
            if msg:
 | 
						|
                LOG.exception(msg, **kwargs)
 | 
						|
 | 
						|
            self._rollback()
 | 
						|
 | 
						|
 | 
						|
def mkfs(fs, path, label=None):
 | 
						|
    """Format a file or block device
 | 
						|
 | 
						|
    :param fs: Filesystem type (examples include 'swap', 'ext3', 'ext4'
 | 
						|
               'btrfs', etc.)
 | 
						|
    :param path: Path to file or block device to format
 | 
						|
    :param label: Volume label to use
 | 
						|
    """
 | 
						|
    if fs == 'swap':
 | 
						|
        args = ['mkswap']
 | 
						|
    else:
 | 
						|
        args = ['mkfs', '-t', fs]
 | 
						|
    #add -F to force no interactive execute on non-block device.
 | 
						|
    if fs in ('ext3', 'ext4'):
 | 
						|
        args.extend(['-F'])
 | 
						|
    if label:
 | 
						|
        if fs in ('msdos', 'vfat'):
 | 
						|
            label_opt = '-n'
 | 
						|
        else:
 | 
						|
            label_opt = '-L'
 | 
						|
        args.extend([label_opt, label])
 | 
						|
    args.append(path)
 | 
						|
    execute(*args)
 | 
						|
 | 
						|
 | 
						|
def last_bytes(file_like_object, num):
 | 
						|
    """Return num bytes from the end of the file, and remaining byte count.
 | 
						|
 | 
						|
    :param file_like_object: The file to read
 | 
						|
    :param num: The number of bytes to return
 | 
						|
 | 
						|
    :returns (data, remaining)
 | 
						|
    """
 | 
						|
 | 
						|
    try:
 | 
						|
        file_like_object.seek(-num, os.SEEK_END)
 | 
						|
    except IOError as e:
 | 
						|
        if e.errno == 22:
 | 
						|
            file_like_object.seek(0, os.SEEK_SET)
 | 
						|
        else:
 | 
						|
            raise
 | 
						|
 | 
						|
    remaining = file_like_object.tell()
 | 
						|
    return (file_like_object.read(), remaining)
 | 
						|
 | 
						|
 | 
						|
def metadata_to_dict(metadata):
 | 
						|
    result = {}
 | 
						|
    for item in metadata:
 | 
						|
        if not item.get('deleted'):
 | 
						|
            result[item['key']] = item['value']
 | 
						|
    return result
 | 
						|
 | 
						|
 | 
						|
def dict_to_metadata(metadata):
 | 
						|
    result = []
 | 
						|
    for key, value in metadata.iteritems():
 | 
						|
        result.append(dict(key=key, value=value))
 | 
						|
    return result
 | 
						|
 | 
						|
 | 
						|
def instance_meta(instance):
 | 
						|
    if isinstance(instance['metadata'], dict):
 | 
						|
        return instance['metadata']
 | 
						|
    else:
 | 
						|
        return metadata_to_dict(instance['metadata'])
 | 
						|
 | 
						|
 | 
						|
def instance_sys_meta(instance):
 | 
						|
    if isinstance(instance['system_metadata'], dict):
 | 
						|
        return instance['system_metadata']
 | 
						|
    else:
 | 
						|
        return metadata_to_dict(instance['system_metadata'])
 | 
						|
 | 
						|
 | 
						|
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)
 | 
						|
 | 
						|
 | 
						|
class ExceptionHelper(object):
 | 
						|
    """Class to wrap another and translate the ClientExceptions raised by its
 | 
						|
    function calls to the actual ones.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, target):
 | 
						|
        self._target = target
 | 
						|
 | 
						|
    def __getattr__(self, name):
 | 
						|
        func = getattr(self._target, name)
 | 
						|
 | 
						|
        @functools.wraps(func)
 | 
						|
        def wrapper(*args, **kwargs):
 | 
						|
            try:
 | 
						|
                return func(*args, **kwargs)
 | 
						|
            except rpc_common.ClientException as e:
 | 
						|
                raise (e._exc_info[1], None, e._exc_info[2])
 | 
						|
        return wrapper
 | 
						|
 | 
						|
 | 
						|
def check_string_length(value, name, min_length=0, max_length=None):
 | 
						|
    """Check the length of specified string
 | 
						|
    :param value: the value of the string
 | 
						|
    :param name: the name of the string
 | 
						|
    :param min_length: the min_length of the string
 | 
						|
    :param max_length: the max_length of the string
 | 
						|
    """
 | 
						|
    if not isinstance(value, basestring):
 | 
						|
        msg = _("%s is not a string or unicode") % name
 | 
						|
        raise exception.InvalidInput(message=msg)
 | 
						|
 | 
						|
    if len(value) < min_length:
 | 
						|
        msg = _("%(name)s has a minimum character requirement of "
 | 
						|
                "%(min_length)s.") % {'name': name, 'min_length': min_length}
 | 
						|
        raise exception.InvalidInput(message=msg)
 | 
						|
 | 
						|
    if max_length and len(value) > max_length:
 | 
						|
        msg = _("%(name)s has more than %(max_length)s "
 | 
						|
                "characters.") % {'name': name, 'max_length': max_length}
 | 
						|
        raise exception.InvalidInput(message=msg)
 | 
						|
 | 
						|
 | 
						|
def validate_integer(value, name, min_value=None, max_value=None):
 | 
						|
    """Make sure that value is a valid integer, potentially within range."""
 | 
						|
    try:
 | 
						|
        value = int(str(value))
 | 
						|
    except ValueError:
 | 
						|
        msg = _('%(value_name)s must be an integer')
 | 
						|
        raise exception.InvalidInput(reason=(
 | 
						|
            msg % {'value_name': name}))
 | 
						|
 | 
						|
    if min_value is not None:
 | 
						|
        if value < min_value:
 | 
						|
            msg = _('%(value_name)s must be >= %(min_value)d')
 | 
						|
            raise exception.InvalidInput(
 | 
						|
                reason=(msg % {'value_name': name,
 | 
						|
                               'min_value': min_value}))
 | 
						|
    if max_value is not None:
 | 
						|
        if value > max_value:
 | 
						|
            msg = _('%(value_name)s must be <= %(max_value)d')
 | 
						|
            raise exception.InvalidInput(
 | 
						|
                reason=(
 | 
						|
                    msg % {'value_name': name,
 | 
						|
                           'max_value': max_value})
 | 
						|
            )
 | 
						|
    return value
 | 
						|
 | 
						|
 | 
						|
def spawn_n(func, *args, **kwargs):
 | 
						|
    """Passthrough method for eventlet.spawn_n.
 | 
						|
 | 
						|
    This utility exists so that it can be stubbed for testing without
 | 
						|
    interfering with the service spawns.
 | 
						|
    """
 | 
						|
    eventlet.spawn_n(func, *args, **kwargs)
 | 
						|
 | 
						|
 | 
						|
def is_none_string(val):
 | 
						|
    """
 | 
						|
    Check if a string represents a None value.
 | 
						|
    """
 | 
						|
    if not isinstance(val, basestring):
 | 
						|
        return False
 | 
						|
 | 
						|
    return val.lower() == 'none'
 | 
						|
 | 
						|
 | 
						|
def convert_version_to_int(version):
 | 
						|
    return version[0] * 1000000 + version[1] * 1000 + version[2]
 | 
						|
 | 
						|
 | 
						|
def is_neutron():
 | 
						|
    global _IS_NEUTRON_ATTEMPTED
 | 
						|
    global _IS_NEUTRON
 | 
						|
 | 
						|
    if _IS_NEUTRON_ATTEMPTED:
 | 
						|
        return _IS_NEUTRON
 | 
						|
 | 
						|
    try:
 | 
						|
        # compatibility with Folsom/Grizzly configs
 | 
						|
        cls_name = CONF.network_api_class
 | 
						|
        if cls_name == 'nova.network.quantumv2.api.API':
 | 
						|
            cls_name = 'nova.network.neutronv2.api.API'
 | 
						|
        _IS_NEUTRON_ATTEMPTED = True
 | 
						|
 | 
						|
        from nova.network.neutronv2 import api as neutron_api
 | 
						|
        _IS_NEUTRON = issubclass(importutils.import_class(cls_name),
 | 
						|
                                 neutron_api.API)
 | 
						|
    except ImportError:
 | 
						|
        _IS_NEUTRON = False
 | 
						|
 | 
						|
    return _IS_NEUTRON
 | 
						|
 | 
						|
 | 
						|
def reset_is_neutron():
 | 
						|
    global _IS_NEUTRON_ATTEMPTED
 | 
						|
    global _IS_NEUTRON
 | 
						|
 | 
						|
    _IS_NEUTRON_ATTEMPTED = False
 | 
						|
    _IS_NEUTRON = False
 |