Adds local i18n and replaces the usage of nova i18n with the local one. This way, there is one fewer dependencies on nova.
213 lines
7.6 KiB
Python
213 lines
7.6 KiB
Python
# Copyright 2013 Cloudbase Solutions Srl
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""
|
|
Utility class for VHD related operations.
|
|
|
|
Official VHD format specs can be retrieved at:
|
|
http://technet.microsoft.com/en-us/library/bb676673.aspx
|
|
See "Download the Specifications Without Registering"
|
|
|
|
Official VHDX format specs can be retrieved at:
|
|
http://www.microsoft.com/en-us/download/details.aspx?id=34750
|
|
"""
|
|
import struct
|
|
import sys
|
|
|
|
if sys.platform == 'win32':
|
|
import wmi
|
|
|
|
from xml.etree import ElementTree
|
|
|
|
from hyperv.i18n import _
|
|
from hyperv.nova import constants
|
|
from hyperv.nova import vmutils
|
|
|
|
|
|
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
|
|
|
|
VHD_SIGNATURE = 'conectix'
|
|
VHDX_SIGNATURE = 'vhdxfile'
|
|
|
|
|
|
class VHDUtils(object):
|
|
|
|
def __init__(self):
|
|
self._vmutils = vmutils.VMUtils()
|
|
if sys.platform == 'win32':
|
|
self._conn = wmi.WMI(moniker='//./root/virtualization')
|
|
|
|
def validate_vhd(self, vhd_path):
|
|
image_man_svc = self._conn.Msvm_ImageManagementService()[0]
|
|
|
|
(job_path, ret_val) = image_man_svc.ValidateVirtualHardDisk(
|
|
Path=vhd_path)
|
|
self._vmutils.check_ret_val(ret_val, job_path)
|
|
|
|
def create_dynamic_vhd(self, path, max_internal_size, format):
|
|
if format != constants.DISK_FORMAT_VHD:
|
|
raise vmutils.HyperVException(_("Unsupported disk format: %s") %
|
|
format)
|
|
|
|
image_man_svc = self._conn.Msvm_ImageManagementService()[0]
|
|
|
|
(job_path, ret_val) = image_man_svc.CreateDynamicVirtualHardDisk(
|
|
Path=path, MaxInternalSize=max_internal_size)
|
|
self._vmutils.check_ret_val(ret_val, job_path)
|
|
|
|
def create_differencing_vhd(self, path, parent_path):
|
|
image_man_svc = self._conn.Msvm_ImageManagementService()[0]
|
|
|
|
(job_path, ret_val) = image_man_svc.CreateDifferencingVirtualHardDisk(
|
|
Path=path, ParentPath=parent_path)
|
|
self._vmutils.check_ret_val(ret_val, job_path)
|
|
|
|
def reconnect_parent_vhd(self, child_vhd_path, parent_vhd_path):
|
|
image_man_svc = self._conn.Msvm_ImageManagementService()[0]
|
|
|
|
(job_path, ret_val) = image_man_svc.ReconnectParentVirtualHardDisk(
|
|
ChildPath=child_vhd_path,
|
|
ParentPath=parent_vhd_path,
|
|
Force=True)
|
|
self._vmutils.check_ret_val(ret_val, job_path)
|
|
|
|
def merge_vhd(self, src_vhd_path, dest_vhd_path):
|
|
image_man_svc = self._conn.Msvm_ImageManagementService()[0]
|
|
|
|
(job_path, ret_val) = image_man_svc.MergeVirtualHardDisk(
|
|
SourcePath=src_vhd_path,
|
|
DestinationPath=dest_vhd_path)
|
|
self._vmutils.check_ret_val(ret_val, job_path)
|
|
|
|
def _get_resize_method(self):
|
|
image_man_svc = self._conn.Msvm_ImageManagementService()[0]
|
|
return image_man_svc.ExpandVirtualHardDisk
|
|
|
|
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
|
|
|
|
resize = self._get_resize_method()
|
|
|
|
(job_path, ret_val) = resize(
|
|
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:
|
|
vhd_parent = self.get_vhd_parent_path(vhd_path)
|
|
return self.get_internal_vhd_size_by_file_size(vhd_parent,
|
|
new_vhd_file_size)
|
|
|
|
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")
|
|
|
|
def get_vhd_info(self, vhd_path):
|
|
image_man_svc = self._conn.Msvm_ImageManagementService()[0]
|
|
|
|
(vhd_info,
|
|
job_path,
|
|
ret_val) = image_man_svc.GetVirtualHardDiskInfo(vhd_path)
|
|
self._vmutils.check_ret_val(ret_val, job_path)
|
|
|
|
vhd_info_dict = {}
|
|
|
|
et = ElementTree.fromstring(vhd_info)
|
|
for item in et.findall("PROPERTY"):
|
|
name = item.attrib["NAME"]
|
|
value_text = item.find("VALUE").text
|
|
if name == "ParentPath":
|
|
vhd_info_dict[name] = value_text
|
|
elif name in ["FileSize", "MaxInternalSize"]:
|
|
vhd_info_dict[name] = long(value_text)
|
|
elif name in ["InSavedState", "InUse"]:
|
|
vhd_info_dict[name] = bool(value_text)
|
|
elif name == "Type":
|
|
vhd_info_dict[name] = int(value_text)
|
|
|
|
return vhd_info_dict
|
|
|
|
def get_vhd_format(self, path):
|
|
with open(path, 'rb') as f:
|
|
# Read header
|
|
if f.read(8) == VHDX_SIGNATURE:
|
|
return constants.DISK_FORMAT_VHDX
|
|
|
|
# Read footer
|
|
f.seek(0, 2)
|
|
file_size = f.tell()
|
|
if file_size >= 512:
|
|
f.seek(-512, 2)
|
|
if f.read(8) == VHD_SIGNATURE:
|
|
return constants.DISK_FORMAT_VHD
|
|
|
|
raise vmutils.HyperVException(_('Unsupported virtual disk format'))
|
|
|
|
def get_best_supported_vhd_format(self):
|
|
return constants.DISK_FORMAT_VHD
|