[hopem,r=jamespage]

Ensure 'host' is set in cinder.conf if rbd storage backend is
related AND all backends are stateless e.g. rbd.

Closes-Bug: 1493931
This commit is contained in:
Edward Hope-Morley 2015-09-13 16:25:10 +01:00
commit db7f68241b
3 changed files with 252 additions and 2 deletions

View File

@ -3,12 +3,15 @@ from charmhelpers.core.hookenv import (
relation_ids,
service_name,
related_units,
relation_get
relation_get,
log,
WARNING,
)
from charmhelpers.contrib.openstack.context import (
OSContextGenerator,
ApacheSSLContext as SSLContext,
SubordinateConfigContext,
)
from charmhelpers.contrib.openstack.utils import (
@ -110,3 +113,44 @@ class LoggingConfigContext(OSContextGenerator):
def __call__(self):
return {'debug': config('debug'), 'verbose': config('verbose')}
class CinderSubordinateConfigContext(SubordinateConfigContext):
def __call__(self):
ctxt = super(CinderSubordinateConfigContext, self).__call__()
# If all backends are stateless we can allow host setting to be set
# across hosts/units to allow for HA volume failover but otherwise we
# have to leave it as unique (LP: #1493931).
rids = []
for interface in self.interfaces:
rids.extend(relation_ids(interface))
stateless = None
any_stateless = False
for rid in rids:
for unit in related_units(rid):
val = relation_get('stateless', rid=rid, unit=unit) or ""
if val.lower() == 'true':
if stateless is None:
stateless = True
else:
stateless = stateless and True
else:
stateless = False
any_stateless = any_stateless or stateless
if stateless:
if 'DEFAULT' in ctxt['sections']:
ctxt['sections']['DEFAULT'].append(('host', service_name()))
else:
ctxt['sections']['DEFAULT'] = [('host', service_name())]
elif any_stateless:
log("One or more stateless backends configured but unable to "
"set host param since there appear to also be stateful "
"backends configured.", level=WARNING)
return ctxt

View File

@ -172,7 +172,7 @@ CONFIG_FILES = OrderedDict([
cinder_contexts.CephContext(),
cinder_contexts.HAProxyContext(),
cinder_contexts.ImageServiceContext(),
context.SubordinateConfigContext(
cinder_contexts.CinderSubordinateConfigContext(
interface='storage-backend',
service='cinder',
config_file=CINDER_CONF),

View File

@ -135,3 +135,209 @@ class TestCinderContext(CharmTestCase):
'namespace': 'cinder'})
self.assertTrue(mock_https.called)
mock_unit_get.assert_called_with('private-address')
@patch('%s.relation_get' % (mod_ch_context))
@patch('%s.related_units' % (mod_ch_context))
@patch('%s.relation_ids' % (mod_ch_context))
@patch('%s.log' % (mod_ch_context), lambda *args, **kwargs: None)
def test_subordinate_config_context_stateless(self, mock_rel_ids,
mock_rel_units,
mock_rel_get):
mock_rel_ids.return_value = ['storage-backend:0']
self.relation_ids.return_value = ['storage-backend:0']
mock_rel_units.return_value = ['cinder-ceph/0']
self.related_units.return_value = ['cinder-ceph/0']
self.service_name.return_value = 'cinder'
settings = \
{'backend_name': 'cinder-ceph',
'private-address': '10.5.8.191',
'stateless': 'True',
'subordinate_configuration':
'{"cinder": '
'{"/etc/cinder/cinder.conf": '
'{"sections": '
'{"cinder-ceph": '
'[["volume_backend_name", '
'"cinder-ceph"], '
'["volume_driver", '
'"cinder.volume.drivers.rbd.RBDDriver"], '
'["rbd_pool", '
'"cinder-ceph"], '
'["rbd_user", '
'"cinder-ceph"]]}}}}'}
def fake_rel_get(attribute=None, unit=None, rid=None):
return settings.get(attribute)
mock_rel_get.side_effect = fake_rel_get
self.relation_get.side_effect = fake_rel_get
ctxt = contexts.CinderSubordinateConfigContext(
interface='storage-backend',
service='cinder',
config_file='/etc/cinder/cinder.conf')()
exp = {'sections': {'DEFAULT': [('host', 'cinder')],
u'cinder-ceph': [[u'volume_backend_name', u'cinder-ceph'],
[u'volume_driver',
u'cinder.volume.drivers.rbd.RBDDriver'],
[u'rbd_pool', u'cinder-ceph'],
[u'rbd_user', u'cinder-ceph']]}}
self.assertEquals(ctxt, exp)
@patch('%s.relation_get' % (mod_ch_context))
@patch('%s.related_units' % (mod_ch_context))
@patch('%s.relation_ids' % (mod_ch_context))
@patch('%s.log' % (mod_ch_context), lambda *args, **kwargs: None)
def test_subordinate_config_context_statefull(self, mock_rel_ids,
mock_rel_units,
mock_rel_get):
mock_rel_ids.return_value = ['storage-backend:0']
self.relation_ids.return_value = ['storage-backend:0']
mock_rel_units.return_value = ['cinder-ceph/0']
self.related_units.return_value = ['cinder-ceph/0']
self.service_name.return_value = 'cinder'
settings = \
{'backend_name': 'cinder-ceph',
'private-address': '10.5.8.191',
'stateless': 'False',
'subordinate_configuration':
'{"cinder": '
'{"/etc/cinder/cinder.conf": '
'{"sections": '
'{"cinder-ceph": '
'[["volume_backend_name", '
'"cinder-ceph"], '
'["volume_driver", '
'"cinder.volume.drivers.rbd.RBDDriver"], '
'["rbd_pool", '
'"cinder-ceph"], '
'["rbd_user", '
'"cinder-ceph"]]}}}}'}
def fake_rel_get(attribute=None, unit=None, rid=None):
return settings.get(attribute)
mock_rel_get.side_effect = fake_rel_get
self.relation_get.side_effect = fake_rel_get
ctxt = contexts.CinderSubordinateConfigContext(
interface='storage-backend',
service='cinder',
config_file='/etc/cinder/cinder.conf')()
exp = {'sections':
{u'cinder-ceph': [[u'volume_backend_name',
u'cinder-ceph'],
[u'volume_driver',
u'cinder.volume.drivers.rbd.RBDDriver'],
[u'rbd_pool', u'cinder-ceph'],
[u'rbd_user', u'cinder-ceph']]}}
self.assertEquals(ctxt, exp)
del settings['stateless']
ctxt = contexts.CinderSubordinateConfigContext(
interface='storage-backend',
service='cinder',
config_file='/etc/cinder/cinder.conf')()
exp = {'sections':
{u'cinder-ceph': [[u'volume_backend_name',
u'cinder-ceph'],
[u'volume_driver',
u'cinder.volume.drivers.rbd.RBDDriver'],
[u'rbd_pool', u'cinder-ceph'],
[u'rbd_user', u'cinder-ceph']]}}
self.assertEquals(ctxt, exp)
@patch('%s.relation_get' % (mod_ch_context))
@patch('%s.related_units' % (mod_ch_context))
@patch('%s.relation_ids' % (mod_ch_context))
@patch.object(contexts, 'log', lambda *args, **kwargs: None)
@patch('%s.log' % (mod_ch_context), lambda *args, **kwargs: None)
def test_subordinate_config_context_mixed(self, mock_rel_ids,
mock_rel_units,
mock_rel_get):
mock_rel_ids.return_value = ['storage-backend:0', 'storage-backend:1']
self.relation_ids.return_value = ['storage-backend:0',
'storage-backend:1']
def fake_rel_units(rid):
if rid == 'storage-backend:0':
return ['cinder-ceph/0']
else:
return ['cinder-other/0']
mock_rel_units.side_effect = fake_rel_units
self.related_units.side_effect = fake_rel_units
self.service_name.return_value = 'cinder'
cinder_ceph_settings = \
{'backend_name': 'cinder-ceph',
'private-address': '10.5.8.191',
'stateless': 'True',
'subordinate_configuration':
'{"cinder": '
'{"/etc/cinder/cinder.conf": '
'{"sections": '
'{"cinder-ceph": '
'[["volume_backend_name", '
'"cinder-ceph"], '
'["volume_driver", '
'"cinder.volume.drivers.rbd.RBDDriver"], '
'["rbd_pool", '
'"cinder-ceph"], '
'["rbd_user", '
'"cinder-ceph"]]}}}}'}
cinder_other_settings = \
{'backend_name': 'cinder-other',
'private-address': '10.5.8.192',
'subordinate_configuration':
'{"cinder": '
'{"/etc/cinder/cinder.conf": '
'{"sections": '
'{"cinder-other": '
'[["volume_backend_name", '
'"cinder-other"], '
'["volume_driver", '
'"cinder.volume.drivers.OtherDriver"]]}}}}'}
def fake_rel_get(attribute=None, unit=None, rid=None):
if unit == 'cinder-ceph/0':
return cinder_ceph_settings.get(attribute)
elif unit == 'cinder-other/0':
return cinder_other_settings.get(attribute)
mock_rel_get.side_effect = fake_rel_get
self.relation_get.side_effect = fake_rel_get
ctxt = contexts.CinderSubordinateConfigContext(
interface='storage-backend',
service='cinder',
config_file='/etc/cinder/cinder.conf')()
exp = {'sections':
{u'cinder-ceph': [[u'volume_backend_name',
u'cinder-ceph'],
[u'volume_driver',
u'cinder.volume.drivers.rbd.RBDDriver'],
[u'rbd_pool', u'cinder-ceph'],
[u'rbd_user', u'cinder-ceph']],
u'cinder-other': [[u'volume_backend_name',
u'cinder-other'],
[u'volume_driver',
u'cinder.volume.drivers.OtherDriver']]}}
self.assertEquals(ctxt, exp)