from pecan.rest import RestController
import common
-import traceback
-from base64 import b64decode
-from functools import wraps
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
import module
-# Helper function to catch and log the exceptions
-def catch(f):
- @wraps(f)
- def catcher(*args, **kwargs):
- try:
- return f(*args, **kwargs)
- except:
- module.instance.log.error(str(traceback.format_exc()))
- response.status = 500
- return {'message': str(traceback.format_exc()).split('\n')}
- return catcher
-
-
-# Handle authorization
-def auth(f):
- @wraps(f)
- def decorated(*args, **kwargs):
- if not request.authorization:
- response.status = 401
- response.headers['WWW-Authenticate'] = 'Basic realm="Login Required"'
- return {'message': 'auth: No HTTP username/password'}
-
- username, password = b64decode(request.authorization[1]).split(':')
-
- # Check that the username exists
- if username not in module.instance.keys:
- response.status = 401
- response.headers['WWW-Authenticate'] = 'Basic realm="Login Required"'
- return {'message': 'auth: No such user'}
-
- # Check the password
- if module.instance.keys[username] != password:
- response.status = 401
- response.headers['WWW-Authenticate'] = 'Basic realm="Login Required"'
- return {'message': 'auth: Incorrect password'}
-
- return f(*args, **kwargs)
- return decorated
-
-
-# Helper function to lock the function
-def lock(f):
- @wraps(f)
- def locker(*args, **kwargs):
- with module.instance.requests_lock:
- return f(*args, **kwargs)
- return locker
-
-
-
class ServerFqdn(RestController):
def __init__(self, fqdn):
self.fqdn = fqdn
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show the information for the server fqdn
"""
class Server(RestController):
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show the information for all the servers
"""
self.request_id = request_id
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show the information for the request id
"""
return {'message': 'Unknown request id "%s"' % str(self.request_id)}
request = request[0]
- return request.humanify()
+ return request
- @expose('json')
+ @expose(template='json')
@catch
@auth
@lock
- def delete(self):
+ 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).humanify()
+ return module.instance.requests.pop(index)
# Failed to find the job to cancel
response.status = 500
class Request(RestController):
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
List all the available requests and their state
"""
return states
- @expose('json')
+ @expose(template='json')
@catch
@auth
@lock
- def delete(self):
+ def delete(self, **kwargs):
"""
Remove all the finished 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
self.pool_id = pool_id
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show the information for the pool id
"""
return pool
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def patch(self):
+ def patch(self, **kwargs):
"""
Modify the information for the pool id
"""
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))
+ return module.instance.submit_request(common.pool_update_commands(pool['pool_name'], args), **kwargs)
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def delete(self):
+ def delete(self, **kwargs):
"""
Remove the pool data for the pool id
"""
'pool': pool['pool_name'],
'pool2': pool['pool_name'],
'sure': '--yes-i-really-really-mean-it'
- }]])
+ }]], **kwargs)
class Pool(RestController):
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show the information for all the pools
"""
return pools
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def post(self):
+ def post(self, **kwargs):
"""
Create a new pool
Requires name and pg_num dict arguments
# Schedule the creation and update requests
return module.instance.submit_request(
[[create_command]] +
- common.pool_update_commands(pool_name, args)
+ common.pool_update_commands(pool_name, args),
+ **kwargs
)
self.osd_id = osd_id
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show implemented commands for the OSD id
"""
return []
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def post(self):
+ def post(self, **kwargs):
"""
Run the implemented command for the OSD id
"""
return module.instance.submit_request([[{
'prefix': 'osd ' + command,
'who': str(self.osd_id)
- }]])
+ }]], **kwargs)
self.command = OsdIdCommand(osd_id)
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show the information for the OSD id
"""
- osd = module.instance.get_osds([str(self.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('json')
+ @expose(template='json')
@catch
@auth
- def patch(self):
+ def patch(self, **kwargs):
"""
Modify the state (up, in) of the OSD id or reweight it
"""
'weight': args['reweight']
})
- return module.instance.submit_request([commands])
+ return module.instance.submit_request([commands], **kwargs)
class Osd(RestController):
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show the information for all the OSDs
"""
# Parse request args
- ids = request.GET.getall('id[]')
- pool_id = request.GET.get('pool', None)
+ # TODO Filter by ids
+ pool_id = kwargs.get('pool', None)
- return module.instance.get_osds(ids, pool_id)
+ return module.instance.get_osds(pool_id)
@expose()
self.name = name
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show the information for the monitor name
"""
class Mon(RestController):
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show the information for all the monitors
"""
class Doc(RestController):
- @expose('json')
+ @expose(template='json')
@catch
- def get(self):
+ def get(self, **kwargs):
"""
Show documentation information
"""
class CrushRuleset(RestController):
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show crush rulesets
"""
class CrushRule(RestController):
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show crush rules
"""
class ConfigOsd(RestController):
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show OSD configuration options
"""
return flags.split(',')
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def patch(self):
+ def patch(self, **kwargs):
"""
Modify OSD configration options
"""
-
args = request.json
commands = []
'key': flag,
})
- return module.instance.submit_request([commands])
+ return module.instance.submit_request([commands], **kwargs)
self.key = key
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show specific configuration option
"""
class ConfigCluster(RestController):
- @expose('json')
+ @expose(template='json')
@catch
@auth
- def get(self):
+ def get(self, **kwargs):
"""
Show all cluster configuration options
"""
request = Request()
server = Server()
- @expose('json')
+ @expose(template='json')
@catch
- def get(self):
+ def get(self, **kwargs):
"""
Show the basic information for the REST API
This includes values like api version or auth method
"""
import json
+import time
import errno
import inspect
-import StringIO
import threading
import traceback
-import ConfigParser
import common
from uuid import uuid4
from pecan import jsonify, make_app
-from OpenSSL import SSL, crypto
+from OpenSSL import crypto
+from tempfile import NamedTemporaryFile
from pecan.rest import RestController
from werkzeug.serving import make_server
return "success"
- def humanify(self):
+ def __json__(self):
return {
'id': self.id,
'running': map(
class Module(MgrModule):
COMMANDS = [
- {
- "cmd": "create_key "
- "name=key_name,type=CephString",
- "desc": "Create an API key with this name",
- "perm": "rw"
- },
- {
- "cmd": "delete_key "
- "name=key_name,type=CephString",
- "desc": "Delete an API key with this name",
- "perm": "rw"
- },
- {
- "cmd": "list_keys",
- "desc": "List all API keys",
- "perm": "rw"
- },
+ {
+ "cmd": "create_key name=key_name,type=CephString",
+ "desc": "Create an API key with this name",
+ "perm": "rw"
+ },
+ {
+ "cmd": "delete_key name=key_name,type=CephString",
+ "desc": "Delete an API key with this name",
+ "perm": "rw"
+ },
+ {
+ "cmd": "list_keys",
+ "desc": "List all API keys",
+ "perm": "rw"
+ },
]
def __init__(self, *args, **kwargs):
self.keys = {}
self.disable_auth = False
- self.shutdown_key = str(uuid4())
-
self.server = None
+ self.cert_file = None
+ self.pkey_file = None
+
def serve(self):
try:
self.set_config_json('cert', self.cert)
self.set_config_json('pkey', self.pkey)
- # use SSL context for https
- context = SSL.Context(SSL.TLSv1_METHOD)
- context.use_certificate(
- crypto.load_certificate(crypto.FILETYPE_PEM, self.cert)
- )
- context.use_privatekey(
- crypto.load_privatekey(crypto.FILETYPE_PEM, self.pkey)
- )
+ self.cert_file = NamedTemporaryFile()
+ self.cert_file.write(self.cert)
+ self.cert_file.flush()
+
+ self.pkey_file = NamedTemporaryFile()
+ self.pkey_file.write(self.pkey)
+ self.pkey_file.flush()
# Create the HTTPS werkzeug server serving pecan app
self.server = make_server(
host='0.0.0.0',
port=8002,
app=make_app('restful.api.Root'),
- ssl_context=context
+ ssl_context=(self.cert_file.name, self.pkey_file.name),
)
self.server.serve_forever()
def shutdown(self):
try:
- self.server.shutdown()
+ if self.server:
+ self.server.shutdown()
+ if self.cert_file:
+ self.cert_file.close()
+ if self.pkey_file:
+ self.pkey_file.close()
except:
self.log.error(str(traceback.format_exc()))
return (
-errno.EINVAL,
"",
- "Command not found '{0}'".format(prefix)
+ "Command not found '{0}'".format(command['prefix'])
)
return osds
- def get_osds(self, ids=[], pool_id=None):
+ def get_osds(self, pool_id=None, ids=None):
# Get data
osd_map = self.get('osd_map')
osd_metadata = self.get('osd_metadata')
osds = osd_map['osds']
# Filter by osd ids
- if ids:
+ if ids is not None:
osds = filter(
lambda x: str(x['osd']) in ids,
osds
return pool[0]
- def submit_request(self, _request):
+ def submit_request(self, _request, **kwargs):
request = CommandsRequest(_request)
- self.requests.append(request)
- return request.humanify()
+ with self.requests_lock:
+ self.requests.append(request)
+ if kwargs.get('wait', 0):
+ while not request.is_finished():
+ time.sleep(0.001)
+ return request
def run_command(self, command):