Re-attach volumes after instance resize

Fixes bug 1051600.

Update the compute manager to pass block_device_info to three
additional driver entry points and update all virtualization drivers
to accept it as an optional argument.

Within libvirt, migrate_disk_and_power_off() will now iterate and disconnect
any existing connections.  finish_migration() simply updates its use
of to_xml(), passing block_device_info, which returns a libvirt XML
fully populated with volumes.  Finally, finish_revert_migration() no
longer uses the on-disk XML file, instead generating it via to_xml; as
the former lacks volume configuration.

Change-Id: I7dccd03ef9cc7d2848f07764d4def3787e41e202
This commit is contained in:
Rafi Khardalian
2012-09-16 16:28:58 +00:00
parent 0ffff7c490
commit b01ec8012a
8 changed files with 69 additions and 24 deletions

View File

@@ -1445,8 +1445,12 @@ class ComputeManager(manager.SchedulerDependentManager):
old_instance_type = migration_ref['old_instance_type_id']
instance_type = instance_types.get_instance_type(old_instance_type)
block_device_info = self._get_instance_volume_block_device_info(
context, instance['uuid'])
self.driver.finish_revert_migration(instance,
self._legacy_nw_info(network_info))
self._legacy_nw_info(network_info),
block_device_info)
# Just roll back the record. There's no need to resize down since
# the 'old' VM already has the preferred attributes
@@ -1558,9 +1562,13 @@ class ComputeManager(manager.SchedulerDependentManager):
self._notify_about_instance_usage(
context, instance, "resize.start", network_info=network_info)
block_device_info = self._get_instance_volume_block_device_info(
context, instance['uuid'])
disk_info = self.driver.migrate_disk_and_power_off(
context, instance, migration_ref['dest_host'],
instance_type_ref, self._legacy_nw_info(network_info))
instance_type_ref, self._legacy_nw_info(network_info),
block_device_info)
self.db.migration_update(context,
migration_id,
@@ -1609,10 +1617,14 @@ class ComputeManager(manager.SchedulerDependentManager):
context, instance, "finish_resize.start",
network_info=network_info)
block_device_info = self._get_instance_volume_block_device_info(
context, instance['uuid'])
self.driver.finish_migration(context, migration_ref, instance,
disk_info,
self._legacy_nw_info(network_info),
image, resize_instance)
image, resize_instance,
block_device_info)
instance = self._instance_update(context,
instance['uuid'],

View File

@@ -3736,7 +3736,8 @@ class LibvirtDriverTestCase(test.TestCase):
def fake_extend(path, size):
pass
def fake_to_xml(instance, network_info):
def fake_to_xml(instance, network_info, image_meta=None, rescue=None,
block_device_info=None):
return ""
def fake_plug_vifs(instance, network_info):
@@ -3802,6 +3803,11 @@ class LibvirtDriverTestCase(test.TestCase):
def fake_get_info(instance):
return {'state': power_state.RUNNING}
def fake_to_xml(instance, network_info, image_meta=None, rescue=None,
block_device_info=None):
return ""
self.stubs.Set(self.libvirtconnection, 'to_xml', fake_to_xml)
self.stubs.Set(self.libvirtconnection, 'plug_vifs', fake_plug_vifs)
self.stubs.Set(utils, 'execute', fake_execute)
fw = base_firewall.NoopFirewallDriver()

View File

@@ -242,7 +242,8 @@ class ComputeDriver(object):
raise NotImplementedError()
def migrate_disk_and_power_off(self, context, instance, dest,
instance_type, network_info):
instance_type, network_info,
block_device_info=None):
"""
Transfers the disk of a running instance in multiple phases, turning
off the instance before the end.
@@ -261,7 +262,8 @@ class ComputeDriver(object):
raise NotImplementedError()
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance):
network_info, image_meta, resize_instance,
block_device_info=None):
"""Completes a resize, turning on the migrated instance
:param network_info:
@@ -277,7 +279,8 @@ class ComputeDriver(object):
# TODO(Vek): Need to pass context in for access to auth_token
raise NotImplementedError()
def finish_revert_migration(self, instance, network_info):
def finish_revert_migration(self, instance, network_info,
block_device_info=None):
"""Finish reverting a resize, powering back on the instance"""
# TODO(Vek): Need to pass context in for access to auth_token
raise NotImplementedError()

View File

@@ -124,10 +124,12 @@ class FakeDriver(driver.ComputeDriver):
pass
def migrate_disk_and_power_off(self, context, instance, dest,
instance_type, network_info):
instance_type, network_info,
block_device_info=None):
pass
def finish_revert_migration(self, instance, network_info):
def finish_revert_migration(self, instance, network_info,
block_device_info=None):
pass
def power_off(self, instance):
@@ -252,7 +254,8 @@ class FakeDriver(driver.ComputeDriver):
return
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance):
network_info, image_meta, resize_instance,
block_device_info=None):
return
def confirm_migration(self, migration, instance, network_info):

View File

@@ -207,12 +207,14 @@ class HyperVDriver(driver.ComputeDriver):
"""Confirms a resize, destroying the source VM"""
LOG.debug(_("confirm_migration called"), instance=instance)
def finish_revert_migration(self, instance, network_info):
def finish_revert_migration(self, instance, network_info,
block_device_info=None):
"""Finish reverting a resize, powering back on the instance"""
LOG.debug(_("finish_revert_migration called"), instance=instance)
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance=False):
network_info, image_meta, resize_instance=False,
block_device_info=None):
"""Completes a resize, turning on the migrated instance"""
LOG.debug(_("finish_migration called"), instance=instance)

View File

@@ -2765,7 +2765,8 @@ class LibvirtDriver(driver.ComputeDriver):
@exception.wrap_exception()
def migrate_disk_and_power_off(self, context, instance, dest,
instance_type, network_info):
instance_type, network_info,
block_device_info=None):
LOG.debug(_("Starting migrate_disk_and_power_off"),
instance=instance)
disk_info_text = self.get_instance_disk_info(instance['name'])
@@ -2773,6 +2774,15 @@ class LibvirtDriver(driver.ComputeDriver):
self.power_off(instance)
block_device_mapping = driver.block_device_info_get_mapping(
block_device_info)
for vol in block_device_mapping:
connection_info = vol['connection_info']
mount_device = vol['mount_device'].rpartition("/")[2]
self.volume_driver_method('disconnect_volume',
connection_info,
mount_device)
# copy disks to destination
# rename instance dir to +_resize at first for using
# shared storage for instance dir (eg. NFS).
@@ -2826,7 +2836,8 @@ class LibvirtDriver(driver.ComputeDriver):
@exception.wrap_exception()
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance):
network_info, image_meta, resize_instance,
block_device_info=None):
LOG.debug(_("Starting finish_migration"), instance=instance)
# resize disks. only "disk" and "disk.local" are necessary.
@@ -2863,18 +2874,21 @@ class LibvirtDriver(driver.ComputeDriver):
'-O', 'qcow2', info['path'], path_qcow)
utils.execute('mv', path_qcow, info['path'])
xml = self.to_xml(instance, network_info)
xml = self.to_xml(instance, network_info,
block_device_info=block_device_info)
# assume _create_image do nothing if a target file exists.
# TODO(oda): injecting files is not necessary
self._create_image(context, instance, xml,
network_info=network_info,
block_device_info=None)
self._create_domain_and_network(xml, instance, network_info)
self._create_domain_and_network(xml, instance, network_info,
block_device_info)
timer = utils.LoopingCall(self._wait_for_running, instance)
timer.start(interval=0.5).wait()
@exception.wrap_exception()
def finish_revert_migration(self, instance, network_info):
def finish_revert_migration(self, instance, network_info,
block_device_info=None):
LOG.debug(_("Starting finish_revert_migration"),
instance=instance)
@@ -2882,9 +2896,10 @@ class LibvirtDriver(driver.ComputeDriver):
inst_base_resize = inst_base + "_resize"
utils.execute('mv', inst_base_resize, inst_base)
xml_path = os.path.join(inst_base, 'libvirt.xml')
xml = open(xml_path).read()
self._create_domain_and_network(xml, instance, network_info)
xml = self.to_xml(instance, network_info,
block_device_info=block_device_info)
self._create_domain_and_network(xml, instance, network_info,
block_device_info)
timer = utils.LoopingCall(self._wait_for_running, instance)
timer.start(interval=0.5).wait()

View File

@@ -183,13 +183,15 @@ class XenAPIDriver(driver.ComputeDriver):
# TODO(Vek): Need to pass context in for access to auth_token
self._vmops.confirm_migration(migration, instance, network_info)
def finish_revert_migration(self, instance, network_info):
def finish_revert_migration(self, instance, network_info,
block_device_info=None):
"""Finish reverting a resize, powering back on the instance"""
# NOTE(vish): Xen currently does not use network info.
self._vmops.finish_revert_migration(instance)
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance=False):
network_info, image_meta, resize_instance=False,
block_device_info=None):
"""Completes a resize, turning on the migrated instance"""
self._vmops.finish_migration(context, migration, instance, disk_info,
network_info, image_meta, resize_instance)
@@ -230,7 +232,8 @@ class XenAPIDriver(driver.ComputeDriver):
self._vmops.unpause(instance)
def migrate_disk_and_power_off(self, context, instance, dest,
instance_type, network_info):
instance_type, network_info,
block_device_info=None):
"""Transfers the VHD of a running instance to another host, then shuts
off the instance copies over the COW disk"""
# NOTE(vish): Xen currently does not use network info.

View File

@@ -189,7 +189,8 @@ class VMOps(object):
self._start(instance, vm_ref)
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance):
network_info, image_meta, resize_instance,
block_device_info=None):
root_vdi = vm_utils.move_disks(self._session, instance, disk_info)
if resize_instance: