¡@

Home 

OpenStack Study: vhdutilsv2.py

OpenStack Index

**** CubicPower OpenStack Study ****

# 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.

Based on the "root/virtualization/v2" namespace available starting with

Hyper-V Server / Windows Server 2012.

"""

import struct

import sys

if sys.platform == 'win32':

import wmi

from nova.openstack.common.gettextutils import _

from nova.openstack.common import units

from nova.virt.hyperv import constants

from nova.virt.hyperv import vhdutils

from nova.virt.hyperv import vmutils

from nova.virt.hyperv import vmutilsv2

from xml.etree import ElementTree

VHDX_BAT_ENTRY_SIZE = 8

VHDX_HEADER_OFFSETS = [64 * units.Ki, 128 * units.Ki]

VHDX_HEADER_SECTION_SIZE = units.Mi

VHDX_LOG_LENGTH_OFFSET = 68

VHDX_METADATA_SIZE_OFFSET = 64

VHDX_REGION_TABLE_OFFSET = 192 * units.Ki

VHDX_BS_METADATA_ENTRY_OFFSET = 48

**** CubicPower OpenStack Study ****

class VHDUtilsV2(vhdutils.VHDUtils):

_VHD_TYPE_DYNAMIC = 3

_VHD_TYPE_DIFFERENCING = 4

_vhd_format_map = {

constants.DISK_FORMAT_VHD: 2,

constants.DISK_FORMAT_VHDX: 3,

}

**** CubicPower OpenStack Study ****

    def __init__(self):

        self._vmutils = vmutilsv2.VMUtilsV2()

        if sys.platform == 'win32':

            self._conn = wmi.WMI(moniker='//./root/virtualization/v2')

**** CubicPower OpenStack Study ****

    def create_dynamic_vhd(self, path, max_internal_size, format):

        vhd_format = self._vhd_format_map.get(format)

        if not vhd_format:

            raise vmutils.HyperVException(_("Unsupported disk format: %s") %

                                          format)

        self._create_vhd(self._VHD_TYPE_DYNAMIC, vhd_format, path,

                         max_internal_size=max_internal_size)

**** CubicPower OpenStack Study ****

    def create_differencing_vhd(self, path, parent_path):

        parent_vhd_info = self.get_vhd_info(parent_path)

        self._create_vhd(self._VHD_TYPE_DIFFERENCING,

                         parent_vhd_info["Format"],

                         path, parent_path=parent_path)

**** CubicPower OpenStack Study ****

    def _create_vhd(self, vhd_type, format, path, max_internal_size=None,

                    parent_path=None):

        vhd_info = self._conn.Msvm_VirtualHardDiskSettingData.new()

        vhd_info.Type = vhd_type

        vhd_info.Format = format

        vhd_info.Path = path

        vhd_info.ParentPath = parent_path

        if max_internal_size:

            vhd_info.MaxInternalSize = max_internal_size

        image_man_svc = self._conn.Msvm_ImageManagementService()[0]

        (job_path, ret_val) = image_man_svc.CreateVirtualHardDisk(

            VirtualDiskSettingData=vhd_info.GetText_(1))

        self._vmutils.check_ret_val(ret_val, job_path)

**** CubicPower OpenStack Study ****

    def reconnect_parent_vhd(self, child_vhd_path, parent_vhd_path):

        image_man_svc = self._conn.Msvm_ImageManagementService()[0]

        vhd_info_xml = self._get_vhd_info_xml(image_man_svc, child_vhd_path)

        # Can't use ".//PROPERTY[@NAME='ParentPath']/VALUE" due to

        # compatibility requirements with Python 2.6

        et = ElementTree.fromstring(vhd_info_xml)

        for item in et.findall("PROPERTY"):

            name = item.attrib["NAME"]

            if name == 'ParentPath':

                item.find("VALUE").text = parent_vhd_path

                break

        vhd_info_xml = ElementTree.tostring(et)

        (job_path, ret_val) = image_man_svc.SetVirtualHardDiskSettingData(

            VirtualDiskSettingData=vhd_info_xml)

        self._vmutils.check_ret_val(ret_val, job_path)

**** CubicPower OpenStack Study ****

    def _get_resize_method(self):

        image_man_svc = self._conn.Msvm_ImageManagementService()[0]

        return image_man_svc.ResizeVirtualHardDisk

**** CubicPower OpenStack Study ****

    def get_internal_vhd_size_by_file_size(self, vhd_path,

                                           new_vhd_file_size):

        """VHDX Size = Header (1 MB)

                        + Log

                        + Metadata Region

                        + BAT

                        + Payload Blocks

            Chunk size = maximum number of bytes described by a SB block

                       = 2 ** 23 * LogicalSectorSize

        """

        vhd_format = self.get_vhd_format(vhd_path)

        if vhd_format == constants.DISK_FORMAT_VHD:

            return super(VHDUtilsV2,

                         self).get_internal_vhd_size_by_file_size(

                            vhd_path, new_vhd_file_size)

        else:

            vhd_info = self.get_vhd_info(vhd_path)

            vhd_type = vhd_info['Type']

            if vhd_type == self._VHD_TYPE_DIFFERENCING:

                raise vmutils.HyperVException(_("Differencing VHDX images "

                                                "are not supported"))

            else:

                try:

                    with open(vhd_path, 'rb') as f:

                        hs = VHDX_HEADER_SECTION_SIZE

                        bes = VHDX_BAT_ENTRY_SIZE

                        lss = vhd_info['LogicalSectorSize']

                        bs = self._get_vhdx_block_size(f)

                        ls = self._get_vhdx_log_size(f)

                        ms = self._get_vhdx_metadata_size_and_offset(f)[0]

                        chunk_ratio = (1 << 23) * lss / bs

                        size = new_vhd_file_size

                        max_internal_size = (bs * chunk_ratio * (size - hs -

                            ls - ms - bes - bes / chunk_ratio) / (bs *

                            chunk_ratio + bes * chunk_ratio + bes))

                        return max_internal_size - (max_internal_size % bs)

                except IOError as ex:

                    raise vmutils.HyperVException(_("Unable to obtain "

                                                    "internal size from VHDX: "

                                                    "%(vhd_path)s. Exception: "

                                                    "%(ex)s") %

                                                    {"vhd_path": vhd_path,

                                                     "ex": ex})

**** CubicPower OpenStack Study ****

    def _get_vhdx_current_header_offset(self, vhdx_file):

        sequence_numbers = []

        for offset in VHDX_HEADER_OFFSETS:

            vhdx_file.seek(offset + 8)

            sequence_numbers.append(struct.unpack('                                    vhdx_file.read(8))[0])

        current_header = sequence_numbers.index(max(sequence_numbers))

        return VHDX_HEADER_OFFSETS[current_header]

**** CubicPower OpenStack Study ****

    def _get_vhdx_log_size(self, vhdx_file):

        current_header_offset = self._get_vhdx_current_header_offset(vhdx_file)

        offset = current_header_offset + VHDX_LOG_LENGTH_OFFSET

        vhdx_file.seek(offset)

        log_size = struct.unpack('        return log_size

**** CubicPower OpenStack Study ****

    def _get_vhdx_metadata_size_and_offset(self, vhdx_file):

        offset = VHDX_METADATA_SIZE_OFFSET + VHDX_REGION_TABLE_OFFSET

        vhdx_file.seek(offset)

        metadata_offset = struct.unpack('        metadata_size = struct.unpack('        return metadata_size, metadata_offset

**** CubicPower OpenStack Study ****

    def _get_vhdx_block_size(self, vhdx_file):

        metadata_offset = self._get_vhdx_metadata_size_and_offset(vhdx_file)[1]

        offset = metadata_offset + VHDX_BS_METADATA_ENTRY_OFFSET

        vhdx_file.seek(offset)

        file_parameter_offset = struct.unpack('

        vhdx_file.seek(file_parameter_offset + metadata_offset)

        block_size = struct.unpack('        return block_size

**** CubicPower OpenStack Study ****

    def _get_vhd_info_xml(self, image_man_svc, vhd_path):

        (job_path,

         ret_val,

         vhd_info_xml) = image_man_svc.GetVirtualHardDiskSettingData(vhd_path)

        self._vmutils.check_ret_val(ret_val, job_path)

        return vhd_info_xml.encode('utf8', 'xmlcharrefreplace')

**** CubicPower OpenStack Study ****

    def get_vhd_info(self, vhd_path):

        image_man_svc = self._conn.Msvm_ImageManagementService()[0]

        vhd_info_xml = self._get_vhd_info_xml(image_man_svc, vhd_path)

        vhd_info_dict = {}

        et = ElementTree.fromstring(vhd_info_xml)

        for item in et.findall("PROPERTY"):

            name = item.attrib["NAME"]

            value_text = item.find("VALUE").text

            if name in ["Path", "ParentPath"]:

                vhd_info_dict[name] = value_text

            elif name in ["BlockSize", "LogicalSectorSize",

                          "PhysicalSectorSize", "MaxInternalSize"]:

                vhd_info_dict[name] = long(value_text)

            elif name in ["Type", "Format"]:

                vhd_info_dict[name] = int(value_text)

        return vhd_info_dict

**** CubicPower OpenStack Study ****

    def get_best_supported_vhd_format(self):

        return constants.DISK_FORMAT_VHDX