libvirt: Resize down an instance booted from a volume

When we boot instance from volume, it doesn't use root disk and
we should provide the possibility to resize down this instance.
This patch allows to change the size down of the instance
booted from volume.

Also this patch got rid of duplicate code in test-cases.

Closes-Bug: #1416361
Change-Id: I0f890de69d268218d44a6ab4117a5c69cf6fbf8f
This commit is contained in:
Mike Durnosvistov 2015-02-04 17:59:30 +02:00
parent 6b4fae76c4
commit 8e1be7c05a
2 changed files with 66 additions and 48 deletions

View File

@ -11811,6 +11811,17 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
old_flavor=None, new_flavor=None,
**inst)
@staticmethod
def _disk_info():
# 10G root and 512M swap disk
disk_info = [{'disk_size': 1, 'type': 'qcow2',
'virt_disk_size': 10737418240, 'path': '/test/disk',
'backing_file': '/base/disk'},
{'disk_size': 1, 'type': 'qcow2',
'virt_disk_size': 536870912, 'path': '/test/disk.swap',
'backing_file': '/base/swap_512'}]
return jsonutils.dumps(disk_info)
def test_migrate_disk_and_power_off_exception(self):
"""Test for nova.virt.libvirt.libvirt_driver.LivirtConnection
.migrate_disk_and_power_off.
@ -11860,24 +11871,18 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
context.get_admin_context(), ins_ref, '10.0.0.2',
flavor_obj, None)
def _test_migrate_disk_and_power_off(self, flavor_obj):
def _test_migrate_disk_and_power_off(self, flavor_obj,
block_device_info=None,
params_for_instance=None):
"""Test for nova.virt.libvirt.libvirt_driver.LivirtConnection
.migrate_disk_and_power_off.
"""
disk_info = [{'type': 'qcow2', 'path': '/test/disk',
'virt_disk_size': '10737418240',
'backing_file': '/base/disk',
'disk_size': '83886080'},
{'type': 'raw', 'path': '/test/disk.local',
'virt_disk_size': '10737418240',
'backing_file': '/base/disk.local',
'disk_size': '83886080'}]
disk_info_text = jsonutils.dumps(disk_info)
disk_info = self._disk_info()
def fake_get_instance_disk_info(instance,
block_device_info=None):
return disk_info_text
return disk_info
def fake_destroy(instance):
pass
@ -11895,19 +11900,19 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
fake_get_host_ip_addr)
self.stubs.Set(utils, 'execute', fake_execute)
ins_ref = self._create_instance()
ins_ref = self._create_instance(params=params_for_instance)
# dest is different host case
out = self.drvr.migrate_disk_and_power_off(
context.get_admin_context(), ins_ref, '10.0.0.2',
flavor_obj, None)
self.assertEqual(out, disk_info_text)
flavor_obj, None, block_device_info=block_device_info)
self.assertEqual(out, disk_info)
# dest is same host case
out = self.drvr.migrate_disk_and_power_off(
context.get_admin_context(), ins_ref, '10.0.0.1',
flavor_obj, None)
self.assertEqual(out, disk_info_text)
flavor_obj, None, block_device_info=block_device_info)
self.assertEqual(out, disk_info)
def test_migrate_disk_and_power_off(self):
flavor = {'root_gb': 10, 'ephemeral_gb': 20}
@ -11915,6 +11920,24 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
self._test_migrate_disk_and_power_off(flavor_obj)
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
def test_migrate_disk_and_power_off_boot_from_volume(self,
disconnect_volume):
info = {'block_device_mapping': [{'boot_index': None,
'mount_device': '/dev/vdd',
'connection_info': None},
{'boot_index': 0,
'mount_device': '/dev/vda',
'connection_info': None}]}
flavor = {'root_gb': 1, 'ephemeral_gb': 0}
flavor_obj = objects.Flavor(**flavor)
# Note(Mike_D): The size of instance's ephemeral_gb is 0 gb.
self._test_migrate_disk_and_power_off(
flavor_obj, block_device_info=info,
params_for_instance={'image_ref': None, 'ephemeral_gb': 0})
disconnect_volume.assert_called_with(
info['block_device_mapping'][1]['connection_info'], 'vda')
@mock.patch('nova.utils.execute')
@mock.patch('nova.virt.libvirt.utils.copy_image')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._destroy')
@ -11931,15 +11954,8 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
"""
self.copy_or_move_swap_called = False
# 10G root and 512M swap disk
disk_info = [{'disk_size': 1, 'type': 'qcow2',
'virt_disk_size': 10737418240, 'path': '/test/disk',
'backing_file': '/base/disk'},
{'disk_size': 1, 'type': 'qcow2',
'virt_disk_size': 536870912, 'path': '/test/disk.swap',
'backing_file': '/base/swap_512'}]
disk_info_text = jsonutils.dumps(disk_info)
mock_get_disk_info.return_value = disk_info_text
disk_info = self._disk_info()
mock_get_disk_info.return_value = disk_info
get_host_ip_addr.return_value = '10.0.0.1'
def fake_copy_image(*args, **kwargs):
@ -11975,22 +11991,16 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
self.assertTrue(get_host_ip_addr.called)
mock_destroy.assert_called_once_with(instance)
self.assertFalse(self.copy_or_move_swap_called)
self.assertEqual(disk_info_text, out)
self.assertEqual(disk_info, out)
def _test_migrate_disk_and_power_off_resize_check(self, expected_exc):
"""Test for nova.virt.libvirt.libvirt_driver.LibvirtConnection
.migrate_disk_and_power_off.
"""
disk_info = [{'type': 'raw', 'path': '/dev/vg/disk',
'disk_size': '83886080'},
{'type': 'raw', 'path': '/dev/disk.local',
'disk_size': '83886080'}]
disk_info_text = jsonutils.dumps(disk_info)
def fake_get_instance_disk_info(instance, xml=None,
block_device_info=None):
return disk_info_text
return self._disk_info()
def fake_destroy(instance):
pass
@ -12042,28 +12052,38 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
expected_exc = exception.InstanceFaultRollback
self._test_migrate_disk_and_power_off_resize_check(expected_exc)
def test_migrate_disk_and_power_off_resize_error(self):
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver'
'.get_instance_disk_info')
def test_migrate_disk_and_power_off_resize_error(self, mock_get_disk_info):
instance = self._create_instance()
flavor = {'root_gb': 5, 'ephemeral_gb': 10}
flavor_obj = objects.Flavor(**flavor)
mock_get_disk_info.return_value = self._disk_info()
self.assertRaises(
exception.InstanceFaultRollback,
self.drvr.migrate_disk_and_power_off,
'ctx', instance, '10.0.0.1', flavor_obj, None)
def test_migrate_disk_and_power_off_resize_error_default_ephemeral(self):
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver'
'.get_instance_disk_info')
def test_migrate_disk_and_power_off_resize_error_default_ephemeral(
self, mock_get_disk_info):
# Note(Mike_D): The size of this instance's ephemeral_gb is 20 gb.
instance = self._create_instance()
flavor = {'root_gb': 10, 'ephemeral_gb': 0}
flavor_obj = objects.Flavor(**flavor)
mock_get_disk_info.return_value = self._disk_info()
self.assertRaises(exception.InstanceFaultRollback,
self.drvr.migrate_disk_and_power_off,
'ctx', instance, '10.0.0.1', flavor_obj, None)
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver'
'.get_instance_disk_info')
@mock.patch('nova.virt.driver.block_device_info_get_ephemerals')
def test_migrate_disk_and_power_off_resize_error_eph(self, mock_get):
def test_migrate_disk_and_power_off_resize_error_eph(self, mock_get,
mock_get_disk_info):
mappings = [
{
'device_name': '/dev/sdb4',
@ -12110,6 +12130,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
# Old flavor, eph is 20, real disk is 3, target is 2, fail
flavor = {'root_gb': 10, 'ephemeral_gb': 2}
flavor_obj = objects.Flavor(**flavor)
mock_get_disk_info.return_value = self._disk_info()
self.assertRaises(
exception.InstanceFaultRollback,
@ -12222,11 +12243,6 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
.finish_migration.
"""
disk_info = [{'type': 'qcow2', 'path': '/test/disk',
'local_gb': 10, 'backing_file': '/base/disk'},
{'type': 'raw', 'path': '/test/disk.local',
'local_gb': 10, 'backing_file': '/base/disk.local'}]
disk_info_text = jsonutils.dumps(disk_info)
powered_on = power_on
self.fake_create_domain_called = False
self.fake_disk_resize_called = False
@ -12296,7 +12312,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
migration.dest_node = 'fake-dest-node'
self.drvr.finish_migration(
context.get_admin_context(), migration, ins_ref,
disk_info_text, [], image_meta,
self._disk_info(), [], image_meta,
resize_instance, None, power_on)
self.assertTrue(self.fake_create_domain_called)
self.assertEqual(

View File

@ -6170,19 +6170,21 @@ class LibvirtDriver(driver.ComputeDriver):
instance.ephemeral_gb)
# Checks if the migration needs a disk resize down.
if (flavor['root_gb'] < instance.root_gb or
flavor['ephemeral_gb'] < eph_size):
root_down = flavor['root_gb'] < instance.root_gb
ephemeral_down = flavor['ephemeral_gb'] < eph_size
disk_info_text = self.get_instance_disk_info(
instance, block_device_info=block_device_info)
booted_from_volume = self._is_booted_from_volume(instance,
disk_info_text)
if (root_down and not booted_from_volume) or ephemeral_down:
reason = _("Unable to resize disk down.")
raise exception.InstanceFaultRollback(
exception.ResizeError(reason=reason))
disk_info_text = self.get_instance_disk_info(instance,
block_device_info=block_device_info)
disk_info = jsonutils.loads(disk_info_text)
# NOTE(dgenin): Migration is not implemented for LVM backed instances.
if (CONF.libvirt.images_type == 'lvm' and
not self._is_booted_from_volume(instance, disk_info_text)):
if CONF.libvirt.images_type == 'lvm' and not booted_from_volume:
reason = _("Migration is not supported for LVM backed instances")
raise exception.InstanceFaultRollback(
exception.MigrationPreCheckError(reason=reason))