From: Pedro Navarro Perez Date: Mon, 26 Aug 2013 11:09:41 +0000 (+0200) Subject: Add support for Havana missing features in Windows driver X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=106bf560e291b9b8a0690680170db575086e04b3;p=openstack-build%2Fcinder-build.git Add support for Havana missing features in Windows driver The following features were added: -copy_volume_to_image (Havana) -copy_image_to_volume (Havana) -get_volume_stats (Havana) -extend_volume (Icehouse) The test mocking based on binary pickled files were replaced by mox. Change-Id: I92c681280d6f7b85bcba7a5e3513f2bdb6c13f1d Implements: blueprint windows-storage-driver-extended --- diff --git a/cinder/image/image_utils.py b/cinder/image/image_utils.py index 2f638ae64..032919595 100644 --- a/cinder/image/image_utils.py +++ b/cinder/image/image_utils.py @@ -183,9 +183,10 @@ class QemuImgInfo(object): def qemu_img_info(path): """Return a object containing the parsed output from qemu-img info.""" - out, err = utils.execute('env', 'LC_ALL=C', 'LANG=C', - 'qemu-img', 'info', path, - run_as_root=True) + cmd = ('env', 'LC_ALL=C', 'LANG=C', 'qemu-img', 'info', path) + if os.name == 'nt': + cmd = cmd[3:] + out, err = utils.execute(*cmd, run_as_root=True) return QemuImgInfo(out) @@ -232,9 +233,23 @@ def fetch_verify_image(context, image_service, image_id, dest, {'fmt': fmt, 'backing_file': backing_file})) +def fetch_to_vhd(context, image_service, + image_id, dest, + user_id=None, project_id=None): + fetch_to_volume_format(context, image_service, image_id, dest, 'vpc', + user_id, project_id) + + def fetch_to_raw(context, image_service, image_id, dest, user_id=None, project_id=None): + fetch_to_volume_format(context, image_service, image_id, dest, 'raw', + user_id, project_id) + + +def fetch_to_volume_format(context, image_service, + image_id, dest, volume_format, + user_id=None, project_id=None): if (CONF.image_conversion_dir and not os.path.exists(CONF.image_conversion_dir)): os.makedirs(CONF.image_conversion_dir) @@ -273,22 +288,29 @@ def fetch_to_raw(context, image_service, # check via 'qemu-img info' that what we copied was in fact a raw # image and not a different format with a backing file, which may be # malicious. - LOG.debug("%s was %s, converting to raw" % (image_id, fmt)) - convert_image(tmp, dest, 'raw') + LOG.debug("%s was %s, converting to %s " % (image_id, fmt, + volume_format)) + convert_image(tmp, dest, volume_format) data = qemu_img_info(dest) - if data.file_format != "raw": + if data.file_format != volume_format: raise exception.ImageUnacceptable( image_id=image_id, - reason=_("Converted to raw, but format is now %s") % - data.file_format) + reason=_("Converted to %(vol_format)s, but format is " + "now %(file_format)s") % {'vol_format': volume_format, + 'file_format': data. + file_format}) -def upload_volume(context, image_service, image_meta, volume_path): +def upload_volume(context, image_service, image_meta, volume_path, + volume_format='raw'): image_id = image_meta['id'] - if (image_meta['disk_format'] == 'raw'): - LOG.debug("%s was raw, no need to convert to %s" % - (image_id, image_meta['disk_format'])) + if (image_meta['disk_format'] == volume_format): + LOG.debug("%s was %s, no need to convert to %s" % + (image_id, volume_format, image_meta['disk_format'])) + if os.name == 'nt': + with fileutils.file_open(volume_path) as image_file: + image_service.update(context, image_id, {}, image_file) with utils.temporary_chown(volume_path): with fileutils.file_open(volume_path) as image_file: image_service.update(context, image_id, {}, image_file) @@ -301,8 +323,8 @@ def upload_volume(context, image_service, image_meta, volume_path): fd, tmp = tempfile.mkstemp(dir=CONF.image_conversion_dir) os.close(fd) with fileutils.remove_path_on_error(tmp): - LOG.debug("%s was raw, converting to %s" % - (image_id, image_meta['disk_format'])) + LOG.debug("%s was %s, converting to %s" % + (image_id, volume_format, image_meta['disk_format'])) convert_image(volume_path, tmp, image_meta['disk_format']) data = qemu_img_info(tmp) diff --git a/cinder/tests/test_drivers_compatibility.py b/cinder/tests/test_drivers_compatibility.py index d9c6cd9dd..1082fe7e0 100644 --- a/cinder/tests/test_drivers_compatibility.py +++ b/cinder/tests/test_drivers_compatibility.py @@ -32,7 +32,7 @@ LEFTHAND_MODULE = "cinder.volume.drivers.san.hp_lefthand.HpSanISCSIDriver" NFS_MODULE = "cinder.volume.drivers.nfs.NfsDriver" SOLIDFIRE_MODULE = "cinder.volume.drivers.solidfire.SolidFireDriver" STORWIZE_SVC_MODULE = "cinder.volume.drivers.storwize_svc.StorwizeSVCDriver" -WINDOWS_MODULE = "cinder.volume.drivers.windows.WindowsDriver" +WINDOWS_MODULE = "cinder.volume.drivers.windows.windows.WindowsDriver" XIV_DS8K_MODULE = "cinder.volume.drivers.xiv_ds8k.XIVDS8KDriver" ZADARA_MODULE = "cinder.volume.drivers.zadara.ZadaraVPSAISCSIDriver" NETAPP_MODULE = "cinder.volume.drivers.netapp.common.Deprecated" diff --git a/cinder/tests/test_windows.py b/cinder/tests/test_windows.py index 05b1907ae..af9697395 100644 --- a/cinder/tests/test_windows.py +++ b/cinder/tests/test_windows.py @@ -20,205 +20,365 @@ Unit tests for Windows Server 2012 OpenStack Cinder volume driver """ -import sys +import os from oslo.config import cfg -from cinder.tests.windows import basetestcase +import mox as mox_lib +from mox import IgnoreArg +from mox import stubout + +from cinder import test + +from cinder.image import image_utils + from cinder.tests.windows import db_fakes -from cinder.tests.windows import windowsutils -from cinder.volume.drivers import windows +from cinder.volume import configuration as conf +from cinder.volume.drivers.windows import windows +from cinder.volume.drivers.windows import windows_utils CONF = cfg.CONF -class TestWindowsDriver(basetestcase.BaseTestCase): +class TestWindowsDriver(test.TestCase): def __init__(self, method): super(TestWindowsDriver, self).__init__(method) def setUp(self): super(TestWindowsDriver, self).setUp() + self._mox = mox_lib.Mox() + self.stubs = stubout.StubOutForTesting() self.flags( windows_iscsi_lun_path='C:\iSCSIVirtualDisks', ) - self._volume_data = None - self._volume_data_2 = None - self._snapshot_data = None - self._connector_data = None - self._volume_id = '10958016-e196-42e3-9e7f-5d8927ae3099' - self._volume_id_2 = '20958016-e196-42e3-9e7f-5d8927ae3098' - self._snapshot_id = '30958016-e196-42e3-9e7f-5d8927ae3097' - self._iqn = "iqn.1991-05.com.microsoft:dell1160dsy" - self._setup_stubs() + configuration = conf.Configuration(None) + configuration.append_config_values(windows.windows_opts) + + self._driver = windows.WindowsDriver(configuration=configuration) + self._driver.do_setup({}) - self._drv = windows.WindowsDriver() - self._drv.do_setup({}) - self._wutils = windowsutils.WindowsUtils() + def tearDown(self): + self._mox.UnsetStubs() + self.stubs.UnsetAll() + super(TestWindowsDriver, self).tearDown() def _setup_stubs(self): - # Modules to mock - modules_to_mock = [ - 'wmi', - 'os', - 'subprocess', - 'multiprocessing' - ] + def fake_wutils__init__(self): + pass + windows_utils.WindowsUtils.__init__ = fake_wutils__init__ - modules_to_test = [ - windows, - windowsutils, - sys.modules[__name__] - ] + def fake_local_path(self, volume): + return os.path.join(CONF.windows_iscsi_lun_path, + str(volume['name']) + ".vhd") - self._inject_mocks_in_modules(modules_to_mock, modules_to_test) + def test_check_for_setup_errors(self): + mox = self._mox + drv = self._driver + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'check_for_setup_error') + windows_utils.WindowsUtils.check_for_setup_error() - def tearDown(self): - try: - if (self._volume_data_2 and - self._wutils.volume_exists(self._volume_data_2['name'])): - self._wutils.delete_volume(self._volume_data_2['name']) - - if (self._volume_data and - self._wutils.volume_exists( - self._volume_data['name'])): - self._wutils.delete_volume(self._volume_data['name']) - if (self._snapshot_data and - self._wutils.snapshot_exists( - self._snapshot_data['name'])): - self._wutils.delete_snapshot(self._snapshot_data['name']) - if (self._connector_data and - self._wutils.initiator_id_exists( - "%s%s" % (CONF.iscsi_target_prefix, - self._volume_data['name']), - self._connector_data['initiator'])): - target_name = "%s%s" % (CONF.iscsi_target_prefix, - self._volume_data['name']) - initiator_name = self._connector_data['initiator'] - self._wutils.delete_initiator_id(target_name, initiator_name) - if (self._volume_data and - self._wutils.export_exists("%s%s" % - (CONF.iscsi_target_prefix, - self._volume_data['name']))): - self._wutils.delete_export( - "%s%s" % (CONF.iscsi_target_prefix, - self._volume_data['name'])) - - finally: - super(TestWindowsDriver, self).tearDown() + mox.ReplayAll() - def test_check_for_setup_errors(self): - self._drv.check_for_setup_error() + drv.check_for_setup_error() + + mox.VerifyAll() def test_create_volume(self): - self._create_volume() + mox = self._mox + drv = self._driver + vol = db_fakes.get_fake_volume_info() + + self.stubs.Set(drv, 'local_path', self.fake_local_path) - wt_disks = self._wutils.find_vhd_by_name(self._volume_data['name']) - self.assertEquals(len(wt_disks), 1) + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'create_volume') - def _create_volume(self): - self._volume_data = db_fakes.get_fake_volume_info(self._volume_id) - self._drv.create_volume(self._volume_data) + windows_utils.WindowsUtils.create_volume(self.fake_local_path(vol), + vol['name'], vol['size']) + + mox.ReplayAll() + + drv.create_volume(vol) + + mox.VerifyAll() def test_delete_volume(self): - self._create_volume() + """delete_volume simple test case.""" + mox = self._mox + drv = self._driver + + vol = db_fakes.get_fake_volume_info() + + mox.StubOutWithMock(drv, 'local_path') + drv.local_path(vol).AndReturn(self.fake_local_path(vol)) - self._drv.delete_volume(self._volume_data) + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'delete_volume') + windows_utils.WindowsUtils.delete_volume(vol['name'], + self.fake_local_path(vol)) + mox.ReplayAll() - wt_disks = self._wutils.find_vhd_by_name(self._volume_data['name']) - self.assertEquals(len(wt_disks), 0) + drv.delete_volume(vol) + + mox.VerifyAll() def test_create_snapshot(self): - #Create a volume - self._create_volume() + mox = self._mox + drv = self._driver + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'create_snapshot') + volume = db_fakes.get_fake_volume_info() + snapshot = db_fakes.get_fake_snapshot_info() + + self.stubs.Set(drv, 'local_path', self.fake_local_path(snapshot)) + + windows_utils.WindowsUtils.create_snapshot(volume['name'], + snapshot['name']) - wt_disks = self._wutils.find_vhd_by_name(self._volume_data['name']) - self.assertEquals(len(wt_disks), 1) - #Create a snapshot from the previous volume - self._create_snapshot() + mox.ReplayAll() - snapshot_name = self._snapshot_data['name'] - wt_snapshots = self._wutils.find_snapshot_by_name(snapshot_name) - self.assertEquals(len(wt_snapshots), 1) + drv.create_snapshot(snapshot) - def _create_snapshot(self): - volume_name = self._volume_data['name'] - snapshot_id = self._snapshot_id - self._snapshot_data = db_fakes.get_fake_snapshot_info(volume_name, - snapshot_id) - self._drv.create_snapshot(self._snapshot_data) + mox.VerifyAll() def test_create_volume_from_snapshot(self): - #Create a volume - self._create_volume() - #Create a snapshot from the previous volume - self._create_snapshot() + mox = self._mox + drv = self._driver + + snapshot = db_fakes.get_fake_snapshot_info() + volume = db_fakes.get_fake_volume_info() + + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'create_volume_from_snapshot') + windows_utils.WindowsUtils.\ + create_volume_from_snapshot(volume['name'], snapshot['name']) - self._volume_data_2 = db_fakes.get_fake_volume_info(self._volume_id_2) + mox.ReplayAll() - self._drv.create_volume_from_snapshot(self._volume_data_2, - self._snapshot_data) + drv.create_volume_from_snapshot(volume, snapshot) - wt_disks = self._wutils.find_vhd_by_name(self._volume_data_2['name']) - self.assertEquals(len(wt_disks), 1) + mox.VerifyAll() def test_delete_snapshot(self): - #Create a volume - self._create_volume() - #Create a snapshot from the previous volume - self._create_snapshot() + mox = self._mox + drv = self._driver - self._drv.delete_snapshot(self._snapshot_data) + snapshot = db_fakes.get_fake_snapshot_info() - snapshot_name = self._snapshot_data['name'] - wt_snapshots = self._wutils.find_snapshot_by_name(snapshot_name) - self.assertEquals(len(wt_snapshots), 0) + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'delete_snapshot') + windows_utils.WindowsUtils.delete_snapshot(snapshot['name']) + + mox.ReplayAll() + + drv.delete_snapshot(snapshot) + + mox.VerifyAll() def test_create_export(self): - #Create a volume - self._create_volume() + mox = self._mox + drv = self._driver + + volume = db_fakes.get_fake_volume_info() + + initiator_name = "%s%s" % (CONF.iscsi_target_prefix, volume['name']) - retval = self._drv.create_export({}, self._volume_data) + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'create_iscsi_target') + windows_utils.WindowsUtils.create_iscsi_target(initiator_name, + IgnoreArg()) + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'add_disk_to_target') + windows_utils.WindowsUtils.add_disk_to_target(volume['name'], + initiator_name) - volume_name = self._volume_data['name'] - self.assertEquals( - retval, - {'provider_location': "%s%s" % (CONF.iscsi_target_prefix, - volume_name)}) + mox.ReplayAll() + + export_info = drv.create_export(None, volume) + + mox.VerifyAll() + + self.assertEquals(export_info['provider_location'], initiator_name) def test_initialize_connection(self): - #Create a volume - self._create_volume() + mox = self._mox + drv = self._driver + + volume = db_fakes.get_fake_volume_info() + initiator_name = "%s%s" % (CONF.iscsi_target_prefix, volume['name']) + + connector = db_fakes.get_fake_connector_info() - self._drv.create_export({}, self._volume_data) + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'associate_initiator_with_iscsi_target') + windows_utils.WindowsUtils.associate_initiator_with_iscsi_target( + volume['provider_location'], initiator_name, ) - self._connector_data = db_fakes.get_fake_connector_info(self._iqn) + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'get_host_information') + windows_utils.WindowsUtils.get_host_information( + volume, volume['provider_location']) - init_data = self._drv.initialize_connection(self._volume_data, - self._connector_data) - target_name = self._volume_data['provider_location'] - initiator_name = self._connector_data['initiator'] + mox.ReplayAll() - wt_initiator_ids = self._wutils.find_initiator_ids(target_name, - initiator_name) - self.assertEquals(len(wt_initiator_ids), 1) + drv.initialize_connection(volume, connector) - properties = init_data['data'] - self.assertNotEqual(properties['target_iqn'], None) + mox.VerifyAll() + + def test_terminate_connection(self): + mox = self._mox + drv = self._driver + + volume = db_fakes.get_fake_volume_info() + initiator_name = "%s%s" % (CONF.iscsi_target_prefix, volume['name']) + connector = db_fakes.get_fake_connector_info() + + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'delete_iscsi_target') + windows_utils.WindowsUtils.delete_iscsi_target( + initiator_name, volume['provider_location']) + + mox.ReplayAll() + + drv.terminate_connection(volume, connector) + + mox.VerifyAll() def test_ensure_export(self): - #Create a volume - self._create_volume() + mox = self._mox + drv = self._driver + + volume = db_fakes.get_fake_volume_info() + + initiator_name = "%s%s" % (CONF.iscsi_target_prefix, volume['name']) + + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'create_iscsi_target') + windows_utils.WindowsUtils.create_iscsi_target(initiator_name, True) + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'add_disk_to_target') + windows_utils.WindowsUtils.add_disk_to_target(volume['name'], + initiator_name) + + mox.ReplayAll() - self._drv.ensure_export({}, self._volume_data) + drv.ensure_export(None, volume) + + mox.VerifyAll() def test_remove_export(self): - #Create a volume - self._create_volume() + mox = self._mox + drv = self._driver + + volume = db_fakes.get_fake_volume_info() + + target_name = volume['provider_location'] + + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'remove_iscsi_target') + windows_utils.WindowsUtils.remove_iscsi_target(target_name) + + mox.ReplayAll() + + drv.remove_export(None, volume) + + mox.VerifyAll() + + def test_copy_image_to_volume(self): + """resize_image common case usage.""" + mox = self._mox + drv = self._driver + + volume = db_fakes.get_fake_volume_info() + + self.stubs.Set(drv, 'local_path', self.fake_local_path) + + mox.StubOutWithMock(image_utils, 'fetch_to_vhd') + image_utils.fetch_to_vhd(None, None, None, + self.fake_local_path(volume)) + + mox.ReplayAll() + + drv.copy_image_to_volume(None, volume, None, None) + + mox.VerifyAll() + + def test_copy_volume_to_image(self): + mox = self._mox + drv = self._driver + + vol = db_fakes.get_fake_volume_info() + + image_meta = db_fakes.get_fake_image_meta() + + self.stubs.Set(drv, 'local_path', self.fake_local_path) + + mox.StubOutWithMock(image_utils, 'upload_volume') + + temp_vhd_path = os.path.join(CONF.image_conversion_dir, + str(image_meta['id']) + ".vhd") + + image_utils.upload_volume(None, None, image_meta, temp_vhd_path, 'vpc') + + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'copy_vhd_disk') + + windows_utils.WindowsUtils.copy_vhd_disk(self.fake_local_path(vol), + temp_vhd_path) + + mox.ReplayAll() + + drv.copy_volume_to_image(None, vol, None, image_meta) + + mox.VerifyAll() + + def test_create_cloned_volume(self): + mox = self._mox + drv = self._driver + + volume = db_fakes.get_fake_volume_info() + volume_cloned = db_fakes.get_fake_volume_info_cloned() + + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'create_volume') + + windows_utils.WindowsUtils.create_volume(IgnoreArg(), IgnoreArg(), + IgnoreArg()) + + self._mox.StubOutWithMock(windows_utils.WindowsUtils, + 'copy_vhd_disk') + windows_utils.WindowsUtils.copy_vhd_disk(self.fake_local_path( + volume_cloned), self.fake_local_path(volume)) + + mox.ReplayAll() + + drv.create_cloned_volume(volume, volume_cloned) + + mox.VerifyAll() + + def test_extend_volume(self): + mox = self._mox + drv = self._driver + + volume = db_fakes.get_fake_volume_info() + + TEST_VOLUME_ADDITIONAL_SIZE_MB = 1024 + TEST_VOLUME_ADDITIONAL_SIZE_GB = 1 + + self._mox.StubOutWithMock(windows_utils.WindowsUtils, 'extend') + + windows_utils.WindowsUtils.extend(volume['name'], + TEST_VOLUME_ADDITIONAL_SIZE_MB) + + new_size = volume['size'] + TEST_VOLUME_ADDITIONAL_SIZE_GB + + mox.ReplayAll() - self._drv.create_export({}, self._volume_data) + drv.extend_volume(volume, new_size) - self._drv.remove_export({}, self._volume_data) + mox.VerifyAll() diff --git a/cinder/tests/windows/basetestcase.py b/cinder/tests/windows/basetestcase.py deleted file mode 100644 index ef749f612..000000000 --- a/cinder/tests/windows/basetestcase.py +++ /dev/null @@ -1,103 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 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. - -""" -TestCase for MockProxy based tests and related classes. -""" - -import cinder.test -import gzip -import os -import pickle - -from cinder.tests.windows import mockproxy - -gen_test_mocks_key = 'CINDER_GENERATE_TEST_MOCKS' - - -class BaseTestCase(cinder.test.TestCase): - """TestCase for MockProxy based tests.""" - - def run(self, result=None): - self._currentResult = result - super(BaseTestCase, self).run(result) - - def setUp(self): - super(BaseTestCase, self).setUp() - self._mps = {} - - def tearDown(self): - super(BaseTestCase, self).tearDown() - - # python-subunit will wrap test results with a decorator. - # Need to access the decorated member of results to get the - # actual test result when using python-subunit. - if hasattr(self._currentResult, 'decorated'): - result = self._currentResult.decorated - else: - result = self._currentResult - has_errors = len([test for (test, msgs) in result.errors - if test.id() == self.id()]) > 0 - failed = len([test for (test, msgs) in result.failures - if test.id() == self.id()]) > 0 - - if not has_errors and not failed: - self._save_mock_proxies() - - def _save_mock(self, name, mock): - path = self._get_stub_file_path(self.id(), name) - pickle.dump(mock, gzip.open(path, 'wb')) - - def _get_stub_file_path(self, test_name, mock_name): - # test naming differs between platforms - prefix = 'cinder.tests.' - if test_name.startswith(prefix): - test_name = test_name[len(prefix):] - file_name = '{0}_{1}.p.gz'.format(test_name, mock_name) - return os.path.join(os.path.dirname(mockproxy.__file__), - "stubs", file_name) - - def _load_mock(self, name): - path = self._get_stub_file_path(self.id(), name) - if os.path.exists(path): - return pickle.load(gzip.open(path, 'rb')) - else: - return None - - def _load_mock_or_create_proxy(self, module_name): - m = None - if (not gen_test_mocks_key in os.environ or - os.environ[gen_test_mocks_key].lower() - not in ['true', 'yes', '1']): - m = self._load_mock(module_name) - else: - module = __import__(module_name) - m = mockproxy.MockProxy(module) - self._mps[module_name] = m - return m - - def _inject_mocks_in_modules(self, objects_to_mock, modules_to_test): - for module_name in objects_to_mock: - mp = self._load_mock_or_create_proxy(module_name) - for mt in modules_to_test: - module_local_name = module_name.split('.')[-1] - setattr(mt, module_local_name, mp) - - def _save_mock_proxies(self): - for name, mp in self._mps.items(): - m = mp.get_mock() - if m.has_values(): - self._save_mock(name, m) diff --git a/cinder/tests/windows/db_fakes.py b/cinder/tests/windows/db_fakes.py index a93dbc6f9..45d5c07c2 100644 --- a/cinder/tests/windows/db_fakes.py +++ b/cinder/tests/windows/db_fakes.py @@ -19,18 +19,32 @@ Stubouts, mocks and fixtures for windows volume test suite """ -def get_fake_volume_info(name): - return {'name': name, +def get_fake_volume_info(): + return {'name': 'volume_name', 'size': 1, - 'provider_location': 'iqn.2010-10.org.openstack:' + name, + 'provider_location': 'iqn.2010-10.org.openstack:' + 'volume_name', 'id': 1, 'provider_auth': None} -def get_fake_snapshot_info(volume_name, snapshot_name): - return {'name': snapshot_name, - 'volume_name': volume_name, } +def get_fake_volume_info_cloned(): + return {'name': 'volume_name_cloned', + 'size': 1, + 'provider_location': 'iqn.2010-10.org.openstack:' + + 'volume_name_cloned', + 'id': 1, + 'provider_auth': None} + + +def get_fake_image_meta(): + return {'id': '10958016-e196-42e3-9e7f-5d8927ae3099' + } + + +def get_fake_snapshot_info(): + return {'name': 'snapshot_name', + 'volume_name': 'volume_name', } -def get_fake_connector_info(initiator): - return {'initiator': initiator, } +def get_fake_connector_info(): + return {'initiator': 'iqn.2010-10.org.openstack:' + 'volume_name', } diff --git a/cinder/tests/windows/mockproxy.py b/cinder/tests/windows/mockproxy.py deleted file mode 100644 index 4a48532ca..000000000 --- a/cinder/tests/windows/mockproxy.py +++ /dev/null @@ -1,241 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 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. -# - - -""" -Classes for dynamic generation of mock objects. -""" - -import inspect - - -def serialize_obj(obj): - if isinstance(obj, float): - val = str(round(obj, 10)) - elif isinstance(obj, dict): - d = {} - for k1, v1 in obj.items(): - d[k1] = serialize_obj(v1) - val = str(d) - elif isinstance(obj, list): - l1 = [] - for i1 in obj: - l1.append(serialize_obj(i1)) - val = str(l1) - elif isinstance(obj, tuple): - l1 = () - for i1 in obj: - l1 = l1 + (serialize_obj(i1),) - val = str(l1) - else: - val = str(obj) - return val - - -def serialize_args(*args, **kwargs): - """Workaround for float string conversion issues in Python 2.6.""" - return serialize_obj((args, kwargs)) - - -class Mock(object): - def _get_next_value(self, name): - c = self._access_count.get(name) - if c is None: - c = 0 - else: - c = c + 1 - self._access_count[name] = c - return self._values[name][c] - - def _get_next_ret_value(self, name, params): - d = self._access_count.get(name) - if d is None: - d = {} - self._access_count[name] = d - c = d.get(params) - if c is None: - c = 0 - else: - c = c + 1 - d[params] = c - return self._values[name][params][c] - - def __init__(self, values): - self._values = values - self._access_count = {} - - def has_values(self): - return len(self._values) > 0 - - def __getattr__(self, name): - if name.startswith('__') and name.endswith('__'): - return object.__getattribute__(self, name) - else: - if isinstance(self._values[name], dict): - def newfunc(*args, **kwargs): - params = serialize_args(args, kwargs) - return self._get_next_ret_value(name, params) - return newfunc - else: - return self._get_next_value(name) - - def __str__(self): - return self._get_next_value('__str__') - - def __iter__(self): - return getattr(self._get_next_value('__iter__'), '__iter__')() - - def __len__(self): - return self._get_next_value('__len__') - - def __getitem__(self, key): - return self._get_next_ret_value('__getitem__', str(key)) - - def __call__(self, *args, **kwargs): - params = serialize_args(args, kwargs) - return self._get_next_ret_value('__call__', params) - - -class MockProxy(object): - def __init__(self, wrapped): - self._wrapped = wrapped - self._recorded_values = {} - - def _get_proxy_object(self, obj): - if (hasattr(obj, '__dict__') or - isinstance(obj, tuple) or - isinstance(obj, list) or - isinstance(obj, dict)): - p = MockProxy(obj) - else: - p = obj - return p - - def __getattr__(self, name): - if name in ['_wrapped']: - return object.__getattribute__(self, name) - else: - attr = getattr(self._wrapped, name) - if (inspect.isfunction(attr) or - inspect.ismethod(attr) or - inspect.isbuiltin(attr)): - def newfunc(*args, **kwargs): - result = attr(*args, **kwargs) - p = self._get_proxy_object(result) - params = serialize_args(args, kwargs) - self._add_recorded_ret_value(name, params, p) - return p - return newfunc - elif (hasattr(attr, '__dict__') or - (hasattr(attr, '__getitem__') and not - (isinstance(attr, str) or isinstance(attr, unicode)))): - p = MockProxy(attr) - else: - p = attr - self._add_recorded_value(name, p) - return p - - def __setattr__(self, name, value): - if name in ['_wrapped', '_recorded_values']: - object.__setattr__(self, name, value) - else: - setattr(self._wrapped, name, value) - - def _add_recorded_ret_value(self, name, params, val): - d = self._recorded_values.get(name) - if d is None: - d = {} - self._recorded_values[name] = d - l = d.get(params) - if l is None: - l = [] - d[params] = l - l.append(val) - - def _add_recorded_value(self, name, val): - if name not in self._recorded_values: - self._recorded_values[name] = [] - self._recorded_values[name].append(val) - - def get_mock(self): - values = {} - for k, v in self._recorded_values.items(): - if isinstance(v, dict): - d = {} - values[k] = d - for k1, v1 in v.items(): - l = [] - d[k1] = l - for i1 in v1: - if isinstance(i1, MockProxy): - l.append(i1.get_mock()) - else: - l.append(i1) - else: - l = [] - values[k] = l - for i in v: - if isinstance(i, MockProxy): - l.append(i.get_mock()) - elif isinstance(i, dict): - d = {} - for k1, v1 in v.items(): - if isinstance(v1, MockProxy): - d[k1] = v1.get_mock() - else: - d[k1] = v1 - l.append(d) - elif isinstance(i, list): - l1 = [] - for i1 in i: - if isinstance(i1, MockProxy): - l1.append(i1.get_mock()) - else: - l1.append(i1) - l.append(l1) - else: - l.append(i) - return Mock(values) - - def __str__(self): - s = str(self._wrapped) - self._add_recorded_value('__str__', s) - return s - - def __len__(self): - l = len(self._wrapped) - self._add_recorded_value('__len__', l) - return l - - def __iter__(self): - it = [] - for i in self._wrapped: - it.append(self._get_proxy_object(i)) - self._add_recorded_value('__iter__', it) - return iter(it) - - def __getitem__(self, key): - p = self._get_proxy_object(self._wrapped[key]) - self._add_recorded_ret_value('__getitem__', str(key), p) - return p - - def __call__(self, *args, **kwargs): - c = self._wrapped(*args, **kwargs) - p = self._get_proxy_object(c) - params = serialize_args(args, kwargs) - self._add_recorded_ret_value('__call__', params, p) - return p diff --git a/cinder/tests/windows/stubs/README.rst b/cinder/tests/windows/stubs/README.rst deleted file mode 100644 index 150fd3ad1..000000000 --- a/cinder/tests/windows/stubs/README.rst +++ /dev/null @@ -1,2 +0,0 @@ -Files with extension p.gz are compressed pickle files containing serialized -mocks used during unit testing diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_check_for_setup_errors_wmi.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_check_for_setup_errors_wmi.p.gz deleted file mode 100644 index dada5e654..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_check_for_setup_errors_wmi.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_export_os.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_export_os.p.gz deleted file mode 100644 index c425615b9..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_export_os.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_export_wmi.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_export_wmi.p.gz deleted file mode 100644 index 2f6ea72b5..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_export_wmi.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_snapshot_os.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_snapshot_os.p.gz deleted file mode 100644 index 5e55a96f3..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_snapshot_os.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_snapshot_wmi.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_snapshot_wmi.p.gz deleted file mode 100644 index 4e5879f67..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_snapshot_wmi.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_from_snapshot_os.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_from_snapshot_os.p.gz deleted file mode 100644 index 5de824a7c..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_from_snapshot_os.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_from_snapshot_wmi.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_from_snapshot_wmi.p.gz deleted file mode 100644 index 8b9ead48f..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_from_snapshot_wmi.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_os.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_os.p.gz deleted file mode 100644 index fc8eee072..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_os.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_wmi.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_wmi.p.gz deleted file mode 100644 index dad1f8520..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_create_volume_wmi.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_snapshot_os.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_snapshot_os.p.gz deleted file mode 100644 index 69184224f..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_snapshot_os.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_snapshot_wmi.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_snapshot_wmi.p.gz deleted file mode 100644 index 26de5bdac..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_snapshot_wmi.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_volume_os.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_volume_os.p.gz deleted file mode 100644 index 5ec542941..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_volume_os.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_volume_wmi.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_volume_wmi.p.gz deleted file mode 100644 index 11fed29bb..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_delete_volume_wmi.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_ensure_export_os.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_ensure_export_os.p.gz deleted file mode 100644 index 3c5813515..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_ensure_export_os.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_ensure_export_wmi.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_ensure_export_wmi.p.gz deleted file mode 100644 index a0b4f4fd3..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_ensure_export_wmi.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_initialize_connection_os.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_initialize_connection_os.p.gz deleted file mode 100644 index 5373f0178..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_initialize_connection_os.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_initialize_connection_wmi.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_initialize_connection_wmi.p.gz deleted file mode 100644 index b2af2519f..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_initialize_connection_wmi.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_remove_export_os.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_remove_export_os.p.gz deleted file mode 100644 index 64fdc6bef..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_remove_export_os.p.gz and /dev/null differ diff --git a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_remove_export_wmi.p.gz b/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_remove_export_wmi.p.gz deleted file mode 100644 index 9a8fe70b4..000000000 Binary files a/cinder/tests/windows/stubs/test_windows.TestWindowsDriver.test_remove_export_wmi.p.gz and /dev/null differ diff --git a/cinder/tests/windows/windowsutils.py b/cinder/tests/windows/windowsutils.py deleted file mode 100644 index 4a35a5b4e..000000000 --- a/cinder/tests/windows/windowsutils.py +++ /dev/null @@ -1,145 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 Pedro Navarro Perez -# -# 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. - -""" -Windows storage classes to be used in testing. -""" - -import os - -from oslo.config import cfg - -# Check needed for unit testing on Unix -if os.name == 'nt': - import wmi - - -CONF = cfg.CONF - - -class WindowsUtils(object): - def __init__(self): - self.__conn_cimv2 = None - self.__conn_wmi = None - - @property - def _conn_cimv2(self): - if self.__conn_cimv2 is None: - self.__conn_cimv2 = wmi.WMI(moniker='//./root/cimv2') - return self.__conn_cimv2 - - @property - def _conn_wmi(self): - if self.__conn_wmi is None: - self.__conn_wmi = wmi.WMI(moniker='//./root/wmi') - return self.__conn_wmi - - def find_vhd_by_name(self, name): - '''Finds a volume by its name.''' - - wt_disks = self._conn_wmi.WT_Disk(Description=name) - return wt_disks - - def volume_exists(self, name): - '''Checks if a volume exists.''' - - wt_disks = self.find_vhd_by_name(name) - if len(wt_disks) > 0: - return True - return False - - def snapshot_exists(self, name): - '''Checks if a snapshot exists.''' - - wt_snapshots = self.find_snapshot_by_name(name) - if len(wt_snapshots) > 0: - return True - return False - - def find_snapshot_by_name(self, name): - '''Finds a snapshot by its name.''' - - wt_snapshots = self._conn_wmi.WT_Snapshot(Description=name) - return wt_snapshots - - def delete_volume(self, name): - '''Deletes a volume.''' - - wt_disk = self._conn_wmi.WT_Disk(Description=name)[0] - wt_disk.Delete_() - vhdfiles = self._conn_cimv2.query( - "Select * from CIM_DataFile where Name = '" + - self._get_vhd_path(name) + "'") - if len(vhdfiles) > 0: - vhdfiles[0].Delete() - - def _get_vhd_path(self, volume_name): - '''Gets the path disk of the volume.''' - - base_vhd_folder = CONF.windows_iscsi_lun_path - return os.path.join(base_vhd_folder, volume_name + ".vhd") - - def delete_snapshot(self, name): - '''Deletes a snapshot.''' - - wt_snapshot = self._conn_wmi.WT_Snapshot(Description=name)[0] - wt_snapshot.Delete_() - vhdfile = self._conn_cimv2.query( - "Select * from CIM_DataFile where Name = '" + - self._get_vhd_path(name) + "'")[0] - vhdfile.Delete() - - def find_initiator_ids(self, target_name, initiator_name): - '''Finds a initiator id by its name.''' - wt_idmethod = self._conn_wmi.WT_IDMethod(HostName=target_name, - Method=4, - Value=initiator_name) - return wt_idmethod - - def initiator_id_exists(self, target_name, initiator_name): - '''Checks if a initiatorId exists.''' - - wt_idmethod = self.find_initiator_ids(target_name, initiator_name) - if len(wt_idmethod) > 0: - return True - return False - - def find_exports(self, target_name): - '''Finds a export id by its name.''' - - wt_host = self._conn_wmi.WT_Host(HostName=target_name) - return wt_host - - def export_exists(self, target_name): - '''Checks if a export exists.''' - - wt_host = self.find_exports(target_name) - if len(wt_host) > 0: - return True - return False - - def delete_initiator_id(self, target_name, initiator_name): - '''Deletes a initiatorId.''' - - wt_init_id = self.find_initiator_ids(target_name, initiator_name)[0] - wt_init_id.Delete_() - - def delete_export(self, target_name): - '''Deletes an export.''' - - wt_host = self.find_exports(target_name)[0] - wt_host.RemoveAllWTDisks() - wt_host.Delete_() diff --git a/cinder/volume/drivers/windows.py b/cinder/volume/drivers/windows.py deleted file mode 100644 index 87672bb50..000000000 --- a/cinder/volume/drivers/windows.py +++ /dev/null @@ -1,245 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 Pedro Navarro Perez -# All Rights Reserved. -# -# 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. -""" -Volume driver for Windows Server 2012 - -This driver requires ISCSI target role installed - -""" - - -import os - -from oslo.config import cfg - -from cinder import exception -from cinder.openstack.common import log as logging -from cinder.volume import driver - -# Check needed for unit testing on Unix -if os.name == 'nt': - import wmi - - -LOG = logging.getLogger(__name__) - -windows_opts = [ - cfg.StrOpt('windows_iscsi_lun_path', - default='C:\iSCSIVirtualDisks', - help='Path to store VHD backed volumes'), -] - -CONF = cfg.CONF -CONF.register_opts(windows_opts) - - -class WindowsDriver(driver.ISCSIDriver): - """Executes volume driver commands on Windows Storage server.""" - - def __init__(self, *args, **kwargs): - super(WindowsDriver, self).__init__(*args, **kwargs) - - def do_setup(self, context): - """Setup the Windows Volume driver. - - Called one time by the manager after the driver is loaded. - Validate the flags we care about - """ - #Set the flags - self._conn_wmi = wmi.WMI(moniker='//./root/wmi') - self._conn_cimv2 = wmi.WMI(moniker='//./root/cimv2') - - def check_for_setup_error(self): - """Check that the driver is working and can communicate. - """ - #Invoking the portal an checking that is listening - wt_portal = self._conn_wmi.WT_Portal()[0] - listen = wt_portal.Listen - if not listen: - raise exception.VolumeBackendAPIException() - - def initialize_connection(self, volume, connector): - """Driver entry point to attach a volume to an instance. - """ - initiator_name = connector['initiator'] - target_name = volume['provider_location'] - - cl = self._conn_wmi.__getattr__("WT_IDMethod") - wt_idmethod = cl.new() - wt_idmethod.HostName = target_name - wt_idmethod.Method = 4 - wt_idmethod.Value = initiator_name - wt_idmethod.put() - #Getting the portal and port information - wt_portal = self._conn_wmi.WT_Portal()[0] - (address, port) = (wt_portal.Address, wt_portal.Port) - #Getting the host information - hosts = self._conn_wmi.WT_Host(Hostname=target_name) - host = hosts[0] - - properties = {} - properties['target_discovered'] = False - properties['target_portal'] = '%s:%s' % (address, port) - properties['target_iqn'] = host.TargetIQN - properties['target_lun'] = 0 - properties['volume_id'] = volume['id'] - - auth = volume['provider_auth'] - if auth: - (auth_method, auth_username, auth_secret) = auth.split() - - properties['auth_method'] = auth_method - properties['auth_username'] = auth_username - properties['auth_password'] = auth_secret - - return { - 'driver_volume_type': 'iscsi', - 'data': properties, - } - - def terminate_connection(self, volume, connector, **kwargs): - """Driver entry point to unattach a volume from an instance. - - Unmask the LUN on the storage system so the given intiator can no - longer access it. - """ - initiator_name = connector['initiator'] - provider_location = volume['provider_location'] - #DesAssigning target to initiators - wt_idmethod = self._conn_wmi.WT_IDMethod(HostName=provider_location, - Method=4, - Value=initiator_name)[0] - wt_idmethod.Delete_() - - def create_volume(self, volume): - """Driver entry point for creating a new volume.""" - vhd_path = self._get_vhd_path(volume) - vol_name = volume['name'] - #The WMI procedure returns a Generic failure - cl = self._conn_wmi.__getattr__("WT_Disk") - cl.NewWTDisk(DevicePath=vhd_path, - Description=vol_name, - SizeInMB=volume['size'] * 1024) - - def _get_vhd_path(self, volume): - base_vhd_folder = CONF.windows_iscsi_lun_path - if not os.path.exists(base_vhd_folder): - LOG.debug(_('Creating folder %s '), base_vhd_folder) - os.makedirs(base_vhd_folder) - return os.path.join(base_vhd_folder, str(volume['name']) + ".vhd") - - def delete_volume(self, volume): - """Driver entry point for destroying existing volumes.""" - vol_name = volume['name'] - wt_disk = self._conn_wmi.WT_Disk(Description=vol_name)[0] - wt_disk.Delete_() - vhdfiles = self._conn_cimv2.query( - "Select * from CIM_DataFile where Name = '" + - self._get_vhd_path(volume) + "'") - if len(vhdfiles) > 0: - vhdfiles[0].Delete() - - def create_snapshot(self, snapshot): - """Driver entry point for creating a snapshot. - """ - #Getting WT_Snapshot class - vol_name = snapshot['volume_name'] - snapshot_name = snapshot['name'] - - wt_disk = self._conn_wmi.WT_Disk(Description=vol_name)[0] - #API Calls gets Generic Failure - cl = self._conn_wmi.__getattr__("WT_Snapshot") - disk_id = wt_disk.WTD - out = cl.Create(WTD=disk_id) - #Setting description since it used as a KEY - wt_snapshot_created = self._conn_wmi.WT_Snapshot(Id=out[0])[0] - wt_snapshot_created.Description = snapshot_name - wt_snapshot_created.put() - - def create_volume_from_snapshot(self, volume, snapshot): - """Driver entry point for exporting snapshots as volumes.""" - snapshot_name = snapshot['name'] - wt_snapshot = self._conn_wmi.WT_Snapshot(Description=snapshot_name)[0] - disk_id = wt_snapshot.Export()[0] - wt_disk = self._conn_wmi.WT_Disk(WTD=disk_id)[0] - wt_disk.Description = volume['name'] - wt_disk.put() - - def delete_snapshot(self, snapshot): - """Driver entry point for deleting a snapshot.""" - snapshot_name = snapshot['name'] - wt_snapshot = self._conn_wmi.WT_Snapshot(Description=snapshot_name)[0] - wt_snapshot.Delete_() - - def _do_export(self, _ctx, volume, ensure=False): - """Do all steps to get disk exported as LUN 0 at separate target. - - :param volume: reference of volume to be exported - :param ensure: if True, ignore errors caused by already existing - resources - :return: iscsiadm-formatted provider location string - """ - target_name = "%s%s" % (CONF.iscsi_target_prefix, volume['name']) - #ISCSI target creation - try: - cl = self._conn_wmi.__getattr__("WT_Host") - cl.NewHost(HostName=target_name) - except Exception as exc: - excep_info = exc.com_error.excepinfo[2] - if not ensure or excep_info.find(u'The file exists') == -1: - raise - else: - LOG.info(_('Ignored target creation error "%s"' - ' while ensuring export'), exc) - #Get the disk to add - vol_name = volume['name'] - q = self._conn_wmi.WT_Disk(Description=vol_name) - if not len(q): - LOG.debug(_('Disk not found: %s'), vol_name) - return None - wt_disk = q[0] - wt_host = self._conn_wmi.WT_Host(HostName=target_name)[0] - wt_host.AddWTDisk(wt_disk.WTD) - - return target_name - - def ensure_export(self, context, volume): - """Driver entry point to get the export info for an existing volume.""" - self._do_export(context, volume, ensure=True) - - def create_export(self, context, volume): - """Driver entry point to get the export info for a new volume.""" - loc = self._do_export(context, volume, ensure=False) - return {'provider_location': loc} - - def remove_export(self, context, volume): - """Driver exntry point to remove an export for a volume. - """ - target_name = "%s%s" % (CONF.iscsi_target_prefix, volume['name']) - - #Get ISCSI target - wt_host = self._conn_wmi.WT_Host(HostName=target_name)[0] - wt_host.RemoveAllWTDisks() - wt_host.Delete_() - - def copy_image_to_volume(self, context, volume, image_service, image_id): - """Fetch the image from image_service and write it to the volume.""" - raise NotImplementedError() - - def copy_volume_to_image(self, context, volume, image_service, image_meta): - """Copy the volume to the specified image.""" - raise NotImplementedError() diff --git a/cinder/volume/drivers/windows/__init__.py b/cinder/volume/drivers/windows/__init__.py new file mode 100644 index 000000000..2b0d839bd --- /dev/null +++ b/cinder/volume/drivers/windows/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2013 OpenStack LLC +# +# 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. diff --git a/cinder/volume/drivers/windows/windows.py b/cinder/volume/drivers/windows/windows.py new file mode 100644 index 000000000..24cf40177 --- /dev/null +++ b/cinder/volume/drivers/windows/windows.py @@ -0,0 +1,227 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Pedro Navarro Perez +# All Rights Reserved. +# +# 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. +""" +Volume driver for Windows Server 2012 + +This driver requires ISCSI target role installed + +""" + +import os + +from oslo.config import cfg + +from cinder.image import image_utils +from cinder.openstack.common import log as logging +from cinder.volume import driver +from cinder.volume.drivers.windows import windows_utils + +LOG = logging.getLogger(__name__) + +windows_opts = [ + cfg.StrOpt('windows_iscsi_lun_path', + default='C:\iSCSIVirtualDisks', + help='Path to store VHD backed volumes'), +] + +CONF = cfg.CONF +CONF.register_opts(windows_opts) + + +class WindowsDriver(driver.ISCSIDriver): + """Executes volume driver commands on Windows Storage server.""" + + VERSION = '1.0.0' + + def __init__(self, *args, **kwargs): + super(WindowsDriver, self).__init__(*args, **kwargs) + self.configuration = kwargs.get('configuration', None) + if self.configuration: + self.configuration.append_config_values(windows_opts) + + def do_setup(self, context): + """Setup the Windows Volume driver. + + Called one time by the manager after the driver is loaded. + Validate the flags we care about + """ + self.utils = windows_utils.WindowsUtils() + + def check_for_setup_error(self): + """Check that the driver is working and can communicate.""" + self.utils.check_for_setup_error() + + def initialize_connection(self, volume, connector): + """Driver entry point to attach a volume to an instance.""" + initiator_name = connector['initiator'] + target_name = volume['provider_location'] + + self.utils.associate_initiator_with_iscsi_target(target_name, + initiator_name) + + properties = self.utils.get_host_information(volume, target_name) + + return { + 'driver_volume_type': 'iscsi', + 'data': properties, + } + + def terminate_connection(self, volume, connector, **kwargs): + """Driver entry point to unattach a volume from an instance. + + Unmask the LUN on the storage system so the given initiator can no + longer access it. + """ + initiator_name = connector['initiator'] + target_name = volume['provider_location'] + self.utils.delete_iscsi_target(initiator_name, target_name) + + def create_volume(self, volume): + """Driver entry point for creating a new volume.""" + vhd_path = self.local_path(volume) + vol_name = volume['name'] + vol_size = volume['size'] + + self.utils.create_volume(vhd_path, vol_name, vol_size) + + def local_path(self, volume): + base_vhd_folder = self.configuration.windows_iscsi_lun_path + if not os.path.exists(base_vhd_folder): + LOG.debug(_('Creating folder %s '), base_vhd_folder) + os.makedirs(base_vhd_folder) + return os.path.join(base_vhd_folder, str(volume['name']) + ".vhd") + + def delete_volume(self, volume): + """Driver entry point for destroying existing volumes.""" + vol_name = volume['name'] + vhd_path = self.local_path(volume) + + self.utils.delete_volume(vol_name, vhd_path) + + def create_snapshot(self, snapshot): + """Driver entry point for creating a snapshot.""" + # Getting WT_Snapshot class + vol_name = snapshot['volume_name'] + snapshot_name = snapshot['name'] + + self.utils.create_snapshot(vol_name, snapshot_name) + + def create_volume_from_snapshot(self, volume, snapshot): + """Driver entry point for exporting snapshots as volumes.""" + snapshot_name = snapshot['name'] + vol_name = volume['name'] + self.utils.create_volume_from_snapshot(vol_name, snapshot_name) + + def delete_snapshot(self, snapshot): + """Driver entry point for deleting a snapshot.""" + snapshot_name = snapshot['name'] + self.utils.delete_snapshot(snapshot_name) + + def _do_export(self, _ctx, volume, ensure=False): + """Do all steps to get disk exported as LUN 0 at separate target. + + :param volume: reference of volume to be exported + :param ensure: if True, ignore errors caused by already existing + resources + :return: iscsiadm-formatted provider location string + """ + target_name = "%s%s" % (self.configuration.iscsi_target_prefix, + volume['name']) + self.utils.create_iscsi_target(target_name, ensure) + + # Get the disk to add + vol_name = volume['name'] + self.utils.add_disk_to_target(vol_name, target_name) + + return target_name + + def ensure_export(self, context, volume): + """Driver entry point to get the export info for an existing volume.""" + self._do_export(context, volume, ensure=True) + + def create_export(self, context, volume): + """Driver entry point to get the export info for a new volume.""" + loc = self._do_export(context, volume, ensure=False) + return {'provider_location': loc} + + def remove_export(self, context, volume): + """Driver entry point to remove an export for a volume. + """ + target_name = "%s%s" % (self.configuration.iscsi_target_prefix, + volume['name']) + + self.utils.remove_iscsi_target(target_name) + + def copy_image_to_volume(self, context, volume, image_service, image_id): + """Fetch the image from image_service and write it to the volume.""" + # Convert to VHD and file back to VHD + image_utils.fetch_to_vhd(context, image_service, image_id, + self.local_path(volume)) + + def copy_volume_to_image(self, context, volume, image_service, image_meta): + """Copy the volume to the specified image.""" + + # Copy the volume to the image conversion dir + temp_vhd_path = os.path.join(self.configuration.image_conversion_dir, + str(image_meta['id']) + ".vhd") + self.utils.copy_vhd_disk(self.local_path(volume), temp_vhd_path) + image_utils.upload_volume(context, image_service, image_meta, + temp_vhd_path, 'vpc') + + def create_cloned_volume(self, volume, src_vref): + """Creates a clone of the specified volume.""" + # Create a new volume + # Copy VHD file of the volume to clone to the created volume + self.create_volume(volume) + self.utils.copy_vhd_disk(self.local_path(src_vref), + self.local_path(volume)) + + def get_volume_stats(self, refresh=False): + """Get volume stats. + + If 'refresh' is True, run update the stats first. + """ + if refresh: + self._update_volume_stats() + + return self._stats + + def _update_volume_stats(self): + """Retrieve stats info for Windows device.""" + + LOG.debug(_("Updating volume stats")) + data = {} + backend_name = self.__class__.__name__ + if self.configuration: + backend_name = self.configuration.safe_get('volume_backend_name') + data["volume_backend_name"] = backend_name or self.__class__.__name__ + data["vendor_name"] = 'Microsoft' + data["driver_version"] = self.VERSION + data["storage_protocol"] = 'iSCSI' + data['total_capacity_gb'] = 'infinite' + data['free_capacity_gb'] = 'infinite' + data['reserved_percentage'] = 100 + data['QoS_support'] = False + self._stats = data + + def extend_volume(self, volume, new_size): + """Extend an Existing Volume.""" + old_size = volume['size'] + LOG.debug(_("Extended volume from %(old_size) to %(new_size)"), + {'old_size': old_size, 'new_size': new_size}) + additional_size = (new_size - old_size) * 1024 + self.utils.extend(volume['name'], additional_size) diff --git a/cinder/volume/drivers/windows/windows_utils.py b/cinder/volume/drivers/windows/windows_utils.py new file mode 100644 index 000000000..938ac08f2 --- /dev/null +++ b/cinder/volume/drivers/windows/windows_utils.py @@ -0,0 +1,297 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 Pedro Navarro Perez +# All Rights Reserved. +# +# 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. +""" +Utility class for Windows Storage Server 2012 volume related operations. +""" + +import os + +from cinder import exception +from cinder.openstack.common import log as logging + +# Check needed for unit testing on Unix +if os.name == 'nt': + import wmi + +LOG = logging.getLogger(__name__) + + +class WindowsUtils(object): + """Executes volume driver commands on Windows Storage server.""" + + def __init__(self, *args, **kwargs): + # Set the flags + self._conn_wmi = wmi.WMI(moniker='//./root/wmi') + self._conn_cimv2 = wmi.WMI(moniker='//./root/cimv2') + + def check_for_setup_error(self): + """Check that the driver is working and can communicate. + Invokes the portal and checks that is listening ISCSI traffic. + """ + try: + wt_portal = self._conn_wmi.WT_Portal()[0] + listen = wt_portal.Listen + except wmi.x_wmi as exc: + err_msg = (_('check_for_setup_error: the state of the WT Portal ' + 'could not be verified. WMI exception: %s')) + LOG.error(err_msg % exc) + raise exception.VolumeBackendAPIException(data=err_msg % exc) + + if not listen: + err_msg = (_('check_for_setup_error: there is no ISCSI traffic ' + 'listening.')) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def get_host_information(self, volume, target_name): + """Getting the portal and port information.""" + try: + wt_portal = self._conn_wmi.WT_Portal()[0] + except wmi.x_wmi as exc: + err_msg = (_('get_host_information: the state of the WT Portal ' + 'could not be verified. WMI exception: %s')) + LOG.error(err_msg % exc) + raise exception.VolumeBackendAPIException(data=err_msg % exc) + (address, port) = (wt_portal.Address, wt_portal.Port) + # Getting the host information + try: + hosts = self._conn_wmi.WT_Host(Hostname=target_name) + host = hosts[0] + except wmi.x_wmi as exc: + err_msg = (_('get_host_information: the ISCSI target information ' + 'could not be retrieved. WMI exception: %s')) + LOG.error(err_msg % exc) + raise exception.VolumeBackendAPIException(data=err_msg) + + properties = {} + properties['target_discovered'] = False + properties['target_portal'] = '%s:%s' % (address, port) + properties['target_iqn'] = host.TargetIQN + properties['target_lun'] = 0 + properties['volume_id'] = volume['id'] + + auth = volume['provider_auth'] + if auth: + (auth_method, auth_username, auth_secret) = auth.split() + + properties['auth_method'] = auth_method + properties['auth_username'] = auth_username + properties['auth_password'] = auth_secret + + def associate_initiator_with_iscsi_target(self, initiator_name, + target_name): + """Sets information used by the iSCSI target entry.""" + try: + cl = self._conn_wmi.__getattr__("WT_IDMethod") + wt_idmethod = cl.new() + wt_idmethod.HostName = target_name + # Identification method is IQN + wt_idmethod.Method = 4 + wt_idmethod.Value = initiator_name + wt_idmethod.put() + except wmi.x_wmi as exc: + err_msg = (_('associate_initiator_with_iscsi_target: an ' + 'association between initiator: %(init)s and ' + 'target name: %(target)s could not be established. ' + 'WMI exception: %(wmi_exc)s') % + {'init': initiator_name, 'target': target_name, + 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def delete_iscsi_target(self, initiator_name, target_name): + """Removes iSCSI targets to hosts.""" + + try: + wt_idmethod = self._conn_wmi.WT_IDMethod(HostName=target_name, + Method=4, + Value=initiator_name)[0] + wt_idmethod.Delete_() + except wmi.x_wmi as exc: + err_msg = (_( + 'delete_iscsi_target: error when deleting the iscsi target ' + 'associated with target name: %(target)s . ' + 'WMI exception: %(wmi_exc)s') % {'target': target_name, + 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def create_volume(self, vhd_path, vol_name, vol_size): + """Creates a volume""" + try: + cl = self._conn_wmi.__getattr__("WT_Disk") + cl.NewWTDisk(DevicePath=vhd_path, + Description=vol_name, + SizeInMB=vol_size * 1024) + except wmi.x_wmi as exc: + err_msg = (_( + 'create_volume: error when creating the volume name: ' + '%(vol_name)s . WMI exception: ' + '%(wmi_exc)s') % {'vol_name': vol_name, 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def delete_volume(self, vol_name, vhd_path): + """Driver entry point for destroying existing volumes.""" + try: + wt_disk = self._conn_wmi.WT_Disk(Description=vol_name)[0] + wt_disk.Delete_() + vhdfiles = self._conn_cimv2.query( + "Select * from CIM_DataFile where Name = '" + + vhd_path + "'") + if len(vhdfiles) > 0: + vhdfiles[0].Delete() + except wmi.x_wmi as exc: + err_msg = (_( + 'delete_volume: error when deleting the volume name: ' + '%(vol_name)s . WMI exception: ' + '%(wmi_exc)s') % {'vol_name': vol_name, 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def create_snapshot(self, vol_name, snapshot_name): + """Driver entry point for creating a snapshot.""" + try: + wt_disk = self._conn_wmi.WT_Disk(Description=vol_name)[0] + # API Calls gets Generic Failure + cl = self._conn_wmi.__getattr__("WT_Snapshot") + disk_id = wt_disk.WTD + out = cl.Create(WTD=disk_id) + # Setting description since it used as a KEY + wt_snapshot_created = self._conn_wmi.WT_Snapshot(Id=out[0])[0] + wt_snapshot_created.Description = snapshot_name + wt_snapshot_created.put() + except wmi.x_wmi as exc: + err_msg = (_( + 'create_snapshot: error when creating the snapshot name: ' + '%(vol_name)s . WMI exception: ' + '%(wmi_exc)s') % {'vol_name': snapshot_name, 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def create_volume_from_snapshot(self, vol_name, snap_name): + """Driver entry point for exporting snapshots as volumes.""" + try: + wt_snapshot = self._conn_wmi.WT_Snapshot(Description=snap_name)[0] + disk_id = wt_snapshot.Export()[0] + wt_disk = self._conn_wmi.WT_Disk(WTD=disk_id)[0] + wt_disk.Description = vol_name + wt_disk.put() + except wmi.x_wmi as exc: + err_msg = (_( + 'create_volume_from_snapshot: error when creating the volume ' + 'name: %(vol_name)s from snapshot name: %(snap_name)s. ' + 'WMI exception: %(wmi_exc)s') % {'vol_name': vol_name, + 'snap_name': snap_name, + 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def delete_snapshot(self, snap_name): + """Driver entry point for deleting a snapshot.""" + try: + wt_snapshot = self._conn_wmi.WT_Snapshot(Description=snap_name)[0] + wt_snapshot.Delete_() + except wmi.x_wmi as exc: + err_msg = (_( + 'delete_snapshot: error when deleting the snapshot name: ' + '%(snap_name)s . WMI exception: ' + '%(wmi_exc)s') % {'snap_name': snap_name, 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def create_iscsi_target(self, target_name, ensure): + """Creates ISCSI target.""" + try: + cl = self._conn_wmi.__getattr__("WT_Host") + cl.NewHost(HostName=target_name) + except wmi.x_wmi as exc: + excep_info = exc.com_error.excepinfo[2] + if not ensure or excep_info.find(u'The file exists') == -1: + err_msg = (_( + 'create_iscsi_target: error when creating iscsi target: ' + '%(tar_name)s . WMI exception: ' + '%(wmi_exc)s') % {'tar_name': target_name, 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + else: + LOG.info(_('Ignored target creation error "%s"' + ' while ensuring export'), exc) + + def remove_iscsi_target(self, target_name): + """Removes ISCSI target.""" + try: + wt_host = self._conn_wmi.WT_Host(HostName=target_name)[0] + wt_host.RemoveAllWTDisks() + wt_host.Delete_() + except wmi.x_wmi as exc: + err_msg = (_( + 'remove_iscsi_target: error when deleting iscsi target: ' + '%(tar_name)s . WMI exception: ' + '%(wmi_exc)s') % {'tar_name': target_name, 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def add_disk_to_target(self, vol_name, target_name): + """Adds the disk to the target""" + try: + q = self._conn_wmi.WT_Disk(Description=vol_name) + wt_disk = q[0] + wt_host = self._conn_wmi.WT_Host(HostName=target_name)[0] + wt_host.AddWTDisk(wt_disk.WTD) + except wmi.x_wmi as exc: + err_msg = (_( + 'add_disk_to_target: error adding disk associated to volume : ' + '%(vol_name)s to the target name: %(tar_name)s ' + '. WMI exception: %(wmi_exc)s') % {'tar_name': target_name, + 'vol_name': vol_name, + 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def copy_vhd_disk(self, source_path, destination_path): + """Copy the vhd disk from source path to destination path.""" + try: + vhdfiles = self._conn_cimv2.query( + "Select * from CIM_DataFile where Name = '" + + source_path + "'") + if len(vhdfiles) > 0: + vhdfiles[0].Copy(destination_path) + except wmi.x_wmi as exc: + err_msg = (_( + 'copy_vhd_disk: error when copying disk from source path : ' + '%(src_path)s to destination path: %(dest_path)s ' + '. WMI exception: ' + '%(wmi_exc)s') % {'src_path': source_path, + 'dest_path': destination_path, + 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + def extend(self, vol_name, additional_size): + """Extend an existing volume.""" + try: + q = self._conn_wmi.WT_Disk(Description=vol_name) + wt_disk = q[0] + wt_disk.Extend(additional_size) + except wmi.x_wmi as exc: + err_msg = (_( + 'extend: error when extending the volumne: %(vol_name)s ' + '.WMI exception: %(wmi_exc)s') % {'vol_name': vol_name, + 'wmi_exc': exc}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) diff --git a/cinder/volume/manager.py b/cinder/volume/manager.py index 4dea45a4f..f2afd322d 100644 --- a/cinder/volume/manager.py +++ b/cinder/volume/manager.py @@ -102,7 +102,9 @@ MAPPING = { 'cinder.volume.storwize_svc.StorwizeSVCDriver': 'cinder.volume.drivers.storwize_svc.StorwizeSVCDriver', 'cinder.volume.windows.WindowsDriver': - 'cinder.volume.drivers.windows.WindowsDriver', + 'cinder.volume.drivers.windows.windows.WindowsDriver', + 'cinder.volume.drivers.windows.WindowsDriver': + 'cinder.volume.drivers.windows.windows.WindowsDriver', 'cinder.volume.xiv.XIVDriver': 'cinder.volume.drivers.xiv_ds8k.XIVDS8KDriver', 'cinder.volume.drivers.xiv.XIVDriver': diff --git a/etc/cinder/cinder.conf.sample b/etc/cinder/cinder.conf.sample index bc8dc6280..693089aca 100644 --- a/etc/cinder/cinder.conf.sample +++ b/etc/cinder/cinder.conf.sample @@ -1541,7 +1541,7 @@ # -# Options defined in cinder.volume.drivers.windows +# Options defined in cinder.volume.drivers.windows.windows # # Path to store VHD backed volumes (string value)