]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
restful: Split api.py file into a module
authorBoris Ranto <branto@redhat.com>
Tue, 9 May 2017 08:52:15 +0000 (10:52 +0200)
committerBoris Ranto <branto@redhat.com>
Mon, 22 May 2017 17:19:02 +0000 (19:19 +0200)
The patch makes the REST api being served by a module, not a single
monolithic file.

Signed-off-by: Boris Ranto <branto@redhat.com>
src/pybind/mgr/restful/api.py [deleted file]
src/pybind/mgr/restful/api/__init__.py [new file with mode: 0644]
src/pybind/mgr/restful/api/config.py [new file with mode: 0644]
src/pybind/mgr/restful/api/crush.py [new file with mode: 0644]
src/pybind/mgr/restful/api/doc.py [new file with mode: 0644]
src/pybind/mgr/restful/api/mon.py [new file with mode: 0644]
src/pybind/mgr/restful/api/osd.py [new file with mode: 0644]
src/pybind/mgr/restful/api/pool.py [new file with mode: 0644]
src/pybind/mgr/restful/api/request.py [new file with mode: 0644]
src/pybind/mgr/restful/api/server.py [new file with mode: 0644]

diff --git a/src/pybind/mgr/restful/api.py b/src/pybind/mgr/restful/api.py
deleted file mode 100644 (file)
index 10de13d..0000000
+++ /dev/null
@@ -1,626 +0,0 @@
-from pecan import expose, request, response
-from pecan.rest import RestController
-
-import common
-
-from collections import defaultdict
-
-from decorators import auth, catch, lock
-
-## We need this to access the instance of the module
-#
-# We can't use 'from module import instance' because
-# the instance is not ready, yet (would be None)
-import module
-
-
-class ServerFqdn(RestController):
-    def __init__(self, fqdn):
-        self.fqdn = fqdn
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show the information for the server fqdn
-        """
-        return module.instance.get_server(self.fqdn)
-
-
-
-class Server(RestController):
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show the information for all the servers
-        """
-        return module.instance.list_servers()
-
-
-    @expose()
-    def _lookup(self, fqdn, *remainder):
-        return ServerFqdn(fqdn), remainder
-
-
-
-class RequestId(RestController):
-    def __init__(self, request_id):
-        self.request_id = request_id
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show the information for the request id
-        """
-        request = filter(
-            lambda x: x.id == self.request_id,
-            module.instance.requests
-        )
-
-        if len(request) != 1:
-            response.status = 500
-            return {'message': 'Unknown request id "%s"' % str(self.request_id)}
-
-        request = request[0]
-        return request
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    @lock
-    def delete(self, **kwargs):
-        """
-        Remove the request id from the database
-        """
-        for index in range(len(module.instance.requests)):
-            if module.instance.requests[index].id == self.request_id:
-                return module.instance.requests.pop(index)
-
-        # Failed to find the job to cancel
-        response.status = 500
-        return {'message': 'No such request id'}
-
-
-
-class Request(RestController):
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        List all the available requests and their state
-        """
-        states = {}
-        for _request in module.instance.requests:
-            states[_request.id] = _request.get_state()
-
-        return states
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    @lock
-    def delete(self, **kwargs):
-        """
-        Remove all the finished requests
-        """
-        num_requests = len(module.instance.requests)
-
-        module.instance.requests = filter(
-            lambda x: not x.is_finished(),
-            module.instance.requests
-        )
-
-        # Return the job statistics
-        return {
-            'cleaned': num_requests - len(module.instance.requests),
-            'remaining': len(module.instance.requests),
-        }
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def post(self, **kwargs):
-        """
-        Pass through method to create any request
-        """
-        return module.instance.submit_request([[request.json]], **kwargs)
-
-
-    @expose()
-    def _lookup(self, request_id, *remainder):
-        return RequestId(request_id), remainder
-
-
-
-class PoolId(RestController):
-    def __init__(self, pool_id):
-        self.pool_id = pool_id
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show the information for the pool id
-        """
-        pool = module.instance.get_pool_by_id(self.pool_id)
-
-        if not pool:
-            response.status = 500
-            return {'message': 'Failed to identify the pool id "%d"' % self.pool_id}
-
-        # pgp_num is called pg_placement_num, deal with that
-        if 'pg_placement_num' in pool:
-            pool['pgp_num'] = pool.pop('pg_placement_num')
-        return pool
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def patch(self, **kwargs):
-        """
-        Modify the information for the pool id
-        """
-        args = request.json
-
-        # Get the pool info for its name
-        pool = module.instance.get_pool_by_id(self.pool_id)
-        if not pool:
-            response.status = 500
-            return {'message': 'Failed to identify the pool id "%d"' % self.pool_id}
-
-        # Check for invalid pool args
-        invalid = common.invalid_pool_args(args)
-        if invalid:
-            response.status = 500
-            return {'message': 'Invalid arguments found: "%s"' % str(invalid)}
-
-        # Schedule the update request
-        return module.instance.submit_request(common.pool_update_commands(pool['pool_name'], args), **kwargs)
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def delete(self, **kwargs):
-        """
-        Remove the pool data for the pool id
-        """
-        pool = module.instance.get_pool_by_id(self.pool_id)
-
-        if not pool:
-            response.status = 500
-            return {'message': 'Failed to identify the pool id "%d"' % self.pool_id}
-
-        return module.instance.submit_request([[{
-            'prefix': 'osd pool delete',
-            'pool': pool['pool_name'],
-            'pool2': pool['pool_name'],
-            'sure': '--yes-i-really-really-mean-it'
-        }]], **kwargs)
-
-
-
-class Pool(RestController):
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show the information for all the pools
-        """
-        pools = module.instance.get('osd_map')['pools']
-
-        # pgp_num is called pg_placement_num, deal with that
-        for pool in pools:
-            if 'pg_placement_num' in pool:
-                pool['pgp_num'] = pool.pop('pg_placement_num')
-
-        return pools
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def post(self, **kwargs):
-        """
-        Create a new pool
-        Requires name and pg_num dict arguments
-        """
-        args = request.json
-
-        # Check for the required arguments
-        pool_name = args.pop('name', None)
-        if pool_name is None:
-            response.status = 500
-            return {'message': 'You need to specify the pool "name" argument'}
-
-        pg_num = args.pop('pg_num', None)
-        if pg_num is None:
-            response.status = 500
-            return {'message': 'You need to specify the "pg_num" argument'}
-
-        # Run the pool create command first
-        create_command = {
-            'prefix': 'osd pool create',
-            'pool': pool_name,
-            'pg_num': pg_num
-        }
-
-        # Check for invalid pool args
-        invalid = common.invalid_pool_args(args)
-        if invalid:
-            response.status = 500
-            return {'message': 'Invalid arguments found: "%s"' % str(invalid)}
-
-        # Schedule the creation and update requests
-        return module.instance.submit_request(
-            [[create_command]] +
-            common.pool_update_commands(pool_name, args),
-            **kwargs
-        )
-
-
-    @expose()
-    def _lookup(self, pool_id, *remainder):
-        return PoolId(int(pool_id)), remainder
-
-
-
-class OsdIdCommand(RestController):
-    def __init__(self, osd_id):
-        self.osd_id = osd_id
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show implemented commands for the OSD id
-        """
-        osd = module.instance.get_osd_by_id(self.osd_id)
-
-        if not osd:
-            response.status = 500
-            return {'message': 'Failed to identify the OSD id "%d"' % self.osd_id}
-
-        if osd['up']:
-            return common.OSD_IMPLEMENTED_COMMANDS
-        else:
-            return []
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def post(self, **kwargs):
-        """
-        Run the implemented command for the OSD id
-        """
-        command = request.json.get('command', None)
-
-        osd = module.instance.get_osd_by_id(self.osd_id)
-
-        if not osd:
-            response.status = 500
-            return {'message': 'Failed to identify the OSD id "%d"' % self.osd_id}
-
-        if not osd['up'] or command not in common.OSD_IMPLEMENTED_COMMANDS:
-            response.status = 500
-            return {'message': 'Command "%s" not available' % command}
-
-        return module.instance.submit_request([[{
-            'prefix': 'osd ' + command,
-            'who': str(self.osd_id)
-        }]], **kwargs)
-
-
-
-class OsdId(RestController):
-    def __init__(self, osd_id):
-        self.osd_id = osd_id
-        self.command = OsdIdCommand(osd_id)
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show the information for the OSD id
-        """
-        osd = module.instance.get_osds(ids=[str(self.osd_id)])
-        if len(osd) != 1:
-            response.status = 500
-            return {'message': 'Failed to identify the OSD id "%d"' % self.osd_id}
-
-        return osd[0]
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def patch(self, **kwargs):
-        """
-        Modify the state (up, in) of the OSD id or reweight it
-        """
-        args = request.json
-
-        commands = []
-
-        if 'in' in args:
-            if args['in']:
-                commands.append({
-                    'prefix': 'osd in',
-                    'ids': [str(self.osd_id)]
-                })
-            else:
-                commands.append({
-                    'prefix': 'osd out',
-                    'ids': [str(self.osd_id)]
-                })
-
-        if 'up' in args:
-            if args['up']:
-                response.status = 500
-                return {'message': "It is not valid to set a down OSD to be up"}
-            else:
-                commands.append({
-                    'prefix': 'osd down',
-                    'ids': [str(self.osd_id)]
-                })
-
-        if 'reweight' in args:
-            commands.append({
-                'prefix': 'osd reweight',
-                'id': self.osd_id,
-                'weight': args['reweight']
-            })
-
-        return module.instance.submit_request([commands], **kwargs)
-
-
-
-class Osd(RestController):
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show the information for all the OSDs
-        """
-        # Parse request args
-        # TODO Filter by ids
-        pool_id = kwargs.get('pool', None)
-
-        return module.instance.get_osds(pool_id)
-
-
-    @expose()
-    def _lookup(self, osd_id, *remainder):
-        return OsdId(int(osd_id)), remainder
-
-
-
-class MonName(RestController):
-    def __init__(self, name):
-        self.name = name
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show the information for the monitor name
-        """
-        mon = filter(
-            lambda x: x['name'] == self.name,
-            module.instance.get_mons()
-        )
-
-        if len(mon) != 1:
-            response.status = 500
-            return {'message': 'Failed to identify the monitor node "%s"' % self.name}
-
-        return mon[0]
-
-
-
-class Mon(RestController):
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show the information for all the monitors
-        """
-        return module.instance.get_mons()
-
-
-    @expose()
-    def _lookup(self, name, *remainder):
-        return MonName(name), remainder
-
-
-
-class Doc(RestController):
-    @expose(template='json')
-    @catch
-    def get(self, **kwargs):
-        """
-        Show documentation information
-        """
-        return module.instance.get_doc_api(Root)
-
-
-
-class CrushRuleset(RestController):
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show crush rulesets
-        """
-        rules = module.instance.get('osd_map_crush')['rules']
-        nodes = module.instance.get('osd_map_tree')['nodes']
-
-        ruleset = defaultdict(list)
-        for rule in rules:
-            rule['osd_count'] = len(common.crush_rule_osds(nodes, rule))
-            ruleset[rule['ruleset']].append(rule)
-
-        return ruleset
-
-
-
-class CrushRule(RestController):
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show crush rules
-        """
-        rules = module.instance.get('osd_map_crush')['rules']
-        nodes = module.instance.get('osd_map_tree')['nodes']
-
-        for rule in rules:
-            rule['osd_count'] = len(common.crush_rule_osds(nodes, rule))
-
-        return rules
-
-
-
-class Crush(RestController):
-    rule = CrushRule()
-    ruleset = CrushRuleset()
-
-
-
-class ConfigOsd(RestController):
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show OSD configuration options
-        """
-        flags = module.instance.get("osd_map")['flags']
-
-        # pause is a valid osd config command that sets pauserd,pausewr
-        flags = flags.replace('pauserd,pausewr', 'pause')
-
-        return flags.split(',')
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def patch(self, **kwargs):
-        """
-        Modify OSD configration options
-        """
-        args = request.json
-
-        commands = []
-
-        valid_flags = set(args.keys()) & set(common.OSD_FLAGS)
-        invalid_flags = list(set(args.keys()) - valid_flags)
-        if invalid_flags:
-            module.instance.log.warn("%s not valid to set/unset" % invalid_flags)
-
-        for flag in list(valid_flags):
-            if args[flag]:
-                mode = 'set'
-            else:
-                mode = 'unset'
-
-            commands.append({
-                'prefix': 'osd ' + mode,
-                'key': flag,
-            })
-
-        return module.instance.submit_request([commands], **kwargs)
-
-
-
-class ConfigClusterKey(RestController):
-    def __init__(self, key):
-        self.key = key
-
-
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show specific configuration option
-        """
-        return module.instance.get("config").get(self.key, None)
-
-
-
-class ConfigCluster(RestController):
-    @expose(template='json')
-    @catch
-    @auth
-    def get(self, **kwargs):
-        """
-        Show all cluster configuration options
-        """
-        return module.instance.get("config")
-
-
-    @expose()
-    def _lookup(self, key, *remainder):
-        return ConfigClusterKey(key), remainder
-
-
-
-class Config(RestController):
-    cluster = ConfigCluster()
-    osd = ConfigOsd()
-
-
-
-class Root(RestController):
-    config = Config()
-    crush = Crush()
-    doc = Doc()
-    mon = Mon()
-    osd = Osd()
-    pool = Pool()
-    request = Request()
-    server = Server()
-
-    @expose(template='json')
-    @catch
-    def get(self, **kwargs):
-        """
-        Show the basic information for the REST API
-        This includes values like api version or auth method
-        """
-        return {
-            'api_version': 1,
-            'auth':
-                'Use ceph auth key pair as HTTP Basic user/password '
-                '(requires caps mon allow * to function properly)',
-            'doc': 'See /doc endpoint',
-            'info': "Ceph Manager RESTful API server",
-        }
diff --git a/src/pybind/mgr/restful/api/__init__.py b/src/pybind/mgr/restful/api/__init__.py
new file mode 100644 (file)
index 0000000..7a3b71e
--- /dev/null
@@ -0,0 +1,40 @@
+from pecan import expose
+from pecan.rest import RestController
+
+from config import Config
+from crush import Crush
+from doc import Doc
+from mon import Mon
+from osd import Osd
+from pool import Pool
+from request import Request
+from server import Server
+
+from restful.decorators import catch
+
+
+class Root(RestController):
+    config = Config()
+    crush = Crush()
+    doc = Doc()
+    mon = Mon()
+    osd = Osd()
+    pool = Pool()
+    request = Request()
+    server = Server()
+
+    @expose(template='json')
+    @catch
+    def get(self, **kwargs):
+        """
+        Show the basic information for the REST API
+        This includes values like api version or auth method
+        """
+        return {
+            'api_version': 1,
+            'auth':
+                'Use ceph auth key pair as HTTP Basic user/password '
+                '(requires caps mon allow * to function properly)',
+            'doc': 'See /doc endpoint',
+            'info': "Ceph Manager RESTful API server",
+        }
diff --git a/src/pybind/mgr/restful/api/config.py b/src/pybind/mgr/restful/api/config.py
new file mode 100644 (file)
index 0000000..cca6a47
--- /dev/null
@@ -0,0 +1,90 @@
+from pecan import expose, request
+from pecan.rest import RestController
+
+from restful import common, module
+from restful.decorators import auth, catch
+
+
+class ConfigOsd(RestController):
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show OSD configuration options
+        """
+        flags = module.instance.get("osd_map")['flags']
+
+        # pause is a valid osd config command that sets pauserd,pausewr
+        flags = flags.replace('pauserd,pausewr', 'pause')
+
+        return flags.split(',')
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def patch(self, **kwargs):
+        """
+        Modify OSD configration options
+        """
+        args = request.json
+
+        commands = []
+
+        valid_flags = set(args.keys()) & set(common.OSD_FLAGS)
+        invalid_flags = list(set(args.keys()) - valid_flags)
+        if invalid_flags:
+            module.instance.log.warn("%s not valid to set/unset" % invalid_flags)
+
+        for flag in list(valid_flags):
+            if args[flag]:
+                mode = 'set'
+            else:
+                mode = 'unset'
+
+            commands.append({
+                'prefix': 'osd ' + mode,
+                'key': flag,
+            })
+
+        return module.instance.submit_request([commands], **kwargs)
+
+
+
+class ConfigClusterKey(RestController):
+    def __init__(self, key):
+        self.key = key
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show specific configuration option
+        """
+        return module.instance.get("config").get(self.key, None)
+
+
+
+class ConfigCluster(RestController):
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show all cluster configuration options
+        """
+        return module.instance.get("config")
+
+
+    @expose()
+    def _lookup(self, key, *remainder):
+        return ConfigClusterKey(key), remainder
+
+
+
+class Config(RestController):
+    cluster = ConfigCluster()
+    osd = ConfigOsd()
diff --git a/src/pybind/mgr/restful/api/crush.py b/src/pybind/mgr/restful/api/crush.py
new file mode 100644 (file)
index 0000000..14338d6
--- /dev/null
@@ -0,0 +1,50 @@
+from pecan import expose
+from pecan.rest import RestController
+
+from restful import common, module
+from collections import defaultdict
+
+from restful.decorators import auth, catch
+
+
+class CrushRuleset(RestController):
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show crush rulesets
+        """
+        rules = module.instance.get('osd_map_crush')['rules']
+        nodes = module.instance.get('osd_map_tree')['nodes']
+
+        ruleset = defaultdict(list)
+        for rule in rules:
+            rule['osd_count'] = len(common.crush_rule_osds(nodes, rule))
+            ruleset[rule['ruleset']].append(rule)
+
+        return ruleset
+
+
+
+class CrushRule(RestController):
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show crush rules
+        """
+        rules = module.instance.get('osd_map_crush')['rules']
+        nodes = module.instance.get('osd_map_tree')['nodes']
+
+        for rule in rules:
+            rule['osd_count'] = len(common.crush_rule_osds(nodes, rule))
+
+        return rules
+
+
+
+class Crush(RestController):
+    rule = CrushRule()
+    ruleset = CrushRuleset()
diff --git a/src/pybind/mgr/restful/api/doc.py b/src/pybind/mgr/restful/api/doc.py
new file mode 100644 (file)
index 0000000..3ccd99b
--- /dev/null
@@ -0,0 +1,17 @@
+from pecan import expose
+from pecan.rest import RestController
+
+from restful import module
+from restful.decorators import catch
+
+import restful
+
+
+class Doc(RestController):
+    @expose(template='json')
+    @catch
+    def get(self, **kwargs):
+        """
+        Show documentation information
+        """
+        return module.instance.get_doc_api(restful.api.Root)
diff --git a/src/pybind/mgr/restful/api/mon.py b/src/pybind/mgr/restful/api/mon.py
new file mode 100644 (file)
index 0000000..c47e753
--- /dev/null
@@ -0,0 +1,46 @@
+from pecan import expose, response
+from pecan.rest import RestController
+
+from restful import module
+from restful.decorators import auth, catch
+
+
+class MonName(RestController):
+    def __init__(self, name):
+        self.name = name
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show the information for the monitor name
+        """
+        mon = filter(
+            lambda x: x['name'] == self.name,
+            module.instance.get_mons()
+        )
+
+        if len(mon) != 1:
+            response.status = 500
+            return {'message': 'Failed to identify the monitor node "%s"' % self.name}
+
+        return mon[0]
+
+
+
+class Mon(RestController):
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show the information for all the monitors
+        """
+        return module.instance.get_mons()
+
+
+    @expose()
+    def _lookup(self, name, *remainder):
+        return MonName(name), remainder
diff --git a/src/pybind/mgr/restful/api/osd.py b/src/pybind/mgr/restful/api/osd.py
new file mode 100644 (file)
index 0000000..d962031
--- /dev/null
@@ -0,0 +1,140 @@
+from pecan import expose, request, response
+from pecan.rest import RestController
+
+from restful import common, module
+from restful.decorators import auth, catch
+
+
+class OsdIdCommand(RestController):
+    def __init__(self, osd_id):
+        self.osd_id = osd_id
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show implemented commands for the OSD id
+        """
+        osd = module.instance.get_osd_by_id(self.osd_id)
+
+        if not osd:
+            response.status = 500
+            return {'message': 'Failed to identify the OSD id "%d"' % self.osd_id}
+
+        if osd['up']:
+            return common.OSD_IMPLEMENTED_COMMANDS
+        else:
+            return []
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def post(self, **kwargs):
+        """
+        Run the implemented command for the OSD id
+        """
+        command = request.json.get('command', None)
+
+        osd = module.instance.get_osd_by_id(self.osd_id)
+
+        if not osd:
+            response.status = 500
+            return {'message': 'Failed to identify the OSD id "%d"' % self.osd_id}
+
+        if not osd['up'] or command not in common.OSD_IMPLEMENTED_COMMANDS:
+            response.status = 500
+            return {'message': 'Command "%s" not available' % command}
+
+        return module.instance.submit_request([[{
+            'prefix': 'osd ' + command,
+            'who': str(self.osd_id)
+        }]], **kwargs)
+
+
+
+class OsdId(RestController):
+    def __init__(self, osd_id):
+        self.osd_id = osd_id
+        self.command = OsdIdCommand(osd_id)
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show the information for the OSD id
+        """
+        osd = module.instance.get_osds(ids=[str(self.osd_id)])
+        if len(osd) != 1:
+            response.status = 500
+            return {'message': 'Failed to identify the OSD id "%d"' % self.osd_id}
+
+        return osd[0]
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def patch(self, **kwargs):
+        """
+        Modify the state (up, in) of the OSD id or reweight it
+        """
+        args = request.json
+
+        commands = []
+
+        if 'in' in args:
+            if args['in']:
+                commands.append({
+                    'prefix': 'osd in',
+                    'ids': [str(self.osd_id)]
+                })
+            else:
+                commands.append({
+                    'prefix': 'osd out',
+                    'ids': [str(self.osd_id)]
+                })
+
+        if 'up' in args:
+            if args['up']:
+                response.status = 500
+                return {'message': "It is not valid to set a down OSD to be up"}
+            else:
+                commands.append({
+                    'prefix': 'osd down',
+                    'ids': [str(self.osd_id)]
+                })
+
+        if 'reweight' in args:
+            commands.append({
+                'prefix': 'osd reweight',
+                'id': self.osd_id,
+                'weight': args['reweight']
+            })
+
+        return module.instance.submit_request([commands], **kwargs)
+
+
+
+class Osd(RestController):
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show the information for all the OSDs
+        """
+        # Parse request args
+        # TODO Filter by ids
+        pool_id = kwargs.get('pool', None)
+
+        return module.instance.get_osds(pool_id)
+
+
+    @expose()
+    def _lookup(self, osd_id, *remainder):
+        return OsdId(int(osd_id)), remainder
diff --git a/src/pybind/mgr/restful/api/pool.py b/src/pybind/mgr/restful/api/pool.py
new file mode 100644 (file)
index 0000000..dca450e
--- /dev/null
@@ -0,0 +1,141 @@
+from pecan import expose, request, response
+from pecan.rest import RestController
+
+from restful import common, module
+from restful.decorators import auth, catch
+
+
+class PoolId(RestController):
+    def __init__(self, pool_id):
+        self.pool_id = pool_id
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show the information for the pool id
+        """
+        pool = module.instance.get_pool_by_id(self.pool_id)
+
+        if not pool:
+            response.status = 500
+            return {'message': 'Failed to identify the pool id "%d"' % self.pool_id}
+
+        # pgp_num is called pg_placement_num, deal with that
+        if 'pg_placement_num' in pool:
+            pool['pgp_num'] = pool.pop('pg_placement_num')
+        return pool
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def patch(self, **kwargs):
+        """
+        Modify the information for the pool id
+        """
+        args = request.json
+
+        # Get the pool info for its name
+        pool = module.instance.get_pool_by_id(self.pool_id)
+        if not pool:
+            response.status = 500
+            return {'message': 'Failed to identify the pool id "%d"' % self.pool_id}
+
+        # Check for invalid pool args
+        invalid = common.invalid_pool_args(args)
+        if invalid:
+            response.status = 500
+            return {'message': 'Invalid arguments found: "%s"' % str(invalid)}
+
+        # Schedule the update request
+        return module.instance.submit_request(common.pool_update_commands(pool['pool_name'], args), **kwargs)
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def delete(self, **kwargs):
+        """
+        Remove the pool data for the pool id
+        """
+        pool = module.instance.get_pool_by_id(self.pool_id)
+
+        if not pool:
+            response.status = 500
+            return {'message': 'Failed to identify the pool id "%d"' % self.pool_id}
+
+        return module.instance.submit_request([[{
+            'prefix': 'osd pool delete',
+            'pool': pool['pool_name'],
+            'pool2': pool['pool_name'],
+            'sure': '--yes-i-really-really-mean-it'
+        }]], **kwargs)
+
+
+
+class Pool(RestController):
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show the information for all the pools
+        """
+        pools = module.instance.get('osd_map')['pools']
+
+        # pgp_num is called pg_placement_num, deal with that
+        for pool in pools:
+            if 'pg_placement_num' in pool:
+                pool['pgp_num'] = pool.pop('pg_placement_num')
+
+        return pools
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def post(self, **kwargs):
+        """
+        Create a new pool
+        Requires name and pg_num dict arguments
+        """
+        args = request.json
+
+        # Check for the required arguments
+        pool_name = args.pop('name', None)
+        if pool_name is None:
+            response.status = 500
+            return {'message': 'You need to specify the pool "name" argument'}
+
+        pg_num = args.pop('pg_num', None)
+        if pg_num is None:
+            response.status = 500
+            return {'message': 'You need to specify the "pg_num" argument'}
+
+        # Run the pool create command first
+        create_command = {
+            'prefix': 'osd pool create',
+            'pool': pool_name,
+            'pg_num': pg_num
+        }
+
+        # Check for invalid pool args
+        invalid = common.invalid_pool_args(args)
+        if invalid:
+            response.status = 500
+            return {'message': 'Invalid arguments found: "%s"' % str(invalid)}
+
+        # Schedule the creation and update requests
+        return module.instance.submit_request(
+            [[create_command]] +
+            common.pool_update_commands(pool_name, args),
+            **kwargs
+        )
+
+
+    @expose()
+    def _lookup(self, pool_id, *remainder):
+        return PoolId(int(pool_id)), remainder
diff --git a/src/pybind/mgr/restful/api/request.py b/src/pybind/mgr/restful/api/request.py
new file mode 100644 (file)
index 0000000..ddf422b
--- /dev/null
@@ -0,0 +1,100 @@
+from pecan import expose, request, response
+from pecan.rest import RestController
+
+from restful import module
+from restful.decorators import auth, catch, lock
+
+
+class RequestId(RestController):
+    def __init__(self, request_id):
+        self.request_id = request_id
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show the information for the request id
+        """
+        request = filter(
+            lambda x: x.id == self.request_id,
+            module.instance.requests
+        )
+
+        if len(request) != 1:
+            response.status = 500
+            return {'message': 'Unknown request id "%s"' % str(self.request_id)}
+
+        request = request[0]
+        return request
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    @lock
+    def delete(self, **kwargs):
+        """
+        Remove the request id from the database
+        """
+        for index in range(len(module.instance.requests)):
+            if module.instance.requests[index].id == self.request_id:
+                return module.instance.requests.pop(index)
+
+        # Failed to find the job to cancel
+        response.status = 500
+        return {'message': 'No such request id'}
+
+
+
+class Request(RestController):
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        List all the available requests and their state
+        """
+        states = {}
+        for _request in module.instance.requests:
+            states[_request.id] = _request.get_state()
+
+        return states
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    @lock
+    def delete(self, **kwargs):
+        """
+        Remove all the finished requests
+        """
+        num_requests = len(module.instance.requests)
+
+        module.instance.requests = filter(
+            lambda x: not x.is_finished(),
+            module.instance.requests
+        )
+
+        # Return the job statistics
+        return {
+            'cleaned': num_requests - len(module.instance.requests),
+            'remaining': len(module.instance.requests),
+        }
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def post(self, **kwargs):
+        """
+        Pass through method to create any request
+        """
+        return module.instance.submit_request([[request.json]], **kwargs)
+
+
+    @expose()
+    def _lookup(self, request_id, *remainder):
+        return RequestId(request_id), remainder
diff --git a/src/pybind/mgr/restful/api/server.py b/src/pybind/mgr/restful/api/server.py
new file mode 100644 (file)
index 0000000..99bf22b
--- /dev/null
@@ -0,0 +1,37 @@
+from pecan import expose
+from pecan.rest import RestController
+
+from restful import module
+from restful.decorators import auth, catch
+
+
+class ServerFqdn(RestController):
+    def __init__(self, fqdn):
+        self.fqdn = fqdn
+
+
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show the information for the server fqdn
+        """
+        return module.instance.get_server(self.fqdn)
+
+
+
+class Server(RestController):
+    @expose(template='json')
+    @catch
+    @auth
+    def get(self, **kwargs):
+        """
+        Show the information for all the servers
+        """
+        return module.instance.list_servers()
+
+
+    @expose()
+    def _lookup(self, fqdn, *remainder):
+        return ServerFqdn(fqdn), remainder