From 35d3050511ef513ff440fbd9f8b44695ea8be797 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Tue, 4 Jan 2011 14:07:46 -0800 Subject: rename Easy API to Direct API --- nova/api/direct.py | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++ nova/api/easy.py | 232 ----------------------------------------------------- 2 files changed, 232 insertions(+), 232 deletions(-) create mode 100644 nova/api/direct.py delete mode 100644 nova/api/easy.py (limited to 'nova/api') diff --git a/nova/api/direct.py b/nova/api/direct.py new file mode 100644 index 000000000..81b3ae202 --- /dev/null +++ b/nova/api/direct.py @@ -0,0 +1,232 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Public HTTP interface that allows services to self-register. + +The general flow of a request is: + - Request is parsed into WSGI bits. + - Some middleware checks authentication. + - Routing takes place based on the URL to find a controller. + (/controller/method) + - Parameters are parsed from the request and passed to a method on the + controller as keyword arguments. + - Optionally 'json' is decoded to provide all the parameters. + - Actual work is done and a result is returned. + - That result is turned into json and returned. + +""" + +import inspect +import urllib + +import routes +import webob + +from nova import context +from nova import flags +from nova import utils +from nova import wsgi + + +ROUTES = {} + + +def register_service(path, handle): + ROUTES[path] = handle + + +class Router(wsgi.Router): + def __init__(self, mapper=None): + if mapper is None: + mapper = routes.Mapper() + + self._load_registered_routes(mapper) + super(Router, self).__init__(mapper=mapper) + + def _load_registered_routes(self, mapper): + for route in ROUTES: + mapper.connect('/%s/{action}' % route, + controller=ServiceWrapper(ROUTES[route])) + + +class DelegatedAuthMiddleware(wsgi.Middleware): + def process_request(self, request): + os_user = request.headers['X-OpenStack-User'] + os_project = request.headers['X-OpenStack-Project'] + context_ref = context.RequestContext(user=os_user, project=os_project) + request.environ['openstack.context'] = context_ref + + +class JsonParamsMiddleware(wsgi.Middleware): + def process_request(self, request): + if 'json' not in request.params: + return + + params_json = request.params['json'] + params_parsed = utils.loads(params_json) + params = {} + for k, v in params_parsed.iteritems(): + if k in ('self', 'context'): + continue + if k.startswith('_'): + continue + params[k] = v + + request.environ['openstack.params'] = params + + +class PostParamsMiddleware(wsgi.Middleware): + def process_request(self, request): + params_parsed = request.params + params = {} + for k, v in params_parsed.iteritems(): + if k in ('self', 'context'): + continue + if k.startswith('_'): + continue + params[k] = v + + request.environ['openstack.params'] = params + + +class Reflection(object): + """Reflection methods to list available methods.""" + def __init__(self): + self._methods = {} + self._controllers = {} + + def _gather_methods(self): + methods = {} + controllers = {} + for route, handler in ROUTES.iteritems(): + controllers[route] = handler.__doc__.split('\n')[0] + for k in dir(handler): + if k.startswith('_'): + continue + f = getattr(handler, k) + if not callable(f): + continue + + # bunch of ugly formatting stuff + argspec = inspect.getargspec(f) + args = [x for x in argspec[0] + if x != 'self' and x != 'context'] + defaults = argspec[3] and argspec[3] or [] + args_r = list(reversed(args)) + defaults_r = list(reversed(defaults)) + + args_out = [] + while args_r: + if defaults_r: + args_out.append((args_r.pop(0), + repr(defaults_r.pop(0)))) + else: + args_out.append((str(args_r.pop(0)),)) + + # if the method accepts keywords + if argspec[2]: + args_out.insert(0, ('**%s' % argspec[2],)) + + methods['/%s/%s' % (route, k)] = { + 'short_doc': f.__doc__.split('\n')[0], + 'doc': f.__doc__, + 'name': k, + 'args': list(reversed(args_out))} + + self._methods = methods + self._controllers = controllers + + def get_controllers(self, context): + """List available controllers.""" + if not self._controllers: + self._gather_methods() + + return self._controllers + + def get_methods(self, context): + """List available methods.""" + if not self._methods: + self._gather_methods() + + method_list = self._methods.keys() + method_list.sort() + methods = {} + for k in method_list: + methods[k] = self._methods[k]['short_doc'] + return methods + + def get_method_info(self, context, method): + """Get detailed information about a method.""" + if not self._methods: + self._gather_methods() + return self._methods[method] + + +class ServiceWrapper(wsgi.Controller): + def __init__(self, service_handle): + self.service_handle = service_handle + + @webob.dec.wsgify + def __call__(self, req): + arg_dict = req.environ['wsgiorg.routing_args'][1] + action = arg_dict['action'] + del arg_dict['action'] + + context = req.environ['openstack.context'] + # allow middleware up the stack to override the params + params = {} + if 'openstack.params' in req.environ: + params = req.environ['openstack.params'] + + # TODO(termie): do some basic normalization on methods + method = getattr(self.service_handle, action) + + result = method(context, **params) + if type(result) is dict or type(result) is list: + return self._serialize(result, req) + else: + return result + + +class Proxy(object): + """Pretend a Direct API endpoint is an object.""" + def __init__(self, app, prefix=None): + self.app = app + self.prefix = prefix + + def __do_request(self, path, context, **kwargs): + req = webob.Request.blank(path) + req.method = 'POST' + req.body = urllib.urlencode({'json': utils.dumps(kwargs)}) + req.environ['openstack.context'] = context + resp = req.get_response(self.app) + try: + return utils.loads(resp.body) + except Exception: + return resp.body + + def __getattr__(self, key): + if self.prefix is None: + return self.__class__(self.app, prefix=key) + + def _wrapper(context, **kwargs): + return self.__do_request('/%s/%s' % (self.prefix, key), + context, + **kwargs) + _wrapper.func_name = key + return _wrapper diff --git a/nova/api/easy.py b/nova/api/easy.py deleted file mode 100644 index 7468e3115..000000000 --- a/nova/api/easy.py +++ /dev/null @@ -1,232 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Public HTTP interface that allows services to self-register. - -The general flow of a request is: - - Request is parsed into WSGI bits. - - Some middleware checks authentication. - - Routing takes place based on the URL to find a controller. - (/controller/method) - - Parameters are parsed from the request and passed to a method on the - controller as keyword arguments. - - Optionally 'json' is decoded to provide all the parameters. - - Actual work is done and a result is returned. - - That result is turned into json and returned. - -""" - -import inspect -import urllib - -import routes -import webob - -from nova import context -from nova import flags -from nova import utils -from nova import wsgi - - -EASY_ROUTES = {} - - -def register_service(path, handle): - EASY_ROUTES[path] = handle - - -class DelegatedAuthMiddleware(wsgi.Middleware): - def process_request(self, request): - os_user = request.headers['X-OpenStack-User'] - os_project = request.headers['X-OpenStack-Project'] - context_ref = context.RequestContext(user=os_user, project=os_project) - request.environ['openstack.context'] = context_ref - - -class JsonParamsMiddleware(wsgi.Middleware): - def process_request(self, request): - if 'json' not in request.params: - return - - params_json = request.params['json'] - params_parsed = utils.loads(params_json) - params = {} - for k, v in params_parsed.iteritems(): - if k in ('self', 'context'): - continue - if k.startswith('_'): - continue - params[k] = v - - request.environ['openstack.params'] = params - - -class ReqParamsMiddleware(wsgi.Middleware): - def process_request(self, request): - params_parsed = request.params - params = {} - for k, v in params_parsed.iteritems(): - if k in ('self', 'context'): - continue - if k.startswith('_'): - continue - params[k] = v - - request.environ['openstack.params'] = params - - -class SundayMorning(wsgi.Router): - def __init__(self, mapper=None): - if mapper is None: - mapper = routes.Mapper() - - self._load_registered_routes(mapper) - super(SundayMorning, self).__init__(mapper=mapper) - - def _load_registered_routes(self, mapper): - for route in EASY_ROUTES: - mapper.connect('/%s/{action}' % route, - controller=ServiceWrapper(EASY_ROUTES[route])) - - -class Reflection(object): - """Reflection methods to list available methods.""" - def __init__(self): - self._methods = {} - self._controllers = {} - - def _gather_methods(self): - methods = {} - controllers = {} - for route, handler in EASY_ROUTES.iteritems(): - controllers[route] = handler.__doc__.split('\n')[0] - for k in dir(handler): - if k.startswith('_'): - continue - f = getattr(handler, k) - if not callable(f): - continue - - # bunch of ugly formatting stuff - argspec = inspect.getargspec(f) - args = [x for x in argspec[0] - if x != 'self' and x != 'context'] - defaults = argspec[3] and argspec[3] or [] - args_r = list(reversed(args)) - defaults_r = list(reversed(defaults)) - - args_out = [] - while args_r: - if defaults_r: - args_out.append((args_r.pop(0), - repr(defaults_r.pop(0)))) - else: - args_out.append((str(args_r.pop(0)),)) - - # if the method accepts keywords - if argspec[2]: - args_out.insert(0, ('**%s' % argspec[2],)) - - methods['/%s/%s' % (route, k)] = { - 'short_doc': f.__doc__.split('\n')[0], - 'doc': f.__doc__, - 'name': k, - 'args': list(reversed(args_out))} - - self._methods = methods - self._controllers = controllers - - def get_controllers(self, context): - """List available controllers.""" - if not self._controllers: - self._gather_methods() - - return self._controllers - - def get_methods(self, context): - """List available methods.""" - if not self._methods: - self._gather_methods() - - method_list = self._methods.keys() - method_list.sort() - methods = {} - for k in method_list: - methods[k] = self._methods[k]['short_doc'] - return methods - - def get_method_info(self, context, method): - """Get detailed information about a method.""" - if not self._methods: - self._gather_methods() - return self._methods[method] - - -class ServiceWrapper(wsgi.Controller): - def __init__(self, service_handle): - self.service_handle = service_handle - - @webob.dec.wsgify - def __call__(self, req): - arg_dict = req.environ['wsgiorg.routing_args'][1] - action = arg_dict['action'] - del arg_dict['action'] - - context = req.environ['openstack.context'] - # allow middleware up the stack to override the params - params = {} - if 'openstack.params' in req.environ: - params = req.environ['openstack.params'] - - # TODO(termie): do some basic normalization on methods - method = getattr(self.service_handle, action) - - result = method(context, **params) - if type(result) is dict or type(result) is list: - return self._serialize(result, req) - else: - return result - - -class Proxy(object): - """Pretend an Easy API endpoint is an object.""" - def __init__(self, app, prefix=None): - self.app = app - self.prefix = prefix - - def __do_request(self, path, context, **kwargs): - req = webob.Request.blank(path) - req.method = 'POST' - req.body = urllib.urlencode({'json': utils.dumps(kwargs)}) - req.environ['openstack.context'] = context - resp = req.get_response(self.app) - try: - return utils.loads(resp.body) - except Exception: - return resp.body - - def __getattr__(self, key): - if self.prefix is None: - return self.__class__(self.app, prefix=key) - - def _wrapper(context, **kwargs): - return self.__do_request('/%s/%s' % (self.prefix, key), - context, - **kwargs) - _wrapper.func_name = key - return _wrapper -- cgit