From e601ab4a1068029b2f0b79789ed506fda1332404 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 16 Dec 2010 20:59:27 -0600 Subject: XenAPI Snapshots first cut --- nova/virt/libvirt_conn.py | 7 ++++ nova/virt/xenapi/vm_utils.py | 91 ++++++++++++++++++++++++++++++++++++++++++++ nova/virt/xenapi/vmops.py | 43 +++++++++++++++++---- nova/virt/xenapi_conn.py | 7 ++++ 4 files changed, 141 insertions(+), 7 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 5a8c71850..112ac7d37 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -265,6 +265,13 @@ class LibvirtConnection(object): raise exception.NotFound("No disk at %s" % mount_device) virt_dom.detachDevice(xml) + @exception.wrap_exception + def snapshot(self, instance): + """ Create snapshot from a running VM instance """ + #TODO(sirp): only exists for XenAPI driver for now + raise NotImplementedError( + "Instance snapshotting is not supported for libvirt at this time") + @exception.wrap_exception def reboot(self, instance): self.destroy(instance, False) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 2f5d78e75..c3e8b30b7 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 time #FIXME(sirp): take this out, replace with greenthread.sleep import logging import urllib from xml.dom import minidom @@ -148,6 +149,63 @@ class VMHelper(): vm_ref, network_ref) return vif_ref + + @classmethod + def create_snapshot(cls, session, vm_ref, label): + logging.debug("Snapshotting VM %s with label '%s'...", vm_ref, label) + + #TODO(sirp): Add quiesce and VSS locking support when Windows support + # is added + + #TODO(sirp): Make safe_lookup_vdi for assert? + vdi_refs = VMHelper.lookup_vm_vdis(session, vm_ref) + if vdi_refs is None or len(vdi_refs) != 1: + raise Exception("Unexpected number of VDIs (%s) found for VM %s" + % (len(vdi_refs), vm_ref)) + vdi_ref = vdi_refs[0] + vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) + vdi_uuid = vdi_rec["uuid"] + + #NOTE(sirp): We may need to wait for our parent to coalese with his + # parent + original_parent_uuid = get_vhd_parent_uuid(session, vdi_ref) + + task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) + template_vm_ref = session.wait_for_task(task) + logging.debug('Created snapshot %s from VM %s.', template_vm_ref, + vm_ref) + + #NOTE(sirp): wait for any coalescing + #NOTE(sirp): for some reason re-scan wasn't occuring automatically on + # XS5.6 + #TODO(sirp): clean this up, perhaps use LoopingCall + sr_ref = vdi_rec["SR"] + scan_sr(session, sr_ref) + parent_uuid = get_vhd_parent_uuid(session, vdi_ref) + time.sleep(5) + while original_parent_uuid and (parent_uuid != original_parent_uuid): + scan_sr(session, sr_ref) + logging.debug( + "Parent %s doesn't match original parent %s, " + "waiting for coalesce...", parent_uuid, original_parent_uuid) + #TODO(sirp): make this non-blocking + time.sleep(5) + parent_uuid = get_vhd_parent_uuid(session, vdi_ref) + + vdi_uuids = [vdi_uuid, parent_uuid] + return template_vm_ref, vdi_uuids + + + @classmethod + def upload_image(cls, session, vdi_uuids, glance_label): + logging.debug("Asking xapi to upload %s as '%s'", vdi_uuids, + glance_label) + kwargs = {'vdi_uuids': ','.join(vdi_uuids), + 'glance_label': glance_label} + task = session.async_call_plugin('glance', 'put_vdis', kwargs) + session.wait_for_task(task) + + @classmethod def fetch_image(cls, session, image, user, project, use_sr): """use_sr: True to put the image as a VDI in an SR, False to place @@ -258,3 +316,36 @@ def get_rrd(host, uuid): return xml.read() except IOError: return None + + +#TODO(sirp): This code comes from XS5.6 pluginlib.py, we should refactor to +# use that implmenetation +def get_vhd_parent(session, vdi_rec): + """ + Returns the VHD parent of the given VDI record, as a (ref, rec) pair. + Returns None if we're at the root of the tree. + """ + if 'vhd-parent' in vdi_rec['sm_config']: + parent_uuid = vdi_rec['sm_config']['vhd-parent'] + #NOTE(sirp): changed xenapi -> get_xenapi() + parent_ref = session.get_xenapi().VDI.get_by_uuid(parent_uuid) + parent_rec = session.get_xenapi().VDI.get_record(parent_ref) + #NOTE(sirp): changed log -> logging + logging.debug("VHD %s has parent %s", vdi_rec['uuid'], parent_ref) + return parent_ref, parent_rec + else: + return None + +def get_vhd_parent_uuid(session, vdi_ref): + vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) + ret = get_vhd_parent(session, vdi_rec) + if ret: + parent_ref, parent_rec = ret + return parent_rec["uuid"] + else: + return None + +def scan_sr(session, sr_ref): + logging.debug("Re-scanning SR %s", sr_ref) + task = session.call_xenapi('Async.SR.scan', sr_ref) + session.wait_for_task(task) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3034df9e1..50b5ec3d6 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -78,6 +78,31 @@ class VMOps(object): self._session.call_xenapi('VM.start', vm_ref, False, False) logging.info('Spawning VM %s created %s.', instance.name, vm_ref) + + def snapshot(self, instance): + """ Create snapshot from a running VM instance """ + #TODO(sirp): Add quiesce and VSS locking support when Windows support + # is added + vm_ref = VMHelper.lookup(self._session, instance.name) + + #TODO(sirp): this is the label in Xen, we need to add a human friendly + # label that we store in paralalx + label = "%s-snapshot" % instance.name + glance_name = "MySnapshot" + + try: + template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( + self._session, vm_ref, label) + except XenAPI.Failure, exc: + logging.error("Unable to Snapshot %s: %s", vm_ref, exc) + return + + try: + # call plugin to ship snapshot off to glance + VMHelper.upload_image( + self._session, template_vdi_uuids, glance_name) + finally: + self._destroy(template_vm_ref, shutdown=False) def reboot(self, instance): """ Reboot VM instance """ @@ -89,20 +114,24 @@ class VMOps(object): self._session.wait_for_task(task) def destroy(self, instance): - """ Destroy VM instance """ vm = VMHelper.lookup(self._session, instance.name) + return self._destroy(vm, shutdown=True) + + def _destroy(self, vm, shutdown=True): + """ Destroy VM instance """ if vm is None: # Don't complain, just return. This lets us clean up instances # that have already disappeared from the underlying platform. return # Get the VDIs related to the VM vdis = VMHelper.lookup_vm_vdis(self._session, vm) - try: - task = self._session.call_xenapi('Async.VM.hard_shutdown', - vm) - self._session.wait_for_task(task) - except XenAPI.Failure, exc: - logging.warn(exc) + if shutdown: + try: + task = self._session.call_xenapi('Async.VM.hard_shutdown', + vm) + self._session.wait_for_task(task) + except XenAPI.Failure, exc: + logging.warn(exc) # Disk clean-up if vdis: for vdi in vdis: diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 6beb08f5e..8aacec507 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -115,6 +115,13 @@ class XenAPIConnection(object): """ Create VM instance """ self._vmops.spawn(instance) + + def snapshot(self, instance): + """ Create snapshot from a running VM instance """ + #TODO(sirp): Add quiesce and VSS locking support when Windows support + # is added + self._vmops.snapshot(instance) + def reboot(self, instance): """ Reboot VM instance """ self._vmops.reboot(instance) -- cgit From ae54d5bdf3e0615c5be9ebe4f03f7256f22484ee Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 17 Dec 2010 14:21:03 -0600 Subject: Add wait_for_vhd_coalesce --- nova/virt/xenapi/vm_utils.py | 52 +++++++++++++++++++++++++++++--------------- nova/virt/xenapi/vmops.py | 3 +++ 2 files changed, 37 insertions(+), 18 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index c3e8b30b7..1b18b0a01 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -175,26 +175,12 @@ class VMHelper(): logging.debug('Created snapshot %s from VM %s.', template_vm_ref, vm_ref) - #NOTE(sirp): wait for any coalescing - #NOTE(sirp): for some reason re-scan wasn't occuring automatically on - # XS5.6 - #TODO(sirp): clean this up, perhaps use LoopingCall sr_ref = vdi_rec["SR"] - scan_sr(session, sr_ref) - parent_uuid = get_vhd_parent_uuid(session, vdi_ref) - time.sleep(5) - while original_parent_uuid and (parent_uuid != original_parent_uuid): - scan_sr(session, sr_ref) - logging.debug( - "Parent %s doesn't match original parent %s, " - "waiting for coalesce...", parent_uuid, original_parent_uuid) - #TODO(sirp): make this non-blocking - time.sleep(5) - parent_uuid = get_vhd_parent_uuid(session, vdi_ref) - - vdi_uuids = [vdi_uuid, parent_uuid] - return template_vm_ref, vdi_uuids + parent_uuid = wait_for_vhd_coalesce( + session, sr_ref, vdi_ref, original_parent_uuid) + #TODO(sirp): we need to assert only one parent, not parents two deep + return template_vm_ref, [vdi_uuid, parent_uuid] @classmethod def upload_image(cls, session, vdi_uuids, glance_label): @@ -336,6 +322,7 @@ def get_vhd_parent(session, vdi_rec): else: return None + def get_vhd_parent_uuid(session, vdi_ref): vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) ret = get_vhd_parent(session, vdi_rec) @@ -345,7 +332,36 @@ def get_vhd_parent_uuid(session, vdi_ref): else: return None + def scan_sr(session, sr_ref): logging.debug("Re-scanning SR %s", sr_ref) task = session.call_xenapi('Async.SR.scan', sr_ref) session.wait_for_task(task) + + +def wait_for_vhd_coalesce(session, sr_ref, vdi_ref, original_parent_uuid): + """ TODO Explain why coalescing has to occur here """ + #TODO(sirp): we need to timeout this req after a while + #NOTE(sirp): for some reason re-scan wasn't occuring automatically on + # XS5.6 + #TODO(sirp): clean this up, perhaps use LoopingCall + def _get_vhd_parent_uuid_with_refresh(first_time): + if not first_time: + #TODO(sirp): should this interval be a gflag? + #TODO(sirp): make this non-blocking + time.sleep(5) + scan_sr(session, sr_ref) + return get_vhd_parent_uuid(session, vdi_ref) + + parent_uuid = _get_vhd_parent_uuid_with_refresh(first_time=True) + logging.debug( + "Parent %s doesn't match original parent %s, " + "waiting for coalesce...", parent_uuid, original_parent_uuid) + while original_parent_uuid and (parent_uuid != original_parent_uuid): + logging.debug( + "Parent %s doesn't match original parent %s, " + "waiting for coalesce...", parent_uuid, original_parent_uuid) + parent_uuid = _get_vhd_parent_uuid_with_refresh(first_time=False) + + return parent_uuid + diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 50b5ec3d6..988c54d6d 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -81,6 +81,7 @@ class VMOps(object): def snapshot(self, instance): """ Create snapshot from a running VM instance """ + logging.debug("Starting snapshot for VM %s", instance) #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added vm_ref = VMHelper.lookup(self._session, instance.name) @@ -104,6 +105,8 @@ class VMOps(object): finally: self._destroy(template_vm_ref, shutdown=False) + logging.debug("Finished snapshot and upload for VM %s", instance) + def reboot(self, instance): """ Reboot VM instance """ instance_name = instance.name -- cgit From 650a0cdbc854d37fd62348ce34a14ef91ccbabad Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 17 Dec 2010 19:17:39 -0600 Subject: Cleaned up TODOs, using flags now --- nova/virt/xenapi/vm_utils.py | 50 +++++++++++++++++++++++--------------------- nova/virt/xenapi/vmops.py | 15 ++++++------- nova/virt/xenapi_conn.py | 9 +++++--- 3 files changed, 38 insertions(+), 36 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 1b18b0a01..729f0daaf 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -19,11 +19,12 @@ Helper methods for operations related to the management of VM records and their attributes like VDIs, VIFs, as well as their lookup functions. """ -import time #FIXME(sirp): take this out, replace with greenthread.sleep import logging +import pickle import urllib from xml.dom import minidom +from eventlet import event from nova import flags from nova import utils from nova.auth.manager import AuthManager @@ -166,8 +167,6 @@ class VMHelper(): vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) vdi_uuid = vdi_rec["uuid"] - #NOTE(sirp): We may need to wait for our parent to coalese with his - # parent original_parent_uuid = get_vhd_parent_uuid(session, vdi_ref) task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) @@ -186,8 +185,14 @@ class VMHelper(): def upload_image(cls, session, vdi_uuids, glance_label): logging.debug("Asking xapi to upload %s as '%s'", vdi_uuids, glance_label) - kwargs = {'vdi_uuids': ','.join(vdi_uuids), - 'glance_label': glance_label} + + params = {'vdi_uuids': vdi_uuids, + 'glance_label': glance_label, + 'glance_storage_location': FLAGS.glance_storage_location, + 'glance_host': FLAGS.glance_host, + 'glance_port': FLAGS.glance_port} + + kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'put_vdis', kwargs) session.wait_for_task(task) @@ -341,27 +346,24 @@ def scan_sr(session, sr_ref): def wait_for_vhd_coalesce(session, sr_ref, vdi_ref, original_parent_uuid): """ TODO Explain why coalescing has to occur here """ - #TODO(sirp): we need to timeout this req after a while #NOTE(sirp): for some reason re-scan wasn't occuring automatically on # XS5.6 - #TODO(sirp): clean this up, perhaps use LoopingCall - def _get_vhd_parent_uuid_with_refresh(first_time): - if not first_time: - #TODO(sirp): should this interval be a gflag? - #TODO(sirp): make this non-blocking - time.sleep(5) - scan_sr(session, sr_ref) - return get_vhd_parent_uuid(session, vdi_ref) - - parent_uuid = _get_vhd_parent_uuid_with_refresh(first_time=True) - logging.debug( - "Parent %s doesn't match original parent %s, " - "waiting for coalesce...", parent_uuid, original_parent_uuid) - while original_parent_uuid and (parent_uuid != original_parent_uuid): - logging.debug( - "Parent %s doesn't match original parent %s, " - "waiting for coalesce...", parent_uuid, original_parent_uuid) - parent_uuid = _get_vhd_parent_uuid_with_refresh(first_time=False) + #TODO(sirp): we need to timeout this req after a while + def _poll_vhds(): + scan_sr(session, sr_ref) + parent_uuid = get_vhd_parent_uuid(session, vdi_ref) + if original_parent_uuid and (parent_uuid != original_parent_uuid): + logging.debug( + "Parent %s doesn't match original parent %s, " + "waiting for coalesce...", parent_uuid, original_parent_uuid) + else: + done.send(parent_uuid) + + done = event.Event() + loop = utils.LoopingCall(_poll_vhds) + loop.start(FLAGS.xenapi_vhd_coalesce_poll_interval, now=True) + parent_uuid = done.wait() + loop.stop() return parent_uuid diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 988c54d6d..a44492d57 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -79,29 +79,26 @@ class VMOps(object): logging.info('Spawning VM %s created %s.', instance.name, vm_ref) - def snapshot(self, instance): + def snapshot(self, instance, name): """ Create snapshot from a running VM instance """ - logging.debug("Starting snapshot for VM %s", instance) + #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added + + logging.debug("Starting snapshot for VM %s", instance) vm_ref = VMHelper.lookup(self._session, instance.name) - #TODO(sirp): this is the label in Xen, we need to add a human friendly - # label that we store in paralalx label = "%s-snapshot" % instance.name - glance_name = "MySnapshot" - try: template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( self._session, vm_ref, label) except XenAPI.Failure, exc: logging.error("Unable to Snapshot %s: %s", vm_ref, exc) return - + try: # call plugin to ship snapshot off to glance - VMHelper.upload_image( - self._session, template_vdi_uuids, glance_name) + VMHelper.upload_image(self._session, template_vdi_uuids, name) finally: self._destroy(template_vm_ref, shutdown=False) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 8aacec507..92e66d32d 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -77,7 +77,10 @@ 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_float('xenapi_vhd_coalesce_poll_interval', + 5.0, + 'The interval used for polling of coalescing vhds.' + ' Used only if connection_type=xenapi.') XenAPI = None @@ -116,11 +119,11 @@ class XenAPIConnection(object): self._vmops.spawn(instance) - def snapshot(self, instance): + def snapshot(self, instance, name): """ Create snapshot from a running VM instance """ #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added - self._vmops.snapshot(instance) + self._vmops.snapshot(instance, name) def reboot(self, instance): """ Reboot VM instance """ -- cgit From af4d6e84c67b8f59f63ef0275778fa897dac9e95 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 22 Dec 2010 13:01:33 -0600 Subject: Getting Snapshots to work with cloudservers command-line tool --- nova/virt/xenapi/vm_utils.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index a4501cdbb..7a93c44c7 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -183,13 +183,11 @@ class VMHelper(): return template_vm_ref, [vdi_uuid, parent_uuid] @classmethod - def upload_image(cls, session, vdi_uuids, glance_label): - logging.debug("Asking xapi to upload %s as '%s'", vdi_uuids, - glance_label) + def upload_image(cls, session, vdi_uuids, image_name): + logging.debug("Asking xapi to upload %s as '%s'", vdi_uuids, image_name) params = {'vdi_uuids': vdi_uuids, - 'glance_label': glance_label, - 'glance_storage_location': FLAGS.glance_storage_location, + 'image_name': image_name, 'glance_host': FLAGS.glance_host, 'glance_port': FLAGS.glance_port} -- cgit From 7c03b9aa49b390e13cfbe8315a62c660778ef854 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 22 Dec 2010 14:00:31 -0600 Subject: i18n support for xs-snaps --- nova/virt/libvirt_conn.py | 4 ++-- nova/virt/xenapi/vm_utils.py | 50 +++++++++++++++++++++++++------------------- nova/virt/xenapi/vmops.py | 26 +++++++++++++---------- nova/virt/xenapi_conn.py | 2 -- 4 files changed, 46 insertions(+), 36 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 3b5954b3b..c8d0aa306 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -239,9 +239,9 @@ class LibvirtConnection(object): @exception.wrap_exception def snapshot(self, instance): """ Create snapshot from a running VM instance """ - #TODO(sirp): only exists for XenAPI driver for now raise NotImplementedError( - "Instance snapshotting is not supported for libvirt at this time") + _("Instance snapshotting is not supported for libvirt" + "at this time")) @exception.wrap_exception def reboot(self, instance): diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index f4e608e4d..5b1c36418 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -153,17 +153,22 @@ class VMHelper(): @classmethod - def create_snapshot(cls, session, vm_ref, label): - logging.debug("Snapshotting VM %s with label '%s'...", vm_ref, label) + def create_snapshot(cls, session, instance_id, vm_ref, label): + logging.debug(_("Snapshotting VM %s with label '%s'..."), vm_ref, label) #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added #TODO(sirp): Make safe_lookup_vdi for assert? vdi_refs = VMHelper.lookup_vm_vdis(session, vm_ref) - if vdi_refs is None or len(vdi_refs) != 1: - raise Exception("Unexpected number of VDIs (%s) found for VM %s" - % (len(vdi_refs), vm_ref)) + if vdi_refs is None: + raise Exception(_("No VDIs found for VM %s") % vm_ref) + else: + num_vdis = len(vdi_refs) + if num_vdis != 1: + raise Exception(_("Unexpected number of VDIs (%s) found for " + "VM %s") % (num_vdis, vm_ref)) + vdi_ref = vdi_refs[0] vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) vdi_uuid = vdi_rec["uuid"] @@ -171,20 +176,21 @@ class VMHelper(): original_parent_uuid = get_vhd_parent_uuid(session, vdi_ref) task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) - template_vm_ref = session.wait_for_task(task) - logging.debug('Created snapshot %s from VM %s.', template_vm_ref, + template_vm_ref = session.wait_for_task(instance_id, task) + logging.debug(_('Created snapshot %s from VM %s.'), template_vm_ref, vm_ref) sr_ref = vdi_rec["SR"] parent_uuid = wait_for_vhd_coalesce( - session, sr_ref, vdi_ref, original_parent_uuid) + session, instance_id, sr_ref, vdi_ref, original_parent_uuid) #TODO(sirp): we need to assert only one parent, not parents two deep return template_vm_ref, [vdi_uuid, parent_uuid] @classmethod - def upload_image(cls, session, vdi_uuids, image_name): - logging.debug("Asking xapi to upload %s as '%s'", vdi_uuids, image_name) + def upload_image(cls, session, instance_id, vdi_uuids, image_name): + logging.debug(_("Asking xapi to upload %s as '%s'"), + vdi_uuids, image_name) params = {'vdi_uuids': vdi_uuids, 'image_name': image_name, @@ -193,11 +199,11 @@ class VMHelper(): kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'put_vdis', kwargs) - session.wait_for_task(task) + session.wait_for_task(instance_id, task) @classmethod - def fetch_image(cls, session, image, user, project, use_sr): + def fetch_image(cls, session, instance_id, image, user, project, use_sr): """use_sr: True to put the image as a VDI in an SR, False to place it on dom0's filesystem. The former is for VM disks, the latter for its kernel and ramdisk (if external kernels are being used). @@ -214,7 +220,7 @@ class VMHelper(): if use_sr: args['add_partition'] = 'true' task = session.async_call_plugin('objectstore', fn, args) - uuid = session.wait_for_task(task) + uuid = session.wait_for_task(instance_id, task) return uuid @classmethod @@ -317,7 +323,7 @@ def get_vhd_parent(session, vdi_rec): parent_ref = session.get_xenapi().VDI.get_by_uuid(parent_uuid) parent_rec = session.get_xenapi().VDI.get_record(parent_ref) #NOTE(sirp): changed log -> logging - logging.debug("VHD %s has parent %s", vdi_rec['uuid'], parent_ref) + logging.debug(_("VHD %s has parent %s"), vdi_rec['uuid'], parent_ref) return parent_ref, parent_rec else: return None @@ -333,25 +339,27 @@ def get_vhd_parent_uuid(session, vdi_ref): return None -def scan_sr(session, sr_ref): - logging.debug("Re-scanning SR %s", sr_ref) +def scan_sr(session, instance_id, sr_ref): + logging.debug(_("Re-scanning SR %s"), sr_ref) task = session.call_xenapi('Async.SR.scan', sr_ref) - session.wait_for_task(task) + session.wait_for_task(instance_id, task) -def wait_for_vhd_coalesce(session, sr_ref, vdi_ref, original_parent_uuid): +def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, + original_parent_uuid): """ TODO Explain why coalescing has to occur here """ #NOTE(sirp): for some reason re-scan wasn't occuring automatically on # XS5.6 #TODO(sirp): we need to timeout this req after a while def _poll_vhds(): - scan_sr(session, sr_ref) + scan_sr(session, instance_id, sr_ref) parent_uuid = get_vhd_parent_uuid(session, vdi_ref) if original_parent_uuid and (parent_uuid != original_parent_uuid): logging.debug( - "Parent %s doesn't match original parent %s, " - "waiting for coalesce...", parent_uuid, original_parent_uuid) + _("Parent %s doesn't match original parent %s, " + "waiting for coalesce..."), + parent_uuid, original_parent_uuid) else: done.send(parent_uuid) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index d011f4489..787f959a5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -67,11 +67,14 @@ class VMOps(object): user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) vdi_uuid = VMHelper.fetch_image( - self._session, instance.image_id, user, project, True) + self._session, instance.id, instance.image_id, user, project, + True) kernel = VMHelper.fetch_image( - self._session, instance.kernel_id, user, project, False) + self._session, instance.id, instance.kernel_id, user, project, + False) ramdisk = VMHelper.fetch_image( - self._session, instance.ramdisk_id, user, project, False) + self._session, instance.id, instance.ramdisk_id, user, project, + False) vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) vm_ref = VMHelper.create_vm( self._session, instance, kernel, ramdisk) @@ -90,24 +93,25 @@ class VMOps(object): #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added - logging.debug("Starting snapshot for VM %s", instance) + logging.debug(_("Starting snapshot for VM %s"), instance) vm_ref = VMHelper.lookup(self._session, instance.name) label = "%s-snapshot" % instance.name try: template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( - self._session, vm_ref, label) + self._session, instance.id, vm_ref, label) except XenAPI.Failure, exc: - logging.error("Unable to Snapshot %s: %s", vm_ref, exc) + logging.error(_("Unable to Snapshot %s: %s"), vm_ref, exc) return try: # call plugin to ship snapshot off to glance - VMHelper.upload_image(self._session, template_vdi_uuids, name) + VMHelper.upload_image( + self._session, instance.id, template_vdi_uuids, name) finally: - self._destroy(template_vm_ref, shutdown=False) + self._destroy(instance, template_vm_ref, shutdown=False) - logging.debug("Finished snapshot and upload for VM %s", instance) + logging.debug(_("Finished snapshot and upload for VM %s"), instance) def reboot(self, instance): """Reboot VM instance""" @@ -121,9 +125,9 @@ class VMOps(object): def destroy(self, instance): """Destroy VM instance""" vm = VMHelper.lookup(self._session, instance.name) - return self._destroy(vm, shutdown=True) + return self._destroy(instance, vm, shutdown=True) - def _destroy(self, vm, shutdown=True): + def _destroy(self, instance, vm, shutdown=True): """ Destroy VM instance """ if vm is None: # Don't complain, just return. This lets us clean up instances diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index e6bc80d6e..58ad6ca6a 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -124,8 +124,6 @@ class XenAPIConnection(object): def snapshot(self, instance, name): """ Create snapshot from a running VM instance """ - #TODO(sirp): Add quiesce and VSS locking support when Windows support - # is added self._vmops.snapshot(instance, name) def reboot(self, instance): -- cgit From f31395c30c835201372802e9cdf9293dcbabdb5c Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 22 Dec 2010 14:54:02 -0600 Subject: Adding more comments regarding XS snapshots --- nova/virt/fake.py | 16 ++++++++++++++++ nova/virt/libvirt_conn.py | 2 +- nova/virt/xenapi/vm_utils.py | 20 +++++++++++++++++--- nova/virt/xenapi/vmops.py | 22 +++++++++++++++++++++- nova/virt/xenapi_conn.py | 1 - 5 files changed, 55 insertions(+), 6 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 73e273edd..c6db66aa6 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -106,6 +106,22 @@ class FakeConnection(object): self.instances[instance.name] = fake_instance fake_instance._state = power_state.RUNNING + + def snapshot(self, instance, name): + """ + Snapshots the specified instance. + + The given parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. + + The second parameter is the name of the snapshot. + + The work will be done asynchronously. This function returns a + Deferred that allows the caller to detect when it is complete. + """ + pass + + def reboot(self, instance): """ Reboot the specified instance. diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c8d0aa306..ad1a35643 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -237,7 +237,7 @@ class LibvirtConnection(object): virt_dom.detachDevice(xml) @exception.wrap_exception - def snapshot(self, instance): + def snapshot(self, instance, name): """ Create snapshot from a running VM instance """ raise NotImplementedError( _("Instance snapshotting is not supported for libvirt" diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 5b1c36418..b7e20121c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -154,6 +154,9 @@ class VMHelper(): @classmethod def create_snapshot(cls, session, instance_id, vm_ref, label): + """ Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, + Snapshot VHD + """ logging.debug(_("Snapshotting VM %s with label '%s'..."), vm_ref, label) #TODO(sirp): Add quiesce and VSS locking support when Windows support @@ -189,6 +192,9 @@ class VMHelper(): @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_name): + """ Requests that the Glance plugin bundle the specified VDIs and + push them into Glance using the specified human-friendly name. + """ logging.debug(_("Asking xapi to upload %s as '%s'"), vdi_uuids, image_name) @@ -347,9 +353,17 @@ def scan_sr(session, instance_id, sr_ref): def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, original_parent_uuid): - """ TODO Explain why coalescing has to occur here """ - #NOTE(sirp): for some reason re-scan wasn't occuring automatically on - # XS5.6 + """ Spin until the parent VHD is coalesced into its parent VHD + + Before coalesce: + * original_parent_vhd + * parent_vhd + snapshot + + Atter coalesce: + * parent_vhd + snapshot + """ #TODO(sirp): we need to timeout this req after a while def _poll_vhds(): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 787f959a5..dd6243dd5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -88,7 +88,27 @@ class VMOps(object): vm_ref) def snapshot(self, instance, name): - """ Create snapshot from a running VM instance """ + """ Create snapshot from a running VM instance + + :param instance: instance to be snapshotted + :param name: name/label to be given to the snapshot + + Steps involved in a XenServer snapshot: + + 1. XAPI-Snapshot: Snapshotting the instance using XenAPI. This + creates: Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, + Snapshot VHD + + 2. Wait-for-coalesce: The Snapshot VDI and Instance VDI both point to + a 'base-copy' VDI. The base_copy is immutable and may be chained + with other base_copies. If chained, the base_copies + coalesce together, so, we must wait for this coalescing to occur to + get a stable representation of the data on disk. + + 3. Push-to-glance: Once coalesced, we call a plugin on the XenServer + that will bundle the VHDs together and then push the bundle into + Glance. + """ #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 58ad6ca6a..816b92a0f 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -121,7 +121,6 @@ class XenAPIConnection(object): """Create VM instance""" self._vmops.spawn(instance) - def snapshot(self, instance, name): """ Create snapshot from a running VM instance """ self._vmops.snapshot(instance, name) -- cgit From 749af384c0b7ca36bdd8c511f02b819a65e5dae0 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 22 Dec 2010 17:09:46 -0600 Subject: Modified InstanceDiagnostics and truncate action --- nova/virt/xenapi_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 146e2f153..7870a21ac 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -204,7 +204,7 @@ class XenAPISession(object): status = self._session.xenapi.task.get_status(task) action = dict( instance_id=int(instance_id), - action=name, + action=name[0:255], # Ensure action is never > 255 error=None) if status == "pending": return -- cgit From 358961f3cf259487a2ff9bbb225defdc7cd9e7a7 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 23 Dec 2010 11:22:51 -0600 Subject: Fixed trunk and PEP8 cleanup --- nova/virt/libvirt_conn.py | 16 +++++++++------- nova/virt/xenapi_conn.py | 4 ++++ 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 651b2af93..809a484dc 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -512,9 +512,11 @@ class LibvirtConnection(object): if FLAGS.allow_project_net_traffic: net, mask = _get_net_and_mask(network['cidr']) - extra_params = ("\n" - "\n" - ) % (net, mask) + extra_params = ( + "\n" + "\n") % ( + net, + mask) else: extra_params = "\n" @@ -800,8 +802,8 @@ class NWFilterFirewall(object): the base filter are all in place. """ - nwfilter_xml = ("\n" - ) % instance['name'] + nwfilter_xml = ("\n") % ( + instance['name']) if instance['image_id'] == FLAGS.vpn_image_id: nwfilter_xml += " \n" @@ -814,8 +816,8 @@ class NWFilterFirewall(object): for security_group in instance.security_groups: self.ensure_security_group_filter(security_group['id']) - nwfilter_xml += (" \n" - ) % security_group['id'] + nwfilter_xml += (" \n") % ( + security_group['id']) nwfilter_xml += "" self._define_filter(nwfilter_xml) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 7870a21ac..af16fcdf3 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -110,6 +110,10 @@ class XenAPIConnection(object): self._vmops = VMOps(session) self._volumeops = VolumeOps(session) + def init_host(self): + """Initialize anything that is necessary for the driver to function""" + return + def list_instances(self): """List VM instances""" return self._vmops.list_instances() -- cgit From 55a80811a5982cb9af5b80e7ac3e925334a1b22d Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 23 Dec 2010 12:23:28 -0600 Subject: Working diagnostics API; removed diagnostics DB model - not needed --- nova/virt/xenapi/vmops.py | 6 +++--- nova/virt/xenapi_conn.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 4d897af35..50e20bff4 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -156,11 +156,11 @@ class VMOps(object): rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_info(rec) - def get_diagnostics(self, instance_id): + def get_diagnostics(self, instance): """Return data about VM diagnostics""" - vm = VMHelper.lookup(self._session, instance_id) + vm = VMHelper.lookup(self._session, instance.name) if vm is None: - raise Exception("instance not present %s" % instance_id) + raise Exception("instance not present %s" % instance.name) rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_diagnostics(self._session, rec) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index af16fcdf3..5c746a08f 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -142,9 +142,9 @@ class XenAPIConnection(object): """Return data about VM instance""" return self._vmops.get_info(instance_id) - def get_diagnostics(self, instance_id): + def get_diagnostics(self, instance): """Return data about VM diagnostics""" - return self._vmops.get_diagnostics(instance_id) + return self._vmops.get_diagnostics(instance) def get_console_output(self, instance): """Return snapshot of console""" -- cgit From 13e8c8d83b8fc44cff343ea751a98f66857d1865 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 23 Dec 2010 13:14:56 -0600 Subject: Resolved merge conflict --- nova/virt/xenapi_conn.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 4ea8a6737..2bf29b295 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -225,13 +225,8 @@ class XenAPISession(object): name = self._session.xenapi.task.get_name_label(task) status = self._session.xenapi.task.get_status(task) action = dict( -<<<<<<< TREE - instance_id=int(instance_id), + instance_id=int(id), action=name[0:255], # Ensure action is never > 255 -======= - id=int(id), - action=name, ->>>>>>> MERGE-SOURCE error=None) if status == "pending": return -- cgit From bd46ab4a721da856da5743c9f55ab5e50ec9b60f Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 23 Dec 2010 13:36:01 -0600 Subject: PEP8 fix --- nova/virt/xenapi/vmops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 5039913ba..7571fe2e4 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -186,7 +186,8 @@ class VMOps(object): """Return data about VM diagnostics""" vm = VMHelper.lookup(self._session, instance.name) if vm is None: - raise exception.NotFound(_("Instance not found %s") % instance.name) + raise exception.NotFound(_("Instance not found %s") % + instance.name) rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_diagnostics(self._session, rec) -- cgit From 748aa8089eabfd15425199c2318079e9bf84578f Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Mon, 27 Dec 2010 14:23:53 -0600 Subject: Fixing bad merge --- nova/virt/xenapi/vmops.py | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a0497b3e2..23531348e 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -96,7 +96,30 @@ class VMOps(object): self._session.call_xenapi('VM.start', vm_ref, False, False) logging.info(_('Spawning VM %s created %s.'), instance.name, vm_ref) - + + # NOTE(armando): Do we really need to do this in virt? + timer = utils.LoopingCall(f=None) + + def _wait_for_boot(): + try: + state = self.get_info(instance['name'])['state'] + db.instance_set_state(context.get_admin_context(), + instance['id'], state) + if state == power_state.RUNNING: + logging.debug(_('Instance %s: booted'), instance['name']) + timer.stop() + except Exception, exc: + logging.warn(exc) + logging.exception(_('instance %s: failed to boot'), + instance['name']) + db.instance_set_state(context.get_admin_context(), + instance['id'], + power_state.SHUTDOWN) + timer.stop() + + timer.f = _wait_for_boot + return timer.start(interval=0.5, now=True) + def snapshot(self, instance, name): """ Create snapshot from a running VM instance @@ -143,29 +166,6 @@ class VMOps(object): logging.debug(_("Finished snapshot and upload for VM %s"), instance) - # NOTE(armando): Do we really need to do this in virt? - timer = utils.LoopingCall(f=None) - - def _wait_for_boot(): - try: - state = self.get_info(instance['name'])['state'] - db.instance_set_state(context.get_admin_context(), - instance['id'], state) - if state == power_state.RUNNING: - logging.debug(_('Instance %s: booted'), instance['name']) - timer.stop() - except Exception, exc: - logging.warn(exc) - logging.exception(_('instance %s: failed to boot'), - instance['name']) - db.instance_set_state(context.get_admin_context(), - instance['id'], - power_state.SHUTDOWN) - timer.stop() - - timer.f = _wait_for_boot - return timer.start(interval=0.5, now=True) - def reboot(self, instance): """Reboot VM instance""" instance_name = instance.name -- cgit From 8d522838ace090a7325d930df08c37f1e9d9803e Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Mon, 27 Dec 2010 15:42:30 -0600 Subject: Fixing snapshots, pep8 fixes --- nova/virt/fake.py | 2 -- nova/virt/xenapi/vm_utils.py | 58 ++++++++++++++++++++++++-------------------- nova/virt/xenapi/vmops.py | 16 ++++++------ nova/virt/xenapi_conn.py | 2 +- 4 files changed, 41 insertions(+), 37 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 6d3cef268..0f456aef6 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -112,7 +112,6 @@ class FakeConnection(object): self.instances[instance.name] = fake_instance fake_instance._state = power_state.RUNNING - def snapshot(self, instance, name): """ Snapshots the specified instance. @@ -127,7 +126,6 @@ class FakeConnection(object): """ pass - def reboot(self, instance): """ Reboot the specified instance. diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 2b76afc62..f4195cb1b 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -27,6 +27,7 @@ from xml.dom import minidom from eventlet import event from nova import exception from nova import flags +from nova import utils from nova.auth.manager import AuthManager from nova.compute import instance_types from nova.compute import power_state @@ -205,44 +206,34 @@ class VMHelper(HelperBase): vm_ref, network_ref) return vif_ref - @classmethod def create_snapshot(cls, session, instance_id, vm_ref, label): """ Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, Snapshot VHD """ - logging.debug(_("Snapshotting VM %s with label '%s'..."), vm_ref, label) - + logging.debug(_("Snapshotting VM %s with label '%s'..."), + vm_ref, label) #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added - - #TODO(sirp): Make safe_lookup_vdi for assert? - vdi_refs = VMHelper.lookup_vm_vdis(session, vm_ref) - if vdi_refs is None: - raise Exception(_("No VDIs found for VM %s") % vm_ref) - else: - num_vdis = len(vdi_refs) - if num_vdis != 1: - raise Exception(_("Unexpected number of VDIs (%s) found for " - "VM %s") % (num_vdis, vm_ref)) - - vdi_ref = vdi_refs[0] - vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) - vdi_uuid = vdi_rec["uuid"] - - original_parent_uuid = get_vhd_parent_uuid(session, vdi_ref) + vm_vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vm_vdi_uuid = vm_vdi_rec["uuid"] + original_parent_uuid = get_vhd_parent_uuid(session, vm_vdi_ref) task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) template_vm_ref = session.wait_for_task(instance_id, task) + template_vdi_ref, template_vdi_rec = get_vdi_for_vm_safely( + session, template_vm_ref) + + template_vdi_uuid = template_vdi_rec["uuid"] logging.debug(_('Created snapshot %s from VM %s.'), template_vm_ref, vm_ref) - sr_ref = vdi_rec["SR"] + sr_ref = vm_vdi_rec["SR"] parent_uuid = wait_for_vhd_coalesce( - session, instance_id, sr_ref, vdi_ref, original_parent_uuid) + 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, [vdi_uuid, parent_uuid] + return template_vm_ref, [template_vdi_uuid, parent_uuid] @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_name): @@ -252,7 +243,7 @@ class VMHelper(HelperBase): logging.debug(_("Asking xapi to upload %s as '%s'"), vdi_uuids, image_name) - params = {'vdi_uuids': vdi_uuids, + params = {'vdi_uuids': vdi_uuids, 'image_name': image_name, 'glance_host': FLAGS.glance_host, 'glance_port': FLAGS.glance_port} @@ -261,7 +252,6 @@ class VMHelper(HelperBase): task = session.async_call_plugin('glance', 'put_vdis', kwargs) session.wait_for_task(instance_id, task) - @classmethod def fetch_image(cls, session, instance_id, image, user, project, type): """ @@ -418,7 +408,7 @@ def scan_sr(session, instance_id, sr_ref): def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, original_parent_uuid): """ Spin until the parent VHD is coalesced into its parent VHD - + Before coalesce: * original_parent_vhd * parent_vhd @@ -440,7 +430,7 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, parent_uuid, original_parent_uuid) else: done.send(parent_uuid) - + done = event.Event() loop = utils.LoopingCall(_poll_vhds) loop.start(FLAGS.xenapi_vhd_coalesce_poll_interval, now=True) @@ -448,3 +438,19 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, loop.stop() return parent_uuid + +def get_vdi_for_vm_safely(session, vm_ref): + """Returns (vdi_ref, vdi_uuid)""" + #TODO(sirp): Make safe_lookup_vdi for assert? + vdi_refs = VMHelper.lookup_vm_vdis(session, vm_ref) + if vdi_refs is None: + raise Exception(_("No VDIs found for VM %s") % vm_ref) + else: + num_vdis = len(vdi_refs) + if num_vdis != 1: + raise Exception(_("Unexpected number of VDIs (%s) found for " + "VM %s") % (num_vdis, vm_ref)) + + vdi_ref = vdi_refs[0] + vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) + return vdi_ref, vdi_rec diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 23531348e..2cc36ad54 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -122,7 +122,7 @@ class VMOps(object): def snapshot(self, instance, name): """ Create snapshot from a running VM instance - + :param instance: instance to be snapshotted :param name: name/label to be given to the snapshot @@ -131,7 +131,7 @@ class VMOps(object): 1. XAPI-Snapshot: Snapshotting the instance using XenAPI. This creates: Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, Snapshot VHD - + 2. Wait-for-coalesce: The Snapshot VDI and Instance VDI both point to a 'base-copy' VDI. The base_copy is immutable and may be chained with other base_copies. If chained, the base_copies @@ -153,14 +153,14 @@ class VMOps(object): try: template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.error(_("Unable to Snapshot %s: %s"), vm_ref, exc) return - + try: # call plugin to ship snapshot off to glance VMHelper.upload_image( - self._session, instance.id, template_vdi_uuids, name) + self._session, instance.id, template_vdi_uuids, name) finally: self._destroy(instance, template_vm_ref, shutdown=False) @@ -193,7 +193,7 @@ class VMOps(object): try: task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) self._session.wait_for_task(instance.id, task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) # Disk clean-up @@ -202,13 +202,13 @@ class VMOps(object): try: task = self._session.call_xenapi('Async.VDI.destroy', vdi) self._session.wait_for_task(instance.id, task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) # VM Destroy try: task = self._session.call_xenapi('Async.VM.destroy', vm) self._session.wait_for_task(instance.id, task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) def _wait_with_callback(self, instance_id, task, callback): diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 1a3dc9f16..abd2a02ee 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -233,7 +233,7 @@ class XenAPISession(object): name = self._session.xenapi.task.get_name_label(task) status = self._session.xenapi.task.get_status(task) action = dict( - id=int(id), + instance_id=int(id), action=name, error=None) if status == "pending": -- cgit From 7ddd833bc61252061b3dfd2449765a93f750bffa Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 28 Dec 2010 12:19:25 -0600 Subject: Added get_diagnostics placeholders to libvirt and fake --- nova/virt/fake.py | 3 +++ nova/virt/libvirt_conn.py | 3 +++ 2 files changed, 6 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 706888b0d..880772b22 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -202,6 +202,9 @@ class FakeConnection(object): 'num_cpu': 2, 'cpu_time': 0} + def get_diagnostics(self, instance_name): + pass + def list_disks(self, instance_name): """ Return the IDs of all the virtual disks attached to the specified diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 65cf65098..06ace7457 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -567,6 +567,9 @@ class LibvirtConnection(object): 'num_cpu': num_cpu, 'cpu_time': cpu_time} + def get_diagnostics(self, instance_name): + raise exception.APIError("diagnostics not supported for libvirt") + def get_disks(self, instance_name): """ Note that this function takes an instance name, not an Instance, so -- cgit From 96384b689953e381f2210d4a78f1b5239a78e507 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 28 Dec 2010 12:53:32 -0600 Subject: Add some basic snapshot tests --- nova/virt/xenapi/fake.py | 64 +++++++++++++++++++++++++++++++++++++++++--- nova/virt/xenapi/vm_utils.py | 15 +++++------ 2 files changed, 67 insertions(+), 12 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 1eaf31c25..aa4026f97 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -55,6 +55,8 @@ import datetime import logging import uuid +from pprint import pformat + from nova import exception @@ -64,6 +66,10 @@ _CLASSES = ['host', 'network', 'session', 'SR', 'VBD',\ _db_content = {} +def log_db_contents(msg=None): + logging.debug(_("%s: _db_content => %s"), msg or "", pformat(_db_content)) + + def reset(): for c in _CLASSES: _db_content[c] = {} @@ -93,6 +99,24 @@ def create_vm(name_label, status, }) +def destroy_vm(vm_ref): + vm_rec = _db_content['VM'][vm_ref] + + vbd_refs = vm_rec['VBDs'] + for vbd_ref in vbd_refs: + destroy_vbd(vbd_ref) + + del _db_content['VM'][vm_ref] + + +def destroy_vbd(vbd_ref): + del _db_content['VBD'][vbd_ref] + + +def destroy_vdi(vdi_ref): + del _db_content['VDI'][vdi_ref] + + def create_vdi(name_label, read_only, sr_ref, sharable): return _create_object('VDI', { 'name_label': name_label, @@ -109,6 +133,23 @@ 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_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""" + vm_ref = vbd_rec['VM'] + vm_rec = _db_content['VM'][vm_ref] + vm_rec['VBDs'] = [vbd_ref] + + vm_name_label = _db_content['VM'][vm_ref]['name_label'] + vbd_rec['vm_name_label'] = vm_name_label + + def create_pbd(config, sr_ref, attached): return _create_object('PBD', { 'device-config': config, @@ -277,11 +318,12 @@ class SessionBase(object): self._check_arg_count(params, 2) return get_record(cls, params[1]) - if (func == 'get_by_name_label' or - func == 'get_by_uuid'): + if func in ('get_by_name_label', 'get_by_uuid'): self._check_arg_count(params, 2) + return_singleton = (func == 'get_by_uuid') return self._get_by_field( - _db_content[cls], func[len('get_by_'):], params[1]) + _db_content[cls], func[len('get_by_'):], params[1], + return_singleton=return_singleton) if len(params) == 2: field = func[len('get_'):] @@ -324,6 +366,13 @@ class SessionBase(object): (cls, _) = name.split('.') ref = is_sr_create and \ _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 + obj = get_record(cls, ref) # Add RO fields @@ -359,11 +408,18 @@ class SessionBase(object): raise Failure(['MESSAGE_PARAMETER_COUNT_MISMATCH', expected, actual]) - def _get_by_field(self, recs, k, v): + def _get_by_field(self, recs, k, v, return_singleton): result = [] for ref, rec in recs.iteritems(): if rec.get(k) == v: result.append(ref) + + if return_singleton: + try: + return result[0] + except IndexError: + return None + return result diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index f4195cb1b..68da807fc 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -211,24 +211,25 @@ class VMHelper(HelperBase): """ Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, Snapshot VHD """ - logging.debug(_("Snapshotting VM %s with label '%s'..."), - vm_ref, label) #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added + logging.debug(_("Snapshotting VM %s with label '%s'..."), + vm_ref, label) + vm_vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) vm_vdi_uuid = vm_vdi_rec["uuid"] + sr_ref = vm_vdi_rec["SR"] + original_parent_uuid = get_vhd_parent_uuid(session, vm_vdi_ref) task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) template_vm_ref = session.wait_for_task(instance_id, task) - template_vdi_ref, template_vdi_rec = get_vdi_for_vm_safely( - session, template_vm_ref) - + template_vdi_rec = get_vdi_for_vm_safely(session, template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] + logging.debug(_('Created snapshot %s from VM %s.'), template_vm_ref, vm_ref) - sr_ref = vm_vdi_rec["SR"] parent_uuid = wait_for_vhd_coalesce( session, instance_id, sr_ref, vm_vdi_ref, original_parent_uuid) @@ -440,8 +441,6 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, def get_vdi_for_vm_safely(session, vm_ref): - """Returns (vdi_ref, vdi_uuid)""" - #TODO(sirp): Make safe_lookup_vdi for assert? vdi_refs = VMHelper.lookup_vm_vdis(session, vm_ref) if vdi_refs is None: raise Exception(_("No VDIs found for VM %s") % vm_ref) -- cgit From 7811a77753943ee87f3c3b10f37d22e61c5119d0 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 28 Dec 2010 14:32:45 -0600 Subject: Make action log available through Admin API --- nova/virt/fake.py | 3 +++ nova/virt/libvirt_conn.py | 5 ++++- nova/virt/xenapi/vmops.py | 8 ++++++++ nova/virt/xenapi_conn.py | 4 ++++ 4 files changed, 19 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 880772b22..e9d1e4bd7 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -205,6 +205,9 @@ class FakeConnection(object): def get_diagnostics(self, instance_name): pass + def get_actions(self, instance_name): + pass + def list_disks(self, instance_name): """ Return the IDs of all the virtual disks attached to the specified diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 06ace7457..9c8aa1fae 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -568,7 +568,10 @@ class LibvirtConnection(object): 'cpu_time': cpu_time} def get_diagnostics(self, instance_name): - raise exception.APIError("diagnostics not supported for libvirt") + raise exception.APIError("diagnostics are not supported for libvirt") + + def get_actions(self, instance_name): + raise exception.APIError("actions are not supported for libvirt") def get_disks(self, instance_name): """ diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 332e603db..93f7f7695 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -226,6 +226,14 @@ class VMOps(object): rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_diagnostics(self._session, rec) + def get_actions(self, instance): + """Return VM action history""" + vm = VMHelper.lookup(self._session, instance.name) + if vm is None: + raise exception.NotFound(_("Instance not found %s") % + instance.name) + return db.instance_get_actions(context.get_admin_context(), instance.id) + def get_console_output(self, instance): """Return snapshot of console""" # TODO: implement this to fix pylint! diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index a7ec22012..92274daf9 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -163,6 +163,10 @@ class XenAPIConnection(object): """Return data about VM diagnostics""" return self._vmops.get_diagnostics(instance) + def get_actions(self, instance): + """Return VM action history""" + return self._vmops.get_actions(instance) + def get_console_output(self, instance): """Return snapshot of console""" return self._vmops.get_console_output(instance) -- cgit From 34f5bed4d9c99af58c83b82b499f898c270124a8 Mon Sep 17 00:00:00 2001 From: Ryan Lucio Date: Wed, 29 Dec 2010 11:24:42 -0800 Subject: Re-added flag definition for injected_network_template. Tested & verified fix in the same env as the original bug. --- nova/virt/libvirt_conn.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f6a218fa4..39215c4e1 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -68,6 +68,9 @@ FLAGS = flags.FLAGS flags.DEFINE_string('rescue_image_id', 'ami-rescue', 'Rescue ami image') flags.DEFINE_string('rescue_kernel_id', 'aki-rescue', 'Rescue aki image') flags.DEFINE_string('rescue_ramdisk_id', 'ari-rescue', 'Rescue ari image') +flags.DEFINE_string('injected_network_template', + utils.abspath('virt/interfaces.template'), + 'Template file for injected network') flags.DEFINE_string('libvirt_xml_template', utils.abspath('virt/libvirt.xml.template'), 'Libvirt XML Template') -- cgit From 823c5fc1ff3c37acbfe9b30d7057f53b050b93c6 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 29 Dec 2010 14:02:57 -0600 Subject: Added tests --- nova/virt/xenapi/vm_utils.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 47fb6db53..cca7c1476 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -299,6 +299,10 @@ class VMHelper(HelperBase): try: host = session.get_xenapi_host() host_ip = session.get_xenapi().host.get_record(host)["address"] + except (cls.XenAPI.Failure, KeyError) as e: + return {"Unable to retrieve diagnostics": e} + + try: diags = {} xml = get_rrd(host_ip, record["uuid"]) if xml: -- cgit From 5b8137b2f50a4ed3eb105e38cefa280927f1c2ea Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 29 Dec 2010 14:15:04 -0600 Subject: PEP8 cleanup --- nova/virt/xenapi/vmops.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 93f7f7695..1dfb9005c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -232,7 +232,9 @@ class VMOps(object): if vm is None: raise exception.NotFound(_("Instance not found %s") % instance.name) - return db.instance_get_actions(context.get_admin_context(), instance.id) + return db.instance_get_actions( + context.get_admin_context(), + instance.id) def get_console_output(self, instance): """Return snapshot of console""" -- cgit From 42f6a993bcc4d0bc8823e4d039b1f59a6d6758a8 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 30 Dec 2010 15:13:32 -0600 Subject: Implemented review feedback --- nova/virt/fake.py | 3 --- nova/virt/libvirt_conn.py | 3 --- nova/virt/xenapi/vmops.py | 10 ---------- nova/virt/xenapi_conn.py | 4 ---- 4 files changed, 20 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index e9d1e4bd7..880772b22 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -205,9 +205,6 @@ class FakeConnection(object): def get_diagnostics(self, instance_name): pass - def get_actions(self, instance_name): - pass - def list_disks(self, instance_name): """ Return the IDs of all the virtual disks attached to the specified diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 8407938c6..cf45ac13b 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -583,9 +583,6 @@ class LibvirtConnection(object): def get_diagnostics(self, instance_name): raise exception.APIError("diagnostics are not supported for libvirt") - def get_actions(self, instance_name): - raise exception.APIError("actions are not supported for libvirt") - def get_disks(self, instance_name): """ Note that this function takes an instance name, not an Instance, so diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 1dfb9005c..332e603db 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -226,16 +226,6 @@ class VMOps(object): rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_diagnostics(self._session, rec) - def get_actions(self, instance): - """Return VM action history""" - vm = VMHelper.lookup(self._session, instance.name) - if vm is None: - raise exception.NotFound(_("Instance not found %s") % - instance.name) - return db.instance_get_actions( - context.get_admin_context(), - instance.id) - def get_console_output(self, instance): """Return snapshot of console""" # TODO: implement this to fix pylint! diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 92274daf9..a7ec22012 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -163,10 +163,6 @@ class XenAPIConnection(object): """Return data about VM diagnostics""" return self._vmops.get_diagnostics(instance) - def get_actions(self, instance): - """Return VM action history""" - return self._vmops.get_actions(instance) - def get_console_output(self, instance): """Return snapshot of console""" return self._vmops.get_console_output(instance) -- cgit