"""
An instance of this class represents an endpoint.
"""
+
def __init__(self, ctrl, func):
self.ctrl = ctrl
self.inst = None
"""
_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
import logging
import cherrypy
-from . import ApiController, RESTController, ControllerDoc, EndpointDoc
+from . import ApiController, RESTController, \
+ allow_empty_body, ControllerDoc, EndpointDoc
from .. import mgr
from ..exceptions import DashboardException
from ..services.auth import AuthManager, JwtManager
component='auth')
@RESTController.Collection('POST')
+ @allow_empty_body
def logout(self):
logger.debug('Logout successful')
token = JwtManager.get_token_from_header()
import cherrypy
import cephfs
-from . import ApiController, ControllerDoc, RESTController, \
- UiApiController, EndpointDoc
+from . import ApiController, ControllerDoc, RESTController, UiApiController, \
+ allow_empty_body, EndpointDoc
from .. import mgr
from ..exceptions import DashboardException
from ..security import Scope
return path
@RESTController.Resource('POST', path='/tree')
+ @allow_empty_body
def mk_tree(self, fs_id, path):
"""
Create a directory.
cfs.rm_dir(path)
@RESTController.Resource('PUT', path='/quota')
+ @allow_empty_body
def quota(self, fs_id, path, max_bytes=None, max_files=None):
"""
Set the quotas of the specified path.
return cfs.get_quotas(path)
@RESTController.Resource('POST', path='/snapshot')
+ @allow_empty_body
def snapshot(self, fs_id, path, name=None):
"""
Create a snapshot.
import logging
import cherrypy
-from . import Controller, BaseController, Endpoint, ENDPOINT_MAP
+from . import Controller, BaseController, Endpoint, ENDPOINT_MAP, \
+ allow_empty_body
from .. import mgr
from ..tools import str_to_bool
@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)
from mgr_util import merge_dicts
from orchestrator import HostSpec
from . import ApiController, RESTController, Task, Endpoint, ReadPermission, \
- UiApiController, BaseController, EndpointDoc, ControllerDoc
+ UiApiController, BaseController, allow_empty_body, ControllerDoc, EndpointDoc
from .orchestrator import raise_if_no_orchestrator
from .. import mgr
from ..exceptions import DashboardException
orch_client = OrchClient.instance()
self._check_orchestrator_host_op(orch_client, hostname, True)
orch_client.hosts.add(hostname)
+ create._cp_config = {'tools.json_in.force': False} # pylint: disable=W0212
@raise_if_no_orchestrator([OrchFeature.HOST_LIST, OrchFeature.HOST_DELETE])
@handle_orchestrator_error('host')
@host_task('delete', {'hostname': '{hostname}'})
+ @allow_empty_body
def delete(self, hostname): # pragma: no cover - requires realtime env
orch_client = OrchClient.instance()
self._check_orchestrator_host_op(orch_client, hostname, False)
# -*- coding: utf-8 -*-
from __future__ import absolute_import
-from . import ApiController, RESTController, ControllerDoc, EndpointDoc
+from . import ApiController, RESTController, \
+ allow_empty_body, ControllerDoc, EndpointDoc
from .. import mgr
from ..security import Scope
from ..services.ceph_service import CephService
@RESTController.Resource('POST')
@handle_send_command_error('mgr_modules')
+ @allow_empty_body
def enable(self, module_name):
"""
Enable the specified Ceph Mgr module.
@RESTController.Resource('POST')
@handle_send_command_error('mgr_modules')
+ @allow_empty_body
def disable(self, module_name):
"""
Disable the specified Ceph Mgr module.
from ceph.deployment.drive_group import DriveGroupSpec, DriveGroupValidationError
from mgr_util import get_most_recent_rate
-from . import ApiController, RESTController, Endpoint, Task, EndpointDoc, ControllerDoc
-from . import CreatePermission, ReadPermission, UpdatePermission, DeletePermission
+from . import ApiController, RESTController, Endpoint, Task
+from . import CreatePermission, ReadPermission, UpdatePermission, DeletePermission, \
+ allow_empty_body, ControllerDoc, EndpointDoc
from .orchestrator import raise_if_no_orchestrator
from .. import mgr
from ..exceptions import DashboardException
@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.
weight=float(weight))
@RESTController.Resource('POST')
+ @allow_empty_body
def mark_lost(self, svc_id):
"""
Note: osd must be marked `down` before marking lost.
component='osd', http_status_code=400, msg='Unknown method: {}'.format(method))
@RESTController.Resource('POST')
+ @allow_empty_body
def purge(self, svc_id):
"""
Note: osd must be marked `down` before removal.
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
import rbd
from . import ApiController, RESTController, Task, UpdatePermission, \
- DeletePermission, CreatePermission, EndpointDoc, ControllerDoc
+ DeletePermission, CreatePermission, allow_empty_body, ControllerDoc, EndpointDoc
from .. import mgr
from ..exceptions import DashboardException
from ..security import Scope
'dest_namespace': '{dest_namespace}',
'dest_image_name': '{dest_image_name}'}, 2.0)
@RESTController.Resource('POST')
+ @allow_empty_body
def copy(self, image_spec, dest_pool_name, dest_namespace, dest_image_name,
snapshot_name=None, obj_size=None, features=None,
stripe_unit=None, stripe_count=None, data_pool=None, configuration=None):
@RbdTask('flatten', ['{image_spec}'], 2.0)
@RESTController.Resource('POST')
@UpdatePermission
+ @allow_empty_body
def flatten(self, image_spec):
def _flatten(ioctx, image):
@RbdTask('trash/move', ['{image_spec}'], 2.0)
@RESTController.Resource('POST')
+ @allow_empty_body
def move_trash(self, image_spec, delay=0):
"""Move an image to the trash.
Images, even ones actively in-use by clones,
['{image_spec}', '{snapshot_name}'], 5.0)
@RESTController.Resource('POST')
@UpdatePermission
+ @allow_empty_body
def rollback(self, image_spec, snapshot_name):
def _rollback(ioctx, img, snapshot_name):
img.rollback_to_snap(snapshot_name)
'child_namespace': '{child_namespace}',
'child_image_name': '{child_image_name}'}, 2.0)
@RESTController.Resource('POST')
+ @allow_empty_body
def clone(self, image_spec, snapshot_name, child_pool_name,
child_image_name, child_namespace=None, obj_size=None, features=None,
stripe_unit=None, stripe_count=None, data_pool=None, configuration=None):
@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())
@RbdTask('trash/restore', ['{image_id_spec}', '{new_image_name}'], 2.0)
@RESTController.Resource('POST')
@CreatePermission
+ @allow_empty_body
def restore(self, image_id_spec, new_image_name):
"""Restore an image from trash."""
pool_name, namespace, image_id = parse_image_spec(image_id_spec)
import rbd
from . import ApiController, Endpoint, Task, BaseController, ReadPermission, \
- UpdatePermission, RESTController, EndpointDoc, ControllerDoc
+ UpdatePermission, RESTController, allow_empty_body, ControllerDoc, EndpointDoc
from .. import mgr
from ..security import Scope
@Endpoint(method='POST', path='token')
@handle_rbd_mirror_error()
@UpdatePermission
+ @allow_empty_body
def create_token(self, pool_name):
ioctx = mgr.rados.open_ioctx(pool_name)
token = rbd.RBD().mirror_peer_bootstrap_create(ioctx)
@Endpoint(method='POST', path='peer')
@handle_rbd_mirror_error()
@UpdatePermission
+ @allow_empty_body
def import_token(self, pool_name, direction, token):
ioctx = mgr.rados.open_ioctx(pool_name)
import cherrypy
from . import ApiController, BaseController, RESTController, Endpoint, \
- ReadPermission, ControllerDoc, EndpointDoc
+ ReadPermission, allow_empty_body, ControllerDoc, EndpointDoc
from ..exceptions import DashboardException
from ..rest_client import RequestException
from ..security import Scope, Permission
return self._append_bid(result)
+ @allow_empty_body
def create(self, bucket, uid, zonegroup=None, placement_target=None,
lock_enabled='false', lock_mode=None,
lock_retention_period_days=None,
except RequestException as e: # pragma: no cover - handling is too obvious
raise DashboardException(e, http_status_code=500, component='rgw')
+ @allow_empty_body
def set(self, bucket, bucket_id, uid, versioning_state=None,
mfa_delete=None, mfa_token_serial=None, mfa_token_pin=None,
lock_mode=None, lock_retention_period_days=None,
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):
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}
# 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,
})
@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}
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,
}, 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):
import cherrypy
-from . import BaseController, ApiController, RESTController, Endpoint, ControllerDoc, EndpointDoc
+from . import BaseController, ApiController, RESTController, Endpoint, \
+ allow_empty_body, ControllerDoc, EndpointDoc
from .. import mgr
from ..exceptions import DashboardException, UserAlreadyExists, \
UserDoesNotExist, PasswordPolicyException, PwdExpirationDateNotValid
class UserPasswordPolicy(RESTController):
@Endpoint('POST')
+ @allow_empty_body
def validate_password(self, password, username=None, old_password=None):
"""
Check if the password meets the password policy.
'application/javascript',
],
'tools.json_in.on': True,
- 'tools.json_in.force': False,
+ 'tools.json_in.force': True,
'tools.plugin_hooks_filter_request.on': True,
}