From 8e1b74aa1c5a2f9113473eedc8e35b38b41445ea Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Mon, 27 Dec 2010 15:15:24 -0800 Subject: Added stack command-line tool --- nova/api/easy.py | 57 +++++++++++++++++++++++++++++++++------------ nova/compute/api.py | 6 +++++ nova/tests/easy_unittest.py | 6 ++--- nova/utils.py | 2 +- nova/wsgi.py | 3 +-- 5 files changed, 53 insertions(+), 21 deletions(-) (limited to 'nova') diff --git a/nova/api/easy.py b/nova/api/easy.py index 0e4f8a892..7468e3115 100644 --- a/nova/api/easy.py +++ b/nova/api/easy.py @@ -25,7 +25,7 @@ The general flow of a request is: (/controller/method) - Parameters are parsed from the request and passed to a method on the controller as keyword arguments. - - Optionally json_body is decoded to provide all the parameters. + - 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. @@ -94,7 +94,7 @@ 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) @@ -103,14 +103,18 @@ class SundayMorning(wsgi.Router): 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 @@ -120,40 +124,63 @@ class Reflection(object): # bunch of ugly formatting stuff argspec = inspect.getargspec(f) - args = [x for x in argspec[0] if x != 'self' and x != 'context'] + 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), defaults_r.pop(0))) + args_out.append((args_r.pop(0), + repr(defaults_r.pop(0)))) else: - args_out.append(str(args_r.pop(0))) + 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))} - return methods + + 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._methods = self._gather_methods() + self._gather_methods() method_list = self._methods.keys() method_list.sort() - return {'methods': method_list} + 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._methods = self._gather_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] @@ -165,10 +192,10 @@ class ServiceWrapper(wsgi.Controller): 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) @@ -181,7 +208,7 @@ class Proxy(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' @@ -196,7 +223,7 @@ class Proxy(object): 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, diff --git a/nova/compute/api.py b/nova/compute/api.py index 5f18539a3..005ed7a68 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -40,6 +40,7 @@ def id_to_default_hostname(internal_id): """Default function to generate a hostname given an instance reference.""" return str(internal_id) + def id_to_ec2_hostname(internal_id): digits = [] while internal_id != 0: @@ -47,9 +48,11 @@ def id_to_ec2_hostname(internal_id): digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder]) return "i-%s" % ''.join(reversed(digits)) + HOSTNAME_FORMATTERS = {'default': id_to_default_hostname, 'ec2': id_to_ec2_hostname} + class ComputeAPI(base.Base): """API for interacting with the compute manager.""" @@ -63,6 +66,7 @@ class ComputeAPI(base.Base): super(ComputeAPI, self).__init__(**kwargs) def get_network_topic(self, context, instance_id): + """Get the network topic for an instance.""" try: instance = self.db.instance_get_by_internal_id(context, instance_id) @@ -221,6 +225,7 @@ class ComputeAPI(base.Base): return self.db.instance_update(context, instance_id, kwargs) def delete_instance(self, context, instance_id): + """Terminate and remove an instance.""" logging.debug("Going to try and terminate %d" % instance_id) try: instance = self.db.instance_get_by_internal_id(context, @@ -264,6 +269,7 @@ class ComputeAPI(base.Base): return self.db.instance_get_all(context) def get_instance(self, context, instance_id): + """Get information about a specific instance.""" rv = self.db.instance_get_by_internal_id(context, instance_id) return dict(rv.iteritems()) diff --git a/nova/tests/easy_unittest.py b/nova/tests/easy_unittest.py index 81990d842..cd13c7710 100644 --- a/nova/tests/easy_unittest.py +++ b/nova/tests/easy_unittest.py @@ -31,6 +31,7 @@ from nova.api import easy from nova.compute import api as compute_api from nova.tests import cloud_unittest + class FakeService(object): def echo(self, context, data): return {'data': data} @@ -49,7 +50,7 @@ class EasyTestCase(test.TestCase): easy.SundayMorning())) self.auth_router = easy.DelegatedAuthMiddleware(self.router) self.context = context.RequestContext('user1', 'proj1') - + def tearDown(self): easy.EASY_ROUTES = {} @@ -61,7 +62,7 @@ class EasyTestCase(test.TestCase): data = json.loads(resp.body) self.assertEqual(data['user'], 'user1') self.assertEqual(data['project'], 'proj1') - + def test_json_params(self): req = webob.Request.blank('/fake/echo') req.environ['openstack.context'] = self.context @@ -99,4 +100,3 @@ class EasyCloudTestCase(cloud_unittest.CloudTestCase): def tearDown(self): super(EasyCloudTestCase, self).tearDown() easy.EASY_ROUTES = {} - diff --git a/nova/utils.py b/nova/utils.py index 7a98ffa5a..337924f10 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -386,7 +386,7 @@ def dumps(value): return json.dumps(value) except TypeError: pass - + return json.dumps(to_primitive(value)) diff --git a/nova/wsgi.py b/nova/wsgi.py index c40f043f9..564805ae7 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -105,8 +105,7 @@ class Application(object): class Middleware(Application): """Base WSGI middleware. - - Modelled after Django's middleware this class allows you to + These classes require an application to be initialized that will be called next. By default the middleware will simply call its wrapped app, or you can override __call__ to customize its -- cgit