Add BlueStore Compression support

Drop support for forwarding requests for manually created Erasure
Coded pools. The initial implementation was based on a incomplete
implementation of EC support in the supporting libraries.

Make use of the forwarding of broker requests to support both
BlueStore Compression and Erasure Coded pools created through
the broker request protocol by charms in the source model.

There is currently no support for forwarding or influencing these
properties for manually created pools.

Depends-On: I0d4ed457e1d59eabed3340f5dc7d8353d5d66f04
Depends-On: I16196053bee93bdc4e5c62f5467d9e786b047b30
Change-Id: I19f66f8594b9bdada5365582ce98365039235f9d
This commit is contained in:
Frode Nordahl
2020-10-08 21:21:11 +02:00
parent 939c6b64ac
commit 85e33607d1
5 changed files with 169 additions and 50 deletions

2
.gitignore vendored
View File

@@ -5,4 +5,6 @@
build
.coverage
cover/
layers/
interfaces/
*.swp

View File

@@ -24,6 +24,7 @@ import charms_openstack.adapters
import charms_openstack.plugins
import charmhelpers.core as ch_core
import charmhelpers.contrib.storage.linux.ceph as ch_ceph
class CephRBDMirrorCharmRelationAdapters(
@@ -156,3 +157,48 @@ class CephRBDMirrorCharm(charms_openstack.plugins.CephCharm):
subprocess.check_call(base_cmd + ['peer', 'add', pool,
'client.{}@remote'
.format(self.ceph_id)])
def pools_in_broker_request(self, rq, ops_to_check=None):
"""Extract pool names touched by a broker request.
:param rq: Ceph Broker Request Object
:type rq: ch_ceph.CephBrokerRq
:param ops_to_check: Set providing which ops to check
:type ops_to_check: Optional[Set[str]]
:returns: Set of pool names
:rtype: Set[str]
"""
assert rq.api_version == 1
ops_to_check = ops_to_check or set(('create-pool',))
result_set = set()
for op in rq.ops:
if op['op'] in ops_to_check:
result_set.add(op['name'])
return result_set
def collapse_and_filter_broker_requests(self, broker_requests,
allowed_ops, require_vp=None):
"""Extract allowed ops from broker requests into one collapsed request.
:param broker_requests: List of broker requests
:type broker_requests: List[ch_ceph.CephBrokerRq]
:param allowed_ops: Set of ops to allow
:type allowed_ops: Set
:param require_vp: Map of required key-value pairs in op
:type require_vp: Optional[Dict[str,any]]
:returns: Collapsed broker request
:rtype: Optional[ch_ceph.CephBrokerRq]
"""
require_vp = require_vp or {}
new_rq = ch_ceph.CephBrokerRq()
for rq in broker_requests:
assert rq['api-version'] == 1
for op in rq['ops']:
if op['op'] in allowed_ops:
for k, v in require_vp.items():
if k not in op or op[k] != v:
break
else:
new_rq.add_op(op)
if len(new_rq.ops):
return new_rq

View File

@@ -18,6 +18,7 @@ import charms_openstack.bus
import charms_openstack.charm as charm
import charmhelpers.core as ch_core
import charmhelpers.contrib.storage.linux.ceph as ch_ceph
charms_openstack.bus.discover()
@@ -105,26 +106,50 @@ def configure_pools():
local = reactive.endpoint_from_flag('ceph-local.available')
remote = reactive.endpoint_from_flag('ceph-remote.available')
with charm.provide_charm_instance() as charm_instance:
rq = charm_instance.collapse_and_filter_broker_requests(
local.broker_requests, set(('create-pool',)),
require_vp={'app-name': 'rbd'})
remote_rq = charm_instance.collapse_and_filter_broker_requests(
remote.broker_requests, set(('create-pool',)),
require_vp={'app-name': 'rbd'})
pools_in_rq = charm_instance.pools_in_broker_request(
rq) if rq else set()
pools_in_rq |= charm_instance.pools_in_broker_request(
remote_rq) if remote_rq else set()
for pool, attrs in charm_instance.eligible_pools(local.pools).items():
if not (charm_instance.mirror_pool_enabled(pool) and
charm_instance.mirror_pool_has_peers(pool)):
ch_core.hookenv.log('Enabling mirroring for pool "{}"'
.format(pool),
level=ch_core.hookenv.INFO)
charm_instance.mirror_pool_enable(pool)
pg_num = attrs['parameters'].get('pg_num', None)
max_bytes = attrs['quota'].get('max_bytes', None)
max_objects = attrs['quota'].get('max_objects', None)
if 'erasure_code_profile' in attrs['parameters']:
ec_profile = attrs['parameters'].get(
'erasure_code_profile', None)
remote.create_erasure_pool(pool,
erasure_profile=ec_profile,
pg_num=pg_num,
app_name='rbd',
max_bytes=max_bytes,
max_objects=max_objects)
else:
size = attrs['parameters'].get('size', None)
remote.create_replicated_pool(pool, replicas=size,
pg_num=pg_num,
app_name='rbd',
max_bytes=max_bytes,
max_objects=max_objects)
if (pool not in pools_in_rq and
'erasure_code_profile' not in attrs['parameters']):
# A pool exists that there is no broker request for which means
# it is a manually created pool. We will forward creation of
# replicated pools but forwarding of manually created Erasure
# Coded pools is not supported.
pg_num = attrs['parameters'].get('pg_num')
max_bytes = attrs['quota'].get('max_bytes')
max_objects = attrs['quota'].get('max_objects')
size = attrs['parameters'].get('size')
ch_core.hookenv.log('Adding manually created pool "{}" to '
'request.'
.format(pool),
level=ch_core.hookenv.INFO)
if not rq:
rq = ch_ceph.CephBrokerRq()
rq.add_op_create_replicated_pool(
pool,
replica_count=size if not size else int(size),
pg_num=pg_num if not pg_num else int(pg_num),
app_name='rbd',
max_bytes=max_bytes if not max_bytes else int(max_bytes),
max_objects=max_objects if not max_objects else int(
max_objects),
)
ch_core.hookenv.log('Request for evaluation: "{}"'
.format(rq),
level=ch_core.hookenv.DEBUG)
if rq:
remote.maybe_send_rq(rq)

View File

@@ -181,34 +181,4 @@ class TestCephRBDMirrorHandlers(test_utils.PatchHelper):
'cinder-ceph')
self.crm_charm.mirror_pool_enable.assert_called_once_with(
'cinder-ceph')
endpoint_remote.create_replicated_pool.assert_called_once_with(
'cinder-ceph', replicas=3, pg_num=42, app_name='rbd',
max_bytes=1024, max_objects=51)
self.assertFalse(endpoint_remote.create_erasure_pool.called)
self.endpoint_from_flag.side_effect = [endpoint_local,
endpoint_remote]
self.crm_charm.mirror_pool_enabled.return_value = True
self.crm_charm.mirror_pool_has_peers.return_value = True
self.crm_charm.mirror_pool_enabled.reset_mock()
self.crm_charm.mirror_pool_enable.reset_mock()
handlers.configure_pools()
self.crm_charm.mirror_pool_enabled.assert_called_once_with(
'cinder-ceph')
self.crm_charm.mirror_pool_has_peers.assert_called_once_with(
'cinder-ceph')
self.assertFalse(self.crm_charm.mirror_pool_enable.called)
endpoint_local.pools = {
'cinder-ceph': {
'applications': {'rbd': {}},
'parameters': {'pg_num': 42, 'erasure_code_profile': 'prof'},
'quota': {'max_bytes': 1024, 'max_objects': 51},
},
}
self.endpoint_from_flag.side_effect = [endpoint_local,
endpoint_remote]
endpoint_remote.create_replicated_pool.reset_mock()
self.crm_charm.eligible_pools.return_value = endpoint_local.pools
handlers.configure_pools()
endpoint_remote.create_erasure_pool.assert_called_once_with(
'cinder-ceph', erasure_profile='prof', pg_num=42, app_name='rbd',
max_bytes=1024, max_objects=51)
endpoint_remote.maybe_send_rq.assert_called_once_with(mock.ANY)

View File

@@ -116,3 +116,79 @@ class TestCephRBDMirrorCharm(Helper):
'mode': 'pool',
'peers': []}
self.assertFalse(crmc.mirror_pool_has_peers('apool'))
def test_pools_in_broker_request(self):
rq = mock.MagicMock()
rq.api_version = 1
rq.ops = [{'op': 'create-pool', 'name': 'fakepool'}]
crmc = ceph_rbd_mirror.CephRBDMirrorCharm()
self.assertIn('fakepool', crmc.pools_in_broker_request(rq))
def test_collapse_and_filter_broker_requests(self):
self.patch_object(ceph_rbd_mirror.ch_ceph, 'CephBrokerRq')
class FakeCephBrokerRq(object):
def __init__(self):
self.ops = []
def add_op(self, op):
self.ops.append(op)
self.CephBrokerRq.side_effect = FakeCephBrokerRq
broker_requests = [
{
'api-version': 1,
'ops': [
{
'op': 'create-pool',
'name': 'pool-rq0',
'app-name': 'rbd',
},
]
},
{
'api-version': 1,
'ops': [
{
'op': 'create-pool',
'name': 'pool-rq1',
'app-name': 'notrbd',
},
]
},
{
'api-version': 1,
'ops': [
{
'op': 'create-pool',
'name': 'pool-rq2',
'app-name': 'rbd',
'someotherkey': 'value',
},
]
},
]
crmc = ceph_rbd_mirror.CephRBDMirrorCharm()
rq = crmc.collapse_and_filter_broker_requests(
broker_requests,
set(('create-pool',)),
require_vp={'app-name': 'rbd'})
self.assertDictEqual(
rq.ops[0],
{'app-name': 'rbd', 'name': 'pool-rq0', 'op': 'create-pool'})
self.assertDictEqual(
rq.ops[1],
{'app-name': 'rbd', 'name': 'pool-rq2', 'op': 'create-pool',
'someotherkey': 'value'})
self.assertTrue(len(rq.ops) == 2)
rq = crmc.collapse_and_filter_broker_requests(
broker_requests,
set(('create-pool',)),
require_vp={'app-name': 'rbd', 'someotherkey': 'value'})
self.assertDictEqual(
rq.ops[0],
{'app-name': 'rbd', 'name': 'pool-rq2', 'op': 'create-pool',
'someotherkey': 'value'})
self.assertTrue(len(rq.ops) == 1)