Merge "Prevent duplicated backend names"
This commit is contained in:
commit
4347547a0d
|
@ -67,11 +67,30 @@ class Backend(object):
|
|||
# With this dictionary the DB class can get the necessary data
|
||||
_volumes_inflight = {}
|
||||
|
||||
def __new__(cls, volume_backend_name, **driver_cfg):
|
||||
# Prevent redefinition of an already initialized backend on the same
|
||||
# persistence storage with a different configuration.
|
||||
backend = Backend.backends.get(volume_backend_name)
|
||||
if backend:
|
||||
# If we are instantiating the same backend return the one we have
|
||||
# saved (singleton pattern).
|
||||
if driver_cfg == backend._original_driver_cfg:
|
||||
return backend
|
||||
raise ValueError('Backend named %s already exists with a different'
|
||||
' configuration' % volume_backend_name)
|
||||
|
||||
return super(Backend, cls).__new__(cls)
|
||||
|
||||
def __init__(self, volume_backend_name, **driver_cfg):
|
||||
if not self.global_initialization:
|
||||
self.global_setup()
|
||||
# Instance already initialized
|
||||
if volume_backend_name in Backend.backends:
|
||||
return
|
||||
# Save the original config before we add the backend name and template
|
||||
# the values.
|
||||
self._original_driver_cfg = driver_cfg.copy()
|
||||
driver_cfg['volume_backend_name'] = volume_backend_name
|
||||
Backend.backends[volume_backend_name] = self
|
||||
|
||||
conf = self._get_backend_config(driver_cfg)
|
||||
self._apply_backend_workarounds(conf)
|
||||
|
@ -99,6 +118,7 @@ class Backend(object):
|
|||
self._stats = self._transform_legacy_stats(stats)
|
||||
|
||||
self._pool_names = tuple(pool['pool_name'] for pool in stats['pools'])
|
||||
Backend.backends[volume_backend_name] = self
|
||||
|
||||
@property
|
||||
def pool_names(self):
|
||||
|
|
|
@ -101,6 +101,51 @@ class TestCinderlib(base.BaseTest):
|
|||
self.assertEqual(('default',), backend.pool_names)
|
||||
mock_workarounds.assert_called_once_with(mock_config.return_value)
|
||||
|
||||
@mock.patch.object(objects.Backend, 'global_initialization', True)
|
||||
@mock.patch.object(objects.Backend, '_apply_backend_workarounds')
|
||||
@mock.patch('oslo_utils.importutils.import_object')
|
||||
@mock.patch.object(objects.Backend, '_get_backend_config')
|
||||
def test_init_call_twice(self, mock_config, mock_import, mock_workarounds):
|
||||
cinderlib.Backend.global_initialization = False
|
||||
driver_cfg = {'k': 'v', 'k2': 'v2', 'volume_backend_name': 'Test'}
|
||||
driver = mock_import.return_value
|
||||
driver.capabilities = {'pools': [{'pool_name': 'default'}]}
|
||||
|
||||
backend = objects.Backend(**driver_cfg)
|
||||
self.assertEqual(1, mock_config.call_count)
|
||||
self.assertEqual(1, mock_import.call_count)
|
||||
self.assertEqual(1, mock_workarounds.call_count)
|
||||
|
||||
# When initiallizing a Backend with the same configuration the Backend
|
||||
# class must behave as a singleton and we won't initialize it again
|
||||
backend_second = objects.Backend(**driver_cfg)
|
||||
self.assertIs(backend, backend_second)
|
||||
self.assertEqual(1, mock_config.call_count)
|
||||
self.assertEqual(1, mock_import.call_count)
|
||||
self.assertEqual(1, mock_workarounds.call_count)
|
||||
|
||||
@mock.patch.object(objects.Backend, 'global_initialization', True)
|
||||
@mock.patch.object(objects.Backend, '_apply_backend_workarounds')
|
||||
@mock.patch('oslo_utils.importutils.import_object')
|
||||
@mock.patch.object(objects.Backend, '_get_backend_config')
|
||||
def test_init_call_twice_different_config(self, mock_config, mock_import,
|
||||
mock_workarounds):
|
||||
cinderlib.Backend.global_initialization = False
|
||||
driver_cfg = {'k': 'v', 'k2': 'v2', 'volume_backend_name': 'Test'}
|
||||
driver = mock_import.return_value
|
||||
driver.capabilities = {'pools': [{'pool_name': 'default'}]}
|
||||
|
||||
objects.Backend(**driver_cfg)
|
||||
self.assertEqual(1, mock_config.call_count)
|
||||
self.assertEqual(1, mock_import.call_count)
|
||||
self.assertEqual(1, mock_workarounds.call_count)
|
||||
|
||||
# It should fail if we reuse the backend name but change the config
|
||||
self.assertRaises(ValueError, objects.Backend, k3='v3', **driver_cfg)
|
||||
self.assertEqual(1, mock_config.call_count)
|
||||
self.assertEqual(1, mock_import.call_count)
|
||||
self.assertEqual(1, mock_workarounds.call_count)
|
||||
|
||||
@mock.patch('cinderlib.Backend._validate_and_set_options')
|
||||
@mock.patch.object(cfg, 'CONF')
|
||||
def test__set_cinder_config(self, conf_mock, validate_mock):
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
fixes:
|
||||
- |
|
||||
Prevent the creation of multiple backends with the same name during the
|
||||
same code run.
|
||||
(Bug #1886164).
|
Loading…
Reference in New Issue