From 41e4e18a0324593c0076c3936d63bb6dcca487cb Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Mon, 14 Feb 2011 23:12:34 +0000 Subject: First cut on XenServer unified-images --- nova/api/openstack/servers.py | 6 +++++- nova/virt/xenapi/vm_utils.py | 47 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 17c5519a1..1c9dcd9a6 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -140,7 +140,11 @@ class Controller(wsgi.Controller): image_id = str(image_id) image = self._image_service.show(req.environ['nova.context'], image_id) - return lookup('kernel_id'), lookup('ramdisk_id') + disk_format = image['properties'].get('disk_format', None) + if disk_format == "vhd": + return None, None + else: + return lookup('kernel_id'), lookup('ramdisk_id') def create(self, req): """ Creates a new server for a given user """ diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4bbd522c1..6d6067da5 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -266,7 +266,9 @@ class VMHelper(HelperBase): session, instance_id, sr_ref, vm_vdi_ref, original_parent_uuid) #TODO(sirp): we need to assert only one parent, not parents two deep - return template_vm_ref, [template_vdi_uuid, parent_uuid] + template_vdi_uuids = {'image': parent_uuid, + 'snap': template_vdi_uuid} + return template_vm_ref, template_vdi_uuids @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_id): @@ -303,15 +305,36 @@ class VMHelper(HelperBase): return cls._fetch_image_objectstore(session, instance_id, image, access, user.secret, type) + @classmethod - def _fetch_image_glance(cls, session, instance_id, image, access, type): + def _fetch_image_glance_vhd(cls, session, instance_id, image, access, type): + LOG.debug(_("Asking xapi to fetch vhd image %(image)s") + % locals()) + + sr_ref = find_sr(session) + if sr_ref is None: + raise exception.NotFound('Cannot find SR to write VDI to') + + params = {'image_id': image, + 'glance_host': FLAGS.glance_host, + 'glance_port': FLAGS.glance_port} + + kwargs = {'params': pickle.dumps(params)} + task = session.async_call_plugin('glance', 'get_vdi', kwargs) + vdi_uuid = session.wait_for_task(instance_id, task) + scan_sr(session, instance_id, sr_ref) + LOG.debug(_("Xapi 'get_vdi' returned VDI UUID %(vdi_uuid)s") % locals()) + return vdi_uuid + + @classmethod + def _fetch_image_glance_disk(cls, session, instance_id, image, access, type): sr = find_sr(session) if sr is None: raise exception.NotFound('Cannot find SR to write VDI to') - c = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) + client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) - meta, image_file = c.get_image(image) + meta, image_file = client.get_image(image) virtual_size = int(meta['size']) vdi_size = virtual_size LOG.debug(_("Size for image %(image)s:%(virtual_size)d") % locals()) @@ -344,6 +367,22 @@ class VMHelper(HelperBase): else: return session.get_xenapi().VDI.get_uuid(vdi) + @classmethod + def _fetch_image_glance(cls, session, instance_id, image, access, type): + client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) + meta = client.get_image_meta(image) + properties = meta['properties'] + disk_format = properties.get('disk_format', None) + + # TODO(sirp): When Glance treats disk_format as a first class + # attribute, we should start using that rather than an image-property + if disk_format == 'vhd': + return cls._fetch_image_glance_vhd( + session, instance_id, image, access, type) + else: + return cls._fetch_image_glance_disk( + session, instance_id, image, access, type) + @classmethod def _fetch_image_objectstore(cls, session, instance_id, image, access, secret, type): -- cgit From fe6efb38ee30c5a3e532cd19faef0fec063b7446 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 01:37:54 +0000 Subject: Adding DISK_VHD to ImageTypes --- nova/virt/xenapi/vm_utils.py | 38 ++++++++++++++++++++++++++++++-------- nova/virt/xenapi/vmops.py | 22 +++++++++++++++------- 2 files changed, 45 insertions(+), 15 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 6d6067da5..a6312d00c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -63,11 +63,14 @@ class ImageType: 0 - kernel/ramdisk image (goes on dom0's filesystem) 1 - disk image (local SR, partitioned by objectstore plugin) 2 - raw disk image (local SR, NOT partitioned by plugin) + 3 - vhd disk image (local SR, NOT inspected by XS, PV assumed for + linux, HVM assumed for Windows) """ KERNEL_RAMDISK = 0 DISK = 1 DISK_RAW = 2 + DISK_VHD = 3 class VMHelper(HelperBase): @@ -368,15 +371,34 @@ class VMHelper(HelperBase): return session.get_xenapi().VDI.get_uuid(vdi) @classmethod - def _fetch_image_glance(cls, session, instance_id, image, access, type): - client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) - meta = client.get_image_meta(image) - properties = meta['properties'] - disk_format = properties.get('disk_format', None) + def determine_disk_image_type(cls, instance): + instance_id = instance.id + if instance.kernel_id: + #if kernel is not present we must download a raw disk + LOG.debug(_("Instance %(instance_id)s will use DISK format") % + locals()) + return ImageType.DISK - # TODO(sirp): When Glance treats disk_format as a first class - # attribute, we should start using that rather than an image-property - if disk_format == 'vhd': + if FLAGS.xenapi_image_service == 'glance': + # if using glance, then we could be VHD format + client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) + meta = client.get_image_meta(instance.image_id) + properties = meta['properties'] + disk_format = properties.get('disk_format', None) + # TODO(sirp): When Glance treats disk_format as a first class + # attribute, we should start using that rather than an image-property + if disk_format == 'vhd': + LOG.debug(_("Instance %(instance_id)s will use DISK_VHD format") % + locals()) + return ImageType.DISK_VHD + + LOG.debug(_("Instance %(instance_id)s will use DISK_RAW format") % + locals()) + return ImageType.DISK_RAW + + @classmethod + def _fetch_image_glance(cls, session, instance_id, image, access, type): + if type == ImageType.DISK_VHD: return cls._fetch_image_glance_vhd( session, instance_id, image, access, type) else: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e84ce20c4..34ceea358 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -74,27 +74,34 @@ class VMOps(object): user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) - #if kernel is not present we must download a raw disk - if instance.kernel_id: - disk_image_type = ImageType.DISK - else: - disk_image_type = ImageType.DISK_RAW + + disk_image_type = VMHelper.determine_disk_image_type(instance) + vdi_uuid = VMHelper.fetch_image(self._session, instance.id, instance.image_id, user, project, disk_image_type) + vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - #Have a look at the VDI and see if it has a PV kernel + pv_kernel = False - if not instance.kernel_id: + if disk_image_type == ImageType.DISK_RAW: + #Have a look at the VDI and see if it has a PV kernel pv_kernel = VMHelper.lookup_image(self._session, instance.id, vdi_ref) + elif disk_image_type == ImageType.DISK_VHD: + # TODO(sirp): Assuming PV for now; this will need to be + # configurable as Windows will use HVM. + pv_kernel = True + kernel = None if instance.kernel_id: kernel = VMHelper.fetch_image(self._session, instance.id, instance.kernel_id, user, project, ImageType.KERNEL_RAMDISK) + ramdisk = None if instance.ramdisk_id: ramdisk = VMHelper.fetch_image(self._session, instance.id, instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK) + vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk, pv_kernel) VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True) @@ -102,6 +109,7 @@ class VMOps(object): if network_ref: VMHelper.create_vif(self._session, vm_ref, network_ref, instance.mac_address) + LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) instance_name = instance.name -- cgit From 15cdeef7820aacd0b1ff95da48816cba9f2544ba Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 18:01:13 +0000 Subject: Adding more documentation, code-cleanup --- nova/virt/xenapi/vm_utils.py | 52 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 12 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index a6312d00c..5eab2a38f 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -24,6 +24,7 @@ import pickle import re import time import urllib +import uuid from xml.dom import minidom from eventlet import event @@ -318,9 +319,15 @@ class VMHelper(HelperBase): if sr_ref is None: raise exception.NotFound('Cannot find SR to write VDI to') + # NOTE(sirp): The Glance plugin runs under Python 2.4 which does not + # have the `uuid` module. To work around this, we generate the uuids + # here (under Python 2.6+) and pass them as arguments + uuid_stack = [str(uuid.uuid4()) for i in xrange(2)] + params = {'image_id': image, 'glance_host': FLAGS.glance_host, - 'glance_port': FLAGS.glance_port} + 'glance_port': FLAGS.glance_port, + 'uuid_stack': uuid_stack} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'get_vdi', kwargs) @@ -372,14 +379,35 @@ class VMHelper(HelperBase): @classmethod def determine_disk_image_type(cls, instance): - instance_id = instance.id + """Disk Image Types are used to determine where the kernel will reside + within an image. To figure out which type we're dealing with, we use + the following rules: + + 1. If the instance is specifying a kernel explicitly, we must be using + a 'disk' image (kernel outside of the image) + + 2. If the kernel isn't specified, then we have two different + scenarios: + + a) If the image is in Glance, then we can use the 'disk_format' + property to determine if the image is really a VHD-style image + or if it's a RAW image + + b) If the image is not in Glance, then it must be a RAW image + (since we don't have a way of identifying VHD images...yet) + """ + def log_disk_format(disk_format): + disk_format = disk_format.upper() + image_id = instance.image_id + instance_id = instance.id + LOG.debug(_("Detected %(disk_format)s format for image " + "%(image_id)s, instance %(instance_id)s") % locals()) + if instance.kernel_id: - #if kernel is not present we must download a raw disk - LOG.debug(_("Instance %(instance_id)s will use DISK format") % - locals()) + # 1. DISK + log_disk_format('disk') return ImageType.DISK - - if FLAGS.xenapi_image_service == 'glance': + elif FLAGS.xenapi_image_service == 'glance': # if using glance, then we could be VHD format client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) meta = client.get_image_meta(instance.image_id) @@ -388,12 +416,12 @@ class VMHelper(HelperBase): # TODO(sirp): When Glance treats disk_format as a first class # attribute, we should start using that rather than an image-property if disk_format == 'vhd': - LOG.debug(_("Instance %(instance_id)s will use DISK_VHD format") % - locals()) + # 2a. DISK_VHD + log_disk_format('disk_vhd') return ImageType.DISK_VHD - - LOG.debug(_("Instance %(instance_id)s will use DISK_RAW format") % - locals()) + + # 2b. DISK_RAW + log_disk_format('disk_raw') return ImageType.DISK_RAW @classmethod -- cgit From 0fc3a184230c479254b9f713ea61de2f24f680ab Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 18:23:14 +0000 Subject: Moving SR path code outside of glance plugin --- nova/virt/xenapi/vm_utils.py | 21 +++++++++++++++++++-- nova/virt/xenapi_conn.py | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 5eab2a38f..e73bc7f2b 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -285,7 +285,8 @@ class VMHelper(HelperBase): params = {'vdi_uuids': vdi_uuids, 'image_id': image_id, 'glance_host': FLAGS.glance_host, - 'glance_port': FLAGS.glance_port} + 'glance_port': FLAGS.glance_port, + 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'put_vdis', kwargs) @@ -327,7 +328,8 @@ class VMHelper(HelperBase): params = {'image_id': image, 'glance_host': FLAGS.glance_host, 'glance_port': FLAGS.glance_port, - 'uuid_stack': uuid_stack} + 'uuid_stack': uuid_stack, + 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'get_vdi', kwargs) @@ -685,6 +687,21 @@ def find_sr(session): return None +def get_sr_path(session): + """Return the path to our Storage Repository + + This is used when we're dealing with VHDs directly, either by taking + snapshots or by restoring an image in the DISK_VHD format. + """ + # TODO(sirp): add safe_find_sr + sr_ref = find_sr(session) + if sr_ref is None: + raise Exception('Cannot find SR to read VDI from') + sr_rec = session.get_xenapi().SR.get_record(sr_ref) + sr_uuid = sr_rec["uuid"] + return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) + + def remap_vbd_dev(dev): """Return the appropriate location for a plugged-in VBD device diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index a0b0499b8..8ae5684b0 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -100,6 +100,8 @@ flags.DEFINE_integer('xenapi_vhd_coalesce_max_attempts', 5, 'Max number of times to poll for VHD to coalesce.' ' Used only if connection_type=xenapi.') +flags.DEFINE_string('xenapi_sr_base_path', '/var/run/sr-mount', + 'Base path to the storage repository') flags.DEFINE_string('target_host', None, 'iSCSI Target Host') -- cgit From b4b1a7fbd55784157b3084016d4dfe2bd0120e51 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 18:36:17 +0000 Subject: Adding safe_find_sr --- nova/virt/xenapi/vm_utils.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index e73bc7f2b..37ce86885 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -316,9 +316,7 @@ class VMHelper(HelperBase): LOG.debug(_("Asking xapi to fetch vhd image %(image)s") % locals()) - sr_ref = find_sr(session) - if sr_ref is None: - raise exception.NotFound('Cannot find SR to write VDI to') + sr_ref = safe_find_sr(session) # NOTE(sirp): The Glance plugin runs under Python 2.4 which does not # have the `uuid` module. To work around this, we generate the uuids @@ -340,16 +338,14 @@ class VMHelper(HelperBase): @classmethod def _fetch_image_glance_disk(cls, session, instance_id, image, access, type): - sr = find_sr(session) - if sr is None: - raise exception.NotFound('Cannot find SR to write VDI to') + sr_ref = safe_find_sr(session) client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) - meta, image_file = client.get_image(image) virtual_size = int(meta['size']) vdi_size = virtual_size LOG.debug(_("Size for image %(image)s:%(virtual_size)d") % locals()) + if type == ImageType.DISK: # Make room for MBR. vdi_size += MBR_SIZE_BYTES @@ -672,7 +668,18 @@ def get_vdi_for_vm_safely(session, vm_ref): return vdi_ref, vdi_rec +def safe_find_sr(session): + """Same as find_sr except raises a NotFound exception if SR cannot be + determined + """ + sr_ref = find_sr(session) + if sr_ref is None: + raise exception.NotFound(_('Cannot find SR to read/write VDI')) + return sr_ref + + def find_sr(session): + """Return the storage repository to hold VM images""" host = session.get_xenapi_host() srs = session.get_xenapi().SR.get_all() for sr in srs: @@ -688,15 +695,12 @@ def find_sr(session): def get_sr_path(session): - """Return the path to our Storage Repository + """Return the path to our storage repository This is used when we're dealing with VHDs directly, either by taking snapshots or by restoring an image in the DISK_VHD format. """ - # TODO(sirp): add safe_find_sr - sr_ref = find_sr(session) - if sr_ref is None: - raise Exception('Cannot find SR to read VDI from') + sr_ref = safe_find_sr(session) sr_rec = session.get_xenapi().SR.get_record(sr_ref) sr_uuid = sr_rec["uuid"] return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) -- cgit From acf95a640cfeb0812a55577b6a08bff972ad523b Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 19:17:27 +0000 Subject: Regrouping methods so they make sense --- nova/virt/xenapi/vm_utils.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 37ce86885..ba3e5e423 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -289,7 +289,7 @@ class VMHelper(HelperBase): 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} - task = session.async_call_plugin('glance', 'put_vdis', kwargs) + task = session.async_call_plugin('glance', 'upload_image', kwargs) session.wait_for_task(instance_id, task) @classmethod @@ -310,7 +310,6 @@ class VMHelper(HelperBase): return cls._fetch_image_objectstore(session, instance_id, image, access, user.secret, type) - @classmethod def _fetch_image_glance_vhd(cls, session, instance_id, image, access, type): LOG.debug(_("Asking xapi to fetch vhd image %(image)s") @@ -330,10 +329,10 @@ class VMHelper(HelperBase): 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} - task = session.async_call_plugin('glance', 'get_vdi', kwargs) + task = session.async_call_plugin('glance', 'download_image', kwargs) vdi_uuid = session.wait_for_task(instance_id, task) scan_sr(session, instance_id, sr_ref) - LOG.debug(_("Xapi 'get_vdi' returned VDI UUID %(vdi_uuid)s") % locals()) + LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") % locals()) return vdi_uuid @classmethod -- cgit From 1d72b9d3ddc835d788ba1fec1a937c2788e94b38 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 21:36:13 +0000 Subject: Using Nova style nokernel --- nova/api/openstack/servers.py | 8 +------- nova/compute/api.py | 2 ++ 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 1c9dcd9a6..cd0cea235 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -138,13 +138,7 @@ class Controller(wsgi.Controller): _("%(param)s property not found for image %(_image_id)s") % locals()) - image_id = str(image_id) - image = self._image_service.show(req.environ['nova.context'], image_id) - disk_format = image['properties'].get('disk_format', None) - if disk_format == "vhd": - return None, None - else: - return lookup('kernel_id'), lookup('ramdisk_id') + return lookup('kernel_id'), lookup('ramdisk_id') def create(self, req): """ Creates a new server for a given user """ diff --git a/nova/compute/api.py b/nova/compute/api.py index ac02dbcfa..6d28e376c 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -106,6 +106,8 @@ class API(base.Base): kernel_id = image.get('kernelId', None) if ramdisk_id is None: ramdisk_id = image.get('ramdiskId', None) + + # FIXME(sirp): is there a way we can remove null_kernel? # No kernel and ramdisk for raw images if kernel_id == str(FLAGS.null_kernel): kernel_id = None -- cgit From c33378fbbe0fd76e807530522715ba4175af18d8 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 21:54:42 +0000 Subject: Fixing typo --- nova/api/openstack/servers.py | 11 ++++++++--- nova/virt/xenapi/vm_utils.py | 14 +++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index cd0cea235..c15e499a0 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -129,8 +129,12 @@ class Controller(wsgi.Controller): Machine images are associated with Kernels and Ramdisk images via metadata stored in Glance as 'image_properties' """ - def lookup(param): - _image_id = image_id + # FIXME(sirp): Currently Nova requires us to specify the `null_kernel` + # identifier ('nokernel') to indicate a RAW (or VHD) image. It would + # be better if we could omit the kernel_id and ramdisk_id properties + # on the image + def lookup(image, param): + _image_id = image.id try: return image['properties'][param] except KeyError: @@ -138,7 +142,8 @@ class Controller(wsgi.Controller): _("%(param)s property not found for image %(_image_id)s") % locals()) - return lookup('kernel_id'), lookup('ramdisk_id') + image = self._image_service.show(req.environ['nova.context'], image_id) + return lookup(image, 'kernel_id'), lookup(image, 'ramdisk_id') def create(self, req): """ Creates a new server for a given user """ diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index ba3e5e423..6fa03ee68 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -331,12 +331,24 @@ class VMHelper(HelperBase): kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'download_image', kwargs) vdi_uuid = session.wait_for_task(instance_id, task) + # TODO(sirp): set name-label on VDI scan_sr(session, instance_id, sr_ref) LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") % locals()) return vdi_uuid @classmethod def _fetch_image_glance_disk(cls, session, instance_id, image, access, type): + """Fetch the image from Glance + + NOTE: + Unlike _fetch_image_glance_vhd, this method does not use the Glance + plugin; instead, it streams the disks through domU to the VDI + directly. + + """ + # FIXME(sirp): Since the Glance plugin seems to be required for the VHD disk, + # it may be worth using the plugin for both VHD and RAW and DISK + # restores sr_ref = safe_find_sr(session) client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) @@ -349,7 +361,7 @@ class VMHelper(HelperBase): # Make room for MBR. vdi_size += MBR_SIZE_BYTES - vdi = cls.create_vdi(session, sr, _('Glance image %s') % image, + vdi = cls.create_vdi(session, sr_ref, _('Glance image %s') % image, vdi_size, False) with_vdi_attached_here(session, vdi, False, -- cgit From 24d988263324c9136a1cd9aa5a3a3c4fdf229651 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 22:19:58 +0000 Subject: Set name-label on VDI --- nova/virt/xenapi/vm_utils.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 6fa03ee68..738372da0 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -331,8 +331,14 @@ class VMHelper(HelperBase): kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'download_image', kwargs) vdi_uuid = session.wait_for_task(instance_id, task) - # TODO(sirp): set name-label on VDI + scan_sr(session, instance_id, sr_ref) + + # Set the name-label to ease debugging + vdi_ref = session.get_xenapi().VDI.get_by_uuid(vdi_uuid) + name_label = get_name_label_for_image(image) + session.get_xenapi().VDI.set_name_label(vdi_ref, name_label) + LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") % locals()) return vdi_uuid @@ -361,8 +367,8 @@ class VMHelper(HelperBase): # Make room for MBR. vdi_size += MBR_SIZE_BYTES - vdi = cls.create_vdi(session, sr_ref, _('Glance image %s') % image, - vdi_size, False) + name_label = get_name_label_for_image(image) + vdi = cls.create_vdi(session, sr_ref, name_label, vdi_size, False) with_vdi_attached_here(session, vdi, False, lambda dev: @@ -848,3 +854,8 @@ def _write_partition(virtual_size, dev): (dest, primary_first, primary_last)) LOG.debug(_('Writing partition table %s done.'), dest) + + +def get_name_label_for_image(image): + # TODO(sirp): This should eventually be the URI for the Glance image + return _('Glance image %s') % image -- cgit From 28ba475d9c32a384570ce6eb0e2f9cfc3dc79a08 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 22:24:48 +0000 Subject: Pep8 fixes --- nova/virt/xenapi/vm_utils.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 738372da0..93009b408 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -311,7 +311,8 @@ class VMHelper(HelperBase): access, user.secret, type) @classmethod - def _fetch_image_glance_vhd(cls, session, instance_id, image, access, type): + def _fetch_image_glance_vhd(cls, session, instance_id, image, access, + type): LOG.debug(_("Asking xapi to fetch vhd image %(image)s") % locals()) @@ -339,11 +340,13 @@ class VMHelper(HelperBase): name_label = get_name_label_for_image(image) session.get_xenapi().VDI.set_name_label(vdi_ref, name_label) - LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") % locals()) + LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") + % locals()) return vdi_uuid @classmethod - def _fetch_image_glance_disk(cls, session, instance_id, image, access, type): + def _fetch_image_glance_disk(cls, session, instance_id, image, access, + type): """Fetch the image from Glance NOTE: @@ -352,9 +355,9 @@ class VMHelper(HelperBase): directly. """ - # FIXME(sirp): Since the Glance plugin seems to be required for the VHD disk, - # it may be worth using the plugin for both VHD and RAW and DISK - # restores + # FIXME(sirp): Since the Glance plugin seems to be required for the + # VHD disk, it may be worth using the plugin for both VHD and RAW and + # DISK restores sr_ref = safe_find_sr(session) client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) @@ -429,12 +432,13 @@ class VMHelper(HelperBase): properties = meta['properties'] disk_format = properties.get('disk_format', None) # TODO(sirp): When Glance treats disk_format as a first class - # attribute, we should start using that rather than an image-property + # attribute, we should start using that rather than an + # image-property if disk_format == 'vhd': # 2a. DISK_VHD log_disk_format('disk_vhd') return ImageType.DISK_VHD - + # 2b. DISK_RAW log_disk_format('disk_raw') return ImageType.DISK_RAW -- cgit From 21a3d77fee681d05c465c74e40177ae022bc24af Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 22:41:27 +0000 Subject: Fixing test by adding stub for get_image_meta --- nova/tests/glance/stubs.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index f182b857a..4cd5c357f 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -29,9 +29,10 @@ class FakeGlance(object): def __init__(self, host, port=None, use_ssl=False): pass - def get_image(self, image): - meta = { - 'size': 0, - } + def get_image_meta(self, image_id): + return {'size': 0, 'properties': {}} + + def get_image(self, image_id): + meta = self.get_image_meta(image_id) image_file = StringIO.StringIO('') return meta, image_file -- cgit From 585ba4d6cf25eabf83b1b33a6de794ce671c0c98 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 16 Feb 2011 18:43:55 +0000 Subject: Putting glance plugin under pep8 control --- nova/compute/api.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 8a16baf45..ea81c7ff0 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -104,11 +104,8 @@ class API(base.Base): image = self.image_service.show(context, image_id) if kernel_id is None: kernel_id = image.get('kernel_id', None) - # FIXME(sirp): which one to use? - #kernel_id = image.get('kernelId', None) if ramdisk_id is None: ramdisk_id = image.get('ramdisk_id', None) - #ramdisk_id = image.get('ramdiskId', None) # FIXME(sirp): is there a way we can remove null_kernel? # No kernel and ramdisk for raw images -- cgit From a5ec2be709d28267075ddc9616c5c29b62622af5 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 16 Feb 2011 23:07:43 +0000 Subject: Adding basic test --- nova/tests/glance/stubs.py | 22 ++++++++++++++++++---- nova/tests/test_xenapi.py | 4 ++++ nova/tests/xenapi/stubs.py | 6 ++++++ 3 files changed, 28 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index 4cd5c357f..fc120e523 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -26,13 +26,27 @@ def stubout_glance_client(stubs, cls): class FakeGlance(object): + IMAGE_FIXTURES = { + 1: {'image_meta': {'name': 'fakemachine', 'size': 0, + 'properties': {}}, + 'image_data': StringIO.StringIO('') }, + 2: {'image_meta': {'name': 'fakekernel', 'size': 0, + 'properties': {}}, + 'image_data': StringIO.StringIO('') }, + 3: {'image_meta': {'name': 'fakekernel', 'size': 0, + 'properties': {}}, + 'image_data': StringIO.StringIO('') }, + 4: {'image_meta': {'name': 'fakekernel', 'size': 0, + 'properties': {'disk_format': 'vhd'}}, + 'image_data': StringIO.StringIO('') }, + } + def __init__(self, host, port=None, use_ssl=False): pass def get_image_meta(self, image_id): - return {'size': 0, 'properties': {}} + return self.IMAGE_FIXTURES[image_id]['image_meta'] def get_image(self, image_id): - meta = self.get_image_meta(image_id) - image_file = StringIO.StringIO('') - return meta, image_file + image = self.IMAGE_FIXTURES[image_id] + return image['image_meta'], image['image_data'] diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index d5660c5d1..75387e7f5 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -279,6 +279,10 @@ class XenAPIVMTestCase(test.TestCase): FLAGS.xenapi_image_service = 'glance' self._test_spawn(1, None, None) + def test_spawn_vhd_glance(self): + FLAGS.xenapi_image_service = 'glance' + self._test_spawn(4, None, None) + def test_spawn_glance(self): FLAGS.xenapi_image_service = 'glance' self._test_spawn(1, 2, 3) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 624995ada..2e3b62a77 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -171,6 +171,12 @@ class FakeSessionForVMTests(fake.SessionBase): def VM_destroy(self, session_ref, vm_ref): fake.destroy_vm(vm_ref) + def SR_scan(self, session_ref, sr_ref): + pass + + def VDI_set_name_label(self, session_ref, vdi_ref, name_label): + pass + class FakeSessionForVolumeTests(fake.SessionBase): """ Stubs out a XenAPISession for Volume tests """ -- cgit From c56b1814cfae7a9c814b2d37388aff5e772771b6 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 16 Feb 2011 23:39:12 +0000 Subject: Pep8 fixes --- nova/tests/glance/stubs.py | 8 ++++---- nova/virt/xenapi/vm_utils.py | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index fc120e523..c58357962 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -29,16 +29,16 @@ class FakeGlance(object): IMAGE_FIXTURES = { 1: {'image_meta': {'name': 'fakemachine', 'size': 0, 'properties': {}}, - 'image_data': StringIO.StringIO('') }, + 'image_data': StringIO.StringIO('')}, 2: {'image_meta': {'name': 'fakekernel', 'size': 0, 'properties': {}}, - 'image_data': StringIO.StringIO('') }, + 'image_data': StringIO.StringIO('')}, 3: {'image_meta': {'name': 'fakekernel', 'size': 0, 'properties': {}}, - 'image_data': StringIO.StringIO('') }, + 'image_data': StringIO.StringIO('')}, 4: {'image_meta': {'name': 'fakekernel', 'size': 0, 'properties': {'disk_format': 'vhd'}}, - 'image_data': StringIO.StringIO('') }, + 'image_data': StringIO.StringIO('')}, } def __init__(self, host, port=None, use_ssl=False): diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d77db2ddb..33945aca3 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -289,6 +289,8 @@ class VMHelper(HelperBase): """ Requests that the Glance plugin bundle the specified VDIs and push them into Glance using the specified human-friendly name. """ + # NOTE(sirp): Currently we only support uploading images as VHD, there + # is no RAW equivalent (yet) logging.debug(_("Asking xapi to upload %(vdi_uuids)s as" " ID %(image_id)s") % locals()) @@ -299,7 +301,7 @@ class VMHelper(HelperBase): 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} - task = session.async_call_plugin('glance', 'upload_image', kwargs) + task = session.async_call_plugin('glance', 'upload_vhd', kwargs) session.wait_for_task(instance_id, task) @classmethod @@ -340,7 +342,7 @@ class VMHelper(HelperBase): 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} - task = session.async_call_plugin('glance', 'download_image', kwargs) + task = session.async_call_plugin('glance', 'download_vhd', kwargs) vdi_uuid = session.wait_for_task(instance_id, task) scan_sr(session, instance_id, sr_ref) @@ -350,7 +352,7 @@ class VMHelper(HelperBase): name_label = get_name_label_for_image(image) session.get_xenapi().VDI.set_name_label(vdi_ref, name_label) - LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") + LOG.debug(_("xapi 'download_vhd' returned VDI UUID %(vdi_uuid)s") % locals()) return vdi_uuid -- cgit From 04e29f6dc4b13b6fd0cbe5013cf241a727eb56ac Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 17 Feb 2011 01:24:31 +0000 Subject: Use glance image type to determine disk type --- nova/tests/glance/stubs.py | 12 ++++---- nova/virt/xenapi/vm_utils.py | 68 +++++++++++++++++++++++--------------------- nova/virt/xenapi/vmops.py | 9 ++++-- 3 files changed, 48 insertions(+), 41 deletions(-) (limited to 'nova') diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index c58357962..1a5fb7ffb 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -28,16 +28,16 @@ def stubout_glance_client(stubs, cls): class FakeGlance(object): IMAGE_FIXTURES = { 1: {'image_meta': {'name': 'fakemachine', 'size': 0, - 'properties': {}}, + 'type': 'machine'}, 'image_data': StringIO.StringIO('')}, 2: {'image_meta': {'name': 'fakekernel', 'size': 0, - 'properties': {}}, + 'type': 'kernel'}, 'image_data': StringIO.StringIO('')}, - 3: {'image_meta': {'name': 'fakekernel', 'size': 0, - 'properties': {}}, + 3: {'image_meta': {'name': 'fakeramdisk', 'size': 0, + 'type': 'ramdisk'}, 'image_data': StringIO.StringIO('')}, - 4: {'image_meta': {'name': 'fakekernel', 'size': 0, - 'properties': {'disk_format': 'vhd'}}, + 4: {'image_meta': {'name': 'fakevhd', 'size': 0, + 'type': 'vhd'}, 'image_data': StringIO.StringIO('')}, } diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 33945aca3..278e52211 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -413,47 +413,51 @@ class VMHelper(HelperBase): within an image. To figure out which type we're dealing with, we use the following rules: - 1. If the instance is specifying a kernel explicitly, we must be using - a 'disk' image (kernel outside of the image) + 1. If we're using Glance, we can use the image_type field to + determine the image_type - 2. If the kernel isn't specified, then we have two different - scenarios: - - a) If the image is in Glance, then we can use the 'disk_format' - property to determine if the image is really a VHD-style image - or if it's a RAW image - - b) If the image is not in Glance, then it must be a RAW image - (since we don't have a way of identifying VHD images...yet) + 2. If we're not using Glance, then we need to deduce this based on + whether a kernel_id is specified. """ - def log_disk_format(disk_format): - disk_format = disk_format.upper() + def log_disk_format(image_type): + pretty_format = {ImageType.KERNEL_RAMDISK: 'KERNEL_RAMDISK', + ImageType.DISK: 'DISK', + ImageType.DISK_RAW: 'DISK_RAW', + ImageType.DISK_VHD: 'DISK_VHD'} + disk_format = pretty_format[image_type] image_id = instance.image_id instance_id = instance.id LOG.debug(_("Detected %(disk_format)s format for image " "%(image_id)s, instance %(instance_id)s") % locals()) - if instance.kernel_id: - # 1. DISK - log_disk_format('disk') - return ImageType.DISK - elif FLAGS.xenapi_image_service == 'glance': - # if using glance, then we could be VHD format + def determine_from_glance(): + glance_type2nova_type = {'machine': ImageType.DISK, + 'raw': ImageType.DISK_RAW, + 'vhd': ImageType.DISK_VHD, + 'kernel': ImageType.KERNEL_RAMDISK, + 'ramdisk': ImageType.KERNEL_RAMDISK} client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) meta = client.get_image_meta(instance.image_id) - properties = meta['properties'] - disk_format = properties.get('disk_format', None) - # TODO(sirp): When Glance treats disk_format as a first class - # attribute, we should start using that rather than an - # image-property - if disk_format == 'vhd': - # 2a. DISK_VHD - log_disk_format('disk_vhd') - return ImageType.DISK_VHD - - # 2b. DISK_RAW - log_disk_format('disk_raw') - return ImageType.DISK_RAW + type_ = meta['type'] + try: + return glance_type2nova_type[type_] + except KeyError: + raise exception.NotFound( + _("Unrecognized image type '%(type_)s'") % locals()) + + def determine_from_instance(): + if instance.kernel_id: + return ImageType.DISK + else: + return ImageType.DISK_RAW + + if FLAGS.xenapi_image_service == 'glance': + image_type = determine_from_glance() + else: + image_type = determine_from_instance() + + log_disk_format(image_type) + return image_type @classmethod def _fetch_image_glance(cls, session, instance_id, image, access, type): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 943d74da3..961d589d5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -224,7 +224,8 @@ class VMOps(object): VMHelper.upload_image( self._session, instance.id, template_vdi_uuids, image_id) finally: - self._destroy(instance, template_vm_ref, shutdown=False) + self._destroy(instance, template_vm_ref, shutdown=False, + destroy_kernel_ramdisk=False) logging.debug(_("Finished snapshot and upload for VM %s"), instance) @@ -368,7 +369,8 @@ class VMOps(object): vm = VMHelper.lookup(self._session, instance.name) return self._destroy(instance, vm, shutdown=True) - def _destroy(self, instance, vm, shutdown=True): + def _destroy(self, instance, vm, shutdown=True, + destroy_kernel_ramdisk=True): """ Destroys VM instance by performing: @@ -385,7 +387,8 @@ class VMOps(object): self._shutdown(instance, vm) self._destroy_vdis(instance, vm) - self._destroy_kernel_ramdisk(instance, vm) + if destroy_kernel_ramdisk: + self._destroy_kernel_ramdisk(instance, vm) self._destroy_vm(instance, vm) def _wait_with_callback(self, instance_id, task, callback): -- cgit From 923a4938b73b84aa8a31f08a7c7b983cc82959fe Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 17 Feb 2011 07:29:50 +0000 Subject: Adding tests --- nova/tests/glance/stubs.py | 25 ++++++++++++---- nova/tests/test_xenapi.py | 70 ++++++++++++++++++++++++++++++++++++++++++-- nova/tests/xenapi/stubs.py | 10 +++++++ nova/virt/xenapi/vm_utils.py | 4 +++ 4 files changed, 100 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index 1a5fb7ffb..3ff8d7ce5 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -26,20 +26,33 @@ def stubout_glance_client(stubs, cls): class FakeGlance(object): + IMAGE_MACHINE = 1 + IMAGE_KERNEL = 2 + IMAGE_RAMDISK = 3 + IMAGE_RAW = 4 + IMAGE_VHD = 5 + IMAGE_FIXTURES = { - 1: {'image_meta': {'name': 'fakemachine', 'size': 0, + IMAGE_MACHINE: { + 'image_meta': {'name': 'fakemachine', 'size': 0, 'type': 'machine'}, 'image_data': StringIO.StringIO('')}, - 2: {'image_meta': {'name': 'fakekernel', 'size': 0, + IMAGE_KERNEL: { + 'image_meta': {'name': 'fakekernel', 'size': 0, 'type': 'kernel'}, 'image_data': StringIO.StringIO('')}, - 3: {'image_meta': {'name': 'fakeramdisk', 'size': 0, + IMAGE_RAMDISK: { + 'image_meta': {'name': 'fakeramdisk', 'size': 0, 'type': 'ramdisk'}, 'image_data': StringIO.StringIO('')}, - 4: {'image_meta': {'name': 'fakevhd', 'size': 0, - 'type': 'vhd'}, + IMAGE_RAW: { + 'image_meta': {'name': 'fakeraw', 'size': 0, + 'type': 'raw'}, 'image_data': StringIO.StringIO('')}, - } + IMAGE_VHD: { + 'image_meta': {'name': 'fakevhd', 'size': 0, + 'type': 'vhd'}, + 'image_data': StringIO.StringIO('')}} def __init__(self, host, port=None, use_ssl=False): pass diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 75387e7f5..f8a3d72c4 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -31,6 +31,7 @@ from nova.compute import power_state from nova.virt import xenapi_conn from nova.virt.xenapi import fake as xenapi_fake from nova.virt.xenapi import volume_utils +from nova.virt.xenapi import vm_utils from nova.virt.xenapi.vmops import SimpleDH from nova.tests.db import fakes as db_fakes from nova.tests.xenapi import stubs @@ -162,6 +163,7 @@ class XenAPIVMTestCase(test.TestCase): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) stubs.stubout_get_this_vm_uuid(self.stubs) stubs.stubout_stream_disk(self.stubs) + stubs.stubout_lookup_image(self.stubs) glance_stubs.stubout_glance_client(self.stubs, glance_stubs.FakeGlance) self.conn = xenapi_conn.get_connection(False) @@ -277,15 +279,17 @@ class XenAPIVMTestCase(test.TestCase): def test_spawn_raw_glance(self): FLAGS.xenapi_image_service = 'glance' - self._test_spawn(1, None, None) + self._test_spawn(glance_stubs.FakeGlance.IMAGE_RAW, None, None) def test_spawn_vhd_glance(self): FLAGS.xenapi_image_service = 'glance' - self._test_spawn(4, None, None) + self._test_spawn(glance_stubs.FakeGlance.IMAGE_VHD, None, None) def test_spawn_glance(self): FLAGS.xenapi_image_service = 'glance' - self._test_spawn(1, 2, 3) + self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE, + glance_stubs.FakeGlance.IMAGE_KERNEL, + glance_stubs.FakeGlance.IMAGE_RAMDISK) def tearDown(self): super(XenAPIVMTestCase, self).tearDown() @@ -334,3 +338,63 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): def tearDown(self): super(XenAPIDiffieHellmanTestCase, self).tearDown() + + +class XenAPIDetermineDiskImageTestCase(test.TestCase): + """ + Unit tests for code that detects the ImageType + """ + def setUp(self): + super(XenAPIDetermineDiskImageTestCase, self).setUp() + glance_stubs.stubout_glance_client(self.stubs, + glance_stubs.FakeGlance) + + class FakeInstance(object): + pass + + self.fake_instance = FakeInstance() + self.fake_instance.id = 42 + + def assert_disk_type(self, disk_type): + dt = vm_utils.VMHelper.determine_disk_image_type( + self.fake_instance) + self.assertEqual(disk_type, dt) + + def test_instance_disk(self): + """ + If a kernel is specified then the image type is DISK (aka machine) + """ + FLAGS.xenapi_image_service = 'objectstore' + self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_MACHINE + self.fake_instance.kernel_id = glance_stubs.FakeGlance.IMAGE_KERNEL + self.assert_disk_type(vm_utils.ImageType.DISK) + + def test_instance_disk_raw(self): + """ + If the kernel isn't specified, and we're not using Glance, then + DISK_RAW is assumed. + """ + FLAGS.xenapi_image_service = 'objectstore' + self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_RAW + self.fake_instance.kernel_id = None + self.assert_disk_type(vm_utils.ImageType.DISK_RAW) + + def test_glance_disk_raw(self): + """ + If we're using Glance, then defer to the image_type field, which in + this case will be 'raw'. + """ + FLAGS.xenapi_image_service = 'glance' + self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_RAW + self.fake_instance.kernel_id = None + self.assert_disk_type(vm_utils.ImageType.DISK_RAW) + + def test_glance_disk_vhd(self): + """ + If we're using Glance, then defer to the image_type field, which in + this case will be 'vhd'. + """ + FLAGS.xenapi_image_service = 'glance' + self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_VHD + self.fake_instance.kernel_id = None + self.assert_disk_type(vm_utils.ImageType.DISK_VHD) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 2e3b62a77..1e6758a33 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -130,6 +130,16 @@ def stubout_stream_disk(stubs): stubs.Set(vm_utils, '_stream_disk', f) +def stubout_lookup_image(stubs): + @classmethod + def fake_lookup_image(cls, session, instance_id, vdi_ref): + # NOTE(sirp): pretending each image is paravirtualized for now + is_pv = True + return is_pv + + stubs.Set(vm_utils.VMHelper, 'lookup_image', fake_lookup_image) + + class FakeSessionForVMTests(fake.SessionBase): """ Stubs out a XenAPISession for VM tests """ def __init__(self, uri): diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 278e52211..9027d58c4 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -490,6 +490,9 @@ class VMHelper(HelperBase): @classmethod def lookup_image(cls, session, instance_id, vdi_ref): + """ + Determine if VDI is using a PV kernel + """ if FLAGS.xenapi_image_service == 'glance': return cls._lookup_image_glance(session, vdi_ref) else: @@ -517,6 +520,7 @@ class VMHelper(HelperBase): def is_vdi_pv(dev): LOG.debug(_("Running pygrub against %s"), dev) + # TODO(sirp): use subprocess here output = os.popen('pygrub -qn /dev/%s' % dev) for line in output.readlines(): #try to find kernel string -- cgit From 94f5a5748158e61bde43327970dd8e513ca36575 Mon Sep 17 00:00:00 2001 From: Nirmal Ranganathan Date: Thu, 17 Feb 2011 17:13:59 -0600 Subject: Always compare incoming flavor_id as an int --- nova/compute/instance_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 309313fd0..7a2a5baa3 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -45,6 +45,6 @@ def get_by_type(instance_type): def get_by_flavor_id(flavor_id): for instance_type, details in INSTANCE_TYPES.iteritems(): - if details['flavorid'] == flavor_id: + if details['flavorid'] == int(flavor_id): return instance_type return FLAGS.default_instance_type -- cgit From cd533e160e9c98a0c14b4e0bc32a6e94c7ab8657 Mon Sep 17 00:00:00 2001 From: Nirmal Ranganathan Date: Fri, 18 Feb 2011 11:44:38 -0600 Subject: Added Author and tests --- nova/tests/test_compute.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'nova') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index b049ac943..e338a7cfa 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -30,6 +30,7 @@ from nova import log as logging from nova import test from nova import utils from nova.auth import manager +from nova.compute import instance_types LOG = logging.getLogger('nova.tests.compute') @@ -266,3 +267,11 @@ class ComputeTestCase(test.TestCase): self.assertEqual(ret_val, None) self.compute.terminate_instance(self.context, instance_id) + + def test_get_by_flavor_id(self): + type = instance_types.get_by_flavor_id(1) + self.assertEqual(type, 'm1.tiny') + + type = instance_types.get_by_flavor_id("1") + self.assertEqual(type, 'm1.tiny') + -- cgit From 8916442e7f2920938a317777de71f75faf463005 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 18 Feb 2011 21:42:04 +0000 Subject: Typo fix --- nova/virt/xenapi/vm_utils.py | 2 ++ nova/virt/xenapi/vmops.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9027d58c4..88a205d2f 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -451,6 +451,8 @@ class VMHelper(HelperBase): else: return ImageType.DISK_RAW + # FIXME(sirp): can we unify the ImageService and xenapi_image_service + # abstractions? if FLAGS.xenapi_image_service == 'glance': image_type = determine_from_glance() else: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 8d1c79c0b..09abd5861 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -394,7 +394,7 @@ class VMOps(object): """ Three situations can occur: - 1. We have netiher a ramdisk or a kernel, in which case we are a + 1. We have neither a ramdisk nor a kernel, in which case we are a RAW image and can omit this step 2. We have one or the other, in which case, we should flag as an -- cgit From eefc8938d8a8010052affab9a5c0d010778d9780 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 18 Feb 2011 22:25:19 +0000 Subject: Changing type -> image_type --- nova/virt/xenapi/vm_utils.py | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 88a205d2f..2114bfa42 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -305,9 +305,10 @@ class VMHelper(HelperBase): session.wait_for_task(instance_id, task) @classmethod - def fetch_image(cls, session, instance_id, image, user, project, type): + def fetch_image(cls, session, instance_id, image, user, project, + image_type): """ - type is interpreted as an ImageType instance + image_type is interpreted as an ImageType instance Related flags: xenapi_image_service = ['glance', 'objectstore'] glance_address = 'address for glance services' @@ -317,14 +318,15 @@ class VMHelper(HelperBase): if FLAGS.xenapi_image_service == 'glance': return cls._fetch_image_glance(session, instance_id, image, - access, type) + access, image_type) else: return cls._fetch_image_objectstore(session, instance_id, image, - access, user.secret, type) + access, user.secret, + image_type) @classmethod def _fetch_image_glance_vhd(cls, session, instance_id, image, access, - type): + image_type): LOG.debug(_("Asking xapi to fetch vhd image %(image)s") % locals()) @@ -358,7 +360,7 @@ class VMHelper(HelperBase): @classmethod def _fetch_image_glance_disk(cls, session, instance_id, image, access, - type): + image_type): """Fetch the image from Glance NOTE: @@ -378,7 +380,7 @@ class VMHelper(HelperBase): vdi_size = virtual_size LOG.debug(_("Size for image %(image)s:%(virtual_size)d") % locals()) - if type == ImageType.DISK: + if image_type == ImageType.DISK: # Make room for MBR. vdi_size += MBR_SIZE_BYTES @@ -387,9 +389,9 @@ class VMHelper(HelperBase): with_vdi_attached_here(session, vdi, False, lambda dev: - _stream_disk(dev, type, + _stream_disk(dev, image_type, virtual_size, image_file)) - if (type == ImageType.KERNEL_RAMDISK): + if image_type == ImageType.KERNEL_RAMDISK: #we need to invoke a plugin for copying VDI's #content into proper path LOG.debug(_("Copying VDI %s to /boot/guest on dom0"), vdi) @@ -462,29 +464,33 @@ class VMHelper(HelperBase): return image_type @classmethod - def _fetch_image_glance(cls, session, instance_id, image, access, type): - if type == ImageType.DISK_VHD: + def _fetch_image_glance(cls, session, instance_id, image, access, + image_type): + if image_type == ImageType.DISK_VHD: return cls._fetch_image_glance_vhd( - session, instance_id, image, access, type) + session, instance_id, image, access, image_type) else: return cls._fetch_image_glance_disk( - session, instance_id, image, access, type) + session, instance_id, image, access, image_type) @classmethod def _fetch_image_objectstore(cls, session, instance_id, image, access, - secret, type): + secret, image_type): url = images.image_url(image) LOG.debug(_("Asking xapi to fetch %(url)s as %(access)s") % locals()) - fn = (type != ImageType.KERNEL_RAMDISK) and 'get_vdi' or 'get_kernel' + if image_type == ImageType.KERNEL_RAMDISK: + fn = 'get_kernel' + else: + fn = 'get_vdi' args = {} args['src_url'] = url args['username'] = access args['password'] = secret args['add_partition'] = 'false' args['raw'] = 'false' - if type != ImageType.KERNEL_RAMDISK: + if image_type != ImageType.KERNEL_RAMDISK: args['add_partition'] = 'true' - if type == ImageType.DISK_RAW: + if image_type == ImageType.DISK_RAW: args['raw'] = 'true' task = session.async_call_plugin('objectstore', fn, args) uuid = session.wait_for_task(instance_id, task) @@ -857,9 +863,9 @@ def get_this_vm_ref(session): return session.get_xenapi().VM.get_by_uuid(get_this_vm_uuid()) -def _stream_disk(dev, type, virtual_size, image_file): +def _stream_disk(dev, image_type, virtual_size, image_file): offset = 0 - if type == ImageType.DISK: + if image_type == ImageType.DISK: offset = MBR_SIZE_BYTES _write_partition(virtual_size, dev) -- cgit From e3d6dc70a6b77d80afcf87473bc79549540ac4ce Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 25 Feb 2011 02:51:14 +0000 Subject: Removing unecessary nokernel stuff --- nova/api/openstack/servers.py | 51 ++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 22 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index c8c94f1fd..f51da0cdd 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -136,28 +136,6 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotFound()) return exc.HTTPAccepted() - def _get_kernel_ramdisk_from_image(self, req, image_id): - """ - Machine images are associated with Kernels and Ramdisk images via - metadata stored in Glance as 'image_properties' - """ - # FIXME(sirp): Currently Nova requires us to specify the `null_kernel` - # identifier ('nokernel') to indicate a RAW (or VHD) image. It would - # be better if we could omit the kernel_id and ramdisk_id properties - # on the image - def lookup(image, param): - _image_id = image['id'] - try: - return image['properties'][param] - except KeyError: - LOG.debug( - _("%(param)s property not found for image %(_image_id)s") % - locals()) - return None - - image = self._image_service.show(req.environ['nova.context'], image_id) - return lookup(image, 'kernel_id'), lookup(image, 'ramdisk_id') - def create(self, req): """ Creates a new server for a given user """ env = self._deserialize(req.body, req) @@ -367,3 +345,32 @@ class Controller(wsgi.Controller): action=item.action, error=item.error)) return dict(actions=actions) + + def _get_kernel_ramdisk_from_image(self, req, image_id): + """Retrevies kernel and ramdisk IDs from Glance + + Only 'machine' (ami) type use kernel and ramdisk outside of the + image. + """ + # FIXME(sirp): Since we're retrieving the kernel_id from an + # image_property, this means only Glance is supported. + # The BaseImageService needs to expose a consistent way of accessing + # kernel_id and ramdisk_id + image = self._image_service.show(req.environ['nova.context'], image_id) + + if image['type'] != 'machine': + return None, None + + try: + kernel_id = image['properties']['kernel_id'] + except KeyError: + raise exception.NotFound( + _("Kernel not found for image %(image_id)s") % locals()) + + try: + ramdisk_id = image['properties']['ramdisk_id'] + except KeyError: + raise exception.NotFound( + _("Ramdisk not found for image %(image_id)s") % locals()) + + return kernel_id, ramdisk_id -- cgit From 079b532a1080da9fe5d99e90fa9c60d16506de06 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 25 Feb 2011 16:24:51 +0000 Subject: Verify status of image is active --- nova/api/openstack/servers.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index f51da0cdd..bf5663f60 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -358,6 +358,11 @@ class Controller(wsgi.Controller): # kernel_id and ramdisk_id image = self._image_service.show(req.environ['nova.context'], image_id) + if image['status'] != 'active': + raise exception.Invalid( + _("Cannot build from image %(image_id)s, status not active") % + locals()) + if image['type'] != 'machine': return None, None -- cgit From a457595224a5ca5cdb0191ba2f6fa542d16e18f5 Mon Sep 17 00:00:00 2001 From: Nirmal Ranganathan Date: Mon, 28 Feb 2011 11:25:14 -0600 Subject: Removed extraneous newline --- nova/tests/test_compute.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index e338a7cfa..949b5e6eb 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -274,4 +274,3 @@ class ComputeTestCase(test.TestCase): type = instance_types.get_by_flavor_id("1") self.assertEqual(type, 'm1.tiny') - -- cgit