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

« back to all changes in this revision

Viewing changes to nova/virt/libvirt/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:
21
21
 
22
22
import errno
23
23
import hashlib
 
24
import json
24
25
import os
25
26
import re
 
27
import time
 
28
 
 
29
from lxml import etree
26
30
 
27
31
from nova import exception
28
 
from nova import flags
29
32
from nova.openstack.common import cfg
 
33
from nova.openstack.common import fileutils
30
34
from nova.openstack.common import jsonutils
 
35
from nova.openstack.common import lockutils
31
36
from nova.openstack.common import log as logging
32
37
from nova import utils
33
38
from nova.virt import images
43
48
                    'non-standard locations')
44
49
    ]
45
50
 
46
 
flags.DECLARE('instances_path', 'nova.compute.manager')
47
 
flags.DECLARE('base_dir_name', 'nova.compute.manager')
48
 
FLAGS = flags.FLAGS
49
 
FLAGS.register_opts(util_opts)
 
51
CONF = cfg.CONF
 
52
CONF.register_opts(util_opts)
 
53
CONF.import_opt('instances_path', 'nova.compute.manager')
 
54
CONF.import_opt('base_dir_name', 'nova.compute.manager')
50
55
 
51
56
 
52
57
def execute(*args, **kwargs):
86
91
    :param backing_file: Existing image on which to base the COW image
87
92
    :param path: Desired location of the COW image
88
93
    """
89
 
    execute('qemu-img', 'create', '-f', 'qcow2', '-o',
90
 
             'backing_file=%s' % backing_file, path)
 
94
    base_cmd = ['qemu-img', 'create', '-f', 'qcow2']
 
95
    cow_opts = []
 
96
    if backing_file:
 
97
        cow_opts += ['backing_file=%s' % backing_file]
 
98
        base_details = images.qemu_img_info(backing_file)
 
99
    else:
 
100
        base_details = None
 
101
    # This doesn't seem to get inherited so force it to...
 
102
    # http://paste.ubuntu.com/1213295/
 
103
    # TODO(harlowja) probably file a bug against qemu-img/qemu
 
104
    if base_details and base_details.cluster_size is not None:
 
105
        cow_opts += ['cluster_size=%s' % base_details.cluster_size]
 
106
    # For now don't inherit this due the following discussion...
 
107
    # See: http://www.gossamer-threads.com/lists/openstack/dev/10592
 
108
    # if 'preallocation' in base_details:
 
109
    #     cow_opts += ['preallocation=%s' % base_details['preallocation']]
 
110
    if base_details and base_details.encryption:
 
111
        cow_opts += ['encryption=%s' % base_details.encryption]
 
112
    if cow_opts:
 
113
        # Format as a comma separated list
 
114
        csv_opts = ",".join(cow_opts)
 
115
        cow_opts = ['-o', csv_opts]
 
116
    cmd = base_cmd + cow_opts + [path]
 
117
    execute(*cmd)
91
118
 
92
119
 
93
120
def create_lvm_image(vg, lv, size, sparse=False):
143
170
 
144
171
    :param vg: volume group name
145
172
    """
146
 
    out, err = execute('lvs', '--noheadings', '-o', 'lv_path', vg,
 
173
    out, err = execute('lvs', '--noheadings', '-o', 'lv_name', vg,
147
174
                       run_as_root=True)
148
175
 
149
176
    return [line.strip() for line in out.splitlines()]
150
177
 
151
178
 
 
179
def logical_volume_info(path):
 
180
    """Get logical volume info.
 
181
 
 
182
    :param path: logical volume path
 
183
    """
 
184
    out, err = execute('lvs', '-o', 'vg_all,lv_all',
 
185
                       '--separator', '|', path, run_as_root=True)
 
186
 
 
187
    info = [line.split('|') for line in out.splitlines()]
 
188
 
 
189
    if len(info) != 2:
 
190
        raise RuntimeError(_("Path %s must be LVM logical volume") % path)
 
191
 
 
192
    return dict(zip(*info))
 
193
 
 
194
 
152
195
def remove_logical_volumes(*paths):
153
196
    """Remove one or more logical volume."""
154
197
    if paths:
169
212
    :param is_block_dev:
170
213
    :returns: driver_name or None
171
214
    """
172
 
    if FLAGS.libvirt_type == "xen":
 
215
    if CONF.libvirt_type == "xen":
173
216
        if is_block_dev:
174
217
            return "phy"
175
218
        else:
176
219
            return "tap"
177
 
    elif FLAGS.libvirt_type in ('kvm', 'qemu'):
 
220
    elif CONF.libvirt_type in ('kvm', 'qemu'):
178
221
        return "qemu"
179
222
    else:
180
223
        # UML doesn't want a driver_name set
188
231
    :returns: Size (in bytes) of the given disk image as it would be seen
189
232
              by a virtual machine.
190
233
    """
191
 
    size = images.qemu_img_info(path)['virtual size']
192
 
    size = size.split('(')[1].split()[0]
 
234
    size = images.qemu_img_info(path).virtual_size
193
235
    return int(size)
194
236
 
195
237
 
199
241
    :param path: Path to the disk image
200
242
    :returns: a path to the image's backing store
201
243
    """
202
 
    backing_file = images.qemu_img_info(path).get('backing file')
203
 
 
 
244
    backing_file = images.qemu_img_info(path).backing_file
204
245
    if backing_file:
205
 
        if 'actual path: ' in backing_file:
206
 
            backing_file = backing_file.split('actual path: ')[1][:-1]
207
246
        backing_file = os.path.basename(backing_file)
208
247
 
209
248
    return backing_file
240
279
            execute('rsync', '--sparse', '--compress', src, dest)
241
280
 
242
281
 
243
 
def mkfs(fs, path, label=None):
244
 
    """Format a file or block device
245
 
 
246
 
    :param fs: Filesystem type (examples include 'swap', 'ext3', 'ext4'
247
 
               'btrfs', etc.)
248
 
    :param path: Path to file or block device to format
249
 
    :param label: Volume label to use
250
 
    """
251
 
    if fs == 'swap':
252
 
        execute('mkswap', path)
253
 
    else:
254
 
        args = ['mkfs', '-t', fs]
255
 
        #add -F to force no interactive excute on non-block device.
256
 
        if fs in ['ext3', 'ext4']:
257
 
            args.extend(['-F'])
258
 
        if label:
259
 
            args.extend(['-n', label])
260
 
        args.append(path)
261
 
        execute(*args)
262
 
 
263
 
 
264
282
def write_to_file(path, contents, umask=None):
265
283
    """Write the given contents to a file
266
284
 
372
390
    return os.unlink(path)
373
391
 
374
392
 
 
393
def find_disk(virt_dom):
 
394
    """Find root device path for instance
 
395
 
 
396
    May be file or device"""
 
397
    xml_desc = virt_dom.XMLDesc(0)
 
398
    domain = etree.fromstring(xml_desc)
 
399
    if CONF.libvirt_type == 'lxc':
 
400
        source = domain.find('devices/filesystem/source')
 
401
        disk_path = source.get('dir')
 
402
        disk_path = disk_path[0:disk_path.rfind('rootfs')]
 
403
        disk_path = os.path.join(disk_path, 'disk')
 
404
    else:
 
405
        source = domain.find('devices/disk/source')
 
406
        disk_path = source.get('file') or source.get('dev')
 
407
 
 
408
    if not disk_path:
 
409
        raise RuntimeError(_("Can't retrieve root device path "
 
410
                             "from instance libvirt configuration"))
 
411
 
 
412
    return disk_path
 
413
 
 
414
 
 
415
def get_disk_type(path):
 
416
    """Retrieve disk type (raw, qcow2, lvm) for given file"""
 
417
    if path.startswith('/dev'):
 
418
        return 'lvm'
 
419
 
 
420
    return images.qemu_img_info(path).file_format
 
421
 
 
422
 
375
423
def get_fs_info(path):
376
424
    """Get free/used/total space info for a filesystem
377
425
 
404
452
    """
405
453
 
406
454
    base_file = os.path.basename(base_path)
407
 
    return (FLAGS.image_info_filename_pattern
 
455
    return (CONF.image_info_filename_pattern
408
456
            % {'image': base_file})
409
457
 
410
458
 
412
460
    """Test if a given path matches the pattern for info files."""
413
461
 
414
462
    digest_size = hashlib.sha1().digestsize * 2
415
 
    regexp = (FLAGS.image_info_filename_pattern
 
463
    regexp = (CONF.image_info_filename_pattern
416
464
              % {'image': ('([0-9a-f]{%(digest_size)d}|'
417
465
                           '[0-9a-f]{%(digest_size)d}_sm|'
418
466
                           '[0-9a-f]{%(digest_size)d}_[0-9]+)'
423
471
    return False
424
472
 
425
473
 
426
 
def read_stored_info(base_path, field=None):
 
474
def _read_possible_json(serialized, info_file):
 
475
    try:
 
476
        d = jsonutils.loads(serialized)
 
477
 
 
478
    except ValueError, e:
 
479
        LOG.error(_('Error reading image info file %(filename)s: '
 
480
                    '%(error)s'),
 
481
                  {'filename': info_file,
 
482
                   'error': e})
 
483
        d = {}
 
484
 
 
485
    return d
 
486
 
 
487
 
 
488
def read_stored_info(target, field=None, timestamped=False):
427
489
    """Read information about an image.
428
490
 
429
491
    Returns an empty dictionary if there is no info, just the field value if
430
492
    a field is requested, or the entire dictionary otherwise.
431
493
    """
432
494
 
433
 
    info_file = get_info_filename(base_path)
 
495
    info_file = get_info_filename(target)
434
496
    if not os.path.exists(info_file):
435
 
        # Special case to handle essex checksums being converted
436
 
        old_filename = base_path + '.sha1'
 
497
        # NOTE(mikal): Special case to handle essex checksums being converted.
 
498
        # There is an assumption here that target is a base image filename.
 
499
        old_filename = target + '.sha1'
437
500
        if field == 'sha1' and os.path.exists(old_filename):
438
501
            hash_file = open(old_filename)
439
502
            hash_value = hash_file.read()
440
503
            hash_file.close()
441
504
 
442
 
            write_stored_info(base_path, field=field, value=hash_value)
 
505
            write_stored_info(target, field=field, value=hash_value)
443
506
            os.remove(old_filename)
444
507
            d = {field: hash_value}
445
508
 
447
510
            d = {}
448
511
 
449
512
    else:
450
 
        LOG.info(_('Reading image info file: %s'), info_file)
451
 
        f = open(info_file, 'r')
452
 
        serialized = f.read().rstrip()
453
 
        f.close()
454
 
        LOG.info(_('Read: %s'), serialized)
455
 
 
456
 
        try:
457
 
            d = jsonutils.loads(serialized)
458
 
 
459
 
        except ValueError, e:
460
 
            LOG.error(_('Error reading image info file %(filename)s: '
461
 
                        '%(error)s'),
462
 
                      {'filename': info_file,
463
 
                       'error': e})
464
 
            d = {}
 
513
        lock_name = 'info-%s' % os.path.split(target)[-1]
 
514
        lock_path = os.path.join(CONF.instances_path, 'locks')
 
515
 
 
516
        @lockutils.synchronized(lock_name, 'nova-', external=True,
 
517
                                lock_path=lock_path)
 
518
        def read_file(info_file):
 
519
            LOG.debug(_('Reading image info file: %s'), info_file)
 
520
            with open(info_file, 'r') as f:
 
521
                return f.read().rstrip()
 
522
 
 
523
        serialized = read_file(info_file)
 
524
        d = _read_possible_json(serialized, info_file)
465
525
 
466
526
    if field:
467
 
        return d.get(field, None)
 
527
        if timestamped:
 
528
            return (d.get(field, None), d.get('%s-timestamp' % field, None))
 
529
        else:
 
530
            return d.get(field, None)
468
531
    return d
469
532
 
470
533
 
475
538
        return
476
539
 
477
540
    info_file = get_info_filename(target)
478
 
    utils.ensure_tree(os.path.dirname(info_file))
479
 
 
480
 
    d = read_stored_info(info_file)
481
 
    d[field] = value
482
 
    serialized = jsonutils.dumps(d)
483
 
 
484
 
    LOG.info(_('Writing image info file: %s'), info_file)
485
 
    LOG.info(_('Wrote: %s'), serialized)
486
 
    f = open(info_file, 'w')
487
 
    f.write(serialized)
488
 
    f.close()
 
541
    LOG.info(_('Writing stored info to %s'), info_file)
 
542
    fileutils.ensure_tree(os.path.dirname(info_file))
 
543
 
 
544
    lock_name = 'info-%s' % os.path.split(target)[-1]
 
545
    lock_path = os.path.join(CONF.instances_path, 'locks')
 
546
 
 
547
    @lockutils.synchronized(lock_name, 'nova-', external=True,
 
548
                            lock_path=lock_path)
 
549
    def write_file(info_file, field, value):
 
550
        d = {}
 
551
 
 
552
        if os.path.exists(info_file):
 
553
            with open(info_file, 'r') as f:
 
554
                d = _read_possible_json(f.read(), info_file)
 
555
 
 
556
        d[field] = value
 
557
        d['%s-timestamp' % field] = time.time()
 
558
 
 
559
        with open(info_file, 'w') as f:
 
560
            f.write(json.dumps(d))
 
561
 
 
562
    write_file(info_file, field, value)