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:
parent
4d84341387
commit
70f3477a3d
|
@ -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,
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue