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
|
# With this dictionary the DB class can get the necessary data
|
||||||
_volumes_inflight = {}
|
_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):
|
def __init__(self, volume_backend_name, **driver_cfg):
|
||||||
if not self.global_initialization:
|
if not self.global_initialization:
|
||||||
self.global_setup()
|
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
|
driver_cfg['volume_backend_name'] = volume_backend_name
|
||||||
Backend.backends[volume_backend_name] = self
|
|
||||||
|
|
||||||
conf = self._get_backend_config(driver_cfg)
|
conf = self._get_backend_config(driver_cfg)
|
||||||
self._apply_backend_workarounds(conf)
|
self._apply_backend_workarounds(conf)
|
||||||
|
@ -99,6 +118,7 @@ class Backend(object):
|
||||||
self._stats = self._transform_legacy_stats(stats)
|
self._stats = self._transform_legacy_stats(stats)
|
||||||
|
|
||||||
self._pool_names = tuple(pool['pool_name'] for pool in stats['pools'])
|
self._pool_names = tuple(pool['pool_name'] for pool in stats['pools'])
|
||||||
|
Backend.backends[volume_backend_name] = self
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pool_names(self):
|
def pool_names(self):
|
||||||
|
|
|
@ -101,6 +101,51 @@ class TestCinderlib(base.BaseTest):
|
||||||
self.assertEqual(('default',), backend.pool_names)
|
self.assertEqual(('default',), backend.pool_names)
|
||||||
mock_workarounds.assert_called_once_with(mock_config.return_value)
|
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('cinderlib.Backend._validate_and_set_options')
|
||||||
@mock.patch.object(cfg, 'CONF')
|
@mock.patch.object(cfg, 'CONF')
|
||||||
def test__set_cinder_config(self, conf_mock, validate_mock):
|
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