From c43a1b4af5e4725528354269daf1daef5c52a1da Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Tue, 24 Apr 2018 18:39:56 +0200 Subject: [PATCH] mgr/dashboard: Exception handling: browsable API * Added display of Exceptions * Fixed missing sub-path * Added delete-form * Fixed default arguments Signed-off-by: Sebastian Wagner --- .../mgr/dashboard/controllers/__init__.py | 61 +++++++++++++++---- src/pybind/mgr/dashboard/controllers/rbd.py | 11 +++- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/pybind/mgr/dashboard/controllers/__init__.py b/src/pybind/mgr/dashboard/controllers/__init__.py index b0fdf02faae02..b8ae52f5ea8a8 100644 --- a/src/pybind/mgr/dashboard/controllers/__init__.py +++ b/src/pybind/mgr/dashboard/controllers/__init__.py @@ -164,28 +164,40 @@ def browsable_api_view(meth): return meth(self, *vpath, **kwargs) if 'text/html' not in cherrypy.request.headers.get('Accept', ''): return meth(self, *vpath, **kwargs) + if '_method' in kwargs: cherrypy.request.method = kwargs.pop('_method').upper() + + # Form typically use None as default, but HTML defaults to empty-string. + for k in kwargs: + if not kwargs[k]: + del kwargs[k] + if '_raw' in kwargs: kwargs.pop('_raw') return meth(self, *vpath, **kwargs) + sub_path = cherrypy.request.path_info.split(self._cp_path_, 1)[-1].strip('/').split('/') + template = """

Browsable API

{docstring}

Request

{method} {breadcrump}

+ {params}

Response

Status: {status_code}

{reponse_headers}
-
+

Data

{data}
+ {exception} {create_form} + {delete_form}

Note

Please note that this API is not an official Ceph REST API to be used by third-party applications. It's primary purpose is to serve @@ -195,33 +207,51 @@ def browsable_api_view(meth): create_form_template = """

Create Form

-
+ {fields}
""" - try: - data = meth(self, *vpath, **kwargs) - except Exception as e: # pylint: disable=broad-except + delete_form_template = """ +

Create Form

+
+ + +
+ """ + + def mk_exception(e): except_template = """

Exception: {etype}: {tostr}

{trace}
- Params: {kwargs} """ import traceback tb = sys.exc_info()[2] cherrypy.response.headers['Content-Type'] = 'text/html' - data = except_template.format( + return except_template.format( etype=e.__class__.__name__, tostr=str(e), trace='\n'.join(traceback.format_tb(tb)), kwargs=kwargs ) - if cherrypy.response.headers['Content-Type'] == 'application/json': - data = json.dumps(json.loads(data), indent=2, sort_keys=True) + try: + data = meth(self, *vpath, **kwargs) + exception = '' + if cherrypy.response.headers['Content-Type'] == 'application/json': + try: + data = json.dumps(json.loads(data), indent=2, sort_keys=True) + except Exception: # pylint: disable=broad-except + pass + except (ViewCacheNoDataException, DashboardException) as e: + cherrypy.response.status = getattr(e, 'status', 400) + data = str(serialize_dashboard_exception(e)) + exception = mk_exception(e) + except Exception as e: # pylint: disable=broad-except + data = '' + exception = mk_exception(e) try: create = getattr(self, 'create') @@ -231,7 +261,7 @@ def browsable_api_view(meth): create_form = create_form_template.format( fields='
'.join(input_fields), path=self._cp_path_, - vpath='/'.join(vpath) + sub_path='/'.join(sub_path) ) except AttributeError: create_form = '' @@ -247,13 +277,18 @@ def browsable_api_view(meth): docstring='
{}
'.format(self.__doc__) if self.__doc__ else '', method=cherrypy.request.method, path=self._cp_path_, - vpath='/'.join(vpath), - breadcrump=mk_breadcrump(['api', self._cp_path_] + list(vpath)), + sub_path='/'.join(sub_path), + breadcrump=mk_breadcrump(['api', self._cp_path_] + list(sub_path)), status_code=cherrypy.response.status, reponse_headers='\n'.join( '{}: {}'.format(k, v) for k, v in cherrypy.response.headers.items()), data=data, - create_form=create_form + exception=exception, + create_form=create_form, + delete_form=delete_form_template.format(path=self._cp_path_, sub_path='/'.join( + sub_path)) if sub_path else '', + params='

Rrequest Params

{}
'.format( + json.dumps(kwargs, indent=2)) if kwargs else '', ) wrapper.exposed = True diff --git a/src/pybind/mgr/dashboard/controllers/rbd.py b/src/pybind/mgr/dashboard/controllers/rbd.py index 557e3c6af379d..666a49733c293 100644 --- a/src/pybind/mgr/dashboard/controllers/rbd.py +++ b/src/pybind/mgr/dashboard/controllers/rbd.py @@ -7,6 +7,8 @@ import math from functools import partial import cherrypy +import six + import rbd from . import ApiController, AuthRequired, RESTController, Task @@ -75,9 +77,12 @@ def _format_features(features): >>> _format_features(None) is None True - >>> _format_features('not a list') is None - True + >>> _format_features('deep-flatten, exclusive-lock') + 32 """ + if isinstance(features, six.string_types): + features = features.split(',') + if not isinstance(features, list): return None @@ -270,6 +275,8 @@ class Rbd(RESTController): def create(self, name, pool_name, size, obj_size=None, features=None, stripe_unit=None, stripe_count=None, data_pool=None): + size = int(size) + def _create(ioctx): rbd_inst = rbd.RBD() -- 2.39.5