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 os
|
||||
from unittest import mock
|
||||
import uuid
|
||||
|
||||
import ddt
|
||||
import six
|
||||
|
@ -134,6 +135,14 @@ class VHDUtilsTestCase(test_base.BaseTestCase):
|
|||
self._mock_close.assert_called_once_with(
|
||||
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')
|
||||
def _test_create_vhd(self, mock_get_dev_id, new_vhd_type):
|
||||
create_params_struct = (
|
||||
|
@ -151,7 +160,8 @@ class VHDUtilsTestCase(test_base.BaseTestCase):
|
|||
new_vhd_type=new_vhd_type,
|
||||
src_path=mock.sentinel.src_path,
|
||||
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(
|
||||
DeviceId=mock_get_dev_id.return_value,
|
||||
|
@ -174,6 +184,11 @@ class VHDUtilsTestCase(test_base.BaseTestCase):
|
|||
self.assertEqual(
|
||||
vhdutils.VIRTUAL_DISK_DEFAULT_SECTOR_SIZE,
|
||||
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(
|
||||
vhdutils.virtdisk.CreateVirtualDisk,
|
||||
|
@ -503,6 +518,44 @@ class VHDUtilsTestCase(test_base.BaseTestCase):
|
|||
**self._run_args)
|
||||
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, '_resize_vhd')
|
||||
@mock.patch.object(vhdutils.VHDUtils, '_check_resize_needed')
|
||||
|
|
|
@ -134,7 +134,7 @@ class VHDUtils(object):
|
|||
self._win32_utils.close_handle(handle)
|
||||
|
||||
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)
|
||||
|
||||
vst = vdisk_struct.VIRTUAL_STORAGE_TYPE(
|
||||
|
@ -152,6 +152,8 @@ class VHDUtils(object):
|
|||
w_const.CREATE_VHD_PARAMS_DEFAULT_BLOCK_SIZE)
|
||||
params.Version2.SectorSizeInBytes = (
|
||||
VIRTUAL_DISK_DEFAULT_SECTOR_SIZE)
|
||||
if guid:
|
||||
params.Version2.UniqueId = wintypes.GUID.from_str(guid)
|
||||
|
||||
handle = wintypes.HANDLE()
|
||||
create_virtual_disk_flag = CREATE_VIRTUAL_DISK_FLAGS.get(
|
||||
|
@ -362,6 +364,27 @@ class VHDUtils(object):
|
|||
ctypes.byref(params),
|
||||
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,
|
||||
validate_new_size=True):
|
||||
if is_file_max_size:
|
||||
|
|
|
@ -330,3 +330,5 @@ GET_VIRTUAL_DISK_INFO_PROVIDER_SUBTYPE = 7
|
|||
GET_VIRTUAL_DISK_INFO_IS_LOADED = 13
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# 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):
|
||||
_anonymous_ = ['_setinfo']
|
||||
_fields_ = [
|
||||
('Version', wintypes.DWORD),
|
||||
('ParentFilePath', wintypes.LPCWSTR)
|
||||
('_setinfo', _SET_VIRTUAL_DISK_INFO_U)
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
import ctypes
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
BYTE = ctypes.c_byte
|
||||
WORD = ctypes.c_ushort
|
||||
|
@ -83,6 +84,12 @@ class GUID(ctypes.Structure):
|
|||
("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):
|
||||
_fields_ = [
|
||||
|
|
Loading…
Reference in New Issue