~0x44/nova/extdoc

« back to all changes in this revision

Viewing changes to nova/utils.py

  • Committer: NTT PF Lab.
  • Date: 2010-12-24 11:38:49 UTC
  • mto: This revision was merged to the branch mainline in revision 564.
  • Revision ID: openstack@lab.ntt.co.jp-20101224113849-z9nemzmki17bxnvw
SupportĀ IPv6

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 
3
3
# Copyright 2010 United States Government as represented by the
4
4
# Administrator of the National Aeronautics and Space Administration.
5
 
# Copyright 2011 Justin Santa Barbara
6
5
# All Rights Reserved.
7
6
#
8
7
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
17
16
#    License for the specific language governing permissions and limitations
18
17
#    under the License.
19
18
 
20
 
"""Utilities and helper functions."""
 
19
"""
 
20
System-level utilities and helper functions.
 
21
"""
21
22
 
22
23
import datetime
23
24
import functools
24
25
import inspect
25
 
import json
26
 
import lockfile
27
 
import netaddr
 
26
import logging
28
27
import os
29
28
import random
30
 
import re
31
 
import shlex
 
29
import subprocess
32
30
import socket
33
 
import struct
34
31
import sys
35
 
import time
36
 
import types
37
 
import uuid
38
 
import pyclbr
39
32
from xml.sax import saxutils
 
33
import re
 
34
import netaddr
40
35
 
41
 
from eventlet import event
42
 
from eventlet import greenthread
43
 
from eventlet import semaphore
44
 
from eventlet.green import subprocess
 
36
from twisted.internet.threads import deferToThread
45
37
 
46
38
from nova import exception
47
39
from nova import flags
48
 
from nova import log as logging
49
 
from nova import version
50
 
 
51
 
 
52
 
LOG = logging.getLogger("nova.utils")
53
 
ISO_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
54
 
PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
 
40
from nova.exception import ProcessExecutionError
 
41
 
 
42
 
55
43
FLAGS = flags.FLAGS
 
44
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
56
45
 
57
46
 
58
47
def import_class(import_str):
59
 
    """Returns a class from a string including module and class."""
 
48
    """Returns a class from a string including module and class"""
60
49
    mod_str, _sep, class_str = import_str.rpartition('.')
 
50
    logging.debug(import_str)
61
51
    try:
62
52
        __import__(mod_str)
63
53
        return getattr(sys.modules[mod_str], class_str)
64
 
    except (ImportError, ValueError, AttributeError), exc:
65
 
        LOG.debug(_('Inner Exception: %s'), exc)
66
 
        raise exception.ClassNotFound(class_name=class_str)
 
54
    except (ImportError, ValueError, AttributeError):
 
55
        logging.debug(ImportError)
 
56
        logging.debug(ValueError)
 
57
        logging.debug(AttributeError)
 
58
        raise exception.NotFound('Class %s cannot be found' % class_str)
67
59
 
68
60
 
69
61
def import_object(import_str):
70
 
    """Returns an object including a module or module and class."""
 
62
    """Returns an object including a module or module and class"""
71
63
    try:
72
64
        __import__(import_str)
73
65
        return sys.modules[import_str]
76
68
        return cls()
77
69
 
78
70
 
79
 
def vpn_ping(address, port, timeout=0.05, session_id=None):
80
 
    """Sends a vpn negotiation packet and returns the server session.
81
 
 
82
 
    Returns False on a failure. Basic packet structure is below.
83
 
 
84
 
    Client packet (14 bytes)::
85
 
     0 1      8 9  13
86
 
    +-+--------+-----+
87
 
    |x| cli_id |?????|
88
 
    +-+--------+-----+
89
 
    x = packet identifier 0x38
90
 
    cli_id = 64 bit identifier
91
 
    ? = unknown, probably flags/padding
92
 
 
93
 
    Server packet (26 bytes)::
94
 
     0 1      8 9  13 14    21 2225
95
 
    +-+--------+-----+--------+----+
96
 
    |x| srv_id |?????| cli_id |????|
97
 
    +-+--------+-----+--------+----+
98
 
    x = packet identifier 0x40
99
 
    cli_id = 64 bit identifier
100
 
    ? = unknown, probably flags/padding
101
 
    bit 9 was 1 and the rest were 0 in testing
102
 
 
103
 
    """
104
 
    if session_id is None:
105
 
        session_id = random.randint(0, 0xffffffffffffffff)
106
 
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
107
 
    data = struct.pack('!BQxxxxxx', 0x38, session_id)
108
 
    sock.sendto(data, (address, port))
109
 
    sock.settimeout(timeout)
110
 
    try:
111
 
        received = sock.recv(2048)
112
 
    except socket.timeout:
113
 
        return False
114
 
    finally:
115
 
        sock.close()
116
 
    fmt = '!BQxxxxxQxxxx'
117
 
    if len(received) != struct.calcsize(fmt):
118
 
        print struct.calcsize(fmt)
119
 
        return False
120
 
    (identifier, server_sess, client_sess) = struct.unpack(fmt, received)
121
 
    if identifier == 0x40 and client_sess == session_id:
122
 
        return server_sess
123
 
 
124
 
 
125
71
def fetchfile(url, target):
126
 
    LOG.debug(_('Fetching %s') % url)
127
 
    execute('curl', '--fail', url, '-o', target)
128
 
 
129
 
 
130
 
def execute(*cmd, **kwargs):
131
 
    """
132
 
    Helper method to execute command with optional retry.
133
 
 
134
 
    :cmd                Passed to subprocess.Popen.
135
 
    :process_input      Send to opened process.
136
 
    :check_exit_code    Defaults to 0. Raise exception.ProcessExecutionError
137
 
                        unless program exits with this code.
138
 
    :delay_on_retry     True | False. Defaults to True. If set to True, wait a
139
 
                        short amount of time before retrying.
140
 
    :attempts           How many times to retry cmd.
141
 
    :run_as_root        True | False. Defaults to False. If set to True,
142
 
                        the command is prefixed by the command specified
143
 
                        in the root_helper FLAG.
144
 
 
145
 
    :raises exception.Error on receiving unknown arguments
146
 
    :raises exception.ProcessExecutionError
147
 
    """
148
 
 
149
 
    process_input = kwargs.pop('process_input', None)
150
 
    check_exit_code = kwargs.pop('check_exit_code', 0)
151
 
    delay_on_retry = kwargs.pop('delay_on_retry', True)
152
 
    attempts = kwargs.pop('attempts', 1)
153
 
    run_as_root = kwargs.pop('run_as_root', False)
154
 
    if len(kwargs):
155
 
        raise exception.Error(_('Got unknown keyword args '
156
 
                                'to utils.execute: %r') % kwargs)
157
 
 
158
 
    if run_as_root:
159
 
        cmd = shlex.split(FLAGS.root_helper) + list(cmd)
160
 
    cmd = map(str, cmd)
161
 
 
162
 
    while attempts > 0:
163
 
        attempts -= 1
164
 
        try:
165
 
            LOG.debug(_('Running cmd (subprocess): %s'), ' '.join(cmd))
166
 
            _PIPE = subprocess.PIPE  # pylint: disable=E1101
167
 
            obj = subprocess.Popen(cmd,
168
 
                                   stdin=_PIPE,
169
 
                                   stdout=_PIPE,
170
 
                                   stderr=_PIPE,
171
 
                                   close_fds=True)
172
 
            result = None
173
 
            if process_input is not None:
174
 
                result = obj.communicate(process_input)
175
 
            else:
176
 
                result = obj.communicate()
177
 
            obj.stdin.close()  # pylint: disable=E1101
178
 
            _returncode = obj.returncode  # pylint: disable=E1101
179
 
            if _returncode:
180
 
                LOG.debug(_('Result was %s') % _returncode)
181
 
                if type(check_exit_code) == types.IntType \
182
 
                        and _returncode != check_exit_code:
183
 
                    (stdout, stderr) = result
184
 
                    raise exception.ProcessExecutionError(
185
 
                            exit_code=_returncode,
186
 
                            stdout=stdout,
187
 
                            stderr=stderr,
188
 
                            cmd=' '.join(cmd))
189
 
            return result
190
 
        except exception.ProcessExecutionError:
191
 
            if not attempts:
192
 
                raise
193
 
            else:
194
 
                LOG.debug(_('%r failed. Retrying.'), cmd)
195
 
                if delay_on_retry:
196
 
                    greenthread.sleep(random.randint(20, 200) / 100.0)
197
 
        finally:
198
 
            # NOTE(termie): this appears to be necessary to let the subprocess
199
 
            #               call clean something up in between calls, without
200
 
            #               it two execute calls in a row hangs the second one
201
 
            greenthread.sleep(0)
202
 
 
203
 
 
204
 
def ssh_execute(ssh, cmd, process_input=None,
205
 
                addl_env=None, check_exit_code=True):
206
 
    LOG.debug(_('Running cmd (SSH): %s'), ' '.join(cmd))
 
72
    logging.debug("Fetching %s" % url)
 
73
#    c = pycurl.Curl()
 
74
#    fp = open(target, "wb")
 
75
#    c.setopt(c.URL, url)
 
76
#    c.setopt(c.WRITEDATA, fp)
 
77
#    c.perform()
 
78
#    c.close()
 
79
#    fp.close()
 
80
    execute("curl --fail %s -o %s" % (url, target))
 
81
 
 
82
 
 
83
def execute(cmd, process_input=None, addl_env=None, check_exit_code=True):
 
84
    logging.debug("Running cmd: %s", cmd)
 
85
    env = os.environ.copy()
207
86
    if addl_env:
208
 
        raise exception.Error(_('Environment not supported over SSH'))
209
 
 
210
 
    if process_input:
211
 
        # This is (probably) fixable if we need it...
212
 
        raise exception.Error(_('process_input not supported over SSH'))
213
 
 
214
 
    stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd)
215
 
    channel = stdout_stream.channel
216
 
 
217
 
    #stdin.write('process_input would go here')
218
 
    #stdin.flush()
219
 
 
220
 
    # NOTE(justinsb): This seems suspicious...
221
 
    # ...other SSH clients have buffering issues with this approach
222
 
    stdout = stdout_stream.read()
223
 
    stderr = stderr_stream.read()
224
 
    stdin_stream.close()
225
 
 
226
 
    exit_status = channel.recv_exit_status()
227
 
 
228
 
    # exit_status == -1 if no exit code was returned
229
 
    if exit_status != -1:
230
 
        LOG.debug(_('Result was %s') % exit_status)
231
 
        if check_exit_code and exit_status != 0:
232
 
            raise exception.ProcessExecutionError(exit_code=exit_status,
233
 
                                                  stdout=stdout,
234
 
                                                  stderr=stderr,
235
 
                                                  cmd=' '.join(cmd))
236
 
 
237
 
    return (stdout, stderr)
 
87
        env.update(addl_env)
 
88
    obj = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
 
89
        stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
 
90
    result = None
 
91
    if process_input != None:
 
92
        result = obj.communicate(process_input)
 
93
    else:
 
94
        result = obj.communicate()
 
95
    obj.stdin.close()
 
96
    if obj.returncode:
 
97
        logging.debug("Result was %s" % (obj.returncode))
 
98
        if check_exit_code and obj.returncode != 0:
 
99
            (stdout, stderr) = result
 
100
            raise ProcessExecutionError(exit_code=obj.returncode,
 
101
                                        stdout=stdout,
 
102
                                        stderr=stderr,
 
103
                                        cmd=cmd)
 
104
    return result
238
105
 
239
106
 
240
107
def abspath(s):
241
108
    return os.path.join(os.path.dirname(__file__), s)
242
109
 
243
110
 
244
 
def novadir():
245
 
    import nova
246
 
    return os.path.abspath(nova.__file__).split('nova/__init__.py')[0]
247
 
 
248
 
 
249
 
def default_flagfile(filename='nova.conf', args=None):
250
 
    if args is None:
251
 
        args = sys.argv
252
 
    for arg in args:
 
111
def default_flagfile(filename='nova.conf'):
 
112
    for arg in sys.argv:
253
113
        if arg.find('flagfile') != -1:
254
114
            break
255
115
    else:
257
117
            # turn relative filename into an absolute path
258
118
            script_dir = os.path.dirname(inspect.stack()[-1][1])
259
119
            filename = os.path.abspath(os.path.join(script_dir, filename))
260
 
        if not os.path.exists(filename):
261
 
            filename = "./nova.conf"
262
 
            if not os.path.exists(filename):
263
 
                filename = '/etc/nova/nova.conf'
264
120
        if os.path.exists(filename):
265
 
            flagfile = '--flagfile=%s' % filename
266
 
            args.insert(1, flagfile)
 
121
            flagfile = ['--flagfile=%s' % filename]
 
122
            sys.argv = sys.argv[:1] + flagfile + sys.argv[1:]
267
123
 
268
124
 
269
125
def debug(arg):
270
 
    LOG.debug(_('debug in callback: %s'), arg)
 
126
    logging.debug('debug in callback: %s', arg)
271
127
    return arg
272
128
 
273
129
 
274
 
def runthis(prompt, *cmd, **kwargs):
275
 
    LOG.debug(_('Running %s'), (' '.join(cmd)))
276
 
    rv, err = execute(*cmd, **kwargs)
 
130
def runthis(prompt, cmd, check_exit_code=True):
 
131
    logging.debug("Running %s" % (cmd))
 
132
    exit_code = subprocess.call(cmd.split(" "))
 
133
    logging.debug(prompt % (exit_code))
 
134
    if check_exit_code and exit_code != 0:
 
135
        raise ProcessExecutionError(exit_code=exit_code,
 
136
                                    stdout=None,
 
137
                                    stderr=None,
 
138
                                    cmd=cmd)
277
139
 
278
140
 
279
141
def generate_uid(topic, size=8):
282
144
    return '%s-%s' % (topic, ''.join(choices))
283
145
 
284
146
 
285
 
# Default symbols to use for passwords. Avoids visually confusing characters.
286
 
# ~6 bits per symbol
287
 
DEFAULT_PASSWORD_SYMBOLS = ('23456789'  # Removed: 0,1
288
 
                            'ABCDEFGHJKLMNPQRSTUVWXYZ'  # Removed: I, O
289
 
                            'abcdefghijkmnopqrstuvwxyz')  # Removed: l
290
 
 
291
 
 
292
 
# ~5 bits per symbol
293
 
EASIER_PASSWORD_SYMBOLS = ('23456789'  # Removed: 0, 1
294
 
                           'ABCDEFGHJKLMNPQRSTUVWXYZ')  # Removed: I, O
295
 
 
296
 
 
297
 
def usage_from_instance(instance_ref, **kw):
298
 
    usage_info = dict(
299
 
          project_id=instance_ref['project_id'],
300
 
          user_id=instance_ref['user_id'],
301
 
          instance_id=instance_ref['id'],
302
 
          instance_type=instance_ref['instance_type']['name'],
303
 
          instance_type_id=instance_ref['instance_type_id'],
304
 
          display_name=instance_ref['display_name'],
305
 
          created_at=str(instance_ref['created_at']),
306
 
          launched_at=str(instance_ref['launched_at']) \
307
 
                      if instance_ref['launched_at'] else '',
308
 
          image_ref=instance_ref['image_ref'])
309
 
    usage_info.update(kw)
310
 
    return usage_info
311
 
 
312
 
 
313
 
def generate_password(length=20, symbols=DEFAULT_PASSWORD_SYMBOLS):
314
 
    """Generate a random password from the supplied symbols.
315
 
 
316
 
    Believed to be reasonably secure (with a reasonable password length!)
317
 
 
318
 
    """
319
 
    r = random.SystemRandom()
320
 
    return ''.join([r.choice(symbols) for _i in xrange(length)])
 
147
def generate_mac():
 
148
    mac = [0x02, 0x16, 0x3e,
 
149
           random.randint(0x00, 0x7f),
 
150
           random.randint(0x00, 0xff),
 
151
           random.randint(0x00, 0xff)]
 
152
    return ':'.join(map(lambda x: "%02x" % x, mac))
321
153
 
322
154
 
323
155
def last_octet(address):
324
 
    return int(address.split('.')[-1])
 
156
    return int(address.split(".")[-1])
 
157
 
 
158
 
 
159
def get_my_ip():
 
160
    """Returns the actual ip of the local machine."""
 
161
    if getattr(FLAGS, 'fake_tests', None):
 
162
        return '127.0.0.1'
 
163
    try:
 
164
        csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 
165
        csock.connect(('8.8.8.8', 80))
 
166
        (addr, port) = csock.getsockname()
 
167
        csock.close()
 
168
        return addr
 
169
    except socket.gaierror as ex:
 
170
        logging.warn("Couldn't get IP, using 127.0.0.1 %s", ex)
 
171
        return "127.0.0.1"
325
172
 
326
173
 
327
174
def  get_my_linklocal(interface):
 
175
    if  getattr(FLAGS, 'fake_tests', None):
 
176
        return 'fe00::'
328
177
    try:
329
 
        if_str = execute('ip', '-f', 'inet6', '-o', 'addr', 'show', interface)
330
 
        condition = '\s+inet6\s+([0-9a-f:]+)/\d+\s+scope\s+link'
 
178
        if_str = execute("ifconfig %s" % interface)
 
179
        condition = "\s+inet6\s+addr:\s+([0-9a-f:]+/\d+)\s+Scope:Link"
331
180
        links = [re.search(condition, x) for x in if_str[0].split('\n')]
332
181
        address = [w.group(1) for w in links if w is not None]
333
182
        if address[0] is not None:
334
183
            return address[0]
335
184
        else:
336
 
            raise exception.Error(_('Link Local address is not found.:%s')
337
 
                                  % if_str)
338
 
    except Exception as ex:
339
 
        raise exception.Error(_("Couldn't get Link Local IP of %(interface)s"
340
 
                                " :%(ex)s") % locals())
341
 
 
342
 
 
343
 
def utcnow():
344
 
    """Overridable version of utils.utcnow."""
345
 
    if utcnow.override_time:
346
 
        return utcnow.override_time
347
 
    return datetime.datetime.utcnow()
348
 
 
349
 
 
350
 
utcnow.override_time = None
351
 
 
352
 
 
353
 
def is_older_than(before, seconds):
354
 
    """Return True if before is older than seconds."""
355
 
    return utcnow() - before > datetime.timedelta(seconds=seconds)
356
 
 
357
 
 
358
 
def utcnow_ts():
359
 
    """Timestamp version of our utcnow function."""
360
 
    return time.mktime(utcnow().timetuple())
361
 
 
362
 
 
363
 
def set_time_override(override_time=datetime.datetime.utcnow()):
364
 
    """Override utils.utcnow to return a constant time."""
365
 
    utcnow.override_time = override_time
366
 
 
367
 
 
368
 
def advance_time_delta(timedelta):
369
 
    """Advance overriden time using a datetime.timedelta."""
370
 
    assert(not utcnow.override_time is None)
371
 
    utcnow.override_time += timedelta
372
 
 
373
 
 
374
 
def advance_time_seconds(seconds):
375
 
    """Advance overriden time by seconds."""
376
 
    advance_time_delta(datetime.timedelta(0, seconds))
377
 
 
378
 
 
379
 
def clear_time_override():
380
 
    """Remove the overridden time."""
381
 
    utcnow.override_time = None
382
 
 
383
 
 
384
 
def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
385
 
    """Returns formatted utcnow."""
 
185
            return None
 
186
    except RuntimeError as ex:
 
187
        logging.warn("Couldn't get Link Local IP of %s :%s", interface, ex)
 
188
        return None
 
189
 
 
190
 
 
191
def to_global_ipv6(prefix, mac):
 
192
    mac64 = netaddr.EUI(mac).eui64().words
 
193
    int_addr = int(''.join(['%02x' % i for i in mac64]), 16)
 
194
    mac64_addr = netaddr.IPAddress(int_addr)
 
195
    maskIP = netaddr.IPNetwork(prefix).ip
 
196
    return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).format()
 
197
 
 
198
 
 
199
def to_mac(ipv6_address):
 
200
    address = netaddr.IPAddress(ipv6_address)
 
201
    mask1 = netaddr.IPAddress("::ffff:ffff:ffff:ffff")
 
202
    mask2 = netaddr.IPAddress("::0200:0:0:0")
 
203
    mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words
 
204
    return ":".join(["%02x" % i for i in mac64[0:3] + mac64[5:8]])
 
205
 
 
206
 
 
207
def isotime(at=None):
386
208
    if not at:
387
 
        at = utcnow()
388
 
    return at.strftime(fmt)
389
 
 
390
 
 
391
 
def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
392
 
    """Turn a formatted time back into a datetime."""
393
 
    return datetime.datetime.strptime(timestr, fmt)
394
 
 
395
 
 
396
 
def isotime(at=None):
397
 
    """Returns iso formatted utcnow."""
398
 
    return strtime(at, ISO_TIME_FORMAT)
 
209
        at = datetime.datetime.utcnow()
 
210
    return at.strftime(TIME_FORMAT)
399
211
 
400
212
 
401
213
def parse_isotime(timestr):
402
 
    """Turn an iso formatted time back into a datetime."""
403
 
    return parse_strtime(timestr, ISO_TIME_FORMAT)
 
214
    return datetime.datetime.strptime(timestr, TIME_FORMAT)
404
215
 
405
216
 
406
217
def parse_mailmap(mailmap='.mailmap'):
433
244
        if not self.__backend:
434
245
            backend_name = self.__pivot.value
435
246
            if backend_name not in self.__backends:
436
 
                raise exception.Error(_('Invalid backend: %s') % backend_name)
 
247
                raise exception.Error('Invalid backend: %s' % backend_name)
437
248
 
438
249
            backend = self.__backends[backend_name]
439
250
            if type(backend) == type(tuple()):
444
255
                fromlist = backend
445
256
 
446
257
            self.__backend = __import__(name, None, None, fromlist)
447
 
            LOG.debug(_('backend %s'), self.__backend)
 
258
            logging.info('backend %s', self.__backend)
448
259
        return self.__backend
449
260
 
450
261
    def __getattr__(self, key):
452
263
        return getattr(backend, key)
453
264
 
454
265
 
455
 
class LoopingCallDone(Exception):
456
 
    """Exception to break out and stop a LoopingCall.
457
 
 
458
 
    The poll-function passed to LoopingCall can raise this exception to
459
 
    break out of the loop normally. This is somewhat analogous to
460
 
    StopIteration.
461
 
 
462
 
    An optional return-value can be included as the argument to the exception;
463
 
    this return-value will be returned by LoopingCall.wait()
464
 
 
465
 
    """
466
 
 
467
 
    def __init__(self, retvalue=True):
468
 
        """:param retvalue: Value that LoopingCall.wait() should return."""
469
 
        self.retvalue = retvalue
470
 
 
471
 
 
472
 
class LoopingCall(object):
473
 
    def __init__(self, f=None, *args, **kw):
474
 
        self.args = args
475
 
        self.kw = kw
476
 
        self.f = f
477
 
        self._running = False
478
 
 
479
 
    def start(self, interval, now=True):
480
 
        self._running = True
481
 
        done = event.Event()
482
 
 
483
 
        def _inner():
484
 
            if not now:
485
 
                greenthread.sleep(interval)
486
 
            try:
487
 
                while self._running:
488
 
                    self.f(*self.args, **self.kw)
489
 
                    if not self._running:
490
 
                        break
491
 
                    greenthread.sleep(interval)
492
 
            except LoopingCallDone, e:
493
 
                self.stop()
494
 
                done.send(e.retvalue)
495
 
            except Exception:
496
 
                logging.exception('in looping call')
497
 
                done.send_exception(*sys.exc_info())
498
 
                return
499
 
            else:
500
 
                done.send(True)
501
 
 
502
 
        self.done = done
503
 
 
504
 
        greenthread.spawn(_inner)
505
 
        return self.done
506
 
 
507
 
    def stop(self):
508
 
        self._running = False
509
 
 
510
 
    def wait(self):
511
 
        return self.done.wait()
 
266
def deferredToThread(f):
 
267
    def g(*args, **kwargs):
 
268
        return deferToThread(f, *args, **kwargs)
 
269
    return g
512
270
 
513
271
 
514
272
def xhtml_escape(value):
518
276
    http://github.com/facebook/tornado/blob/master/tornado/escape.py
519
277
 
520
278
    """
521
 
    return saxutils.escape(value, {'"': '"'})
 
279
    return saxutils.escape(value, {'"': """})
522
280
 
523
281
 
524
282
def utf8(value):
529
287
 
530
288
    """
531
289
    if isinstance(value, unicode):
532
 
        return value.encode('utf-8')
 
290
        return value.encode("utf-8")
533
291
    assert isinstance(value, str)
534
292
    return value
535
 
 
536
 
 
537
 
def to_primitive(value, convert_instances=False, level=0):
538
 
    """Convert a complex object into primitives.
539
 
 
540
 
    Handy for JSON serialization. We can optionally handle instances,
541
 
    but since this is a recursive function, we could have cyclical
542
 
    data structures.
543
 
 
544
 
    To handle cyclical data structures we could track the actual objects
545
 
    visited in a set, but not all objects are hashable. Instead we just
546
 
    track the depth of the object inspections and don't go too deep.
547
 
 
548
 
    Therefore, convert_instances=True is lossy ... be aware.
549
 
 
550
 
    """
551
 
    nasty = [inspect.ismodule, inspect.isclass, inspect.ismethod,
552
 
             inspect.isfunction, inspect.isgeneratorfunction,
553
 
             inspect.isgenerator, inspect.istraceback, inspect.isframe,
554
 
             inspect.iscode, inspect.isbuiltin, inspect.isroutine,
555
 
             inspect.isabstract]
556
 
    for test in nasty:
557
 
        if test(value):
558
 
            return unicode(value)
559
 
 
560
 
    if level > 3:
561
 
        return '?'
562
 
 
563
 
    # The try block may not be necessary after the class check above,
564
 
    # but just in case ...
565
 
    try:
566
 
        if type(value) is type([]) or type(value) is type((None,)):
567
 
            o = []
568
 
            for v in value:
569
 
                o.append(to_primitive(v, convert_instances=convert_instances,
570
 
                                      level=level))
571
 
            return o
572
 
        elif type(value) is type({}):
573
 
            o = {}
574
 
            for k, v in value.iteritems():
575
 
                o[k] = to_primitive(v, convert_instances=convert_instances,
576
 
                                    level=level)
577
 
            return o
578
 
        elif isinstance(value, datetime.datetime):
579
 
            return str(value)
580
 
        elif hasattr(value, 'iteritems'):
581
 
            return to_primitive(dict(value.iteritems()),
582
 
                                convert_instances=convert_instances,
583
 
                                level=level)
584
 
        elif hasattr(value, '__iter__'):
585
 
            return to_primitive(list(value), level)
586
 
        elif convert_instances and hasattr(value, '__dict__'):
587
 
            # Likely an instance of something. Watch for cycles.
588
 
            # Ignore class member vars.
589
 
            return to_primitive(value.__dict__,
590
 
                                convert_instances=convert_instances,
591
 
                                level=level + 1)
592
 
        else:
593
 
            return value
594
 
    except TypeError, e:
595
 
        # Class objects are tricky since they may define something like
596
 
        # __iter__ defined but it isn't callable as list().
597
 
        return unicode(value)
598
 
 
599
 
 
600
 
def dumps(value):
601
 
    try:
602
 
        return json.dumps(value)
603
 
    except TypeError:
604
 
        pass
605
 
    return json.dumps(to_primitive(value))
606
 
 
607
 
 
608
 
def loads(s):
609
 
    return json.loads(s)
610
 
 
611
 
 
612
 
try:
613
 
    import anyjson
614
 
except ImportError:
615
 
    pass
616
 
else:
617
 
    anyjson._modules.append(("nova.utils", "dumps", TypeError,
618
 
                                           "loads", ValueError))
619
 
    anyjson.force_implementation("nova.utils")
620
 
 
621
 
 
622
 
_semaphores = {}
623
 
 
624
 
 
625
 
class _NoopContextManager(object):
626
 
    def __enter__(self):
627
 
        pass
628
 
 
629
 
    def __exit__(self, exc_type, exc_val, exc_tb):
630
 
        pass
631
 
 
632
 
 
633
 
def synchronized(name, external=False):
634
 
    """Synchronization decorator.
635
 
 
636
 
    Decorating a method like so:
637
 
    @synchronized('mylock')
638
 
    def foo(self, *args):
639
 
       ...
640
 
 
641
 
    ensures that only one thread will execute the bar method at a time.
642
 
 
643
 
    Different methods can share the same lock:
644
 
    @synchronized('mylock')
645
 
    def foo(self, *args):
646
 
       ...
647
 
 
648
 
    @synchronized('mylock')
649
 
    def bar(self, *args):
650
 
       ...
651
 
 
652
 
    This way only one of either foo or bar can be executing at a time.
653
 
 
654
 
    The external keyword argument denotes whether this lock should work across
655
 
    multiple processes. This means that if two different workers both run a
656
 
    a method decorated with @synchronized('mylock', external=True), only one
657
 
    of them will execute at a time.
658
 
 
659
 
    """
660
 
 
661
 
    def wrap(f):
662
 
        @functools.wraps(f)
663
 
        def inner(*args, **kwargs):
664
 
            # NOTE(soren): If we ever go natively threaded, this will be racy.
665
 
            #              See http://stackoverflow.com/questions/5390569/dyn\
666
 
            #              amically-allocating-and-destroying-mutexes
667
 
            if name not in _semaphores:
668
 
                _semaphores[name] = semaphore.Semaphore()
669
 
            sem = _semaphores[name]
670
 
            LOG.debug(_('Attempting to grab semaphore "%(lock)s" for method '
671
 
                        '"%(method)s"...' % {'lock': name,
672
 
                                             'method': f.__name__}))
673
 
            with sem:
674
 
                if external:
675
 
                    LOG.debug(_('Attempting to grab file lock "%(lock)s" for '
676
 
                                'method "%(method)s"...' %
677
 
                                {'lock': name, 'method': f.__name__}))
678
 
                    lock_file_path = os.path.join(FLAGS.lock_path,
679
 
                                                  'nova-%s.lock' % name)
680
 
                    lock = lockfile.FileLock(lock_file_path)
681
 
                else:
682
 
                    lock = _NoopContextManager()
683
 
 
684
 
                with lock:
685
 
                    retval = f(*args, **kwargs)
686
 
 
687
 
            # If no-one else is waiting for it, delete it.
688
 
            # See note about possible raciness above.
689
 
            if not sem.balance < 1:
690
 
                del _semaphores[name]
691
 
 
692
 
            return retval
693
 
        return inner
694
 
    return wrap
695
 
 
696
 
 
697
 
def get_from_path(items, path):
698
 
    """Returns a list of items matching the specified path.
699
 
 
700
 
    Takes an XPath-like expression e.g. prop1/prop2/prop3, and for each item
701
 
    in items, looks up items[prop1][prop2][prop3].  Like XPath, if any of the
702
 
    intermediate results are lists it will treat each list item individually.
703
 
    A 'None' in items or any child expressions will be ignored, this function
704
 
    will not throw because of None (anywhere) in items.  The returned list
705
 
    will contain no None values.
706
 
 
707
 
    """
708
 
    if path is None:
709
 
        raise exception.Error('Invalid mini_xpath')
710
 
 
711
 
    (first_token, sep, remainder) = path.partition('/')
712
 
 
713
 
    if first_token == '':
714
 
        raise exception.Error('Invalid mini_xpath')
715
 
 
716
 
    results = []
717
 
 
718
 
    if items is None:
719
 
        return results
720
 
 
721
 
    if not isinstance(items, types.ListType):
722
 
        # Wrap single objects in a list
723
 
        items = [items]
724
 
 
725
 
    for item in items:
726
 
        if item is None:
727
 
            continue
728
 
        get_method = getattr(item, 'get', None)
729
 
        if get_method is None:
730
 
            continue
731
 
        child = get_method(first_token)
732
 
        if child is None:
733
 
            continue
734
 
        if isinstance(child, types.ListType):
735
 
            # Flatten intermediate lists
736
 
            for x in child:
737
 
                results.append(x)
738
 
        else:
739
 
            results.append(child)
740
 
 
741
 
    if not sep:
742
 
        # No more tokens
743
 
        return results
744
 
    else:
745
 
        return get_from_path(results, remainder)
746
 
 
747
 
 
748
 
def flatten_dict(dict_, flattened=None):
749
 
    """Recursively flatten a nested dictionary."""
750
 
    flattened = flattened or {}
751
 
    for key, value in dict_.iteritems():
752
 
        if hasattr(value, 'iteritems'):
753
 
            flatten_dict(value, flattened)
754
 
        else:
755
 
            flattened[key] = value
756
 
    return flattened
757
 
 
758
 
 
759
 
def partition_dict(dict_, keys):
760
 
    """Return two dicts, one with `keys` the other with everything else."""
761
 
    intersection = {}
762
 
    difference = {}
763
 
    for key, value in dict_.iteritems():
764
 
        if key in keys:
765
 
            intersection[key] = value
766
 
        else:
767
 
            difference[key] = value
768
 
    return intersection, difference
769
 
 
770
 
 
771
 
def map_dict_keys(dict_, key_map):
772
 
    """Return a dict in which the dictionaries keys are mapped to new keys."""
773
 
    mapped = {}
774
 
    for key, value in dict_.iteritems():
775
 
        mapped_key = key_map[key] if key in key_map else key
776
 
        mapped[mapped_key] = value
777
 
    return mapped
778
 
 
779
 
 
780
 
def subset_dict(dict_, keys):
781
 
    """Return a dict that only contains a subset of keys."""
782
 
    subset = partition_dict(dict_, keys)[0]
783
 
    return subset
784
 
 
785
 
 
786
 
def check_isinstance(obj, cls):
787
 
    """Checks that obj is of type cls, and lets PyLint infer types."""
788
 
    if isinstance(obj, cls):
789
 
        return obj
790
 
    raise Exception(_('Expected object of type: %s') % (str(cls)))
791
 
    # TODO(justinsb): Can we make this better??
792
 
    return cls()  # Ugly PyLint hack
793
 
 
794
 
 
795
 
def parse_server_string(server_str):
796
 
    """
797
 
    Parses the given server_string and returns a list of host and port.
798
 
    If it's not a combination of host part and port, the port element
799
 
    is a null string. If the input is invalid expression, return a null
800
 
    list.
801
 
    """
802
 
    try:
803
 
        # First of all, exclude pure IPv6 address (w/o port).
804
 
        if netaddr.valid_ipv6(server_str):
805
 
            return (server_str, '')
806
 
 
807
 
        # Next, check if this is IPv6 address with a port number combination.
808
 
        if server_str.find("]:") != -1:
809
 
            (address, port) = server_str.replace('[', '', 1).split(']:')
810
 
            return (address, port)
811
 
 
812
 
        # Third, check if this is a combination of an address and a port
813
 
        if server_str.find(':') == -1:
814
 
            return (server_str, '')
815
 
 
816
 
        # This must be a combination of an address and a port
817
 
        (address, port) = server_str.split(':')
818
 
        return (address, port)
819
 
 
820
 
    except Exception:
821
 
        LOG.debug(_('Invalid server_string: %s' % server_str))
822
 
        return ('', '')
823
 
 
824
 
 
825
 
def gen_uuid():
826
 
    return uuid.uuid4()
827
 
 
828
 
 
829
 
def is_uuid_like(val):
830
 
    """For our purposes, a UUID is a string in canoical form:
831
 
 
832
 
        aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
833
 
    """
834
 
    if not isinstance(val, basestring):
835
 
        return False
836
 
    return (len(val) == 36) and (val.count('-') == 4)
837
 
 
838
 
 
839
 
def bool_from_str(val):
840
 
    """Convert a string representation of a bool into a bool value"""
841
 
 
842
 
    if not val:
843
 
        return False
844
 
    try:
845
 
        return True if int(val) else False
846
 
    except ValueError:
847
 
        return val.lower() == 'true'
848
 
 
849
 
 
850
 
def is_valid_ipv4(address):
851
 
    """valid the address strictly as per format xxx.xxx.xxx.xxx.
852
 
    where xxx is a value between 0 and 255.
853
 
    """
854
 
    parts = address.split(".")
855
 
    if len(parts) != 4:
856
 
        return False
857
 
    for item in parts:
858
 
        try:
859
 
            if not 0 <= int(item) <= 255:
860
 
                return False
861
 
        except ValueError:
862
 
            return False
863
 
    return True
864
 
 
865
 
 
866
 
def monkey_patch():
867
 
    """  If the Flags.monkey_patch set as True,
868
 
    this functuion patches a decorator
869
 
    for all functions in specified modules.
870
 
    You can set decorators for each modules
871
 
    using FLAGS.monkey_patch_modules.
872
 
    The format is "Module path:Decorator function".
873
 
    Example: 'nova.api.ec2.cloud:nova.notifier.api.notify_decorator'
874
 
 
875
 
    Parameters of the decorator is as follows.
876
 
    (See nova.notifier.api.notify_decorator)
877
 
 
878
 
    name - name of the function
879
 
    function - object of the function
880
 
    """
881
 
    # If FLAGS.monkey_patch is not True, this function do nothing.
882
 
    if not FLAGS.monkey_patch:
883
 
        return
884
 
    # Get list of modules and decorators
885
 
    for module_and_decorator in FLAGS.monkey_patch_modules:
886
 
        module, decorator_name = module_and_decorator.split(':')
887
 
        # import decorator function
888
 
        decorator = import_class(decorator_name)
889
 
        __import__(module)
890
 
        # Retrieve module information using pyclbr
891
 
        module_data = pyclbr.readmodule_ex(module)
892
 
        for key in module_data.keys():
893
 
            # set the decorator for the class methods
894
 
            if isinstance(module_data[key], pyclbr.Class):
895
 
                clz = import_class("%s.%s" % (module, key))
896
 
                for method, func in inspect.getmembers(clz, inspect.ismethod):
897
 
                    setattr(clz, method,\
898
 
                        decorator("%s.%s.%s" % (module, key, method), func))
899
 
            # set the decorator for the function
900
 
            if isinstance(module_data[key], pyclbr.Function):
901
 
                func = import_class("%s.%s" % (module, key))
902
 
                setattr(sys.modules[module], key,\
903
 
                    decorator("%s.%s" % (module, key), func))
904
 
 
905
 
 
906
 
def convert_to_list_dict(lst, label):
907
 
    """Convert a value or list into a list of dicts"""
908
 
    if not lst:
909
 
        return None
910
 
    if not isinstance(lst, list):
911
 
        lst = [lst]
912
 
    return [{label: x} for x in lst]