From: Boris Ranto Date: Tue, 9 May 2017 08:52:15 +0000 (+0200) Subject: restful: Split api.py file into a module X-Git-Tag: ses5-milestone6~9^2~47^2~10 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=21af9555739433ca5db656814bf846d0536e01b8;p=ceph.git restful: Split api.py file into a module The patch makes the REST api being served by a module, not a single monolithic file. Signed-off-by: Boris Ranto --- diff --git a/src/pybind/mgr/restful/api.py b/src/pybind/mgr/restful/api.py deleted file mode 100644 index 10de13de7e5..00000000000 --- a/src/pybind/mgr/restful/api.py +++ /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 index 00000000000..7a3b71e955a --- /dev/null +++ b/src/pybind/mgr/restful/api/__init__.py @@ -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 index 00000000000..cca6a472829 --- /dev/null +++ b/src/pybind/mgr/restful/api/config.py @@ -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 index 00000000000..14338d6b566 --- /dev/null +++ b/src/pybind/mgr/restful/api/crush.py @@ -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 index 00000000000..3ccd99b9df8 --- /dev/null +++ b/src/pybind/mgr/restful/api/doc.py @@ -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 index 00000000000..c47e75335cb --- /dev/null +++ b/src/pybind/mgr/restful/api/mon.py @@ -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 index 00000000000..d962031d9ef --- /dev/null +++ b/src/pybind/mgr/restful/api/osd.py @@ -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 index 00000000000..dca450ee501 --- /dev/null +++ b/src/pybind/mgr/restful/api/pool.py @@ -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 index 00000000000..ddf422bba2c --- /dev/null +++ b/src/pybind/mgr/restful/api/request.py @@ -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 index 00000000000..99bf22bc52c --- /dev/null +++ b/src/pybind/mgr/restful/api/server.py @@ -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