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:
Lenz Grimmer 2018-03-14 15:57:32 +01:00 committed by GitHub
commit 78022e13e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 142 additions and 12 deletions

View File

@ -2,6 +2,8 @@
from __future__ import absolute_import
import unittest
from .helper import DashboardTestCase, authenticate
@ -40,3 +42,72 @@ class RbdTest(DashboardTestCase):
self.assertEqual(img2['obj_size'], 4194304)
self.assertEqual(img2['features_name'],
'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"})

View File

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import math
import cherrypy
import rbd
from .. import mgr
@ -11,6 +13,18 @@ from ..tools import ApiController, AuthRequired, RESTController, ViewCache
@AuthRequired()
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):
self.rbd = None
@ -22,21 +36,33 @@ class Rbd(RESTController):
>>> Rbd._format_bitmask(45)
'deep-flatten, exclusive-lock, layering, object-map'
"""
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",
}
names = [val for key, val in RBD_FEATURES_NAME_MAPPING.items()
names = [val for key, val in Rbd.RBD_FEATURES_NAME_MAPPING.items()
if key & features == key]
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()
def _rbd_list(self, pool_name):
ioctx = mgr.rados.open_ioctx(pool_name)
@ -68,3 +94,36 @@ class Rbd(RESTController):
if status == ViewCache.VALUE_EXCEPTION:
raise 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}