]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Switch controller to actually call the plugins
authorKevin Benton <blak111@gmail.com>
Thu, 11 Jun 2015 14:35:48 +0000 (07:35 -0700)
committerKevin Benton <kevinbenton@buttewifi.com>
Sat, 1 Aug 2015 18:57:50 +0000 (18:57 +0000)
This swaps out the controller methods to actually dispatch the
requests to the plugins and return the result. After this patch
merges, pecan should serve as a minimally working API server for
development purposes.

TODOs:
* AMQP server - this will not listen for AMQP messages
* Cleanup policy and context modules - they only serve a happy path
* Implement the notifiers
* Move the functional tests into an appropriate location
* Bulk operations
* Address any other of the TODOs in the code

Partially-Implements: blueprint wsgi-pecan-switch
Co-Authored-By: Brandon Logan <brandon.logan@rackspace.com>
Change-Id: If5dc76a2974d13d45f0cc88419bcccb3332576cf

neutron/newapi/controllers/root.py
neutron/tests/functional/newapi/test_functional.py

index 4185080f5c4fe9443993dbbea3408e08d4856216..3426d9c38db3990d813342f671031f39ba7cc1aa 100644 (file)
@@ -15,6 +15,7 @@
 #    under the License.
 
 import pecan
+from pecan import request
 
 from neutron.api import extensions
 
@@ -52,7 +53,7 @@ class V2Controller(object):
     def _lookup(self, endpoint, *remainder):
         if endpoint == 'extensions':
             return ExtensionsController(), remainder
-        return GeneralController(endpoint), remainder
+        return CollectionsController(endpoint), remainder
 
 
 class ExtensionsController(object):
@@ -85,30 +86,57 @@ class ExtensionController(object):
         return {'extension': extensions.ExtensionController._translate(ext)}
 
 
-class GeneralController(object):
+class CollectionsController(object):
 
-    def __init__(self, token):
-        self.token = token
+    def __init__(self, collection):
+        self.collection = collection
 
     @expose()
-    def _lookup(self, token, *remainder):
-        return GeneralController(token), remainder
+    def _lookup(self, item, *remainder):
+        return ItemController(item), remainder
 
     @expose(generic=True)
-    def index(self):
+    def index(self, *args, **kwargs):
+        return self.get(*args, **kwargs)
+
+    def get(self, *args, **kwargs):
+        # list request
+        # TODO(kevinbenton): allow fields after policy enforced fields present
+        kwargs.pop('fields', None)
+        _listify = lambda x: x if isinstance(x, list) else [x]
+        filters = {k: _listify(v) for k, v in kwargs.items()}
+        lister = getattr(request.plugin, 'get_%s' % self.collection)
+        return {self.collection: lister(request.context, filters=filters)}
+
+    @when(index, method='POST')
+    def post(self, *args, **kwargs):
+        # TODO(kevinbenton): bulk!
+        creator = getattr(request.plugin, 'create_%s' % request.resource_type)
+        return {request.resource_type: creator(request.context,
+                                               request.prepared_data)}
+
+
+class ItemController(object):
+
+    def __init__(self, item):
+        self.item = item
+
+    @expose(generic=True)
+    def index(self, *args, **kwargs):
         return self.get()
 
-    def get(self):
-        return {'message': 'GET'}
+    def get(self, *args, **kwargs):
+        getter = getattr(request.plugin, 'get_%s' % request.resource_type)
+        return {request.resource_type: getter(request.context, self.item)}
 
     @when(index, method='PUT')
-    def put(self, **kw):
-        return {'message': 'PUT'}
-
-    @when(index, method='POST')
-    def post(self, **kw):
-        return {'message': 'POST'}
+    def put(self, *args, **kwargs):
+        # TODO(kevinbenton): bulk?
+        updater = getattr(request.plugin, 'update_%s' % request.resource_type)
+        return updater(request.context, self.item, request.prepared_data)
 
     @when(index, method='DELETE')
     def delete(self):
-        return {'message': 'DELETE'}
+        # TODO(kevinbenton): bulk?
+        deleter = getattr(request.plugin, 'delete_%s' % request.resource_type)
+        return deleter(request.context, self.item)
index e4e4506d2aae7943d9eef23119e16c0360514d1b..458be23ac1e962755d2b80d6f368cfd5c251582a 100644 (file)
@@ -85,7 +85,8 @@ class TestV2Controller(PecanFunctionalTest):
         self.assertEqual(response.status_int, 200)
 
     def test_delete(self):
-        response = self.app.delete('/v2.0/ports/%s.json' % self.port['id'])
+        response = self.app.delete('/v2.0/ports/%s.json' % self.port['id'],
+                                   headers={'X-Tenant-Id': 'tenid'})
         self.assertEqual(response.status_int, 200)
 
     def test_plugin_initialized(self):
@@ -151,14 +152,14 @@ class TestExceptionTranslationHook(PecanFunctionalTest):
         # this endpoint raises a Neutron notfound exception. make sure it gets
         # translated into a 404 error
         with mock.patch(
-                'neutron.newapi.controllers.root.GeneralController.get',
+                'neutron.newapi.controllers.root.CollectionsController.get',
                 side_effect=n_exc.NotFound()):
             response = self.app.get('/v2.0/ports.json', expect_errors=True)
             self.assertEqual(response.status_int, 404)
 
     def test_unexpected_exception(self):
         with mock.patch(
-                'neutron.newapi.controllers.root.GeneralController.get',
+                'neutron.newapi.controllers.root.CollectionsController.get',
                 side_effect=ValueError('secretpassword')):
             response = self.app.get('/v2.0/ports.json', expect_errors=True)
             self.assertNotIn(response.body, 'secretpassword')
@@ -181,7 +182,7 @@ class TestRequestPopulatingHooks(PecanFunctionalTest):
                 'plugin': request.plugin
             }
         mock.patch(
-            'neutron.newapi.controllers.root.GeneralController.get',
+            'neutron.newapi.controllers.root.CollectionsController.get',
             side_effect=capture_request_details
         ).start()