Adds Windows Storage Manager API support
This is needed on Nano Server where VDS is not available. The logic for extending the volumes was decoupled from the plugin to a windows storage utility (VDS) alongside other extending utility which is available on Nano and versions greater than ws2012/w8 (WSM). According to the current Windows version, a suitable utility for extending will be loaded through a factory manager. Implements: blueprint windows-storage-manager Co-Authored-By: Cosmin Poieana <cpoieana@cloudbasesolutions.com> Change-Id: I7c8eea35a632c812e99808befb5e7528d66363cc
This commit is contained in:
parent
98e1248e69
commit
dd6b4d74c5
@ -12,18 +12,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ctypes
|
||||
import re
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as oslo_logging
|
||||
|
||||
from cloudbaseinit.plugins.common import base
|
||||
from cloudbaseinit.utils.windows import vds
|
||||
|
||||
ole32 = ctypes.windll.ole32
|
||||
ole32.CoTaskMemFree.restype = None
|
||||
ole32.CoTaskMemFree.argtypes = [ctypes.c_void_p]
|
||||
from cloudbaseinit.utils.windows.storage import factory as storage_factory
|
||||
|
||||
opts = [
|
||||
cfg.ListOpt('volumes_to_extend',
|
||||
@ -38,134 +30,16 @@ opts = [
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(opts)
|
||||
|
||||
LOG = oslo_logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ExtendVolumesPlugin(base.BasePlugin):
|
||||
|
||||
def _extend_volumes(self, pack, volume_idxs=None):
|
||||
enum = pack.QueryVolumes()
|
||||
while True:
|
||||
(unk, c) = enum.Next(1)
|
||||
if not c:
|
||||
break
|
||||
volume = unk.QueryInterface(vds.IVdsVolume)
|
||||
volume_prop = volume.GetProperties()
|
||||
try:
|
||||
extend_volume = True
|
||||
if volume_idxs is not None:
|
||||
volume_name = ctypes.wstring_at(volume_prop.pwszName)
|
||||
volume_idx = self._get_volume_index(volume_name)
|
||||
if volume_idx not in volume_idxs:
|
||||
extend_volume = False
|
||||
|
||||
if extend_volume:
|
||||
self._extend_volume(pack, volume, volume_prop)
|
||||
finally:
|
||||
ole32.CoTaskMemFree(volume_prop.pwszName)
|
||||
|
||||
def _get_volume_index(self, volume_name):
|
||||
m = re.match(r"[^0-9]+([0-9]+)$", volume_name)
|
||||
if m:
|
||||
return int(m.group(1))
|
||||
|
||||
def _extend_volume(self, pack, volume, volume_prop):
|
||||
volume_extents = self._get_volume_extents_to_resize(pack,
|
||||
volume_prop.id)
|
||||
input_disks = []
|
||||
|
||||
for (volume_extent, volume_extend_size) in volume_extents:
|
||||
input_disk = vds.VDS_INPUT_DISK()
|
||||
input_disks.append(input_disk)
|
||||
|
||||
input_disk.diskId = volume_extent.diskId
|
||||
input_disk.memberIdx = volume_extent.memberIdx
|
||||
input_disk.plexId = volume_extent.plexId
|
||||
input_disk.ullSize = volume_extend_size
|
||||
|
||||
if input_disks:
|
||||
extend_size = sum([i.ullSize for i in input_disks])
|
||||
volume_name = ctypes.wstring_at(volume_prop.pwszName)
|
||||
LOG.info('Extending volume "%s" with %s bytes' %
|
||||
(volume_name, extend_size))
|
||||
|
||||
input_disks_ar = (vds.VDS_INPUT_DISK *
|
||||
len(input_disks))(*input_disks)
|
||||
async = volume.Extend(input_disks_ar, len(input_disks))
|
||||
async.Wait()
|
||||
|
||||
def _get_volume_extents_to_resize(self, pack, volume_id):
|
||||
volume_extents = []
|
||||
|
||||
enum = pack.QueryDisks()
|
||||
while True:
|
||||
(unk, c) = enum.Next(1)
|
||||
if not c:
|
||||
break
|
||||
disk = unk.QueryInterface(vds.IVdsDisk)
|
||||
|
||||
(extents_p, num_extents) = disk.QueryExtents()
|
||||
try:
|
||||
extents_array_type = vds.VDS_DISK_EXTENT * num_extents
|
||||
extents_array = extents_array_type.from_address(
|
||||
ctypes.addressof(extents_p.contents))
|
||||
|
||||
volume_extent_extend_size = None
|
||||
|
||||
for extent in extents_array:
|
||||
if extent.volumeId == volume_id:
|
||||
# Copy the extent in order to return it safely
|
||||
# after the source is deallocated
|
||||
extent_copy = vds.VDS_DISK_EXTENT()
|
||||
ctypes.pointer(extent_copy)[0] = extent
|
||||
|
||||
volume_extent_extend_size = [extent_copy, 0]
|
||||
volume_extents.append(volume_extent_extend_size)
|
||||
elif (volume_extent_extend_size and
|
||||
extent.type == vds.VDS_DET_FREE):
|
||||
volume_extent_extend_size[1] += extent.ullSize
|
||||
else:
|
||||
volume_extent_extend_size = None
|
||||
finally:
|
||||
ole32.CoTaskMemFree(extents_p)
|
||||
|
||||
# Return only the extents that need to be resized
|
||||
return [ve for ve in volume_extents if ve[1] > 0]
|
||||
|
||||
def _query_providers(self, svc):
|
||||
providers = []
|
||||
enum = svc.QueryProviders(vds.VDS_QUERY_SOFTWARE_PROVIDERS)
|
||||
while True:
|
||||
(unk, c) = enum.Next(1)
|
||||
if not c:
|
||||
break
|
||||
providers.append(unk.QueryInterface(vds.IVdsSwProvider))
|
||||
return providers
|
||||
|
||||
def _query_packs(self, provider):
|
||||
packs = []
|
||||
enum = provider.QueryPacks()
|
||||
while True:
|
||||
(unk, c) = enum.Next(1)
|
||||
if not c:
|
||||
break
|
||||
packs.append(unk.QueryInterface(vds.IVdsPack))
|
||||
return packs
|
||||
|
||||
def _get_volumes_to_extend(self):
|
||||
if CONF.volumes_to_extend is not None:
|
||||
return list(map(int, CONF.volumes_to_extend))
|
||||
|
||||
def execute(self, service, shared_data):
|
||||
svc = vds.load_vds_service()
|
||||
providers = self._query_providers(svc)
|
||||
|
||||
volumes_to_extend = self._get_volumes_to_extend()
|
||||
|
||||
for provider in providers:
|
||||
packs = self._query_packs(provider)
|
||||
for pack in packs:
|
||||
self._extend_volumes(pack, volumes_to_extend)
|
||||
volumes_indexes = self._get_volumes_to_extend()
|
||||
storage_manager = storage_factory.get_storage_manager()
|
||||
storage_manager.extend_volumes(volumes_indexes)
|
||||
|
||||
return base.PLUGIN_EXECUTE_ON_NEXT_BOOT, False
|
||||
|
||||
|
@ -12,8 +12,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import importlib
|
||||
import re
|
||||
import unittest
|
||||
|
||||
try:
|
||||
@ -21,10 +21,12 @@ try:
|
||||
except ImportError:
|
||||
import mock
|
||||
|
||||
from cloudbaseinit.plugins.common import base
|
||||
from cloudbaseinit.tests import testutils
|
||||
|
||||
|
||||
class ExtendVolumesPluginTests(unittest.TestCase):
|
||||
class TestExtendVolumesPlugin(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._ctypes_mock = mock.MagicMock()
|
||||
self._comtypes_mock = mock.MagicMock()
|
||||
@ -35,182 +37,33 @@ class ExtendVolumesPluginTests(unittest.TestCase):
|
||||
'ctypes': self._ctypes_mock})
|
||||
|
||||
self._module_patcher.start()
|
||||
self.addCleanup(self._module_patcher.stop)
|
||||
|
||||
extendvolumes = importlib.import_module('cloudbaseinit.plugins.'
|
||||
'windows.extendvolumes')
|
||||
self._extend_volumes = extendvolumes.ExtendVolumesPlugin()
|
||||
|
||||
def tearDown(self):
|
||||
self._module_patcher.stop()
|
||||
|
||||
@mock.patch('cloudbaseinit.plugins.windows.extendvolumes'
|
||||
'.ExtendVolumesPlugin._get_volume_index')
|
||||
@mock.patch('cloudbaseinit.plugins.windows.extendvolumes'
|
||||
'.ExtendVolumesPlugin._extend_volume')
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.IVdsVolume')
|
||||
def test_extend_volumes(self, _vds_mock, mock_extend_volume,
|
||||
mock_get_volume_index):
|
||||
mock_pack = mock.MagicMock()
|
||||
mock_volume_idxs = mock.MagicMock()
|
||||
mock_enum = mock.MagicMock()
|
||||
mock_unk = mock.MagicMock()
|
||||
mock_c = mock.MagicMock()
|
||||
mock_volume = mock.MagicMock()
|
||||
mock_properties = mock.MagicMock()
|
||||
mock_pack.QueryVolumes.return_value = mock_enum
|
||||
mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
|
||||
mock_unk.QueryInterface.return_value = mock_volume
|
||||
mock_volume.GetProperties.return_value = mock_properties
|
||||
self._ctypes_mock.wstring_at.return_value = 'fake name'
|
||||
mock_get_volume_index.return_value = mock_volume_idxs
|
||||
self._extend_volumes._extend_volumes(mock_pack, [mock_volume_idxs])
|
||||
mock_pack.QueryVolumes.assert_called_once_with()
|
||||
mock_enum.Next.assert_called_with(1)
|
||||
mock_unk.QueryInterface.assert_called_once_with(_vds_mock)
|
||||
mock_volume.GetProperties.assert_called_once_with()
|
||||
self._ctypes_mock.wstring_at.assert_called_with(
|
||||
mock_properties.pwszName)
|
||||
mock_get_volume_index.assert_called_once_with('fake name')
|
||||
mock_extend_volume.assert_called_once_with(mock_pack, mock_volume,
|
||||
mock_properties)
|
||||
self._ctypes_mock.windll.ole32.CoTaskMemFree.assert_called_once_with(
|
||||
mock_properties.pwszName)
|
||||
|
||||
def test_get_volume_index(self):
|
||||
mock_value = mock.MagicMock()
|
||||
re.match = mock.MagicMock(return_value=mock_value)
|
||||
mock_value.group.return_value = '9999'
|
||||
response = self._extend_volumes._get_volume_index('$2')
|
||||
mock_value.group.assert_called_once_with(1)
|
||||
self.assertTrue(response == 9999)
|
||||
|
||||
@mock.patch('cloudbaseinit.plugins.windows.extendvolumes'
|
||||
'.ExtendVolumesPlugin._get_volume_extents_to_resize')
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.VDS_INPUT_DISK')
|
||||
def test_extend_volume(self, mock_VDS_INPUT_DISK,
|
||||
mock_get_volume_extents_to_resize):
|
||||
mock_disk = mock.MagicMock()
|
||||
mock_pack = mock.MagicMock()
|
||||
mock_volume = mock.MagicMock()
|
||||
mock_properties = mock.MagicMock()
|
||||
mock_volume_extent = mock.MagicMock()
|
||||
mock_async = mock.MagicMock()
|
||||
mock_get_volume_extents_to_resize.return_value = [(mock_volume_extent,
|
||||
9999)]
|
||||
mock_VDS_INPUT_DISK.return_value = mock_disk
|
||||
mock_volume.Extend.return_value = mock_async
|
||||
|
||||
self._extend_volumes._extend_volume(mock_pack, mock_volume,
|
||||
mock_properties)
|
||||
|
||||
mock_get_volume_extents_to_resize.assert_called_once_with(
|
||||
mock_pack, mock_properties.id)
|
||||
self._ctypes_mock.wstring_at.assert_called_with(
|
||||
mock_properties.pwszName)
|
||||
mock_volume.Extend.assert_called_once_with(
|
||||
mock_VDS_INPUT_DISK.__mul__()(), 1)
|
||||
mock_async.Wait.assert_called_once_with()
|
||||
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.IVdsDisk')
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.VDS_DISK_EXTENT')
|
||||
def test_get_volume_extents_to_resize(self, mock_VDS_DISK_EXTENT,
|
||||
mock_IVdsDisk):
|
||||
mock_pack = mock.MagicMock()
|
||||
mock_extents_p = mock.MagicMock()
|
||||
mock_unk = mock.MagicMock()
|
||||
mock_c = mock.MagicMock()
|
||||
mock_disk = mock.MagicMock()
|
||||
mock_enum = mock.MagicMock()
|
||||
fake_volume_id = '$1'
|
||||
mock_array = mock.MagicMock()
|
||||
mock_array.volumeId = fake_volume_id
|
||||
mock_pack.QueryDisks.return_value = mock_enum
|
||||
mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
|
||||
mock_unk.QueryInterface.return_value = mock_disk
|
||||
mock_disk.QueryExtents.return_value = (mock_extents_p,
|
||||
1)
|
||||
mock_VDS_DISK_EXTENT.__mul__().from_address.return_value = [mock_array]
|
||||
|
||||
response = self._extend_volumes._get_volume_extents_to_resize(
|
||||
mock_pack, fake_volume_id)
|
||||
|
||||
mock_pack.QueryDisks.assert_called_once_with()
|
||||
mock_enum.Next.assert_called_with(1)
|
||||
mock_unk.QueryInterface.assert_called_once_with(mock_IVdsDisk)
|
||||
self._ctypes_mock.addressof.assert_called_with(mock_extents_p.contents)
|
||||
mock_VDS_DISK_EXTENT.__mul__().from_address.assert_called_with(
|
||||
self._ctypes_mock.addressof(mock_extents_p.contents))
|
||||
|
||||
self._ctypes_mock.pointer.assert_called_once_with(
|
||||
mock_VDS_DISK_EXTENT())
|
||||
self.assertEqual([], response)
|
||||
|
||||
self._ctypes_mock.windll.ole32.CoTaskMemFree.assert_called_with(
|
||||
mock_extents_p)
|
||||
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.'
|
||||
'VDS_QUERY_SOFTWARE_PROVIDERS')
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.IVdsSwProvider')
|
||||
def test_query_providers(self, mock_IVdsSwProvider,
|
||||
mock_VDS_QUERY_SOFTWARE_PROVIDERS):
|
||||
mock_svc = mock.MagicMock()
|
||||
mock_enum = mock.MagicMock()
|
||||
mock_unk = mock.MagicMock()
|
||||
mock_c = mock.MagicMock()
|
||||
mock_svc.QueryProviders.return_value = mock_enum
|
||||
mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
|
||||
mock_unk.QueryInterface.return_value = 'fake providers'
|
||||
|
||||
response = self._extend_volumes._query_providers(mock_svc)
|
||||
mock_svc.QueryProviders.assert_called_once_with(
|
||||
mock_VDS_QUERY_SOFTWARE_PROVIDERS)
|
||||
mock_enum.Next.assert_called_with(1)
|
||||
mock_unk.QueryInterface.assert_called_once_with(mock_IVdsSwProvider)
|
||||
self.assertEqual(['fake providers'], response)
|
||||
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.IVdsPack')
|
||||
def test_query_packs(self, mock_IVdsPack):
|
||||
mock_provider = mock.MagicMock()
|
||||
mock_enum = mock.MagicMock()
|
||||
mock_unk = mock.MagicMock()
|
||||
mock_c = mock.MagicMock()
|
||||
mock_provider.QueryPacks.return_value = mock_enum
|
||||
mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
|
||||
mock_unk.QueryInterface.return_value = 'fake packs'
|
||||
|
||||
response = self._extend_volumes._query_packs(mock_provider)
|
||||
|
||||
mock_provider.QueryPacks.assert_called_once_with()
|
||||
mock_enum.Next.assert_called_with(1)
|
||||
mock_unk.QueryInterface.assert_called_once_with(mock_IVdsPack)
|
||||
self.assertEqual(['fake packs'], response)
|
||||
|
||||
def test_get_volumes_to_extend(self):
|
||||
with testutils.ConfPatcher('volumes_to_extend', '1'):
|
||||
response = self._extend_volumes._get_volumes_to_extend()
|
||||
self.assertEqual([1], response)
|
||||
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.load_vds_service')
|
||||
@mock.patch('cloudbaseinit.plugins.windows.extendvolumes.'
|
||||
'ExtendVolumesPlugin._query_providers')
|
||||
@mock.patch('cloudbaseinit.plugins.windows.extendvolumes.'
|
||||
'ExtendVolumesPlugin._query_packs')
|
||||
@mock.patch('cloudbaseinit.plugins.windows.extendvolumes.'
|
||||
'ExtendVolumesPlugin._extend_volumes')
|
||||
def test_execute(self, mock_extend_volumes, mock_query_packs,
|
||||
mock_query_providers, mock_load_vds_service):
|
||||
mock_svc = mock.MagicMock()
|
||||
fake_providers = ['fake providers']
|
||||
fake_packs = ['fake packs']
|
||||
mock_service = mock.MagicMock()
|
||||
fake_data = 'fake data'
|
||||
mock_load_vds_service.return_value = mock_svc
|
||||
mock_query_providers.return_value = fake_providers
|
||||
mock_query_packs.return_value = fake_packs
|
||||
@mock.patch("cloudbaseinit.utils.windows.storage.factory"
|
||||
".get_storage_manager")
|
||||
@mock.patch("cloudbaseinit.plugins.windows.extendvolumes"
|
||||
".ExtendVolumesPlugin._get_volumes_to_extend")
|
||||
def test_execute(self, mock_get_volumes_to_extend,
|
||||
mock_get_storage_manager):
|
||||
volumes_indexes = [1, 3]
|
||||
mock_get_volumes_to_extend.return_value = volumes_indexes
|
||||
storage_manager = mock.Mock()
|
||||
mock_get_storage_manager.return_value = storage_manager
|
||||
|
||||
with testutils.ConfPatcher('volumes_to_extend', '1'):
|
||||
self._extend_volumes.execute(mock_service, fake_data)
|
||||
response = self._extend_volumes.execute(mock.Mock(), mock.Mock())
|
||||
|
||||
mock_query_providers.assert_called_once_with(mock_svc)
|
||||
mock_query_packs.assert_called_once_with('fake providers')
|
||||
mock_extend_volumes.assert_called_with('fake packs', [1])
|
||||
mock_get_volumes_to_extend.assert_called_once_with()
|
||||
mock_get_storage_manager.assert_called_once_with()
|
||||
storage_manager.extend_volumes.assert_called_once_with(
|
||||
volumes_indexes)
|
||||
self.assertEqual((base.PLUGIN_EXECUTE_ON_NEXT_BOOT, False),
|
||||
response)
|
||||
|
70
cloudbaseinit/tests/utils/windows/storage/test_factory.py
Normal file
70
cloudbaseinit/tests/utils/windows/storage/test_factory.py
Normal file
@ -0,0 +1,70 @@
|
||||
# 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 ctypes as _ # noqa
|
||||
import importlib
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
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)
|
||||
|
||||
@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
|
||||
|
||||
self.mock_os.name = "nt"
|
||||
mock_get_os_utils.return_value.check_os_version.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)
|
||||
|
||||
def test_get_storage_manager_nano(self):
|
||||
self._test_get_storage_manager(nano=True)
|
||||
|
||||
def test_get_storage_manager(self):
|
||||
self._test_get_storage_manager()
|
@ -0,0 +1,217 @@
|
||||
# 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 ctypes.util
|
||||
import importlib
|
||||
import re
|
||||
import unittest
|
||||
|
||||
try:
|
||||
import unittest.mock as mock
|
||||
except ImportError:
|
||||
import mock
|
||||
|
||||
|
||||
class TestVDSStorageManager(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._ctypes_mock = mock.MagicMock()
|
||||
self._comtypes_mock = mock.MagicMock()
|
||||
self._ctypes_mock.util = ctypes.util
|
||||
|
||||
self._module_patcher = mock.patch.dict(
|
||||
'sys.modules',
|
||||
{'comtypes': self._comtypes_mock,
|
||||
'ctypes': self._ctypes_mock})
|
||||
|
||||
self._module_patcher.start()
|
||||
|
||||
vds_store = importlib.import_module(
|
||||
"cloudbaseinit.utils.windows.storage.vds_storage_manager")
|
||||
self._vds_storage_manager = vds_store.VDSStorageManager()
|
||||
|
||||
self.addCleanup(self._module_patcher.stop)
|
||||
|
||||
@mock.patch("cloudbaseinit.utils.windows.storage.vds_storage_manager"
|
||||
".VDSStorageManager._get_volume_index")
|
||||
@mock.patch("cloudbaseinit.utils.windows.storage.vds_storage_manager"
|
||||
".VDSStorageManager._extend_volume")
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.IVdsVolume')
|
||||
def test__extend_volumes(self, _vds_mock, mock_extend_volume,
|
||||
mock_get_volume_index):
|
||||
mock_pack = mock.MagicMock()
|
||||
mock_volume_idxs = mock.MagicMock()
|
||||
mock_enum = mock.MagicMock()
|
||||
mock_unk = mock.MagicMock()
|
||||
mock_c = mock.MagicMock()
|
||||
mock_volume = mock.MagicMock()
|
||||
mock_properties = mock.MagicMock()
|
||||
mock_pack.QueryVolumes.return_value = mock_enum
|
||||
mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
|
||||
mock_unk.QueryInterface.return_value = mock_volume
|
||||
mock_volume.GetProperties.return_value = mock_properties
|
||||
self._ctypes_mock.wstring_at.return_value = 'fake name'
|
||||
mock_get_volume_index.return_value = mock_volume_idxs
|
||||
self._vds_storage_manager._extend_volumes(mock_pack,
|
||||
[mock_volume_idxs])
|
||||
mock_pack.QueryVolumes.assert_called_once_with()
|
||||
mock_enum.Next.assert_called_with(1)
|
||||
mock_unk.QueryInterface.assert_called_once_with(_vds_mock)
|
||||
mock_volume.GetProperties.assert_called_once_with()
|
||||
self._ctypes_mock.wstring_at.assert_called_with(
|
||||
mock_properties.pwszName)
|
||||
mock_get_volume_index.assert_called_once_with('fake name')
|
||||
mock_extend_volume.assert_called_once_with(mock_pack, mock_volume,
|
||||
mock_properties)
|
||||
self._ctypes_mock.windll.ole32.CoTaskMemFree.assert_called_once_with(
|
||||
mock_properties.pwszName)
|
||||
|
||||
def test_get_volume_index(self):
|
||||
mock_value = mock.MagicMock()
|
||||
re.match = mock.MagicMock(return_value=mock_value)
|
||||
mock_value.group.return_value = '9999'
|
||||
response = self._vds_storage_manager._get_volume_index('$2')
|
||||
mock_value.group.assert_called_once_with(1)
|
||||
self.assertTrue(response == 9999)
|
||||
|
||||
@mock.patch("cloudbaseinit.utils.windows.storage.vds_storage_manager"
|
||||
".VDSStorageManager._get_volume_extents_to_resize")
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.VDS_INPUT_DISK')
|
||||
def test_extend_volume(self, mock_VDS_INPUT_DISK,
|
||||
mock_get_volume_extents_to_resize):
|
||||
mock_disk = mock.MagicMock()
|
||||
mock_pack = mock.MagicMock()
|
||||
mock_volume = mock.MagicMock()
|
||||
mock_properties = mock.MagicMock()
|
||||
mock_volume_extent = mock.MagicMock()
|
||||
mock_async = mock.MagicMock()
|
||||
mock_get_volume_extents_to_resize.return_value = [(mock_volume_extent,
|
||||
9999)]
|
||||
mock_VDS_INPUT_DISK.return_value = mock_disk
|
||||
mock_volume.Extend.return_value = mock_async
|
||||
|
||||
self._vds_storage_manager._extend_volume(mock_pack, mock_volume,
|
||||
mock_properties)
|
||||
|
||||
mock_get_volume_extents_to_resize.assert_called_once_with(
|
||||
mock_pack, mock_properties.id)
|
||||
self._ctypes_mock.wstring_at.assert_called_with(
|
||||
mock_properties.pwszName)
|
||||
mock_volume.Extend.assert_called_once_with(
|
||||
mock_VDS_INPUT_DISK.__mul__()(), 1)
|
||||
mock_async.Wait.assert_called_once_with()
|
||||
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.IVdsDisk')
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.VDS_DISK_EXTENT')
|
||||
def test_get_volume_extents_to_resize(self, mock_VDS_DISK_EXTENT,
|
||||
mock_IVdsDisk):
|
||||
mock_pack = mock.MagicMock()
|
||||
mock_extents_p = mock.MagicMock()
|
||||
mock_unk = mock.MagicMock()
|
||||
mock_c = mock.MagicMock()
|
||||
mock_disk = mock.MagicMock()
|
||||
mock_enum = mock.MagicMock()
|
||||
fake_volume_id = '$1'
|
||||
mock_array = mock.MagicMock()
|
||||
mock_array.volumeId = fake_volume_id
|
||||
mock_pack.QueryDisks.return_value = mock_enum
|
||||
mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
|
||||
mock_unk.QueryInterface.return_value = mock_disk
|
||||
mock_disk.QueryExtents.return_value = (mock_extents_p,
|
||||
1)
|
||||
mock_VDS_DISK_EXTENT.__mul__().from_address.return_value = [mock_array]
|
||||
|
||||
response = self._vds_storage_manager._get_volume_extents_to_resize(
|
||||
mock_pack, fake_volume_id)
|
||||
|
||||
mock_pack.QueryDisks.assert_called_once_with()
|
||||
mock_enum.Next.assert_called_with(1)
|
||||
mock_unk.QueryInterface.assert_called_once_with(mock_IVdsDisk)
|
||||
self._ctypes_mock.addressof.assert_called_with(mock_extents_p.contents)
|
||||
mock_VDS_DISK_EXTENT.__mul__().from_address.assert_called_with(
|
||||
self._ctypes_mock.addressof(mock_extents_p.contents))
|
||||
|
||||
self._ctypes_mock.pointer.assert_called_once_with(
|
||||
mock_VDS_DISK_EXTENT())
|
||||
self.assertEqual([], response)
|
||||
|
||||
self._ctypes_mock.windll.ole32.CoTaskMemFree.assert_called_with(
|
||||
mock_extents_p)
|
||||
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.'
|
||||
'VDS_QUERY_SOFTWARE_PROVIDERS')
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.IVdsSwProvider')
|
||||
def test_query_providers(self, mock_IVdsSwProvider,
|
||||
mock_VDS_QUERY_SOFTWARE_PROVIDERS):
|
||||
mock_svc = mock.MagicMock()
|
||||
mock_enum = mock.MagicMock()
|
||||
mock_unk = mock.MagicMock()
|
||||
mock_c = mock.MagicMock()
|
||||
mock_svc.QueryProviders.return_value = mock_enum
|
||||
mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
|
||||
mock_unk.QueryInterface.return_value = 'fake providers'
|
||||
|
||||
response = self._vds_storage_manager._query_providers(mock_svc)
|
||||
mock_svc.QueryProviders.assert_called_once_with(
|
||||
mock_VDS_QUERY_SOFTWARE_PROVIDERS)
|
||||
mock_enum.Next.assert_called_with(1)
|
||||
mock_unk.QueryInterface.assert_called_once_with(mock_IVdsSwProvider)
|
||||
self.assertEqual(['fake providers'], response)
|
||||
|
||||
@mock.patch('cloudbaseinit.utils.windows.vds.IVdsPack')
|
||||
def test_query_packs(self, mock_IVdsPack):
|
||||
mock_provider = mock.MagicMock()
|
||||
mock_enum = mock.MagicMock()
|
||||
mock_unk = mock.MagicMock()
|
||||
mock_c = mock.MagicMock()
|
||||
mock_provider.QueryPacks.return_value = mock_enum
|
||||
mock_enum.Next.side_effect = [(mock_unk, mock_c), (None, None)]
|
||||
mock_unk.QueryInterface.return_value = 'fake packs'
|
||||
|
||||
response = self._vds_storage_manager._query_packs(mock_provider)
|
||||
|
||||
mock_provider.QueryPacks.assert_called_once_with()
|
||||
mock_enum.Next.assert_called_with(1)
|
||||
mock_unk.QueryInterface.assert_called_once_with(mock_IVdsPack)
|
||||
self.assertEqual(['fake packs'], response)
|
||||
|
||||
@mock.patch("cloudbaseinit.utils.windows.storage.vds_storage_manager"
|
||||
".VDSStorageManager._extend_volumes")
|
||||
@mock.patch("cloudbaseinit.utils.windows.storage.vds_storage_manager"
|
||||
".VDSStorageManager._query_packs")
|
||||
@mock.patch("cloudbaseinit.utils.windows.storage.vds_storage_manager"
|
||||
".VDSStorageManager._query_providers")
|
||||
@mock.patch("cloudbaseinit.utils.windows.vds.load_vds_service")
|
||||
def test_extend_volumes(self, mock_load_vds_service, mock_query_providers,
|
||||
mock_query_packs, mock_extend_volumes):
|
||||
mock_svc = mock.Mock()
|
||||
providers = [mock.Mock()] * 5
|
||||
packs = [mock.Mock()] * 3
|
||||
volume_indexes = mock.Mock()
|
||||
|
||||
mock_load_vds_service.return_value = mock_svc
|
||||
mock_query_providers.return_value = providers
|
||||
mock_query_packs.return_value = packs
|
||||
|
||||
self._vds_storage_manager.extend_volumes(
|
||||
volume_indexes=volume_indexes)
|
||||
mock_load_vds_service.assert_called_once_with()
|
||||
mock_query_providers.assert_called_once_with(
|
||||
mock_svc)
|
||||
mock_query_packs.assert_has_calls(
|
||||
[mock.call(provider) for provider in providers])
|
||||
mock_extend_volumes.assert_has_calls(
|
||||
[mock.call(pack, volume_indexes) for pack in packs] *
|
||||
len(providers))
|
@ -0,0 +1,106 @@
|
||||
# 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 importlib
|
||||
import unittest
|
||||
|
||||
try:
|
||||
import unittest.mock as mock
|
||||
except ImportError:
|
||||
import mock
|
||||
|
||||
from cloudbaseinit import exception
|
||||
|
||||
|
||||
class TestWSMStorageManager(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.mock_wmi = mock.MagicMock()
|
||||
|
||||
patcher = mock.patch.dict(
|
||||
"sys.modules",
|
||||
{
|
||||
"wmi": self.mock_wmi
|
||||
}
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
wsm_store = importlib.import_module(
|
||||
"cloudbaseinit.utils.windows.storage.wsm_storage_manager")
|
||||
self.wsm = wsm_store.WSMStorageManager()
|
||||
|
||||
def test_init(self):
|
||||
self.mock_wmi.WMI.assert_called_once_with(
|
||||
moniker='//./Root/Microsoft/Windows/Storage')
|
||||
|
||||
def _test_extend_volumes(self, extend=True, fail=False,
|
||||
size_ret=0, resize_ret=0):
|
||||
volume_indexes = [1, 3]
|
||||
volumes = [mock.Mock(), mock.Mock(), mock.Mock()]
|
||||
partitions = [mock.Mock()]
|
||||
for volume in volumes:
|
||||
volume.associators.return_value = partitions
|
||||
for partition in partitions:
|
||||
size_max = partition.Size = 100
|
||||
if extend:
|
||||
size_max = partition.Size + 10
|
||||
partition.GetSupportedSize.return_value = [
|
||||
size_ret,
|
||||
mock.Mock(),
|
||||
size_max,
|
||||
mock.Mock()]
|
||||
partition.Resize.return_value = [
|
||||
resize_ret,
|
||||
mock.Mock()]
|
||||
|
||||
conn = self.mock_wmi.WMI.return_value
|
||||
conn.MSFT_Volume.return_value = volumes
|
||||
|
||||
if fail:
|
||||
if size_ret or extend:
|
||||
with self.assertRaises(exception.CloudbaseInitException):
|
||||
self.wsm.extend_volumes(volume_indexes=volume_indexes)
|
||||
return
|
||||
self.wsm.extend_volumes(volume_indexes=volume_indexes)
|
||||
|
||||
conn.MSFT_Volume.assert_called_once_with()
|
||||
for idx in volume_indexes:
|
||||
volumes[idx - 1].associators.assert_called_once_with(
|
||||
wmi_result_class='MSFT_Partition')
|
||||
volumes[1].associators.assert_not_called()
|
||||
for partition in partitions:
|
||||
calls = [mock.call()] * len(volume_indexes)
|
||||
partition.GetSupportedSize.assert_has_calls(calls)
|
||||
|
||||
if not extend:
|
||||
for partition in partitions:
|
||||
partition.Resize.assert_not_called()
|
||||
return
|
||||
for partition in partitions:
|
||||
size_max = partition.GetSupportedSize.return_value[2]
|
||||
calls = [mock.call(size_max)] * len(volume_indexes)
|
||||
partition.Resize.assert_has_calls(calls)
|
||||
|
||||
def test_extend_volumes_fail_size(self):
|
||||
self._test_extend_volumes(fail=True, size_ret=1)
|
||||
|
||||
def test_extend_volumes_fail_resize(self):
|
||||
self._test_extend_volumes(fail=True, resize_ret=1)
|
||||
|
||||
def test_extend_volumes_no_extend(self):
|
||||
self._test_extend_volumes(extend=False)
|
||||
|
||||
def test_extend_volumes(self):
|
||||
self._test_extend_volumes()
|
0
cloudbaseinit/utils/windows/storage/__init__.py
Normal file
0
cloudbaseinit/utils/windows/storage/__init__.py
Normal file
25
cloudbaseinit/utils/windows/storage/base.py
Normal file
25
cloudbaseinit/utils/windows/storage/base.py
Normal file
@ -0,0 +1,25 @@
|
||||
# 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 abc
|
||||
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseStorageManager(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
def extend_volumes(self, volume_indexes=None):
|
||||
pass
|
40
cloudbaseinit/utils/windows/storage/factory.py
Normal file
40
cloudbaseinit/utils/windows/storage/factory.py
Normal file
@ -0,0 +1,40 @@
|
||||
# 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 os
|
||||
|
||||
from cloudbaseinit.osutils import factory as osutils_factory
|
||||
from cloudbaseinit.utils import classloader
|
||||
|
||||
|
||||
def get_storage_manager():
|
||||
class_paths = {
|
||||
"VDS": "cloudbaseinit.utils.windows.storage.vds_storage_manager."
|
||||
"VDSStorageManager",
|
||||
"WSM": "cloudbaseinit.utils.windows.storage.wsm_storage_manager."
|
||||
"WSMStorageManager",
|
||||
}
|
||||
|
||||
osutils = osutils_factory.get_os_utils()
|
||||
cl = classloader.ClassLoader()
|
||||
|
||||
if os.name == "nt":
|
||||
if osutils.check_os_version(10, 0):
|
||||
# VDS is not available on Nano Server
|
||||
# WSM supersedes VDS since Windows Server 2012 / Windows 8
|
||||
return cl.load_class(class_paths["WSM"])()
|
||||
else:
|
||||
return cl.load_class(class_paths["VDS"])()
|
||||
|
||||
raise NotImplementedError("No storage manager available for this platform")
|
147
cloudbaseinit/utils/windows/storage/vds_storage_manager.py
Normal file
147
cloudbaseinit/utils/windows/storage/vds_storage_manager.py
Normal file
@ -0,0 +1,147 @@
|
||||
# Copyright 2013 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 ctypes
|
||||
import re
|
||||
|
||||
from oslo_log import log as oslo_logging
|
||||
|
||||
from cloudbaseinit.utils.windows.storage import base
|
||||
from cloudbaseinit.utils.windows import vds
|
||||
|
||||
LOG = oslo_logging.getLogger(__name__)
|
||||
|
||||
ole32 = ctypes.windll.ole32
|
||||
ole32.CoTaskMemFree.restype = None
|
||||
ole32.CoTaskMemFree.argtypes = [ctypes.c_void_p]
|
||||
|
||||
|
||||
class VDSStorageManager(base.BaseStorageManager):
|
||||
def _extend_volumes(self, pack, volume_indexes):
|
||||
enum = pack.QueryVolumes()
|
||||
while True:
|
||||
(unk, c) = enum.Next(1)
|
||||
if not c:
|
||||
break
|
||||
volume = unk.QueryInterface(vds.IVdsVolume)
|
||||
volume_prop = volume.GetProperties()
|
||||
try:
|
||||
extend_volume = True
|
||||
if volume_indexes:
|
||||
volume_name = ctypes.wstring_at(volume_prop.pwszName)
|
||||
volume_idx = self._get_volume_index(volume_name)
|
||||
if volume_idx not in volume_indexes:
|
||||
extend_volume = False
|
||||
|
||||
if extend_volume:
|
||||
self._extend_volume(pack, volume, volume_prop)
|
||||
finally:
|
||||
ole32.CoTaskMemFree(volume_prop.pwszName)
|
||||
|
||||
def _get_volume_index(self, volume_name):
|
||||
m = re.match(r"[^0-9]+([0-9]+)$", volume_name)
|
||||
if m:
|
||||
return int(m.group(1))
|
||||
|
||||
def _extend_volume(self, pack, volume, volume_prop):
|
||||
volume_extents = self._get_volume_extents_to_resize(pack,
|
||||
volume_prop.id)
|
||||
input_disks = []
|
||||
|
||||
for (volume_extent, volume_extend_size) in volume_extents:
|
||||
input_disk = vds.VDS_INPUT_DISK()
|
||||
input_disks.append(input_disk)
|
||||
|
||||
input_disk.diskId = volume_extent.diskId
|
||||
input_disk.memberIdx = volume_extent.memberIdx
|
||||
input_disk.plexId = volume_extent.plexId
|
||||
input_disk.ullSize = volume_extend_size
|
||||
|
||||
if input_disks:
|
||||
extend_size = sum([i.ullSize for i in input_disks])
|
||||
volume_name = ctypes.wstring_at(volume_prop.pwszName)
|
||||
LOG.info('Extending volume "%s" with %s bytes' %
|
||||
(volume_name, extend_size))
|
||||
|
||||
input_disks_ar = (vds.VDS_INPUT_DISK *
|
||||
len(input_disks))(*input_disks)
|
||||
async = volume.Extend(input_disks_ar, len(input_disks))
|
||||
async.Wait()
|
||||
|
||||
def _get_volume_extents_to_resize(self, pack, volume_id):
|
||||
volume_extents = []
|
||||
|
||||
enum = pack.QueryDisks()
|
||||
while True:
|
||||
(unk, c) = enum.Next(1)
|
||||
if not c:
|
||||
break
|
||||
disk = unk.QueryInterface(vds.IVdsDisk)
|
||||
|
||||
(extents_p, num_extents) = disk.QueryExtents()
|
||||
try:
|
||||
extents_array_type = vds.VDS_DISK_EXTENT * num_extents
|
||||
extents_array = extents_array_type.from_address(
|
||||
ctypes.addressof(extents_p.contents))
|
||||
|
||||
volume_extent_extend_size = None
|
||||
|
||||
for extent in extents_array:
|
||||
if extent.volumeId == volume_id:
|
||||
# Copy the extent in order to return it safely
|
||||
# after the source is deallocated
|
||||
extent_copy = vds.VDS_DISK_EXTENT()
|
||||
ctypes.pointer(extent_copy)[0] = extent
|
||||
|
||||
volume_extent_extend_size = [extent_copy, 0]
|
||||
volume_extents.append(volume_extent_extend_size)
|
||||
elif (volume_extent_extend_size and
|
||||
extent.type == vds.VDS_DET_FREE):
|
||||
volume_extent_extend_size[1] += extent.ullSize
|
||||
else:
|
||||
volume_extent_extend_size = None
|
||||
finally:
|
||||
ole32.CoTaskMemFree(extents_p)
|
||||
|
||||
# Return only the extents that need to be resized
|
||||
return [ve for ve in volume_extents if ve[1] > 0]
|
||||
|
||||
def _query_providers(self, svc):
|
||||
providers = []
|
||||
enum = svc.QueryProviders(vds.VDS_QUERY_SOFTWARE_PROVIDERS)
|
||||
while True:
|
||||
(unk, c) = enum.Next(1)
|
||||
if not c:
|
||||
break
|
||||
providers.append(unk.QueryInterface(vds.IVdsSwProvider))
|
||||
return providers
|
||||
|
||||
def _query_packs(self, provider):
|
||||
packs = []
|
||||
enum = provider.QueryPacks()
|
||||
while True:
|
||||
(unk, c) = enum.Next(1)
|
||||
if not c:
|
||||
break
|
||||
packs.append(unk.QueryInterface(vds.IVdsPack))
|
||||
return packs
|
||||
|
||||
def extend_volumes(self, volume_indexes=None):
|
||||
svc = vds.load_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)
|
52
cloudbaseinit/utils/windows/storage/wsm_storage_manager.py
Normal file
52
cloudbaseinit/utils/windows/storage/wsm_storage_manager.py
Normal file
@ -0,0 +1,52 @@
|
||||
# Copyright 2013 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 wmi
|
||||
|
||||
from oslo_log import log as oslo_logging
|
||||
|
||||
from cloudbaseinit import exception
|
||||
from cloudbaseinit.utils.windows.storage import base
|
||||
|
||||
LOG = oslo_logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WSMStorageManager(base.BaseStorageManager):
|
||||
def __init__(self):
|
||||
self._conn = wmi.WMI(moniker='//./Root/Microsoft/Windows/Storage')
|
||||
|
||||
def extend_volumes(self, volume_indexes=None):
|
||||
volumes = self._conn.MSFT_Volume()
|
||||
|
||||
for idx, volume in enumerate(volumes, 1):
|
||||
# TODO(alexpilotti): don't rely on the volumes WMI query order
|
||||
if volume_indexes and idx not in volume_indexes:
|
||||
continue
|
||||
|
||||
partitions = volume.associators(wmi_result_class='MSFT_Partition')
|
||||
for partition in partitions:
|
||||
(ret_val, _, size_max, _) = partition.GetSupportedSize()
|
||||
if ret_val:
|
||||
raise exception.CloudbaseInitException(
|
||||
"GetSupportedSize failed with error: %s" % ret_val)
|
||||
|
||||
if size_max > partition.Size:
|
||||
LOG.info('Extending partition "%(partition_number)s" '
|
||||
'to %(size)s bytes' %
|
||||
{'partition_number': partition.PartitionNumber,
|
||||
'size': size_max})
|
||||
(ret_val, _) = partition.Resize(size_max)
|
||||
if ret_val:
|
||||
raise exception.CloudbaseInitException(
|
||||
"Resize failed with error: %s" % ret_val)
|
Loading…
x
Reference in New Issue
Block a user