mirror of
https://github.com/ceph/ceph
synced 2025-03-22 18:27:10 +00:00
Merge pull request #20751 from s0nea/wip-mgr-dashboard_rbd_add
mgr/dashboard_v2: Add RBD create functionality to the Python backend Reviewed-by: Ricardo Dias <rdias@suse.com> Reviewed-by: Sebastian Wagner <sebastian.wagner@suse.com> Reviewed-by: John Spray <john.spray@redhat.com> Reviewed-by: Ricardo Marques <rimarques@suse.com>
This commit is contained in:
commit
78022e13e7
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
from .helper import DashboardTestCase, authenticate
|
from .helper import DashboardTestCase, authenticate
|
||||||
|
|
||||||
|
|
||||||
@ -40,3 +42,72 @@ class RbdTest(DashboardTestCase):
|
|||||||
self.assertEqual(img2['obj_size'], 4194304)
|
self.assertEqual(img2['obj_size'], 4194304)
|
||||||
self.assertEqual(img2['features_name'],
|
self.assertEqual(img2['features_name'],
|
||||||
'deep-flatten, exclusive-lock, fast-diff, layering, object-map')
|
'deep-flatten, exclusive-lock, fast-diff, layering, object-map')
|
||||||
|
|
||||||
|
@authenticate
|
||||||
|
def test_create(self):
|
||||||
|
rbd_name = 'test_rbd'
|
||||||
|
data = {'pool_name': 'rbd',
|
||||||
|
'name': rbd_name,
|
||||||
|
'size': 10240}
|
||||||
|
self._post('/api/rbd', data)
|
||||||
|
self.assertStatus(201)
|
||||||
|
self.assertJsonBody({"success": True})
|
||||||
|
|
||||||
|
# TODO: change to GET the specific RBD instead of the list as soon as it is available?
|
||||||
|
get_res = self._get('/api/rbd/rbd')
|
||||||
|
self.assertStatus(200)
|
||||||
|
|
||||||
|
for rbd in get_res['value']:
|
||||||
|
if rbd['name'] == rbd_name:
|
||||||
|
self.assertEqual(rbd['size'], 10240)
|
||||||
|
self.assertEqual(rbd['num_objs'], 1)
|
||||||
|
self.assertEqual(rbd['obj_size'], 4194304)
|
||||||
|
self.assertEqual(rbd['features_name'],
|
||||||
|
'deep-flatten, exclusive-lock, fast-diff, layering, object-map')
|
||||||
|
break
|
||||||
|
|
||||||
|
# TODO: Re-enable this test for bluestore cluster by figuring out how to skip none-bluestore
|
||||||
|
# ones automatically
|
||||||
|
@unittest.skip("requires bluestore cluster")
|
||||||
|
@authenticate
|
||||||
|
def test_create_rbd_in_data_pool(self):
|
||||||
|
self._ceph_cmd(['osd', 'pool', 'create', 'data_pool', '12', '12', 'erasure'])
|
||||||
|
self._ceph_cmd(['osd', 'pool', 'application', 'enable', 'data_pool', 'rbd'])
|
||||||
|
self._ceph_cmd(['osd', 'pool', 'set', 'data_pool', 'allow_ec_overwrites', 'true'])
|
||||||
|
|
||||||
|
rbd_name = 'test_rbd_in_data_pool'
|
||||||
|
data = {'pool_name': 'rbd',
|
||||||
|
'name': rbd_name,
|
||||||
|
'size': 10240,
|
||||||
|
'data_pool': 'data_pool'}
|
||||||
|
self._post('/api/rbd', data)
|
||||||
|
self.assertStatus(201)
|
||||||
|
self.assertJsonBody({"success": True})
|
||||||
|
|
||||||
|
# TODO: possibly change to GET the specific RBD (see above)
|
||||||
|
get_res = self._get('/api/rbd/rbd')
|
||||||
|
self.assertStatus(200)
|
||||||
|
|
||||||
|
for rbd in get_res['value']:
|
||||||
|
if rbd['name'] == rbd_name:
|
||||||
|
self.assertEqual(rbd['size'], 10240)
|
||||||
|
self.assertEqual(rbd['num_objs'], 1)
|
||||||
|
self.assertEqual(rbd['obj_size'], 4194304)
|
||||||
|
self.assertEqual(rbd['features_name'], 'data-pool, deep-flatten, exclusive-lock, '
|
||||||
|
'fast-diff, layering, object-map')
|
||||||
|
break
|
||||||
|
|
||||||
|
self._ceph_cmd(['osd', 'pool', 'delete', 'data_pool', 'data_pool',
|
||||||
|
'--yes-i-really-really-mean-it'])
|
||||||
|
|
||||||
|
@authenticate
|
||||||
|
def test_create_rbd_twice(self):
|
||||||
|
data = {'pool_name': 'rbd',
|
||||||
|
'name': 'test_rbd_twice',
|
||||||
|
'size': 10240}
|
||||||
|
self._post('/api/rbd', data)
|
||||||
|
|
||||||
|
self._post('/api/rbd', data)
|
||||||
|
self.assertStatus(400)
|
||||||
|
self.assertJsonBody({"success": False, "errno": 17,
|
||||||
|
"detail": "[errno 17] error creating image"})
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import math
|
||||||
|
import cherrypy
|
||||||
import rbd
|
import rbd
|
||||||
|
|
||||||
from .. import mgr
|
from .. import mgr
|
||||||
@ -11,6 +13,18 @@ from ..tools import ApiController, AuthRequired, RESTController, ViewCache
|
|||||||
@AuthRequired()
|
@AuthRequired()
|
||||||
class Rbd(RESTController):
|
class Rbd(RESTController):
|
||||||
|
|
||||||
|
RBD_FEATURES_NAME_MAPPING = {
|
||||||
|
rbd.RBD_FEATURE_LAYERING: "layering",
|
||||||
|
rbd.RBD_FEATURE_STRIPINGV2: "striping",
|
||||||
|
rbd.RBD_FEATURE_EXCLUSIVE_LOCK: "exclusive-lock",
|
||||||
|
rbd.RBD_FEATURE_OBJECT_MAP: "object-map",
|
||||||
|
rbd.RBD_FEATURE_FAST_DIFF: "fast-diff",
|
||||||
|
rbd.RBD_FEATURE_DEEP_FLATTEN: "deep-flatten",
|
||||||
|
rbd.RBD_FEATURE_JOURNALING: "journaling",
|
||||||
|
rbd.RBD_FEATURE_DATA_POOL: "data-pool",
|
||||||
|
rbd.RBD_FEATURE_OPERATIONS: "operations",
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.rbd = None
|
self.rbd = None
|
||||||
|
|
||||||
@ -22,21 +36,33 @@ class Rbd(RESTController):
|
|||||||
>>> Rbd._format_bitmask(45)
|
>>> Rbd._format_bitmask(45)
|
||||||
'deep-flatten, exclusive-lock, layering, object-map'
|
'deep-flatten, exclusive-lock, layering, object-map'
|
||||||
"""
|
"""
|
||||||
RBD_FEATURES_NAME_MAPPING = {
|
names = [val for key, val in Rbd.RBD_FEATURES_NAME_MAPPING.items()
|
||||||
rbd.RBD_FEATURE_LAYERING: "layering",
|
|
||||||
rbd.RBD_FEATURE_STRIPINGV2: "striping",
|
|
||||||
rbd.RBD_FEATURE_EXCLUSIVE_LOCK: "exclusive-lock",
|
|
||||||
rbd.RBD_FEATURE_OBJECT_MAP: "object-map",
|
|
||||||
rbd.RBD_FEATURE_FAST_DIFF: "fast-diff",
|
|
||||||
rbd.RBD_FEATURE_DEEP_FLATTEN: "deep-flatten",
|
|
||||||
rbd.RBD_FEATURE_JOURNALING: "journaling",
|
|
||||||
rbd.RBD_FEATURE_DATA_POOL: "data-pool",
|
|
||||||
rbd.RBD_FEATURE_OPERATIONS: "operations",
|
|
||||||
}
|
|
||||||
names = [val for key, val in RBD_FEATURES_NAME_MAPPING.items()
|
|
||||||
if key & features == key]
|
if key & features == key]
|
||||||
return ', '.join(sorted(names))
|
return ', '.join(sorted(names))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_features(features):
|
||||||
|
"""
|
||||||
|
Converts the features list to bitmask:
|
||||||
|
|
||||||
|
>>> Rbd._format_features(['deep-flatten', 'exclusive-lock', 'layering', 'object-map'])
|
||||||
|
45
|
||||||
|
|
||||||
|
>>> Rbd._format_features(None) is None
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> Rbd._format_features('not a list') is None
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
if not features or not isinstance(features, list):
|
||||||
|
return None
|
||||||
|
|
||||||
|
res = 0
|
||||||
|
for key, value in Rbd.RBD_FEATURES_NAME_MAPPING.items():
|
||||||
|
if value in features:
|
||||||
|
res = key | res
|
||||||
|
return res
|
||||||
|
|
||||||
@ViewCache()
|
@ViewCache()
|
||||||
def _rbd_list(self, pool_name):
|
def _rbd_list(self, pool_name):
|
||||||
ioctx = mgr.rados.open_ioctx(pool_name)
|
ioctx = mgr.rados.open_ioctx(pool_name)
|
||||||
@ -68,3 +94,36 @@ class Rbd(RESTController):
|
|||||||
if status == ViewCache.VALUE_EXCEPTION:
|
if status == ViewCache.VALUE_EXCEPTION:
|
||||||
raise value
|
raise value
|
||||||
return {'status': status, 'value': value}
|
return {'status': status, 'value': value}
|
||||||
|
|
||||||
|
def create(self, data):
|
||||||
|
if not self.rbd:
|
||||||
|
self.rbd = rbd.RBD()
|
||||||
|
|
||||||
|
# Get input values
|
||||||
|
name = data.get('name')
|
||||||
|
pool_name = data.get('pool_name')
|
||||||
|
size = data.get('size')
|
||||||
|
obj_size = data.get('obj_size')
|
||||||
|
features = data.get('features')
|
||||||
|
stripe_unit = data.get('stripe_unit')
|
||||||
|
stripe_count = data.get('stripe_count')
|
||||||
|
data_pool = data.get('data_pool')
|
||||||
|
|
||||||
|
# Set order
|
||||||
|
order = None
|
||||||
|
if obj_size and obj_size > 0:
|
||||||
|
order = int(round(math.log(float(obj_size), 2)))
|
||||||
|
|
||||||
|
# Set features
|
||||||
|
feature_bitmask = self._format_features(features)
|
||||||
|
|
||||||
|
ioctx = mgr.rados.open_ioctx(pool_name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.rbd.create(ioctx, name, size, order=order, old_format=False,
|
||||||
|
features=feature_bitmask, stripe_unit=stripe_unit,
|
||||||
|
stripe_count=stripe_count, data_pool=data_pool)
|
||||||
|
except rbd.OSError as e:
|
||||||
|
cherrypy.response.status = 400
|
||||||
|
return {'success': False, 'detail': str(e), 'errno': e.errno}
|
||||||
|
return {'success': True}
|
||||||
|
Loading…
Reference in New Issue
Block a user