Added summary/description and example value for one endpoint from each component in Rest API Documentation
Fixes:https://tracker.ceph.com/issues/40767
Signed-off-by: Aashish Sharma <aasharma@redhat.com>
import logging
import cherrypy
-from . import ApiController, RESTController
+from . import ApiController, RESTController, ControllerDoc, EndpointDoc
from .. import mgr
from ..exceptions import DashboardException
from ..services.auth import AuthManager, JwtManager
logger = logging.getLogger('controllers.auth')
+AUTH_CHECK_SCHEMA = {
+ "username": (str, "Username"),
+ "permissions": ({
+ "cephfs": ([str], "")
+ }, "List of permissions acquired"),
+ "sso": (bool, "Uses single sign on?"),
+ "pwdUpdateRequired": (bool, "Is password update required?")
+}
+
@ApiController('/auth', secure=False)
+@ControllerDoc("Initiate a session with Ceph", "Auth")
class Auth(RESTController):
"""
Provide authenticates and returns JWT token.
"""
-
def create(self, username, password):
user_data = AuthManager.authenticate(username, password)
user_perms, pwd_expiration_date, pwd_update_required = None, None, None
return 'auth/saml2/login'
return '#/login'
- @RESTController.Collection('POST')
+ @RESTController.Collection('POST', query_params=['token'])
+ @EndpointDoc("Check token Authentication",
+ parameters={'token': (str, 'Authentication Token')},
+ responses={201: AUTH_CHECK_SCHEMA})
def check(self, token):
if token:
user = JwtManager.get_user(token)
import cherrypy
import cephfs
-from . import ApiController, ControllerDoc, RESTController, UiApiController
+from . import ApiController, ControllerDoc, RESTController, \
+ UiApiController, EndpointDoc
from .. import mgr
from ..exceptions import DashboardException
from ..security import Scope
from ..services.ceph_service import CephService
from ..tools import ViewCache
+GET_QUOTAS_SCHEMA = {
+ 'max_bytes': (int, ''),
+ 'max_files': (int, '')
+}
+
@ApiController('/cephfs', Scope.CEPHFS)
+@ControllerDoc("Cephfs Management API", "Cephfs")
class CephFS(RESTController):
def __init__(self): # pragma: no cover
super(CephFS, self).__init__()
cfs.rm_snapshot(path, name)
@RESTController.Resource('GET')
+ @EndpointDoc("Get Cephfs Quotas of the specified path",
+ parameters={
+ 'fs_id': (str, 'File System Identifier'),
+ 'path': (str, 'File System Path'),
+ },
+ responses={200: GET_QUOTAS_SCHEMA})
def get_quotas(self, fs_id, path):
"""
Get the quotas of the specified path.
import cherrypy
-from . import ApiController, RESTController
+from . import ApiController, RESTController, ControllerDoc, EndpointDoc
from .. import mgr
from ..security import Scope
from ..services.ceph_service import CephService
from ..exceptions import DashboardException
+FILTER_SCHEMA = [{
+ "name": (str, 'Name of the config option'),
+ "type": (str, 'Config option type'),
+ "level": (str, 'Config option level'),
+ "desc": (str, 'Description of the configuration'),
+ "long_desc": (str, 'Elaborated description'),
+ "default": (str, 'Default value for the config option'),
+ "daemon_default": (str, 'Daemon specific default value'),
+ "tags": ([str], 'Tags associated with the cluster'),
+ "services": ([str], 'Services associated with the config option'),
+ "see_also": ([str], 'Related config options'),
+ "enum_values": ([str], 'List of enums allowed'),
+ "min": (str, 'Minimum value'),
+ "max": (str, 'Maximum value'),
+ "can_update_at_runtime": (bool, 'Check if can update at runtime'),
+ "flags": ([str], 'List of flags associated')
+}]
+
+
@ApiController('/cluster_conf', Scope.CONFIG_OPT)
+@ControllerDoc("Manage Cluster Configurations", "ClusterConfiguration")
class ClusterConfiguration(RESTController):
def _append_config_option_values(self, options):
return self._get_config_option(name)
@RESTController.Collection('GET', query_params=['name'])
+ @EndpointDoc("Get Cluster Configuration by name",
+ parameters={
+ 'names': (str, 'Config option names'),
+ },
+ responses={200: FILTER_SCHEMA})
def filter(self, names=None):
config_options = []
from cherrypy import NotFound
from . import ApiController, ControllerDoc, RESTController, Endpoint, ReadPermission, \
- UiApiController
+ UiApiController, EndpointDoc
from ..security import Scope
from ..services.ceph_service import CephService
from .. import mgr
+LIST_SCHEMA = {
+ "rule_id": (int, 'Rule ID'),
+ "rule_name": (str, 'Rule Name'),
+ "ruleset": (int, 'RuleSet related to the rule'),
+ "type": (int, 'Type of Rule'),
+ "min_size": (int, 'Minimum size of Rule'),
+ "max_size": (int, 'Maximum size of Rule'),
+ 'steps': ([{str}], 'Steps included in the rule')
+}
+
+
@ApiController('/crush_rule', Scope.POOL)
+@ControllerDoc("Crush Rule Management API", "CrushRule")
class CrushRule(RESTController):
+ @EndpointDoc("List Crush Rule Configuration",
+ responses={200: LIST_SCHEMA})
def list(self):
return mgr.get('osd_map_crush')['rules']
@classmethod
def _gen_paths(cls, all_endpoints):
+ # pylint: disable=R0912
method_order = ['get', 'post', 'put', 'delete']
paths = {}
for path, endpoints in sorted(list(ENDPOINT_MAP.items()),
'application/json': {
'schema': cls._gen_schema_for_content(body_params)}}}
+ if endpoint.query_params:
+ query_params = cls._add_param_info(endpoint.query_params, p_info)
+ methods[method.lower()]['requestBody'] = {
+ 'content': {
+ 'application/json': {
+ 'schema': cls._gen_schema_for_content(query_params)}}}
+
if endpoint.is_secure:
methods[method.lower()]['security'] = [{'jwt': []}]
from cherrypy import NotFound
from . import ApiController, ControllerDoc, RESTController, Endpoint, ReadPermission, \
- UiApiController
+ UiApiController, EndpointDoc
from ..security import Scope
from ..services.ceph_service import CephService
from .. import mgr
+LIST_CODE__SCHEMA = {
+ "crush-failure-domain": (str, ''),
+ "k": (int, 'Number of data chunks'),
+ "m": (int, 'Number of coding chunks'),
+ "plugin": (str, 'Plugin Info'),
+ "technique": (str, ''),
+ "name": (str, 'Name of the profile')
+}
+
@ApiController('/erasure_code_profile', Scope.POOL)
+@ControllerDoc("Erasure Code Profile Management API", "ErasureCodeProfile")
class ErasureCodeProfile(RESTController):
"""
create() supports additional key-value arguments that are passed to the
ECP plugin.
"""
-
+ @EndpointDoc("List Erasure Code Profile Information",
+ responses={'200': [LIST_CODE__SCHEMA]})
def list(self):
return CephService.get_erasure_code_profiles()
from . import (ApiController, BaseController, Endpoint, ReadPermission,
UpdatePermission)
+from . import ControllerDoc, EndpointDoc
from ..exceptions import DashboardException
from ..grafana import GrafanaRestClient, push_local_dashboards
from ..security import Scope
from ..settings import Settings
+URL_SCHEMA = {
+ "instance": (str, "grafana instance")
+}
+
+
@ApiController('/grafana', Scope.GRAFANA)
+@ControllerDoc("Grafana Management API", "Grafana")
class Grafana(BaseController):
@Endpoint()
@ReadPermission
+ @EndpointDoc("List Grafana URL Instance",
+ responses={200: URL_SCHEMA})
def url(self):
response = {'instance': Settings.GRAFANA_API_URL}
return response
import json
-from . import ApiController, Endpoint, BaseController
+from . import ApiController, Endpoint, BaseController, ControllerDoc, EndpointDoc
from .. import mgr
from ..rest_client import RequestException
from ..tools import partial_dict
from .host import get_hosts
+HEALTH_MINIMAL_SCHEMA = ({
+ 'client_perf': ({
+ 'read_bytes_sec': (int, ''),
+ 'read_op_per_sec': (int, ''),
+ 'recovering_bytes_per_sec': (int, ''),
+ 'write_bytes_sec': (int, ''),
+ 'write_op_per_sec': (int, ''),
+ }, ''),
+ 'df': ({
+ 'stats': ({
+ 'total_avail_bytes': (int, ''),
+ 'total_bytes': (int, ''),
+ 'total_used_raw_bytes': (int, ''),
+ }, '')
+ }, ''),
+ 'fs_map': ({
+ 'filesystems': ([{
+ 'mdsmap': ({
+ 'session_autoclose': (int, ''),
+ 'balancer': (str, ''),
+ 'up': (str, ''),
+ 'last_failure_osd_epoch': (int, ''),
+ 'in': ([int], ''),
+ 'last_failure': (int, ''),
+ 'max_file_size': (int, ''),
+ 'explicitly_allowed_features': (int, ''),
+ 'damaged': ([int], ''),
+ 'tableserver': (int, ''),
+ 'failed': ([int], ''),
+ 'metadata_pool': (int, ''),
+ 'epoch': (int, ''),
+ 'stopped': ([int], ''),
+ 'max_mds': (int, ''),
+ 'compat': ({
+ 'compat': (str, ''),
+ 'ro_compat': (str, ''),
+ 'incompat': (str, ''),
+ }, ''),
+ 'required_client_features': (str, ''),
+ 'data_pools': ([int], ''),
+ 'info': (str, ''),
+ 'fs_name': (str, ''),
+ 'created': (str, ''),
+ 'standby_count_wanted': (int, ''),
+ 'enabled': (bool, ''),
+ 'modified': (str, ''),
+ 'session_timeout': (int, ''),
+ 'flags': (int, ''),
+ 'ever_allowed_features': (int, ''),
+ 'root': (int, ''),
+ }, ''),
+ 'standbys': (str, ''),
+ }], ''),
+ }, ''),
+ 'health': ({
+ 'checks': (str, ''),
+ 'mutes': (str, ''),
+ 'status': (str, ''),
+ }, ''),
+ 'hosts': (int, ''),
+ 'iscsi_daemons': ({
+ 'up': (int, ''),
+ 'down': (int, '')
+ }, ''),
+ 'mgr_map': ({
+ 'active_name': (str, ''),
+ 'standbys': (str, '')
+ }, ''),
+ 'mon_status': ({
+ 'monmap': ({
+ 'mons': (str, ''),
+ }, ''),
+ 'quorum': ([int], '')
+ }, ''),
+ 'osd_map': ({
+ 'osds': ([{
+ 'in': (int, ''),
+ 'up': (int, ''),
+ }], '')
+ }, ''),
+ 'pg_info': ({
+ 'object_stats': ({
+ 'num_objects': (int, ''),
+ 'num_object_copies': (int, ''),
+ 'num_objects_degraded': (int, ''),
+ 'num_objects_misplaced': (int, ''),
+ 'num_objects_unfound': (int, ''),
+ }, ''),
+ 'pgs_per_osd': (int, ''),
+ 'statuses': (str, '')
+ }, ''),
+ 'pools': (str, ''),
+ 'rgw': (int, ''),
+ 'scrub_status': (str, '')
+})
+
class HealthData(object):
"""
@ApiController('/health')
+@ControllerDoc("Display Detailed Cluster health Status", "Health")
class Health(BaseController):
def __init__(self):
super(Health, self).__init__()
return self.health_full.all_health()
@Endpoint()
+ @EndpointDoc("Get Cluster's minimal health report",
+ responses={200: HEALTH_MINIMAL_SCHEMA})
def minimal(self):
return self.health_minimal.all_health()
from mgr_util import merge_dicts
from orchestrator import HostSpec
from . import ApiController, RESTController, Task, Endpoint, ReadPermission, \
- UiApiController, BaseController
+ UiApiController, BaseController, EndpointDoc, ControllerDoc
from .orchestrator import raise_if_no_orchestrator
from .. import mgr
from ..exceptions import DashboardException
from ..services.ceph_service import CephService
from ..services.exception import handle_orchestrator_error
+LIST_HOST_SCHEMA = {
+ "hostname": (str, "Hostname"),
+ "services": ([{
+ "type": (str, "type of service"),
+ "id": (str, "Service Id"),
+ }], "Services related to the host"),
+ "ceph_version": (str, "Ceph version"),
+ "addr": (str, "Host address"),
+ "labels": ([str], "Labels related to the host"),
+ "service_type": (str, ""),
+ "sources": ({
+ "ceph": (bool, ""),
+ "orchestrator": (bool, "")
+ }, "Host Sources"),
+ "status": (str, "")
+}
+
def host_task(name, metadata, wait_for=10.0):
return Task("host/{}".format(name), metadata, wait_for)
@ApiController('/host', Scope.HOSTS)
+@ControllerDoc("Get Host Details", "Host")
class Host(RESTController):
+ @EndpointDoc("List Host Specifications",
+ parameters={
+ 'sources': (str, 'Host Sources'),
+ },
+ responses={200: LIST_HOST_SCHEMA})
def list(self, sources=None):
if sources is None:
return get_hosts()
# -*- coding: utf-8 -*-
+# pylint: disable=C0302
# pylint: disable=too-many-branches
# pylint: disable=too-many-lines
from __future__ import absolute_import
import rbd
from . import ApiController, UiApiController, RESTController, BaseController, Endpoint,\
- ReadPermission, UpdatePermission, Task
+ ReadPermission, UpdatePermission, Task, ControllerDoc, EndpointDoc
from .. import mgr
from ..rest_client import RequestException
from ..security import Scope
except ImportError:
no_type_check = object() # Just for type checking
+ISCSI_SCHEMA = {
+ 'user': (str, 'username'),
+ 'password': (str, 'password'),
+ 'mutual_user': (str, ''),
+ 'mutual_password': (str, '')
+}
+
@UiApiController('/iscsi', Scope.ISCSI)
class IscsiUi(BaseController):
@ApiController('/iscsi', Scope.ISCSI)
+@ControllerDoc("Iscsi Management API", "Iscsi")
class Iscsi(BaseController):
-
@Endpoint('GET', 'discoveryauth')
@ReadPermission
+ @EndpointDoc("Get Iscsi discoveryauth Details",
+ responses={'200': [ISCSI_SCHEMA]})
def get_discoveryauth(self):
gateway = get_available_gateway()
return self._get_discoveryauth(gateway)
- @Endpoint('PUT', 'discoveryauth')
+ @Endpoint('PUT', 'discoveryauth',
+ query_params=['user', 'password', 'mutual_user', 'mutual_password'])
@UpdatePermission
+ @EndpointDoc("Set Iscsi discoveryauth",
+ parameters={
+ 'user': (str, 'Username'),
+ 'password': (str, 'Password'),
+ 'mutual_user': (str, 'Mutual UserName'),
+ 'mutual_password': (str, 'Mutual Password'),
+ })
def set_discoveryauth(self, user, password, mutual_user, mutual_password):
validate_auth({
'user': user,
@ApiController('/iscsi/target', Scope.ISCSI)
+@ControllerDoc("Get Iscsi Target Details", "IscsiTarget")
class IscsiTarget(RESTController):
def list(self):
import collections
-from . import ApiController, Endpoint, BaseController, ReadPermission
+from . import ApiController, Endpoint, BaseController, ReadPermission, ControllerDoc, EndpointDoc
from ..security import Scope
from ..services.ceph_service import CephService
from ..tools import NotificationQueue
LOG_BUFFER_SIZE = 30
+LOGS_SCHEMA = {
+ "clog": ([str], ""),
+ "audit_log": ([{
+ "name": (str, ""),
+ "rank": (str, ""),
+ "addrs": ({
+ "addrvec": ([{
+ "type": (str, ""),
+ "addr": (str, "IP Address"),
+ "nonce": (int, ""),
+ }], ""),
+ }, ""),
+ "stamp": (str, ""),
+ "seq": (int, ""),
+ "channel": (str, ""),
+ "priority": (str, ""),
+ "message": (str, ""),
+ }], "Audit log")
+}
+
@ApiController('/logs', Scope.LOG)
+@ControllerDoc("Logs Management API", "Logs")
class Logs(BaseController):
def __init__(self):
super(Logs, self).__init__()
@Endpoint()
@ReadPermission
+ @EndpointDoc("Display Logs Configuration",
+ responses={200: LOGS_SCHEMA})
def all(self):
self.initialize_buffers()
return dict(
# -*- coding: utf-8 -*-
from __future__ import absolute_import
-from . import ApiController, RESTController
+from . import ApiController, RESTController, ControllerDoc, EndpointDoc
from .. import mgr
from ..security import Scope
from ..services.ceph_service import CephService
from ..services.exception import handle_send_command_error
from ..tools import find_object_in_list, str_to_bool
+MGR_MODULE_SCHEMA = ([{
+ "name": (str, "Module Name"),
+ "enabled": (bool, "Is Module Enabled"),
+ "always_on": (bool, "Is it an always on module?"),
+ "options": ({
+ "Option_name": ({
+ "name": (str, "Name of the option"),
+ "type": (str, "Type of the option"),
+ "level": (str, "Option level"),
+ "flags": (int, "List of flags associated"),
+ "default_value": (int, "Default value for the option"),
+ "min": (str, "Minimum value"),
+ "max": (str, "Maximum value"),
+ "enum_allowed": ([str], ""),
+ "desc": (str, "Description of the option"),
+ "long_desc": (str, "Elaborated description"),
+ "tags": ([str], "Tags associated with the option"),
+ "see_also": ([str], "Related options")
+ }, "Options")
+ }, "Module Options")
+}])
+
@ApiController('/mgr/module', Scope.CONFIG_OPT)
+@ControllerDoc("Get details of MGR Module", "MgrModule")
class MgrModules(RESTController):
ignore_modules = ['selftest']
-
+ @EndpointDoc("List Mgr modules",
+ responses={200: MGR_MODULE_SCHEMA})
def list(self):
"""
Get the list of managed modules.
import json
-from . import ApiController, Endpoint, BaseController, ReadPermission
+from . import ApiController, Endpoint, BaseController, ReadPermission, ControllerDoc, EndpointDoc
from .. import mgr
from ..security import Scope
+MONITOR_SCHEMA = {
+ "mon_status": ({
+ "name": (str, ""),
+ "rank": (int, ""),
+ "state": (str, ""),
+ "election_epoch": (int, ""),
+ "quorum": ([int], ""),
+ "quorum_age": (int, ""),
+ "features": ({
+ "required_con": (str, ""),
+ "required_mon": ([int], ""),
+ "quorum_con": (str, ""),
+ "quorum_mon": ([str], "")
+ }, ""),
+ "outside_quorum": ([str], ""),
+ "extra_probe_peers": ([str], ""),
+ "sync_provider": ([str], ""),
+ "monmap": ({
+ "epoch": (int, ""),
+ "fsid": (str, ""),
+ "modified": (str, ""),
+ "created": (str, ""),
+ "min_mon_release": (int, ""),
+ "min_mon_release_name": (str, ""),
+ "features": ({
+ "persistent": ([str], ""),
+ "optional": ([str], "")
+ }, ""),
+ "mons": ([{
+ "rank": (int, ""),
+ "name": (str, ""),
+ "public_addrs": ({
+ "addrvec": ([{
+ "type": (str, ""),
+ "addr": (str, ""),
+ "nonce": (int, "")
+ }], "")
+ }, ""),
+ "addr": (str, ""),
+ "public_addr": (str, ""),
+ "priority": (int, ""),
+ "weight": (int, ""),
+ "stats": ({
+ "num_sessions": ([int], ""),
+ }, "")
+ }], "")
+ }, ""),
+ "feature_map": ({
+ "mon": ([{
+ "features": (str, ""),
+ "release": (str, ""),
+ "num": (int, "")
+ }], ""),
+ "mds": ([{
+ "features": (str, ""),
+ "release": (str, ""),
+ "num": (int, "")
+ }], ""),
+ "client": ([{
+ "features": (str, ""),
+ "release": (str, ""),
+ "num": (int, "")
+ }], ""),
+ "mgr": ([{
+ "features": (str, ""),
+ "release": (str, ""),
+ "num": (int, "")
+ }], ""),
+ }, "")
+ }, ""),
+ "in_quorum": ([{
+ "rank": (int, ""),
+ "name": (str, ""),
+ "public_addrs": ({
+ "addrvec": ([{
+ "type": (str, ""),
+ "addr": (str, ""),
+ "nonce": (int, "")
+ }], "")
+ }, ""),
+ "addr": (str, ""),
+ "public_addr": (str, ""),
+ "priority": (int, ""),
+ "weight": (int, ""),
+ "stats": ({
+ "num_sessions": ([int], "")
+ }, "")
+ }], ""),
+ "out_quorum": ([int], "")
+}
+
@ApiController('/monitor', Scope.MONITOR)
+@ControllerDoc("Get Monitor Details", "Monitor")
class Monitor(BaseController):
@Endpoint()
@ReadPermission
+ @EndpointDoc("Get Monitor Details",
+ responses={200: MONITOR_SCHEMA})
def __call__(self):
in_quorum, out_quorum = [], []
import time
from functools import wraps
-from . import ApiController, Endpoint, ReadPermission, UpdatePermission
+from . import ApiController, Endpoint, ReadPermission, UpdatePermission, ControllerDoc, EndpointDoc
from . import RESTController, Task
from .. import mgr
from ..exceptions import DashboardException
from ..services.orchestrator import OrchClient
from ..tools import TaskManager
+STATUS_SCHEMA = {
+ "available": (bool, "Orchestrator status"),
+ "description": (str, "Description")
+}
+
def get_device_osd_map():
"""Get mappings from inventory devices to OSD IDs.
@ApiController('/orchestrator')
+@ControllerDoc("Orchestrator Management API", "Orchestrator")
class Orchestrator(RESTController):
@Endpoint()
@ReadPermission
+ @EndpointDoc("Display Orchestrator Status",
+ responses={200: STATUS_SCHEMA})
def status(self):
return OrchClient.instance().status()
@ApiController('/orchestrator/inventory', Scope.HOSTS)
+@ControllerDoc("Get Orchestrator Inventory Details", "OrchestratorInventory")
class OrchestratorInventory(RESTController):
@raise_if_no_orchestrator
from ceph.deployment.drive_group import DriveGroupSpec, DriveGroupValidationError
from mgr_util import get_most_recent_rate
-from . import ApiController, RESTController, Endpoint, Task
+from . import ApiController, RESTController, Endpoint, Task, EndpointDoc, ControllerDoc
from . import CreatePermission, ReadPermission, UpdatePermission, DeletePermission
from .orchestrator import raise_if_no_orchestrator
from .. import mgr
logger = logging.getLogger('controllers.osd')
+SAFE_TO_DESTROY_SCHEMA = {
+ "safe_to_destroy": ([str], "Is OSD safe to destroy?"),
+ "active": ([int], ""),
+ "missing_stats": ([str], ""),
+ "stored_pgs": ([str], "Stored Pool groups in Osd"),
+ "is_safe_to_destroy": (bool, "Is OSD safe to destroy?")
+}
+
+EXPORT_FLAGS_SCHEMA = {
+ "list_of_flags": ([str], "")
+}
+
def osd_task(name, metadata, wait_for=2.0):
return Task("osd/{}".format(name), metadata, wait_for)
@ApiController('/osd', Scope.OSD)
+@ControllerDoc("Get OSD Details", "OSD")
class Osd(RESTController):
def list(self):
osds = self.get_osd_map()
@Endpoint('GET', query_params=['ids'])
@ReadPermission
+ @EndpointDoc("Check If OSD is Safe to Destroy",
+ parameters={
+ 'ids': (str, 'OSD Service Identifier'),
+ },
+ responses={200: SAFE_TO_DESTROY_SCHEMA})
def safe_to_destroy(self, ids):
"""
:type ids: int|[int]
@ApiController('/osd/flags', Scope.OSD)
+@ControllerDoc("OSD Flags controller Management API", "OsdFlagsController")
class OsdFlagsController(RESTController):
@staticmethod
def _osd_flags():
set(enabled_flags) - {'pauserd', 'pausewr'} | {'pause'})
return sorted(enabled_flags)
+ @EndpointDoc("Display OSD Flags",
+ responses={200: EXPORT_FLAGS_SCHEMA})
def list(self):
return self._osd_flags()
import cherrypy
-from . import ApiController, RESTController
+from . import ApiController, RESTController, EndpointDoc, ControllerDoc
from .. import mgr
from ..security import Scope
from ..services.ceph_service import CephService
+PERF_SCHEMA = {
+ "mon.a": ({
+ ".cache_bytes": ({
+ "description": (str, ""),
+ "nick": (str, ""),
+ "type": (int, ""),
+ "priority": (int, ""),
+ "units": (int, ""),
+ "value": (int, "")
+ }, ""),
+ }, "Service ID"),
+}
+
class PerfCounter(RESTController):
service_type = None # type: str
@ApiController('perf_counters/mds', Scope.CEPHFS)
+@ControllerDoc("Mds Perf Counters Management API", "MdsPerfCounter")
class MdsPerfCounter(PerfCounter):
service_type = 'mds'
@ApiController('perf_counters/mon', Scope.MONITOR)
+@ControllerDoc("Mon Perf Counters Management API", "MonPerfCounter")
class MonPerfCounter(PerfCounter):
service_type = 'mon'
@ApiController('perf_counters/osd', Scope.OSD)
+@ControllerDoc("OSD Perf Counters Management API", "OsdPerfCounter")
class OsdPerfCounter(PerfCounter):
service_type = 'osd'
@ApiController('perf_counters/rgw', Scope.RGW)
+@ControllerDoc("Rgw Perf Counters Management API", "RgwPerfCounter")
class RgwPerfCounter(PerfCounter):
service_type = 'rgw'
@ApiController('perf_counters/rbd-mirror', Scope.RBD_MIRRORING)
+@ControllerDoc("Rgw Mirroring Perf Counters Management API", "RgwMirrorPerfCounter")
class RbdMirrorPerfCounter(PerfCounter):
service_type = 'rbd-mirror'
@ApiController('perf_counters/mgr', Scope.MANAGER)
+@ControllerDoc("Mgr Perf Counters Management API", "MgrPerfCounter")
class MgrPerfCounter(PerfCounter):
service_type = 'mgr'
@ApiController('perf_counters/tcmu-runner', Scope.ISCSI)
+@ControllerDoc("Tcmu Runner Perf Counters Management API", "TcmuRunnerPerfCounter")
class TcmuRunnerPerfCounter(PerfCounter):
service_type = 'tcmu-runner'
@ApiController('perf_counters')
+@ControllerDoc("Perf Counters Management API", "PerfCounters")
class PerfCounters(RESTController):
+ @EndpointDoc("Display Perf Counters",
+ responses={200: PERF_SCHEMA})
def list(self):
return mgr.get_all_perf_counters()
import cherrypy
from . import ApiController, ControllerDoc, RESTController, Endpoint, ReadPermission, Task, \
- UiApiController
+ UiApiController, EndpointDoc
from .. import mgr
from ..security import Scope
from ..services.ceph_service import CephService
from ..services.exception import handle_send_command_error
from ..tools import str_to_bool, TaskManager
+POOL_SCHEMA = ([{
+ "pool": (int, "pool id"),
+ "pool_name": (str, "pool name"),
+ "flags": (int, ""),
+ "flags_names": (str, "flags name"),
+ "type": (str, "type of pool"),
+ "size": (int, "pool size"),
+ "min_size": (int, ""),
+ "crush_rule": (str, ""),
+ "object_hash": (int, ""),
+ "pg_autoscale_mode": (str, ""),
+ "pg_num": (int, ""),
+ "pg_placement_num": (int, ""),
+ "pg_placement_num_target": (int, ""),
+ "pg_num_target": (int, ""),
+ "pg_num_pending": (int, ""),
+ "last_pg_merge_meta": ({
+ "ready_epoch": (int, ""),
+ "last_epoch_started": (int, ""),
+ "last_epoch_clean": (int, ""),
+ "source_pgid": (str, ""),
+ "source_version": (str, ""),
+ "target_version": (str, ""),
+ }, ""),
+ "auid": (int, ""),
+ "snap_mode": (str, ""),
+ "snap_seq": (int, ""),
+ "snap_epoch": (int, ""),
+ "pool_snaps": ([str], ""),
+ "quota_max_bytes": (int, ""),
+ "quota_max_objects": (int, ""),
+ "tiers": ([str], ""),
+ "tier_of": (int, ""),
+ "read_tier": (int, ""),
+ "write_tier": (int, ""),
+ "cache_mode": (str, ""),
+ "target_max_bytes": (int, ""),
+ "target_max_objects": (int, ""),
+ "cache_target_dirty_ratio_micro": (int, ""),
+ "cache_target_dirty_high_ratio_micro": (int, ""),
+ "cache_target_full_ratio_micro": (int, ""),
+ "cache_min_flush_age": (int, ""),
+ "cache_min_evict_age": (int, ""),
+ "erasure_code_profile": (str, ""),
+ "hit_set_params": ({
+ "type": (str, "")
+ }, ""),
+ "hit_set_period": (int, ""),
+ "hit_set_count": (int, ""),
+ "use_gmt_hitset": (bool, ""),
+ "min_read_recency_for_promote": (int, ""),
+ "min_write_recency_for_promote": (int, ""),
+ "hit_set_grade_decay_rate": (int, ""),
+ "hit_set_search_last_n": (int, ""),
+ "grade_table": ([str], ""),
+ "stripe_width": (int, ""),
+ "expected_num_objects": (int, ""),
+ "fast_read": (bool, ""),
+ "options": ({
+ "pg_num_min": (int, "")
+ }, ""),
+ "application_metadata": ([str], ""),
+ "create_time": (str, ""),
+ "last_change": (str, ""),
+ "last_force_op_resend": (str, ""),
+ "last_force_op_resend_prenautilus": (str, ""),
+ "last_force_op_resend_preluminous": (str, ""),
+ "removed_snaps": ([str], "")
+}])
+
def pool_task(name, metadata, wait_for=2.0):
return Task("pool/{}".format(name), metadata, wait_for)
@ApiController('/pool', Scope.POOL)
+@ControllerDoc("Get pool details by pool name", "Pool")
class Pool(RESTController):
@staticmethod
return [cls._serialize_pool(pool, attrs) for pool in pools]
+ @EndpointDoc("Display Pool List",
+ parameters={
+ 'attrs': (str, 'Pool Attributes'),
+ 'stats': (bool, 'Pool Stats')
+ },
+ responses={200: POOL_SCHEMA})
def list(self, attrs=None, stats=False):
return self._pool_list(attrs, stats)
import json
import requests
-from . import Controller, ApiController, BaseController, RESTController, Endpoint
+from . import Controller, ApiController, BaseController, RESTController, Endpoint, ControllerDoc
from ..security import Scope
from ..settings import Settings
from ..exceptions import DashboardException
@ApiController('/prometheus', Scope.PROMETHEUS)
+@ControllerDoc("Prometheus Management API", "Prometheus")
class Prometheus(PrometheusRESTController):
def list(self, **params):
return self.alert_proxy('GET', '/alerts', params)
@ApiController('/prometheus/notifications', Scope.PROMETHEUS)
+@ControllerDoc("Prometheus Notifications Management API", "PrometheusNotifications")
class PrometheusNotifications(RESTController):
def list(self, **params):
import rbd
from . import ApiController, RESTController, Task, UpdatePermission, \
- DeletePermission, CreatePermission
+ DeletePermission, CreatePermission, EndpointDoc, ControllerDoc
from .. import mgr
from ..exceptions import DashboardException
from ..security import Scope
logger = logging.getLogger(__name__)
+RBD_SCHEMA = ([{
+ "status": (int, 'Status of the image'),
+ "value": ([str], ''),
+ "pool_name": (str, 'pool name')
+}])
+
+RBD_TRASH_SCHEMA = [{
+ "status": (int, ''),
+ "value": ([str], ''),
+ "pool_name": (str, 'pool name')
+}]
+
# pylint: disable=not-callable
def RbdTask(name, metadata, wait_for): # noqa: N802
@ApiController('/block/image', Scope.RBD_IMAGE)
+@ControllerDoc("RBD Management API", "Rbd")
class Rbd(RESTController):
# set of image features that can be enable on existing images
@handle_rbd_error()
@handle_rados_error('pool')
+ @EndpointDoc("Display Rbd Images",
+ parameters={
+ 'pool_name': (str, 'Pool Name'),
+ },
+ responses={200: RBD_SCHEMA})
def list(self, pool_name=None):
return self._rbd_list(pool_name)
@ApiController('/block/image/{image_spec}/snap', Scope.RBD_IMAGE)
+@ControllerDoc("RBD Snapshot Management API", "RbdSnapshot")
class RbdSnapshot(RESTController):
RESOURCE_ID = "snapshot_name"
@ApiController('/block/image/trash', Scope.RBD_IMAGE)
+@ControllerDoc("RBD Trash Management API", "RbdTrash")
class RbdTrash(RESTController):
RESOURCE_ID = "image_id_spec"
rbd_inst = rbd.RBD()
@handle_rbd_error()
@handle_rados_error('pool')
+ @EndpointDoc("Get RBD Trash Details by pool name",
+ parameters={
+ 'pool_name': (str, 'Name of the pool'),
+ },
+ responses={200: RBD_TRASH_SCHEMA})
def list(self, pool_name=None):
"""List all entries from trash."""
return self._trash_list(pool_name)
@ApiController('/block/pool/{pool_name}/namespace', Scope.RBD_IMAGE)
+@ControllerDoc("RBD Namespace Management API", "RbdNamespace")
class RbdNamespace(RESTController):
rbd_inst = rbd.RBD()
import rbd
from . import ApiController, Endpoint, Task, BaseController, ReadPermission, \
- UpdatePermission, RESTController
+ UpdatePermission, RESTController, EndpointDoc, ControllerDoc
from .. import mgr
from ..security import Scope
_get_content_data.reset()
+RBD_MIRROR_SCHEMA = {
+ "site_name": (str, "Site Name")
+}
+
+RBDM_POOL_SCHEMA = {
+ "mirror_mode": (str, "Mirror Mode")
+}
+
+RBDM_SUMMARY_SCHEMA = {
+ "site_name": (str, "site name"),
+ "status": (int, ""),
+ "content_data": ({
+ "daemons": ([str], ""),
+ "pools": ([{
+ "name": (str, "Pool name"),
+ "health_color": (str, ""),
+ "health": (str, "pool health"),
+ "mirror_mode": (str, "status"),
+ "peer_uuids": ([str], "")
+ }], "Pools"),
+ "image_error": ([str], ""),
+ "image_syncing": ([str], ""),
+ "image_ready": ([str], "")
+ }, "")
+}
+
+
@ApiController('/block/mirroring', Scope.RBD_MIRRORING)
+@ControllerDoc("RBD Mirroring Management API", "RbdMirroring")
class RbdMirroring(BaseController):
@Endpoint(method='GET', path='site_name')
@handle_rbd_mirror_error()
@ReadPermission
+ @EndpointDoc("Display Rbd Mirroring sitename",
+ responses={200: RBD_MIRROR_SCHEMA})
def get(self):
return self._get_site_name()
@ApiController('/block/mirroring/summary', Scope.RBD_MIRRORING)
+@ControllerDoc("RBD Mirroring Summary Management API", "RbdMirroringSummary")
class RbdMirroringSummary(BaseController):
@Endpoint()
@handle_rbd_mirror_error()
@ReadPermission
+ @EndpointDoc("Display Rbd Mirroring Summary",
+ responses={200: RBDM_SUMMARY_SCHEMA})
def __call__(self):
site_name = rbd.RBD().mirror_site_name_get(mgr.rados)
@ApiController('/block/mirroring/pool', Scope.RBD_MIRRORING)
+@ControllerDoc("RBD Mirroring Pool Mode Management API", "RbdMirroringPoolMode")
class RbdMirroringPoolMode(RESTController):
RESOURCE_ID = "pool_name"
}
@handle_rbd_mirror_error()
+ @EndpointDoc("Display Rbd Mirroring Summary",
+ parameters={
+ 'pool_name': (str, 'Pool Name'),
+ },
+ responses={200: RBDM_POOL_SCHEMA})
def get(self, pool_name):
ioctx = mgr.rados.open_ioctx(pool_name)
mode = rbd.RBD().mirror_mode_get(ioctx)
@ApiController('/block/mirroring/pool/{pool_name}/bootstrap',
Scope.RBD_MIRRORING)
+@ControllerDoc("RBD Mirroring Pool Bootstrap Management API", "RbdMirroringPoolBootstrap")
class RbdMirroringPoolBootstrap(BaseController):
@Endpoint(method='POST', path='token')
@ApiController('/block/mirroring/pool/{pool_name}/peer', Scope.RBD_MIRRORING)
+@ControllerDoc("RBD Mirroring Pool Peer Management API", "RbdMirroringPoolPeer")
class RbdMirroringPoolPeer(RESTController):
RESOURCE_ID = "peer_uuid"
import cherrypy
from . import ApiController, BaseController, RESTController, Endpoint, \
- ReadPermission
+ ReadPermission, ControllerDoc, EndpointDoc
from ..exceptions import DashboardException
from ..rest_client import RequestException
from ..security import Scope, Permission
except ImportError: # pragma: no cover
pass # Just for type checking
-logger = logging.getLogger('controllers.rgw')
+logger = logging.getLogger("controllers.rgw")
+
+RGW_SCHEMA = {
+ "available": (bool, "Is RGW available?"),
+ "message": (str, "Descriptions")
+}
+
+RGW_DAEMON_SCHEMA = {
+ "id": (str, "Daemon ID"),
+ "version": (str, "Ceph Version"),
+ "server_hostname": (str, "")
+}
+
+RGW_USER_SCHEMA = {
+ "list_of_users": ([str], "list of rgw users")
+}
@ApiController('/rgw', Scope.RGW)
+@ControllerDoc("RGW Management API", "Rgw")
class Rgw(BaseController):
@Endpoint()
@ReadPermission
+ @EndpointDoc("Display RGW Status",
+ responses={200: RGW_SCHEMA})
def status(self):
status = {'available': False, 'message': None}
try:
@ApiController('/rgw/daemon', Scope.RGW)
+@ControllerDoc("RGW Daemon Management API", "RgwDaemon")
class RgwDaemon(RESTController):
+ @EndpointDoc("Display RGW Daemons",
+ responses={200: RGW_DAEMON_SCHEMA})
def list(self):
# type: () -> List[dict]
daemons = []
@ApiController('/rgw/site', Scope.RGW)
+@ControllerDoc("RGW Site Management API", "RgwSite")
class RgwSite(RgwRESTController):
def list(self, query=None):
if query == 'placement-targets':
@ApiController('/rgw/bucket', Scope.RGW)
+@ControllerDoc("RGW Bucket Management API", "RgwBucket")
class RgwBucket(RgwRESTController):
def _append_bid(self, bucket):
"""
@ApiController('/rgw/user', Scope.RGW)
+@ControllerDoc("RGW User Management API", "RgwUser")
class RgwUser(RgwRESTController):
def _append_uid(self, user):
"""
return Scope.RGW in permissions and Permission.READ in permissions[Scope.RGW] \
and len(set(edit_permissions).intersection(set(permissions[Scope.RGW]))) > 0
+ @EndpointDoc("Display RGW Users",
+ responses={200: RGW_USER_SCHEMA})
def list(self):
# type: () -> List[str]
users = [] # type: List[str]
import cherrypy
from . import ApiController, RESTController, UiApiController,\
- CreatePermission
+ CreatePermission, ControllerDoc, EndpointDoc
from .. import mgr
from ..exceptions import RoleDoesNotExist, DashboardException,\
RoleIsAssociatedWithUser, RoleAlreadyExists
from ..security import Scope as SecurityScope, Permission
from ..services.access_control import SYSTEM_ROLES
+ROLE_SCHEMA = [{
+ "name": (str, "Role Name"),
+ "description": (str, "Role Descriptions"),
+ "scopes_permissions": ({
+ "cephfs": ([str], "")
+ }, ""),
+ "system": (bool, "")
+}]
+
@ApiController('/role', SecurityScope.USER)
+@ControllerDoc("Role Management API", "Role")
class Role(RESTController):
@staticmethod
def _role_to_dict(role):
if permissions:
role.set_scope_permissions(scope, permissions)
+ @EndpointDoc("Display Role list",
+ responses={200: ROLE_SCHEMA})
def list(self):
# type: () -> list
roles = dict(mgr.ACCESS_CTRL_DB.roles)
import cherrypy
from ceph.deployment.service_spec import ServiceSpec
-from . import ApiController, RESTController, Task, Endpoint, ReadPermission
-from . import CreatePermission, DeletePermission
+from . import ApiController, ControllerDoc, RESTController, Task, Endpoint, ReadPermission, \
+ CreatePermission, DeletePermission
from .orchestrator import raise_if_no_orchestrator
from ..exceptions import DashboardException
from ..security import Scope
@ApiController('/service', Scope.HOSTS)
+@ControllerDoc("Service Management API", "Service")
class Service(RESTController):
@Endpoint()
import cherrypy
-from . import ApiController, RESTController, UiApiController
+from . import ApiController, RESTController, UiApiController, ControllerDoc, EndpointDoc
from ..settings import Settings as SettingsModule, Options
from ..security import Scope
+SETTINGS_SCHEMA = [{
+ "name": (str, 'Settings Name'),
+ "default": (bool, 'Default Settings'),
+ "type": (str, 'Type of Settings'),
+ "value": (bool, 'Settings Value')
+}]
+
@ApiController('/settings', Scope.CONFIG_OPT)
+@ControllerDoc("Settings Management API", "Settings")
class Settings(RESTController):
"""
Enables to manage the settings of the dashboard (not the Ceph cluster).
def _to_native(setting):
return setting.upper().replace('-', '_')
+ @EndpointDoc("Display Settings Information",
+ parameters={
+ 'names': (str, 'Name of Settings'),
+ },
+ responses={200: SETTINGS_SCHEMA})
def list(self, names=None):
"""
Get the list of available options.
import json
-from . import ApiController, Endpoint, BaseController
+from . import ApiController, Endpoint, BaseController, ControllerDoc, EndpointDoc
from .. import mgr
from ..security import Permission, Scope
from ..controllers.rbd_mirroring import get_daemons_and_pools
from ..tools import TaskManager
from ..services import progress
+SUMMARY_SCHEMA = {
+ "health_status": (str, ""),
+ "mgr_id": (str, ""),
+ "mgr_host": (str, ""),
+ "have_mon_connection": (str, ""),
+ "executing_tasks": ([str], ""),
+ "finished_tasks": ([{
+ "name": (str, ""),
+ "metadata": ({
+ "pool": (int, ""),
+ }, ""),
+ "begin_time": (str, ""),
+ "end_time": (str, ""),
+ "duration": (int, ""),
+ "progress": (int, ""),
+ "success": (bool, ""),
+ "ret_value": (str, ""),
+ "exception": (str, ""),
+ }], ""),
+ "version": (str, ""),
+ "rbd_mirroring": ({
+ "warnings": (int, ""),
+ "errors": (int, "")
+ }, "")
+}
+
@ApiController('/summary')
+@ControllerDoc("Get Ceph Summary Details", "Summary")
class Summary(BaseController):
def _health_status(self):
health_data = mgr.get("health")
return services['dashboard'] if 'dashboard' in services else ''
@Endpoint()
+ @EndpointDoc("Display Summary",
+ responses={200: SUMMARY_SCHEMA})
def __call__(self):
exe_t, fin_t = TaskManager.list_serializable()
executing_tasks = [task for task in exe_t if self._task_permissions(task['name'])]
# -*- coding: utf-8 -*-
from __future__ import absolute_import
-from . import ApiController, RESTController
+from . import ApiController, RESTController, ControllerDoc, EndpointDoc
from ..tools import TaskManager
from ..services import progress
+TASK_SCHEMA = {
+ "executing_tasks": (str, "ongoing executing tasks"),
+ "finished_tasks": ([{
+ "name": (str, "finished tasks name"),
+ "metadata": ({
+ "pool": (int, "")
+ }, ""),
+ "begin_time": (str, "Task begin time"),
+ "end_time": (str, "Task end time"),
+ "duration": (int, ""),
+ "progress": (int, "Progress of tasks"),
+ "success": (bool, ""),
+ "ret_value": (bool, ""),
+ "exception": (bool, "")
+ }], "")
+}
+
@ApiController('/task')
+@ControllerDoc("Task Management API", "Task")
class Task(RESTController):
+ @EndpointDoc("Display Tasks",
+ parameters={
+ 'name': (str, 'Task Name'),
+ },
+ responses={200: TASK_SCHEMA})
def list(self, name=None):
executing_t, finished_t = TaskManager.list_serializable(name)
# -*- coding: utf-8 -*-
from __future__ import absolute_import
-from . import ApiController, RESTController
+from . import ApiController, RESTController, ControllerDoc, EndpointDoc
from .. import mgr
from ..exceptions import DashboardException
from ..security import Scope
+REPORT_SCHEMA = {
+ "report": ({
+ "leaderboard": (bool, ""),
+ "report_version": (int, ""),
+ "report_timestamp": (str, ""),
+ "report_id": (str, ""),
+ "channels": ([str], ""),
+ "channels_available": ([str], ""),
+ "license": (str, ""),
+ "created": (str, ""),
+ "mon": ({
+ "count": (int, ""),
+ "features": ({
+ "persistent": ([str], ""),
+ "optional": ([int], "")
+ }, ""),
+ "min_mon_release": (int, ""),
+ "v1_addr_mons": (int, ""),
+ "v2_addr_mons": (int, ""),
+ "ipv4_addr_mons": (int, ""),
+ "ipv6_addr_mons": (int, ""),
+ }, ""),
+ "config": ({
+ "cluster_changed": ([str], ""),
+ "active_changed": ([str], "")
+ }, ""),
+ "rbd": ({
+ "num_pools": (int, ""),
+ "num_images_by_pool": ([int], ""),
+ "mirroring_by_pool": ([bool], ""),
+ }, ""),
+ "pools": ([{
+ "pool": (int, ""),
+ "type": (str, ""),
+ "pg_num": (int, ""),
+ "pgp_num": (int, ""),
+ "size": (int, ""),
+ "min_size": (int, ""),
+ "pg_autoscale_mode": (str, ""),
+ "target_max_bytes": (int, ""),
+ "target_max_objects": (int, ""),
+ "erasure_code_profile": (str, ""),
+ "cache_mode": (str, ""),
+ }], ""),
+ "osd": ({
+ "count": (int, ""),
+ "require_osd_release": (str, ""),
+ "require_min_compat_client": (str, ""),
+ "cluster_network": (bool, ""),
+ }, ""),
+ "crush": ({
+ "num_devices": (int, ""),
+ "num_types": (int, ""),
+ "num_buckets": (int, ""),
+ "num_rules": (int, ""),
+ "device_classes": ([int], ""),
+ "tunables": ({
+ "choose_local_tries": (int, ""),
+ "choose_local_fallback_tries": (int, ""),
+ "choose_total_tries": (int, ""),
+ "chooseleaf_descend_once": (int, ""),
+ "chooseleaf_vary_r": (int, ""),
+ "chooseleaf_stable": (int, ""),
+ "straw_calc_version": (int, ""),
+ "allowed_bucket_algs": (int, ""),
+ "profile": (str, ""),
+ "optimal_tunables": (int, ""),
+ "legacy_tunables": (int, ""),
+ "minimum_required_version": (str, ""),
+ "require_feature_tunables": (int, ""),
+ "require_feature_tunables2": (int, ""),
+ "has_v2_rules": (int, ""),
+ "require_feature_tunables3": (int, ""),
+ "has_v3_rules": (int, ""),
+ "has_v4_buckets": (int, ""),
+ "require_feature_tunables5": (int, ""),
+ "has_v5_rules": (int, ""),
+ }, ""),
+ "compat_weight_set": (bool, ""),
+ "num_weight_sets": (int, ""),
+ "bucket_algs": ({
+ "straw2": (int, ""),
+ }, ""),
+ "bucket_sizes": ({
+ "1": (int, ""),
+ "3": (int, ""),
+ }, ""),
+ "bucket_types": ({
+ "1": (int, ""),
+ "11": (int, ""),
+ }, ""),
+ }, ""),
+ "fs": ({
+ "count": (int, ""),
+ "feature_flags": ({
+ "enable_multiple": (bool, ""),
+ "ever_enabled_multiple": (bool, ""),
+ }, ""),
+ "num_standby_mds": (int, ""),
+ "filesystems": ([int], ""),
+ "total_num_mds": (int, ""),
+ }, ""),
+ "metadata": ({
+ "osd": ({
+ "osd_objectstore": ({
+ "bluestore": (int, ""),
+ }, ""),
+ "rotational": ({
+ "1": (int, ""),
+ }, ""),
+ "arch": ({
+ "x86_64": (int, ""),
+ }, ""),
+ "ceph_version": ({
+ "ceph version 16.0.0-3151-gf202994fcf": (int, ""),
+ }, ""),
+ "os": ({
+ "Linux": (int, ""),
+ }, ""),
+ "cpu": ({
+ "Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz": (int, ""),
+ }, ""),
+ "kernel_description": ({
+ "#1 SMP Wed Jul 1 19:53:01 UTC 2020": (int, ""),
+ }, ""),
+ "kernel_version": ({
+ "5.7.7-200.fc32.x86_64": (int, ""),
+ }, ""),
+ "distro_description": ({
+ "CentOS Linux 8 (Core)": (int, ""),
+ }, ""),
+ "distro": ({
+ "centos": (int, ""),
+ }, ""),
+ }, ""),
+ "mon": ({
+ "arch": ({
+ "x86_64": (int, ""),
+ }, ""),
+ "ceph_version": ({
+ "ceph version 16.0.0-3151-gf202994fcf": (int, ""),
+ }, ""),
+ "os": ({
+ "Linux": (int, ""),
+ }, ""),
+ "cpu": ({
+ "Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz": (int, ""),
+ }, ""),
+ "kernel_description": ({
+ "#1 SMP Wed Jul 1 19:53:01 UTC 2020": (int, ""),
+ }, ""),
+ "kernel_version": ({
+ "5.7.7-200.fc32.x86_64": (int, ""),
+ }, ""),
+ "distro_description": ({
+ "CentOS Linux 8 (Core)": (int, ""),
+ }, ""),
+ "distro": ({
+ "centos": (int, ""),
+ }, ""),
+ }, ""),
+ }, ""),
+ "hosts": ({
+ "num": (int, ""),
+ "num_with_mon": (int, ""),
+ "num_with_mds": (int, ""),
+ "num_with_osd": (int, ""),
+ "num_with_mgr": (int, ""),
+ }, ""),
+ "usage": ({
+ "pools": (int, ""),
+ "pg_num": (int, ""),
+ "total_used_bytes": (int, ""),
+ "total_bytes": (int, ""),
+ "total_avail_bytes": (int, ""),
+ }, ""),
+ "services": ({
+ "rgw": (int, ""),
+ }, ""),
+ "rgw": ({
+ "count": (int, ""),
+ "zones": (int, ""),
+ "zonegroups": (int, ""),
+ "frontends": ([str], "")
+ }, ""),
+ "balancer": ({
+ "active": (bool, ""),
+ "mode": (str, ""),
+ }, ""),
+ "crashes": ([int], "")
+ }, ""),
+ "device_report": (str, "")
+}
+
@ApiController('/telemetry', Scope.CONFIG_OPT)
+@ControllerDoc("Display Telemetry Report", "Telemetry")
class Telemetry(RESTController):
@RESTController.Collection('GET')
+ @EndpointDoc("Get Detailed Telemetry report",
+ responses={200: REPORT_SCHEMA})
def report(self):
"""
Get Ceph and device report data
import cherrypy
-from . import BaseController, ApiController, RESTController, Endpoint
+from . import BaseController, ApiController, RESTController, Endpoint, ControllerDoc, EndpointDoc
from .. import mgr
from ..exceptions import DashboardException, UserAlreadyExists, \
UserDoesNotExist, PasswordPolicyException, PwdExpirationDateNotValid
from ..services.access_control import SYSTEM_ROLES, PasswordPolicy
from ..services.auth import JwtManager
+USER_SCHEMA = ([{
+ "username": (str, 'Username of the user'),
+ "roles": ([str], 'User Roles'),
+ "name": (str, 'User Name'),
+ "email": (str, 'User email address'),
+ "lastUpdate": (int, 'Details last updated'),
+ "enabled": (bool, 'Is the user enabled?'),
+ "pwdExpirationDate": (str, 'Password Expiration date'),
+ "pwdUpdateRequired": (bool, 'Is Password Update Required?')
+}], '')
+
def validate_password_policy(password, username=None, old_password=None):
"""
@ApiController('/user', Scope.USER)
+@ControllerDoc("Display User Details", "User")
class User(RESTController):
@staticmethod
raise DashboardException(msg='Role does not exist',
code='role_does_not_exist',
component='user')
-
+ @EndpointDoc("Get List Of Users",
+ responses={200: USER_SCHEMA})
def list(self):
users = mgr.ACCESS_CTRL_DB.users
result = [User._user_to_dict(u) for _, u in users.items()]
@ApiController('/user')
+@ControllerDoc("Get User Password Policy Details", "UserPasswordPolicy")
class UserPasswordPolicy(RESTController):
@Endpoint('POST')
@ApiController('/user/{username}')
+@ControllerDoc("Change User Password", "UserChangePassword")
class UserChangePassword(BaseController):
@Endpoint('POST')
from enum import Enum
import cherrypy
from mgr_module import CLICommand, Option
-
from . import PLUGIN_MANAGER as PM
from . import interfaces as I # noqa: E741,N812
from .ttl_cache import ttl_cache
PREDISABLED_FEATURES = set() # type: Set[str]
-
Feature2Controller = {
Features.RBD: [Rbd, RbdSnapshot, RbdTrash],
Features.MIRRORING: [
@PM.add_hook
def get_controllers(self):
- from ..controllers import ApiController, RESTController
+ from ..controllers import ApiController, RESTController, ControllerDoc, EndpointDoc
+
+ FEATURES_SCHEMA = {
+ "rbd": (bool, ''),
+ "mirroring": (bool, ''),
+ "iscsi": (bool, ''),
+ "cephfs": (bool, ''),
+ "rgw": (bool, ''),
+ "nfs": (bool, '')
+ }
@ApiController('/feature_toggles')
+ @ControllerDoc("Manage Features API", "FeatureTogglesEndpoint")
class FeatureTogglesEndpoint(RESTController):
-
+ @EndpointDoc("Get List Of Features",
+ responses={200: FEATURES_SCHEMA})
def list(_): # pylint: disable=no-self-argument # noqa: N805
return {
# pylint: disable=protected-access