]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: REST API returns 500 when no Content-Type is specified 37307/head
authorAvan Thakkar <athakkar@redhat.com>
Wed, 29 Jul 2020 17:56:04 +0000 (23:26 +0530)
committerAvan Thakkar <athakkar@redhat.com>
Tue, 22 Sep 2020 12:49:30 +0000 (18:19 +0530)
Fixes: https://tracker.ceph.com/issues/41060
Signed-off-by: Avan Thakkar <athakkar@redhat.com>
(cherry picked from commit ea031de0908a249b416617a1d9cc806c356520e4)
(cherry picked from commit a5bf74e6a6c7678bbb3689a4b52fe817418da58b)
(cherry picked from commit 9dd629ca7ea5bafe883f48d257386a8c5d32c1c7)

src/pybind/mgr/dashboard/controllers/__init__.py
src/pybind/mgr/dashboard/controllers/auth.py
src/pybind/mgr/dashboard/controllers/docs.py
src/pybind/mgr/dashboard/controllers/mgr_modules.py
src/pybind/mgr/dashboard/controllers/osd.py
src/pybind/mgr/dashboard/controllers/rbd.py
src/pybind/mgr/dashboard/controllers/rgw.py
src/pybind/mgr/dashboard/module.py

index c7f75dc5b5c8b06e2b74e7d632a1d9b629f133de..0353835b1f556b1b07e1a0a718850a3268a3877b 100644 (file)
@@ -477,6 +477,7 @@ class BaseController(object):
         """
         An instance of this class represents an endpoint.
         """
+
         def __init__(self, ctrl, func):
             self.ctrl = ctrl
             self.inst = None
@@ -931,3 +932,17 @@ def DeletePermission(func):
 def UpdatePermission(func):
     _set_func_permissions(func, Permission.UPDATE)
     return func
+
+
+# Empty request body decorator
+
+def allow_empty_body(func):  # noqa: N802
+    """
+    The POST/PUT request methods decorated with ``@allow_empty_body``
+    are allowed to send empty request body.
+    """
+    try:
+        func._cp_config['tools.json_in.force'] = False
+    except (AttributeError, KeyError):
+        func._cp_config = {'tools.json_in.force': False}
+    return func
index f1c6545a1754e9b52b9938da83f75d8607692c0a..ea7d6f627855920bedf2a11f87c1f4b69ca640be 100644 (file)
@@ -4,7 +4,8 @@ from __future__ import absolute_import
 import cherrypy
 import jwt
 
-from . import ApiController, RESTController
+from . import ApiController, RESTController, \
+    allow_empty_body
 from .. import logger, mgr
 from ..exceptions import DashboardException
 from ..services.auth import AuthManager, JwtManager
@@ -36,6 +37,7 @@ class Auth(RESTController):
                                  component='auth')
 
     @RESTController.Collection('POST')
+    @allow_empty_body
     def logout(self):
         logger.debug('Logout successful')
         token = JwtManager.get_token_from_header()
index 1e487f97715a1d9f390bdcc2b83fe67ea8aa153f..d4c33783587e992912fbaac5eb631e16244feafe 100644 (file)
@@ -3,7 +3,8 @@ from __future__ import absolute_import
 
 import cherrypy
 
-from . import Controller, BaseController, Endpoint, ENDPOINT_MAP
+from . import Controller, BaseController, Endpoint, ENDPOINT_MAP, \
+    allow_empty_body
 from .. import logger, mgr
 
 from ..tools import str_to_bool
@@ -444,5 +445,6 @@ class Docs(BaseController):
 
     @Endpoint('POST', path="/", json_response=False,
               query_params="{all_endpoints}")
+    @allow_empty_body
     def _with_token(self, token, all_endpoints=False):
         return self._swagger_ui_page(all_endpoints, token)
index 6e3cb5a3ee18013853ef8d3e4916dd6c769aaab2..c37225c9b971dd6e16284e32076e4111fd0007c6 100644 (file)
@@ -1,7 +1,8 @@
 # -*- coding: utf-8 -*-
 from __future__ import absolute_import
 
-from . import ApiController, RESTController
+from . import ApiController, RESTController, \
+    allow_empty_body
 from .. import mgr
 from ..security import Scope
 from ..services.ceph_service import CephService
@@ -69,6 +70,7 @@ class MgrModules(RESTController):
 
     @RESTController.Resource('POST')
     @handle_send_command_error('mgr_modules')
+    @allow_empty_body
     def enable(self, module_name):
         """
         Enable the specified Ceph Mgr module.
@@ -81,6 +83,7 @@ class MgrModules(RESTController):
 
     @RESTController.Resource('POST')
     @handle_send_command_error('mgr_modules')
+    @allow_empty_body
     def disable(self, module_name):
         """
         Disable the specified Ceph Mgr module.
index f5606cd0339679bc66bb68b1e29a8eccd386f042..a971a512738e4a7dc91698175a15255ae69e0fc6 100644 (file)
@@ -3,7 +3,8 @@ from __future__ import absolute_import
 
 from mgr_util import get_most_recent_rate
 
-from . import ApiController, RESTController, UpdatePermission
+from . import ApiController, RESTController, UpdatePermission, \
+    allow_empty_body
 from .. import mgr, logger
 from ..security import Scope
 from ..services.ceph_service import CephService, SendCommandError
@@ -92,23 +93,28 @@ class Osd(RESTController):
 
     @RESTController.Resource('POST', query_params=['deep'])
     @UpdatePermission
+    @allow_empty_body
     def scrub(self, svc_id, deep=False):
         api_scrub = "osd deep-scrub" if str_to_bool(deep) else "osd scrub"
         CephService.send_command("mon", api_scrub, who=svc_id)
 
     @RESTController.Resource('POST')
+    @allow_empty_body
     def mark_out(self, svc_id):
         CephService.send_command('mon', 'osd out', ids=[svc_id])
 
     @RESTController.Resource('POST')
+    @allow_empty_body
     def mark_in(self, svc_id):
         CephService.send_command('mon', 'osd in', ids=[svc_id])
 
     @RESTController.Resource('POST')
+    @allow_empty_body
     def mark_down(self, svc_id):
         CephService.send_command('mon', 'osd down', ids=[svc_id])
 
     @RESTController.Resource('POST')
+    @allow_empty_body
     def reweight(self, svc_id, weight):
         """
         Reweights the OSD temporarily.
@@ -130,6 +136,7 @@ class Osd(RESTController):
             weight=float(weight))
 
     @RESTController.Resource('POST')
+    @allow_empty_body
     def mark_lost(self, svc_id):
         """
         Note: osd must be marked `down` before marking lost.
@@ -155,6 +162,7 @@ class Osd(RESTController):
         }
 
     @RESTController.Resource('POST')
+    @allow_empty_body
     def purge(self, svc_id):
         """
         Note: osd must be marked `down` before removal.
@@ -163,6 +171,7 @@ class Osd(RESTController):
                                  yes_i_really_mean_it=True)
 
     @RESTController.Resource('POST')
+    @allow_empty_body
     def destroy(self, svc_id):
         """
         Mark osd as being destroyed. Keeps the ID intact (allowing reuse), but
index 8e0b184c9b7cc1331f8d6f8c3014910538e90a0c..52dca087044b3258a596f29c70afb194ca327198 100644 (file)
@@ -12,7 +12,7 @@ import cherrypy
 import rbd
 
 from . import ApiController, RESTController, Task, UpdatePermission, \
-              DeletePermission, CreatePermission, ReadPermission
+              DeletePermission, CreatePermission, ReadPermission, allow_empty_body
 from .. import mgr, logger
 from ..security import Scope
 from ..services.ceph_service import CephService
@@ -307,6 +307,7 @@ class Rbd(RESTController):
               'dest_pool_name': '{dest_pool_name}',
               'dest_image_name': '{dest_image_name}'}, 2.0)
     @RESTController.Resource('POST')
+    @allow_empty_body
     def copy(self, pool_name, image_name, dest_pool_name, dest_image_name,
              snapshot_name=None, obj_size=None, features=None, stripe_unit=None,
              stripe_count=None, data_pool=None, configuration=None):
@@ -336,6 +337,7 @@ class Rbd(RESTController):
     @RbdTask('flatten', ['{pool_name}', '{image_name}'], 2.0)
     @RESTController.Resource('POST')
     @UpdatePermission
+    @allow_empty_body
     def flatten(self, pool_name, image_name):
 
         def _flatten(ioctx, image):
@@ -350,6 +352,7 @@ class Rbd(RESTController):
 
     @RbdTask('trash/move', ['{pool_name}', '{image_name}'], 2.0)
     @RESTController.Resource('POST')
+    @allow_empty_body
     def move_trash(self, pool_name, image_name, delay=0):
         """Move an image to the trash.
         Images, even ones actively in-use by clones,
@@ -408,6 +411,7 @@ class RbdSnapshot(RESTController):
              ['{pool_name}', '{image_name}', '{snapshot_name}'], 5.0)
     @RESTController.Resource('POST')
     @UpdatePermission
+    @allow_empty_body
     def rollback(self, pool_name, image_name, snapshot_name):
         def _rollback(ioctx, img, snapshot_name):
             img.rollback_to_snap(snapshot_name)
@@ -420,6 +424,7 @@ class RbdSnapshot(RESTController):
               'child_pool_name': '{child_pool_name}',
               'child_image_name': '{child_image_name}'}, 2.0)
     @RESTController.Resource('POST')
+    @allow_empty_body
     def clone(self, pool_name, image_name, snapshot_name, child_pool_name,
               child_image_name, obj_size=None, features=None, stripe_unit=None, stripe_count=None,
               data_pool=None, configuration=None):
@@ -491,6 +496,7 @@ class RbdTrash(RESTController):
     @RbdTask('trash/purge', ['{pool_name}'], 2.0)
     @RESTController.Collection('POST', query_params=['pool_name'])
     @DeletePermission
+    @allow_empty_body
     def purge(self, pool_name=None):
         """Remove all expired images from trash."""
         now = "{}Z".format(datetime.utcnow().isoformat())
@@ -506,6 +512,7 @@ class RbdTrash(RESTController):
     @RbdTask('trash/restore', ['{pool_name}', '{image_id}', '{new_image_name}'], 2.0)
     @RESTController.Resource('POST')
     @CreatePermission
+    @allow_empty_body
     def restore(self, pool_name, image_id, new_image_name):
         """Restore an image from trash."""
         return _rbd_call(pool_name, self.rbd_inst.trash_restore, image_id, new_image_name)
index 2a53f368352c66d69851abf20bb0522b805910f0..2e7014d2e10e258ddfe464bed28ba79ec80c2bf9 100644 (file)
@@ -6,7 +6,7 @@ import json
 import cherrypy
 
 from . import ApiController, BaseController, RESTController, Endpoint, \
-    ReadPermission
+    ReadPermission, allow_empty_body
 from .. import logger
 from ..exceptions import DashboardException
 from ..rest_client import RequestException
@@ -149,6 +149,7 @@ class RgwBucket(RgwRESTController):
         result = self.proxy('GET', 'bucket', {'bucket': bucket})
         return self._append_bid(result)
 
+    @allow_empty_body
     def create(self, bucket, uid):
         try:
             rgw_client = RgwClient.instance(uid)
@@ -156,6 +157,7 @@ class RgwBucket(RgwRESTController):
         except RequestException as e:
             raise DashboardException(e, http_status_code=500, component='rgw')
 
+    @allow_empty_body
     def set(self, bucket, bucket_id, uid):
         result = self.proxy('PUT', 'bucket', {
             'bucket': RgwBucket.strip_tenant_from_bucket_name(bucket, uid),
@@ -231,6 +233,7 @@ class RgwUser(RgwRESTController):
                 emails.append(user["email"])
         return emails
 
+    @allow_empty_body
     def create(self, uid, display_name, email=None, max_buckets=None,
                suspended=None, generate_key=None, access_key=None,
                secret_key=None):
@@ -252,6 +255,7 @@ class RgwUser(RgwRESTController):
         result = self.proxy('PUT', 'user', params)
         return self._append_uid(result)
 
+    @allow_empty_body
     def set(self, uid, display_name=None, email=None, max_buckets=None,
             suspended=None):
         params = {'uid': uid}
@@ -281,6 +285,7 @@ class RgwUser(RgwRESTController):
 
     # pylint: disable=redefined-builtin
     @RESTController.Resource(method='POST', path='/capability', status=201)
+    @allow_empty_body
     def create_cap(self, uid, type, perm):
         return self.proxy('PUT', 'user?caps', {
             'uid': uid,
@@ -296,6 +301,7 @@ class RgwUser(RgwRESTController):
         })
 
     @RESTController.Resource(method='POST', path='/key', status=201)
+    @allow_empty_body
     def create_key(self, uid, key_type='s3', subuser=None, generate_key='true',
                    access_key=None, secret_key=None):
         params = {'uid': uid, 'key-type': key_type, 'generate-key': generate_key}
@@ -321,6 +327,7 @@ class RgwUser(RgwRESTController):
         return self.proxy('GET', 'user?quota', {'uid': uid})
 
     @RESTController.Resource(method='PUT', path='/quota')
+    @allow_empty_body
     def set_quota(self, uid, quota_type, enabled, max_size_kb, max_objects):
         return self.proxy('PUT', 'user?quota', {
             'uid': uid,
@@ -331,6 +338,7 @@ class RgwUser(RgwRESTController):
         }, json_response=False)
 
     @RESTController.Resource(method='POST', path='/subuser', status=201)
+    @allow_empty_body
     def create_subuser(self, uid, subuser, access, key_type='s3',
                        generate_secret='true', access_key=None,
                        secret_key=None):
index db7c903d1655768489384f963e988fdb60894dd7..132eea006542addcca8e773bec436d51a9cf5a1b 100644 (file)
@@ -143,7 +143,7 @@ class CherryPyConfig(object):
                 'application/javascript',
             ],
             'tools.json_in.on': True,
-            'tools.json_in.force': False,
+            'tools.json_in.force': True,
             'tools.plugin_hooks_filter_request.on': True,
         }