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 <mmicu@cloudbasesolutions.com> Co-Authored-By: Stefan Caraiman <scaraiman@cloudbasesolutions.com>
This commit is contained in:
parent
c2db56a10b
commit
ad6a2cf0f9
@ -80,6 +80,12 @@ class GlobalOptions(conf_base.Options):
|
|||||||
'By default all the available volumes can be extended. '
|
'By default all the available volumes can be extended. '
|
||||||
'Volumes must be specified using a comma separated list '
|
'Volumes must be specified using a comma separated list '
|
||||||
'of volume indexes, e.g.: "1,2"'),
|
'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(
|
cfg.StrOpt(
|
||||||
'local_scripts_path', default=None,
|
'local_scripts_path', default=None,
|
||||||
help='Path location containing scripts to be executed when '
|
help='Path location containing scripts to be executed when '
|
||||||
|
@ -30,6 +30,11 @@ CD_LOCATIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
POLICY_IGNORE_ALL_FAILURES = "ignoreallfailures"
|
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'
|
CLEAR_TEXT_INJECTED_ONLY = 'clear_text_injected_only'
|
||||||
ALWAYS_CHANGE = 'always'
|
ALWAYS_CHANGE = 'always'
|
||||||
NEVER_CHANGE = 'no'
|
NEVER_CHANGE = 'no'
|
||||||
|
48
cloudbaseinit/plugins/windows/sanpolicy.py
Normal file
48
cloudbaseinit/plugins/windows/sanpolicy.py
Normal file
@ -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)
|
94
cloudbaseinit/tests/plugins/windows/test_sanpolicy.py
Normal file
94
cloudbaseinit/tests/plugins/windows/test_sanpolicy.py
Normal file
@ -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)
|
@ -196,3 +196,10 @@ class CloudbaseInitTestBase(unittest.TestCase):
|
|||||||
|
|
||||||
expected_msg = expected_msg % mock_format_error.return_value
|
expected_msg = expected_msg % mock_format_error.return_value
|
||||||
self.assertEqual(expected_msg, cm.exception.args[0])
|
self.assertEqual(expected_msg, cm.exception.args[0])
|
||||||
|
|
||||||
|
|
||||||
|
class FakeWindowsError(Exception):
|
||||||
|
"""WindowsError is available on Windows only."""
|
||||||
|
|
||||||
|
def __init__(self, errno):
|
||||||
|
self.errno = errno
|
||||||
|
@ -17,36 +17,32 @@ import ctypes as _ # noqa
|
|||||||
import importlib
|
import importlib
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
try:
|
||||||
|
import unittest.mock as mock
|
||||||
|
except ImportError:
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
MODPATH = "cloudbaseinit.utils.windows.storage.factory"
|
||||||
|
|
||||||
|
|
||||||
class TestStorageManager(unittest.TestCase):
|
class TestStorageManager(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.mock_os = mock.MagicMock()
|
self.factory = importlib.import_module(MODPATH)
|
||||||
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)
|
|
||||||
|
|
||||||
@mock.patch("cloudbaseinit.utils.classloader.ClassLoader")
|
@mock.patch("cloudbaseinit.utils.classloader.ClassLoader")
|
||||||
@mock.patch("cloudbaseinit.osutils.factory.get_os_utils")
|
@mock.patch("cloudbaseinit.osutils.factory.get_os_utils")
|
||||||
def _test_get_storage_manager(self, mock_get_os_utils, mock_class_loader,
|
def _test_get_storage_manager(self, mock_get_os_utils, mock_class_loader,
|
||||||
nano=False, fail=False):
|
nano=False, fail=False):
|
||||||
if fail:
|
if fail:
|
||||||
self.mock_os.name = "linux"
|
with mock.patch.object(self.factory, 'os') as os_mock:
|
||||||
|
os_mock.name = 'linux'
|
||||||
with self.assertRaises(NotImplementedError):
|
with self.assertRaises(NotImplementedError):
|
||||||
self.factory.get_storage_manager()
|
self.factory.get_storage_manager()
|
||||||
return
|
return
|
||||||
|
|
||||||
self.mock_os.name = "nt"
|
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_get_os_utils.return_value.is_nano_server.return_value = nano
|
||||||
mock_load_class = mock_class_loader.return_value.load_class
|
mock_load_class = mock_class_loader.return_value.load_class
|
||||||
response = self.factory.get_storage_manager()
|
response = self.factory.get_storage_manager()
|
||||||
|
@ -22,23 +22,35 @@ except ImportError:
|
|||||||
import mock
|
import mock
|
||||||
|
|
||||||
from cloudbaseinit import exception
|
from cloudbaseinit import exception
|
||||||
|
from cloudbaseinit.tests import testutils
|
||||||
|
from cloudbaseinit.utils.windows.storage import base
|
||||||
|
|
||||||
|
|
||||||
class TestWSMStorageManager(unittest.TestCase):
|
class TestWSMStorageManager(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
self._mock_ctypes = mock.MagicMock()
|
||||||
self.mock_wmi = 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(
|
patcher = mock.patch.dict(
|
||||||
"sys.modules",
|
"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()
|
patcher.start()
|
||||||
self.addCleanup(patcher.stop)
|
self.addCleanup(patcher.stop)
|
||||||
wsm_store = importlib.import_module(
|
wsm_store = importlib.import_module(
|
||||||
"cloudbaseinit.utils.windows.storage.wsm_storage_manager")
|
"cloudbaseinit.utils.windows.storage.wsm_storage_manager")
|
||||||
|
|
||||||
|
wsm_store.WindowsError = testutils.FakeWindowsError
|
||||||
|
wsm_store.kernel32 = self._kernel32_mock
|
||||||
self.wsm = wsm_store.WSMStorageManager()
|
self.wsm = wsm_store.WSMStorageManager()
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
@ -104,3 +116,77 @@ class TestWSMStorageManager(unittest.TestCase):
|
|||||||
|
|
||||||
def test_extend_volumes(self):
|
def test_extend_volumes(self):
|
||||||
self._test_extend_volumes()
|
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)
|
||||||
|
@ -20,12 +20,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
from cloudbaseinit.tests import testutils
|
||||||
class FakeWindowsError(Exception):
|
|
||||||
"""WindowsError is available on Windows only."""
|
|
||||||
|
|
||||||
def __init__(self, errno):
|
|
||||||
self.errno = errno
|
|
||||||
|
|
||||||
|
|
||||||
class WindowsSecurityUtilsTests(unittest.TestCase):
|
class WindowsSecurityUtilsTests(unittest.TestCase):
|
||||||
@ -42,7 +37,7 @@ class WindowsSecurityUtilsTests(unittest.TestCase):
|
|||||||
|
|
||||||
self.security = importlib.import_module(
|
self.security = importlib.import_module(
|
||||||
"cloudbaseinit.utils.windows.security")
|
"cloudbaseinit.utils.windows.security")
|
||||||
self.security.WindowsError = FakeWindowsError
|
self.security.WindowsError = testutils.FakeWindowsError
|
||||||
|
|
||||||
self._security_utils = self.security.WindowsSecurityUtils()
|
self._security_utils = self.security.WindowsSecurityUtils()
|
||||||
|
|
||||||
|
@ -20,6 +20,16 @@ from ctypes import wintypes
|
|||||||
ERROR_BUFFER_OVERFLOW = 111
|
ERROR_BUFFER_OVERFLOW = 111
|
||||||
ERROR_NO_DATA = 232
|
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):
|
class GUID(ctypes.Structure):
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
@ -54,3 +64,22 @@ HeapAlloc.restype = wintypes.LPVOID
|
|||||||
HeapFree = windll.kernel32.HeapFree
|
HeapFree = windll.kernel32.HeapFree
|
||||||
HeapFree.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.LPVOID]
|
HeapFree.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.LPVOID]
|
||||||
HeapFree.restype = wintypes.BOOL
|
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
|
||||||
|
@ -16,6 +16,11 @@ import abc
|
|||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
SAN_POLICY_UNKNOWN = 0
|
||||||
|
SAN_POLICY_ONLINE = 1
|
||||||
|
SAN_POLICY_OFFLINE_SHARED = 2
|
||||||
|
SAN_POLICY_OFFLINE = 3
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class BaseStorageManager(object):
|
class BaseStorageManager(object):
|
||||||
@ -23,3 +28,11 @@ class BaseStorageManager(object):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def extend_volumes(self, volume_indexes=None):
|
def extend_volumes(self, volume_indexes=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_san_policy(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def set_san_policy(self, san_policy):
|
||||||
|
pass
|
||||||
|
@ -28,7 +28,6 @@ def get_storage_manager():
|
|||||||
|
|
||||||
osutils = osutils_factory.get_os_utils()
|
osutils = osutils_factory.get_os_utils()
|
||||||
cl = classloader.ClassLoader()
|
cl = classloader.ClassLoader()
|
||||||
|
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
if osutils.is_nano_server():
|
if osutils.is_nano_server():
|
||||||
# VDS is not available on Nano Server
|
# VDS is not available on Nano Server
|
||||||
|
@ -37,6 +37,16 @@ def _enumerate(query):
|
|||||||
|
|
||||||
|
|
||||||
class VDSStorageManager(base.BaseStorageManager):
|
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):
|
def _extend_volumes(self, pack, volume_indexes):
|
||||||
for unk in _enumerate(pack.QueryVolumes()):
|
for unk in _enumerate(pack.QueryVolumes()):
|
||||||
volume = unk.QueryInterface(vds.IVdsVolume)
|
volume = unk.QueryInterface(vds.IVdsVolume)
|
||||||
@ -128,10 +138,20 @@ class VDSStorageManager(base.BaseStorageManager):
|
|||||||
for unk in _enumerate(provider.QueryPacks())]
|
for unk in _enumerate(provider.QueryPacks())]
|
||||||
|
|
||||||
def extend_volumes(self, volume_indexes=None):
|
def extend_volumes(self, volume_indexes=None):
|
||||||
svc = vds.load_vds_service()
|
svc = self._get_vds_service()
|
||||||
providers = self._query_providers(svc)
|
providers = self._query_providers(svc)
|
||||||
|
|
||||||
for provider in providers:
|
for provider in providers:
|
||||||
packs = self._query_packs(provider)
|
packs = self._query_packs(provider)
|
||||||
for pack in packs:
|
for pack in packs:
|
||||||
self._extend_volumes(pack, volume_indexes)
|
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)
|
||||||
|
@ -12,11 +12,14 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import ctypes
|
||||||
import wmi
|
import wmi
|
||||||
|
|
||||||
from oslo_log import log as oslo_logging
|
from oslo_log import log as oslo_logging
|
||||||
|
from six.moves import winreg
|
||||||
|
|
||||||
from cloudbaseinit import exception
|
from cloudbaseinit import exception
|
||||||
|
from cloudbaseinit.utils.windows import kernel32
|
||||||
from cloudbaseinit.utils.windows.storage import base
|
from cloudbaseinit.utils.windows.storage import base
|
||||||
|
|
||||||
LOG = oslo_logging.getLogger(__name__)
|
LOG = oslo_logging.getLogger(__name__)
|
||||||
@ -50,3 +53,42 @@ class WSMStorageManager(base.BaseStorageManager):
|
|||||||
if ret_val:
|
if ret_val:
|
||||||
raise exception.CloudbaseInitException(
|
raise exception.CloudbaseInitException(
|
||||||
"Resize failed with error: %s" % ret_val)
|
"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)
|
||||||
|
@ -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():
|
def load_vds_service():
|
||||||
loader = client.CreateObject(CLSID_VdsLoader, interface=IVdsServiceLoader)
|
loader = client.CreateObject(CLSID_VdsLoader, interface=IVdsServiceLoader)
|
||||||
svc = loader.LoadService(None)
|
svc = loader.LoadService(None)
|
||||||
|
Loading…
Reference in New Issue
Block a user