from __future__ import absolute_import
-import unittest
+import six
-from .helper import DashboardTestCase
+from .helper import DashboardTestCase, JObj, JList
class ECPTest(DashboardTestCase):
@DashboardTestCase.RunAs('test', 'test', ['block-manager'])
def test_read_access_permissions(self):
- self._get("/api/erasure_code_profile")
+ self._get('/api/erasure_code_profile')
self.assertStatus(403)
@DashboardTestCase.RunAs('test', 'test', ['read-only'])
def test_write_access_permissions(self):
- self._get("/api/erasure_code_profile")
+ self._get('/api/erasure_code_profile')
self.assertStatus(200)
data = {'name': 'ecp32', 'k': 3, 'm': 2}
self._post('/api/erasure_code_profile', data)
if default:
default_ecp = {
'k': 2,
- 'technique': "reed_sol_van",
+ 'technique': 'reed_sol_van',
'm': 1,
- 'name': "default",
- 'plugin': "jerasure"
+ 'name': 'default',
+ 'plugin': 'jerasure'
}
if 'crush-failure-domain' in default[0]:
default_ecp['crush-failure-domain'] = default[0]['crush-failure-domain']
self._get('/api/erasure_code_profile/ecp32')
self.assertJsonBody({
- "crush-device-class": "",
- "crush-failure-domain": "osd",
- "crush-root": "default",
- "jerasure-per-chunk-alignment": "false",
- "k": 3,
- "m": 2,
- "name": "ecp32",
- "plugin": "jerasure",
- "technique": "reed_sol_van",
- "w": "8"
+ 'crush-device-class': '',
+ 'crush-failure-domain': 'osd',
+ 'crush-root': 'default',
+ 'jerasure-per-chunk-alignment': 'false',
+ 'k': 3,
+ 'm': 2,
+ 'name': 'ecp32',
+ 'plugin': 'jerasure',
+ 'technique': 'reed_sol_van',
+ 'w': '8'
})
self.assertStatus(200)
self._get('/api/erasure_code_profile/lrc')
self.assertJsonBody({
- "crush-device-class": "",
- "crush-failure-domain": "host",
- "crush-root": "default",
- "k": 2,
- "l": "2",
- "m": 2,
- "name": "lrc",
- "plugin": "lrc"
+ 'crush-device-class': '',
+ 'crush-failure-domain': 'host',
+ 'crush-root': 'default',
+ 'k': 2,
+ 'l': '2',
+ 'm': 2,
+ 'name': 'lrc',
+ 'plugin': 'lrc'
})
self.assertStatus(200)
self._delete('/api/erasure_code_profile/lrc')
self.assertStatus(204)
+ def test_ecp_info(self):
+ self._get('/api/erasure_code_profile/_info')
+ self.assertSchemaBody(JObj({
+ 'names': JList(six.string_types),
+ 'failure_domains': JList(six.string_types),
+ 'plugins': JList(six.string_types),
+ 'devices': JList(six.string_types),
+ 'directory': six.string_types,
+ }))
+
from cherrypy import NotFound
-from . import ApiController, RESTController
+from . import ApiController, RESTController, Endpoint, ReadPermission
from ..security import Scope
from ..services.ceph_service import CephService
from .. import mgr
def _serialize_ecp(name, ecp):
+ def serialize_numbers(key):
+ value = ecp.get(key)
+ if value is not None:
+ ecp[key] = int(value)
+
ecp['name'] = name
- ecp['k'] = int(ecp['k'])
- ecp['m'] = int(ecp['m'])
+ serialize_numbers('k')
+ serialize_numbers('m')
return ecp
@ApiController('/erasure_code_profile', Scope.POOL)
class ErasureCodeProfile(RESTController):
- """
+ '''
create() supports additional key-value arguments that are passed to the
ECP plugin.
- """
+ '''
def list(self):
ret = []
except KeyError:
raise NotFound('No such erasure code profile')
- def create(self, name, k, m, plugin=None, ruleset_failure_domain=None, **kwargs):
- kwargs['k'] = k
- kwargs['m'] = m
- if plugin:
- kwargs['plugin'] = plugin
- if ruleset_failure_domain:
- kwargs['ruleset_failure_domain'] = ruleset_failure_domain
-
+ def create(self, name, **kwargs):
profile = ['{}={}'.format(key, value) for key, value in kwargs.items()]
CephService.send_command('mon', 'osd erasure-code-profile set', name=name,
profile=profile)
def delete(self, name):
CephService.send_command('mon', 'osd erasure-code-profile rm', name=name)
+
+ @Endpoint()
+ @ReadPermission
+ def _info(self):
+ '''Used for profile creation and editing'''
+ config = mgr.get('config')
+ osd_map_crush = mgr.get('osd_map_crush')
+ return {
+ # Because 'shec' is experimental it's not included
+ 'plugins': config['osd_erasure_code_plugins'].split() + ['shec'],
+ 'directory': config['erasure_code_dir'],
+ 'devices': list(set([device['class'] for device in osd_map_crush['devices']])),
+ 'failure_domains': [domain['name'] for domain in osd_map_crush['types']],
+ 'names': [name for name, _ in
+ mgr.get('osd_map').get('erasure_code_profiles', {}).items()]
+ }