Fix hyper-v vhd real size bigger than flavor issue

In hyper-v, after deploy the image, real vhd file size will be
extended from flavor size plus vhd metadata size, this patch
fix this issue by check the vhd type and minus the vhd metadata
size according to the specific vhd type.

Fixes bug #1177927

Change-Id: I51223f4f778f33e73dce1e0f53e23e8d7ac77904
This commit is contained in:
guohliu 2013-06-05 15:37:01 +08:00
parent 2f6022f6bf
commit 690cf0bdbe
5 changed files with 111 additions and 4 deletions

View File

@ -1000,7 +1000,8 @@ class HyperVAPITestCase(test.TestCase):
else:
fake.PathUtils.copyfile(mox.IsA(str), mox.IsA(str))
m = vhdutils.VHDUtils.get_vhd_info(mox.IsA(str))
m.AndReturn({'MaxInternalSize': 1024})
m.AndReturn({'MaxInternalSize': 1024, 'FileSize': 1024,
'Type': 2})
vhdutils.VHDUtils.resize_vhd(mox.IsA(str), mox.IsA(object))
self._setup_check_admin_permissions_mocks(

View File

@ -20,6 +20,7 @@ from nova import test
from nova.virt.hyperv import constants
from nova.virt.hyperv import vhdutils
from nova.virt.hyperv import vmutils
class VHDUtilsTestCase(test.TestCase):
@ -52,3 +53,38 @@ class VHDUtilsTestCase(test.TestCase):
mock_img_svc.CreateDynamicVirtualHardDisk.assert_called_once_with(
Path=self._FAKE_VHD_PATH,
MaxInternalSize=self._FAKE_MAK_INTERNAL_SIZE)
def test_get_internal_vhd_size_by_file_size_fixed(self):
vhdutil = vhdutils.VHDUtils()
root_vhd_size = 1 * 1024 ** 3
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)
expected_vhd_size = 1 * 1024 ** 3 - 512
self.assertEqual(expected_vhd_size, real_size)
def test_get_internal_vhd_size_by_file_size_dynamic(self):
vhdutil = vhdutils.VHDUtils()
root_vhd_size = 20 * 1024 ** 3
vhdutil.get_vhd_info = mock.MagicMock()
vhdutil.get_vhd_info.return_value = {'Type':
constants.VHD_TYPE_DYNAMIC}
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)
expected_vhd_size = 20 * 1024 ** 3 - 43008
self.assertEqual(expected_vhd_size, real_size)
def test_get_internal_vhd_size_by_file_size_unsupported(self):
vhdutil = vhdutils.VHDUtils()
root_vhd_size = 20 * 1024 ** 3
vhdutil.get_vhd_info = mock.MagicMock()
vhdutil.get_vhd_info.return_value = {'Type': 5}
self.assertRaises(vmutils.HyperVException,
vhdutil._get_internal_vhd_size_by_file_size,
None, root_vhd_size)

View File

@ -72,3 +72,6 @@ IDE_DVD = "DVD"
DISK_FORMAT_VHD = "VHD"
DISK_FORMAT_VHDX = "VHDX"
VHD_TYPE_FIXED = 2
VHD_TYPE_DYNAMIC = 3

View File

@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import struct
import sys
if sys.platform == 'win32':
@ -26,6 +27,14 @@ from nova.virt.hyperv import vmutils
from xml.etree import ElementTree
VHD_HEADER_SIZE_FIX = 512
VHD_BAT_ENTRY_SIZE = 4
VHD_DYNAMIC_DISK_HEADER_SIZE = 1024
VHD_HEADER_SIZE_DYNAMIC = 512
VHD_FOOTER_SIZE_DYNAMIC = 512
VHD_BLK_SIZE_OFFSET = 544
class VHDUtils(object):
def __init__(self):
@ -75,13 +84,71 @@ class VHDUtils(object):
DestinationPath=dest_vhd_path)
self._vmutils.check_ret_val(ret_val, job_path)
def resize_vhd(self, vhd_path, new_max_size):
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(
vhd_path, new_max_size)
else:
new_internal_max_size = new_max_size
image_man_svc = self._conn.Msvm_ImageManagementService()[0]
(job_path, ret_val) = image_man_svc.ExpandVirtualHardDisk(
Path=vhd_path, MaxInternalSize=new_max_size)
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):
"""Fixed VHD size = Data Block size + 512 bytes
Dynamic_VHD_size = Dynamic Disk Header
+ Copy of hard disk footer
+ Hard Disk Footer
+ Data Block
+ BAT
Dynamic Disk header fields
Copy of hard disk footer (512 bytes)
Dynamic Disk Header (1024 bytes)
BAT (Block Allocation table)
Data Block 1
Data Block 2
Data Block n
Hard Disk Footer (512 bytes)
Default block size is 2M
BAT entry size is 4byte
"""
base_vhd_info = self.get_vhd_info(vhd_path)
vhd_type = base_vhd_info['Type']
if vhd_type == constants.VHD_TYPE_FIXED:
vhd_header_size = VHD_HEADER_SIZE_FIX
return new_vhd_file_size - vhd_header_size
elif vhd_type == constants.VHD_TYPE_DYNAMIC:
bs = self._get_vhd_dynamic_blk_size(vhd_path)
bes = VHD_BAT_ENTRY_SIZE
ddhs = VHD_DYNAMIC_DISK_HEADER_SIZE
hs = VHD_HEADER_SIZE_DYNAMIC
fs = VHD_FOOTER_SIZE_DYNAMIC
max_internal_size = (new_vhd_file_size -
(hs + ddhs + fs)) * bs / (bes + bs)
return max_internal_size
else:
raise vmutils.HyperVException(_("The %(vhd_type)s type VHD "
"is not supported") %
{"vhd_type": vhd_type})
def _get_vhd_dynamic_blk_size(self, vhd_path):
blk_size_offset = VHD_BLK_SIZE_OFFSET
try:
with open(vhd_path, "rb") as f:
f.seek(blk_size_offset)
version = f.read(4)
except IOError:
raise vmutils.HyperVException(_("Unable to obtain block size from"
" VHD %(vhd_path)s") %
{"vhd_path": vhd_path})
return struct.unpack('>i', version)[0]
def get_vhd_parent_path(self, vhd_path):
return self.get_vhd_info(vhd_path).get("ParentPath")

View File

@ -157,7 +157,7 @@ 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['MaxInternalSize']
base_vhd_size = base_vhd_info['FileSize']
root_vhd_size = instance['root_gb'] * 1024 ** 3
if root_vhd_size < base_vhd_size: