~ubuntu-branches/ubuntu/raring/nova/raring-proposed

« back to all changes in this revision

Viewing changes to nova/utils.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Adam Gandelman, Chuck Short
  • Date: 2012-11-23 09:04:58 UTC
  • mfrom: (1.1.66)
  • Revision ID: package-import@ubuntu.com-20121123090458-91565o7aev1i1h71
Tags: 2013.1~g1-0ubuntu1
[ Adam Gandelman ]
* debian/control: Ensure novaclient is upgraded with nova,
  require python-keystoneclient >= 1:2.9.0. (LP: #1073289)
* debian/patches/{ubuntu/*, rbd-security.patch}: Dropped, applied
  upstream.
* debian/control: Add python-testtools to Build-Depends.

[ Chuck Short ]
* New upstream version.
* Refreshed debian/patches/avoid_setuptools_git_dependency.patch.
* debian/rules: FTBFS if missing binaries.
* debian/nova-scheudler.install: Add missing rabbit-queues and
  nova-rpc-zmq-receiver.
* Remove nova-volume since it doesnt exist anymore, transition to cinder-*.
* debian/rules: install apport hook in the right place.
* debian/patches/ubuntu-show-tests.patch: Display test failures.
* debian/control: Add depends on genisoimage
* debian/control: Suggest guestmount.
* debian/control: Suggest websockify. (LP: #1076442)
* debian/nova.conf: Disable nova-volume service.
* debian/control: Depend on xen-system-* rather than the hypervisor.
* debian/control, debian/mans/nova-conductor.8, debian/nova-conductor.init,
  debian/nova-conductor.install, debian/nova-conductor.logrotate
  debian/nova-conductor.manpages, debian/nova-conductor.postrm
  debian/nova-conductor.upstart.in: Add nova-conductor service.
* debian/control: Add python-fixtures as a build deps.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
import re
32
32
import shlex
33
33
import shutil
 
34
import signal
34
35
import socket
35
36
import struct
36
37
import sys
37
38
import tempfile
38
39
import time
39
 
import uuid
40
40
import weakref
41
41
from xml.sax import saxutils
42
42
 
46
46
from eventlet import semaphore
47
47
import netaddr
48
48
 
49
 
from nova.common import deprecated
50
49
from nova import exception
51
 
from nova import flags
52
50
from nova.openstack.common import cfg
53
51
from nova.openstack.common import excutils
54
52
from nova.openstack.common import importutils
57
55
 
58
56
 
59
57
LOG = logging.getLogger(__name__)
60
 
FLAGS = flags.FLAGS
61
 
 
62
 
FLAGS.register_opt(
 
58
CONF = cfg.CONF
 
59
CONF.register_opt(
63
60
    cfg.BoolOpt('disable_process_locking', default=False,
64
61
                help='Whether to disable inter-process locks'))
 
62
CONF.import_opt('glance_port', 'nova.config')
 
63
CONF.import_opt('instance_usage_audit_period', 'nova.config')
 
64
CONF.import_opt('monkey_patch', 'nova.config')
 
65
CONF.import_opt('rootwrap_config', 'nova.config')
 
66
CONF.import_opt('service_down_time', 'nova.config')
 
67
 
 
68
# Used for looking up extensions of text
 
69
# to their 'multiplied' byte amount
 
70
BYTE_MULTIPLIERS = {
 
71
    '': 1,
 
72
    't': 1024 ** 4,
 
73
    'g': 1024 ** 3,
 
74
    'm': 1024 ** 2,
 
75
    'k': 1024,
 
76
}
65
77
 
66
78
 
67
79
def vpn_ping(address, port, timeout=0.05, session_id=None):
112
124
        return server_sess
113
125
 
114
126
 
 
127
def _subprocess_setup():
 
128
    # Python installs a SIGPIPE handler by default. This is usually not what
 
129
    # non-Python subprocesses expect.
 
130
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
 
131
 
 
132
 
115
133
def execute(*cmd, **kwargs):
116
134
    """Helper method to execute command with optional retry.
117
135
 
129
147
                               before retrying.
130
148
    :param attempts:           How many times to retry cmd.
131
149
    :param run_as_root:        True | False. Defaults to False. If set to True,
132
 
                               the command is prefixed by the command specified
133
 
                               in the root_helper FLAG.
 
150
                               the command is run with rootwrap.
134
151
 
135
152
    :raises exception.NovaException: on receiving unknown arguments
136
153
    :raises exception.ProcessExecutionError:
155
172
        raise exception.NovaException(_('Got unknown keyword args '
156
173
                                        'to utils.execute: %r') % kwargs)
157
174
 
158
 
    if run_as_root:
159
 
 
160
 
        if FLAGS.rootwrap_config is None or FLAGS.root_helper != 'sudo':
161
 
            deprecated.warn(_('The root_helper option (which lets you specify '
162
 
                              'a root wrapper different from nova-rootwrap, '
163
 
                              'and defaults to using sudo) is now deprecated. '
164
 
                              'You should use the rootwrap_config option '
165
 
                              'instead.'))
166
 
 
167
 
        if (FLAGS.rootwrap_config is not None):
168
 
            cmd = ['sudo', 'nova-rootwrap', FLAGS.rootwrap_config] + list(cmd)
169
 
        else:
170
 
            cmd = shlex.split(FLAGS.root_helper) + list(cmd)
 
175
    if run_as_root and os.geteuid() != 0:
 
176
        cmd = ['sudo', 'nova-rootwrap', CONF.rootwrap_config] + list(cmd)
 
177
 
171
178
    cmd = map(str, cmd)
172
179
 
173
180
    while attempts > 0:
175
182
        try:
176
183
            LOG.debug(_('Running cmd (subprocess): %s'), ' '.join(cmd))
177
184
            _PIPE = subprocess.PIPE  # pylint: disable=E1101
 
185
 
 
186
            if os.name == 'nt':
 
187
                preexec_fn = None
 
188
                close_fds = False
 
189
            else:
 
190
                preexec_fn = _subprocess_setup
 
191
                close_fds = True
 
192
 
178
193
            obj = subprocess.Popen(cmd,
179
194
                                   stdin=_PIPE,
180
195
                                   stdout=_PIPE,
181
196
                                   stderr=_PIPE,
182
 
                                   close_fds=True,
 
197
                                   close_fds=close_fds,
 
198
                                   preexec_fn=preexec_fn,
183
199
                                   shell=shell)
184
200
            result = None
185
201
            if process_input is not None:
241
257
 
242
258
def ssh_execute(ssh, cmd, process_input=None,
243
259
                addl_env=None, check_exit_code=True):
244
 
    LOG.debug(_('Running cmd (SSH): %s'), ' '.join(cmd))
 
260
    LOG.debug(_('Running cmd (SSH): %s'), cmd)
245
261
    if addl_env:
246
262
        raise exception.NovaException(_('Environment not supported over SSH'))
247
263
 
271
287
            raise exception.ProcessExecutionError(exit_code=exit_status,
272
288
                                                  stdout=stdout,
273
289
                                                  stderr=stderr,
274
 
                                                  cmd=' '.join(cmd))
 
290
                                                  cmd=cmd)
275
291
 
276
292
    return (stdout, stderr)
277
293
 
324
340
              The begin timestamp of this audit period is the same as the
325
341
              end of the previous."""
326
342
    if not unit:
327
 
        unit = FLAGS.instance_usage_audit_period
 
343
        unit = CONF.instance_usage_audit_period
328
344
 
329
345
    offset = 0
330
346
    if '@' in unit:
477
493
 
478
494
    def __get_backend(self):
479
495
        if not self.__backend:
480
 
            backend_name = FLAGS[self.__pivot]
 
496
            backend_name = CONF[self.__pivot]
481
497
            if backend_name not in self.__backends:
482
498
                msg = _('Invalid backend: %s') % backend_name
483
499
                raise exception.NovaException(msg)
491
507
                fromlist = backend
492
508
 
493
509
            self.__backend = __import__(name, None, None, fromlist)
494
 
            LOG.debug(_('backend %s'), self.__backend)
495
510
        return self.__backend
496
511
 
497
512
    def __getattr__(self, key):
579
594
    return value
580
595
 
581
596
 
582
 
class _InterProcessLock(object):
583
 
    """Lock implementation which allows multiple locks, working around
584
 
    issues like bugs.debian.org/cgi-bin/bugreport.cgi?bug=632857 and does
585
 
    not require any cleanup. Since the lock is always held on a file
586
 
    descriptor rather than outside of the process, the lock gets dropped
587
 
    automatically if the process crashes, even if __exit__ is not executed.
588
 
 
589
 
    There are no guarantees regarding usage by multiple green threads in a
590
 
    single process here. This lock works only between processes. Exclusive
591
 
    access between local threads should be achieved using the semaphores
592
 
    in the @synchronized decorator.
593
 
 
594
 
    Note these locks are released when the descriptor is closed, so it's not
595
 
    safe to close the file descriptor while another green thread holds the
596
 
    lock. Just opening and closing the lock file can break synchronisation,
597
 
    so lock files must be accessed only using this abstraction.
598
 
    """
599
 
 
600
 
    def __init__(self, name):
601
 
        self.lockfile = None
602
 
        self.fname = name
603
 
 
604
 
    def __enter__(self):
605
 
        self.lockfile = open(self.fname, 'w')
606
 
 
607
 
        while True:
608
 
            try:
609
 
                # Using non-blocking locks since green threads are not
610
 
                # patched to deal with blocking locking calls.
611
 
                # Also upon reading the MSDN docs for locking(), it seems
612
 
                # to have a laughable 10 attempts "blocking" mechanism.
613
 
                self.trylock()
614
 
                return self
615
 
            except IOError, e:
616
 
                if e.errno in (errno.EACCES, errno.EAGAIN):
617
 
                    # external locks synchronise things like iptables
618
 
                    # updates - give it some time to prevent busy spinning
619
 
                    time.sleep(0.01)
620
 
                else:
621
 
                    raise
622
 
 
623
 
    def __exit__(self, exc_type, exc_val, exc_tb):
624
 
        try:
625
 
            self.unlock()
626
 
            self.lockfile.close()
627
 
        except IOError:
628
 
            LOG.exception(_("Could not release the acquired lock `%s`")
629
 
                             % self.fname)
630
 
 
631
 
    def trylock(self):
632
 
        raise NotImplementedError()
633
 
 
634
 
    def unlock(self):
635
 
        raise NotImplementedError()
636
 
 
637
 
 
638
 
class _WindowsLock(_InterProcessLock):
639
 
    def trylock(self):
640
 
        msvcrt.locking(self.lockfile, msvcrt.LK_NBLCK, 1)
641
 
 
642
 
    def unlock(self):
643
 
        msvcrt.locking(self.lockfile, msvcrt.LK_UNLCK, 1)
644
 
 
645
 
 
646
 
class _PosixLock(_InterProcessLock):
647
 
    def trylock(self):
648
 
        fcntl.lockf(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
649
 
 
650
 
    def unlock(self):
651
 
        fcntl.lockf(self.lockfile, fcntl.LOCK_UN)
652
 
 
653
 
 
654
 
if os.name == 'nt':
655
 
    import msvcrt
656
 
    InterProcessLock = _WindowsLock
657
 
else:
658
 
    import fcntl
659
 
    InterProcessLock = _PosixLock
660
 
 
661
 
_semaphores = weakref.WeakValueDictionary()
662
 
 
663
 
 
664
 
def synchronized(name, external=False, lock_path=None):
665
 
    """Synchronization decorator.
666
 
 
667
 
    Decorating a method like so::
668
 
 
669
 
        @synchronized('mylock')
670
 
        def foo(self, *args):
671
 
           ...
672
 
 
673
 
    ensures that only one thread will execute the bar method at a time.
674
 
 
675
 
    Different methods can share the same lock::
676
 
 
677
 
        @synchronized('mylock')
678
 
        def foo(self, *args):
679
 
           ...
680
 
 
681
 
        @synchronized('mylock')
682
 
        def bar(self, *args):
683
 
           ...
684
 
 
685
 
    This way only one of either foo or bar can be executing at a time.
686
 
 
687
 
    The external keyword argument denotes whether this lock should work across
688
 
    multiple processes. This means that if two different workers both run a
689
 
    a method decorated with @synchronized('mylock', external=True), only one
690
 
    of them will execute at a time.
691
 
 
692
 
    The lock_path keyword argument is used to specify a special location for
693
 
    external lock files to live. If nothing is set, then FLAGS.lock_path is
694
 
    used as a default.
695
 
    """
696
 
 
697
 
    def wrap(f):
698
 
        @functools.wraps(f)
699
 
        def inner(*args, **kwargs):
700
 
            # NOTE(soren): If we ever go natively threaded, this will be racy.
701
 
            #              See http://stackoverflow.com/questions/5390569/dyn
702
 
            #              amically-allocating-and-destroying-mutexes
703
 
            sem = _semaphores.get(name, semaphore.Semaphore())
704
 
            if name not in _semaphores:
705
 
                # this check is not racy - we're already holding ref locally
706
 
                # so GC won't remove the item and there was no IO switch
707
 
                # (only valid in greenthreads)
708
 
                _semaphores[name] = sem
709
 
 
710
 
            with sem:
711
 
                LOG.debug(_('Got semaphore "%(lock)s" for method '
712
 
                            '"%(method)s"...'), {'lock': name,
713
 
                                                 'method': f.__name__})
714
 
                if external and not FLAGS.disable_process_locking:
715
 
                    LOG.debug(_('Attempting to grab file lock "%(lock)s" for '
716
 
                                'method "%(method)s"...'),
717
 
                              {'lock': name, 'method': f.__name__})
718
 
                    cleanup_dir = False
719
 
 
720
 
                    # We need a copy of lock_path because it is non-local
721
 
                    local_lock_path = lock_path
722
 
                    if not local_lock_path:
723
 
                        local_lock_path = FLAGS.lock_path
724
 
 
725
 
                    if not local_lock_path:
726
 
                        cleanup_dir = True
727
 
                        local_lock_path = tempfile.mkdtemp()
728
 
 
729
 
                    if not os.path.exists(local_lock_path):
730
 
                        cleanup_dir = True
731
 
                        ensure_tree(local_lock_path)
732
 
 
733
 
                    # NOTE(mikal): the lock name cannot contain directory
734
 
                    # separators
735
 
                    safe_name = name.replace(os.sep, '_')
736
 
                    lock_file_path = os.path.join(local_lock_path,
737
 
                                                  'nova-%s' % safe_name)
738
 
                    try:
739
 
                        lock = InterProcessLock(lock_file_path)
740
 
                        with lock:
741
 
                            LOG.debug(_('Got file lock "%(lock)s" for '
742
 
                                        'method "%(method)s"...'),
743
 
                                      {'lock': name, 'method': f.__name__})
744
 
                            retval = f(*args, **kwargs)
745
 
                    finally:
746
 
                        # NOTE(vish): This removes the tempdir if we needed
747
 
                        #             to create one. This is used to cleanup
748
 
                        #             the locks left behind by unit tests.
749
 
                        if cleanup_dir:
750
 
                            shutil.rmtree(local_lock_path)
751
 
                else:
752
 
                    retval = f(*args, **kwargs)
753
 
 
754
 
            return retval
755
 
        return inner
756
 
    return wrap
 
597
def to_bytes(text, default=0):
 
598
    """Try to turn a string into a number of bytes. Looks at the last
 
599
    characters of the text to determine what conversion is needed to
 
600
    turn the input text into a byte number.
 
601
 
 
602
    Supports: B/b, K/k, M/m, G/g, T/t (or the same with b/B on the end)
 
603
 
 
604
    """
 
605
    # Take off everything not number 'like' (which should leave
 
606
    # only the byte 'identifier' left)
 
607
    mult_key_org = text.lstrip('-1234567890')
 
608
    mult_key = mult_key_org.lower()
 
609
    mult_key_len = len(mult_key)
 
610
    if mult_key.endswith("b"):
 
611
        mult_key = mult_key[0:-1]
 
612
    try:
 
613
        multiplier = BYTE_MULTIPLIERS[mult_key]
 
614
        if mult_key_len:
 
615
            # Empty cases shouldn't cause text[0:-0]
 
616
            text = text[0:-mult_key_len]
 
617
        return int(text) * multiplier
 
618
    except KeyError:
 
619
        msg = _('Unknown byte multiplier: %s') % mult_key_org
 
620
        raise TypeError(msg)
 
621
    except ValueError:
 
622
        return default
757
623
 
758
624
 
759
625
def delete_if_exists(pathname):
912
778
        return ('', '')
913
779
 
914
780
 
915
 
def gen_uuid():
916
 
    return uuid.uuid4()
917
 
 
918
 
 
919
 
def is_uuid_like(val):
920
 
    """For our purposes, a UUID is a string in canonical form:
921
 
 
922
 
        aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
923
 
    """
924
 
    try:
925
 
        uuid.UUID(val)
926
 
        return True
927
 
    except (TypeError, ValueError, AttributeError):
928
 
        return False
929
 
 
930
 
 
931
781
def bool_from_str(val):
932
782
    """Convert a string representation of a bool into a bool value"""
933
783
 
995
845
    this function patches a decorator
996
846
    for all functions in specified modules.
997
847
    You can set decorators for each modules
998
 
    using FLAGS.monkey_patch_modules.
 
848
    using CONF.monkey_patch_modules.
999
849
    The format is "Module path:Decorator function".
1000
850
    Example: 'nova.api.ec2.cloud:nova.notifier.api.notify_decorator'
1001
851
 
1005
855
    name - name of the function
1006
856
    function - object of the function
1007
857
    """
1008
 
    # If FLAGS.monkey_patch is not True, this function do nothing.
1009
 
    if not FLAGS.monkey_patch:
 
858
    # If CONF.monkey_patch is not True, this function do nothing.
 
859
    if not CONF.monkey_patch:
1010
860
        return
1011
861
    # Get list of modules and decorators
1012
 
    for module_and_decorator in FLAGS.monkey_patch_modules:
 
862
    for module_and_decorator in CONF.monkey_patch_modules:
1013
863
        module, decorator_name = module_and_decorator.split(':')
1014
864
        # import decorator function
1015
865
        decorator = importutils.import_class(decorator_name)
1057
907
    """Generate the URL to glance."""
1058
908
    # TODO(jk0): This will eventually need to take SSL into consideration
1059
909
    # when supported in glance.
1060
 
    return "http://%s:%d" % (FLAGS.glance_host, FLAGS.glance_port)
 
910
    return "http://%s:%d" % (CONF.glance_host, CONF.glance_port)
1061
911
 
1062
912
 
1063
913
def generate_image_url(image_ref):
1188
1038
    last_heartbeat = service['updated_at'] or service['created_at']
1189
1039
    # Timestamps in DB are UTC.
1190
1040
    elapsed = total_seconds(timeutils.utcnow() - last_heartbeat)
1191
 
    return abs(elapsed) <= FLAGS.service_down_time
 
1041
    return abs(elapsed) <= CONF.service_down_time
1192
1042
 
1193
1043
 
1194
1044
def generate_mac_address():
1200
1050
    #             properly: 0xfa.
1201
1051
    #             Discussion: https://bugs.launchpad.net/nova/+bug/921838
1202
1052
    mac = [0xfa, 0x16, 0x3e,
1203
 
           random.randint(0x00, 0x7f),
 
1053
           random.randint(0x00, 0xff),
1204
1054
           random.randint(0x00, 0xff),
1205
1055
           random.randint(0x00, 0xff)]
1206
1056
    return ':'.join(map(lambda x: "%02x" % x, mac))
1307
1157
            self._rollback()
1308
1158
 
1309
1159
 
1310
 
def ensure_tree(path):
1311
 
    """Create a directory (and any ancestor directories required)
 
1160
def mkfs(fs, path, label=None):
 
1161
    """Format a file or block device
1312
1162
 
1313
 
    :param path: Directory to create
 
1163
    :param fs: Filesystem type (examples include 'swap', 'ext3', 'ext4'
 
1164
               'btrfs', etc.)
 
1165
    :param path: Path to file or block device to format
 
1166
    :param label: Volume label to use
1314
1167
    """
1315
 
    try:
1316
 
        os.makedirs(path)
1317
 
    except OSError as exc:
1318
 
        if exc.errno == errno.EEXIST:
1319
 
            if not os.path.isdir(path):
1320
 
                raise
 
1168
    if fs == 'swap':
 
1169
        args = ['mkswap']
 
1170
    else:
 
1171
        args = ['mkfs', '-t', fs]
 
1172
    #add -F to force no interactive execute on non-block device.
 
1173
    if fs in ('ext3', 'ext4'):
 
1174
        args.extend(['-F'])
 
1175
    if label:
 
1176
        if fs in ('msdos', 'vfat'):
 
1177
            label_opt = '-n'
1321
1178
        else:
1322
 
            raise
 
1179
            label_opt = '-L'
 
1180
        args.extend([label_opt, label])
 
1181
    args.append(path)
 
1182
    execute(*args)