Evacuate feature

This patch adds the 'evacuate' feature to the compute-hyperv driver.

It works by removing the 'delete_disk_files' from the 'spawn' method,
preventing it from happening every time a new instance is spawned, and
checking whether or not disk files already exist for the given machine.
If they do, they will be used when evacuating the instance on another
host

Change-Id: Ie3e847b9f285cf47b474cdcc96e289c7d053c8a0
This commit is contained in:
AlexMuresan 2018-01-04 14:03:57 +02:00 committed by Alexandru Muresan
parent 4d84341387
commit 70f3477a3d
3 changed files with 100 additions and 35 deletions

View File

@ -97,7 +97,7 @@ exception_conversion_map = {
class HyperVDriver(driver.ComputeDriver):
capabilities = {
"has_imagecache": True,
"supports_recreate": False,
"supports_recreate": True,
"supports_migrate_to_same_host": False,
"supports_attach_interface": True,
"supports_device_tagging": True,

View File

@ -155,7 +155,12 @@ class VMOps(object):
root_iso_path_cached = self._imagecache.get_cached_image(context,
instance)
root_iso_path = self._pathutils.get_root_vhd_path(instance.name, 'iso')
self._pathutils.copyfile(root_iso_path_cached, root_iso_path)
if not os.path.exists(root_iso_path):
self._pathutils.copyfile(root_iso_path_cached, root_iso_path)
else:
LOG.info("Root iso '%s' already exists. Reusing it.",
root_iso_path)
return root_iso_path
@ -170,6 +175,12 @@ class VMOps(object):
root_vhd_path = self._pathutils.get_root_vhd_path(instance.name,
format_ext,
is_rescue_vhd)
if os.path.exists(root_vhd_path):
LOG.info("Root vhd '%s' already exists. Reusing it.",
root_vhd_path)
return root_vhd_path
root_vhd_size = instance.flavor.root_gb * units.Gi
try:
@ -234,12 +245,16 @@ class VMOps(object):
self.create_ephemeral_disk(instance.name, eph)
def create_ephemeral_disk(self, instance_name, eph_info):
self._vhdutils.create_dynamic_vhd(eph_info['path'],
eph_info['size'] * units.Gi)
if not os.path.exists(eph_info['path']):
self._vhdutils.create_dynamic_vhd(eph_info['path'],
eph_info['size'] * units.Gi)
else:
LOG.info("Ephemeral '%s' disk already exists. Reusing it.",
eph_info['path'])
def get_attached_ephemeral_disks(self, instance_name):
vm_image_disks = self._vmutils.get_vm_storage_paths(
instance_name)[0]
instance_name)[0]
return [image_path for image_path in vm_image_disks
if os.path.basename(image_path).lower().startswith('eph')]
@ -291,11 +306,14 @@ class VMOps(object):
if self._vmutils.vm_exists(instance_name):
raise exception.InstanceExists(name=instance_name)
# Make sure we're starting with a clean slate.
self._delete_disk_files(instance)
vm_gen = self.get_image_vm_generation(instance.uuid, image_meta)
instance_dir = self._pathutils.get_instance_dir(instance.name,
create_dir=False)
if instance_dir:
LOG.info("Instance directory already exists."
"Reusing existing files.")
self._block_dev_man.validate_and_update_bdi(
instance, image_meta, vm_gen, block_device_info)
root_device = block_device_info['root_disk']

View File

@ -167,13 +167,16 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
mock_create_root_iso.assert_called_once_with(self.context,
mock_instance)
@mock.patch('os.path.exists')
@mock.patch.object(vmops.imagecache.ImageCache, 'get_cached_image')
def test_create_root_iso(self, mock_get_cached_image):
def _test_create_root_iso(self, mock_get_cached_image,
mock_os_path_exists, iso_already_exists=False):
mock_instance = fake_instance.fake_instance_obj(self.context)
mock_get_root_vhd_path = self._vmops._pathutils.get_root_vhd_path
mock_get_root_vhd_path.return_value = mock.sentinel.ROOT_ISO_PATH
mock_get_cached_image.return_value = mock.sentinel.CACHED_ISO_PATH
mock_os_path_exists.return_value = iso_already_exists
self._vmops._create_root_iso(self.context, mock_instance)
@ -181,8 +184,17 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
mock_instance)
mock_get_root_vhd_path.assert_called_once_with(mock_instance.name,
'iso')
self._vmops._pathutils.copyfile.assert_called_once_with(
mock.sentinel.CACHED_ISO_PATH, mock.sentinel.ROOT_ISO_PATH)
if not iso_already_exists:
self._vmops._pathutils.copyfile.assert_called_once_with(
mock.sentinel.CACHED_ISO_PATH, mock.sentinel.ROOT_ISO_PATH)
else:
self._vmops._pathutils.copyfile.assert_not_called()
def test_create_root_iso(self):
self._test_create_root_iso()
def test_create_root_iso_already_existing_image(self):
self._test_create_root_iso(iso_already_exists=True)
def _prepare_create_root_device_mocks(self, use_cow_images, vhd_format,
vhd_size):
@ -199,15 +211,17 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
return mock_instance
@mock.patch('os.path.exists')
@mock.patch('compute_hyperv.nova.imagecache.ImageCache.get_cached_image')
def _test_create_root_vhd_exception(self, mock_get_cached_image,
vhd_format):
mock_os_path_exists, vhd_format):
mock_instance = self._prepare_create_root_device_mocks(
use_cow_images=False, vhd_format=vhd_format,
vhd_size=(self.FAKE_SIZE + 1))
fake_vhd_path = self.FAKE_ROOT_PATH % vhd_format
mock_get_cached_image.return_value = fake_vhd_path
fake_root_path = self._vmops._pathutils.get_root_vhd_path.return_value
mock_os_path_exists.return_value = False
self.assertRaises(exception.FlavorDiskSmallerThanImage,
self._vmops._create_root_vhd, self.context,
@ -219,13 +233,17 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
self._vmops._pathutils.remove.assert_called_once_with(
fake_root_path)
@mock.patch('os.path.exists')
@mock.patch('compute_hyperv.nova.imagecache.ImageCache.get_cached_image')
def _test_create_root_vhd_qcow(self, mock_get_cached_image, vhd_format):
def _test_create_root_vhd_qcow(self, mock_get_cached_image,
mock_os_path_exists, vhd_format,
vhd_already_exists=False):
mock_instance = self._prepare_create_root_device_mocks(
use_cow_images=True, vhd_format=vhd_format,
vhd_size=(self.FAKE_SIZE - 1))
fake_vhd_path = self.FAKE_ROOT_PATH % vhd_format
mock_get_cached_image.return_value = fake_vhd_path
mock_os_path_exists.return_value = vhd_already_exists
fake_root_path = self._vmops._pathutils.get_root_vhd_path.return_value
root_vhd_internal_size = mock_instance.flavor.root_gb * units.Gi
@ -237,23 +255,32 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
self.assertEqual(fake_root_path, response)
self._vmops._pathutils.get_root_vhd_path.assert_called_with(
mock_instance.name, vhd_format, False)
differencing_vhd = self._vmops._vhdutils.create_differencing_vhd
differencing_vhd.assert_called_with(fake_root_path, fake_vhd_path)
self._vmops._vhdutils.get_vhd_info.assert_called_once_with(
fake_vhd_path)
if vhd_format is constants.DISK_FORMAT_VHD:
self.assertFalse(get_size.called)
self.assertFalse(self._vmops._vhdutils.resize_vhd.called)
if not vhd_already_exists:
differencing_vhd.assert_called_with(fake_root_path, fake_vhd_path)
self._vmops._vhdutils.get_vhd_info.assert_called_once_with(
fake_vhd_path)
if vhd_format is constants.DISK_FORMAT_VHD:
self.assertFalse(get_size.called)
self.assertFalse(self._vmops._vhdutils.resize_vhd.called)
else:
get_size.assert_called_once_with(fake_vhd_path,
root_vhd_internal_size)
self._vmops._vhdutils.resize_vhd.assert_called_once_with(
fake_root_path, root_vhd_internal_size,
is_file_max_size=False)
else:
get_size.assert_called_once_with(fake_vhd_path,
root_vhd_internal_size)
self._vmops._vhdutils.resize_vhd.assert_called_once_with(
fake_root_path, root_vhd_internal_size, is_file_max_size=False)
differencing_vhd.assert_not_called()
self._vmops._vhdutils.resize_vhd.assert_not_called()
@mock.patch('os.path.exists')
@mock.patch('compute_hyperv.nova.imagecache.ImageCache.get_cached_image')
def _test_create_root_vhd(self, mock_get_cached_image, vhd_format,
is_rescue_vhd=False):
def _test_create_root_vhd(self, mock_get_cached_image, mock_os_path_exists,
vhd_format, is_rescue_vhd=False,
vhd_already_exists=False):
mock_instance = self._prepare_create_root_device_mocks(
use_cow_images=False, vhd_format=vhd_format,
vhd_size=(self.FAKE_SIZE - 1))
@ -265,6 +292,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
fake_root_path = self._vmops._pathutils.get_root_vhd_path.return_value
root_vhd_internal_size = mock_instance.flavor.root_gb * units.Gi
get_size = self._vmops._vhdutils.get_internal_vhd_size_by_file_size
mock_os_path_exists.return_value = vhd_already_exists
response = self._vmops._create_root_vhd(
context=self.context,
@ -278,15 +306,20 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
self._vmops._pathutils.get_root_vhd_path.assert_called_with(
mock_instance.name, vhd_format, is_rescue_vhd)
self._vmops._pathutils.copyfile.assert_called_once_with(
fake_vhd_path, fake_root_path)
get_size.assert_called_once_with(fake_vhd_path, root_vhd_internal_size)
if is_rescue_vhd:
self.assertFalse(self._vmops._vhdutils.resize_vhd.called)
if not vhd_already_exists:
self._vmops._pathutils.copyfile.assert_called_once_with(
fake_vhd_path, fake_root_path)
get_size.assert_called_once_with(fake_vhd_path,
root_vhd_internal_size)
if is_rescue_vhd:
self.assertFalse(self._vmops._vhdutils.resize_vhd.called)
else:
self._vmops._vhdutils.resize_vhd.assert_called_once_with(
fake_root_path, root_vhd_internal_size,
is_file_max_size=False)
else:
self._vmops._vhdutils.resize_vhd.assert_called_once_with(
fake_root_path, root_vhd_internal_size,
is_file_max_size=False)
self._vmops._pathutils.copyfile.assert_not_called()
def test_create_root_vhd(self):
self._test_create_root_vhd(vhd_format=constants.DISK_FORMAT_VHD)
@ -294,12 +327,28 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
def test_create_root_vhdx(self):
self._test_create_root_vhd(vhd_format=constants.DISK_FORMAT_VHDX)
def test_create_root_vhd_existing_disk(self):
self._test_create_root_vhd(vhd_format=constants.DISK_FORMAT_VHD,
vhd_already_exists=True)
def test_create_root_vhdx_existing_disk(self):
self._test_create_root_vhd(vhd_format=constants.DISK_FORMAT_VHDX,
vhd_already_exists=True)
def test_create_root_vhd_use_cow_images_true(self):
self._test_create_root_vhd_qcow(vhd_format=constants.DISK_FORMAT_VHD)
def test_create_root_vhdx_use_cow_images_true(self):
self._test_create_root_vhd_qcow(vhd_format=constants.DISK_FORMAT_VHDX)
def test_create_root_vhd_use_already_existing_cow_images(self):
self._test_create_root_vhd_qcow(vhd_format=constants.DISK_FORMAT_VHD,
vhd_already_exists=True)
def test_create_root_vhdx_use_already_existing_cow_images(self):
self._test_create_root_vhd_qcow(vhd_format=constants.DISK_FORMAT_VHDX,
vhd_already_exists=True)
def test_create_rescue_vhd(self):
self._test_create_root_vhd(vhd_format=constants.DISK_FORMAT_VHD,
is_rescue_vhd=True)
@ -488,8 +537,6 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
mock.sentinel.network_info, block_device_info)
self._vmops._vmutils.vm_exists.assert_called_once_with(
mock_instance.name)
mock_delete_disk_files.assert_called_once_with(
mock_instance)
mock_validate_and_update_bdi = (
self._vmops._block_dev_man.validate_and_update_bdi)
mock_validate_and_update_bdi.assert_called_once_with(