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:
Petrut Lucian
2013-10-08 21:28:02 +03:00
parent a0546fd3f4
commit efdca8eeaf
5 changed files with 45 additions and 13 deletions

View File

@@ -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))

View File

@@ -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)

View File

@@ -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,

View File

@@ -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

View File

@@ -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,