diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index a9e6fe850a2c..55f537741787 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -15,7 +15,6 @@ # under the License. import functools -import inspect import math import time @@ -545,90 +544,25 @@ class Resource(wsgi.Application): def deserialize(self, body): return JSONDeserializer().deserialize(body) - # NOTE(sdague): I didn't start the fire, however here is what all - # of this is about. - # - # In the legacy v2 code stack, extensions could extend actions - # with a generator that let 1 method be split into a top and - # bottom half. The top half gets executed before the main - # processing of the request (so effectively gets to modify the - # request before it gets to the main method). - # - # Returning a response triggers a shortcut to fail out. The - # response will nearly always be a failure condition, as it ends - # up skipping further processing one level up from here. - # - # This then passes on the list of extensions, in reverse order, - # on. post_process will run through all those, again with same - # basic logic. - # - # In tree this is only used in the legacy v2 stack, and only in - # the DiskConfig and SchedulerHints from what I can see. - # - # pre_process_extensions can be removed when the legacyv2 code - # goes away. post_process_extensions can be massively simplified - # at that point. - def pre_process_extensions(self, extensions, request, action_args): - # List of callables for post-processing extensions - post = [] - - for ext in extensions: - if inspect.isgeneratorfunction(ext): - response = None - - # If it's a generator function, the part before the - # yield is the preprocessing stage - try: - with ResourceExceptionHandler(): - gen = ext(req=request, **action_args) - response = next(gen) - except Fault as ex: - response = ex - - # We had a response... - if response: - return response, [] - - # No response, queue up generator for post-processing - post.append(gen) - else: - # Regular functions only perform post-processing - post.append(ext) - - # None is response, it means we keep going. We reverse the - # extension list for post-processing. - return None, reversed(post) - - def post_process_extensions(self, extensions, resp_obj, request, - action_args): + def process_extensions(self, extensions, resp_obj, request, + action_args): for ext in extensions: response = None - if inspect.isgenerator(ext): - # If it's a generator, run the second half of - # processing - try: - with ResourceExceptionHandler(): - response = ext.send(resp_obj) - except StopIteration: - # Normal exit of generator - continue - except Fault as ex: - response = ex - else: - # Regular functions get post-processing... - try: - with ResourceExceptionHandler(): - response = ext(req=request, resp_obj=resp_obj, - **action_args) - except exception.VersionNotFoundForAPIMethod: - # If an attached extension (@wsgi.extends) for the - # method has no version match its not an error. We - # just don't run the extends code - continue - except Fault as ex: - response = ex + # Regular functions get post-processing... + try: + with ResourceExceptionHandler(): + response = ext(req=request, resp_obj=resp_obj, + **action_args) + except exception.VersionNotFoundForAPIMethod: + # If an attached extension (@wsgi.extends) for the + # method has no version match its not an error. We + # just don't run the extends code + continue + except Fault as ex: + response = ex - # We had a response... + # We had a response return it, to exit early. This is + # actually a failure mode. None is success. if response: return response @@ -727,16 +661,12 @@ class Resource(wsgi.Application): 'context_project_id': context.project_id} return Fault(webob.exc.HTTPBadRequest(explanation=msg)) - # Run pre-processing extensions - response, post = self.pre_process_extensions(extensions, - request, action_args) - - if not response: - try: - with ResourceExceptionHandler(): - action_result = self.dispatch(meth, request, action_args) - except Fault as ex: - response = ex + response = None + try: + with ResourceExceptionHandler(): + action_result = self.dispatch(meth, request, action_args) + except Fault as ex: + response = ex if not response: # No exceptions; convert action_result into a @@ -754,8 +684,8 @@ class Resource(wsgi.Application): # Do a preserialize to set up the response object if hasattr(meth, 'wsgi_code'): resp_obj._default_code = meth.wsgi_code - # Process post-processing extensions - response = self.post_process_extensions(post, resp_obj, + # Process extensions + response = self.process_extensions(extensions, resp_obj, request, action_args) if resp_obj and not response: diff --git a/nova/tests/unit/api/openstack/test_wsgi.py b/nova/tests/unit/api/openstack/test_wsgi.py index ff3c036d6ac5..491a6b586706 100644 --- a/nova/tests/unit/api/openstack/test_wsgi.py +++ b/nova/tests/unit/api/openstack/test_wsgi.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import inspect - import mock import six import testscenarios @@ -785,7 +783,7 @@ class ResourceTest(MicroversionedTest): self.assertEqual(method, extended._delete) self.assertEqual(extensions, []) - def test_pre_process_extensions_regular(self): + def test_process_extensions_regular(self): class Controller(object): def index(self, req, pants=None): return pants @@ -803,96 +801,12 @@ class ResourceTest(MicroversionedTest): called.append(2) return None - extensions = [extension1, extension2] - response, post = resource.pre_process_extensions(extensions, None, {}) - self.assertEqual(called, []) - self.assertIsNone(response) - self.assertEqual(list(post), [extension2, extension1]) - - def test_pre_process_extensions_generator(self): - class Controller(object): - def index(self, req, pants=None): - return pants - - controller = Controller() - resource = wsgi.Resource(controller) - - called = [] - - def extension1(req): - called.append('pre1') - yield - called.append('post1') - - def extension2(req): - called.append('pre2') - yield - called.append('post2') - - extensions = [extension1, extension2] - response, post = resource.pre_process_extensions(extensions, None, {}) - post = list(post) - self.assertEqual(called, ['pre1', 'pre2']) - self.assertIsNone(response) - self.assertEqual(len(post), 2) - self.assertTrue(inspect.isgenerator(post[0])) - self.assertTrue(inspect.isgenerator(post[1])) - - for gen in post: - try: - gen.send(None) - except StopIteration: - continue - - self.assertEqual(called, ['pre1', 'pre2', 'post2', 'post1']) - - def test_pre_process_extensions_generator_response(self): - class Controller(object): - def index(self, req, pants=None): - return pants - - controller = Controller() - resource = wsgi.Resource(controller) - - called = [] - - def extension1(req): - called.append('pre1') - yield 'foo' - - def extension2(req): - called.append('pre2') - - extensions = [extension1, extension2] - response, post = resource.pre_process_extensions(extensions, None, {}) - self.assertEqual(called, ['pre1']) - self.assertEqual(response, 'foo') - self.assertEqual(post, []) - - def test_post_process_extensions_regular(self): - class Controller(object): - def index(self, req, pants=None): - return pants - - controller = Controller() - resource = wsgi.Resource(controller) - - called = [] - - def extension1(req, resp_obj): - called.append(1) - return None - - def extension2(req, resp_obj): - called.append(2) - return None - - response = resource.post_process_extensions([extension2, extension1], + response = resource.process_extensions([extension2, extension1], None, None, {}) self.assertEqual(called, [2, 1]) self.assertIsNone(response) - def test_post_process_extensions_regular_response(self): + def test_process_extensions_regular_response(self): class Controller(object): def index(self, req, pants=None): return pants @@ -910,70 +824,11 @@ class ResourceTest(MicroversionedTest): called.append(2) return 'foo' - response = resource.post_process_extensions([extension2, extension1], + response = resource.process_extensions([extension2, extension1], None, None, {}) self.assertEqual(called, [2]) self.assertEqual(response, 'foo') - def test_post_process_extensions_generator(self): - class Controller(object): - def index(self, req, pants=None): - return pants - - controller = Controller() - resource = wsgi.Resource(controller) - - called = [] - - def extension1(req): - yield - called.append(1) - - def extension2(req): - yield - called.append(2) - - ext1 = extension1(None) - next(ext1) - ext2 = extension2(None) - next(ext2) - - response = resource.post_process_extensions([ext2, ext1], - None, None, {}) - - self.assertEqual(called, [2, 1]) - self.assertIsNone(response) - - def test_post_process_extensions_generator_response(self): - class Controller(object): - def index(self, req, pants=None): - return pants - - controller = Controller() - resource = wsgi.Resource(controller) - - called = [] - - def extension1(req): - yield - called.append(1) - - def extension2(req): - yield - called.append(2) - yield 'foo' - - ext1 = extension1(None) - next(ext1) - ext2 = extension2(None) - next(ext2) - - response = resource.post_process_extensions([ext2, ext1], - None, None, {}) - - self.assertEqual(called, [2]) - self.assertEqual(response, 'foo') - def test_resource_exception_handler_type_error(self): # A TypeError should be translated to a Fault/HTTP 400. def foo(a,):