From 28f311c9ad392a8d2bb21373e57f4792049dc793 Mon Sep 17 00:00:00 2001 From: Valeriy Ponomaryov Date: Thu, 18 Dec 2014 14:30:48 +0200 Subject: [PATCH] Add driver mode interface Implement driver mode interface for share driver, that will allow operator to switch share drivers to work in specific mode, like single or multi SVM. Add usage of driver mode interface to Generic driver as an example. To set driver mode use config option 'share_driver_mode'. Available values are specified within its help message. Partially Implements bp driver-modes Change-Id: I2744cccfbe2c09c3d942822f902793493cd9befb --- manila/common/constants.py | 8 ++ manila/share/driver.py | 62 +++++++++++++++- manila/share/drivers/generic.py | 1 + manila/tests/fake_driver.py | 2 + manila/tests/share/drivers/test_generic.py | 28 +++++++ manila/tests/share/test_driver.py | 85 +++++++++++++++++++++- 6 files changed, 181 insertions(+), 5 deletions(-) diff --git a/manila/common/constants.py b/manila/common/constants.py index 0b67610881..b3ed617ecd 100644 --- a/manila/common/constants.py +++ b/manila/common/constants.py @@ -51,3 +51,11 @@ PING_PORTS = ( SERVICE_INSTANCE_SECGROUP_DATA = ( CIFS_PORTS + NFS_PORTS + SSH_PORTS + PING_PORTS) + +SINGLE_SVM_MODE = 'single_svm' +MULTI_SVM_MODE = 'multi_svm' + +VALID_SHARE_DRIVER_MODES = ( + SINGLE_SVM_MODE, + MULTI_SVM_MODE, +) diff --git a/manila/share/driver.py b/manila/share/driver.py index 85346e871c..5d37d3cfde 100644 --- a/manila/share/driver.py +++ b/manila/share/driver.py @@ -20,7 +20,9 @@ Drivers for shares. import time from oslo.config import cfg +import six +from manila.common import constants from manila import exception from manila.i18n import _LE from manila import network @@ -51,6 +53,13 @@ share_opts = [ "If not set, the share backend's config group will be used." "If an option is not found within provided group, then" "'DEFAULT' group will be used for search of option."), + cfg.ListOpt( + 'share_driver_mode', + default=None, + help="One specific mode for driver to use. Available values: " + "%s. What modes are supported and can be used is " + "up to driver. If set None then default will be used." % + six.text_type(constants.VALID_SHARE_DRIVER_MODES)), ] ssh_opts = [ @@ -76,11 +85,8 @@ CONF.register_opts(ssh_opts) class ExecuteMixin(object): """Provides an executable functionality to a driver class.""" - def __init__(self, *args, **kwargs): - self.db = None - self.configuration = kwargs.get('configuration', None) + def init_execute_mixin(self, *args, **kwargs): if self.configuration: - self.configuration.append_config_values(share_opts) self.configuration.append_config_values(ssh_opts) self.set_execute(kwargs.pop('execute', utils.execute)) @@ -115,10 +121,58 @@ class ShareDriver(object): self.configuration.append_config_values(share_opts) network_config_group = (self.configuration.network_config_group or self.configuration.config_group) + self.mode = self.configuration.safe_get('share_driver_mode') else: network_config_group = None + self.mode = CONF.share_driver_mode + + if hasattr(self, 'init_execute_mixin'): + # Instance with 'ExecuteMixin' + self.init_execute_mixin(*args, **kwargs) # pylint: disable=E1101 self.network_api = network.API(config_group_name=network_config_group) + def _validate_driver_mode(self, mode): + valid = constants.VALID_SHARE_DRIVER_MODES + if mode not in valid: + data = {'mode': mode, 'valid': valid} + msg = ("Provided unsupported driver mode '%(mode)s'. List of " + "valid driver modes is %(valid)s." % data) + LOG.error(msg) + raise exception.InvalidParameterValue(msg) + return mode + + def get_driver_mode(self, supported_driver_modes): + """Verify and return driver mode. + + Call this method within share driver to get value for 'mode' attr, + + :param supported_driver_modes: list of supported modes by share driver, + see list of available values in + manila.common.constants.VALID_SHARE_DRIVER_MODES + :returns: text_type -- name of enabled driver mode. + :raises: exception.InvalidParameterValue + """ + msg = None + if not len(supported_driver_modes): + msg = "At least one mode should be supported by share driver." + elif self.mode: + if self.mode not in supported_driver_modes: + data = {'mode': self.mode, 'supported': supported_driver_modes} + msg = ("Unsupported driver mode '%(mode)s' is provided. " + "List of supported is %(supported)s." % data) + else: + return self._validate_driver_mode(self.mode) + elif len(supported_driver_modes) > 1: + msg = ("Driver mode was not specified explicitly and amount of " + "supported driver modes %s is bigger than one, please " + "specify it using config option 'share_driver_mode'." % + six.text_type(supported_driver_modes)) + + if msg: + LOG.error(msg) + raise exception.InvalidParameterValue(msg) + return self._validate_driver_mode(supported_driver_modes[0]) + def create_share(self, context, share, share_server=None): """Is called to create share.""" raise NotImplementedError() diff --git a/manila/share/drivers/generic.py b/manila/share/drivers/generic.py index 94e70b5382..1bd2704a49 100644 --- a/manila/share/drivers/generic.py +++ b/manila/share/drivers/generic.py @@ -114,6 +114,7 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): self.db = db self.configuration.append_config_values(share_opts) self.configuration.append_config_values(service_instance.server_opts) + self.mode = self.get_driver_mode([const.MULTI_SVM_MODE, ]) self._helpers = {} self.backend_name = self.configuration.safe_get( 'share_backend_name') or "Cinder_Volumes" diff --git a/manila/tests/fake_driver.py b/manila/tests/fake_driver.py index 70308a467f..653f3b74cc 100644 --- a/manila/tests/fake_driver.py +++ b/manila/tests/fake_driver.py @@ -15,6 +15,7 @@ import mock +from manila.common import constants from manila.openstack.common import log as logging from manila.share import driver @@ -28,6 +29,7 @@ class FakeShareDriver(driver.ShareDriver): super(FakeShareDriver, self).__init__(execute=self.fake_execute, *args, **kwargs) self.db = mock.Mock() + self.mode = constants.MULTI_SVM_MODE def share_network_update(*args, **kwargs): pass diff --git a/manila/tests/share/drivers/test_generic.py b/manila/tests/share/drivers/test_generic.py index 164cbb05a2..572682c6c1 100644 --- a/manila/tests/share/drivers/test_generic.py +++ b/manila/tests/share/drivers/test_generic.py @@ -878,6 +878,34 @@ class GenericShareDriverTestCase(test.TestCase): ) self.assertEqual(ssh_output, result) + @mock.patch.object( + generic.service_instance, 'ServiceInstanceManager', mock.Mock()) + def test_driver_mode_valid_value(self): + mode = const.MULTI_SVM_MODE + CONF.set_override('share_driver_mode', mode) + + driver = generic.GenericShareDriver( + self._db, execute=self._execute, configuration=self.fake_conf) + + self.assertEqual(mode, driver.mode) + generic.service_instance.ServiceInstanceManager.\ + assert_called_once_with(self._db, driver_config=self.fake_conf) + + def test_driver_mode_invalid_value(self): + mode = const.SINGLE_SVM_MODE + CONF.set_override('share_driver_mode', mode) + + self.assertRaises( + exception.InvalidParameterValue, + generic.GenericShareDriver, + self._db, + execute=self._execute, + configuration=self.fake_conf) + + def test_driver_mode_default_share_driver_modes(self): + mode = const.MULTI_SVM_MODE + self.assertEqual(mode, self._driver.mode) + class NFSHelperTestCase(test.TestCase): """Test case for NFS helper of generic driver.""" diff --git a/manila/tests/share/test_driver.py b/manila/tests/share/test_driver.py index d7793c59f4..84e2db163c 100644 --- a/manila/tests/share/test_driver.py +++ b/manila/tests/share/test_driver.py @@ -19,6 +19,7 @@ import time import mock +from manila.common import constants from manila import exception from manila import network from manila.share import configuration @@ -35,6 +36,10 @@ def fake_sleep(duration): pass +class ShareDriverWithExecuteMixin(driver.ShareDriver, driver.ExecuteMixin): + pass + + class ShareDriverTestCase(test.TestCase): def setUp(self): @@ -45,7 +50,7 @@ class ShareDriverTestCase(test.TestCase): self.stubs.Set(self.time, 'sleep', fake_sleep) def test__try_execute(self): - execute_mixin = driver.ExecuteMixin( + execute_mixin = ShareDriverWithExecuteMixin( configuration=configuration.Configuration(None)) self.assertRaises(exception.ProcessExecutionError, execute_mixin._try_execute) @@ -67,6 +72,8 @@ class ShareDriverTestCase(test.TestCase): else: network.API.assert_called_once_with( config_group_name=config.config_group) + self.assertTrue(hasattr(share_driver, 'mode')) + return share_driver def test_instantiate_share_driver(self): self._instantiate_share_driver(None) @@ -81,3 +88,79 @@ class ShareDriverTestCase(test.TestCase): self.assertEqual(None, share_driver.configuration) network.API.assert_called_once_with(config_group_name=None) + + def test_get_driver_mode_empty_list(self): + share_driver = self._instantiate_share_driver(None) + self.assertRaises( + exception.InvalidParameterValue, + share_driver.get_driver_mode, []) + + def test_get_driver_mode_one_value_in_list_mode_is_not_set(self): + share_driver = self._instantiate_share_driver(None) + share_driver.mode = None + + mode = share_driver.get_driver_mode([constants.SINGLE_SVM_MODE, ]) + + self.assertEqual(constants.SINGLE_SVM_MODE, mode) + + def test_get_driver_mode_one_value_in_list_mode_is_set_and_equal(self): + share_driver = self._instantiate_share_driver(None) + share_driver.mode = constants.SINGLE_SVM_MODE + + mode = share_driver.get_driver_mode([constants.SINGLE_SVM_MODE, ]) + + self.assertEqual(constants.SINGLE_SVM_MODE, mode) + + def test_get_driver_mode_one_value_in_list_mode_is_set_and_not_equal(self): + share_driver = self._instantiate_share_driver(None) + share_driver.mode = constants.SINGLE_SVM_MODE + + self.assertRaises( + exception.InvalidParameterValue, + share_driver.get_driver_mode, + [constants.MULTI_SVM_MODE, ]) + + def test_get_driver_mode_two_values_in_list_mode_is_not_set(self): + share_driver = self._instantiate_share_driver(None) + share_driver.mode = None + + self.assertRaises( + exception.InvalidParameterValue, + share_driver.get_driver_mode, + [constants.SINGLE_SVM_MODE, constants.MULTI_SVM_MODE]) + + def test_get_driver_mode_two_values_in_list_mode_is_set(self): + share_driver = self._instantiate_share_driver(None) + share_driver.mode = constants.MULTI_SVM_MODE + + mode = share_driver.get_driver_mode( + [constants.SINGLE_SVM_MODE, constants.MULTI_SVM_MODE, ]) + + self.assertEqual(constants.MULTI_SVM_MODE, mode) + + def test_get_driver_mode_one_invalid_value_in_list_mode_is_not_set(self): + share_driver = self._instantiate_share_driver(None) + share_driver.mode = None + + self.assertRaises( + exception.InvalidParameterValue, + share_driver.get_driver_mode, + ['fake', ]) + + def test_get_driver_mode_one_valid_value_in_list_mode_is_invalid(self): + share_driver = self._instantiate_share_driver(None) + share_driver.mode = 'fake' + + self.assertRaises( + exception.InvalidParameterValue, + share_driver.get_driver_mode, + [constants.MULTI_SVM_MODE, ]) + + def test_get_driver_mode_two_values_in_list_invalid_mode_set(self): + share_driver = self._instantiate_share_driver(None) + share_driver.mode = 'fake' + + self.assertRaises( + exception.InvalidParameterValue, + share_driver.get_driver_mode, + [constants.SINGLE_SVM_MODE, constants.MULTI_SVM_MODE, ])