From 193ef47ae87afde18f780c5141a597480845de1e Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Fri, 7 Jan 2011 03:08:38 +0000 Subject: Fix Nova not to immediately blow up when talking to Glance: we were using the wrong URL to get the image metadata, and ended up getting the whole image instead (and trying to parse it as json). Also, fix some URLs, all of which were missing the leading slash. --- nova/image/glance.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/image/glance.py b/nova/image/glance.py index cb3936df1..e40468364 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -89,7 +89,7 @@ class ParallaxClient(object): """ try: c = self.connection_type(self.netloc, self.port) - c.request("GET", "images/detail") + c.request("GET", "/images/detail") res = c.getresponse() if res.status == 200: # Parallax returns a JSONified dict(images=image_list) @@ -108,12 +108,12 @@ class ParallaxClient(object): """ try: c = self.connection_type(self.netloc, self.port) - c.request("GET", "images/%s" % image_id) + c.request("HEAD", "/images/%s" % image_id) res = c.getresponse() if res.status == 200: - # Parallax returns a JSONified dict(image=image_info) - data = json.loads(res.read())['image'] - return data + # TODO(ewanmellor): Temporary hack! We should be parsing + # the response from Glance properly. + return { 'url': '/images/%s' % image_id } else: # TODO(jaypipes): log the error? return None @@ -146,7 +146,7 @@ class ParallaxClient(object): try: c = self.connection_type(self.netloc, self.port) body = json.dumps(image_metadata) - c.request("PUT", "images/%s" % image_id, body) + c.request("PUT", "/images/%s" % image_id, body) res = c.getresponse() return res.status == 200 finally: @@ -158,7 +158,7 @@ class ParallaxClient(object): """ try: c = self.connection_type(self.netloc, self.port) - c.request("DELETE", "images/%s" % image_id) + c.request("DELETE", "/images/%s" % image_id) res = c.getresponse() return res.status == 200 finally: -- cgit From df2785fb12d38cf0842921d380de2ed2d1c6bf5b Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Fri, 7 Jan 2011 03:10:28 +0000 Subject: Half-finished implementation of the streaming from Glance to a VDI through nova-compute. --- nova/virt/xenapi/vm_utils.py | 158 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9d1b51848..074ca9f87 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -19,6 +19,7 @@ Helper methods for operations related to the management of VM records and their attributes like VDIs, VIFs, as well as their lookup functions. """ +import glance import logging import pickle import urllib @@ -45,6 +46,8 @@ XENAPI_POWER_STATE = { 'Suspended': power_state.SUSPENDED, 'Crashed': power_state.CRASHED} +BUFSIZE = 65536 + class ImageType: """ @@ -206,6 +209,25 @@ class VMHelper(HelperBase): vm_ref, network_ref) return vif_ref + @classmethod + def create_vdi(cls, session, sr_ref, name_label, virtual_size, read_only): + """Create a VDI record and returns its reference.""" + vdi_ref = session.xenapi.VDI.create( + {'name_label': name_label, + 'name_description': '', + 'SR': sr_ref, + 'virtual_size': str(virtual_size), + 'type': 'User', + 'sharable': False, + 'read_only': read_only, + 'xenstore_data': {}, + 'other_config': {}, + 'sm_config': {}, + 'tags': []}) + logging.debug(_('Created VDI %s (%s, %s, %s) on %s.'), vdi_ref, + name_label, virtual_size, read_only, sr_ref) + return vdi_ref + @classmethod def create_snapshot(cls, session, instance_id, vm_ref, label): """ Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, @@ -257,9 +279,52 @@ class VMHelper(HelperBase): def fetch_image(cls, session, instance_id, image, user, project, type): """ type is interpreted as an ImageType instance + Related flags: + xenapi_image_service = ['glance', 'objectstore'] + glance_address = 'address for glance services' + glance_port = 'port for glance services' """ - url = images.image_url(image) access = AuthManager().get_access_key(user, project) + + if FLAGS.xenapi_image_service == 'glance': + cls._fetch_image_glance(session, instance_id, image, access, type) + else: + cls._fetch_image_objectstore(session, instance_id, image, access, + type) + + #### raw_image=validate_bool(args, 'raw', 'false') + #### add_partition = validate_bool(args, 'add_partition', 'false') + + @classmethod + def _fetch_image_glance(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) + + raise exception.NotFound("DAM") + + meta, image_file = c.get_image(image) + vdi_size = meta['size'] + + vdi = create_vdi(session, sr, _('Glance image %s') % image, vdi_size, + False) + + def stream(dev): + with open('/dev/%s' % dev, 'wb') as f: + while True: + buf = image_file.read(BUFSIZE) + if not buf: + break + f.write(buf) + with_vdi_attached_here(session, vdi, False, stream) + return session.xenapi.VDI.get_uuid(vdi) + + @classmethod + def _fetch_image_objectstore(cls, session, instance_id, image, access, + type): + url = images.image_url(image) logging.debug("Asking xapi to fetch %s as %s", url, access) fn = (type != ImageType.KERNEL_RAMDISK) and 'get_vdi' or 'get_kernel' args = {} @@ -461,3 +526,94 @@ def get_vdi_for_vm_safely(session, vm_ref): vdi_ref = vdi_refs[0] vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) return vdi_ref, vdi_rec + + +def find_sr(session): + host = get_this_host(session) + srs = session.xenapi.SR.get_all() + for sr in srs: + sr_rec = session.xenapi.SR.get_record(sr) + if not ('i18n-key' in sr_rec['other_config'] and + sr_rec['other_config']['i18n-key'] == 'local-storage'): + continue + for pbd in sr_rec['PBDs']: + pbd_rec = session.xenapi.PBD.get_record(pbd) + if pbd_rec['host'] == host: + return sr + return None + + +def with_vdi_attached_here(session, vdi, read_only, f): + this_vm_ref = get_this_vm_ref(session) + vbd_rec = {} + vbd_rec['VM'] = this_vm_ref + vbd_rec['VDI'] = vdi + vbd_rec['userdevice'] = 'autodetect' + vbd_rec['bootable'] = False + vbd_rec['mode'] = read_only and 'RO' or 'RW' + vbd_rec['type'] = 'disk' + 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'] = [] + logging.debug(_('Creating VBD for VDI %s ... '), vdi) + vbd = session.xenapi.VBD.create(vbd_rec) + logging.debug(_('Creating VBD for VDI %s done.'), vdi) + try: + logging.debug(_('Plugging VBD %s ... '), vbd) + session.xenapi.VBD.plug(vbd) + logging.debug(_('Plugging VBD %s done.'), vbd) + return f(session.xenapi.VBD.get_device(vbd)) + finally: + logging.debug(_('Destroying VBD for VDI %s ... '), vdi) + vbd_unplug_with_retry(session, vbd) + ignore_failure(session.xenapi.VBD.destroy, vbd) + logging.debug(_('Destroying VBD for VDI %s done.'), vdi) + + +def vbd_unplug_with_retry(session, vbd): + """Call VBD.unplug on the given VBD, with a retry if we get + DEVICE_DETACH_REJECTED. For reasons which I don't understand, we're + seeing the device still in use, even when all processes using the device + should be dead.""" + while True: + try: + session.xenapi.VBD.unplug(vbd) + logging.debug(_('VBD.unplug successful first time.')) + return + except XenAPI.Failure, e: + if (len(e.details) > 0 and + e.details[0] == 'DEVICE_DETACH_REJECTED'): + logging.debug(_('VBD.unplug rejected: retrying...')) + time.sleep(1) + elif (len(e.details) > 0 and + e.details[0] == 'DEVICE_ALREADY_DETACHED'): + logging.debug(_('VBD.unplug successful eventually.')) + return + else: + logging.error(_('Ignoring XenAPI.Failure in VBD.unplug: %s'), + e) + return + + +def ignore_failure(func, *args, **kwargs): + try: + return func(*args, **kwargs) + except XenAPI.Failure, e: + logging.error(_('Ignoring XenAPI.Failure %s'), e) + return None + + +def get_this_host(session): + return session.xenapi.session.get_this_host(session.handle) + + +def get_this_vm_uuid(): + with file('/sys/hypervisor/uuid') as f: + return f.readline().strip() + + +def get_this_vm_ref(session): + return session.xenapi.VM.get_by_uuid(get_this_vm_uuid()) -- cgit From b23dece0d29d295f6ee140b96230ed27c7fd1268 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Fri, 7 Jan 2011 18:11:41 +0000 Subject: pv/hvm detection with pygrub updated for glance --- nova/virt/xenapi/vm_utils.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 074ca9f87..9d333bcea 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -343,6 +343,14 @@ class VMHelper(HelperBase): @classmethod def lookup_image(cls, session, vdi_ref): + if FLAGS.xenapi_image_service == 'glance': + cls.lookup_image_glance(session, vdi_ref) + else: + cls.lookup_image_objectstore(session, vdi_ref) + return + + @classmethod + def _lookup_image_objectstore(cls,session,vdi_ref): logging.debug("Looking up vdi %s for PV kernel", vdi_ref) fn = "is_vdi_pv" args = {} @@ -357,6 +365,25 @@ class VMHelper(HelperBase): logging.debug("PV Kernel in VDI:%d", pv) return pv + @classmethod + def _lookup_image_glance(cls,session,vdi_ref): + logging.debug("Looking up vdi %s for PV kernel", vdi_ref) + + def is_vdi_pv(dest): + logging.debug("Running pygrub against %s",dest) + output=os.popen('pygrub -qn %s' % dest) + pv=False + for line in output.readlines(): + #try to find kernel string + m=re.search('(?<=kernel:)/.*(?:>)',line) + if m: + if m.group(0).find('xen')!=-1: + pv=True + logging.debug("PV:%d",pv) + return pv + pv=with_vdi_attached_here(session, vdi_ref, False, is_vdi_pv) + return pv + @classmethod def lookup(cls, session, i): """Look the instance i up, and returns it if available""" -- cgit From e92f0a9352bf7de0f42951b5b6f1bb452a609bf6 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Fri, 7 Jan 2011 20:19:59 +0000 Subject: Many fixes to the Glance integration. --- nova/virt/xenapi/vm_utils.py | 75 +++++++++++++++++++------------------------- nova/virt/xenapi_conn.py | 3 ++ 2 files changed, 36 insertions(+), 42 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9d333bcea..98427acee 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -19,8 +19,9 @@ Helper methods for operations related to the management of VM records and their attributes like VDIs, VIFs, as well as their lookup functions. """ -import glance +import glance.client import logging +import os import pickle import urllib from xml.dom import minidom @@ -46,8 +47,6 @@ XENAPI_POWER_STATE = { 'Suspended': power_state.SUSPENDED, 'Crashed': power_state.CRASHED} -BUFSIZE = 65536 - class ImageType: """ @@ -212,7 +211,7 @@ class VMHelper(HelperBase): @classmethod def create_vdi(cls, session, sr_ref, name_label, virtual_size, read_only): """Create a VDI record and returns its reference.""" - vdi_ref = session.xenapi.VDI.create( + vdi_ref = session.get_xenapi().VDI.create( {'name_label': name_label, 'name_description': '', 'SR': sr_ref, @@ -287,39 +286,35 @@ class VMHelper(HelperBase): access = AuthManager().get_access_key(user, project) if FLAGS.xenapi_image_service == 'glance': - cls._fetch_image_glance(session, instance_id, image, access, type) + return cls._fetch_image_glance(session, instance_id, image, + access, type) else: - cls._fetch_image_objectstore(session, instance_id, image, access, - type) + return cls._fetch_image_objectstore(session, instance_id, image, + access, type) #### raw_image=validate_bool(args, 'raw', 'false') #### add_partition = validate_bool(args, 'add_partition', 'false') @classmethod - def _fetch_image_glance(cls, session, instance_id, image, access, type): + def _fetch_image_glance(cls, session, instance_id, image, access, typ): 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) - raise exception.NotFound("DAM") - meta, image_file = c.get_image(image) vdi_size = meta['size'] - vdi = create_vdi(session, sr, _('Glance image %s') % image, vdi_size, - False) + vdi = cls.create_vdi(session, sr, _('Glance image %s') % image, + vdi_size, False) def stream(dev): with open('/dev/%s' % dev, 'wb') as f: - while True: - buf = image_file.read(BUFSIZE) - if not buf: - break - f.write(buf) + for chunk in image_file: + f.write(chunk) with_vdi_attached_here(session, vdi, False, stream) - return session.xenapi.VDI.get_uuid(vdi) + return session.get_xenapi().VDI.get_uuid(vdi) @classmethod def _fetch_image_objectstore(cls, session, instance_id, image, access, @@ -344,9 +339,9 @@ class VMHelper(HelperBase): @classmethod def lookup_image(cls, session, vdi_ref): if FLAGS.xenapi_image_service == 'glance': - cls.lookup_image_glance(session, vdi_ref) + cls._lookup_image_glance(session, vdi_ref) else: - cls.lookup_image_objectstore(session, vdi_ref) + cls._lookup_image_objectstore(session, vdi_ref) return @classmethod @@ -369,19 +364,19 @@ class VMHelper(HelperBase): def _lookup_image_glance(cls,session,vdi_ref): logging.debug("Looking up vdi %s for PV kernel", vdi_ref) - def is_vdi_pv(dest): - logging.debug("Running pygrub against %s",dest) - output=os.popen('pygrub -qn %s' % dest) - pv=False + def is_vdi_pv(dev): + logging.debug("Running pygrub against %s", dev) + output = os.popen('pygrub -qn /dev/%s' % dev) + pv = False for line in output.readlines(): #try to find kernel string - m=re.search('(?<=kernel:)/.*(?:>)',line) + m = re.search('(?<=kernel:)/.*(?:>)',line) if m: if m.group(0).find('xen')!=-1: - pv=True + pv = True logging.debug("PV:%d",pv) - return pv - pv=with_vdi_attached_here(session, vdi_ref, False, is_vdi_pv) + return pv + pv = with_vdi_attached_here(session, vdi_ref, False, is_vdi_pv) return pv @classmethod @@ -556,15 +551,15 @@ def get_vdi_for_vm_safely(session, vm_ref): def find_sr(session): - host = get_this_host(session) - srs = session.xenapi.SR.get_all() + host = session.get_xenapi_host() + srs = session.get_xenapi().SR.get_all() for sr in srs: - sr_rec = session.xenapi.SR.get_record(sr) + sr_rec = session.get_xenapi().SR.get_record(sr) if not ('i18n-key' in sr_rec['other_config'] and sr_rec['other_config']['i18n-key'] == 'local-storage'): continue for pbd in sr_rec['PBDs']: - pbd_rec = session.xenapi.PBD.get_record(pbd) + pbd_rec = session.get_xenapi().PBD.get_record(pbd) if pbd_rec['host'] == host: return sr return None @@ -586,17 +581,17 @@ def with_vdi_attached_here(session, vdi, read_only, f): vbd_rec['qos_algorithm_params'] = {} vbd_rec['qos_supported_algorithms'] = [] logging.debug(_('Creating VBD for VDI %s ... '), vdi) - vbd = session.xenapi.VBD.create(vbd_rec) + vbd = session.get_xenapi().VBD.create(vbd_rec) logging.debug(_('Creating VBD for VDI %s done.'), vdi) try: logging.debug(_('Plugging VBD %s ... '), vbd) - session.xenapi.VBD.plug(vbd) + session.get_xenapi().VBD.plug(vbd) logging.debug(_('Plugging VBD %s done.'), vbd) - return f(session.xenapi.VBD.get_device(vbd)) + return f(session.get_xenapi().VBD.get_device(vbd)) finally: logging.debug(_('Destroying VBD for VDI %s ... '), vdi) vbd_unplug_with_retry(session, vbd) - ignore_failure(session.xenapi.VBD.destroy, vbd) + ignore_failure(session.get_xenapi().VBD.destroy, vbd) logging.debug(_('Destroying VBD for VDI %s done.'), vdi) @@ -607,7 +602,7 @@ def vbd_unplug_with_retry(session, vbd): should be dead.""" while True: try: - session.xenapi.VBD.unplug(vbd) + session.get_xenapi().VBD.unplug(vbd) logging.debug(_('VBD.unplug successful first time.')) return except XenAPI.Failure, e: @@ -633,14 +628,10 @@ def ignore_failure(func, *args, **kwargs): return None -def get_this_host(session): - return session.xenapi.session.get_this_host(session.handle) - - def get_this_vm_uuid(): with file('/sys/hypervisor/uuid') as f: return f.readline().strip() def get_this_vm_ref(session): - return session.xenapi.VM.get_by_uuid(get_this_vm_uuid()) + return session.get_xenapi().VM.get_by_uuid(get_this_vm_uuid()) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index c48f5b7cb..3820d3d30 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -84,6 +84,9 @@ flags.DEFINE_float('xenapi_task_poll_interval', 'The interval used for polling of remote tasks ' '(Async.VM.start, etc). Used only if ' 'connection_type=xenapi.') +flags.DEFINE_string('xenapi_image_service', + 'glance', + 'Where to get VM images: glance or objectstore.') flags.DEFINE_float('xenapi_vhd_coalesce_poll_interval', 5.0, 'The interval used for polling of coalescing vhds.' -- cgit From 5ca8ec42037ed4e2a1475bf29064f61068308687 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Fri, 7 Jan 2011 20:26:25 +0000 Subject: Fix pep8 errors. --- nova/image/glance.py | 2 +- nova/virt/xenapi/vm_utils.py | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'nova') diff --git a/nova/image/glance.py b/nova/image/glance.py index e40468364..3f8982c51 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -113,7 +113,7 @@ class ParallaxClient(object): if res.status == 200: # TODO(ewanmellor): Temporary hack! We should be parsing # the response from Glance properly. - return { 'url': '/images/%s' % image_id } + return {'url': '/images/%s' % image_id} else: # TODO(jaypipes): log the error? return None diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 98427acee..c5bd83b27 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -344,8 +344,8 @@ class VMHelper(HelperBase): cls._lookup_image_objectstore(session, vdi_ref) return - @classmethod - def _lookup_image_objectstore(cls,session,vdi_ref): + @classmethod + def _lookup_image_objectstore(cls, session, vdi_ref): logging.debug("Looking up vdi %s for PV kernel", vdi_ref) fn = "is_vdi_pv" args = {} @@ -360,25 +360,25 @@ class VMHelper(HelperBase): logging.debug("PV Kernel in VDI:%d", pv) return pv - @classmethod - def _lookup_image_glance(cls,session,vdi_ref): + @classmethod + def _lookup_image_glance(cls, session, vdi_ref): logging.debug("Looking up vdi %s for PV kernel", vdi_ref) - + def is_vdi_pv(dev): logging.debug("Running pygrub against %s", dev) output = os.popen('pygrub -qn /dev/%s' % dev) pv = False for line in output.readlines(): #try to find kernel string - m = re.search('(?<=kernel:)/.*(?:>)',line) + m = re.search('(?<=kernel:)/.*(?:>)', line) if m: - if m.group(0).find('xen')!=-1: + if m.group(0).find('xen') != -1: pv = True - logging.debug("PV:%d",pv) + logging.debug("PV:%d", pv) return pv pv = with_vdi_attached_here(session, vdi_ref, False, is_vdi_pv) return pv - + @classmethod def lookup(cls, session, i): """Look the instance i up, and returns it if available""" -- cgit From fedf946c7d04465fb958707e143d8de558ea4321 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Fri, 7 Jan 2011 22:28:59 +0000 Subject: Some fixes to _lookup_image_glance: fix the return value from lookup_image, attach the disk read-only before running pygrub, and add some debug logging. --- nova/virt/xenapi/vm_utils.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index c5bd83b27..76094f35c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -23,6 +23,7 @@ import glance.client import logging import os import pickle +import re import urllib from xml.dom import minidom @@ -339,10 +340,9 @@ class VMHelper(HelperBase): @classmethod def lookup_image(cls, session, vdi_ref): if FLAGS.xenapi_image_service == 'glance': - cls._lookup_image_glance(session, vdi_ref) + return cls._lookup_image_glance(session, vdi_ref) else: - cls._lookup_image_objectstore(session, vdi_ref) - return + return cls._lookup_image_objectstore(session, vdi_ref) @classmethod def _lookup_image_objectstore(cls, session, vdi_ref): @@ -367,17 +367,15 @@ class VMHelper(HelperBase): def is_vdi_pv(dev): logging.debug("Running pygrub against %s", dev) output = os.popen('pygrub -qn /dev/%s' % dev) - pv = False for line in output.readlines(): #try to find kernel string m = re.search('(?<=kernel:)/.*(?:>)', line) - if m: - if m.group(0).find('xen') != -1: - pv = True - logging.debug("PV:%d", pv) - return pv - pv = with_vdi_attached_here(session, vdi_ref, False, is_vdi_pv) - return pv + if m and m.group(0).find('xen') != -1: + logging.debug("Found Xen kernel %s" % m.group(0)) + return True + logging.debug("No Xen kernel found. Booting HVM.") + return False + return with_vdi_attached_here(session, vdi_ref, True, is_vdi_pv) @classmethod def lookup(cls, session, i): -- cgit From 6e0665415a65bc800b4c6f2778d66e944cbbe81e Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Fri, 7 Jan 2011 22:56:11 +0000 Subject: Fix indentation. --- nova/virt/xenapi/vm_utils.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 76094f35c..c9c22f7b2 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -50,16 +50,16 @@ XENAPI_POWER_STATE = { class ImageType: - """ - Enumeration class for distinguishing different image types - 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) - """ + """ + Enumeration class for distinguishing different image types + 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) + """ - KERNEL_RAMDISK = 0 - DISK = 1 - DISK_RAW = 2 + KERNEL_RAMDISK = 0 + DISK = 1 + DISK_RAW = 2 class VMHelper(HelperBase): -- cgit From 9f8719b334df28521154be8587bd7d30c431a993 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Sat, 8 Jan 2011 00:25:54 +0000 Subject: First cut at implementing partition-adding in combination with the Glance streaming. Untested. --- nova/virt/xenapi/vm_utils.py | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index c9c22f7b2..e601ccff9 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -49,6 +49,11 @@ XENAPI_POWER_STATE = { 'Crashed': power_state.CRASHED} +SECTOR_SIZE = 512 +MBR_SIZE_SECTORS = 63 +MBR_SIZE_BYTES = MBR_SIZE_SECTORS * SECTOR_SIZE + + class ImageType: """ Enumeration class for distinguishing different image types @@ -293,9 +298,6 @@ class VMHelper(HelperBase): return cls._fetch_image_objectstore(session, instance_id, image, access, type) - #### raw_image=validate_bool(args, 'raw', 'false') - #### add_partition = validate_bool(args, 'add_partition', 'false') - @classmethod def _fetch_image_glance(cls, session, instance_id, image, access, typ): sr = find_sr(session) @@ -305,15 +307,27 @@ class VMHelper(HelperBase): c = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) meta, image_file = c.get_image(image) - vdi_size = meta['size'] + virtual_size = meta['size'] + + vdi_size = virtual_size + if typ == ImageType.DISK: + # Make room for MBR. + vdi_size += MBR_SIZE_BYTES vdi = cls.create_vdi(session, sr, _('Glance image %s') % image, vdi_size, False) def stream(dev): + offset = 0 + if typ == ImageType.DISK: + offset = MBR_SIZE_BYTES + _write_partition(virtual_size, dev) + with open('/dev/%s' % dev, 'wb') as f: + f.seek(offset) for chunk in image_file: f.write(chunk) + with_vdi_attached_here(session, vdi, False, stream) return session.get_xenapi().VDI.get_uuid(vdi) @@ -633,3 +647,24 @@ def get_this_vm_uuid(): def get_this_vm_ref(session): return session.get_xenapi().VM.get_by_uuid(get_this_vm_uuid()) + + +def _write_partition(virtual_size, dev): + dest = '/dev/%s' % dev + mbr_last = MBR_SIZE_SECTORS - 1 + primary_first = MBR_SIZE_SECTORS + primary_last = MBR_SIZE_SECTORS + (virtual_size / SECTOR_SIZE) - 1 + + logging.debug('Writing partition table %d %d to %s...', + primary_first, primary_last, dest) + + def execute(cmd, process_input=None, check_exit_code=True): + return utils.execute(cmd=cmd, + process_input=process_input, + check_exit_code=check_exit_code) + + execute('parted --script %s mklabel msdos' % dest) + execute('parted --script %s mkpart primary %ds %ds' % + (dest, primary_first, primary_last)) + + logging.debug('Writing partition table %s done.', dest) -- cgit From ed84e51475471c5ae37eacdd4d5c93ef91ebcf10 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Sun, 9 Jan 2011 01:40:51 +0000 Subject: fixed small glitch in _fetch_image_glance virtual_size = imeta['size'] changed to virtual_size = int(meta['size']) --- nova/virt/xenapi/vm_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index e601ccff9..51418c444 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -307,7 +307,7 @@ class VMHelper(HelperBase): c = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) meta, image_file = c.get_image(image) - virtual_size = meta['size'] + virtual_size = int(meta['size']) vdi_size = virtual_size if typ == ImageType.DISK: @@ -585,6 +585,7 @@ def with_vdi_attached_here(session, vdi, read_only, f): vbd_rec['userdevice'] = 'autodetect' vbd_rec['bootable'] = False vbd_rec['mode'] = read_only and 'RO' or 'RW' + logging.debug("read_only: %s",str(read_only)) vbd_rec['type'] = 'disk' vbd_rec['unpluggable'] = True vbd_rec['empty'] = False -- cgit From 97ff39bd1d83f3cfa412f291087e025a91d147cd Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Mon, 10 Jan 2011 18:26:40 +0000 Subject: Can now correctly launch images with external kernels through glance. Further tests and Pep8 fixes to come. --- nova/virt/xenapi/vm_utils.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 51418c444..674459841 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -52,7 +52,7 @@ XENAPI_POWER_STATE = { SECTOR_SIZE = 512 MBR_SIZE_SECTORS = 63 MBR_SIZE_BYTES = MBR_SIZE_SECTORS * SECTOR_SIZE - +KERNEL_DIR = '/boot/guest' class ImageType: """ @@ -299,7 +299,7 @@ class VMHelper(HelperBase): access, type) @classmethod - def _fetch_image_glance(cls, session, instance_id, image, access, typ): + def _fetch_image_glance(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') @@ -310,7 +310,8 @@ class VMHelper(HelperBase): virtual_size = int(meta['size']) vdi_size = virtual_size - if typ == ImageType.DISK: + logging.debug("Size for image %s:%d",image,virtual_size) + if type == ImageType.DISK: # Make room for MBR. vdi_size += MBR_SIZE_BYTES @@ -319,7 +320,7 @@ class VMHelper(HelperBase): def stream(dev): offset = 0 - if typ == ImageType.DISK: + if type == ImageType.DISK: offset = MBR_SIZE_BYTES _write_partition(virtual_size, dev) @@ -329,7 +330,18 @@ class VMHelper(HelperBase): f.write(chunk) with_vdi_attached_here(session, vdi, False, stream) - return session.get_xenapi().VDI.get_uuid(vdi) + if (type==ImageType.KERNEL_RAMDISK): + #we need to invoke a plugin for copying VDI's content into proper path + fn = "copy_kernel_vdi" + args = {} + args['vdi-ref'] = vdi + args['image-size']=str(vdi_size) + task = session.async_call_plugin('glance', fn, args) + filename=session.wait_for_task(instance_id,task) + #TODO(salvatore-orlando): remove the VDI as it is not needed anymore + return filename + else: + return session.get_xenapi().VDI.get_uuid(vdi) @classmethod def _fetch_image_objectstore(cls, session, instance_id, image, access, @@ -364,7 +376,6 @@ class VMHelper(HelperBase): fn = "is_vdi_pv" args = {} args['vdi-ref'] = vdi_ref - #TODO: Call proper function in plugin task = session.async_call_plugin('objectstore', fn, args) pv_str = session.wait_for_task(task) if pv_str.lower() == 'true': -- cgit From 6ba35582eec774253d725ab7a6959fdc12cea215 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Tue, 11 Jan 2011 01:50:14 +0000 Subject: Now removing kernel/ramdisk VDI after copy Code tested with PV and HVM guests Fixed pep8 errors Could not run tests - test environment broken on dev machine --- nova/tests/test_xenapi.py | 81 +++++++++++++++++++++++++++----------------- nova/virt/hyperv.py | 2 +- nova/virt/xenapi/vm_utils.py | 20 +++++++---- 3 files changed, 63 insertions(+), 40 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index ec9462ada..7c256968f 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -206,40 +206,57 @@ class XenAPIVMTestCase(test.TestCase): check() - def test_spawn(self): - instance = self._create_instance() - - def check(): - instances = self.conn.list_instances() - self.assertEquals(instances, [1]) - - # Get Nova record for VM - vm_info = self.conn.get_info(1) + def check_vm_record(self, conn): + instances = conn.list_instances() + self.assertEquals(instances, [1]) + + # Get Nova record for VM + vm_info = conn.get_info(1) + + # Get XenAPI record for VM + vms = fake.get_all('VM') + vm = fake.get_record('VM', vms[0]) + + # Check that m1.large above turned into the right thing. + instance_type = instance_types.INSTANCE_TYPES['m1.large'] + mem_kib = long(instance_type['memory_mb']) << 10 + mem_bytes = str(mem_kib << 10) + vcpus = instance_type['vcpus'] + self.assertEquals(vm_info['max_mem'], mem_kib) + self.assertEquals(vm_info['mem'], mem_kib) + self.assertEquals(vm['memory_static_max'], mem_bytes) + self.assertEquals(vm['memory_dynamic_max'], mem_bytes) + self.assertEquals(vm['memory_dynamic_min'], mem_bytes) + self.assertEquals(vm['VCPUs_max'], str(vcpus)) + self.assertEquals(vm['VCPUs_at_startup'], str(vcpus)) + + # Check that the VM is running according to Nova + self.assertEquals(vm_info['state'], power_state.RUNNING) + + # Check that the VM is running according to XenAPI. + self.assertEquals(vm['power_state'], 'Running') + + def _test_spawn(self, image_id, kernel_id, ramdisk_id): + stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) + values = {'name': 1, + 'project_id': self.project.id, + 'user_id': self.user.id, + 'image_id': image_id, + 'kernel_id': kernel_id, + 'ramdisk_id': ramdisk_id, + 'instance_type': 'm1.large', + 'mac_address': 'aa:bb:cc:dd:ee:ff', + } + conn = xenapi_conn.get_connection(False) + instance = db.instance_create(values) + conn.spawn(instance) + self.check_vm_record(conn) - # Get XenAPI record for VM - vms = xenapi_fake.get_all('VM') - vm = xenapi_fake.get_record('VM', vms[0]) - - # Check that m1.large above turned into the right thing. - instance_type = instance_types.INSTANCE_TYPES['m1.large'] - mem_kib = long(instance_type['memory_mb']) << 10 - mem_bytes = str(mem_kib << 10) - vcpus = instance_type['vcpus'] - self.assertEquals(vm_info['max_mem'], mem_kib) - self.assertEquals(vm_info['mem'], mem_kib) - self.assertEquals(vm['memory_static_max'], mem_bytes) - self.assertEquals(vm['memory_dynamic_max'], mem_bytes) - self.assertEquals(vm['memory_dynamic_min'], mem_bytes) - self.assertEquals(vm['VCPUs_max'], str(vcpus)) - self.assertEquals(vm['VCPUs_at_startup'], str(vcpus)) - - # Check that the VM is running according to Nova - self.assertEquals(vm_info['state'], power_state.RUNNING) - - # Check that the VM is running according to XenAPI. - self.assertEquals(vm['power_state'], 'Running') + def test_spawn_raw(self): + self._test_spawn(1, None, None) - check() + def test_spawn(self): + self._test_spawn(1, 2, 3) def tearDown(self): super(XenAPIVMTestCase, self).tearDown() diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 4b9f6f946..4f0f6f9c7 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -89,7 +89,7 @@ REQ_POWER_STATE = { 'Reboot': 10, 'Reset': 11, 'Paused': 32768, - 'Suspended': 32769 + 'Suspended': 32769, } diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 674459841..4f2c754fa 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -54,6 +54,7 @@ MBR_SIZE_SECTORS = 63 MBR_SIZE_BYTES = MBR_SIZE_SECTORS * SECTOR_SIZE KERNEL_DIR = '/boot/guest' + class ImageType: """ Enumeration class for distinguishing different image types @@ -310,7 +311,7 @@ class VMHelper(HelperBase): virtual_size = int(meta['size']) vdi_size = virtual_size - logging.debug("Size for image %s:%d",image,virtual_size) + logging.debug("Size for image %s:%d", image, virtual_size) if type == ImageType.DISK: # Make room for MBR. vdi_size += MBR_SIZE_BYTES @@ -330,15 +331,20 @@ class VMHelper(HelperBase): f.write(chunk) with_vdi_attached_here(session, vdi, False, stream) - if (type==ImageType.KERNEL_RAMDISK): - #we need to invoke a plugin for copying VDI's content into proper path + if (type == ImageType.KERNEL_RAMDISK): + #we need to invoke a plugin for copying VDI's + #content into proper path + logging.debug("Copying VDI %s to /boot/guest on dom0", vdi) fn = "copy_kernel_vdi" args = {} args['vdi-ref'] = vdi - args['image-size']=str(vdi_size) + #let the plugin copy the correct number of bytes + args['image-size'] = str(vdi_size) task = session.async_call_plugin('glance', fn, args) - filename=session.wait_for_task(instance_id,task) - #TODO(salvatore-orlando): remove the VDI as it is not needed anymore + filename = session.wait_for_task(instance_id, task) + #remove the VDI as it is not needed anymore + session.get_xenapi().VDI.destroy(vdi) + logging.debug("Kernel/Ramdisk VDI %s destroyed", vdi) return filename else: return session.get_xenapi().VDI.get_uuid(vdi) @@ -596,7 +602,7 @@ def with_vdi_attached_here(session, vdi, read_only, f): vbd_rec['userdevice'] = 'autodetect' vbd_rec['bootable'] = False vbd_rec['mode'] = read_only and 'RO' or 'RW' - logging.debug("read_only: %s",str(read_only)) + logging.debug("read_only: %s", str(read_only)) vbd_rec['type'] = 'disk' vbd_rec['unpluggable'] = True vbd_rec['empty'] = False -- cgit From 2f9ac0fd02115ff9af2e96f5a92f3442d273c6b0 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 12 Jan 2011 02:41:44 +0000 Subject: Fixed test environments. Fixed bugs in _fetch_image_objecstore and _lookup_image_objcestore (objectstore was broken!) Added tests for glance NEED TO: - add SR & PBD records to fake xenapi session for find_sr to work - fake somehow stream in _fetch_image_glance --- nova/tests/test_xenapi.py | 26 ++++++++++++++++++++++---- nova/tests/xenapi/stubs.py | 11 +++++++++++ nova/virt/xenapi/vm_utils.py | 22 +++++++++++++--------- nova/virt/xenapi/vmops.py | 7 ++++++- 4 files changed, 52 insertions(+), 14 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 7c256968f..5829fa452 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -48,6 +48,7 @@ class XenAPIVolumeTestCase(test.TestCase): FLAGS.xenapi_connection_url = 'test_url' FLAGS.xenapi_connection_password = 'test_pass' db_fakes.stub_out_db_instance_api(self.stubs) + stubs.stubout_glance_client(self.stubs) stubs.stub_out_get_target(self.stubs) xenapi_fake.reset() self.values = {'name': 1, 'id': 1, @@ -104,6 +105,7 @@ class XenAPIVolumeTestCase(test.TestCase): def test_attach_volume(self): """ This shows how to test Ops classes' methods """ stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests) + stubs.stubout_glance_client(self.stubs) conn = xenapi_conn.get_connection(False) volume = self._create_volume() instance = db.instance_create(self.values) @@ -126,6 +128,7 @@ class XenAPIVolumeTestCase(test.TestCase): """ This shows how to test when exceptions are raised """ stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeFailedTests) + stubs.stubout_glance_client(self.stubs) conn = xenapi_conn.get_connection(False) volume = self._create_volume() instance = db.instance_create(self.values) @@ -159,6 +162,7 @@ class XenAPIVMTestCase(test.TestCase): db_fakes.stub_out_db_instance_api(self.stubs) xenapi_fake.create_network('fake', FLAGS.flat_network_bridge) stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) + stubs.stubout_glance_client(self.stubs) self.conn = xenapi_conn.get_connection(False) def test_list_instances_0(self): @@ -214,8 +218,8 @@ class XenAPIVMTestCase(test.TestCase): vm_info = conn.get_info(1) # Get XenAPI record for VM - vms = fake.get_all('VM') - vm = fake.get_record('VM', vms[0]) + vms = xenapi_fake.get_all('VM') + vm = xenapi_fake.get_record('VM', vms[0]) # Check that m1.large above turned into the right thing. instance_type = instance_types.INSTANCE_TYPES['m1.large'] @@ -238,7 +242,9 @@ class XenAPIVMTestCase(test.TestCase): def _test_spawn(self, image_id, kernel_id, ramdisk_id): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) + stubs.stubout_glance_client(self.stubs) values = {'name': 1, + 'id':1, 'project_id': self.project.id, 'user_id': self.user.id, 'image_id': image_id, @@ -252,12 +258,24 @@ class XenAPIVMTestCase(test.TestCase): conn.spawn(instance) self.check_vm_record(conn) - def test_spawn_raw(self): + def test_spawn_raw_objectstore(self): + FLAGS.xenapi_image_service='objectstore' self._test_spawn(1, None, None) - def test_spawn(self): + def test_spawn_objectstore(self): + FLAGS.xenapi_image_service='objectstore' self._test_spawn(1, 2, 3) + def test_spawn_raw_glance(self): + xenapi_fake._create_sr('SR',['','',{'other_config':{'i18n-key':'local-storage'}},'', + '','','iscsi']) + FLAGS.xenapi_image_service='glance' + self._test_spawn(1, None, None) + + def test_spawn_glance(self): + FLAGS.xenapi_image_service='glance' + self._test_spawn(1, 2, 3) + def tearDown(self): super(XenAPIVMTestCase, self).tearDown() self.manager.delete_project(self.project) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 55f751f11..1b6cf1182 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -17,6 +17,7 @@ """Stubouts, mocks and fixtures for the test suite""" from nova.virt import xenapi_conn +from nova.virt.xenapi import vmops from nova.virt.xenapi import fake from nova.virt.xenapi import volume_utils from nova.virt.xenapi import vm_utils @@ -69,6 +70,16 @@ def stubout_instance_snapshot(stubs): stubs.Set(vm_utils, 'wait_for_vhd_coalesce', fake_wait_for_vhd_coalesce) +def stubout_glance_client(stubs): + """Stubs out glance import method for importing fake client""" + def fake_import(self): + """Stubs out get_imported_xenapi of XenAPISession""" + fake_module = 'nova.tests.glance.fake_client' + from_list = ['fake_client'] + return __import__(fake_module, globals(), locals(), from_list, -1) + + stubs.Set(vmops.VMOps, '_get_imported_glance',fake_import) + def stubout_session(stubs, cls): """Stubs out two methods from XenAPISession""" def fake_import(self): diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4f2c754fa..76b58b247 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -19,7 +19,6 @@ Helper methods for operations related to the management of VM records and their attributes like VDIs, VIFs, as well as their lookup functions. """ -import glance.client import logging import os import pickle @@ -73,6 +72,8 @@ class VMHelper(HelperBase): The class that wraps the helper methods together. """ + Glance = None + @classmethod def create_vm(cls, session, instance, kernel, ramdisk, pv_kernel=False): """Create a VM record. Returns a Deferred that gives the new @@ -297,7 +298,7 @@ class VMHelper(HelperBase): access, type) else: return cls._fetch_image_objectstore(session, instance_id, image, - access, type) + user,access, type) @classmethod def _fetch_image_glance(cls, session, instance_id, image, access, type): @@ -305,7 +306,7 @@ class VMHelper(HelperBase): if sr is None: raise exception.NotFound('Cannot find SR to write VDI to') - c = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) + c = cls.Glance.Client(FLAGS.glance_host, FLAGS.glance_port) meta, image_file = c.get_image(image) virtual_size = int(meta['size']) @@ -350,8 +351,8 @@ class VMHelper(HelperBase): return session.get_xenapi().VDI.get_uuid(vdi) @classmethod - def _fetch_image_objectstore(cls, session, instance_id, image, access, - type): + def _fetch_image_objectstore(cls, session, instance_id, image, + user,access,type): url = images.image_url(image) logging.debug("Asking xapi to fetch %s as %s", url, access) fn = (type != ImageType.KERNEL_RAMDISK) and 'get_vdi' or 'get_kernel' @@ -370,20 +371,21 @@ class VMHelper(HelperBase): return uuid @classmethod - def lookup_image(cls, session, vdi_ref): + def lookup_image(cls, session, instance_id,vdi_ref): if FLAGS.xenapi_image_service == 'glance': return cls._lookup_image_glance(session, vdi_ref) else: - return cls._lookup_image_objectstore(session, vdi_ref) + return cls._lookup_image_objectstore(session, instance_id,vdi_ref) @classmethod - def _lookup_image_objectstore(cls, session, vdi_ref): + def _lookup_image_objectstore(cls, session, instance_id,vdi_ref): logging.debug("Looking up vdi %s for PV kernel", vdi_ref) fn = "is_vdi_pv" args = {} args['vdi-ref'] = vdi_ref task = session.async_call_plugin('objectstore', fn, args) - pv_str = session.wait_for_task(task) + pv_str = session.wait_for_task(instance_id,task) + pv = None if pv_str.lower() == 'true': pv = True elif pv_str.lower() == 'false': @@ -580,10 +582,12 @@ def get_vdi_for_vm_safely(session, vm_ref): def find_sr(session): + logging.warning("IN find_sr") host = session.get_xenapi_host() srs = session.get_xenapi().SR.get_all() for sr in srs: sr_rec = session.get_xenapi().SR.get_record(sr) + logging.warning("HERE: %s",sr_rec['uuid']) if not ('i18n-key' in sr_rec['other_config'] and sr_rec['other_config']['i18n-key'] == 'local-storage'): continue diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b6d620782..cec8ecdcc 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -42,6 +42,11 @@ class VMOps(object): self.XenAPI = session.get_imported_xenapi() self._session = session VMHelper.XenAPI = self.XenAPI + VMHelper.Glance = self._get_imported_glance() + + def _get_imported_glance(self): + """Stubout point. This can be replaced with a mock glance module.""" + return __import__('glance') def list_instances(self): """List VM instances""" @@ -77,7 +82,7 @@ class VMOps(object): #Have a look at the VDI and see if it has a PV kernel pv_kernel = False if not instance.kernel_id: - pv_kernel = VMHelper.lookup_image(self._session, vdi_ref) + pv_kernel = VMHelper.lookup_image(self._session, instance.id,vdi_ref) kernel = None if instance.kernel_id: kernel = VMHelper.fetch_image(self._session, instance.id, -- cgit From 6f9408d7ac38d5c857e1e1cdd92c49e000742734 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Wed, 12 Jan 2011 11:08:08 +0000 Subject: Added unit tests for the xenapi-glance integration. This adds a glance simulator that can stub in place of glance.client.Client, and enhances the xapi simulator to add the additional calls that the Glance-specific path requires. The test itself is just the spawn test, but now we run through with xenapi_image_service set to "objectstore", and then again set to "glance". --- nova/tests/glance/__init__.py | 20 ++++++++++ nova/tests/glance/stubs.py | 37 +++++++++++++++++++ nova/tests/test_xenapi.py | 28 ++++++++++---- nova/tests/xenapi/stubs.py | 24 +++++++++--- nova/virt/xenapi/fake.py | 86 ++++++++++++++++++++++++++++++++++++++----- nova/virt/xenapi/vm_utils.py | 35 +++++++++--------- 6 files changed, 191 insertions(+), 39 deletions(-) create mode 100644 nova/tests/glance/__init__.py create mode 100644 nova/tests/glance/stubs.py (limited to 'nova') diff --git a/nova/tests/glance/__init__.py b/nova/tests/glance/__init__.py new file mode 100644 index 000000000..ef9fa05a7 --- /dev/null +++ b/nova/tests/glance/__init__.py @@ -0,0 +1,20 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Citrix Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +:mod:`glance` -- Stubs for Glance +================================= +""" diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py new file mode 100644 index 000000000..2ac5653bb --- /dev/null +++ b/nova/tests/glance/stubs.py @@ -0,0 +1,37 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Citrix Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import StringIO + +import glance.client + + +def stubout_glance_client(stubs, cls): + """Stubs out glance.client.Client""" + stubs.Set(glance.client, 'Client', + lambda *args, **kwargs: cls(*args, **kwargs)) + + +class FakeGlance(object): + def __init__(self, host, port=None, use_ssl=False): + pass + + def get_image(self, image): + meta = { + 'size': 0, + } + image_file = StringIO.StringIO('') + return meta, image_file diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index ec9462ada..4ddfe6829 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -33,6 +33,7 @@ from nova.virt.xenapi import fake as xenapi_fake from nova.virt.xenapi import volume_utils from nova.tests.db import fakes as db_fakes from nova.tests.xenapi import stubs +from nova.tests.glance import stubs as glance_stubs FLAGS = flags.FLAGS @@ -107,18 +108,16 @@ class XenAPIVolumeTestCase(test.TestCase): conn = xenapi_conn.get_connection(False) volume = self._create_volume() instance = db.instance_create(self.values) - xenapi_fake.create_vm(instance.name, 'Running') + vm = xenapi_fake.create_vm(instance.name, 'Running') result = conn.attach_volume(instance.name, volume['id'], '/dev/sdc') def check(): # check that the VM has a VBD attached to it - # Get XenAPI reference for the VM - vms = xenapi_fake.get_all('VM') # Get XenAPI record for VBD vbds = xenapi_fake.get_all('VBD') vbd = xenapi_fake.get_record('VBD', vbds[0]) vm_ref = vbd['VM'] - self.assertEqual(vm_ref, vms[0]) + self.assertEqual(vm_ref, vm) check() @@ -156,9 +155,14 @@ class XenAPIVMTestCase(test.TestCase): FLAGS.xenapi_connection_url = 'test_url' FLAGS.xenapi_connection_password = 'test_pass' xenapi_fake.reset() + xenapi_fake.create_local_srs() db_fakes.stub_out_db_instance_api(self.stubs) xenapi_fake.create_network('fake', FLAGS.flat_network_bridge) stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) + stubs.stubout_get_this_vm_uuid(self.stubs) + stubs.stubout_stream_disk(self.stubs) + glance_stubs.stubout_glance_client(self.stubs, + glance_stubs.FakeGlance) self.conn = xenapi_conn.get_connection(False) def test_list_instances_0(self): @@ -206,7 +210,15 @@ class XenAPIVMTestCase(test.TestCase): check() - def test_spawn(self): + def test_spawn_glance(self): + FLAGS.xenapi_image_service = 'glance' + self._test_spawn() + + def test_spawn_objectstore(self): + FLAGS.xenapi_image_service = 'objectstore' + self._test_spawn() + + def _test_spawn(self): instance = self._create_instance() def check(): @@ -217,8 +229,10 @@ class XenAPIVMTestCase(test.TestCase): vm_info = self.conn.get_info(1) # Get XenAPI record for VM - vms = xenapi_fake.get_all('VM') - vm = xenapi_fake.get_record('VM', vms[0]) + vms = [rec for ref, rec + in xenapi_fake.get_all_records('VM').iteritems() + if not rec['is_control_domain']] + vm = vms[0] # Check that m1.large above turned into the right thing. instance_type = instance_types.INSTANCE_TYPES['m1.large'] diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 55f751f11..ffbca9560 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -91,6 +91,21 @@ def stub_out_get_target(stubs): stubs.Set(volume_utils, '_get_target', fake_get_target) +def stubout_get_this_vm_uuid(stubs): + def f(): + vms = [rec['uuid'] for ref, rec + in fake.get_all_records('VM').iteritems() + if rec['is_control_domain']] + return vms[0] + stubs.Set(vm_utils, 'get_this_vm_uuid', f) + + +def stubout_stream_disk(stubs): + def f(_): + pass + stubs.Set(vm_utils, '_stream_disk', f) + + class FakeSessionForVMTests(fake.SessionBase): """ Stubs out a XenAPISession for VM tests """ def __init__(self, uri): @@ -100,7 +115,10 @@ class FakeSessionForVMTests(fake.SessionBase): return self.xenapi.network.get_all_records() def host_call_plugin(self, _1, _2, _3, _4, _5): - return '' + sr_ref = fake.get_all('SR')[0] + vdi_ref = fake.create_vdi('', False, sr_ref, False) + vdi_rec = fake.get_record('VDI', vdi_ref) + return '%s' % vdi_rec['uuid'] def VM_start(self, _1, ref, _2, _3): vm = fake.get_record('VM', ref) @@ -135,10 +153,6 @@ class FakeSessionForVolumeTests(fake.SessionBase): def __init__(self, uri): super(FakeSessionForVolumeTests, self).__init__(uri) - def VBD_plug(self, _1, ref): - rec = fake.get_record('VBD', ref) - rec['currently-attached'] = True - def VDI_introduce(self, _1, uuid, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11): valid_vdi = False diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index aa4026f97..cd7c96b22 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -74,6 +74,7 @@ def reset(): for c in _CLASSES: _db_content[c] = {} create_host('fake') + create_vm('fake', 'Running', is_a_template=False, is_control_domain=True) def create_host(name_label): @@ -134,14 +135,20 @@ def create_vdi(name_label, read_only, sr_ref, sharable): def create_vbd(vm_ref, vdi_ref): - vbd_rec = {'VM': vm_ref, 'VDI': vdi_ref} + vbd_rec = { + 'VM': vm_ref, + 'VDI': vdi_ref, + 'currently_attached': False, + } vbd_ref = _create_object('VBD', vbd_rec) after_VBD_create(vbd_ref, vbd_rec) return vbd_ref def after_VBD_create(vbd_ref, vbd_rec): - """Create backref from VM to VBD when VBD is created""" + """Create read-only fields and backref from VM to VBD when VBD is created""" + vbd_rec['currently_attached'] = False + vbd_rec['device'] = '' vm_ref = vbd_rec['VM'] vm_rec = _db_content['VM'][vm_ref] vm_rec['VBDs'] = [vbd_ref] @@ -150,9 +157,10 @@ def after_VBD_create(vbd_ref, vbd_rec): vbd_rec['vm_name_label'] = vm_name_label -def create_pbd(config, sr_ref, attached): +def create_pbd(config, host_ref, sr_ref, attached): return _create_object('PBD', { 'device-config': config, + 'host': host_ref, 'SR': sr_ref, 'currently-attached': attached, }) @@ -165,6 +173,33 @@ def create_task(name_label): }) +def create_local_srs(): + """Create an SR that looks like the one created on the local disk by + default by the XenServer installer. Do this one per host.""" + for host_ref in _db_content['host'].keys(): + _create_local_sr(host_ref) + + +def _create_local_sr(host_ref): + sr_ref = _create_object('SR', { + 'name_label': 'Local storage', + 'type': 'lvm', + 'content_type': 'user', + 'shared': False, + 'physical_size': str(1 << 30), + 'physical_utilisation': str(0), + 'virtual_allocation': str(0), + 'other_config': { + 'i18n-original-value-name_label': 'Local storage', + 'i18n-key': 'local-storage', + }, + 'VDIs': [] + }) + pbd_ref = create_pbd('', host_ref, sr_ref, True) + _db_content['SR'][sr_ref]['PBDs'] = [pbd_ref] + return sr_ref + + def _create_object(table, obj): ref = str(uuid.uuid4()) obj['uuid'] = str(uuid.uuid4()) @@ -177,9 +212,10 @@ def _create_sr(table, obj): # Forces fake to support iscsi only if sr_type != 'iscsi': raise Failure(['SR_UNKNOWN_DRIVER', sr_type]) + host_ref = _db_content['host'].keys()[0] sr_ref = _create_object(table, obj[2]) vdi_ref = create_vdi('', False, sr_ref, False) - pbd_ref = create_pbd('', sr_ref, True) + pbd_ref = create_pbd('', host_ref, sr_ref, True) _db_content['SR'][sr_ref]['VDIs'] = [vdi_ref] _db_content['SR'][sr_ref]['PBDs'] = [pbd_ref] _db_content['VDI'][vdi_ref]['SR'] = sr_ref @@ -231,6 +267,20 @@ class SessionBase(object): def __init__(self, uri): self._session = None + def VBD_plug(self, _1, ref): + rec = get_record('VBD', ref) + if rec['currently_attached']: + raise Failure(['DEVICE_ALREADY_ATTACHED', ref]) + rec['currently_attached'] = True + rec['device'] = rec['userdevice'] + + def VBD_unplug(self, _1, ref): + rec = get_record('VBD', ref) + if not rec['currently_attached']: + raise Failure(['DEVICE_ALREADY_DETACHED', ref]) + rec['currently_attached'] = False + rec['device'] = '' + def xenapi_request(self, methodname, params): if methodname.startswith('login'): self._login(methodname, params) @@ -287,6 +337,8 @@ class SessionBase(object): return lambda *params: self._getter(name, params) elif self._is_create(name): return lambda *params: self._create(name, params) + elif self._is_destroy(name): + return lambda *params: self._destroy(name, params) else: return None @@ -297,10 +349,16 @@ class SessionBase(object): bits[1].startswith(getter and 'get_' or 'set_')) def _is_create(self, name): + return self._is_method(name, 'create') + + def _is_destroy(self, name): + return self._is_method(name, 'destroy') + + def _is_method(self, name, meth): bits = name.split('.') return (len(bits) == 2 and bits[0] in _CLASSES and - bits[1] == 'create') + bits[1] == meth) def _getter(self, name, params): self._check_session(params) @@ -368,10 +426,9 @@ class SessionBase(object): _create_sr(cls, params) or _create_object(cls, params[1]) # Call hook to provide any fixups needed (ex. creating backrefs) - try: - globals()["after_%s_create" % cls](ref, params[1]) - except KeyError: - pass + after_hook = 'after_%s_create' % cls + if after_hook in globals(): + globals()[after_hook](ref, params[1]) obj = get_record(cls, ref) @@ -381,6 +438,15 @@ class SessionBase(object): return ref + def _destroy(self, name, params): + self._check_session(params) + self._check_arg_count(params, 2) + table, _ = name.split('.') + ref = params[1] + if ref not in _db_content[table]: + raise Failure(['HANDLE_INVALID', table, ref]) + del _db_content[table][ref] + def _async(self, name, params): task_ref = create_task(name) task = _db_content['task'][task_ref] @@ -418,7 +484,7 @@ class SessionBase(object): try: return result[0] except IndexError: - return None + raise Failure(['UUID_INVALID', v, result, recs, k]) return result diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 674459841..63f25f76c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -296,7 +296,7 @@ class VMHelper(HelperBase): access, type) else: return cls._fetch_image_objectstore(session, instance_id, image, - access, type) + access, user.secret, type) @classmethod def _fetch_image_glance(cls, session, instance_id, image, access, type): @@ -318,18 +318,7 @@ class VMHelper(HelperBase): vdi = cls.create_vdi(session, sr, _('Glance image %s') % image, vdi_size, False) - def stream(dev): - offset = 0 - if type == ImageType.DISK: - offset = MBR_SIZE_BYTES - _write_partition(virtual_size, dev) - - with open('/dev/%s' % dev, 'wb') as f: - f.seek(offset) - for chunk in image_file: - f.write(chunk) - - with_vdi_attached_here(session, vdi, False, stream) + with_vdi_attached_here(session, vdi, False, _stream_disk) if (type==ImageType.KERNEL_RAMDISK): #we need to invoke a plugin for copying VDI's content into proper path fn = "copy_kernel_vdi" @@ -345,14 +334,14 @@ class VMHelper(HelperBase): @classmethod def _fetch_image_objectstore(cls, session, instance_id, image, access, - type): + secret, type): url = images.image_url(image) logging.debug("Asking xapi to fetch %s as %s", url, access) fn = (type != ImageType.KERNEL_RAMDISK) and 'get_vdi' or 'get_kernel' args = {} args['src_url'] = url args['username'] = access - args['password'] = user.secret + args['password'] = secret args['add_partition'] = 'false' args['raw'] = 'false' if type != ImageType.KERNEL_RAMDISK: @@ -629,7 +618,7 @@ def vbd_unplug_with_retry(session, vbd): session.get_xenapi().VBD.unplug(vbd) logging.debug(_('VBD.unplug successful first time.')) return - except XenAPI.Failure, e: + except VMHelper.XenAPI.Failure, e: if (len(e.details) > 0 and e.details[0] == 'DEVICE_DETACH_REJECTED'): logging.debug(_('VBD.unplug rejected: retrying...')) @@ -647,7 +636,7 @@ def vbd_unplug_with_retry(session, vbd): def ignore_failure(func, *args, **kwargs): try: return func(*args, **kwargs) - except XenAPI.Failure, e: + except VMHelper.XenAPI.Failure, e: logging.error(_('Ignoring XenAPI.Failure %s'), e) return None @@ -661,6 +650,18 @@ def get_this_vm_ref(session): return session.get_xenapi().VM.get_by_uuid(get_this_vm_uuid()) +def _stream_disk(dev): + offset = 0 + if type == ImageType.DISK: + offset = MBR_SIZE_BYTES + _write_partition(virtual_size, dev) + + with open('/dev/%s' % dev, 'wb') as f: + f.seek(offset) + for chunk in image_file: + f.write(chunk) + + def _write_partition(virtual_size, dev): dest = '/dev/%s' % dev mbr_last = MBR_SIZE_SECTORS - 1 -- cgit From ba0f974c126c2a24ca6b1464ccc4a06be071b04e Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Wed, 12 Jan 2011 11:54:58 +0000 Subject: PEP8 fixes, and switch to using the new LOG in vm_utils, matching what's just come in from trunk. --- nova/tests/glance/stubs.py | 2 +- nova/tests/test_xenapi.py | 14 +++++------ nova/virt/xenapi/fake.py | 3 ++- nova/virt/xenapi/vm_utils.py | 55 +++++++++++++++++++++----------------------- nova/virt/xenapi/vmops.py | 3 ++- 5 files changed, 37 insertions(+), 40 deletions(-) (limited to 'nova') diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index 2ac5653bb..f182b857a 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -28,7 +28,7 @@ def stubout_glance_client(stubs, cls): class FakeGlance(object): def __init__(self, host, port=None, use_ssl=False): pass - + def get_image(self, image): meta = { 'size': 0, diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 19e550636..ce0bc002a 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -245,7 +245,7 @@ class XenAPIVMTestCase(test.TestCase): def _test_spawn(self, image_id, kernel_id, ramdisk_id): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) values = {'name': 1, - 'id':1, + 'id': 1, 'project_id': self.project.id, 'user_id': self.user.id, 'image_id': image_id, @@ -260,23 +260,21 @@ class XenAPIVMTestCase(test.TestCase): self.check_vm_record(conn) def test_spawn_raw_objectstore(self): - FLAGS.xenapi_image_service='objectstore' + FLAGS.xenapi_image_service = 'objectstore' self._test_spawn(1, None, None) def test_spawn_objectstore(self): - FLAGS.xenapi_image_service='objectstore' + FLAGS.xenapi_image_service = 'objectstore' self._test_spawn(1, 2, 3) def test_spawn_raw_glance(self): - xenapi_fake._create_sr('SR',['','',{'other_config':{'i18n-key':'local-storage'}},'', - '','','iscsi']) - FLAGS.xenapi_image_service='glance' + FLAGS.xenapi_image_service = 'glance' self._test_spawn(1, None, None) def test_spawn_glance(self): - FLAGS.xenapi_image_service='glance' + FLAGS.xenapi_image_service = 'glance' self._test_spawn(1, 2, 3) - + def tearDown(self): super(XenAPIVMTestCase, self).tearDown() self.manager.delete_project(self.project) diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 96333a58f..4bfaf4b57 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -148,7 +148,8 @@ def create_vbd(vm_ref, vdi_ref): def after_VBD_create(vbd_ref, vbd_rec): - """Create read-only fields and backref from VM to VBD when VBD is created""" + """Create read-only fields and backref from VM to VBD when VBD is + created.""" vbd_rec['currently_attached'] = False vbd_rec['device'] = '' vm_ref = vbd_rec['VM'] diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4e32c880e..7df00111e 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -19,6 +19,7 @@ Helper methods for operations related to the management of VM records and their attributes like VDIs, VIFs, as well as their lookup functions. """ +import os import pickle import re import urllib @@ -229,8 +230,8 @@ class VMHelper(HelperBase): 'other_config': {}, 'sm_config': {}, 'tags': []}) - logging.debug(_('Created VDI %s (%s, %s, %s) on %s.'), vdi_ref, - name_label, virtual_size, read_only, sr_ref) + LOG.debug(_('Created VDI %s (%s, %s, %s) on %s.'), vdi_ref, + name_label, virtual_size, read_only, sr_ref) return vdi_ref @classmethod @@ -308,7 +309,7 @@ class VMHelper(HelperBase): virtual_size = int(meta['size']) vdi_size = virtual_size - logging.debug("Size for image %s:%d", image, virtual_size) + LOG.debug(_("Size for image %s:%d"), image, virtual_size) if type == ImageType.DISK: # Make room for MBR. vdi_size += MBR_SIZE_BYTES @@ -320,7 +321,7 @@ class VMHelper(HelperBase): if (type == ImageType.KERNEL_RAMDISK): #we need to invoke a plugin for copying VDI's #content into proper path - logging.debug("Copying VDI %s to /boot/guest on dom0", vdi) + LOG.debug(_("Copying VDI %s to /boot/guest on dom0"), vdi) fn = "copy_kernel_vdi" args = {} args['vdi-ref'] = vdi @@ -330,7 +331,7 @@ class VMHelper(HelperBase): filename = session.wait_for_task(instance_id, task) #remove the VDI as it is not needed anymore session.get_xenapi().VDI.destroy(vdi) - logging.debug("Kernel/Ramdisk VDI %s destroyed", vdi) + LOG.debug(_("Kernel/Ramdisk VDI %s destroyed"), vdi) return filename else: return session.get_xenapi().VDI.get_uuid(vdi) @@ -339,7 +340,6 @@ class VMHelper(HelperBase): def _fetch_image_objectstore(cls, session, instance_id, image, access, secret, type): url = images.image_url(image) - access = AuthManager().get_access_key(user, project) LOG.debug(_("Asking xapi to fetch %s as %s"), url, access) fn = (type != ImageType.KERNEL_RAMDISK) and 'get_vdi' or 'get_kernel' args = {} @@ -357,7 +357,7 @@ class VMHelper(HelperBase): return uuid @classmethod - def lookup_image(cls, session, instance_id,vdi_ref): + def lookup_image(cls, session, instance_id, vdi_ref): if FLAGS.xenapi_image_service == 'glance': return cls._lookup_image_glance(session, vdi_ref) else: @@ -370,7 +370,7 @@ class VMHelper(HelperBase): args = {} args['vdi-ref'] = vdi_ref task = session.async_call_plugin('objectstore', fn, args) - pv_str = session.wait_for_task(instance_id,task) + pv_str = session.wait_for_task(instance_id, task) pv = None if pv_str.lower() == 'true': pv = True @@ -381,18 +381,18 @@ class VMHelper(HelperBase): @classmethod def _lookup_image_glance(cls, session, vdi_ref): - logging.debug("Looking up vdi %s for PV kernel", vdi_ref) + LOG.debug(_("Looking up vdi %s for PV kernel"), vdi_ref) def is_vdi_pv(dev): - logging.debug("Running pygrub against %s", dev) + LOG.debug(_("Running pygrub against %s"), dev) output = os.popen('pygrub -qn /dev/%s' % dev) for line in output.readlines(): #try to find kernel string m = re.search('(?<=kernel:)/.*(?:>)', line) if m and m.group(0).find('xen') != -1: - logging.debug("Found Xen kernel %s" % m.group(0)) + LOG.debug(_("Found Xen kernel %s") % m.group(0)) return True - logging.debug("No Xen kernel found. Booting HVM.") + LOG.debug(_("No Xen kernel found. Booting HVM.")) return False return with_vdi_attached_here(session, vdi_ref, True, is_vdi_pv) @@ -566,12 +566,10 @@ def get_vdi_for_vm_safely(session, vm_ref): def find_sr(session): - logging.warning("IN find_sr") host = session.get_xenapi_host() srs = session.get_xenapi().SR.get_all() for sr in srs: sr_rec = session.get_xenapi().SR.get_record(sr) - logging.warning("HERE: %s",sr_rec['uuid']) if not ('i18n-key' in sr_rec['other_config'] and sr_rec['other_config']['i18n-key'] == 'local-storage'): continue @@ -590,7 +588,6 @@ def with_vdi_attached_here(session, vdi, read_only, f): vbd_rec['userdevice'] = 'autodetect' vbd_rec['bootable'] = False vbd_rec['mode'] = read_only and 'RO' or 'RW' - logging.debug("read_only: %s", str(read_only)) vbd_rec['type'] = 'disk' vbd_rec['unpluggable'] = True vbd_rec['empty'] = False @@ -598,19 +595,19 @@ def with_vdi_attached_here(session, vdi, read_only, f): vbd_rec['qos_algorithm_type'] = '' vbd_rec['qos_algorithm_params'] = {} vbd_rec['qos_supported_algorithms'] = [] - logging.debug(_('Creating VBD for VDI %s ... '), vdi) + LOG.debug(_('Creating VBD for VDI %s ... '), vdi) vbd = session.get_xenapi().VBD.create(vbd_rec) - logging.debug(_('Creating VBD for VDI %s done.'), vdi) + LOG.debug(_('Creating VBD for VDI %s done.'), vdi) try: - logging.debug(_('Plugging VBD %s ... '), vbd) + LOG.debug(_('Plugging VBD %s ... '), vbd) session.get_xenapi().VBD.plug(vbd) - logging.debug(_('Plugging VBD %s done.'), vbd) + LOG.debug(_('Plugging VBD %s done.'), vbd) return f(session.get_xenapi().VBD.get_device(vbd)) finally: - logging.debug(_('Destroying VBD for VDI %s ... '), vdi) + LOG.debug(_('Destroying VBD for VDI %s ... '), vdi) vbd_unplug_with_retry(session, vbd) ignore_failure(session.get_xenapi().VBD.destroy, vbd) - logging.debug(_('Destroying VBD for VDI %s done.'), vdi) + LOG.debug(_('Destroying VBD for VDI %s done.'), vdi) def vbd_unplug_with_retry(session, vbd): @@ -621,19 +618,19 @@ def vbd_unplug_with_retry(session, vbd): while True: try: session.get_xenapi().VBD.unplug(vbd) - logging.debug(_('VBD.unplug successful first time.')) + LOG.debug(_('VBD.unplug successful first time.')) return except VMHelper.XenAPI.Failure, e: if (len(e.details) > 0 and e.details[0] == 'DEVICE_DETACH_REJECTED'): - logging.debug(_('VBD.unplug rejected: retrying...')) + LOG.debug(_('VBD.unplug rejected: retrying...')) time.sleep(1) elif (len(e.details) > 0 and e.details[0] == 'DEVICE_ALREADY_DETACHED'): - logging.debug(_('VBD.unplug successful eventually.')) + LOG.debug(_('VBD.unplug successful eventually.')) return else: - logging.error(_('Ignoring XenAPI.Failure in VBD.unplug: %s'), + LOG.error(_('Ignoring XenAPI.Failure in VBD.unplug: %s'), e) return @@ -642,7 +639,7 @@ def ignore_failure(func, *args, **kwargs): try: return func(*args, **kwargs) except VMHelper.XenAPI.Failure, e: - logging.error(_('Ignoring XenAPI.Failure %s'), e) + LOG.error(_('Ignoring XenAPI.Failure %s'), e) return None @@ -673,8 +670,8 @@ def _write_partition(virtual_size, dev): primary_first = MBR_SIZE_SECTORS primary_last = MBR_SIZE_SECTORS + (virtual_size / SECTOR_SIZE) - 1 - logging.debug('Writing partition table %d %d to %s...', - primary_first, primary_last, dest) + LOG.debug(_('Writing partition table %d %d to %s...'), + primary_first, primary_last, dest) def execute(cmd, process_input=None, check_exit_code=True): return utils.execute(cmd=cmd, @@ -685,4 +682,4 @@ def _write_partition(virtual_size, dev): execute('parted --script %s mkpart primary %ds %ds' % (dest, primary_first, primary_last)) - logging.debug('Writing partition table %s done.', dest) + LOG.debug(_('Writing partition table %s done.'), dest) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 34e3f9c9f..9ed8896b6 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -80,7 +80,8 @@ class VMOps(object): #Have a look at the VDI and see if it has a PV kernel pv_kernel = False if not instance.kernel_id: - pv_kernel = VMHelper.lookup_image(self._session, instance.id, vdi_ref) + pv_kernel = VMHelper.lookup_image(self._session, instance.id, + vdi_ref) kernel = None if instance.kernel_id: kernel = VMHelper.fetch_image(self._session, instance.id, -- cgit From 7d56986366a349f5636f8de6018fb52e9befd440 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 12 Jan 2011 14:17:22 +0000 Subject: Fix for _stream_disk --- nova/virt/xenapi/vm_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 7df00111e..4b8cec97b 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -307,7 +307,6 @@ class VMHelper(HelperBase): meta, image_file = c.get_image(image) virtual_size = int(meta['size']) - vdi_size = virtual_size LOG.debug(_("Size for image %s:%d"), image, virtual_size) if type == ImageType.DISK: @@ -317,7 +316,8 @@ class VMHelper(HelperBase): vdi = cls.create_vdi(session, sr, _('Glance image %s') % image, vdi_size, False) - with_vdi_attached_here(session, vdi, False, _stream_disk) + with_vdi_attached_here(session, vdi, False, + lambda dev:_stream_disk(dev,image_file)) if (type == ImageType.KERNEL_RAMDISK): #we need to invoke a plugin for copying VDI's #content into proper path @@ -652,7 +652,7 @@ def get_this_vm_ref(session): return session.get_xenapi().VM.get_by_uuid(get_this_vm_uuid()) -def _stream_disk(dev): +def _stream_disk(dev,image_file): offset = 0 if type == ImageType.DISK: offset = MBR_SIZE_BYTES -- cgit From 32eac05776d18dcbde49aa022f149fd597907cbe Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 12 Jan 2011 14:28:50 +0000 Subject: Fixing the stub for _stream_disk as well --- nova/tests/xenapi/stubs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 36a984f0e..9add7e592 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -125,7 +125,7 @@ def stubout_get_this_vm_uuid(stubs): def stubout_stream_disk(stubs): - def f(_): + def f(_1,_2): pass stubs.Set(vm_utils, '_stream_disk', f) -- cgit From f3dba791b9f10fec759dce0fe4e2abc214e3fd61 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 12 Jan 2011 14:37:03 +0000 Subject: pep8 fixes --- nova/tests/xenapi/stubs.py | 2 +- nova/virt/xenapi/vm_utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 9add7e592..0c0fe5bc9 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -125,7 +125,7 @@ def stubout_get_this_vm_uuid(stubs): def stubout_stream_disk(stubs): - def f(_1,_2): + def f(_1, _2): pass stubs.Set(vm_utils, '_stream_disk', f) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4b8cec97b..6f19f5970 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -317,7 +317,7 @@ class VMHelper(HelperBase): vdi_size, False) with_vdi_attached_here(session, vdi, False, - lambda dev:_stream_disk(dev,image_file)) + lambda dev: _stream_disk(dev, image_file)) if (type == ImageType.KERNEL_RAMDISK): #we need to invoke a plugin for copying VDI's #content into proper path @@ -652,7 +652,7 @@ def get_this_vm_ref(session): return session.get_xenapi().VM.get_by_uuid(get_this_vm_uuid()) -def _stream_disk(dev,image_file): +def _stream_disk(dev, image_file): offset = 0 if type == ImageType.DISK: offset = MBR_SIZE_BYTES -- cgit From c71d5d41bb6e5d7a046a76563eed75a4d6e77e90 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 12 Jan 2011 17:05:40 +0000 Subject: Fixed another issue in _stream_disk, as it did never execute _write_partition. Fixed fake method accordingly. Fixed pep8 errors. --- nova/tests/xenapi/stubs.py | 2 +- nova/virt/xenapi/vm_utils.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 0c0fe5bc9..624995ada 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -125,7 +125,7 @@ def stubout_get_this_vm_uuid(stubs): def stubout_stream_disk(stubs): - def f(_1, _2): + def f(_1, _2, _3, _4): pass stubs.Set(vm_utils, '_stream_disk', f) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 6f19f5970..2c9d53858 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -317,7 +317,9 @@ class VMHelper(HelperBase): vdi_size, False) with_vdi_attached_here(session, vdi, False, - lambda dev: _stream_disk(dev, image_file)) + lambda dev: + _stream_disk(dev, type, + virtual_size, image_file)) if (type == ImageType.KERNEL_RAMDISK): #we need to invoke a plugin for copying VDI's #content into proper path @@ -652,7 +654,7 @@ def get_this_vm_ref(session): return session.get_xenapi().VM.get_by_uuid(get_this_vm_uuid()) -def _stream_disk(dev, image_file): +def _stream_disk(dev, type, virtual_size, image_file): offset = 0 if type == ImageType.DISK: offset = MBR_SIZE_BYTES -- cgit