Merge "Add retry support to pecan"

This commit is contained in:
Jenkins 2016-05-17 04:35:12 +00:00 committed by Gerrit Code Review
commit f3eb620ebf
2 changed files with 76 additions and 6 deletions

View File

@ -13,27 +13,76 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
import functools
from neutron_lib import constants
import pecan
from pecan import request
from neutron.api.v2 import attributes as api_attributes
from neutron.db import api as db_api
from neutron import manager
# Utility functions for Pecan controllers.
def expose(*args, **kwargs):
class Fakecode(object):
co_varnames = ()
def _composed(*decorators):
"""Takes a list of decorators and returns a single decorator."""
def final_decorator(f):
for d in decorators:
# workaround for pecan bug that always assumes decorators
# have a __code__ attr
if not hasattr(d, '__code__'):
setattr(d, '__code__', Fakecode())
f = d(f)
return f
return final_decorator
def _protect_original_resources(f):
"""Wrapper to ensure that mutated resources are discarded on retries."""
@functools.wraps(f)
def wrapped(*args, **kwargs):
ctx = request.context
if 'resources' in ctx:
orig = ctx.get('protected_resources')
if not orig:
# this is the first call so we just take the whole reference
ctx['protected_resources'] = ctx['resources']
# TODO(blogan): Once bug 157751 is fixed and released in
# neutron-lib this memo will no longer be needed. This is just
# quick way to not depend on a release of neutron-lib.
# The version that has that bug fix will need to be updated in
# neutron-lib.
memo = {id(constants.ATTR_NOT_SPECIFIED):
constants.ATTR_NOT_SPECIFIED}
ctx['resources'] = copy.deepcopy(ctx['protected_resources'],
memo=memo)
return f(*args, **kwargs)
return wrapped
def _pecan_generator_wrapper(func, *args, **kwargs):
"""Helper function so we don't have to specify json for everything."""
kwargs.setdefault('content_type', 'application/json')
kwargs.setdefault('template', 'json')
return pecan.expose(*args, **kwargs)
return _composed(_protect_original_resources, db_api.retry_db_errors,
func(*args, **kwargs))
def expose(*args, **kwargs):
return _pecan_generator_wrapper(pecan.expose, *args, **kwargs)
def when(index, *args, **kwargs):
"""Helper function so we don't have to specify json for everything."""
kwargs.setdefault('content_type', 'application/json')
kwargs.setdefault('template', 'json')
return index.when(*args, **kwargs)
return _pecan_generator_wrapper(index.when, *args, **kwargs)
class NeutronPecanController(object):

View File

@ -15,6 +15,7 @@ from collections import namedtuple
import mock
from neutron_lib import constants as n_const
from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_policy import policy as oslo_policy
from oslo_serialization import jsonutils
import pecan
@ -268,6 +269,26 @@ class TestResourceController(TestRootController):
headers={'X-Project-Id': 'tenid'})
self.assertEqual(response.status_int, 201)
def test_post_with_retry(self):
self._create_failed = False
orig = self.plugin.create_port
def new_create(*args, **kwargs):
if not self._create_failed:
self._create_failed = True
raise db_exc.RetryRequest(ValueError())
return orig(*args, **kwargs)
with mock.patch.object(self.plugin, 'create_port',
new=new_create):
response = self.app.post_json(
'/v2.0/ports.json',
params={'port': {'network_id': self.port['network_id'],
'admin_state_up': True,
'tenant_id': 'tenid'}},
headers={'X-Project-Id': 'tenid'})
self.assertEqual(201, response.status_int)
def test_put(self):
response = self.app.put_json('/v2.0/ports/%s.json' % self.port['id'],
params={'port': {'name': 'test'}},