summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonal Lafferty <donal.lafferty@citrix.com>2011-08-14 04:25:46 +0000
committerTarmac <>2011-08-14 04:25:46 +0000
commit1fe628fbe1f9964ac4536ce1c859d84d9cd8cb08 (patch)
treeb8c3d4fbe3e9225c9acdcc777fea6663b9111bab
parenteede601db836643a0fbc6689fb9ee9db15a822bc (diff)
parentd2aa9ddcb26b1217de55fc5cd6c886059e781cda (diff)
Added ability too boot VM from install ISO. System detects an image of type iso. Images is streamed to a VDI and mounted to the VM. Blank disk allocated to VM based on instance type.
Currently available for XenServer.
-rw-r--r--Authors1
-rw-r--r--nova/tests/glance/stubs.py6
-rw-r--r--nova/tests/test_xenapi.py5
-rw-r--r--nova/virt/xenapi/fake.py20
-rw-r--r--nova/virt/xenapi/vm_utils.py109
-rw-r--r--nova/virt/xenapi/vmops.py56
6 files changed, 179 insertions, 18 deletions
diff --git a/Authors b/Authors
index ccd70baaf..531994cb0 100644
--- a/Authors
+++ b/Authors
@@ -27,6 +27,7 @@ David Pravec <David.Pravec@danix.org>
Dean Troyer <dtroyer@gmail.com>
Devendra Modium <dmodium@isi.edu>
Devin Carlen <devin.carlen@gmail.com>
+Donal Lafferty <donal.lafferty@citrix.com>
Ed Leafe <ed@leafe.com>
Eldar Nugaev <reldan@oscloud.ru>
Eric Day <eday@oddments.org>
diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py
index d51b19ccd..f2a19f22d 100644
--- a/nova/tests/glance/stubs.py
+++ b/nova/tests/glance/stubs.py
@@ -32,6 +32,7 @@ class FakeGlance(object):
IMAGE_RAMDISK = 3
IMAGE_RAW = 4
IMAGE_VHD = 5
+ IMAGE_ISO = 6
IMAGE_FIXTURES = {
IMAGE_MACHINE: {
@@ -58,6 +59,11 @@ class FakeGlance(object):
'image_meta': {'name': 'fakevhd', 'size': 0,
'disk_format': 'vhd',
'container_format': 'ovf'},
+ 'image_data': StringIO.StringIO('')},
+ IMAGE_ISO: {
+ 'image_meta': {'name': 'fakeiso', 'size': 0,
+ 'disk_format': 'iso',
+ 'container_format': 'bare'},
'image_data': StringIO.StringIO('')}}
def __init__(self, host, port=None, use_ssl=False, auth_tok=None):
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 1deb5a780..2f0559366 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -519,6 +519,11 @@ class XenAPIVMTestCase(test.TestCase):
os_type="windows", architecture="i386")
self.check_vm_params_for_windows()
+ def test_spawn_iso_glance(self):
+ self._test_spawn(glance_stubs.FakeGlance.IMAGE_ISO, None, None,
+ os_type="windows", architecture="i386")
+ self.check_vm_params_for_windows()
+
def test_spawn_glance(self):
self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE,
glance_stubs.FakeGlance.IMAGE_KERNEL,
diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py
index d5ac39473..1aa642e4e 100644
--- a/nova/virt/xenapi/fake.py
+++ b/nova/virt/xenapi/fake.py
@@ -194,6 +194,7 @@ def create_local_pifs():
Do this one per host."""
for host_ref in _db_content['host'].keys():
_create_local_pif(host_ref)
+ _create_local_sr_iso(host_ref)
def create_local_srs():
@@ -222,6 +223,25 @@ def _create_local_sr(host_ref):
return sr_ref
+def _create_local_sr_iso(host_ref):
+ sr_ref = _create_object(
+ 'SR',
+ {'name_label': 'Local storage ISO',
+ 'type': 'lvm',
+ 'content_type': 'iso',
+ 'shared': False,
+ 'physical_size': str(1 << 30),
+ 'physical_utilisation': str(0),
+ 'virtual_allocation': str(0),
+ 'other_config': {
+ 'i18n-original-value-name_label': 'Local storage ISO',
+ 'i18n-key': 'local-storage-iso'},
+ 'VDIs': []})
+ pbd_ref = create_pbd('', host_ref, sr_ref, True)
+ _db_content['SR'][sr_ref]['PBDs'] = [pbd_ref]
+ return sr_ref
+
+
def _create_local_pif(host_ref):
pif_ref = _create_object('PIF',
{'name-label': 'Fake PIF',
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 6c44d53d4..ba5cf4b49 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -77,6 +77,7 @@ class ImageType:
3 - raw disk image (local SR, NOT partitioned by plugin)
4 - vhd disk image (local SR, NOT inspected by XS, PV assumed for
linux, HVM assumed for Windows)
+ 5 - ISO disk image (local SR, NOT partitioned by plugin)
"""
KERNEL = 0
@@ -84,14 +85,17 @@ class ImageType:
DISK = 2
DISK_RAW = 3
DISK_VHD = 4
- _ids = (KERNEL, RAMDISK, DISK, DISK_RAW, DISK_VHD)
+ DISK_ISO = 5
+ _ids = (KERNEL, RAMDISK, DISK, DISK_RAW, DISK_VHD, DISK_ISO)
KERNEL_STR = "kernel"
RAMDISK_STR = "ramdisk"
DISK_STR = "os"
DISK_RAW_STR = "os_raw"
DISK_VHD_STR = "vhd"
- _strs = (KERNEL_STR, RAMDISK_STR, DISK_STR, DISK_RAW_STR, DISK_VHD_STR)
+ DISK_ISO_STR = "iso"
+ _strs = (KERNEL_STR, RAMDISK_STR, DISK_STR, DISK_RAW_STR, DISK_VHD_STR,
+ DISK_ISO_STR)
@classmethod
def to_string(cls, image_type):
@@ -223,6 +227,30 @@ class VMHelper(HelperBase):
return vbd_ref
@classmethod
+ def create_cd_vbd(cls, session, vm_ref, vdi_ref, userdevice, bootable):
+ """Create a VBD record. Returns a Deferred that gives the new
+ VBD reference specific to CDRom devices."""
+ vbd_rec = {}
+ vbd_rec['VM'] = vm_ref
+ vbd_rec['VDI'] = vdi_ref
+ vbd_rec['userdevice'] = str(userdevice)
+ vbd_rec['bootable'] = bootable
+ vbd_rec['mode'] = 'RO'
+ vbd_rec['type'] = 'CD'
+ vbd_rec['unpluggable'] = True
+ vbd_rec['empty'] = False
+ vbd_rec['other_config'] = {}
+ vbd_rec['qos_algorithm_type'] = ''
+ vbd_rec['qos_algorithm_params'] = {}
+ vbd_rec['qos_supported_algorithms'] = []
+ LOG.debug(_('Creating a CDROM-specific VBD for VM %(vm_ref)s,'
+ ' VDI %(vdi_ref)s ... ') % locals())
+ vbd_ref = session.call_xenapi('VBD.create', vbd_rec)
+ LOG.debug(_('Created a CDROM-specific VBD %(vbd_ref)s '
+ ' for VM %(vm_ref)s, VDI %(vdi_ref)s.') % locals())
+ return vbd_ref
+
+ @classmethod
def find_vbd_by_number(cls, session, vm_ref, number):
"""Get the VBD reference from the device number"""
vbd_refs = session.get_xenapi().VM.get_VBDs(vm_ref)
@@ -368,6 +396,23 @@ class VMHelper(HelperBase):
session.wait_for_task(task, instance.id)
@classmethod
+ def fetch_blank_disk(cls, session, instance_type_id):
+ # Size the blank harddrive to suit the machine type:
+ one_gig = 1024 * 1024 * 1024
+ req_type = instance_types.get_instance_type(instance_type_id)
+ req_size = req_type['local_gb']
+
+ LOG.debug("Creating blank HD of size %(req_size)d gigs"
+ % locals())
+ vdi_size = one_gig * req_size
+
+ LOG.debug("ISO vm create: Looking for the SR")
+ sr_ref = safe_find_sr(session)
+
+ vdi_ref = cls.create_vdi(session, sr_ref, 'blank HD', vdi_size, False)
+ return vdi_ref
+
+ @classmethod
def fetch_image(cls, context, session, instance_id, image, user_id,
project_id, image_type):
"""Fetch image from glance based on image type.
@@ -449,7 +494,12 @@ class VMHelper(HelperBase):
# DISK restores
LOG.debug(_("Fetching image %(image)s") % locals())
LOG.debug(_("Image Type: %s"), ImageType.to_string(image_type))
- sr_ref = safe_find_sr(session)
+
+ if image_type == ImageType.DISK_ISO:
+ sr_ref = safe_find_iso_sr(session)
+ LOG.debug(_("ISO: Found sr possibly containing the ISO image"))
+ else:
+ sr_ref = safe_find_sr(session)
glance_client, image_id = nova.image.get_glance_client(image)
glance_client.set_auth_token(getattr(context, 'auth_token', None))
@@ -527,7 +577,8 @@ class VMHelper(HelperBase):
ImageType.RAMDISK: 'RAMDISK',
ImageType.DISK: 'DISK',
ImageType.DISK_RAW: 'DISK_RAW',
- ImageType.DISK_VHD: 'DISK_VHD'}
+ ImageType.DISK_VHD: 'DISK_VHD',
+ ImageType.DISK_ISO: 'DISK_ISO'}
disk_format = pretty_format[image_type]
image_ref = instance.image_ref
instance_id = instance.id
@@ -540,7 +591,8 @@ class VMHelper(HelperBase):
'aki': ImageType.KERNEL,
'ari': ImageType.RAMDISK,
'raw': ImageType.DISK_RAW,
- 'vhd': ImageType.DISK_VHD}
+ 'vhd': ImageType.DISK_VHD,
+ 'iso': ImageType.DISK_ISO}
image_ref = instance.image_ref
glance_client, image_id = nova.image.get_glance_client(image_ref)
meta = glance_client.get_image_meta(image_id)
@@ -574,6 +626,8 @@ class VMHelper(HelperBase):
available
3. Glance (DISK): pv is assumed
+
+ 4. Glance (DISK_ISO): no pv is assumed
"""
LOG.debug(_("Looking up vdi %s for PV kernel"), vdi_ref)
@@ -589,6 +643,9 @@ class VMHelper(HelperBase):
elif disk_image_type == ImageType.DISK:
# 3. Disk
is_pv = True
+ elif disk_image_type == ImageType.DISK_ISO:
+ # 4. ISO
+ is_pv = False
else:
raise exception.Error(_("Unknown image format %(disk_image_type)s")
% locals())
@@ -832,6 +889,48 @@ def find_sr(session):
return None
+def safe_find_iso_sr(session):
+ """Same as find_iso_sr except raises a NotFound exception if SR cannot be
+ determined
+ """
+ sr_ref = find_iso_sr(session)
+ if sr_ref is None:
+ raise exception.NotFound(_('Cannot find SR of content-type ISO'))
+ return sr_ref
+
+
+def find_iso_sr(session):
+ """Return the storage repository to hold ISO images"""
+ host = session.get_xenapi_host()
+ sr_refs = session.get_xenapi().SR.get_all()
+ for sr_ref in sr_refs:
+ sr_rec = session.get_xenapi().SR.get_record(sr_ref)
+
+ LOG.debug(_("ISO: looking at SR %(sr_rec)s") % locals())
+ if not sr_rec['content_type'] == 'iso':
+ LOG.debug(_("ISO: not iso content"))
+ continue
+ if not 'i18n-key' in sr_rec['other_config']:
+ LOG.debug(_("ISO: iso content_type, no 'i18n-key' key"))
+ continue
+ if not sr_rec['other_config']['i18n-key'] == 'local-storage-iso':
+ LOG.debug(_("ISO: iso content_type, i18n-key value not "
+ "'local-storage-iso'"))
+ continue
+
+ LOG.debug(_("ISO: SR MATCHing our criteria"))
+ for pbd_ref in sr_rec['PBDs']:
+ LOG.debug(_("ISO: ISO, looking to see if it is host local"))
+ pbd_rec = session.get_xenapi().PBD.get_record(pbd_ref)
+ pbd_rec_host = pbd_rec['host']
+ LOG.debug(_("ISO: PBD matching, want %(pbd_rec)s, have %(host)s") %
+ locals())
+ if pbd_rec_host == host:
+ LOG.debug(_("ISO: SR with local PBD"))
+ return sr_ref
+ return None
+
+
def remap_vbd_dev(dev):
"""Return the appropriate location for a plugged-in VBD device
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index b1522729a..1fefd1291 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -235,12 +235,51 @@ class VMOps(object):
raise vm_create_error
- VMHelper.create_vbd(session=self._session, vm_ref=vm_ref,
- vdi_ref=first_vdi_ref, userdevice=0, bootable=True)
+ # Add disks to VM
+ self._attach_disks(instance, disk_image_type, vm_ref, first_vdi_ref,
+ vdis)
+
+ # Alter the image before VM start for, e.g. network injection
+ if FLAGS.flat_injected:
+ VMHelper.preconfigure_instance(self._session, instance,
+ first_vdi_ref, network_info)
+
+ self.create_vifs(vm_ref, instance, network_info)
+ self.inject_network_info(instance, network_info, vm_ref)
+ return vm_ref
+
+ def _attach_disks(self, instance, disk_image_type, vm_ref, first_vdi_ref,
+ vdis):
+ # device 0 reserved for RW disk
+ userdevice = 0
+
+ # DISK_ISO needs two VBDs: the ISO disk and a blank RW disk
+ if disk_image_type == ImageType.DISK_ISO:
+ LOG.debug("detected ISO image type, going to create blank VM for "
+ "install")
+
+ cd_vdi_ref = first_vdi_ref
+ first_vdi_ref = VMHelper.fetch_blank_disk(session=self._session,
+ instance_type_id=instance.instance_type_id)
+
+ VMHelper.create_vbd(session=self._session, vm_ref=vm_ref,
+ vdi_ref=first_vdi_ref, userdevice=userdevice, bootable=False)
+
+ # device 1 reserved for rescue disk and we've used '0'
+ userdevice = 2
+ VMHelper.create_cd_vbd(session=self._session, vm_ref=vm_ref,
+ vdi_ref=cd_vdi_ref, userdevice=userdevice, bootable=True)
+
+ # set user device to next free value
+ userdevice += 1
+ else:
+ VMHelper.create_vbd(session=self._session, vm_ref=vm_ref,
+ vdi_ref=first_vdi_ref, userdevice=userdevice, bootable=True)
+ # set user device to next free value
+ # userdevice 1 is reserved for rescue and we've used '0'
+ userdevice = 2
# Attach any other disks
- # userdevice 1 is reserved for rescue
- userdevice = 2
for vdi in vdis[1:]:
# vdi['vdi_type'] is either 'os' or 'swap', but we don't
# really care what it is right here.
@@ -251,15 +290,6 @@ class VMOps(object):
bootable=False)
userdevice += 1
- # Alter the image before VM start for, e.g. network injection
- if FLAGS.flat_injected:
- VMHelper.preconfigure_instance(self._session, instance,
- first_vdi_ref, network_info)
-
- self.create_vifs(vm_ref, instance, network_info)
- self.inject_network_info(instance, network_info, vm_ref)
- return vm_ref
-
def _spawn(self, instance, vm_ref):
"""Spawn a new instance."""
LOG.debug(_('Starting VM %s...'), vm_ref)