From 89b3b8d1eb82045cf16d13ba513d714b86235ed9 Mon Sep 17 00:00:00 2001 From: Pedro Guimaraes Date: Mon, 1 Jun 2020 10:16:30 +0200 Subject: [PATCH] Update support for Erasure Coded pools Add support to allow 'allow_ec_overwrites' to be set on a pool, supporting RBD and CephFS uses cases for Erasure Coding. Add support to allow the EC technique to be provided when creating EC profiles. Add support for extended configuration of EC plugins. Depends-On: https://github.com/juju/charm-helpers/pull/498 Change-Id: I2547933964849f7af1c623b2fbc014fb332839ef --- charms_ceph/broker.py | 41 +++++++++++++++++++----- unit_tests/test_broker.py | 66 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/charms_ceph/broker.py b/charms_ceph/broker.py index 15552cd..d5c8389 100644 --- a/charms_ceph/broker.py +++ b/charms_ceph/broker.py @@ -155,25 +155,47 @@ def handle_create_erasure_profile(request, service): :param service: The ceph client to run the command under. :returns: dict. exit-code and reason if not 0 """ - # "local" | "shec" or it defaults to "jerasure" + # "isa" | "lrc" | "shec" | "clay" or it defaults to "jerasure" erasure_type = request.get('erasure-type') - # "host" | "rack" or it defaults to "host" # Any valid Ceph bucket + # dependent on erasure coding type + erasure_technique = request.get('erasure-technique') + # "host" | "rack" | ... failure_domain = request.get('failure-domain') name = request.get('name') # Binary Distribution Matrix (BDM) parameters bdm_k = request.get('k') bdm_m = request.get('m') + # LRC parameters bdm_l = request.get('l') + crush_locality = request.get('crush-locality') + # SHEC parameters + bdm_c = request.get('c') + # CLAY parameters + bdm_d = request.get('d') + scalar_mds = request.get('scalar-mds') + # Device Class + device_class = request.get('device-class') - if failure_domain not in CEPH_BUCKET_TYPES: + if failure_domain and failure_domain not in CEPH_BUCKET_TYPES: msg = "failure-domain must be one of {}".format(CEPH_BUCKET_TYPES) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} - create_erasure_profile(service=service, erasure_plugin_name=erasure_type, - profile_name=name, failure_domain=failure_domain, - data_chunks=bdm_k, coding_chunks=bdm_m, - locality=bdm_l) + create_erasure_profile(service=service, + erasure_plugin_name=erasure_type, + profile_name=name, + failure_domain=failure_domain, + data_chunks=bdm_k, + coding_chunks=bdm_m, + locality=bdm_l, + durability_estimator=bdm_d, + helper_chunks=bdm_c, + scalar_mds=scalar_mds, + crush_locality=crush_locality, + device_class=device_class, + erasure_plugin_technique=erasure_technique) + + return {'exit-code': 0} def handle_add_permissions_to_key(request, service): @@ -387,6 +409,7 @@ def handle_erasure_pool(request, service): max_objects = request.get('max-objects') weight = request.get('weight') group_name = request.get('group') + allow_ec_overwrites = request.get('allow-ec-overwrites') if erasure_profile is None: erasure_profile = "default-canonical" @@ -416,7 +439,9 @@ def handle_erasure_pool(request, service): pool = ErasurePool(service=service, name=pool_name, erasure_code_profile=erasure_profile, - percent_data=weight, app_name=app_name) + percent_data=weight, + app_name=app_name, + allow_ec_overwrites=allow_ec_overwrites) # Ok make the erasure pool if not pool_exists(service=service, name=pool_name): log("Creating pool '{}' (erasure_profile={})" diff --git a/unit_tests/test_broker.py b/unit_tests/test_broker.py index e0e4a9a..b6b1819 100644 --- a/unit_tests/test_broker.py +++ b/unit_tests/test_broker.py @@ -15,7 +15,7 @@ import json import unittest -from unittest.mock import patch +from unittest.mock import patch, ANY import charms_ceph.broker @@ -703,3 +703,67 @@ class CephBrokerTestCase(unittest.TestCase): 'osd', ('allow rwx pool=glance, ' 'allow class-read object_prefix rbd_children')]) + + @patch.object(charms_ceph.broker, 'create_erasure_profile') + def test_handle_create_erasure_profile(self, + mock_create_erasure_profile): + request = { + 'erasure-type': 'jerasure', + 'erasure-technique': 'reed_sol', + 'name': 'newprofile', + 'failure-domain': 'rack', + 'k': 4, + 'm': 2, + 'l': 3, + 'c': 5, + 'd': 9, + 'scalar-mds': 'isa', + 'crush-locality': 'host', + 'device-class': 'ssd', + } + resp = charms_ceph.broker.handle_create_erasure_profile( + request, + 'testservice' + ) + self.assertEqual(resp, {'exit-code': 0}) + + mock_create_erasure_profile.assert_called_once_with( + service='testservice', + erasure_plugin_name='jerasure', + profile_name='newprofile', + failure_domain='rack', + data_chunks=4, + coding_chunks=2, + locality=3, + durability_estimator=9, + helper_chunks=5, + scalar_mds='isa', + crush_locality='host', + device_class='ssd', + erasure_plugin_technique='reed_sol' + ) + + @patch.object(charms_ceph.broker, 'create_erasure_profile') + def test_handle_create_erasure_profile_bad(self, + mock_create_erasure_profile): + request = { + 'erasure-type': 'jerasure', + 'erasure-technique': 'reed_sol', + 'name': 'newprofile', + 'failure-domain': 'foobar', + 'k': 4, + 'm': 2, + 'l': None, + } + resp = charms_ceph.broker.handle_create_erasure_profile( + request, + 'testservice' + ) + self.assertEqual( + resp, + { + 'exit-code': 1, + 'stderr': ANY, + } + ) + mock_create_erasure_profile.assert_not_called()