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:
parent
2f6022f6bf
commit
690cf0bdbe
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -72,3 +72,6 @@ IDE_DVD = "DVD"
|
|||
|
||||
DISK_FORMAT_VHD = "VHD"
|
||||
DISK_FORMAT_VHDX = "VHDX"
|
||||
|
||||
VHD_TYPE_FIXED = 2
|
||||
VHD_TYPE_DYNAMIC = 3
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue