]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Placeholders for Documenting REST API 36347/head
authorAashish Sharma <aashishsharma@localhost.localdomain>
Wed, 29 Jul 2020 11:48:21 +0000 (17:18 +0530)
committerAashish Sharma <aashishsharma@localhost.localdomain>
Mon, 17 Aug 2020 16:51:34 +0000 (22:21 +0530)
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>
29 files changed:
src/pybind/mgr/dashboard/controllers/auth.py
src/pybind/mgr/dashboard/controllers/cephfs.py
src/pybind/mgr/dashboard/controllers/cluster_configuration.py
src/pybind/mgr/dashboard/controllers/crush_rule.py
src/pybind/mgr/dashboard/controllers/docs.py
src/pybind/mgr/dashboard/controllers/erasure_code_profile.py
src/pybind/mgr/dashboard/controllers/grafana.py
src/pybind/mgr/dashboard/controllers/health.py
src/pybind/mgr/dashboard/controllers/host.py
src/pybind/mgr/dashboard/controllers/iscsi.py
src/pybind/mgr/dashboard/controllers/logs.py
src/pybind/mgr/dashboard/controllers/mgr_modules.py
src/pybind/mgr/dashboard/controllers/monitor.py
src/pybind/mgr/dashboard/controllers/orchestrator.py
src/pybind/mgr/dashboard/controllers/osd.py
src/pybind/mgr/dashboard/controllers/perf_counters.py
src/pybind/mgr/dashboard/controllers/pool.py
src/pybind/mgr/dashboard/controllers/prometheus.py
src/pybind/mgr/dashboard/controllers/rbd.py
src/pybind/mgr/dashboard/controllers/rbd_mirroring.py
src/pybind/mgr/dashboard/controllers/rgw.py
src/pybind/mgr/dashboard/controllers/role.py
src/pybind/mgr/dashboard/controllers/service.py
src/pybind/mgr/dashboard/controllers/settings.py
src/pybind/mgr/dashboard/controllers/summary.py
src/pybind/mgr/dashboard/controllers/task.py
src/pybind/mgr/dashboard/controllers/telemetry.py
src/pybind/mgr/dashboard/controllers/user.py
src/pybind/mgr/dashboard/plugins/feature_toggles.py

index 68c3827b4e85e1920101a8a91bd3f5c3e4e453f4..fb021be84201d436c4ea0e5016176a3e5fc6a5c7 100644 (file)
@@ -4,7 +4,7 @@ from __future__ import absolute_import
 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
@@ -12,13 +12,22 @@ 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
@@ -63,7 +72,10 @@ class Auth(RESTController):
             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)
index 499fae94612ce4a2367417637aff0b234afb0a9e..e6eb9c83efe38207630c376ca3156b0bcbfb1fe2 100644 (file)
@@ -8,7 +8,8 @@ import os
 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
@@ -16,8 +17,14 @@ from ..services.cephfs import CephFS as CephFS_
 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__()
@@ -431,6 +438,12 @@ class CephFS(RESTController):
         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.
index 810adf333448434e3ed7e2fec08daa7846ce49b8..382e18eb01ac2e2ad90e9f68a078139caac8c3a9 100644 (file)
@@ -3,14 +3,34 @@ from __future__ import absolute_import
 
 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):
@@ -39,6 +59,11 @@ class ClusterConfiguration(RESTController):
         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 = []
 
index 3aeef258aa3bbf862d5c9926909493c31866ff98..fe62f090f61d61687d9c2937791b6835c9c5ff87 100644 (file)
@@ -4,14 +4,28 @@ from __future__ import absolute_import
 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']
 
index 84de247010c1bf1398073ef05f1031dde750b1d5..fce5341193c3115215a4fa5bc063bdb7fcf72822 100644 (file)
@@ -254,6 +254,7 @@ class Docs(BaseController):
 
     @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()),
@@ -305,6 +306,13 @@ class Docs(BaseController):
                                 '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': []}]
 
index 3c8ba61f9f8ab9387038d56a879d5e499d630a82..e9dc01e7e8a3eb76847f8e9cd6e820e8e028465e 100644 (file)
@@ -4,19 +4,30 @@ from __future__ import absolute_import
 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()
 
index 0d4331ff246372348aafc8378db7b019062dd42c..9639d0260f13a9cf3e963a5f47c8dae02dcb7ffc 100644 (file)
@@ -3,17 +3,26 @@ from __future__ import absolute_import
 
 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
index 35e7b07b216e7a136be14971beaf42ec2dd5ce8f..b8f91ca8f514d6c13c9f9ab93415bcbde6bfde4d 100644 (file)
@@ -3,7 +3,7 @@ from __future__ import absolute_import
 
 import json
 
-from . import ApiController, Endpoint, BaseController
+from . import ApiController, Endpoint, BaseController, ControllerDoc, EndpointDoc
 
 from .. import mgr
 from ..rest_client import RequestException
@@ -14,6 +14,102 @@ from ..services.iscsi_client import IscsiClient
 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):
     """
@@ -181,6 +277,7 @@ class HealthData(object):
 
 
 @ApiController('/health')
+@ControllerDoc("Display Detailed Cluster health Status", "Health")
 class Health(BaseController):
     def __init__(self):
         super(Health, self).__init__()
@@ -192,5 +289,7 @@ class Health(BaseController):
         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()
index db498c8aaad8983eeae721bb5a5afb97ce997a5d..21c9bb1b6b918e8c8fbd763c7b6924cc6b2932b1 100644 (file)
@@ -10,7 +10,7 @@ import cherrypy
 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
@@ -19,6 +19,23 @@ from ..services.orchestrator import OrchClient
 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)
@@ -105,7 +122,13 @@ def get_host(hostname: str) -> Dict:
 
 
 @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()
index 81a54ce61e77400f1ed82f6ac6c4038064769370..cb5e657cbb5e2b5c6dc8b59ed6bafb13118d72de 100644 (file)
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+# pylint: disable=C0302
 # pylint: disable=too-many-branches
 # pylint: disable=too-many-lines
 from __future__ import absolute_import
@@ -12,7 +13,7 @@ import rados
 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
@@ -29,6 +30,13 @@ try:
 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):
@@ -188,16 +196,26 @@ 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,
@@ -235,6 +253,7 @@ def iscsi_target_task(name, metadata, wait_for=2.0):
 
 
 @ApiController('/iscsi/target', Scope.ISCSI)
+@ControllerDoc("Get Iscsi Target Details", "IscsiTarget")
 class IscsiTarget(RESTController):
 
     def list(self):
index 9dc5286f3bd4b979519193c6b3dd6ffaa6c0523b..63f7bfdb414efc6ec4b4980e7768b1f3aaf66cee 100644 (file)
@@ -3,7 +3,7 @@ from __future__ import absolute_import
 
 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
@@ -11,8 +11,29 @@ 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__()
@@ -43,6 +64,8 @@ class Logs(BaseController):
 
     @Endpoint()
     @ReadPermission
+    @EndpointDoc("Display Logs Configuration",
+                 responses={200: LOGS_SCHEMA})
     def all(self):
         self.initialize_buffers()
         return dict(
index 0d4cc5222560c54a9708a654212d11cd35dea42d..ca834b96f893d18be0588199e5e4913836992732 100644 (file)
@@ -1,18 +1,42 @@
 # -*- 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.
index d4512fcfe357fc7e1f1865a5fce8efc310e2b1a9..5b5a44fec747e98756f59e5ac6351aec42737090 100644 (file)
@@ -3,15 +3,109 @@ from __future__ import absolute_import
 
 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 = [], []
 
index 14ddd39298aebe0faf9d9cfd0e3046547da90075..8ef183285e72fbc8d5e8d885e23e2c88b5755766 100644 (file)
@@ -5,7 +5,7 @@ import os.path
 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
@@ -14,6 +14,11 @@ from ..services.exception import handle_orchestrator_error
 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.
@@ -67,10 +72,13 @@ def raise_if_no_orchestrator(method):
 
 
 @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()
 
@@ -100,6 +108,7 @@ class Orchestrator(RESTController):
 
 
 @ApiController('/orchestrator/inventory', Scope.HOSTS)
+@ControllerDoc("Get Orchestrator Inventory Details", "OrchestratorInventory")
 class OrchestratorInventory(RESTController):
 
     @raise_if_no_orchestrator
index caff46e5eaadfad4897d352d0cb0c4e1a8892ccc..bc8b4bc172fcb97c2610e5c9af1cc8a22fe5a0d3 100644 (file)
@@ -7,7 +7,7 @@ import time
 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
@@ -25,12 +25,25 @@ except ImportError:  # pragma: no cover
 
 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()
@@ -301,6 +314,11 @@ class Osd(RESTController):
 
     @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]
@@ -345,6 +363,7 @@ class Osd(RESTController):
 
 
 @ApiController('/osd/flags', Scope.OSD)
+@ControllerDoc("OSD Flags controller Management API", "OsdFlagsController")
 class OsdFlagsController(RESTController):
     @staticmethod
     def _osd_flags():
@@ -358,6 +377,8 @@ class OsdFlagsController(RESTController):
                 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()
 
index 158641ca1562f2dca7830e09e627a7df54760bb4..30d62358e37a3c4410dacad29036f5769a03c166 100644 (file)
@@ -3,11 +3,24 @@ from __future__ import absolute_import
 
 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
@@ -45,41 +58,51 @@ class PerfCounter(RESTController):
 
 
 @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()
index a1f2e6d916ad2e6b377cc74651ff5cf2b7d7f648..8e3294bfbbc7b926bb745ca6a1c09682d3359a03 100644 (file)
@@ -6,7 +6,7 @@ import time
 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
@@ -14,12 +14,83 @@ from ..services.rbd import RbdConfiguration
 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
@@ -58,6 +129,12 @@ class Pool(RESTController):
 
         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)
 
index 219adfa86f70bb8e2196d4e68062f5314a165fb7..c13e6bd64dc73c5b5dea04c49a1ecf8240ce14b9 100644 (file)
@@ -5,7 +5,7 @@ from datetime import datetime
 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
@@ -57,6 +57,7 @@ class PrometheusRESTController(RESTController):
 
 
 @ApiController('/prometheus', Scope.PROMETHEUS)
+@ControllerDoc("Prometheus Management API", "Prometheus")
 class Prometheus(PrometheusRESTController):
     def list(self, **params):
         return self.alert_proxy('GET', '/alerts', params)
@@ -79,6 +80,7 @@ class Prometheus(PrometheusRESTController):
 
 
 @ApiController('/prometheus/notifications', Scope.PROMETHEUS)
+@ControllerDoc("Prometheus Notifications Management API", "PrometheusNotifications")
 class PrometheusNotifications(RESTController):
 
     def list(self, **params):
index ec959949381fbf64bd91f8f13c9f02a6c51276df..a3ccf634276f651e2e522d4e4765223298066000 100644 (file)
@@ -11,7 +11,7 @@ from datetime import datetime
 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
@@ -24,6 +24,18 @@ from ..services.exception import handle_rados_error, handle_rbd_error, \
 
 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
@@ -55,6 +67,7 @@ def _sort_features(features, enable=True):
 
 
 @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
@@ -82,6 +95,11 @@ class Rbd(RESTController):
 
     @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)
 
@@ -229,6 +247,7 @@ class Rbd(RESTController):
 
 
 @ApiController('/block/image/{image_spec}/snap', Scope.RBD_IMAGE)
+@ControllerDoc("RBD Snapshot Management API", "RbdSnapshot")
 class RbdSnapshot(RESTController):
 
     RESOURCE_ID = "snapshot_name"
@@ -317,6 +336,7 @@ class RbdSnapshot(RESTController):
 
 
 @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()
@@ -355,6 +375,11 @@ class RbdTrash(RESTController):
 
     @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)
@@ -398,6 +423,7 @@ class RbdTrash(RESTController):
 
 
 @ApiController('/block/pool/{pool_name}/namespace', Scope.RBD_IMAGE)
+@ControllerDoc("RBD Namespace Management API", "RbdNamespace")
 class RbdNamespace(RESTController):
     rbd_inst = rbd.RBD()
 
index 027289030854d8a1cc8cd43214f1bd682ad02d8a..c089e0a7275199c4a2af9ca4ec65c56bafdfc086 100644 (file)
@@ -12,7 +12,7 @@ import cherrypy
 import rbd
 
 from . import ApiController, Endpoint, Task, BaseController, ReadPermission, \
-    UpdatePermission, RESTController
+    UpdatePermission, RESTController, EndpointDoc, ControllerDoc
 
 from .. import mgr
 from ..security import Scope
@@ -335,12 +335,42 @@ def _reset_view_cache():
     _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()
 
@@ -356,11 +386,14 @@ class RbdMirroring(BaseController):
 
 
 @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)
 
@@ -371,6 +404,7 @@ class RbdMirroringSummary(BaseController):
 
 
 @ApiController('/block/mirroring/pool', Scope.RBD_MIRRORING)
+@ControllerDoc("RBD Mirroring Pool Mode Management API", "RbdMirroringPoolMode")
 class RbdMirroringPoolMode(RESTController):
 
     RESOURCE_ID = "pool_name"
@@ -381,6 +415,11 @@ class RbdMirroringPoolMode(RESTController):
     }
 
     @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)
@@ -408,6 +447,7 @@ class RbdMirroringPoolMode(RESTController):
 
 @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')
@@ -438,6 +478,7 @@ class RbdMirroringPoolBootstrap(BaseController):
 
 
 @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"
index a0dc444dcabff7d497f294c3ed1b9972352a8a8c..6f0ded0b513f38a2eda66937c1c9d5ddbf99e3ec 100644 (file)
@@ -6,7 +6,7 @@ import json
 
 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
@@ -20,13 +20,31 @@ try:
 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:
@@ -52,7 +70,10 @@ class Rgw(BaseController):
 
 
 @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 = []
@@ -111,6 +132,7 @@ class RgwRESTController(RESTController):
 
 
 @ApiController('/rgw/site', Scope.RGW)
+@ControllerDoc("RGW Site Management API", "RgwSite")
 class RgwSite(RgwRESTController):
     def list(self, query=None):
         if query == 'placement-targets':
@@ -125,6 +147,7 @@ class RgwSite(RgwRESTController):
 
 
 @ApiController('/rgw/bucket', Scope.RGW)
+@ControllerDoc("RGW Bucket Management API", "RgwBucket")
 class RgwBucket(RgwRESTController):
     def _append_bid(self, bucket):
         """
@@ -273,6 +296,7 @@ class RgwBucket(RgwRESTController):
 
 
 @ApiController('/rgw/user', Scope.RGW)
+@ControllerDoc("RGW User Management API", "RgwUser")
 class RgwUser(RgwRESTController):
     def _append_uid(self, user):
         """
@@ -296,6 +320,8 @@ class RgwUser(RgwRESTController):
         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]
index 6bf616d08ef12cd1cd9ba3011e80c17bf044e24f..cfa28fa5c410932d0eacefee0672688e4fc61614 100644 (file)
@@ -4,15 +4,25 @@ from __future__ import absolute_import
 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):
@@ -42,6 +52,8 @@ class Role(RESTController):
                 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)
index baacff080ddb205f9677ad38394b3330f9d444e9..0628e6d8e33665f57aa89f98c2277f7b51380432 100644 (file)
@@ -2,8 +2,8 @@ from typing import List, Optional, Dict
 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
@@ -16,6 +16,7 @@ def service_task(name, metadata, wait_for=2.0):
 
 
 @ApiController('/service', Scope.HOSTS)
+@ControllerDoc("Service Management API", "Service")
 class Service(RESTController):
 
     @Endpoint()
index 5235992a1741ae28ba3320c593bda55ebd48e8b1..dc35eec9561ab6f6bc382872218acb3d5550a899 100644 (file)
@@ -4,12 +4,20 @@ from contextlib import contextmanager
 
 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).
@@ -37,6 +45,11 @@ class Settings(RESTController):
     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.
index 3b181cb4d4e4eed49e898342766578e46d83414b..5febf4a6d83da335ea07a9def48e586a634c645a 100644 (file)
@@ -3,7 +3,7 @@ from __future__ import absolute_import
 
 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
@@ -11,8 +11,35 @@ from ..exceptions import ViewCacheNoDataException
 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")
@@ -69,6 +96,8 @@ class Summary(BaseController):
         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'])]
index 8ac97fdb8f1e746a1d5e301c30223c9fbf7553dc..1668f26af37ebd955551fb771f3f3bb36a5ba404 100644 (file)
@@ -1,13 +1,36 @@
 # -*- 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)
 
index c68432ece17573cd156efa4473ab9217a9aaa3dd..0478ba68755241ca5185fdb6f20e6af0958d8043 100644 (file)
 # -*- 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
index d32ee4cc8339ae4ce3aa37568e060623ad3a1d79..bd50da5c0befc9e1c5e2220b09f97ef8eaf8b883 100644 (file)
@@ -7,7 +7,7 @@ import time
 
 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
@@ -15,6 +15,17 @@ from ..security import Scope
 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):
     """
@@ -36,6 +47,7 @@ def validate_password_policy(password, username=None, old_password=None):
 
 
 @ApiController('/user', Scope.USER)
+@ControllerDoc("Display User Details", "User")
 class User(RESTController):
 
     @staticmethod
@@ -54,7 +66,8 @@ class User(RESTController):
             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()]
@@ -143,6 +156,7 @@ class User(RESTController):
 
 
 @ApiController('/user')
+@ControllerDoc("Get User Password Policy Details", "UserPasswordPolicy")
 class UserPasswordPolicy(RESTController):
 
     @Endpoint('POST')
@@ -174,6 +188,7 @@ class UserPasswordPolicy(RESTController):
 
 
 @ApiController('/user/{username}')
+@ControllerDoc("Change User Password", "UserChangePassword")
 class UserChangePassword(BaseController):
 
     @Endpoint('POST')
index 5e7c4790589663fe36525ee459ccb45e09a86a7a..f72afc58c13e722039cd913b55b0edf9f7fad7eb 100644 (file)
@@ -4,7 +4,6 @@ from __future__ import absolute_import
 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
@@ -34,7 +33,6 @@ class Features(Enum):
 
 PREDISABLED_FEATURES = set()  # type: Set[str]
 
-
 Feature2Controller = {
     Features.RBD: [Rbd, RbdSnapshot, RbdTrash],
     Features.MIRRORING: [
@@ -139,11 +137,22 @@ class FeatureToggles(I.CanMgr, I.Setupable, I.HasOptions,
 
     @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