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
This commit is contained in:
parent
b67caf3256
commit
106bf560e2
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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._drv = windows.WindowsDriver()
|
||||
self._drv.do_setup({})
|
||||
self._wutils = windowsutils.WindowsUtils()
|
||||
self._driver = windows.WindowsDriver(configuration=configuration)
|
||||
self._driver.do_setup({})
|
||||
|
||||
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__]
|
||||
]
|
||||
|
||||
self._inject_mocks_in_modules(modules_to_mock, modules_to_test)
|
||||
|
||||
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()
|
||||
def fake_local_path(self, volume):
|
||||
return os.path.join(CONF.windows_iscsi_lun_path,
|
||||
str(volume['name']) + ".vhd")
|
||||
|
||||
def test_check_for_setup_errors(self):
|
||||
self._drv.check_for_setup_error()
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
self._mox.StubOutWithMock(windows_utils.WindowsUtils,
|
||||
'check_for_setup_error')
|
||||
windows_utils.WindowsUtils.check_for_setup_error()
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
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()
|
||||
|
||||
wt_disks = self._wutils.find_vhd_by_name(self._volume_data['name'])
|
||||
self.assertEquals(len(wt_disks), 1)
|
||||
self.stubs.Set(drv, 'local_path', self.fake_local_path)
|
||||
|
||||
def _create_volume(self):
|
||||
self._volume_data = db_fakes.get_fake_volume_info(self._volume_id)
|
||||
self._drv.create_volume(self._volume_data)
|
||||
self._mox.StubOutWithMock(windows_utils.WindowsUtils,
|
||||
'create_volume')
|
||||
|
||||
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
|
||||
|
||||
self._drv.delete_volume(self._volume_data)
|
||||
vol = db_fakes.get_fake_volume_info()
|
||||
|
||||
wt_disks = self._wutils.find_vhd_by_name(self._volume_data['name'])
|
||||
self.assertEquals(len(wt_disks), 0)
|
||||
mox.StubOutWithMock(drv, 'local_path')
|
||||
drv.local_path(vol).AndReturn(self.fake_local_path(vol))
|
||||
|
||||
self._mox.StubOutWithMock(windows_utils.WindowsUtils,
|
||||
'delete_volume')
|
||||
windows_utils.WindowsUtils.delete_volume(vol['name'],
|
||||
self.fake_local_path(vol))
|
||||
mox.ReplayAll()
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
self.stubs.Set(drv, 'local_path', self.fake_local_path(snapshot))
|
||||
|
||||
snapshot_name = self._snapshot_data['name']
|
||||
wt_snapshots = self._wutils.find_snapshot_by_name(snapshot_name)
|
||||
self.assertEquals(len(wt_snapshots), 1)
|
||||
windows_utils.WindowsUtils.create_snapshot(volume['name'],
|
||||
snapshot['name'])
|
||||
|
||||
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.ReplayAll()
|
||||
|
||||
drv.create_snapshot(snapshot)
|
||||
|
||||
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
|
||||
|
||||
self._volume_data_2 = db_fakes.get_fake_volume_info(self._volume_id_2)
|
||||
snapshot = db_fakes.get_fake_snapshot_info()
|
||||
volume = db_fakes.get_fake_volume_info()
|
||||
|
||||
self._drv.create_volume_from_snapshot(self._volume_data_2,
|
||||
self._snapshot_data)
|
||||
self._mox.StubOutWithMock(windows_utils.WindowsUtils,
|
||||
'create_volume_from_snapshot')
|
||||
windows_utils.WindowsUtils.\
|
||||
create_volume_from_snapshot(volume['name'], snapshot['name'])
|
||||
|
||||
wt_disks = self._wutils.find_vhd_by_name(self._volume_data_2['name'])
|
||||
self.assertEquals(len(wt_disks), 1)
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.create_volume_from_snapshot(volume, snapshot)
|
||||
|
||||
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
|
||||
|
||||
retval = self._drv.create_export({}, self._volume_data)
|
||||
volume = db_fakes.get_fake_volume_info()
|
||||
|
||||
volume_name = self._volume_data['name']
|
||||
self.assertEquals(
|
||||
retval,
|
||||
{'provider_location': "%s%s" % (CONF.iscsi_target_prefix,
|
||||
volume_name)})
|
||||
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,
|
||||
IgnoreArg())
|
||||
self._mox.StubOutWithMock(windows_utils.WindowsUtils,
|
||||
'add_disk_to_target')
|
||||
windows_utils.WindowsUtils.add_disk_to_target(volume['name'],
|
||||
initiator_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
|
||||
|
||||
self._drv.create_export({}, self._volume_data)
|
||||
volume = db_fakes.get_fake_volume_info()
|
||||
initiator_name = "%s%s" % (CONF.iscsi_target_prefix, volume['name'])
|
||||
|
||||
self._connector_data = db_fakes.get_fake_connector_info(self._iqn)
|
||||
connector = db_fakes.get_fake_connector_info()
|
||||
|
||||
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']
|
||||
self._mox.StubOutWithMock(windows_utils.WindowsUtils,
|
||||
'associate_initiator_with_iscsi_target')
|
||||
windows_utils.WindowsUtils.associate_initiator_with_iscsi_target(
|
||||
volume['provider_location'], initiator_name, )
|
||||
|
||||
wt_initiator_ids = self._wutils.find_initiator_ids(target_name,
|
||||
initiator_name)
|
||||
self.assertEquals(len(wt_initiator_ids), 1)
|
||||
self._mox.StubOutWithMock(windows_utils.WindowsUtils,
|
||||
'get_host_information')
|
||||
windows_utils.WindowsUtils.get_host_information(
|
||||
volume, volume['provider_location'])
|
||||
|
||||
properties = init_data['data']
|
||||
self.assertNotEqual(properties['target_iqn'], None)
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.initialize_connection(volume, connector)
|
||||
|
||||
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
|
||||
|
||||
self._drv.ensure_export({}, self._volume_data)
|
||||
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()
|
||||
|
||||
drv.ensure_export(None, volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_remove_export(self):
|
||||
#Create a volume
|
||||
self._create_volume()
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self._drv.create_export({}, self._volume_data)
|
||||
volume = db_fakes.get_fake_volume_info()
|
||||
|
||||
self._drv.remove_export({}, self._volume_data)
|
||||
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()
|
||||
|
||||
drv.extend_volume(volume, new_size)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
|
|
@ -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)
|
|
@ -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_connector_info(initiator):
|
||||
return {'initiator': initiator, }
|
||||
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():
|
||||
return {'initiator': 'iqn.2010-10.org.openstack:' + 'volume_name', }
|
||||
|
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
Files with extension p.gz are compressed pickle files containing serialized
|
||||
mocks used during unit testing
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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_()
|
|
@ -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()
|
|
@ -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.
|
|
@ -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)
|
|
@ -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)
|
|
@ -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':
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue