diff --git a/ceph/broker.py b/ceph/broker.py index 0d5a7e8..3226f4c 100644 --- a/ceph/broker.py +++ b/ceph/broker.py @@ -369,7 +369,8 @@ def handle_erasure_pool(request, service): """ pool_name = request.get('name') erasure_profile = request.get('erasure-profile') - quota = request.get('max-bytes') + max_bytes = request.get('max-bytes') + max_objects = request.get('max-objects') weight = request.get('weight') group_name = request.get('group') @@ -409,8 +410,9 @@ def handle_erasure_pool(request, service): pool.create() # Set a quota if requested - if quota is not None: - set_pool_quota(service=service, pool_name=pool_name, max_bytes=quota) + if max_bytes or max_objects: + set_pool_quota(service=service, pool_name=pool_name, + max_bytes=max_bytes, max_objects=max_objects) def handle_replicated_pool(request, service): @@ -422,7 +424,8 @@ def handle_replicated_pool(request, service): """ pool_name = request.get('name') replicas = request.get('replicas') - quota = request.get('max-bytes') + max_bytes = request.get('max-bytes') + max_objects = request.get('max-objects') weight = request.get('weight') group_name = request.get('group') @@ -469,8 +472,9 @@ def handle_replicated_pool(request, service): level=DEBUG) # Set a quota if requested - if quota is not None: - set_pool_quota(service=service, pool_name=pool_name, max_bytes=quota) + if max_bytes or max_objects: + set_pool_quota(service=service, pool_name=pool_name, + max_bytes=max_bytes, max_objects=max_objects) def handle_create_cache_tier(request, service): diff --git a/ceph/utils.py b/ceph/utils.py index 6a68994..b4f8790 100644 --- a/ceph/utils.py +++ b/ceph/utils.py @@ -2533,7 +2533,8 @@ def list_pools(client='admin'): try: pool_list = [] pools = subprocess.check_output(['rados', '--id', client, 'lspools'], - universal_newlines=True) + universal_newlines=True, + stderr=subprocess.STDOUT) for pool in pools.splitlines(): pool_list.append(pool) return pool_list @@ -2558,8 +2559,8 @@ def get_pool_param(pool, param, client='admin'): """ try: output = subprocess.check_output( - ['ceph', '--id', client, 'osd', 'pool', 'get', - pool, param], universal_newlines=True) + ['ceph', '--id', client, 'osd', 'pool', 'get', pool, param], + universal_newlines=True, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as cp: if cp.returncode == 2 and 'ENOENT: option' in cp.output: return None @@ -2568,6 +2569,27 @@ def get_pool_param(pool, param, client='admin'): return output.split(':')[1].lstrip().rstrip() +def get_pool_erasure_profile(pool, client='admin'): + """Get erasure code profile for pool. + + :param pool: Name of pool to get variable from + :type pool: str + :param client: (Optional) client id for ceph key to use + Defaults to ``admin`` + :type cilent: str + :returns: Erasure code profile of pool or None + :rtype: str or None + :raises: subprocess.CalledProcessError + """ + try: + return get_pool_param(pool, 'erasure_code_profile', client=client) + except subprocess.CalledProcessError as cp: + if cp.returncode == 13 and 'EACCES: pool' in cp.output: + # Not a Erasure coded pool + return None + raise + + def get_pool_quota(pool, client='admin'): """Get pool quota. @@ -2582,7 +2604,7 @@ def get_pool_quota(pool, client='admin'): """ output = subprocess.check_output( ['ceph', '--id', client, 'osd', 'pool', 'get-quota', pool], - universal_newlines=True) + universal_newlines=True, stderr=subprocess.STDOUT) rc = re.compile(r'\s+max\s+(\S+)\s*:\s+(\d+)') result = {} for line in output.splitlines(): @@ -2610,7 +2632,9 @@ def get_pool_applications(pool='', client='admin'): if pool: cmd.append(pool) try: - output = subprocess.check_output(cmd, universal_newlines=True) + output = subprocess.check_output(cmd, + universal_newlines=True, + stderr=subprocess.STDOUT) except subprocess.CalledProcessError as cp: if cp.returncode == 2 and 'ENOENT' in cp.output: return {} @@ -2646,6 +2670,10 @@ def list_pools_detail(): for param in get_params: result[pool]['parameters'].update({ param: get_pool_param(pool, param)}) + erasure_profile = get_pool_erasure_profile(pool) + if erasure_profile: + result[pool]['parameters'].update({ + 'erasure_code_profile': erasure_profile}) return result diff --git a/unit_tests/test_utils.py b/unit_tests/test_utils.py index 4952460..e258a23 100644 --- a/unit_tests/test_utils.py +++ b/unit_tests/test_utils.py @@ -13,6 +13,7 @@ # limitations under the License. import collections +import subprocess import unittest from mock import ( @@ -821,15 +822,18 @@ class CephTestCase(unittest.TestCase): _check_output.return_value = 'poola\npoolb\n' self.assertEqual(utils.list_pools('someuser'), ['poola', 'poolb']) _check_output.assert_called_with(['rados', '--id', 'someuser', - 'lspools'], universal_newlines=True) + 'lspools'], universal_newlines=True, + stderr=subprocess.STDOUT) self.assertEqual(utils.list_pools(client='someotheruser'), ['poola', 'poolb']) _check_output.assert_called_with(['rados', '--id', 'someotheruser', - 'lspools'], universal_newlines=True) + 'lspools'], universal_newlines=True, + stderr=subprocess.STDOUT) self.assertEqual(utils.list_pools(), ['poola', 'poolb']) _check_output.assert_called_with(['rados', '--id', 'admin', - 'lspools'], universal_newlines=True) + 'lspools'], universal_newlines=True, + stderr=subprocess.STDOUT) @patch.object(utils.subprocess, 'check_output') def test_get_pool_param(self, _check_output): @@ -837,7 +841,22 @@ class CephTestCase(unittest.TestCase): self.assertEqual(utils.get_pool_param('rbd', 'size'), '3') _check_output.assert_called_with(['ceph', '--id', 'admin', 'osd', 'pool', 'get', 'rbd', 'size'], - universal_newlines=True) + universal_newlines=True, + stderr=subprocess.STDOUT) + + @patch.object(utils, 'get_pool_param') + def test_get_pool_erasure_profile(self, _get_pool_param): + _get_pool_param.side_effect = subprocess.CalledProcessError( + 13, [], 'EACCES: pool') + self.assertEqual(utils.get_pool_erasure_profile('cinder-ceph'), None) + _get_pool_param.side_effect = subprocess.CalledProcessError( + 22, [], 'EINVAL: invalid') + with self.assertRaises(subprocess.CalledProcessError): + utils.get_pool_erasure_profile('cinder-ceph') + _get_pool_param.side_effect = None + _get_pool_param.return_value = 'my-ec-profile' + self.assertEqual(utils.get_pool_erasure_profile('cinder-ceph'), + 'my-ec-profile') @patch.object(utils.subprocess, 'check_output') def test_get_pool_quota(self, _check_output): @@ -849,7 +868,8 @@ class CephTestCase(unittest.TestCase): {}) _check_output.assert_called_with(['ceph', '--id', 'admin', 'osd', 'pool', 'get-quota', 'rbd'], - universal_newlines=True) + universal_newlines=True, + stderr=subprocess.STDOUT) _check_output.return_value = ( "quotas for pool 'rbd':\n" " max objects: 10\n" @@ -879,38 +899,46 @@ class CephTestCase(unittest.TestCase): {'pool': {'application': {}}}) _check_output.assert_called_with(['ceph', '--id', 'admin', 'osd', 'pool', 'application', 'get'], - universal_newlines=True) + universal_newlines=True, + stderr=subprocess.STDOUT) utils.get_pool_applications('42') _check_output.assert_called_with(['ceph', '--id', 'admin', 'osd', 'pool', 'application', 'get', '42'], - universal_newlines=True) + universal_newlines=True, + stderr=subprocess.STDOUT) + @patch.object(utils, 'get_pool_erasure_profile') @patch.object(utils, 'get_pool_param') @patch.object(utils, 'get_pool_quota') @patch.object(utils, 'list_pools') @patch.object(utils, 'get_pool_applications') def test_list_pools_detail(self, _get_pool_applications, _list_pools, - _get_pool_quota, _get_pool_param): + _get_pool_quota, _get_pool_param, + _get_pool_erasure_profile): self.assertEqual(utils.list_pools_detail(), {}) _get_pool_applications.return_value = {'pool': {'application': {}}} _list_pools.return_value = ['pool', 'pool2'] _get_pool_quota.return_value = {'max_objects': '10', 'max_bytes': '1000'} _get_pool_param.return_value = '42' - self.assertEqual(utils.list_pools_detail(), - {'pool': {'applications': {'application': {}}, - 'parameters': {'pg_num': '42', - 'size': '42'}, - 'quota': {'max_bytes': '1000', - 'max_objects': '10'}, - }, - 'pool2': {'applications': {}, - 'parameters': {'pg_num': '42', - 'size': '42'}, - 'quota': {'max_bytes': '1000', - 'max_objects': '10'}, - }, - }) + _get_pool_erasure_profile.return_value = 'my-ec-profile' + self.assertEqual( + utils.list_pools_detail(), + {'pool': {'applications': {'application': {}}, + 'parameters': {'pg_num': '42', + 'size': '42', + 'erasure_code_profile': 'my-ec-profile'}, + 'quota': {'max_bytes': '1000', + 'max_objects': '10'}, + }, + 'pool2': {'applications': {}, + 'parameters': {'pg_num': '42', + 'size': '42', + 'erasure_code_profile': 'my-ec-profile'}, + 'quota': {'max_bytes': '1000', + 'max_objects': '10'}, + }, + }) class CephVolumeSizeCalculatorTestCase(unittest.TestCase):