~gandelman-a/ubuntu/precise/nova/UCA_2012.2.1

« back to all changes in this revision

Viewing changes to nova/virt/xenapi/vm_utils.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Chuck Short, Adam Gandelman
  • Date: 2012-08-16 14:04:11 UTC
  • mfrom: (1.1.59)
  • Revision ID: package-import@ubuntu.com-20120816140411-8dvudjblnx1w0mwx
Tags: 2012.2~f3-0ubuntu1
[ Chuck Short ]
* New upstream version.
* debian/rules: Re-enable testsuite.
* debian/control:
  - Add python-quantumclient as a build depends.
  - Bump standards to 3.9.3
  - Fix lintian warnings.
  - Recommend python-glanceclient and python-keystoneclient.
  - Add dependency of iptables for nova-network.
* debian/watch: Update
* debian/rules: Do not run pep8 tests since upstream is still using an
  older pep8.
* debian/patches/0001-Update-tools-hacking-for-pep8-1.2-and-
  beyond.patch: Get the testsuite running again.
* debian/nova-volume.install, debian/nova_tgt: Add support for
  persistent volumes.

[ Adam Gandelman ]
* debian/{nova-api.install, nova-api-metadata.install}: Install
  api-metadata.filters. (LP: #1002111)
* debian/control: Added python-glanceclient.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 
3
3
# Copyright (c) 2010 Citrix Systems, Inc.
4
4
# Copyright 2011 Piston Cloud Computing, Inc.
 
5
# Copyright 2012 Openstack, LLC.
5
6
#
6
7
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
7
8
#    not use this file except in compliance with the License. You may obtain
34
35
 
35
36
from eventlet import greenthread
36
37
 
 
38
from nova import block_device
37
39
from nova.compute import instance_types
38
40
from nova.compute import power_state
39
41
from nova import db
46
48
from nova.openstack.common import log as logging
47
49
from nova import utils
48
50
from nova.virt.disk import api as disk
49
 
from nova.virt import xenapi
 
51
from nova.virt import driver
50
52
from nova.virt.xenapi import volume_utils
51
53
 
52
54
 
97
99
MBR_SIZE_SECTORS = 63
98
100
MBR_SIZE_BYTES = MBR_SIZE_SECTORS * SECTOR_SIZE
99
101
KERNEL_DIR = '/boot/guest'
 
102
MAX_VDI_CHAIN_SIZE = 16
100
103
 
101
104
 
102
105
class ImageType(object):
137
140
        return dict(zip(ImageType._strs, ImageType._ids)).get(image_type_str)
138
141
 
139
142
 
140
 
def create_vm(session, instance, kernel, ramdisk, use_pv_kernel=False):
 
143
def create_vm(session, instance, name_label, kernel, ramdisk,
 
144
              use_pv_kernel=False):
141
145
    """Create a VM record.  Returns new VM reference.
142
146
    the use_pv_kernel flag indicates whether the guest is HVM or PV
143
147
 
149
153
 
150
154
        3. Using hardware virtualization
151
155
    """
152
 
    inst_type_id = instance.instance_type_id
 
156
    inst_type_id = instance['instance_type_id']
153
157
    instance_type = instance_types.get_instance_type(inst_type_id)
154
158
    mem = str(long(instance_type['memory_mb']) * 1024 * 1024)
155
159
    vcpus = str(instance_type['vcpus'])
171
175
        'memory_static_max': mem,
172
176
        'memory_target': mem,
173
177
        'name_description': '',
174
 
        'name_label': instance.name,
 
178
        'name_label': name_label,
175
179
        'other_config': {'allowvssprovider': str(False),
176
 
                         'nova_uuid': str(instance.uuid)},
 
180
                         'nova_uuid': str(instance['uuid'])},
177
181
        'PCI_bus': '',
178
182
        'platform': {'acpi': 'true', 'apic': 'true', 'pae': 'true',
179
183
                     'viridian': 'true', 'timeoffset': '0'},
195
199
    # non-raw/raw with PV kernel/raw in HVM mode
196
200
    if use_pv_kernel:
197
201
        rec['platform']['nx'] = 'false'
198
 
        if instance.kernel_id:
 
202
        if instance['kernel_id']:
199
203
            # 1. Kernel explicitly passed in, use that
200
204
            rec['PV_args'] = 'root=/dev/xvda1'
201
205
            rec['PV_kernel'] = kernel
214
218
    return vm_ref
215
219
 
216
220
 
 
221
def destroy_vm(session, instance, vm_ref):
 
222
    """Destroys a VM record."""
 
223
    try:
 
224
        session.call_xenapi('VM.destroy', vm_ref)
 
225
    except session.XenAPI.Failure, exc:
 
226
        LOG.exception(exc)
 
227
        return
 
228
 
 
229
    LOG.debug(_("VM destroyed"), instance=instance)
 
230
 
 
231
 
 
232
def shutdown_vm(session, instance, vm_ref, hard=True):
 
233
    vm_rec = session.call_xenapi("VM.get_record", vm_ref)
 
234
    state = compile_info(vm_rec)['state']
 
235
    if state == power_state.SHUTDOWN:
 
236
        LOG.warn(_("VM already halted, skipping shutdown..."),
 
237
                 instance=instance)
 
238
        return
 
239
 
 
240
    LOG.debug(_("Shutting down VM"), instance=instance)
 
241
    try:
 
242
        if hard:
 
243
            session.call_xenapi('VM.hard_shutdown', vm_ref)
 
244
        else:
 
245
            session.call_xenapi('VM.clean_shutdown', vm_ref)
 
246
    except session.XenAPI.Failure, exc:
 
247
        LOG.exception(exc)
 
248
 
 
249
 
217
250
def ensure_free_mem(session, instance):
218
 
    inst_type_id = instance.instance_type_id
 
251
    inst_type_id = instance['instance_type_id']
219
252
    instance_type = instance_types.get_instance_type(inst_type_id)
220
253
    mem = long(instance_type['memory_mb']) * 1024 * 1024
221
 
    #get free memory from host
222
254
    host = session.get_xenapi_host()
223
255
    host_free_mem = long(session.call_xenapi("host.compute_free_memory",
224
256
                                             host))
314
346
                _('Unable to destroy VDI %s') % vdi_ref)
315
347
 
316
348
 
317
 
def create_vdi(session, sr_ref, info, disk_type, virtual_size,
 
349
def safe_destroy_vdis(session, vdi_refs):
 
350
    """Destroys the requested VDIs, logging any StorageError exceptions."""
 
351
    for vdi_ref in vdi_refs:
 
352
        try:
 
353
            destroy_vdi(session, vdi_ref)
 
354
        except volume_utils.StorageError as exc:
 
355
            LOG.error(exc)
 
356
 
 
357
 
 
358
def create_vdi(session, sr_ref, instance, name_label, disk_type, virtual_size,
318
359
               read_only=False):
319
360
    """Create a VDI record and returns its reference."""
320
361
    # create_vdi may be called simply while creating a volume
321
362
    # hence information about instance may or may not be present
322
 
    otherconf = {}
323
 
    if not isinstance(info, basestring):
324
 
        name_label = info['name']
325
 
        otherconf = {'nova_instance_uuid': info['uuid'],
326
 
                     'nova_disk_type': disk_type}
327
 
    else:
328
 
        name_label = info
 
363
    otherconf = {'nova_disk_type': disk_type}
 
364
    if instance:
 
365
        otherconf['nova_instance_uuid'] = instance['uuid']
329
366
    vdi_ref = session.call_xenapi("VDI.create",
330
367
         {'name_label': name_label,
331
368
          'name_description': disk_type,
344
381
    return vdi_ref
345
382
 
346
383
 
347
 
def copy_vdi(session, sr_ref, vdi_to_copy_ref):
348
 
    """Copy a VDI and return the new VDIs reference."""
349
 
    vdi_ref = session.call_xenapi('VDI.copy', vdi_to_copy_ref, sr_ref)
350
 
    LOG.debug(_('Copied VDI %(vdi_ref)s from VDI '
351
 
                '%(vdi_to_copy_ref)s on %(sr_ref)s.') % locals())
 
384
def get_vdis_for_boot_from_vol(session, instance, dev_params):
 
385
    vdis = {}
 
386
    sr_uuid = dev_params['sr_uuid']
 
387
    sr_ref = volume_utils.find_sr_by_uuid(session,
 
388
                                          sr_uuid)
 
389
    if sr_ref:
 
390
        session.call_xenapi("SR.scan", sr_ref)
 
391
        return {'root': dict(uuid=dev_params['vdi_uuid'],
 
392
                                file=None)}
 
393
    return vdis
 
394
 
 
395
 
 
396
def _volume_in_mapping(mount_device, block_device_info):
 
397
    block_device_list = [block_device.strip_prefix(vol['mount_device'])
 
398
                         for vol in
 
399
                         driver.block_device_info_get_mapping(
 
400
                         block_device_info)]
 
401
    swap = driver.block_device_info_get_swap(block_device_info)
 
402
    if driver.swap_is_usable(swap):
 
403
        swap_dev = swap['device_name']
 
404
        block_device_list.append(block_device.strip_prefix(swap_dev))
 
405
    block_device_list += [block_device.strip_prefix(ephemeral['device_name'])
 
406
                          for ephemeral in
 
407
                          driver.block_device_info_get_ephemerals(
 
408
                          block_device_info)]
 
409
    LOG.debug(_("block_device_list %s"), block_device_list)
 
410
    return block_device.strip_prefix(mount_device) in block_device_list
 
411
 
 
412
 
 
413
def get_vdis_for_instance(context, session, instance, name_label, image,
 
414
                          image_type, block_device_info=None):
 
415
    if block_device_info:
 
416
        LOG.debug(_("block device info: %s"), block_device_info)
 
417
        rootdev = block_device_info['root_device_name']
 
418
        if _volume_in_mapping(rootdev, block_device_info):
 
419
            # call function to return the vdi in connection info of block
 
420
            # device.
 
421
            # make it a point to return from here.
 
422
            bdm_root_dev = block_device_info['block_device_mapping'][0]
 
423
            dev_params = bdm_root_dev['connection_info']['data']
 
424
            LOG.debug(dev_params)
 
425
            return get_vdis_for_boot_from_vol(session,
 
426
                                             instance,
 
427
                                             dev_params)
 
428
    return _create_image(context, session, instance, name_label, image,
 
429
                        image_type)
 
430
 
 
431
 
 
432
@contextlib.contextmanager
 
433
def _dummy_vm(session, instance, vdi_ref):
 
434
    """This creates a temporary VM so that we can snapshot a VDI.
 
435
 
 
436
    VDI's can't be snapshotted directly since the API expects a `vm_ref`. To
 
437
    work around this, we need to create a temporary VM and then map the VDI to
 
438
    the VM using a temporary VBD.
 
439
    """
 
440
    name_label = "dummy"
 
441
    vm_ref = create_vm(session, instance, name_label, None, None)
 
442
    try:
 
443
        vbd_ref = create_vbd(session, vm_ref, vdi_ref, 'autodetect',
 
444
                             read_only=True)
 
445
        try:
 
446
            yield vm_ref
 
447
        finally:
 
448
            try:
 
449
                destroy_vbd(session, vbd_ref)
 
450
            except volume_utils.StorageError:
 
451
                # destroy_vbd() will log error
 
452
                pass
 
453
    finally:
 
454
        destroy_vm(session, instance, vm_ref)
 
455
 
 
456
 
 
457
def _safe_copy_vdi(session, sr_ref, instance, vdi_to_copy_ref):
 
458
    """Copy a VDI and return the new VDIs reference.
 
459
 
 
460
    This function differs from the XenAPI `VDI.copy` call in that the copy is
 
461
    atomic and isolated, meaning we don't see half-downloaded images. It
 
462
    accomplishes this by copying the VDI's into a temporary directory and then
 
463
    atomically renaming them into the SR when the copy is completed.
 
464
 
 
465
    The correct long term solution is to fix `VDI.copy` so that it is atomic
 
466
    and isolated.
 
467
    """
 
468
    with _dummy_vm(session, instance, vdi_to_copy_ref) as vm_ref:
 
469
        label = "snapshot"
 
470
 
 
471
        with snapshot_attached_here(
 
472
                session, instance, vm_ref, label) as vdi_uuids:
 
473
            params = {'sr_path': get_sr_path(session),
 
474
                      'vdi_uuids': vdi_uuids,
 
475
                      'uuid_stack': _make_uuid_stack()}
 
476
 
 
477
            kwargs = {'params': pickle.dumps(params)}
 
478
            result = session.call_plugin(
 
479
                    'workarounds', 'safe_copy_vdis', kwargs)
 
480
            imported_vhds = jsonutils.loads(result)
 
481
 
 
482
    root_uuid = imported_vhds['root']['uuid']
 
483
 
 
484
    # TODO(sirp): for safety, we should probably re-scan the SR after every
 
485
    # call to a dom0 plugin, since there is a possibility that the underlying
 
486
    # VHDs changed
 
487
    scan_default_sr(session)
 
488
    vdi_ref = session.call_xenapi('VDI.get_by_uuid', root_uuid)
352
489
    return vdi_ref
353
490
 
354
491
 
355
 
def clone_vdi(session, vdi_to_clone_ref):
 
492
def _clone_vdi(session, vdi_to_clone_ref):
356
493
    """Clones a VDI and return the new VDIs reference."""
357
494
    vdi_ref = session.call_xenapi('VDI.clone', vdi_to_clone_ref)
358
495
    LOG.debug(_('Cloned VDI %(vdi_ref)s from VDI '
379
516
                                  % locals())
380
517
 
381
518
 
382
 
def create_snapshot(session, instance, vm_ref, label):
383
 
    """Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI,
384
 
    Snapshot VHD"""
385
 
    LOG.debug(_("Snapshotting with label '%(label)s'"), locals(),
386
 
              instance=instance)
 
519
@contextlib.contextmanager
 
520
def snapshot_attached_here(session, instance, vm_ref, label):
 
521
    LOG.debug(_("Starting snapshot for VM"), instance=instance)
387
522
 
 
523
    # Memorize the original_parent_uuid so we can poll for coalesce
388
524
    vm_vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref)
389
 
    sr_ref = vm_vdi_rec["SR"]
390
 
 
391
 
    original_parent_uuid = get_vhd_parent_uuid(session, vm_vdi_ref)
392
 
 
 
525
    original_parent_uuid = _get_vhd_parent_uuid(session, vm_vdi_ref)
 
526
 
 
527
    template_vm_ref, template_vdi_uuid = _create_snapshot(
 
528
            session, instance, vm_ref, label)
 
529
 
 
530
    try:
 
531
        sr_ref = vm_vdi_rec["SR"]
 
532
        parent_uuid, base_uuid = _wait_for_vhd_coalesce(
 
533
                session, instance, sr_ref, vm_vdi_ref, original_parent_uuid)
 
534
 
 
535
        vdi_uuids = [vdi_rec['uuid'] for vdi_rec in
 
536
                     _walk_vdi_chain(session, template_vdi_uuid)]
 
537
 
 
538
        yield vdi_uuids
 
539
    finally:
 
540
        _destroy_snapshot(session, instance, template_vm_ref)
 
541
 
 
542
 
 
543
def _create_snapshot(session, instance, vm_ref, label):
393
544
    template_vm_ref = session.call_xenapi('VM.snapshot', vm_ref, label)
394
545
    template_vdi_rec = get_vdi_for_vm_safely(session, template_vm_ref)[1]
395
546
    template_vdi_uuid = template_vdi_rec["uuid"]
396
547
 
397
 
    LOG.debug(_('Created snapshot %(template_vm_ref)s'), locals(),
398
 
              instance=instance)
399
 
 
400
 
    parent_uuid, base_uuid = _wait_for_vhd_coalesce(
401
 
            session, instance, sr_ref, vm_vdi_ref, original_parent_uuid)
402
 
 
403
 
    template_vdi_uuids = {'base': base_uuid,
404
 
                          'image': parent_uuid,
405
 
                          'snap': template_vdi_uuid}
406
 
    return template_vm_ref, template_vdi_uuids
 
548
    LOG.debug(_("Created snapshot %(template_vdi_uuid)s with label"
 
549
                " '%(label)s'"), locals(), instance=instance)
 
550
 
 
551
    return template_vm_ref, template_vdi_uuid
 
552
 
 
553
 
 
554
def _destroy_snapshot(session, instance, vm_ref):
 
555
    vdi_refs = lookup_vm_vdis(session, vm_ref)
 
556
    safe_destroy_vdis(session, vdi_refs)
 
557
 
 
558
    destroy_vm(session, instance, vm_ref)
407
559
 
408
560
 
409
561
def get_sr_path(session):
418
570
    return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid)
419
571
 
420
572
 
421
 
def find_cached_image(session, image_id, sr_ref):
 
573
def destroy_cached_images(session, sr_ref, all_cached=False, dry_run=False):
 
574
    """Destroy used or unused cached images.
 
575
 
 
576
    A cached image that is being used by at least one VM is said to be 'used'.
 
577
 
 
578
    In the case of an 'unused' image, the cached image will be the only
 
579
    descendent of the base-copy. So when we delete the cached-image, the
 
580
    refcount will drop to zero and XenServer will automatically destroy the
 
581
    base-copy for us.
 
582
 
 
583
    The default behavior of this function is to destroy only 'unused' cached
 
584
    images. To destroy all cached images, use the `all_cached=True` kwarg.
 
585
    """
 
586
    cached_images = _find_cached_images(session, sr_ref)
 
587
    destroyed = set()
 
588
 
 
589
    def destroy_cached_vdi(vdi_uuid, vdi_ref):
 
590
        LOG.debug(_("Destroying cached VDI '%(vdi_uuid)s'"))
 
591
        if not dry_run:
 
592
            destroy_vdi(session, vdi_ref)
 
593
        destroyed.add(vdi_uuid)
 
594
 
 
595
    for vdi_ref in cached_images.values():
 
596
        vdi_uuid = session.call_xenapi('VDI.get_uuid', vdi_ref)
 
597
 
 
598
        if all_cached:
 
599
            destroy_cached_vdi(vdi_uuid, vdi_ref)
 
600
            continue
 
601
 
 
602
        # Unused-Only: Search for siblings
 
603
 
 
604
        # Chain length greater than two implies a VM must be holding a ref to
 
605
        # the base-copy (otherwise it would have coalesced), so consider this
 
606
        # cached image used.
 
607
        chain = list(_walk_vdi_chain(session, vdi_uuid))
 
608
        if len(chain) > 2:
 
609
            continue
 
610
        elif len(chain) == 2:
 
611
            # Siblings imply cached image is used
 
612
            root_vdi_rec = chain[-1]
 
613
            children = _child_vhds(session, sr_ref, root_vdi_rec['uuid'])
 
614
            if len(children) > 1:
 
615
                continue
 
616
 
 
617
        destroy_cached_vdi(vdi_uuid, vdi_ref)
 
618
 
 
619
    return destroyed
 
620
 
 
621
 
 
622
def _find_cached_images(session, sr_ref):
 
623
    """Return a dict(uuid=vdi_ref) representing all cached images."""
 
624
    cached_images = {}
 
625
    for vdi_ref, vdi_rec in _get_all_vdis_in_sr(session, sr_ref):
 
626
        try:
 
627
            image_id = vdi_rec['other_config']['image-id']
 
628
        except KeyError:
 
629
            continue
 
630
 
 
631
        cached_images[image_id] = vdi_ref
 
632
 
 
633
    return cached_images
 
634
 
 
635
 
 
636
def _find_cached_image(session, image_id, sr_ref):
422
637
    """Returns the vdi-ref of the cached image."""
423
 
    for vdi_ref, vdi_rec in _get_all_vdis_in_sr(session, sr_ref):
424
 
        other_config = vdi_rec['other_config']
425
 
 
426
 
        try:
427
 
            image_id_match = other_config['image-id'] == image_id
428
 
        except KeyError:
429
 
            image_id_match = False
430
 
 
431
 
        # NOTE(sirp): `VDI.copy` stores the partially-completed file in the SR.
432
 
        # In order to avoid these half-baked files, we compare its current size
433
 
        # to the expected size pulled from the original cache file.
434
 
        try:
435
 
            size_match = (other_config['expected_physical_utilisation'] ==
436
 
                          vdi_rec['physical_utilisation'])
437
 
        except KeyError:
438
 
            size_match = False
439
 
 
440
 
        if image_id_match and size_match:
441
 
            return vdi_ref
442
 
 
443
 
    return None
 
638
    cached_images = _find_cached_images(session, sr_ref)
 
639
    return cached_images.get(image_id)
444
640
 
445
641
 
446
642
def upload_image(context, session, instance, vdi_uuids, image_id):
452
648
    LOG.debug(_("Asking xapi to upload %(vdi_uuids)s as"
453
649
                " ID %(image_id)s"), locals(), instance=instance)
454
650
 
455
 
    glance_host, glance_port = glance.pick_glance_api_server()
 
651
    glance_api_servers = glance.get_api_servers()
 
652
    glance_host, glance_port = glance_api_servers.next()
456
653
 
 
654
    # TODO(sirp): this inherit-image-property code should probably go in
 
655
    # nova/compute/manager so it can be shared across hypervisors
 
656
    sys_meta = db.instance_system_metadata_get(context, instance['uuid'])
457
657
    properties = {}
458
 
    properties['auto_disk_config'] = instance.auto_disk_config
459
 
    properties['os_type'] = instance.os_type or FLAGS.default_os_type
 
658
    prefix = 'image_'
 
659
    for key, value in sys_meta.iteritems():
 
660
        if key.startswith(prefix):
 
661
            key = key[len(prefix):]
 
662
        if key in FLAGS.non_inheritable_image_properties:
 
663
            continue
 
664
        properties[key] = value
 
665
    properties['auto_disk_config'] = instance['auto_disk_config']
 
666
    properties['os_type'] = instance['os_type'] or FLAGS.default_os_type
460
667
 
461
668
    params = {'vdi_uuids': vdi_uuids,
462
669
              'image_id': image_id,
482
689
 
483
690
        # Create new VDI
484
691
        vdi_size = instance_type['root_gb'] * 1024 * 1024 * 1024
485
 
        new_ref = create_vdi(session, sr_ref, instance, 'root', vdi_size)
 
692
        # NOTE(johannes): No resizing allowed for rescue instances, so
 
693
        # using instance['name'] is safe here
 
694
        new_ref = create_vdi(session, sr_ref, instance, instance['name'],
 
695
                             'root', vdi_size)
486
696
 
487
697
        new_uuid = session.call_xenapi('VDI.get_uuid', new_ref)
488
698
 
523
733
            _resize_part_and_fs(dev, start, old_sectors, new_sectors)
524
734
 
525
735
 
526
 
def _generate_disk(session, instance, vm_ref, userdevice, name, size_mb,
527
 
                   fs_type):
 
736
def _generate_disk(session, instance, vm_ref, userdevice, name_label,
 
737
                   disk_type, size_mb, fs_type):
528
738
    """
529
739
    Steps to programmatically generate a disk:
530
740
 
540
750
    sr_ref = safe_find_sr(session)
541
751
    ONE_MEG = 1024 * 1024
542
752
    virtual_size = size_mb * ONE_MEG
543
 
    vdi_ref = create_vdi(session, sr_ref, instance, name, virtual_size)
 
753
    vdi_ref = create_vdi(session, sr_ref, instance, name_label, disk_type,
 
754
                         virtual_size)
544
755
 
545
756
    try:
546
757
        # 2. Attach VDI to compute worker (VBD hotplug)
573
784
            destroy_vdi(session, vdi_ref)
574
785
 
575
786
 
576
 
def generate_swap(session, instance, vm_ref, userdevice, swap_mb):
 
787
def generate_swap(session, instance, vm_ref, userdevice, name_label, swap_mb):
577
788
    # NOTE(jk0): We use a FAT32 filesystem for the Windows swap
578
789
    # partition because that is what parted supports.
579
 
    is_windows = instance.os_type == "windows"
 
790
    is_windows = instance['os_type'] == "windows"
580
791
    fs_type = "vfat" if is_windows else "linux-swap"
581
792
 
582
 
    _generate_disk(session, instance, vm_ref, userdevice, 'swap', swap_mb,
583
 
                   fs_type)
584
 
 
585
 
 
586
 
def generate_ephemeral(session, instance, vm_ref, userdevice, size_gb):
587
 
    _generate_disk(session, instance, vm_ref, userdevice, 'ephemeral',
588
 
                   size_gb * 1024, FLAGS.default_ephemeral_format)
589
 
 
590
 
 
591
 
def create_kernel_image(context, session, instance, image_id, user_id,
592
 
                        project_id, image_type):
 
793
    _generate_disk(session, instance, vm_ref, userdevice, name_label,
 
794
                   'swap', swap_mb, fs_type)
 
795
 
 
796
 
 
797
def generate_ephemeral(session, instance, vm_ref, userdevice, name_label,
 
798
                       size_gb):
 
799
    _generate_disk(session, instance, vm_ref, userdevice, name_label,
 
800
                   'ephemeral', size_gb * 1024,
 
801
                   FLAGS.default_ephemeral_format)
 
802
 
 
803
 
 
804
def create_kernel_image(context, session, instance, name_label, image_id,
 
805
                        image_type):
593
806
    """Creates kernel/ramdisk file from the image stored in the cache.
594
807
    If the image is not present in the cache, it streams it from glance.
595
808
 
600
813
        args = {}
601
814
        args['cached-image'] = image_id
602
815
        args['new-image-uuid'] = str(uuid.uuid4())
603
 
        filename = session.call_plugin(
604
 
                'kernel', 'create_kernel_ramdisk', args)
 
816
        filename = session.call_plugin('kernel', 'create_kernel_ramdisk', args)
605
817
 
606
818
    if filename == "":
607
 
        return _fetch_image(context, session, instance, image_id, image_type)
 
819
        return _fetch_disk_image(context, session, instance, name_label,
 
820
                                 image_id, image_type)
608
821
    else:
609
822
        vdi_type = ImageType.to_string(image_type)
610
823
        return {vdi_type: dict(uuid=None, file=filename)}
619
832
    session.call_plugin('kernel', 'remove_kernel_ramdisk', args)
620
833
 
621
834
 
622
 
def _create_cached_image(context, session, instance, image_id, image_type):
 
835
def _create_cached_image(context, session, instance, name_label,
 
836
                         image_id, image_type):
623
837
    sr_ref = safe_find_sr(session)
624
838
    sr_type = session.call_xenapi('SR.get_record', sr_ref)["type"]
625
839
    vdis = {}
630
844
                      "type %(sr_type)s. Ignoring the cow flag.")
631
845
                      % locals())
632
846
 
633
 
    root_vdi_ref = find_cached_image(session, image_id, sr_ref)
 
847
    root_vdi_ref = _find_cached_image(session, image_id, sr_ref)
634
848
    if root_vdi_ref is None:
635
 
        vdis = _fetch_image(context, session, instance, image_id,
636
 
                                   image_type)
 
849
        vdis = _fetch_image(context, session, instance, name_label,
 
850
                            image_id, image_type)
637
851
        root_vdi = vdis['root']
638
852
        root_vdi_ref = session.call_xenapi('VDI.get_by_uuid',
639
853
                                           root_vdi['uuid'])
642
856
        session.call_xenapi('VDI.add_to_other_config',
643
857
                            root_vdi_ref, 'image-id', str(image_id))
644
858
 
645
 
        for vdi_type, vdi in vdis.iteritems():
646
 
            vdi_ref = session.call_xenapi('VDI.get_by_uuid',
647
 
                                          vdi['uuid'])
648
 
 
649
 
            vdi_rec = session.call_xenapi('VDI.get_record', vdi_ref)
650
 
            session.call_xenapi('VDI.add_to_other_config',
651
 
                                vdi_ref, 'expected_physical_utilisation',
652
 
                                vdi_rec['physical_utilisation'])
653
 
 
654
 
            if vdi_type == 'swap':
655
 
                session.call_xenapi('VDI.add_to_other_config',
656
 
                                    root_vdi_ref, 'swap-disk',
657
 
                                    str(vdi['uuid']))
 
859
        swap_vdi = vdis.get('swap')
 
860
        if swap_vdi:
 
861
            session.call_xenapi(
 
862
                    'VDI.add_to_other_config', root_vdi_ref, 'swap-disk',
 
863
                    str(swap_vdi['uuid']))
658
864
 
659
865
    if FLAGS.use_cow_images and sr_type == 'ext':
660
 
        new_vdi_ref = clone_vdi(session, root_vdi_ref)
 
866
        new_vdi_ref = _clone_vdi(session, root_vdi_ref)
661
867
    else:
662
 
        new_vdi_ref = copy_vdi(session, sr_ref, root_vdi_ref)
 
868
        new_vdi_ref = _safe_copy_vdi(session, sr_ref, instance, root_vdi_ref)
663
869
 
664
870
    # Set the name label for the image we just created and remove image id
665
871
    # field from other-config.
677
883
        swap_disk_uuid = vdi_rec['other_config']['swap-disk']
678
884
        swap_vdi_ref = session.call_xenapi('VDI.get_by_uuid',
679
885
                                           swap_disk_uuid)
680
 
        new_swap_vdi_ref = copy_vdi(session, sr_ref, swap_vdi_ref)
 
886
        new_swap_vdi_ref = _safe_copy_vdi(
 
887
                session, sr_ref, instance, swap_vdi_ref)
681
888
        new_swap_vdi_uuid = session.call_xenapi('VDI.get_uuid',
682
889
                                                new_swap_vdi_ref)
683
890
        vdis['swap'] = dict(uuid=new_swap_vdi_uuid, file=None)
685
892
    return vdis
686
893
 
687
894
 
688
 
def create_image(context, session, instance, image_id, image_type):
 
895
def _create_image(context, session, instance, name_label, image_id,
 
896
                  image_type):
689
897
    """Creates VDI from the image stored in the local cache. If the image
690
898
    is not present in the cache, it streams it from glance.
691
899
 
715
923
 
716
924
    # Fetch (and cache) the image
717
925
    if cache:
718
 
        vdis = _create_cached_image(
719
 
                context, session, instance, image_id, image_type)
 
926
        vdis = _create_cached_image(context, session, instance, name_label,
 
927
                                    image_id, image_type)
720
928
    else:
721
 
        vdis = _fetch_image(
722
 
                context, session, instance, image_id, image_type)
 
929
        vdis = _fetch_image(context, session, instance, name_label,
 
930
                            image_id, image_type)
723
931
 
724
932
    # Set the name label and description to easily identify what
725
933
    # instance and disk it's for
726
934
    for vdi_type, vdi in vdis.iteritems():
727
 
        set_vdi_name(session, vdi['uuid'], instance.name, vdi_type)
 
935
        set_vdi_name(session, vdi['uuid'], name_label, vdi_type)
728
936
 
729
937
    return vdis
730
938
 
731
939
 
732
 
def _fetch_image(context, session, instance, image_id, image_type):
 
940
def _fetch_image(context, session, instance, name_label, image_id, image_type):
733
941
    """Fetch image from glance based on image type.
734
942
 
735
943
    Returns: A single filename if image_type is KERNEL or RAMDISK
738
946
    if image_type == ImageType.DISK_VHD:
739
947
        vdis = _fetch_vhd_image(context, session, instance, image_id)
740
948
    else:
741
 
        vdis = _fetch_disk_image(context, session, instance, image_id,
742
 
                                 image_type)
 
949
        vdis = _fetch_disk_image(context, session, instance, name_label,
 
950
                                 image_id, image_type)
743
951
 
744
952
    for vdi_type, vdi in vdis.iteritems():
745
953
        vdi_uuid = vdi['uuid']
746
954
        LOG.debug(_("Fetched VDIs of type '%(vdi_type)s' with UUID"
747
 
                    "  '%(vdi_uuid)s'"),
 
955
                    " '%(vdi_uuid)s'"),
748
956
                  locals(), instance=instance)
749
957
 
750
958
    return vdis
779
987
    raise exception.CouldNotFetchImage(image_id=image_id)
780
988
 
781
989
 
782
 
def _fetch_vhd_image(context, session, instance, image_id):
783
 
    """Tell glance to download an image and put the VHDs into the SR
784
 
 
785
 
    Returns: A list of dictionaries that describe VDIs
786
 
    """
787
 
    LOG.debug(_("Asking xapi to fetch vhd image %(image_id)s"), locals(),
788
 
              instance=instance)
789
 
 
 
990
def _make_uuid_stack():
790
991
    # NOTE(sirp): The XenAPI plugins run under Python 2.4
791
992
    # which does not have the `uuid` module. To work around this,
792
993
    # we generate the uuids here (under Python 2.6+) and
793
994
    # pass them as arguments
 
995
    return [str(uuid.uuid4()) for i in xrange(MAX_VDI_CHAIN_SIZE)]
 
996
 
 
997
 
 
998
def _fetch_vhd_image(context, session, instance, image_id):
 
999
    """Tell glance to download an image and put the VHDs into the SR
 
1000
 
 
1001
    Returns: A list of dictionaries that describe VDIs
 
1002
    """
 
1003
    LOG.debug(_("Asking xapi to fetch vhd image %(image_id)s"), locals(),
 
1004
              instance=instance)
 
1005
 
794
1006
    params = {'image_id': image_id,
795
 
              'uuid_stack': [str(uuid.uuid4()) for i in xrange(3)],
 
1007
              'uuid_stack': _make_uuid_stack(),
796
1008
              'sr_path': get_sr_path(session),
797
1009
              'auth_token': getattr(context, 'auth_token', None)}
798
1010
 
 
1011
    glance_api_servers = glance.get_api_servers()
 
1012
 
799
1013
    def pick_glance(params):
800
 
        glance_host, glance_port = glance.pick_glance_api_server()
 
1014
        glance_host, glance_port = glance_api_servers.next()
801
1015
        params['glance_host'] = glance_host
802
1016
        params['glance_port'] = glance_port
803
1017
 
807
1021
            callback=pick_glance)
808
1022
 
809
1023
    sr_ref = safe_find_sr(session)
810
 
    scan_sr(session, sr_ref)
 
1024
    _scan_sr(session, sr_ref)
811
1025
 
812
1026
    # Pull out the UUID of the root VDI
813
1027
    root_vdi_uuid = vdis['root']['uuid']
814
1028
 
815
1029
    # Set the name-label to ease debugging
816
 
    set_vdi_name(session, root_vdi_uuid, instance.name, 'root')
 
1030
    set_vdi_name(session, root_vdi_uuid, instance['name'], 'root')
817
1031
 
818
1032
    _check_vdi_size(context, session, instance, root_vdi_uuid)
819
1033
    return vdis
827
1041
    the total.
828
1042
    """
829
1043
    size_bytes = 0
830
 
    for vdi_rec in walk_vdi_chain(session, vdi_uuid):
 
1044
    for vdi_rec in _walk_vdi_chain(session, vdi_uuid):
831
1045
        cur_vdi_uuid = vdi_rec['uuid']
832
1046
        vdi_size_bytes = int(vdi_rec['physical_utilisation'])
833
1047
        LOG.debug(_('vdi_uuid=%(cur_vdi_uuid)s vdi_size_bytes='
856
1070
        raise exception.ImageTooLarge()
857
1071
 
858
1072
 
859
 
def _fetch_disk_image(context, session, instance, image_id, image_type):
 
1073
def _fetch_disk_image(context, session, instance, name_label, image_id,
 
1074
                      image_type):
860
1075
    """Fetch the image from Glance
861
1076
 
862
1077
    NOTE:
875
1090
              locals(), instance=instance)
876
1091
 
877
1092
    if image_type == ImageType.DISK_ISO:
878
 
        sr_ref = safe_find_iso_sr(session)
 
1093
        sr_ref = _safe_find_iso_sr(session)
879
1094
    else:
880
1095
        sr_ref = safe_find_sr(session)
881
1096
 
896
1111
            _("Kernel/Ramdisk image is too large: %(vdi_size)d bytes, "
897
1112
              "max %(max_size)d bytes") % locals())
898
1113
 
899
 
    vdi_ref = create_vdi(session, sr_ref, instance, image_type_str, vdi_size)
 
1114
    vdi_ref = create_vdi(session, sr_ref, instance, name_label,
 
1115
                         image_type_str, vdi_size)
900
1116
    # From this point we have a VDI on Xen host;
901
1117
    # If anything goes wrong, we need to remember its uuid.
902
1118
    try:
1030
1246
            yield vm_ref, vm_rec
1031
1247
 
1032
1248
 
1033
 
def lookup(session, name_label):
1034
 
    """Look the instance up and return it if available"""
1035
 
    vm_refs = session.call_xenapi("VM.get_by_name_label", name_label)
1036
 
    n = len(vm_refs)
1037
 
    if n == 0:
1038
 
        return None
1039
 
    elif n > 1:
1040
 
        raise exception.InstanceExists(name=name_label)
1041
 
    else:
1042
 
        return vm_refs[0]
1043
 
 
1044
 
 
1045
 
def lookup_vm_vdis(session, vm_ref):
 
1249
def lookup_vm_vdis(session, vm_ref, nodestroys=None):
1046
1250
    """Look for the VDIs that are attached to the VM"""
1047
1251
    # Firstly we get the VBDs, then the VDIs.
1048
1252
    # TODO(Armando): do we leave the read-only devices?
1058
1262
            except session.XenAPI.Failure, exc:
1059
1263
                LOG.exception(exc)
1060
1264
            else:
1061
 
                vdi_refs.append(vdi_ref)
 
1265
                if not nodestroys or record['uuid'] not in nodestroys:
 
1266
                    vdi_refs.append(vdi_ref)
1062
1267
    return vdi_refs
1063
1268
 
1064
1269
 
 
1270
def lookup(session, name_label):
 
1271
    """Look the instance up and return it if available"""
 
1272
    vm_refs = session.call_xenapi("VM.get_by_name_label", name_label)
 
1273
    n = len(vm_refs)
 
1274
    if n == 0:
 
1275
        return None
 
1276
    elif n > 1:
 
1277
        raise exception.InstanceExists(name=name_label)
 
1278
    else:
 
1279
        return vm_refs[0]
 
1280
 
 
1281
 
1065
1282
def preconfigure_instance(session, instance, vdi_ref, network_info):
1066
1283
    """Makes alterations to the image before launching as part of spawn.
1067
1284
    """
1110
1327
        keys = []
1111
1328
        diags = {}
1112
1329
        vm_uuid = record["uuid"]
1113
 
        xml = get_rrd(get_rrd_server(), vm_uuid)
 
1330
        xml = _get_rrd(_get_rrd_server(), vm_uuid)
1114
1331
        if xml:
1115
1332
            rrd = minidom.parseString(xml)
1116
1333
            for i, node in enumerate(rrd.firstChild.childNodes):
1144
1361
       this host"""
1145
1362
    start_time = int(start_time)
1146
1363
 
1147
 
    xml = get_rrd_updates(get_rrd_server(), start_time)
 
1364
    xml = _get_rrd_updates(_get_rrd_server(), start_time)
1148
1365
    if xml:
1149
1366
        doc = minidom.parseString(xml)
1150
 
        return parse_rrd_update(doc, start_time, stop_time)
 
1367
        return _parse_rrd_update(doc, start_time, stop_time)
1151
1368
 
1152
1369
    raise exception.CouldNotFetchMetrics()
1153
1370
 
1154
1371
 
1155
 
def scan_sr(session, sr_ref=None):
 
1372
def _scan_sr(session, sr_ref=None):
1156
1373
    """Scans the SR specified by sr_ref"""
1157
1374
    if sr_ref:
1158
1375
        LOG.debug(_("Re-scanning SR %s"), sr_ref)
1161
1378
 
1162
1379
def scan_default_sr(session):
1163
1380
    """Looks for the system default SR and triggers a re-scan"""
1164
 
    scan_sr(session, find_sr(session))
 
1381
    _scan_sr(session, _find_sr(session))
1165
1382
 
1166
1383
 
1167
1384
def safe_find_sr(session):
1168
 
    """Same as find_sr except raises a NotFound exception if SR cannot be
 
1385
    """Same as _find_sr except raises a NotFound exception if SR cannot be
1169
1386
    determined
1170
1387
    """
1171
 
    sr_ref = find_sr(session)
 
1388
    sr_ref = _find_sr(session)
1172
1389
    if sr_ref is None:
1173
1390
        raise exception.StorageRepositoryNotFound()
1174
1391
    return sr_ref
1175
1392
 
1176
1393
 
1177
 
def find_sr(session):
 
1394
def _find_sr(session):
1178
1395
    """Return the storage repository to hold VM images"""
1179
1396
    host = session.get_xenapi_host()
1180
1397
    try:
1208
1425
    return None
1209
1426
 
1210
1427
 
1211
 
def safe_find_iso_sr(session):
1212
 
    """Same as find_iso_sr except raises a NotFound exception if SR
 
1428
def _safe_find_iso_sr(session):
 
1429
    """Same as _find_iso_sr except raises a NotFound exception if SR
1213
1430
    cannot be determined
1214
1431
    """
1215
 
    sr_ref = find_iso_sr(session)
 
1432
    sr_ref = _find_iso_sr(session)
1216
1433
    if sr_ref is None:
1217
1434
        raise exception.NotFound(_('Cannot find SR of content-type ISO'))
1218
1435
    return sr_ref
1219
1436
 
1220
1437
 
1221
 
def find_iso_sr(session):
 
1438
def _find_iso_sr(session):
1222
1439
    """Return the storage repository to hold ISO images"""
1223
1440
    host = session.get_xenapi_host()
1224
1441
    for sr_ref, sr_rec in session.get_all_refs_and_recs('SR'):
1250
1467
    return None
1251
1468
 
1252
1469
 
1253
 
def get_rrd_server():
 
1470
def _get_rrd_server():
1254
1471
    """Return server's scheme and address to use for retrieving RRD XMLs."""
1255
1472
    xs_url = urlparse.urlparse(FLAGS.xenapi_connection_url)
1256
1473
    return [xs_url.scheme, xs_url.netloc]
1257
1474
 
1258
1475
 
1259
 
def get_rrd(server, vm_uuid):
 
1476
def _get_rrd(server, vm_uuid):
1260
1477
    """Return the VM RRD XML as a string"""
1261
1478
    try:
1262
1479
        xml = urllib.urlopen("%s://%s:%s@%s/vm_rrd?uuid=%s" % (
1272
1489
        return None
1273
1490
 
1274
1491
 
1275
 
def get_rrd_updates(server, start_time):
 
1492
def _get_rrd_updates(server, start_time):
1276
1493
    """Return the RRD updates XML as a string"""
1277
1494
    try:
1278
1495
        xml = urllib.urlopen("%s://%s:%s@%s/rrd_updates?start=%s" % (
1288
1505
        return None
1289
1506
 
1290
1507
 
1291
 
def parse_rrd_meta(doc):
 
1508
def _parse_rrd_meta(doc):
1292
1509
    data = {}
1293
1510
    meta = doc.getElementsByTagName('meta')[0]
1294
1511
    for tag in ('start', 'end', 'step'):
1298
1515
    return data
1299
1516
 
1300
1517
 
1301
 
def parse_rrd_data(doc):
 
1518
def _parse_rrd_data(doc):
1302
1519
    dnode = doc.getElementsByTagName('data')[0]
1303
1520
    return [dict(
1304
1521
            time=int(child.getElementsByTagName('t')[0].firstChild.data),
1307
1524
            for child in dnode.childNodes]
1308
1525
 
1309
1526
 
1310
 
def parse_rrd_update(doc, start, until=None):
 
1527
def _parse_rrd_update(doc, start, until=None):
1311
1528
    sum_data = {}
1312
 
    meta = parse_rrd_meta(doc)
1313
 
    data = parse_rrd_data(doc)
 
1529
    meta = _parse_rrd_meta(doc)
 
1530
    data = _parse_rrd_data(doc)
1314
1531
    for col, collabel in enumerate(meta['legend']):
1315
1532
        _datatype, _objtype, uuid, name = collabel.split(':')
1316
1533
        vm_data = sum_data.get(uuid, dict())
1317
1534
        if name.startswith('vif'):
1318
 
            vm_data[name] = integrate_series(data, col, start, until)
 
1535
            vm_data[name] = _integrate_series(data, col, start, until)
1319
1536
        else:
1320
 
            vm_data[name] = average_series(data, col, until)
 
1537
            vm_data[name] = _average_series(data, col, until)
1321
1538
        sum_data[uuid] = vm_data
1322
1539
    return sum_data
1323
1540
 
1324
1541
 
1325
 
def average_series(data, col, until=None):
 
1542
def _average_series(data, col, until=None):
1326
1543
    vals = [row['values'][col] for row in data
1327
1544
            if (not until or (row['time'] <= until)) and
1328
1545
                row['values'][col].is_finite()]
1344
1561
        return decimal.Decimal('0.0000')
1345
1562
 
1346
1563
 
1347
 
def integrate_series(data, col, start, until=None):
 
1564
def _integrate_series(data, col, start, until=None):
1348
1565
    total = decimal.Decimal('0.0000')
1349
1566
    prev_time = int(start)
1350
1567
    prev_val = None
1378
1595
            continue
1379
1596
 
1380
1597
 
1381
 
#TODO(sirp): This code comes from XS5.6 pluginlib.py, we should refactor to
1382
 
# use that implmenetation
1383
 
def get_vhd_parent(session, vdi_rec):
1384
 
    """
1385
 
    Returns the VHD parent of the given VDI record, as a (ref, rec) pair.
1386
 
    Returns None if we're at the root of the tree.
1387
 
    """
1388
 
    if 'vhd-parent' in vdi_rec['sm_config']:
1389
 
        parent_uuid = vdi_rec['sm_config']['vhd-parent']
1390
 
        parent_ref = session.call_xenapi("VDI.get_by_uuid", parent_uuid)
1391
 
        parent_rec = session.call_xenapi("VDI.get_record", parent_ref)
1392
 
        vdi_uuid = vdi_rec['uuid']
1393
 
        LOG.debug(_("VHD %(vdi_uuid)s has parent %(parent_ref)s") % locals())
1394
 
        return parent_ref, parent_rec
1395
 
    else:
1396
 
        return None
1397
 
 
1398
 
 
1399
 
def get_vhd_parent_uuid(session, vdi_ref):
 
1598
def _get_vhd_parent_uuid(session, vdi_ref):
1400
1599
    vdi_rec = session.call_xenapi("VDI.get_record", vdi_ref)
1401
 
    ret = get_vhd_parent(session, vdi_rec)
1402
 
    if ret:
1403
 
        _parent_ref, parent_rec = ret
1404
 
        return parent_rec["uuid"]
1405
 
    else:
 
1600
 
 
1601
    if 'vhd-parent' not in vdi_rec['sm_config']:
1406
1602
        return None
1407
1603
 
1408
 
 
1409
 
def walk_vdi_chain(session, vdi_uuid):
 
1604
    parent_uuid = vdi_rec['sm_config']['vhd-parent']
 
1605
    vdi_uuid = vdi_rec['uuid']
 
1606
    LOG.debug(_("VHD %(vdi_uuid)s has parent %(parent_uuid)s") % locals())
 
1607
    return parent_uuid
 
1608
 
 
1609
 
 
1610
def _walk_vdi_chain(session, vdi_uuid):
1410
1611
    """Yield vdi_recs for each element in a VDI chain"""
1411
 
    # TODO(jk0): perhaps make get_vhd_parent use this
 
1612
    scan_default_sr(session)
1412
1613
    while True:
1413
1614
        vdi_ref = session.call_xenapi("VDI.get_by_uuid", vdi_uuid)
1414
1615
        vdi_rec = session.call_xenapi("VDI.get_record", vdi_ref)
1415
1616
        yield vdi_rec
1416
1617
 
1417
 
        parent_uuid = vdi_rec['sm_config'].get('vhd-parent')
1418
 
        if parent_uuid:
1419
 
            vdi_uuid = parent_uuid
1420
 
        else:
 
1618
        parent_uuid = _get_vhd_parent_uuid(session, vdi_ref)
 
1619
        if not parent_uuid:
1421
1620
            break
1422
1621
 
 
1622
        vdi_uuid = parent_uuid
 
1623
 
 
1624
 
 
1625
def _child_vhds(session, sr_ref, vdi_uuid):
 
1626
    """Return the immediate children of a given VHD.
 
1627
 
 
1628
    This is not recursive, only the immediate children are returned.
 
1629
    """
 
1630
    children = set()
 
1631
    for ref, rec in _get_all_vdis_in_sr(session, sr_ref):
 
1632
        rec_uuid = rec['uuid']
 
1633
 
 
1634
        if rec_uuid == vdi_uuid:
 
1635
            continue
 
1636
 
 
1637
        parent_uuid = _get_vhd_parent_uuid(session, ref)
 
1638
        if parent_uuid != vdi_uuid:
 
1639
            continue
 
1640
 
 
1641
        children.add(rec_uuid)
 
1642
 
 
1643
    return children
 
1644
 
1423
1645
 
1424
1646
def _wait_for_vhd_coalesce(session, instance, sr_ref, vdi_ref,
1425
1647
                           original_parent_uuid):
1441
1663
        # Search for any other vdi which parents to original parent and is not
1442
1664
        # in the active vm/instance vdi chain.
1443
1665
        vdi_uuid = session.call_xenapi('VDI.get_record', vdi_ref)['uuid']
1444
 
        parent_vdi_uuid = get_vhd_parent_uuid(session, vdi_ref)
 
1666
        parent_vdi_uuid = _get_vhd_parent_uuid(session, vdi_ref)
1445
1667
        for _ref, rec in _get_all_vdis_in_sr(session, sr_ref):
1446
1668
            if ((rec['uuid'] != vdi_uuid) and
1447
1669
               (rec['uuid'] != parent_vdi_uuid) and
1454
1676
    # Check if original parent has any other child. If so, coalesce will
1455
1677
    # not take place.
1456
1678
    if _another_child_vhd():
1457
 
        parent_uuid = get_vhd_parent_uuid(session, vdi_ref)
 
1679
        parent_uuid = _get_vhd_parent_uuid(session, vdi_ref)
1458
1680
        parent_ref = session.call_xenapi("VDI.get_by_uuid", parent_uuid)
1459
 
        base_uuid = get_vhd_parent_uuid(session, parent_ref)
 
1681
        base_uuid = _get_vhd_parent_uuid(session, parent_ref)
1460
1682
        return parent_uuid, base_uuid
1461
1683
 
 
1684
    # NOTE(sirp): This rescan is necessary to ensure the VM's `sm_config`
 
1685
    # matches the underlying VHDs.
 
1686
    _scan_sr(session, sr_ref)
 
1687
 
1462
1688
    max_attempts = FLAGS.xenapi_vhd_coalesce_max_attempts
1463
1689
    for i in xrange(max_attempts):
1464
 
        scan_sr(session, sr_ref)
1465
 
        parent_uuid = get_vhd_parent_uuid(session, vdi_ref)
 
1690
        _scan_sr(session, sr_ref)
 
1691
        parent_uuid = _get_vhd_parent_uuid(session, vdi_ref)
1466
1692
        if original_parent_uuid and (parent_uuid != original_parent_uuid):
1467
1693
            LOG.debug(_("Parent %(parent_uuid)s doesn't match original parent"
1468
1694
                        " %(original_parent_uuid)s, waiting for coalesce..."),
1469
1695
                      locals(), instance=instance)
1470
1696
        else:
1471
1697
            parent_ref = session.call_xenapi("VDI.get_by_uuid", parent_uuid)
1472
 
            base_uuid = get_vhd_parent_uuid(session, parent_ref)
 
1698
            base_uuid = _get_vhd_parent_uuid(session, parent_ref)
1473
1699
            return parent_uuid, base_uuid
1474
1700
 
1475
1701
        greenthread.sleep(FLAGS.xenapi_vhd_coalesce_poll_interval)
1479
1705
    raise exception.NovaException(msg)
1480
1706
 
1481
1707
 
1482
 
def remap_vbd_dev(dev):
 
1708
def _remap_vbd_dev(dev):
1483
1709
    """Return the appropriate location for a plugged-in VBD device
1484
1710
 
1485
1711
    Ubuntu Maverick moved xvd? -> sd?. This is considered a bug and will be
1514
1740
 
1515
1741
def cleanup_attached_vdis(session):
1516
1742
    """Unplug any instance VDIs left after an unclean restart"""
1517
 
    this_vm_ref = get_this_vm_ref(session)
 
1743
    this_vm_ref = _get_this_vm_ref(session)
1518
1744
 
1519
1745
    vbd_refs = session.call_xenapi('VM.get_VBDs', this_vm_ref)
1520
1746
    for vbd_ref in vbd_refs:
1537
1763
 
1538
1764
@contextlib.contextmanager
1539
1765
def vdi_attached_here(session, vdi_ref, read_only=False):
1540
 
    this_vm_ref = get_this_vm_ref(session)
 
1766
    this_vm_ref = _get_this_vm_ref(session)
1541
1767
 
1542
1768
    vbd_ref = create_vbd(session, this_vm_ref, vdi_ref, 'autodetect',
1543
1769
                         read_only=read_only, bootable=False)
1548
1774
            LOG.debug(_('Plugging VBD %s done.'), vbd_ref)
1549
1775
            orig_dev = session.call_xenapi("VBD.get_device", vbd_ref)
1550
1776
            LOG.debug(_('VBD %(vbd_ref)s plugged as %(orig_dev)s') % locals())
1551
 
            dev = remap_vbd_dev(orig_dev)
 
1777
            dev = _remap_vbd_dev(orig_dev)
1552
1778
            if dev != orig_dev:
1553
1779
                LOG.debug(_('VBD %(vbd_ref)s plugged into wrong dev, '
1554
1780
                            'remapping to %(dev)s') % locals())
1571
1797
        return f.readline().strip()
1572
1798
 
1573
1799
 
1574
 
def get_this_vm_ref(session):
 
1800
def _get_this_vm_ref(session):
1575
1801
    return session.call_xenapi("VM.get_by_uuid", get_this_vm_uuid())
1576
1802
 
1577
1803
 
1808
2034
                if not _find_guest_agent(tmpdir, FLAGS.xenapi_agent_path):
1809
2035
                    LOG.info(_('Manipulating interface files directly'))
1810
2036
                    # for xenapi, we don't 'inject' admin_password here,
1811
 
                    # it's handled at instance startup time
 
2037
                    # it's handled at instance startup time, nor do we
 
2038
                    # support injecting arbitrary files here.
1812
2039
                    disk.inject_data_into_fs(tmpdir,
1813
 
                                             key, net, None, metadata,
1814
 
                                             utils.execute)
 
2040
                                             key, net, metadata, None, None)
1815
2041
            finally:
1816
2042
                utils.execute('umount', dev_path, run_as_root=True)
1817
2043
        else:
1929
2155
            raise
1930
2156
        raise Exception(_('This domU must be running on the host '
1931
2157
                          'specified by xenapi_connection_url'))
 
2158
 
 
2159
 
 
2160
def move_disks(session, instance, disk_info):
 
2161
    """Move and possibly link VHDs via the XAPI plugin."""
 
2162
    params = {'instance_uuid': instance['uuid'],
 
2163
              'sr_path': get_sr_path(session),
 
2164
              'uuid_stack': _make_uuid_stack()}
 
2165
 
 
2166
    result = session.call_plugin(
 
2167
            'migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)})
 
2168
    imported_vhds = jsonutils.loads(result)
 
2169
 
 
2170
    # Now we rescan the SR so we find the VHDs
 
2171
    scan_default_sr(session)
 
2172
 
 
2173
    # Set name-label so we can find if we need to clean up a failed
 
2174
    # migration
 
2175
    root_uuid = imported_vhds['root']['uuid']
 
2176
    set_vdi_name(session, root_uuid, instance['name'], 'root')
 
2177
 
 
2178
    root_vdi_ref = session.call_xenapi('VDI.get_by_uuid', root_uuid)
 
2179
 
 
2180
    return {'uuid': root_uuid, 'ref': root_vdi_ref}