Unified Volume Driver for IBM XIV and IBM DS8K

* Refactored xiv.py to xiv_ds8k.py
    * Updated tests
    * Updated flags
    * Updated compatibility checks
    * DocImpact
    * Added extend_volume and migrate_volume

implements blueprint ibm-unify-xiv-ds8k

Change-Id: Ifd64fc1dab91ada6fd42a7e92a41361cf220042f
This commit is contained in:
Erik Zaadi 2013-08-12 13:27:19 +03:00
parent 289f2ccba9
commit 77bdcf525e
7 changed files with 758 additions and 604 deletions

View File

@ -25,7 +25,7 @@ CONF = cfg.CONF
CONF.import_opt('iscsi_num_targets', 'cinder.volume.drivers.lvm')
CONF.import_opt('policy_file', 'cinder.policy')
CONF.import_opt('volume_driver', 'cinder.volume.manager')
CONF.import_opt('xiv_proxy', 'cinder.volume.drivers.xiv')
CONF.import_opt('xiv_ds8k_proxy', 'cinder.volume.drivers.xiv_ds8k')
CONF.import_opt('backup_driver', 'cinder.backup.manager')
def_vol_type = 'fake_vol_type'
@ -44,5 +44,7 @@ def set_defaults(conf):
conf.set_default('connection', 'sqlite://', group='database')
conf.set_default('sqlite_synchronous', False)
conf.set_default('policy_file', 'cinder/tests/policy.json')
conf.set_default('xiv_proxy', 'cinder.tests.test_xiv.XIVFakeProxyDriver')
conf.set_default(
'xiv_ds8k_proxy',
'cinder.tests.test_xiv_ds8k.XIVDS8KFakeProxyDriver')
conf.set_default('backup_driver', 'cinder.tests.backup.fake_service')

View File

@ -33,7 +33,7 @@ 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"
XIV_MODULE = "cinder.volume.drivers.xiv.XIVDriver"
XIV_DS8K_MODULE = "cinder.volume.drivers.xiv_ds8k.XIVDS8KDriver"
ZADARA_MODULE = "cinder.volume.drivers.zadara.ZadaraVPSAISCSIDriver"
@ -148,11 +148,11 @@ class VolumeDriverCompatibility(test.TestCase):
def test_xiv_old(self):
self._load_driver('cinder.volume.xiv.XIVDriver')
self.assertEquals(self._driver_module_name(), XIV_MODULE)
self.assertEquals(self._driver_module_name(), XIV_DS8K_MODULE)
def test_xiv_new(self):
self._load_driver(XIV_MODULE)
self.assertEquals(self._driver_module_name(), XIV_MODULE)
def test_xiv_ds8k_new(self):
self._load_driver(XIV_DS8K_MODULE)
self.assertEquals(self._driver_module_name(), XIV_DS8K_MODULE)
def test_zadara_old(self):
self._load_driver('cinder.volume.zadara.ZadaraVPSAISCSIDriver')

View File

@ -1,7 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 IBM Corp.
# Copyright (c) 2012 OpenStack LLC.
# Copyright 2013 IBM Corp.
# Copyright (c) 2013 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -27,7 +27,7 @@ from oslo.config import cfg
from cinder import exception
from cinder import test
from cinder.volume import configuration as conf
from cinder.volume.drivers import xiv
from cinder.volume.drivers import xiv_ds8k
FAKE = "fake"
@ -40,27 +40,30 @@ CONNECTOR = {'initiator': "iqn.2012-07.org.fake:01:948f189c4695", }
CONF = cfg.CONF
class XIVFakeProxyDriver(object):
"""Fake XIV Proxy Driver."""
class XIVDS8KFakeProxyDriver(object):
"""Fake IBM XIV and DS8K Proxy Driver."""
def __init__(self, xiv_info, logger, expt):
def __init__(self, xiv_ds8k_info, logger, expt, driver=None):
"""
Initialize Proxy
"""
self.xiv_info = xiv_info
self.xiv_ds8k_info = xiv_ds8k_info
self.logger = logger
self.exception = expt
self.xiv_portal = \
self.xiv_iqn = FAKE
self.xiv_ds8k_portal = \
self.xiv_ds8k_iqn = FAKE
self.volumes = {}
self.driver = driver
def setup(self, context):
if self.xiv_info['xiv_user'] != CONF.san_login:
if self.xiv_ds8k_info['xiv_ds8k_user'] != self.driver\
.configuration.san_login:
raise self.exception.NotAuthorized()
if self.xiv_info['xiv_address'] != CONF.san_ip:
if self.xiv_ds8k_info['xiv_ds8k_address'] != self.driver\
.configuration.san_ip:
raise self.exception.HostNotFound(host='fake')
def create_volume(self, volume):
@ -85,14 +88,14 @@ class XIVFakeProxyDriver(object):
return {'driver_volume_type': 'iscsi',
'data': {'target_discovered': True,
'target_discovered': True,
'target_portal': self.xiv_portal,
'target_iqn': self.xiv_iqn,
'target_portal': self.xiv_ds8k_portal,
'target_iqn': self.xiv_ds8k_iqn,
'target_lun': lun_id,
'volume_id': volume['id'],
'multipath': True,
'provider_location': "%s,1 %s %s" % (
self.xiv_portal,
self.xiv_iqn,
self.xiv_ds8k_portal,
self.xiv_ds8k_iqn,
lun_id), },
}
@ -111,41 +114,53 @@ class XIVFakeProxyDriver(object):
== connector)
class XIVVolumeDriverTest(test.TestCase):
"""Test IBM XIV volume driver."""
class XIVDS8KVolumeDriverTest(test.TestCase):
"""Test IBM XIV and DS8K volume driver."""
def setUp(self):
"""Initialize IVM XIV Driver."""
super(XIVVolumeDriverTest, self).setUp()
"""Initialize IBM XIV and DS8K Driver."""
super(XIVDS8KVolumeDriverTest, self).setUp()
configuration = mox.MockObject(conf.Configuration)
configuration.san_is_local = False
configuration.xiv_ds8k_proxy = \
'cinder.tests.test_xiv_ds8k.XIVDS8KFakeProxyDriver'
configuration.xiv_ds8k_connection_type = 'iscsi'
configuration.san_ip = FAKE
configuration.san_login = FAKE
configuration.san_clustername = FAKE
configuration.san_password = FAKE
configuration.append_config_values(mox.IgnoreArg())
self.driver = xiv.XIVDriver(configuration=configuration)
self.driver = xiv_ds8k.XIVDS8KDriver(configuration=configuration)
def test_initialized_should_set_xiv_info(self):
"""Test that the san flags are passed to the XIV proxy."""
def test_initialized_should_set_xiv_ds8k_info(self):
"""Test that the san flags are passed to the IBM proxy."""
self.assertEquals(self.driver.xiv_proxy.xiv_info['xiv_user'],
CONF.san_login)
self.assertEquals(self.driver.xiv_proxy.xiv_info['xiv_pass'],
CONF.san_password)
self.assertEquals(self.driver.xiv_proxy.xiv_info['xiv_address'],
CONF.san_ip)
self.assertEquals(self.driver.xiv_proxy.xiv_info['xiv_vol_pool'],
CONF.san_clustername)
self.assertEquals(
self.driver.xiv_ds8k_proxy.xiv_ds8k_info['xiv_ds8k_user'],
self.driver.configuration.san_login)
self.assertEquals(
self.driver.xiv_ds8k_proxy.xiv_ds8k_info['xiv_ds8k_pass'],
self.driver.configuration.san_password)
self.assertEquals(
self.driver.xiv_ds8k_proxy.xiv_ds8k_info['xiv_ds8k_address'],
self.driver.configuration.san_ip)
self.assertEquals(
self.driver.xiv_ds8k_proxy.xiv_ds8k_info['xiv_ds8k_vol_pool'],
self.driver.configuration.san_clustername)
def test_setup_should_fail_if_credentials_are_invalid(self):
"""Test that the xiv_proxy validates credentials."""
"""Test that the xiv_ds8k_proxy validates credentials."""
self.driver.xiv_proxy.xiv_info['xiv_user'] = 'invalid'
self.driver.xiv_ds8k_proxy.xiv_ds8k_info['xiv_ds8k_user'] = 'invalid'
self.assertRaises(exception.NotAuthorized, self.driver.do_setup, None)
def test_setup_should_fail_if_connection_is_invalid(self):
"""Test that the xiv_proxy validates connection."""
"""Test that the xiv_ds8k_proxy validates connection."""
self.driver.xiv_proxy.xiv_info['xiv_address'] = 'invalid'
self.driver.xiv_ds8k_proxy.xiv_ds8k_info['xiv_ds8k_address'] = \
'invalid'
self.assertRaises(exception.HostNotFound, self.driver.do_setup, None)
def test_create_volume(self):
@ -153,7 +168,7 @@ class XIVVolumeDriverTest(test.TestCase):
self.driver.do_setup(None)
self.driver.create_volume(VOLUME)
has_volume = self.driver.xiv_proxy.volume_exists(VOLUME)
has_volume = self.driver.xiv_ds8k_proxy.volume_exists(VOLUME)
self.assertTrue(has_volume)
self.driver.delete_volume(VOLUME)
@ -161,7 +176,8 @@ class XIVVolumeDriverTest(test.TestCase):
"""Test the volume exist method with a volume that doesn't exist."""
self.driver.do_setup(None)
self.assertFalse(self.driver.xiv_proxy.volume_exists({'name': FAKE}))
self.assertFalse(
self.driver.xiv_ds8k_proxy.volume_exists({'name': FAKE}))
def test_delete_volume(self):
"""Verify that a volume is deleted."""
@ -169,7 +185,7 @@ class XIVVolumeDriverTest(test.TestCase):
self.driver.do_setup(None)
self.driver.create_volume(VOLUME)
self.driver.delete_volume(VOLUME)
has_volume = self.driver.xiv_proxy.volume_exists(VOLUME)
has_volume = self.driver.xiv_ds8k_proxy.volume_exists(VOLUME)
self.assertFalse(has_volume)
def test_delete_volume_should_fail_for_not_existing_volume(self):
@ -179,7 +195,7 @@ class XIVVolumeDriverTest(test.TestCase):
self.driver.delete_volume(VOLUME)
def test_create_volume_should_fail_if_no_pool_space_left(self):
"""Vertify that the xiv_proxy validates volume pool space."""
"""Vertify that the xiv_ds8k_proxy validates volume pool space."""
self.driver.do_setup(None)
self.assertRaises(exception.VolumeBackendAPIException,
@ -196,7 +212,7 @@ class XIVVolumeDriverTest(test.TestCase):
self.driver.initialize_connection(VOLUME, CONNECTOR)
self.assertTrue(
self.driver.xiv_proxy.is_volume_attached(VOLUME, CONNECTOR))
self.driver.xiv_ds8k_proxy.is_volume_attached(VOLUME, CONNECTOR))
self.driver.terminate_connection(VOLUME, CONNECTOR)
self.driver.delete_volume(VOLUME)
@ -218,8 +234,9 @@ class XIVVolumeDriverTest(test.TestCase):
self.driver.initialize_connection(VOLUME, CONNECTOR)
self.driver.terminate_connection(VOLUME, CONNECTOR)
self.assertFalse(self.driver.xiv_proxy.is_volume_attached(VOLUME,
CONNECTOR))
self.assertFalse(self.driver.xiv_ds8k_proxy.is_volume_attached(
VOLUME,
CONNECTOR))
self.driver.delete_volume(VOLUME)

View File

@ -1,121 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 IBM Corp.
# Copyright (c) 2012 OpenStack LLC.
# 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.
#
# Authors:
# Erik Zaadi <erikz@il.ibm.com>
# Avishay Traeger <avishay@il.ibm.com>
"""
Volume driver for IBM XIV storage systems.
"""
from oslo.config import cfg
from cinder import exception
from cinder.openstack.common import importutils
from cinder.openstack.common import log as logging
from cinder.volume.drivers.san import san
ibm_xiv_opts = [
cfg.StrOpt('xiv_proxy',
default='xiv_openstack.nova_proxy.XIVNovaProxy',
help='Proxy driver'),
]
CONF = cfg.CONF
CONF.register_opts(ibm_xiv_opts)
LOG = logging.getLogger('cinder.volume.xiv')
class XIVDriver(san.SanISCSIDriver):
"""IBM XIV volume driver."""
def __init__(self, *args, **kwargs):
"""Initialize the driver."""
proxy = importutils.import_class(CONF.xiv_proxy)
self.xiv_proxy = proxy({"xiv_user": CONF.san_login,
"xiv_pass": CONF.san_password,
"xiv_address": CONF.san_ip,
"xiv_vol_pool": CONF.san_clustername},
LOG,
exception)
san.SanISCSIDriver.__init__(self, *args, **kwargs)
def do_setup(self, context):
"""Setup and verify IBM XIV storage connection."""
self.xiv_proxy.setup(context)
def ensure_export(self, context, volume):
"""Ensure an export."""
return self.xiv_proxy.ensure_export(context, volume)
def create_export(self, context, volume):
"""Create an export."""
return self.xiv_proxy.create_export(context, volume)
def create_volume(self, volume):
"""Create a volume on the IBM XIV storage system."""
return self.xiv_proxy.create_volume(volume)
def delete_volume(self, volume):
"""Delete a volume on the IBM XIV storage system."""
self.xiv_proxy.delete_volume(volume)
def remove_export(self, context, volume):
"""Disconnect a volume from an attached instance."""
return self.xiv_proxy.remove_export(context, volume)
def initialize_connection(self, volume, connector):
"""Map the created volume."""
return self.xiv_proxy.initialize_connection(volume, connector)
def terminate_connection(self, volume, connector, **kwargs):
"""Terminate a connection to a volume."""
return self.xiv_proxy.terminate_connection(volume, connector)
def create_volume_from_snapshot(self, volume, snapshot):
"""Create a volume from a snapshot."""
return self.xiv_proxy.create_volume_from_snapshot(volume,
snapshot)
def create_snapshot(self, snapshot):
"""Create a snapshot."""
return self.xiv_proxy.create_snapshot(snapshot)
def delete_snapshot(self, snapshot):
"""Delete a snapshot."""
return self.xiv_proxy.delete_snapshot(snapshot)
def get_volume_stats(self, refresh=False):
"""Get volume stats."""
return self.xiv_proxy.get_volume_stats(refresh)

View File

@ -0,0 +1,157 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 IBM Corp.
# Copyright (c) 2013 OpenStack LLC.
# 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.
#
# Authors:
# Erik Zaadi <erikz@il.ibm.com>
# Avishay Traeger <avishay@il.ibm.com>
"""
Unified Volume driver for IBM XIV and DS8K Storage Systems.
"""
from oslo.config import cfg
from cinder import exception
from cinder.openstack.common import importutils
from cinder.openstack.common import log as logging
from cinder.volume.drivers.san import san
xiv_ds8k_opts = [
cfg.StrOpt(
'xiv_ds8k_proxy',
default='xiv_ds8k_openstack.nova_proxy.XIVDS8KNovaProxy',
help='Proxy driver that connects to the IBM Storage Array'),
cfg.StrOpt(
'xiv_ds8k_connection_type',
default='iscsi',
help='Connection type to the IBM Storage Array'
' (fibre_channel|iscsi)'),
]
CONF = cfg.CONF
CONF.register_opts(xiv_ds8k_opts)
LOG = logging.getLogger(__name__)
class XIVDS8KDriver(san.SanDriver):
"""Unified IBM XIV and DS8K volume driver."""
def __init__(self, *args, **kwargs):
"""Initialize the driver."""
super(XIVDS8KDriver, self).__init__(*args, **kwargs)
self.configuration.append_config_values(xiv_ds8k_opts)
proxy = importutils.import_class(self.configuration.xiv_ds8k_proxy)
#NOTE: All Array specific configurations are prefixed with:
#"xiv_ds8k_array_"
#These additional flags should be specified in the cinder.conf
#preferably in each backend configuration.
self.xiv_ds8k_proxy = proxy(
{
"xiv_ds8k_user": self.configuration.san_login,
"xiv_ds8k_pass": self.configuration.san_password,
"xiv_ds8k_address": self.configuration.san_ip,
"xiv_ds8k_vol_pool": self.configuration.san_clustername,
"xiv_ds8k_connection_type":
self.configuration.xiv_ds8k_connection_type
},
LOG,
exception,
driver=self)
def do_setup(self, context):
"""Setup and verify IBM XIV and DS8K Storage connection."""
self.xiv_ds8k_proxy.setup(context)
def ensure_export(self, context, volume):
"""Ensure an export."""
return self.xiv_ds8k_proxy.ensure_export(context, volume)
def create_export(self, context, volume):
"""Create an export."""
return self.xiv_ds8k_proxy.create_export(context, volume)
def create_volume(self, volume):
"""Create a volume on the IBM XIV and DS8K Storage system."""
return self.xiv_ds8k_proxy.create_volume(volume)
def delete_volume(self, volume):
"""Delete a volume on the IBM XIV and DS8K Storage system."""
self.xiv_ds8k_proxy.delete_volume(volume)
def remove_export(self, context, volume):
"""Disconnect a volume from an attached instance."""
return self.xiv_ds8k_proxy.remove_export(context, volume)
def initialize_connection(self, volume, connector):
"""Map the created volume."""
return self.xiv_ds8k_proxy.initialize_connection(volume, connector)
def terminate_connection(self, volume, connector, **kwargs):
"""Terminate a connection to a volume."""
return self.xiv_ds8k_proxy.terminate_connection(volume, connector)
def create_volume_from_snapshot(self, volume, snapshot):
"""Create a volume from a snapshot."""
return self.xiv_ds8k_proxy.create_volume_from_snapshot(
volume,
snapshot)
def create_snapshot(self, snapshot):
"""Create a snapshot."""
return self.xiv_ds8k_proxy.create_snapshot(snapshot)
def delete_snapshot(self, snapshot):
"""Delete a snapshot."""
return self.xiv_ds8k_proxy.delete_snapshot(snapshot)
def get_volume_stats(self, refresh=False):
"""Get volume stats."""
return self.xiv_ds8k_proxy.get_volume_stats(refresh)
def create_cloned_volume(self, tgt_volume, src_volume):
"""Create Cloned Volume."""
return self.xiv_ds8k_proxy.create_cloned_volume(tgt_volume, src_volume)
def extend_volume(self, volume, new_size):
"""Extend Created Volume."""
self.xiv_ds8k_proxy.extend_volume(volume, new_size)
def migrate_volume(self, context, volume, host):
"""Migrate the volume to the specified host."""
return self.xiv_ds8k_proxy.migrate_volume(context, volume, host)

View File

@ -101,7 +101,9 @@ MAPPING = {
'cinder.volume.windows.WindowsDriver':
'cinder.volume.drivers.windows.WindowsDriver',
'cinder.volume.xiv.XIVDriver':
'cinder.volume.drivers.xiv.XIVDriver',
'cinder.volume.drivers.xiv_ds8k.XIVDS8KDriver',
'cinder.volume.drivers.xiv.XIVDriver':
'cinder.volume.drivers.xiv_ds8k.XIVDS8KDriver',
'cinder.volume.zadara.ZadaraVPSAISCSIDriver':
'cinder.volume.drivers.zadara.ZadaraVPSAISCSIDriver',
'cinder.volume.driver.ISCSIDriver':

File diff suppressed because it is too large Load Diff