Allow setting VHD GUIDs
Hyper-V exposes the VHD GUIDs to guests, which can be used to easily identify attached disks. This change will allow setting the VHD GUID for new as well as existing images. Worth mentioning that the Kubernetes Openstack provider will benefit from this. Without it, k8s Persistent Volumes can't be identified by some guests. http://paste.openstack.org/raw/801011/ Change-Id: Ied73997e6f5f3ded9827703867f059ef3dfca159
This commit is contained in:
parent
fa9061f31d
commit
cce95b44a9
|
@ -15,6 +15,7 @@
|
||||||
import ctypes
|
import ctypes
|
||||||
import os
|
import os
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
import uuid
|
||||||
|
|
||||||
import ddt
|
import ddt
|
||||||
import six
|
import six
|
||||||
|
@ -134,6 +135,14 @@ class VHDUtilsTestCase(test_base.BaseTestCase):
|
||||||
self._mock_close.assert_called_once_with(
|
self._mock_close.assert_called_once_with(
|
||||||
mock.sentinel.handle)
|
mock.sentinel.handle)
|
||||||
|
|
||||||
|
def test_guid_from_str(self):
|
||||||
|
buff = list(range(16))
|
||||||
|
py_uuid = uuid.UUID(bytes=bytes(buff))
|
||||||
|
guid = wintypes.GUID.from_str(str(py_uuid))
|
||||||
|
guid_bytes = ctypes.cast(ctypes.byref(guid),
|
||||||
|
ctypes.POINTER(wintypes.BYTE * 16)).contents
|
||||||
|
self.assertEqual(buff, guid_bytes[:])
|
||||||
|
|
||||||
@mock.patch.object(vhdutils.VHDUtils, '_get_vhd_device_id')
|
@mock.patch.object(vhdutils.VHDUtils, '_get_vhd_device_id')
|
||||||
def _test_create_vhd(self, mock_get_dev_id, new_vhd_type):
|
def _test_create_vhd(self, mock_get_dev_id, new_vhd_type):
|
||||||
create_params_struct = (
|
create_params_struct = (
|
||||||
|
@ -151,7 +160,8 @@ class VHDUtilsTestCase(test_base.BaseTestCase):
|
||||||
new_vhd_type=new_vhd_type,
|
new_vhd_type=new_vhd_type,
|
||||||
src_path=mock.sentinel.src_path,
|
src_path=mock.sentinel.src_path,
|
||||||
max_internal_size=mock.sentinel.max_internal_size,
|
max_internal_size=mock.sentinel.max_internal_size,
|
||||||
parent_path=mock.sentinel.parent_path)
|
parent_path=mock.sentinel.parent_path,
|
||||||
|
guid=mock.sentinel.guid)
|
||||||
|
|
||||||
self._fake_vst_struct.assert_called_once_with(
|
self._fake_vst_struct.assert_called_once_with(
|
||||||
DeviceId=mock_get_dev_id.return_value,
|
DeviceId=mock_get_dev_id.return_value,
|
||||||
|
@ -174,6 +184,11 @@ class VHDUtilsTestCase(test_base.BaseTestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
vhdutils.VIRTUAL_DISK_DEFAULT_SECTOR_SIZE,
|
vhdutils.VIRTUAL_DISK_DEFAULT_SECTOR_SIZE,
|
||||||
fake_create_params.Version2.SectorSizeInBytes)
|
fake_create_params.Version2.SectorSizeInBytes)
|
||||||
|
self.assertEqual(
|
||||||
|
vhdutils.wintypes.GUID.from_str.return_value,
|
||||||
|
fake_create_params.Version2.UniqueId)
|
||||||
|
vhdutils.wintypes.GUID.from_str.assert_called_once_with(
|
||||||
|
mock.sentinel.guid)
|
||||||
|
|
||||||
self._mock_run.assert_called_once_with(
|
self._mock_run.assert_called_once_with(
|
||||||
vhdutils.virtdisk.CreateVirtualDisk,
|
vhdutils.virtdisk.CreateVirtualDisk,
|
||||||
|
@ -503,6 +518,44 @@ class VHDUtilsTestCase(test_base.BaseTestCase):
|
||||||
**self._run_args)
|
**self._run_args)
|
||||||
self._mock_close.assert_called_once_with(mock.sentinel.handle)
|
self._mock_close.assert_called_once_with(mock.sentinel.handle)
|
||||||
|
|
||||||
|
@mock.patch.object(vhdutils.VHDUtils, '_open')
|
||||||
|
def test_set_vhd_guid(self, mock_open):
|
||||||
|
set_vdisk_info_struct = (
|
||||||
|
self._vdisk_struct.SET_VIRTUAL_DISK_INFO)
|
||||||
|
open_params_struct = (
|
||||||
|
self._vdisk_struct.OPEN_VIRTUAL_DISK_PARAMETERS)
|
||||||
|
|
||||||
|
fake_set_params = set_vdisk_info_struct.return_value
|
||||||
|
fake_open_params = open_params_struct.return_value
|
||||||
|
mock_open.return_value = mock.sentinel.handle
|
||||||
|
|
||||||
|
self._vhdutils.set_vhd_guid(mock.sentinel.vhd_path,
|
||||||
|
mock.sentinel.guid)
|
||||||
|
|
||||||
|
self.assertEqual(w_const.OPEN_VIRTUAL_DISK_VERSION_2,
|
||||||
|
fake_open_params.Version)
|
||||||
|
self.assertFalse(fake_open_params.Version2.GetInfoOnly)
|
||||||
|
|
||||||
|
self._vhdutils._open.assert_called_once_with(
|
||||||
|
mock.sentinel.vhd_path,
|
||||||
|
open_flag=w_const.OPEN_VIRTUAL_DISK_FLAG_NO_PARENTS,
|
||||||
|
open_access_mask=0,
|
||||||
|
open_params=vhdutils.ctypes.byref(fake_open_params))
|
||||||
|
vhdutils.wintypes.GUID.from_str.assert_called_once_with(
|
||||||
|
mock.sentinel.guid)
|
||||||
|
|
||||||
|
self.assertEqual(w_const.SET_VIRTUAL_DISK_INFO_VIRTUAL_DISK_ID,
|
||||||
|
fake_set_params.Version)
|
||||||
|
self.assertEqual(vhdutils.wintypes.GUID.from_str.return_value,
|
||||||
|
fake_set_params.VirtualDiskId)
|
||||||
|
|
||||||
|
self._mock_run.assert_called_once_with(
|
||||||
|
vhdutils.virtdisk.SetVirtualDiskInformation,
|
||||||
|
mock.sentinel.handle,
|
||||||
|
vhdutils.ctypes.byref(fake_set_params),
|
||||||
|
**self._run_args)
|
||||||
|
self._mock_close.assert_called_once_with(mock.sentinel.handle)
|
||||||
|
|
||||||
@mock.patch.object(vhdutils.VHDUtils, 'get_internal_vhd_size_by_file_size')
|
@mock.patch.object(vhdutils.VHDUtils, 'get_internal_vhd_size_by_file_size')
|
||||||
@mock.patch.object(vhdutils.VHDUtils, '_resize_vhd')
|
@mock.patch.object(vhdutils.VHDUtils, '_resize_vhd')
|
||||||
@mock.patch.object(vhdutils.VHDUtils, '_check_resize_needed')
|
@mock.patch.object(vhdutils.VHDUtils, '_check_resize_needed')
|
||||||
|
|
|
@ -134,7 +134,7 @@ class VHDUtils(object):
|
||||||
self._win32_utils.close_handle(handle)
|
self._win32_utils.close_handle(handle)
|
||||||
|
|
||||||
def create_vhd(self, new_vhd_path, new_vhd_type, src_path=None,
|
def create_vhd(self, new_vhd_path, new_vhd_type, src_path=None,
|
||||||
max_internal_size=0, parent_path=None):
|
max_internal_size=0, parent_path=None, guid=None):
|
||||||
new_device_id = self._get_vhd_device_id(new_vhd_path)
|
new_device_id = self._get_vhd_device_id(new_vhd_path)
|
||||||
|
|
||||||
vst = vdisk_struct.VIRTUAL_STORAGE_TYPE(
|
vst = vdisk_struct.VIRTUAL_STORAGE_TYPE(
|
||||||
|
@ -152,6 +152,8 @@ class VHDUtils(object):
|
||||||
w_const.CREATE_VHD_PARAMS_DEFAULT_BLOCK_SIZE)
|
w_const.CREATE_VHD_PARAMS_DEFAULT_BLOCK_SIZE)
|
||||||
params.Version2.SectorSizeInBytes = (
|
params.Version2.SectorSizeInBytes = (
|
||||||
VIRTUAL_DISK_DEFAULT_SECTOR_SIZE)
|
VIRTUAL_DISK_DEFAULT_SECTOR_SIZE)
|
||||||
|
if guid:
|
||||||
|
params.Version2.UniqueId = wintypes.GUID.from_str(guid)
|
||||||
|
|
||||||
handle = wintypes.HANDLE()
|
handle = wintypes.HANDLE()
|
||||||
create_virtual_disk_flag = CREATE_VIRTUAL_DISK_FLAGS.get(
|
create_virtual_disk_flag = CREATE_VIRTUAL_DISK_FLAGS.get(
|
||||||
|
@ -362,6 +364,27 @@ class VHDUtils(object):
|
||||||
ctypes.byref(params),
|
ctypes.byref(params),
|
||||||
cleanup_handle=handle)
|
cleanup_handle=handle)
|
||||||
|
|
||||||
|
def set_vhd_guid(self, vhd_path, guid):
|
||||||
|
# VHDX parents will not be updated, regardless of the open flag.
|
||||||
|
open_params = vdisk_struct.OPEN_VIRTUAL_DISK_PARAMETERS()
|
||||||
|
open_params.Version = w_const.OPEN_VIRTUAL_DISK_VERSION_2
|
||||||
|
open_params.Version2.GetInfoOnly = False
|
||||||
|
|
||||||
|
handle = self._open(
|
||||||
|
vhd_path,
|
||||||
|
open_flag=w_const.OPEN_VIRTUAL_DISK_FLAG_NO_PARENTS,
|
||||||
|
open_access_mask=0,
|
||||||
|
open_params=ctypes.byref(open_params))
|
||||||
|
|
||||||
|
params = vdisk_struct.SET_VIRTUAL_DISK_INFO()
|
||||||
|
params.Version = w_const.SET_VIRTUAL_DISK_INFO_VIRTUAL_DISK_ID
|
||||||
|
params.VirtualDiskId = wintypes.GUID.from_str(guid)
|
||||||
|
|
||||||
|
self._run_and_check_output(virtdisk.SetVirtualDiskInformation,
|
||||||
|
handle,
|
||||||
|
ctypes.byref(params),
|
||||||
|
cleanup_handle=handle)
|
||||||
|
|
||||||
def resize_vhd(self, vhd_path, new_max_size, is_file_max_size=True,
|
def resize_vhd(self, vhd_path, new_max_size, is_file_max_size=True,
|
||||||
validate_new_size=True):
|
validate_new_size=True):
|
||||||
if is_file_max_size:
|
if is_file_max_size:
|
||||||
|
|
|
@ -330,3 +330,5 @@ GET_VIRTUAL_DISK_INFO_PROVIDER_SUBTYPE = 7
|
||||||
GET_VIRTUAL_DISK_INFO_IS_LOADED = 13
|
GET_VIRTUAL_DISK_INFO_IS_LOADED = 13
|
||||||
|
|
||||||
SET_VIRTUAL_DISK_INFO_PARENT_PATH = 1
|
SET_VIRTUAL_DISK_INFO_PARENT_PATH = 1
|
||||||
|
SET_VIRTUAL_DISK_INFO_IDENTIFIER = 2
|
||||||
|
SET_VIRTUAL_DISK_INFO_VIRTUAL_DISK_ID = 5
|
||||||
|
|
|
@ -180,11 +180,19 @@ class GET_VIRTUAL_DISK_INFO(ctypes.Structure):
|
||||||
PGET_VIRTUAL_DISK_INFO = ctypes.POINTER(GET_VIRTUAL_DISK_INFO)
|
PGET_VIRTUAL_DISK_INFO = ctypes.POINTER(GET_VIRTUAL_DISK_INFO)
|
||||||
|
|
||||||
|
|
||||||
# Only this version is used, we avoid defining a union.
|
class _SET_VIRTUAL_DISK_INFO_U(ctypes.Union):
|
||||||
|
_fields_ = [
|
||||||
|
('ParentFilePath', wintypes.LPCWSTR),
|
||||||
|
('UniqueIdentifier', wintypes.GUID),
|
||||||
|
('VirtualDiskId', wintypes.GUID),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class SET_VIRTUAL_DISK_INFO(ctypes.Structure):
|
class SET_VIRTUAL_DISK_INFO(ctypes.Structure):
|
||||||
|
_anonymous_ = ['_setinfo']
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('Version', wintypes.DWORD),
|
('Version', wintypes.DWORD),
|
||||||
('ParentFilePath', wintypes.LPCWSTR)
|
('_setinfo', _SET_VIRTUAL_DISK_INFO_U)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
import ctypes
|
import ctypes
|
||||||
import sys
|
import sys
|
||||||
|
import uuid
|
||||||
|
|
||||||
BYTE = ctypes.c_byte
|
BYTE = ctypes.c_byte
|
||||||
WORD = ctypes.c_ushort
|
WORD = ctypes.c_ushort
|
||||||
|
@ -83,6 +84,12 @@ class GUID(ctypes.Structure):
|
||||||
("Data4", BYTE * 8)
|
("Data4", BYTE * 8)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_str(cls, guid_str):
|
||||||
|
py_uuid = uuid.UUID(guid_str)
|
||||||
|
uuid_buff = (BYTE * 16)(*py_uuid.bytes)
|
||||||
|
return ctypes.cast(uuid_buff, ctypes.POINTER(GUID)).contents
|
||||||
|
|
||||||
|
|
||||||
class OVERLAPPED(ctypes.Structure):
|
class OVERLAPPED(ctypes.Structure):
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
|
|
Loading…
Reference in New Issue