Merge "Replace the NetApp driver proxy layer with a proper factory."
This commit is contained in:
commit
c89875d1dc
|
@ -258,6 +258,7 @@ class TestCase(testtools.TestCase):
|
|||
patcher = mock.patch.object(obj, attr_name, new_attr, **kwargs)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
return new_attr
|
||||
|
||||
# Useful assertions
|
||||
def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
|
||||
|
|
|
@ -745,21 +745,6 @@ class NetAppDriverNegativeTestCase(test.TestCase):
|
|||
except exception.InvalidInput:
|
||||
pass
|
||||
|
||||
def test_non_netapp_driver(self):
|
||||
self.mock_object(utils, 'OpenStackInfo')
|
||||
configuration = create_configuration()
|
||||
common.netapp_unified_plugin_registry['test_family'] =\
|
||||
{'iscsi': 'cinder.volume.drivers.arbitrary.IscsiDriver'}
|
||||
configuration.netapp_storage_family = 'test_family'
|
||||
configuration.netapp_storage_protocol = 'iscsi'
|
||||
try:
|
||||
common.NetAppDriver(configuration=configuration)
|
||||
raise AssertionError('Non NetApp driver is getting instantiated.')
|
||||
except exception.InvalidInput:
|
||||
pass
|
||||
finally:
|
||||
common.netapp_unified_plugin_registry.pop('test_family')
|
||||
|
||||
|
||||
class FakeDirect7MODEServerHandler(FakeHTTPRequestHandler):
|
||||
"""HTTP handler that fakes enough stuff to allow the driver to run."""
|
||||
|
@ -1213,8 +1198,8 @@ class NetAppDirect7modeISCSIDriverTestCase_NV(
|
|||
self.driver.volume_list = []
|
||||
|
||||
def test_connect(self):
|
||||
self.driver.driver.library.zapi_client = mock.MagicMock()
|
||||
self.driver.driver.library.zapi_client.get_ontapi_version.\
|
||||
self.driver.library.zapi_client = mock.MagicMock()
|
||||
self.driver.library.zapi_client.get_ontapi_version.\
|
||||
return_value = (1, 20)
|
||||
self.driver.check_for_setup_error()
|
||||
|
||||
|
|
|
@ -853,10 +853,10 @@ class NetAppEseriesISCSIDriverTestCase(test.TestCase):
|
|||
fake_pool['volumeGroupRef'] = 'foo'
|
||||
fake_pools = [fake_pool]
|
||||
storage_pools.return_value = fake_pools
|
||||
drv = self.driver
|
||||
storage_vol = drv.driver._create_volume(self.fake_eseries_pool_label,
|
||||
self.fake_eseries_volume_label,
|
||||
self.fake_size_gb)
|
||||
storage_vol = self.driver._create_volume(
|
||||
self.fake_eseries_pool_label,
|
||||
self.fake_eseries_volume_label,
|
||||
self.fake_size_gb)
|
||||
log_info.assert_called_once_with("Created volume with label %s.",
|
||||
self.fake_eseries_volume_label)
|
||||
self.assertEqual('CorrectVolume', storage_vol)
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
# Copyright (c) 2014 Clinton Knight. 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.
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
||||
from cinder import exception
|
||||
from cinder import test
|
||||
import cinder.tests.volume.drivers.netapp.fakes as na_fakes
|
||||
import cinder.volume.drivers.netapp.common as na_common
|
||||
import cinder.volume.drivers.netapp.dataontap.fc_cmode as fc_cmode
|
||||
import cinder.volume.drivers.netapp.utils as na_utils
|
||||
|
||||
|
||||
class NetAppDriverFactoryTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NetAppDriverFactoryTestCase, self).setUp()
|
||||
self.mock_object(na_common, 'LOG')
|
||||
|
||||
def test_new(self):
|
||||
|
||||
self.mock_object(na_utils.OpenStackInfo, 'info',
|
||||
mock.Mock(return_value='fake_info'))
|
||||
mock_create_driver = self.mock_object(na_common.NetAppDriver,
|
||||
'create_driver')
|
||||
|
||||
config = na_fakes.create_configuration()
|
||||
config.netapp_storage_family = 'fake_family'
|
||||
config.netapp_storage_protocol = 'fake_protocol'
|
||||
|
||||
kwargs = {'configuration': config}
|
||||
na_common.NetAppDriver(**kwargs)
|
||||
|
||||
kwargs['app_version'] = 'fake_info'
|
||||
mock_create_driver.assert_called_with('fake_family', 'fake_protocol',
|
||||
*(), **kwargs)
|
||||
|
||||
def test_new_missing_config(self):
|
||||
|
||||
self.mock_object(na_utils.OpenStackInfo, 'info')
|
||||
self.mock_object(na_common.NetAppDriver, 'create_driver')
|
||||
|
||||
self.assertRaises(exception.InvalidInput, na_common.NetAppDriver, **{})
|
||||
|
||||
def test_new_missing_family(self):
|
||||
|
||||
self.mock_object(na_utils.OpenStackInfo, 'info')
|
||||
self.mock_object(na_common.NetAppDriver, 'create_driver')
|
||||
|
||||
config = na_fakes.create_configuration()
|
||||
config.netapp_storage_protocol = 'fake_protocol'
|
||||
config.netapp_storage_family = None
|
||||
|
||||
kwargs = {'configuration': config}
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
na_common.NetAppDriver,
|
||||
**kwargs)
|
||||
|
||||
def test_new_missing_protocol(self):
|
||||
|
||||
self.mock_object(na_utils.OpenStackInfo, 'info')
|
||||
self.mock_object(na_common.NetAppDriver, 'create_driver')
|
||||
|
||||
config = na_fakes.create_configuration()
|
||||
config.netapp_storage_protocol = None
|
||||
config.netapp_storage_family = 'fake_family'
|
||||
|
||||
kwargs = {'configuration': config}
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
na_common.NetAppDriver,
|
||||
**kwargs)
|
||||
|
||||
def test_create_driver(self):
|
||||
|
||||
def get_full_class_name(obj):
|
||||
return obj.__module__ + '.' + obj.__class__.__name__
|
||||
|
||||
kwargs = {'configuration': na_fakes.create_configuration(),
|
||||
'app_version': 'fake_info'}
|
||||
|
||||
registry = na_common.NETAPP_UNIFIED_DRIVER_REGISTRY
|
||||
|
||||
for family in six.iterkeys(registry):
|
||||
for protocol, full_class_name in six.iteritems(registry[family]):
|
||||
driver = na_common.NetAppDriver.create_driver(
|
||||
family, protocol, **kwargs)
|
||||
self.assertEqual(full_class_name, get_full_class_name(driver))
|
||||
|
||||
def test_create_driver_case_insensitive(self):
|
||||
|
||||
kwargs = {'configuration': na_fakes.create_configuration(),
|
||||
'app_version': 'fake_info'}
|
||||
|
||||
driver = na_common.NetAppDriver.create_driver('ONTAP_CLUSTER', 'FC',
|
||||
**kwargs)
|
||||
|
||||
self.assertIsInstance(driver, fc_cmode.NetAppCmodeFibreChannelDriver)
|
||||
|
||||
def test_create_driver_invalid_family(self):
|
||||
|
||||
kwargs = {'configuration': na_fakes.create_configuration(),
|
||||
'app_version': 'fake_info'}
|
||||
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
na_common.NetAppDriver.create_driver,
|
||||
'kardashian', 'iscsi', **kwargs)
|
||||
|
||||
def test_create_driver_invalid_protocol(self):
|
||||
|
||||
kwargs = {'configuration': na_fakes.create_configuration(),
|
||||
'app_version': 'fake_info'}
|
||||
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
na_common.NetAppDriver.create_driver,
|
||||
'ontap_7mode', 'carrier_pigeon', **kwargs)
|
|
@ -1,4 +1,3 @@
|
|||
# Copyright (c) 2012 NetApp, Inc. All rights reserved.
|
||||
# Copyright (c) 2014 Navneet Singh. All rights reserved.
|
||||
# Copyright (c) 2014 Clinton Knight. All rights reserved.
|
||||
#
|
||||
|
@ -16,162 +15,98 @@
|
|||
"""
|
||||
Unified driver for NetApp storage systems.
|
||||
|
||||
Supports call to multiple storage systems of different families and protocols.
|
||||
Supports multiple storage systems of different families and protocols.
|
||||
"""
|
||||
|
||||
from oslo.utils import importutils
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LI
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder.volume import driver
|
||||
from cinder.volume.drivers.netapp.options import netapp_proxy_opts
|
||||
from cinder.volume.drivers.netapp import utils as na_utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# NOTE(singn): Holds family:{protocol:driver} registration information.
|
||||
# Plug in new families and protocols to support new drivers.
|
||||
# No other code modification required.
|
||||
|
||||
DATAONTAP_PATH = 'cinder.volume.drivers.netapp.dataontap'
|
||||
ESERIES_PATH = 'cinder.volume.drivers.netapp.eseries'
|
||||
|
||||
netapp_unified_plugin_registry =\
|
||||
{'ontap_cluster':
|
||||
{
|
||||
'iscsi': DATAONTAP_PATH + '.iscsi_cmode.NetAppCmodeISCSIDriver',
|
||||
'nfs': DATAONTAP_PATH + '.nfs_cmode.NetAppCmodeNfsDriver',
|
||||
'fc': DATAONTAP_PATH + '.fc_cmode.NetAppCmodeFibreChannelDriver'
|
||||
},
|
||||
'ontap_7mode':
|
||||
{
|
||||
'iscsi': DATAONTAP_PATH + '.iscsi_7mode.NetApp7modeISCSIDriver',
|
||||
'nfs': DATAONTAP_PATH + '.nfs_7mode.NetApp7modeNfsDriver',
|
||||
'fc': DATAONTAP_PATH + '.fc_7mode.NetApp7modeFibreChannelDriver'
|
||||
},
|
||||
'eseries':
|
||||
{
|
||||
'iscsi': ESERIES_PATH + '.iscsi.NetAppEseriesISCSIDriver'
|
||||
},
|
||||
}
|
||||
# Add new drivers here, no other code changes required.
|
||||
NETAPP_UNIFIED_DRIVER_REGISTRY = {
|
||||
'ontap_cluster':
|
||||
{
|
||||
'iscsi': DATAONTAP_PATH + '.iscsi_cmode.NetAppCmodeISCSIDriver',
|
||||
'nfs': DATAONTAP_PATH + '.nfs_cmode.NetAppCmodeNfsDriver',
|
||||
'fc': DATAONTAP_PATH + '.fc_cmode.NetAppCmodeFibreChannelDriver'
|
||||
},
|
||||
'ontap_7mode':
|
||||
{
|
||||
'iscsi': DATAONTAP_PATH + '.iscsi_7mode.NetApp7modeISCSIDriver',
|
||||
'nfs': DATAONTAP_PATH + '.nfs_7mode.NetApp7modeNfsDriver',
|
||||
'fc': DATAONTAP_PATH + '.fc_7mode.NetApp7modeFibreChannelDriver'
|
||||
},
|
||||
'eseries':
|
||||
{
|
||||
'iscsi': ESERIES_PATH + '.iscsi.NetAppEseriesISCSIDriver'
|
||||
}}
|
||||
|
||||
|
||||
class NetAppDriver(object):
|
||||
""""NetApp unified block storage driver.
|
||||
|
||||
Acts as a mediator to NetApp storage drivers.
|
||||
Proxies requests based on the storage family and protocol configured.
|
||||
Override the proxy driver method by adding method in this driver.
|
||||
Acts as a factory to create NetApp storage drivers based on the
|
||||
storage family and protocol configured.
|
||||
"""
|
||||
|
||||
REQUIRED_FLAGS = ['netapp_storage_family', 'netapp_storage_protocol']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NetAppDriver, self).__init__()
|
||||
def __new__(cls, *args, **kwargs):
|
||||
|
||||
config = kwargs.get('configuration', None)
|
||||
if not config:
|
||||
raise exception.InvalidInput(
|
||||
reason=_('Required configuration not found'))
|
||||
|
||||
config.append_config_values(netapp_proxy_opts)
|
||||
na_utils.check_flags(NetAppDriver.REQUIRED_FLAGS, config)
|
||||
|
||||
app_version = na_utils.OpenStackInfo().info()
|
||||
LOG.info(_LI('OpenStack OS Version Info: %(info)s') % {
|
||||
'info': app_version})
|
||||
|
||||
self.configuration = kwargs.get('configuration', None)
|
||||
if not self.configuration:
|
||||
raise exception.InvalidInput(
|
||||
reason=_("Required configuration not found"))
|
||||
|
||||
self.configuration.append_config_values(netapp_proxy_opts)
|
||||
na_utils.check_flags(self.REQUIRED_FLAGS, self.configuration)
|
||||
|
||||
kwargs['app_version'] = app_version
|
||||
|
||||
self.driver = NetAppDriverFactory.create_driver(
|
||||
self.configuration.netapp_storage_family,
|
||||
self.configuration.netapp_storage_protocol,
|
||||
*args, **kwargs)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""Sets the attribute."""
|
||||
if getattr(self, 'driver', None):
|
||||
self.driver.__setattr__(name, value)
|
||||
return
|
||||
object.__setattr__(self, name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
""""Gets the attribute."""
|
||||
drv = object.__getattribute__(self, 'driver')
|
||||
return getattr(drv, name)
|
||||
|
||||
|
||||
class NetAppDriverFactory(object):
|
||||
"""Factory to instantiate appropriate NetApp driver."""
|
||||
return NetAppDriver.create_driver(config.netapp_storage_family,
|
||||
config.netapp_storage_protocol,
|
||||
*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def create_driver(storage_family, storage_protocol, *args, **kwargs):
|
||||
""""Creates an appropriate driver based on family and protocol."""
|
||||
|
||||
fmt = {'storage_family': storage_family.lower(),
|
||||
'storage_protocol': storage_protocol.lower()}
|
||||
LOG.info(_LI('Requested unified config: %(storage_family)s and '
|
||||
'%(storage_protocol)s') % fmt)
|
||||
storage_family = storage_family.lower()
|
||||
storage_protocol = storage_protocol.lower()
|
||||
|
||||
family_meta = netapp_unified_plugin_registry.get(storage_family)
|
||||
fmt = {'storage_family': storage_family,
|
||||
'storage_protocol': storage_protocol}
|
||||
LOG.info(_LI('Requested unified config: %(storage_family)s and '
|
||||
'%(storage_protocol)s.') % fmt)
|
||||
|
||||
family_meta = NETAPP_UNIFIED_DRIVER_REGISTRY.get(storage_family)
|
||||
if family_meta is None:
|
||||
raise exception.InvalidInput(
|
||||
reason=_('Storage family %s is not supported')
|
||||
reason=_('Storage family %s is not supported.')
|
||||
% storage_family)
|
||||
|
||||
driver_loc = family_meta.get(storage_protocol)
|
||||
if driver_loc is None:
|
||||
raise exception.InvalidInput(
|
||||
reason=_('Protocol %(storage_protocol)s is not supported'
|
||||
' for storage family %(storage_family)s') % fmt)
|
||||
reason=_('Protocol %(storage_protocol)s is not supported '
|
||||
'for storage family %(storage_family)s.') % fmt)
|
||||
|
||||
NetAppDriverFactory.check_netapp_driver(driver_loc)
|
||||
kwargs = kwargs or {}
|
||||
kwargs['netapp_mode'] = 'proxy'
|
||||
driver = importutils.import_object(driver_loc, *args, **kwargs)
|
||||
LOG.info(_LI('NetApp driver of family %(storage_family)s and protocol'
|
||||
' %(storage_protocol)s loaded') % fmt)
|
||||
LOG.info(_LI('NetApp driver of family %(storage_family)s and protocol '
|
||||
'%(storage_protocol)s loaded.') % fmt)
|
||||
return driver
|
||||
|
||||
@staticmethod
|
||||
def check_netapp_driver(location):
|
||||
"""Checks if the driver requested is a netapp driver."""
|
||||
if location.find(".netapp.") == -1:
|
||||
raise exception.InvalidInput(
|
||||
reason=_("Only loading netapp drivers supported."))
|
||||
|
||||
|
||||
class Deprecated(driver.VolumeDriver):
|
||||
"""Deprecated driver for NetApp.
|
||||
|
||||
This driver is used for mapping deprecated
|
||||
drivers to itself in manager. It prevents cinder
|
||||
from getting errored out in case of upgrade scenarios
|
||||
and also suggests further steps.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._log_deprecated_warn()
|
||||
|
||||
def _log_deprecated_warn(self):
|
||||
"""Logs appropriate warning and suggestion."""
|
||||
|
||||
link = "https://communities.netapp.com/groups/openstack"
|
||||
msg = _("The configured NetApp driver is deprecated."
|
||||
" Please refer the link to resolve the issue '%s'.")
|
||||
LOG.warning(msg % link)
|
||||
|
||||
def check_for_setup_error(self):
|
||||
pass
|
||||
|
||||
def ensure_export(self, context, volume):
|
||||
pass
|
||||
|
||||
def get_volume_stats(self, refresh=False):
|
||||
"""Return the current state of the volume service. If 'refresh' is
|
||||
True, run the update first.
|
||||
"""
|
||||
self._log_deprecated_warn()
|
||||
return None
|
||||
|
|
Loading…
Reference in New Issue