summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRick Harris <rconradharris@gmail.com>2011-09-16 18:06:27 +0000
committerRick Harris <rconradharris@gmail.com>2011-09-16 18:06:27 +0000
commit10f7128079942b14e7627fa34b93e2e0ae05058f (patch)
tree33235b129734f7aea78ad3e60b4b66bcc78e7d63
parent80462f3e446964560198e77af6903d84a05cab87 (diff)
Adding migration progress
-rw-r--r--nova/api/openstack/views/servers.py3
-rw-r--r--nova/compute/manager.py2
-rw-r--r--nova/tests/test_virt_drivers.py3
-rw-r--r--nova/tests/test_xenapi.py2
-rw-r--r--nova/virt/driver.py3
-rw-r--r--nova/virt/fake.py2
-rw-r--r--nova/virt/xenapi/vmops.py97
-rw-r--r--nova/virt/xenapi_conn.py4
8 files changed, 70 insertions, 46 deletions
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 6640be810..925668e56 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -139,7 +139,8 @@ class ViewBuilderV11(ViewBuilder):
response['server']['updated'] = utils.isotime(inst['updated_at'])
status = response['server'].get('status')
- if status in ('ACTIVE', 'BUILD', 'REBUILD'):
+ if status in ('ACTIVE', 'BUILD', 'REBUILD', 'RESIZE',
+ 'VERIFY_RESIZE'):
response['server']['progress'] = inst['progress'] or 0
response['server']['accessIPv4'] = inst.get('access_ip_v4') or ""
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index dc67252ca..d80fa6e70 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -977,7 +977,7 @@ class ComputeManager(manager.SchedulerDependentManager):
{'status': 'migrating'})
disk_info = self.driver.migrate_disk_and_power_off(
- instance_ref, migration_ref['dest_host'])
+ context, instance_ref, migration_ref['dest_host'])
self.db.migration_update(context,
migration_id,
{'status': 'post-migrating'})
diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py
index 440d3401b..244e4e803 100644
--- a/nova/tests/test_virt_drivers.py
+++ b/nova/tests/test_virt_drivers.py
@@ -181,7 +181,8 @@ class _VirtDriverTestCase(test.TestCase):
instance_ref = test_utils.get_test_instance()
network_info = test_utils.get_test_network_info()
self.connection.spawn(self.ctxt, instance_ref, network_info)
- self.connection.migrate_disk_and_power_off(instance_ref, 'dest_host')
+ self.connection.migrate_disk_and_power_off(
+ self.ctxt, instance_ref, 'dest_host')
@catch_notimplementederror
def test_pause(self):
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 6b5bb240c..a8a03b56b 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -763,7 +763,7 @@ class XenAPIMigrateInstance(test.TestCase):
instance = db.instance_create(self.context, self.instance_values)
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
conn = xenapi_conn.get_connection(False)
- conn.migrate_disk_and_power_off(instance, '127.0.0.1')
+ conn.migrate_disk_and_power_off(self.context, instance, '127.0.0.1')
def test_revert_migrate(self):
instance = db.instance_create(self.context, self.instance_values)
diff --git a/nova/virt/driver.py b/nova/virt/driver.py
index 8b9d3053f..d1055bf2d 100644
--- a/nova/virt/driver.py
+++ b/nova/virt/driver.py
@@ -225,12 +225,11 @@ class ComputeDriver(object):
"""
raise NotImplementedError()
- def migrate_disk_and_power_off(self, instance, dest):
+ def migrate_disk_and_power_off(self, context, instance, dest):
"""
Transfers the disk of a running instance in multiple phases, turning
off the instance before the end.
"""
- # TODO(Vek): Need to pass context in for access to auth_token
raise NotImplementedError()
def snapshot(self, context, instance, image_id):
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index 3596d8353..eca1bf27b 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -130,7 +130,7 @@ class FakeConnection(driver.ComputeDriver):
def poll_rescued_instances(self, timeout):
pass
- def migrate_disk_and_power_off(self, instance, dest):
+ def migrate_disk_and_power_off(self, context, instance, dest):
pass
def pause(self, instance, callback):
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index ca5ba9392..bf746a8b4 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -55,6 +55,9 @@ flags.DEFINE_string('xenapi_vif_driver',
'nova.virt.xenapi.vif.XenAPIBridgeDriver',
'The XenAPI VIF driver using XenServer Network APIs.')
+RESIZE_TOTAL_STEPS = 5
+BUILD_TOTAL_STEPS = 4
+
def cmp_version(a, b):
"""Compare two version strings (eg 0.0.1.10 > 0.0.1.9)"""
@@ -137,7 +140,13 @@ class VMOps(object):
network_info)
if resize_instance:
self.resize_instance(instance, vdi_uuid)
+
+ # 5. Start VM
self._start(instance, vm_ref=vm_ref)
+ self._update_instance_progress(context, instance,
+ step=5,
+ total_steps=RESIZE_TOTAL_STEPS)
+
def _start(self, instance, vm_ref=None):
"""Power on a VM instance"""
@@ -157,34 +166,7 @@ class VMOps(object):
disk_image_type)
return vdis
- def _update_instance_progress(self, context, instance, progress):
- if progress < 0:
- progress = 0
- elif progress > 100:
- progress = 100
- instance_id = instance['id']
- LOG.debug(_("Updating instance '%(instance_id)s' progress to"
- " %(progress)d") % locals())
- db.instance_update(context, instance_id, {'progress': progress})
-
def spawn(self, context, instance, network_info):
- total_steps = 4
- progress = {'value': 0}
-
- def bump_progress():
- # FIXME(sirp): for now we're taking a KISS approach to
- # instance-build-progress:
- # divide the action's workflow into discrete steps and "bump" the
- # instance's progress field as each step is completed.
- #
- # For a first cut this should be fine, however, as image size
- # grows, the _create_disks step begins to dominate the equation. A
- # better approximation would reflect the percentage of the image
- # that has been streamed to the host machine.
- progress['value'] += 100 / total_steps
- self._update_instance_progress(
- context, instance, progress['value'])
-
vdis = None
try:
# 1. Vanity Step
@@ -194,19 +176,28 @@ class VMOps(object):
# progress remaining at 0% for too long, which will appear to be
# an error, we insert a "vanity" step to bump the progress up one
# notch above 0.
- bump_progress()
+ self._update_instance_progress(context, instance,
+ step=1,
+ total_steps=BUILD_TOTAL_STEPS)
# 2. Fetch the Image over the Network
vdis = self._create_disks(context, instance)
- bump_progress()
+ self._update_instance_progress(context, instance,
+ step=2,
+ total_steps=BUILD_TOTAL_STEPS)
# 3. Create the VM records
vm_ref = self._create_vm(context, instance, vdis, network_info)
- bump_progress()
+ self._update_instance_progress(context, instance,
+ step=3,
+ total_steps=BUILD_TOTAL_STEPS)
# 4. Boot the Instance
self._spawn(instance, vm_ref)
- bump_progress()
+ self._update_instance_progress(context, instance,
+ step=4,
+ total_steps=BUILD_TOTAL_STEPS)
+
except (self.XenAPI.Failure, OSError, IOError) as spawn_error:
LOG.exception(_("instance %s: Failed to spawn"),
instance.id, exc_info=sys.exc_info())
@@ -214,8 +205,6 @@ class VMOps(object):
instance.id)
self._handle_spawn_error(vdis, spawn_error)
raise spawn_error
- else:
- self._update_instance_progress(context, instance, 100)
def spawn_rescue(self, context, instance, network_info):
"""Spawn a rescue instance."""
@@ -228,7 +217,7 @@ class VMOps(object):
if vm_ref is not None:
raise exception.InstanceExists(name=instance_name)
- #ensure enough free memory is available
+ # Ensure enough free memory is available
if not VMHelper.ensure_free_mem(self._session, instance):
LOG.exception(_('instance %(instance_name)s: not enough free '
'memory') % locals())
@@ -608,7 +597,25 @@ class VMOps(object):
def _get_orig_vm_name_label(self, instance):
return instance.name + '-orig'
- def migrate_disk_and_power_off(self, instance, dest):
+ def _update_instance_progress(self, context, instance, step, total_steps):
+ """Update instance progress percent to reflect current step number
+ """
+ # FIXME(sirp): for now we're taking a KISS approach to instance
+ # progress:
+ # Divide the action's workflow into discrete steps and "bump" the
+ # instance's progress field as each step is completed.
+ #
+ # For a first cut this should be fine, however, for large VM images,
+ # the _create_disks step begins to dominate the equation. A
+ # better approximation would use the percentage of the VM image that
+ # has been streamed to the destination host.
+ progress = round(float(step) / total_steps * 100)
+ instance_id = instance['id']
+ LOG.debug(_("Updating instance '%(instance_id)s' progress to"
+ " %(progress)d") % locals())
+ db.instance_update(context, instance_id, {'progress': progress})
+
+ def migrate_disk_and_power_off(self, context, instance, dest):
"""Copies a VHD from one host machine to another.
:param instance: the instance that owns the VHD in question.
@@ -625,8 +632,13 @@ class VMOps(object):
base_copy_uuid = cow_uuid = None
template_vdi_uuids = template_vm_ref = None
try:
+ # 1. Create Snapshot
template_vm_ref, template_vdi_uuids =\
self._create_snapshot(instance)
+ self._update_instance_progress(context, instance,
+ step=1,
+ total_steps=RESIZE_TOTAL_STEPS)
+
base_copy_uuid = template_vdi_uuids['image']
vdi_ref, vm_vdi_rec = \
VMHelper.get_vdi_for_vm_safely(self._session, vm_ref)
@@ -634,12 +646,23 @@ class VMOps(object):
sr_path = VMHelper.get_sr_path(self._session)
- # transfer the base copy
+ # 2. Transfer the base copy
self._migrate_vhd(instance, base_copy_uuid, dest, sr_path)
+ self._update_instance_progress(context, instance,
+ step=2,
+ total_steps=RESIZE_TOTAL_STEPS)
- # Now power down the instance and transfer the COW VHD
+ # 3. Now power down the instance
self._shutdown(instance, vm_ref, hard=False)
+ self._update_instance_progress(context, instance,
+ step=3,
+ total_steps=RESIZE_TOTAL_STEPS)
+
+ # 4. Transfer the COW VHD
self._migrate_vhd(instance, cow_uuid, dest, sr_path)
+ self._update_instance_progress(context, instance,
+ step=4,
+ total_steps=RESIZE_TOTAL_STEPS)
# NOTE(sirp): in case we're resizing to the same host (for dev
# purposes), apply a suffix to name-label so the two VM records
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index 306991bf3..8d9a177f8 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -234,10 +234,10 @@ class XenAPIConnection(driver.ComputeDriver):
"""Unpause paused VM instance"""
self._vmops.unpause(instance, callback)
- def migrate_disk_and_power_off(self, instance, dest):
+ def migrate_disk_and_power_off(self, context, instance, dest):
"""Transfers the VHD of a running instance to another host, then shuts
off the instance copies over the COW disk"""
- return self._vmops.migrate_disk_and_power_off(instance, dest)
+ return self._vmops.migrate_disk_and_power_off(context, instance, dest)
def suspend(self, instance, callback):
"""suspend the specified instance"""