From ad6a2cf0f9dfcc24c15884be29212567fcf2694a Mon Sep 17 00:00:00 2001 From: Alessandro Pilotti Date: Thu, 2 Feb 2017 18:01:44 +0200 Subject: [PATCH] Adds plugin for setting SAN policy Adds a plugin to specify what SAN policy is applied by the OS when a new disk is discovered. The behaviour is controlled by the configuration option "san_policy". Possible values are: - Not set (None): no policy configuration is set - OnlineAll: mounts all discovered disks - OfflineShared: mounts all disks except shared ones - OfflineAll: does not mount any discovered disk Change-Id: I484eb72eb81290799032f49eff3b4d2e28b46fe3 Implements: blueprint san-policy Co-Authored-By: Matei-Marius Micu Co-Authored-By: Stefan Caraiman --- cloudbaseinit/conf/default.py | 6 ++ cloudbaseinit/constant.py | 5 + cloudbaseinit/plugins/windows/sanpolicy.py | 48 ++++++++++ .../tests/plugins/windows/test_sanpolicy.py | 94 +++++++++++++++++++ cloudbaseinit/tests/testutils.py | 7 ++ .../utils/windows/storage/test_factory.py | 56 +++++------ .../storage/test_wsm_storage_manager.py | 88 ++++++++++++++++- .../tests/utils/windows/test_security.py | 9 +- cloudbaseinit/utils/windows/kernel32.py | 29 ++++++ cloudbaseinit/utils/windows/storage/base.py | 13 +++ .../utils/windows/storage/factory.py | 1 - .../windows/storage/vds_storage_manager.py | 22 ++++- .../windows/storage/wsm_storage_manager.py | 42 +++++++++ cloudbaseinit/utils/windows/vds.py | 12 +++ 14 files changed, 392 insertions(+), 40 deletions(-) create mode 100644 cloudbaseinit/plugins/windows/sanpolicy.py create mode 100644 cloudbaseinit/tests/plugins/windows/test_sanpolicy.py diff --git a/cloudbaseinit/conf/default.py b/cloudbaseinit/conf/default.py index 63f9fa4f..9d2e8b33 100644 --- a/cloudbaseinit/conf/default.py +++ b/cloudbaseinit/conf/default.py @@ -80,6 +80,12 @@ class GlobalOptions(conf_base.Options): 'By default all the available volumes can be extended. ' 'Volumes must be specified using a comma separated list ' 'of volume indexes, e.g.: "1,2"'), + cfg.StrOpt( + 'san_policy', default=None, + choices=[constant.SAN_POLICY_ONLINE_STR, + constant.SAN_POLICY_OFFLINE_STR, + constant.SAN_POLICY_OFFLINE_SHARED_STR], + help='If not None, the SAN policy is set to the given value'), cfg.StrOpt( 'local_scripts_path', default=None, help='Path location containing scripts to be executed when ' diff --git a/cloudbaseinit/constant.py b/cloudbaseinit/constant.py index 9f21e23c..dd35500c 100644 --- a/cloudbaseinit/constant.py +++ b/cloudbaseinit/constant.py @@ -30,6 +30,11 @@ CD_LOCATIONS = { } POLICY_IGNORE_ALL_FAILURES = "ignoreallfailures" + +SAN_POLICY_ONLINE_STR = 'OnlineAll' +SAN_POLICY_OFFLINE_STR = 'OfflineAll' +SAN_POLICY_OFFLINE_SHARED_STR = 'OfflineShared' + CLEAR_TEXT_INJECTED_ONLY = 'clear_text_injected_only' ALWAYS_CHANGE = 'always' NEVER_CHANGE = 'no' diff --git a/cloudbaseinit/plugins/windows/sanpolicy.py b/cloudbaseinit/plugins/windows/sanpolicy.py new file mode 100644 index 00000000..2d62671e --- /dev/null +++ b/cloudbaseinit/plugins/windows/sanpolicy.py @@ -0,0 +1,48 @@ +# Copyright (c) 2017 Cloudbase Solutions Srl +# +# 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. + +from oslo_log import log as oslo_logging + +from cloudbaseinit import conf as cloudbaseinit_conf +from cloudbaseinit import constant +from cloudbaseinit.plugins.common import base +from cloudbaseinit.utils.windows.storage import base as storage_base +from cloudbaseinit.utils.windows.storage import factory as storage_factory + +CONF = cloudbaseinit_conf.CONF +LOG = oslo_logging.getLogger(__name__) + + +class SANPolicyPlugin(base.BasePlugin): + + def execute(self, service, shared_data): + san_policy_map = { + constant.SAN_POLICY_ONLINE_STR: storage_base.SAN_POLICY_ONLINE, + constant.SAN_POLICY_OFFLINE_STR: storage_base.SAN_POLICY_OFFLINE, + constant.SAN_POLICY_OFFLINE_SHARED_STR: + storage_base.SAN_POLICY_OFFLINE_SHARED, + } + + if CONF.san_policy: + storage_manager = storage_factory.get_storage_manager() + + new_san_policy = san_policy_map[CONF.san_policy] + if storage_manager.get_san_policy() != new_san_policy: + storage_manager.set_san_policy(new_san_policy) + LOG.info("SAN policy set to: %s", new_san_policy) + + return base.PLUGIN_EXECUTION_DONE, False + + def get_os_requirements(self): + return 'win32', (6, 1) diff --git a/cloudbaseinit/tests/plugins/windows/test_sanpolicy.py b/cloudbaseinit/tests/plugins/windows/test_sanpolicy.py new file mode 100644 index 00000000..75f2dc05 --- /dev/null +++ b/cloudbaseinit/tests/plugins/windows/test_sanpolicy.py @@ -0,0 +1,94 @@ +# Copyright 2015 Cloudbase Solutions Srl +# +# 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. + +import unittest + +try: + import unittest.mock as mock +except ImportError: + import mock + +from cloudbaseinit import constant +from cloudbaseinit.plugins.windows import sanpolicy +from cloudbaseinit.tests import testutils +from cloudbaseinit.utils.windows.storage import base as storage_base + + +class SANPolicyPluginTests(unittest.TestCase): + + def setUp(self): + self._san_policy = sanpolicy.SANPolicyPlugin() + self._san_policy_map = { + constant.SAN_POLICY_ONLINE_STR: storage_base.SAN_POLICY_ONLINE, + constant.SAN_POLICY_OFFLINE_STR: storage_base.SAN_POLICY_OFFLINE, + constant.SAN_POLICY_OFFLINE_SHARED_STR: + storage_base.SAN_POLICY_OFFLINE_SHARED, + } + + def test_get_os_requirements(self): + response = self._san_policy.get_os_requirements() + + self.assertEqual(response, ('win32', (6, 1))) + + @mock.patch('cloudbaseinit.utils.windows.storage.factory' + '.get_storage_manager') + def _test_set_policy(self, policy, mock_storage_factory): + mock_storage_manager = mock.MagicMock() + mock_storage_manager.get_san_policy.return_value = "fake policy" + mock_storage_factory.return_value = mock_storage_manager + + with testutils.ConfPatcher('san_policy', policy): + self._san_policy.execute(None, "") + + mock_storage_manager.set_san_policy.assert_called_once_with( + self._san_policy_map[policy]) + + @mock.patch('cloudbaseinit.utils.windows.storage.factory' + '.get_storage_manager') + def _test_set_policy_already_set(self, policy, mock_storage_factory): + mock_storage_manager = mock.MagicMock() + san_policy = self._san_policy_map[policy] + mock_storage_manager.get_san_policy.return_value = san_policy + mock_storage_factory.return_value = mock_storage_manager + + with testutils.ConfPatcher('san_policy', policy): + self._san_policy.execute(None, "") + + self.assertEqual(mock_storage_manager.call_count, 0) + + def test_set_policy_online(self): + self._test_set_policy(constant.SAN_POLICY_ONLINE_STR) + + def test_set_policy_offline(self): + self._test_set_policy(constant.SAN_POLICY_OFFLINE_STR) + + def test_set_policy_offline_shared(self): + self._test_set_policy(constant.SAN_POLICY_OFFLINE_SHARED_STR) + + def test_set_policy_online_already_set(self): + self._test_set_policy_already_set(constant.SAN_POLICY_ONLINE_STR) + + def test_set_policy_offline_already_set(self): + self._test_set_policy_already_set(constant.SAN_POLICY_OFFLINE_STR) + + def test_set_policy_offline_shared_already_set(self): + self._test_set_policy_already_set( + constant.SAN_POLICY_OFFLINE_SHARED_STR) + + @mock.patch('cloudbaseinit.utils.windows.storage.factory' + '.get_storage_manager') + def test_san_policy_not_set(self, mock_storage_factory): + self._san_policy.execute(None, "") + + self.assertEqual(mock_storage_factory.call_count, 0) diff --git a/cloudbaseinit/tests/testutils.py b/cloudbaseinit/tests/testutils.py index 268f7b7d..a3e3303b 100644 --- a/cloudbaseinit/tests/testutils.py +++ b/cloudbaseinit/tests/testutils.py @@ -196,3 +196,10 @@ class CloudbaseInitTestBase(unittest.TestCase): expected_msg = expected_msg % mock_format_error.return_value self.assertEqual(expected_msg, cm.exception.args[0]) + + +class FakeWindowsError(Exception): + """WindowsError is available on Windows only.""" + + def __init__(self, errno): + self.errno = errno diff --git a/cloudbaseinit/tests/utils/windows/storage/test_factory.py b/cloudbaseinit/tests/utils/windows/storage/test_factory.py index d189bf46..da09ce0b 100644 --- a/cloudbaseinit/tests/utils/windows/storage/test_factory.py +++ b/cloudbaseinit/tests/utils/windows/storage/test_factory.py @@ -17,48 +17,44 @@ import ctypes as _ # noqa import importlib import unittest -import mock +try: + import unittest.mock as mock +except ImportError: + import mock + +MODPATH = "cloudbaseinit.utils.windows.storage.factory" class TestStorageManager(unittest.TestCase): def setUp(self): - self.mock_os = mock.MagicMock() - patcher = mock.patch.dict( - "sys.modules", - { - "os": self.mock_os - } - ) - patcher.start() - - self.factory = importlib.import_module( - "cloudbaseinit.utils.windows.storage.factory") - self.addCleanup(patcher.stop) + self.factory = importlib.import_module(MODPATH) @mock.patch("cloudbaseinit.utils.classloader.ClassLoader") @mock.patch("cloudbaseinit.osutils.factory.get_os_utils") def _test_get_storage_manager(self, mock_get_os_utils, mock_class_loader, nano=False, fail=False): if fail: - self.mock_os.name = "linux" - with self.assertRaises(NotImplementedError): - self.factory.get_storage_manager() - return + with mock.patch.object(self.factory, 'os') as os_mock: + os_mock.name = 'linux' + with self.assertRaises(NotImplementedError): + self.factory.get_storage_manager() + return - self.mock_os.name = "nt" - mock_get_os_utils.return_value.is_nano_server.return_value = nano - mock_load_class = mock_class_loader.return_value.load_class - response = self.factory.get_storage_manager() - if nano: - class_path = ("cloudbaseinit.utils.windows.storage." - "wsm_storage_manager.WSMStorageManager") - else: - class_path = ("cloudbaseinit.utils.windows.storage." - "vds_storage_manager.VDSStorageManager") - mock_load_class.assert_called_once_with(class_path) - self.assertEqual(mock_load_class.return_value.return_value, - response) + with mock.patch.object(self.factory, 'os') as os_mock: + os_mock.name = 'nt' + mock_get_os_utils.return_value.is_nano_server.return_value = nano + mock_load_class = mock_class_loader.return_value.load_class + response = self.factory.get_storage_manager() + if nano: + class_path = ("cloudbaseinit.utils.windows.storage." + "wsm_storage_manager.WSMStorageManager") + else: + class_path = ("cloudbaseinit.utils.windows.storage." + "vds_storage_manager.VDSStorageManager") + mock_load_class.assert_called_once_with(class_path) + self.assertEqual(mock_load_class.return_value.return_value, + response) def test_get_storage_manager_fail(self): self._test_get_storage_manager(fail=True) diff --git a/cloudbaseinit/tests/utils/windows/storage/test_wsm_storage_manager.py b/cloudbaseinit/tests/utils/windows/storage/test_wsm_storage_manager.py index 2a75cf8c..a0c2babc 100644 --- a/cloudbaseinit/tests/utils/windows/storage/test_wsm_storage_manager.py +++ b/cloudbaseinit/tests/utils/windows/storage/test_wsm_storage_manager.py @@ -22,23 +22,35 @@ except ImportError: import mock from cloudbaseinit import exception +from cloudbaseinit.tests import testutils +from cloudbaseinit.utils.windows.storage import base class TestWSMStorageManager(unittest.TestCase): def setUp(self): + self._mock_ctypes = mock.MagicMock() self.mock_wmi = mock.MagicMock() + self._moves_mock = mock.MagicMock() + self._winreg_mock = self._moves_mock.winreg + self._kernel32_mock = mock.MagicMock() patcher = mock.patch.dict( "sys.modules", { - "wmi": self.mock_wmi + "wmi": self.mock_wmi, + "six.moves": self._moves_mock, + "ctypes": self._mock_ctypes, + "oslo_log": mock.MagicMock() } ) patcher.start() self.addCleanup(patcher.stop) wsm_store = importlib.import_module( "cloudbaseinit.utils.windows.storage.wsm_storage_manager") + + wsm_store.WindowsError = testutils.FakeWindowsError + wsm_store.kernel32 = self._kernel32_mock self.wsm = wsm_store.WSMStorageManager() def test_init(self): @@ -104,3 +116,77 @@ class TestWSMStorageManager(unittest.TestCase): def test_extend_volumes(self): self._test_extend_volumes() + + def _test_get_san_policy(self, fail=False, errno=None): + key = self._winreg_mock.OpenKey.return_value.__enter__.return_value + + self._winreg_mock.QueryValueEx.return_value = [mock.sentinel.policy] + + error = testutils.FakeWindowsError(None) + error.winerror = errno + + if fail: + self._winreg_mock.QueryValueEx.side_effect = [error] + + if errno != 2: + self.assertRaises(testutils.FakeWindowsError, + self.wsm.get_san_policy) + else: + response = self.wsm.get_san_policy() + self.assertEqual(response, base.SAN_POLICY_OFFLINE_SHARED) + return + + response = self.wsm.get_san_policy() + + self.assertEqual(response, mock.sentinel.policy) + + self._winreg_mock.QueryValueEx.assert_called_once_with( + key, 'SanPolicy') + + self._winreg_mock.OpenKey.assert_called_with( + self._winreg_mock.HKEY_LOCAL_MACHINE, + 'SYSTEM\\CurrentControlSet\\Services\\partmgr\\Parameters') + + def test_get_san_policy(self): + self._test_get_san_policy() + + def test_get_san_policy_fail(self): + self._test_get_san_policy(fail=True, errno=1) + + def test_get_san_policy_not_found(self): + self._test_get_san_policy(fail=True, errno=2) + + def _test_set_san_policy(self, policy=None, error=False, + device_error=False): + if policy != base.SAN_POLICY_ONLINE: + self.assertRaises( + exception.CloudbaseInitException, + self.wsm.set_san_policy, policy) + return + + if error: + mock_filew = self._kernel32_mock.CreateFileW + mock_filew.return_value = self._kernel32_mock.INVALID_HANDLE_VALUE + + self.assertRaises( + exception.WindowsCloudbaseInitException, + self.wsm.set_san_policy, policy) + return + + if device_error: + self._kernel32_mock.DeviceIoControl.return_value = False + self.assertRaises(exception.WindowsCloudbaseInitException, + self.wsm.set_san_policy, policy) + + self._kernel32_mock.CloseHandle.assert_called_once_with( + self._kernel32_mock.CreateFileW()) + + def test_set_san_policy_not_supported(self): + self._test_set_san_policy(policy=base.SAN_POLICY_OFFLINE) + + def test_set_san_policy_error(self): + self._test_set_san_policy(policy=base.SAN_POLICY_ONLINE, error=True) + + def test_set_san_policy_device_error(self): + self._test_set_san_policy(policy=base.SAN_POLICY_ONLINE, + device_error=True) diff --git a/cloudbaseinit/tests/utils/windows/test_security.py b/cloudbaseinit/tests/utils/windows/test_security.py index c7734335..01c3ff91 100644 --- a/cloudbaseinit/tests/utils/windows/test_security.py +++ b/cloudbaseinit/tests/utils/windows/test_security.py @@ -20,12 +20,7 @@ try: except ImportError: import mock - -class FakeWindowsError(Exception): - """WindowsError is available on Windows only.""" - - def __init__(self, errno): - self.errno = errno +from cloudbaseinit.tests import testutils class WindowsSecurityUtilsTests(unittest.TestCase): @@ -42,7 +37,7 @@ class WindowsSecurityUtilsTests(unittest.TestCase): self.security = importlib.import_module( "cloudbaseinit.utils.windows.security") - self.security.WindowsError = FakeWindowsError + self.security.WindowsError = testutils.FakeWindowsError self._security_utils = self.security.WindowsSecurityUtils() diff --git a/cloudbaseinit/utils/windows/kernel32.py b/cloudbaseinit/utils/windows/kernel32.py index 976ab033..b92b371d 100644 --- a/cloudbaseinit/utils/windows/kernel32.py +++ b/cloudbaseinit/utils/windows/kernel32.py @@ -20,6 +20,16 @@ from ctypes import wintypes ERROR_BUFFER_OVERFLOW = 111 ERROR_NO_DATA = 232 +GENERIC_READ = 0x80000000 +GENERIC_WRITE = 0x40000000 + +FILE_SHARE_READ = 1 +FILE_SHARE_WRITE = 2 + +OPEN_EXISTING = 3 + +INVALID_HANDLE_VALUE = wintypes.HANDLE(-1) + class GUID(ctypes.Structure): _fields_ = [ @@ -54,3 +64,22 @@ HeapAlloc.restype = wintypes.LPVOID HeapFree = windll.kernel32.HeapFree HeapFree.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.LPVOID] HeapFree.restype = wintypes.BOOL + +CreateFileW = windll.kernel32.CreateFileW +CreateFileW.argtypes = [wintypes.LPCWSTR, wintypes.DWORD, + wintypes.DWORD, wintypes.LPVOID, + wintypes.DWORD, wintypes.DWORD, + wintypes.HANDLE] +CreateFileW.restype = wintypes.HANDLE + +DeviceIoControl = windll.kernel32.DeviceIoControl +DeviceIoControl.argtypes = [wintypes.HANDLE, wintypes.DWORD, + wintypes.LPVOID, wintypes.DWORD, + wintypes.LPVOID, wintypes.DWORD, + ctypes.POINTER(wintypes.DWORD), + wintypes.LPVOID] +DeviceIoControl.restype = wintypes.BOOL + +CloseHandle = windll.kernel32.CloseHandle +CloseHandle.argtypes = [wintypes.HANDLE] +CloseHandle.restype = wintypes.BOOL diff --git a/cloudbaseinit/utils/windows/storage/base.py b/cloudbaseinit/utils/windows/storage/base.py index 44bd6121..19dffe44 100644 --- a/cloudbaseinit/utils/windows/storage/base.py +++ b/cloudbaseinit/utils/windows/storage/base.py @@ -16,6 +16,11 @@ import abc import six +SAN_POLICY_UNKNOWN = 0 +SAN_POLICY_ONLINE = 1 +SAN_POLICY_OFFLINE_SHARED = 2 +SAN_POLICY_OFFLINE = 3 + @six.add_metaclass(abc.ABCMeta) class BaseStorageManager(object): @@ -23,3 +28,11 @@ class BaseStorageManager(object): @abc.abstractmethod def extend_volumes(self, volume_indexes=None): pass + + @abc.abstractmethod + def get_san_policy(self): + pass + + @abc.abstractmethod + def set_san_policy(self, san_policy): + pass diff --git a/cloudbaseinit/utils/windows/storage/factory.py b/cloudbaseinit/utils/windows/storage/factory.py index 1c33a00a..0f248895 100644 --- a/cloudbaseinit/utils/windows/storage/factory.py +++ b/cloudbaseinit/utils/windows/storage/factory.py @@ -28,7 +28,6 @@ def get_storage_manager(): osutils = osutils_factory.get_os_utils() cl = classloader.ClassLoader() - if os.name == "nt": if osutils.is_nano_server(): # VDS is not available on Nano Server diff --git a/cloudbaseinit/utils/windows/storage/vds_storage_manager.py b/cloudbaseinit/utils/windows/storage/vds_storage_manager.py index 594ee863..3bfbacd0 100644 --- a/cloudbaseinit/utils/windows/storage/vds_storage_manager.py +++ b/cloudbaseinit/utils/windows/storage/vds_storage_manager.py @@ -37,6 +37,16 @@ def _enumerate(query): class VDSStorageManager(base.BaseStorageManager): + + def __init__(self, *args, **kwargs): + super(VDSStorageManager, self).__init__(*args, **kwargs) + self._vds_service = None + + def _get_vds_service(self): + if not self._vds_service: + self._vds_service = vds.load_vds_service() + return self._vds_service + def _extend_volumes(self, pack, volume_indexes): for unk in _enumerate(pack.QueryVolumes()): volume = unk.QueryInterface(vds.IVdsVolume) @@ -128,10 +138,20 @@ class VDSStorageManager(base.BaseStorageManager): for unk in _enumerate(provider.QueryPacks())] def extend_volumes(self, volume_indexes=None): - svc = vds.load_vds_service() + svc = self._get_vds_service() providers = self._query_providers(svc) for provider in providers: packs = self._query_packs(provider) for pack in packs: self._extend_volumes(pack, volume_indexes) + + def get_san_policy(self): + svc = self._get_vds_service() + svc_san = svc.QueryInterface(vds.IVdsServiceSAN) + return svc_san.GetSANPolicy() + + def set_san_policy(self, san_policy): + svc = self._get_vds_service() + svc_san = svc.QueryInterface(vds.IVdsServiceSAN) + svc_san.SetSANPolicy(san_policy) diff --git a/cloudbaseinit/utils/windows/storage/wsm_storage_manager.py b/cloudbaseinit/utils/windows/storage/wsm_storage_manager.py index 76e40550..0179c857 100644 --- a/cloudbaseinit/utils/windows/storage/wsm_storage_manager.py +++ b/cloudbaseinit/utils/windows/storage/wsm_storage_manager.py @@ -12,11 +12,14 @@ # License for the specific language governing permissions and limitations # under the License. +import ctypes import wmi from oslo_log import log as oslo_logging +from six.moves import winreg from cloudbaseinit import exception +from cloudbaseinit.utils.windows import kernel32 from cloudbaseinit.utils.windows.storage import base LOG = oslo_logging.getLogger(__name__) @@ -50,3 +53,42 @@ class WSMStorageManager(base.BaseStorageManager): if ret_val: raise exception.CloudbaseInitException( "Resize failed with error: %s" % ret_val) + + def get_san_policy(self): + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, + 'SYSTEM\\CurrentControlSet\\Services\\partmgr\\' + 'Parameters') as key: + try: + san_policy = winreg.QueryValueEx(key, 'SanPolicy')[0] + except WindowsError as ex: + if ex.winerror != 2: + raise + san_policy = base.SAN_POLICY_OFFLINE_SHARED + return san_policy + + def set_san_policy(self, san_policy): + if san_policy != base.SAN_POLICY_ONLINE: + raise exception.CloudbaseInitException( + "Only SAN_POLICY_ONLINE is currently supported") + handle = kernel32.CreateFileW( + u"\\\\.\\PartmgrControl", + kernel32.GENERIC_READ | kernel32.GENERIC_WRITE, + kernel32.FILE_SHARE_READ | kernel32.FILE_SHARE_WRITE, + None, kernel32.OPEN_EXISTING, 0, None) + + if handle == kernel32.INVALID_HANDLE_VALUE: + raise exception.WindowsCloudbaseInitException( + "Cannot access PartmgrControl: %r") + + try: + input_data_online = ctypes.c_int64(0x100000008) + input_data_size = 8 + control_code = 0x7C204 + + if not kernel32.DeviceIoControl( + handle, control_code, ctypes.addressof(input_data_online), + input_data_size, None, 0, None, None): + raise exception.WindowsCloudbaseInitException( + "DeviceIoControl failed: %r") + finally: + kernel32.CloseHandle(handle) diff --git a/cloudbaseinit/utils/windows/vds.py b/cloudbaseinit/utils/windows/vds.py index 315f33dc..80ccda26 100644 --- a/cloudbaseinit/utils/windows/vds.py +++ b/cloudbaseinit/utils/windows/vds.py @@ -316,6 +316,18 @@ class IVdsVolume(comtypes.IUnknown): ] +class IVdsServiceSAN(comtypes.IUnknown): + _iid_ = comtypes.GUID("{fc5d23e8-a88b-41a5-8de0-2d2f73c5a630}") + + _methods_ = [ + comtypes.COMMETHOD([], comtypes.HRESULT, 'GetSANPolicy', + (['out'], ctypes.POINTER(wintypes.DWORD), + 'pSanPolicy')), + comtypes.COMMETHOD([], comtypes.HRESULT, 'SetSANPolicy', + (['in'], wintypes.DWORD, 'SanPolicy')), + ] + + def load_vds_service(): loader = client.CreateObject(CLSID_VdsLoader, interface=IVdsServiceLoader) svc = loader.LoadService(None)