Merge "Pecan: Find subresource controllers by parent"

This commit is contained in:
Jenkins 2016-11-17 01:10:58 +00:00 committed by Gerrit Code Review
commit de8c390dda
6 changed files with 116 additions and 44 deletions

View File

@ -82,8 +82,9 @@ class ItemController(utils.NeutronPecanController):
@utils.expose()
def _lookup(self, collection, *remainder):
request.context['collection'] = collection
collection_path = '/'.join([self.resource, collection])
controller = manager.NeutronManager.get_controller_for_resource(
collection)
collection_path)
if not controller:
if collection not in self._member_actions:
LOG.warning(_LW("No controller found for: %s - returning"
@ -157,16 +158,17 @@ class CollectionsController(utils.NeutronPecanController):
creator = self.plugin_bulk_creator
key = self.collection
data = {key: [{self.resource: res} for res in resources]}
creator_kwargs = {self.collection: data}
else:
creator = self.plugin_creator
key = self.resource
data = {key: resources[0]}
creator_kwargs = {self.resource: data}
neutron_context = request.context['neutron_context']
creator_args = [neutron_context]
if 'parent_id' in request.context:
creator_args.append(request.context['parent_id'])
creator_args.append(data)
return {key: creator(*creator_args)}
if 'parent_id' in request.context and self._parent_id_name:
creator_kwargs[self._parent_id_name] = request.context['parent_id']
return {key: creator(*creator_args, **creator_kwargs)}
class MemberActionController(ItemController):

View File

@ -127,8 +127,8 @@ class NeutronPecanController(object):
self.parent = parent_resource
parent_resource = '_%s' % parent_resource if parent_resource else ''
self._parent_id_name = ('%s_id' % parent_resource
if parent_resource else None)
self._parent_id_name = ('%s_id' % self.parent
if self.parent else None)
self._plugin_handlers = {
self.LIST: 'get%s_%s' % (parent_resource, self.collection),
self.SHOW: 'get%s_%s' % (parent_resource, self.resource)

View File

@ -18,7 +18,7 @@ from oslo_serialization import jsonutils
from pecan import hooks
from neutron.api.v2 import base as v2_base
from neutron import manager
from neutron.pecan_wsgi.hooks import utils
LOG = log.getLogger(__name__)
@ -51,8 +51,7 @@ class BodyValidationHook(hooks.PecanHook):
# member action is being processed or on agent scheduler operations
return
# Prepare data to be passed to the plugin from request body
controller = manager.NeutronManager.get_controller_for_resource(
collection)
controller = utils.get_controller(state)
data = v2_base.Controller.prepare_request_body(
neutron_context,
json_data,

View File

@ -59,7 +59,18 @@ def initialize_all():
for ext_res in resources:
path_prefix = ext_res.path_prefix.strip('/')
collection = ext_res.collection
if manager.NeutronManager.get_controller_for_resource(collection):
# Retrieving the parent resource. It is expected the format of
# the parent resource to be:
# {'collection_name': 'name-of-collection',
# 'member_name': 'name-of-resource'}
# collection_name does not appear to be used in the legacy code
# inside the controller logic, so we can assume we do not need it.
parent = ext_res.parent or {}
parent_resource = parent.get('member_name')
collection_key = collection
if parent_resource:
collection_key = '/'.join([parent_resource, collection])
if manager.NeutronManager.get_controller_for_resource(collection_key):
# This is a collection that already has a pecan controller, we
# do not need to do anything else
continue
@ -71,14 +82,6 @@ def initialize_all():
plugin = legacy_controller.plugin
attr_info = legacy_controller.attr_info
member_actions = legacy_controller.member_actions
# Retrieving the parent resource. It is expected the format of
# the parent resource to be:
# {'collection_name': 'name-of-collection',
# 'member_name': 'name-of-resource'}
# collection_name does not appear to be used in the legacy code
# inside the controller logic, so we can assume we do not need it.
parent = legacy_controller.parent or {}
parent_resource = parent.get('member_name')
new_controller = res_ctrl.CollectionsController(
collection, resource, resource_info=attr_info,
parent_resource=parent_resource, member_actions=member_actions)
@ -93,7 +96,7 @@ def initialize_all():
LOG.warning(_LW("Unknown controller type encountered %s. It will"
"be ignored."), legacy_controller)
manager.NeutronManager.set_controller_for_resource(
collection, new_controller)
collection_key, new_controller)
# Certain policy checks require that the extensions are loaded
# and the RESOURCE_ATTRIBUTE_MAP populated before they can be

View File

@ -894,3 +894,45 @@ class TestMemberActionController(test_functional.PecanFunctionalTest):
url = '/v2.0/{}/something/put_meh.json'.format(self.collection)
resp = self.app.get(url, expect_errors=True)
self.assertEqual(405, resp.status_int)
class TestParentSubresourceController(test_functional.PecanFunctionalTest):
def setUp(self):
fake_ext = pecan_utils.FakeExtension()
fake_plugin = pecan_utils.FakePlugin()
plugins = {pecan_utils.FakePlugin.PLUGIN_TYPE: fake_plugin}
new_extensions = {fake_ext.get_alias(): fake_ext}
super(TestParentSubresourceController, self).setUp(
service_plugins=plugins, extensions=new_extensions)
policy.init()
policy._ENFORCER.set_rules(
oslo_policy.Rules.from_dict(
{'get_fake_duplicate': '',
'get_meh_meh_fake_duplicates': ''}),
overwrite=False)
self.addCleanup(policy.reset)
hyphen_collection = pecan_utils.FakeExtension.HYPHENATED_COLLECTION
self.collection = hyphen_collection.replace('_', '-')
self.fake_collection = (pecan_utils.FakeExtension.
FAKE_PARENT_SUBRESOURCE_COLLECTION)
def test_get_duplicate_parent_resource(self):
url = '/v2.0/{}'.format(self.fake_collection)
resp = self.app.get(url)
self.assertEqual(200, resp.status_int)
self.assertEqual({'fake_duplicates': [{'fake': 'fakeduplicates'}]},
resp.json)
def test_get_duplicate_parent_resource_item(self):
url = '/v2.0/{}/something'.format(self.fake_collection)
resp = self.app.get(url)
self.assertEqual(200, resp.status_int)
self.assertEqual({'fake_duplicate': {'fake': 'something'}}, resp.json)
def test_get_parent_resource_and_duplicate_subresources(self):
url = '/v2.0/{0}/something/{1}'.format(self.collection,
self.fake_collection)
resp = self.app.get(url)
self.assertEqual(200, resp.status_int)
self.assertEqual({'fake_duplicates': [{'fake': 'something'}]},
resp.json)

View File

@ -109,23 +109,35 @@ class FakeExtension(extensions.ExtensionDescriptor):
HYPHENATED_RESOURCE = 'meh_meh'
HYPHENATED_COLLECTION = HYPHENATED_RESOURCE + 's'
FAKE_PARENT_SUBRESOURCE_COLLECTION = 'fake_duplicates'
FAKE_SUB_RESOURCE_COLLECTION = 'fake_subresources'
RESOURCE_ATTRIBUTE_MAP = {
'meh_mehs': {
'fake': {'is_visible': True}
},
'fake_duplicates': {
'fake': {'is_visible': True}
}
}
SUB_RESOURCE_ATTRIBUTE_MAP = {
'fake_subresources': {
'parent': {
'collection_name': (
HYPHENATED_COLLECTION),
'member_name': HYPHENATED_RESOURCE},
'meh_mehs'),
'member_name': 'meh_meh'},
'parameters': {'foo': {'is_visible': True},
'bar': {'is_visible': True}
}
}
}
FAKE_SUB_RESOURCE_COLLECTION = 'fake_subresources'
RAM = {
HYPHENATED_COLLECTION: {
'fake': {'is_visible': True}
},
'fake_duplicates': {
'parent': {
'collection_name': (
'meh_mehs'),
'member_name': 'meh_meh'},
'parameters': {'fake': {'is_visible': True}
}
}
}
@ -146,21 +158,26 @@ class FakeExtension(extensions.ExtensionDescriptor):
return "meh"
def get_resources(self):
collection = self.HYPHENATED_COLLECTION.replace('_', '-')
params = self.RAM.get(self.HYPHENATED_COLLECTION, {})
attributes.PLURALS.update({self.HYPHENATED_COLLECTION:
self.HYPHENATED_RESOURCE})
member_actions = {'put_meh': 'PUT', 'boo_meh': 'GET'}
"""Returns Ext Resources."""
resources = []
fake_plugin = FakePlugin()
controller = base.create_resource(
collection, self.HYPHENATED_RESOURCE, FakePlugin(),
params, allow_bulk=True, allow_pagination=True,
allow_sorting=True, member_actions=member_actions)
resources = [extensions.ResourceExtension(
collection, controller, attr_map=params,
member_actions=member_actions)]
for collection_name in self.RESOURCE_ATTRIBUTE_MAP:
resource_name = collection_name[:-1]
params = self.RESOURCE_ATTRIBUTE_MAP.get(collection_name, {})
attributes.PLURALS.update({collection_name: resource_name})
member_actions = {'put_meh': 'PUT', 'boo_meh': 'GET'}
if collection_name == self.HYPHENATED_COLLECTION:
collection_name = collection_name.replace('_', '-')
controller = base.create_resource(
collection_name, resource_name, fake_plugin,
params, allow_bulk=True, allow_pagination=True,
allow_sorting=True, member_actions=member_actions)
resource = extensions.ResourceExtension(
collection_name, controller, attr_map=params)
resources.append(resource)
for collection_name in self.SUB_RESOURCE_ATTRIBUTE_MAP:
resource_name = collection_name
resource_name = collection_name[:-1]
parent = self.SUB_RESOURCE_ATTRIBUTE_MAP[collection_name].get(
'parent')
params = self.SUB_RESOURCE_ATTRIBUTE_MAP[collection_name].get(
@ -170,7 +187,6 @@ class FakeExtension(extensions.ExtensionDescriptor):
fake_plugin, params,
allow_bulk=True,
parent=parent)
resource = extensions.ResourceExtension(
collection_name,
controller, parent,
@ -182,7 +198,7 @@ class FakeExtension(extensions.ExtensionDescriptor):
def get_extended_resources(self, version):
if version == "2.0":
return self.RAM
return self.RESOURCE_ATTRIBUTE_MAP
else:
return {}
@ -202,6 +218,16 @@ class FakePlugin(object):
def get_meh_mehs(self, context, filters=None, fields=None):
return [{'fake': 'fake'}]
def get_fake_duplicate(self, context, id_, fields=None):
return {'fake': id_}
def get_fake_duplicates(self, context, filters=None, fields=None):
return [{'fake': 'fakeduplicates'}]
def get_meh_meh_fake_duplicates(self, context, id_, fields=None,
filters=None):
return [{'fake': id_}]
def get_meh_meh_fake_subresources(self, context, id_, fields=None,
filters=None):
return {'foo': id_}