Fixes Hyper-V snapshot spawning issue
In Hyper-V, snapshoting an instance and then spawning a new one using the resulted snapshot image with the same flavor, will result an Hyper-V Exception. Hyper-V will get the request to resize the base image to it's actual size, leading to an error. The reason is that the logic of the resize_vhd method has been changed, as it is meant to resize images to the flavor size minus the image metadata size, thus it's maximum internal size. For this reason, the way to decide whether the root image should be resized or not must also be changed. So, instead of comparing the root image and base image sizes, we should compare their maximum internal sizes. Closes-Bug: #1234759 Change-Id: I83d2df76b726d705cedc52a8309a2be7037d6a79
This commit is contained in:
@@ -178,6 +178,8 @@ class HyperVAPITestCase(test.NoDBTestCase):
|
||||
self._mox.StubOutWithMock(vhdutils.VHDUtils, 'get_vhd_parent_path')
|
||||
self._mox.StubOutWithMock(vhdutils.VHDUtils, 'get_vhd_info')
|
||||
self._mox.StubOutWithMock(vhdutils.VHDUtils, 'resize_vhd')
|
||||
self._mox.StubOutWithMock(vhdutils.VHDUtils,
|
||||
'get_internal_vhd_size_by_file_size')
|
||||
self._mox.StubOutWithMock(vhdutils.VHDUtils, 'validate_vhd')
|
||||
self._mox.StubOutWithMock(vhdutils.VHDUtils, 'get_vhd_format')
|
||||
self._mox.StubOutWithMock(vhdutils.VHDUtils, 'create_dynamic_vhd')
|
||||
@@ -965,6 +967,11 @@ class HyperVAPITestCase(test.NoDBTestCase):
|
||||
m.AndReturn({'MaxInternalSize': 1024})
|
||||
|
||||
fake.PathUtils.copyfile(mox.IsA(str), mox.IsA(str))
|
||||
|
||||
m = vhdutils.VHDUtils.get_internal_vhd_size_by_file_size(
|
||||
mox.IsA(str), mox.IsA(object))
|
||||
m.AndReturn(1025)
|
||||
|
||||
vhdutils.VHDUtils.resize_vhd(mox.IsA(str), mox.IsA(object))
|
||||
|
||||
def _setup_spawn_instance_mocks(self, cow, setup_vif_mocks_func=None,
|
||||
@@ -1002,6 +1009,9 @@ class HyperVAPITestCase(test.NoDBTestCase):
|
||||
m = vhdutils.VHDUtils.get_vhd_info(mox.IsA(str))
|
||||
m.AndReturn({'MaxInternalSize': 1024, 'FileSize': 1024,
|
||||
'Type': 2})
|
||||
m = vhdutils.VHDUtils.get_internal_vhd_size_by_file_size(
|
||||
mox.IsA(str), mox.IsA(object))
|
||||
m.AndReturn(1025)
|
||||
vhdutils.VHDUtils.resize_vhd(mox.IsA(str), mox.IsA(object))
|
||||
|
||||
self._setup_check_admin_permissions_mocks(
|
||||
@@ -1432,6 +1442,9 @@ class HyperVAPITestCase(test.NoDBTestCase):
|
||||
m = vhdutils.VHDUtils.get_vhd_info(mox.IsA(str))
|
||||
m.AndReturn({'ParentPath': fake_parent_vhd_path,
|
||||
'MaxInternalSize': 1})
|
||||
m = vhdutils.VHDUtils.get_internal_vhd_size_by_file_size(
|
||||
mox.IsA(str), mox.IsA(object))
|
||||
m.AndReturn(1025)
|
||||
|
||||
vhdutils.VHDUtils.reconnect_parent_vhd(mox.IsA(str), mox.IsA(str))
|
||||
|
||||
|
||||
@@ -60,8 +60,8 @@ class VHDUtilsTestCase(test.NoDBTestCase):
|
||||
vhdutil.get_vhd_info = mock.MagicMock()
|
||||
vhdutil.get_vhd_info.return_value = {'Type': constants.VHD_TYPE_FIXED}
|
||||
|
||||
real_size = vhdutil._get_internal_vhd_size_by_file_size(None,
|
||||
root_vhd_size)
|
||||
real_size = vhdutil.get_internal_vhd_size_by_file_size(None,
|
||||
root_vhd_size)
|
||||
expected_vhd_size = 1 * 1024 ** 3 - 512
|
||||
self.assertEqual(expected_vhd_size, real_size)
|
||||
|
||||
@@ -74,8 +74,8 @@ class VHDUtilsTestCase(test.NoDBTestCase):
|
||||
vhdutil._get_vhd_dynamic_blk_size = mock.MagicMock()
|
||||
vhdutil._get_vhd_dynamic_blk_size.return_value = 2097152
|
||||
|
||||
real_size = vhdutil._get_internal_vhd_size_by_file_size(None,
|
||||
root_vhd_size)
|
||||
real_size = vhdutil.get_internal_vhd_size_by_file_size(None,
|
||||
root_vhd_size)
|
||||
expected_vhd_size = 20 * 1024 ** 3 - 43008
|
||||
self.assertEqual(expected_vhd_size, real_size)
|
||||
|
||||
@@ -86,5 +86,5 @@ class VHDUtilsTestCase(test.NoDBTestCase):
|
||||
vhdutil.get_vhd_info.return_value = {'Type': 5}
|
||||
|
||||
self.assertRaises(vmutils.HyperVException,
|
||||
vhdutil._get_internal_vhd_size_by_file_size,
|
||||
vhdutil.get_internal_vhd_size_by_file_size,
|
||||
None, root_vhd_size)
|
||||
|
||||
@@ -27,6 +27,7 @@ from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
from nova.virt.hyperv import utilsfactory
|
||||
from nova.virt.hyperv import vhdutilsv2
|
||||
from nova.virt.hyperv import vmutils
|
||||
from nova.virt import images
|
||||
|
||||
@@ -65,14 +66,23 @@ class ImageCache(object):
|
||||
root_vhd_size_gb = self._get_root_vhd_size_gb(instance)
|
||||
root_vhd_size = root_vhd_size_gb * 1024 ** 3
|
||||
|
||||
if root_vhd_size < vhd_size:
|
||||
# NOTE(lpetrut): Checking the namespace is needed as the following
|
||||
# method is not yet implemented in the vhdutilsv2 module.
|
||||
if not isinstance(self._vhdutils, vhdutilsv2.VHDUtilsV2):
|
||||
root_vhd_internal_size = (
|
||||
self._vhdutils.get_internal_vhd_size_by_file_size(
|
||||
vhd_path, root_vhd_size))
|
||||
else:
|
||||
root_vhd_internal_size = root_vhd_size
|
||||
|
||||
if root_vhd_internal_size < vhd_size:
|
||||
raise vmutils.HyperVException(
|
||||
_("Cannot resize the image to a size smaller than the VHD "
|
||||
"max. internal size: %(vhd_size)s. Requested disk size: "
|
||||
"%(root_vhd_size)s") %
|
||||
{'vhd_size': vhd_size, 'root_vhd_size': root_vhd_size}
|
||||
)
|
||||
if root_vhd_size > vhd_size:
|
||||
if root_vhd_internal_size > vhd_size:
|
||||
path_parts = os.path.splitext(vhd_path)
|
||||
resized_vhd_path = '%s_%s%s' % (path_parts[0],
|
||||
root_vhd_size_gb,
|
||||
|
||||
@@ -86,7 +86,7 @@ class VHDUtils(object):
|
||||
|
||||
def resize_vhd(self, vhd_path, new_max_size, is_file_max_size=True):
|
||||
if is_file_max_size:
|
||||
new_internal_max_size = self._get_internal_vhd_size_by_file_size(
|
||||
new_internal_max_size = self.get_internal_vhd_size_by_file_size(
|
||||
vhd_path, new_max_size)
|
||||
else:
|
||||
new_internal_max_size = new_max_size
|
||||
@@ -97,8 +97,7 @@ class VHDUtils(object):
|
||||
Path=vhd_path, MaxInternalSize=new_internal_max_size)
|
||||
self._vmutils.check_ret_val(ret_val, job_path)
|
||||
|
||||
def _get_internal_vhd_size_by_file_size(self, vhd_path,
|
||||
new_vhd_file_size):
|
||||
def get_internal_vhd_size_by_file_size(self, vhd_path, new_vhd_file_size):
|
||||
"""Fixed VHD size = Data Block size + 512 bytes
|
||||
Dynamic_VHD_size = Dynamic Disk Header
|
||||
+ Copy of hard disk footer
|
||||
|
||||
@@ -36,6 +36,7 @@ from nova.virt import configdrive
|
||||
from nova.virt.hyperv import constants
|
||||
from nova.virt.hyperv import imagecache
|
||||
from nova.virt.hyperv import utilsfactory
|
||||
from nova.virt.hyperv import vhdutilsv2
|
||||
from nova.virt.hyperv import vmutils
|
||||
from nova.virt.hyperv import volumeops
|
||||
|
||||
@@ -157,13 +158,22 @@ class VMOps(object):
|
||||
self._pathutils.copyfile(base_vhd_path, root_vhd_path)
|
||||
|
||||
base_vhd_info = self._vhdutils.get_vhd_info(base_vhd_path)
|
||||
base_vhd_size = base_vhd_info['FileSize']
|
||||
base_vhd_size = base_vhd_info['MaxInternalSize']
|
||||
root_vhd_size = instance['root_gb'] * 1024 ** 3
|
||||
|
||||
if root_vhd_size < base_vhd_size:
|
||||
# NOTE(lpetrut): Checking the namespace is needed as the
|
||||
# following method is not yet implemented in vhdutilsv2.
|
||||
if not isinstance(self._vhdutils, vhdutilsv2.VHDUtilsV2):
|
||||
root_vhd_internal_size = (
|
||||
self._vhdutils.get_internal_vhd_size_by_file_size(
|
||||
root_vhd_path, root_vhd_size))
|
||||
else:
|
||||
root_vhd_internal_size = root_vhd_size
|
||||
|
||||
if root_vhd_internal_size < base_vhd_size:
|
||||
raise vmutils.HyperVException(_("Cannot resize a VHD to a "
|
||||
"smaller size"))
|
||||
elif root_vhd_size > base_vhd_size:
|
||||
elif root_vhd_internal_size > base_vhd_size:
|
||||
LOG.debug(_("Resizing VHD %(root_vhd_path)s to new "
|
||||
"size %(root_vhd_size)s"),
|
||||
{'base_vhd_path': base_vhd_path,
|
||||
|
||||
Reference in New Issue
Block a user