Merge "Pecan: Handle member actions"
This commit is contained in:
commit
8eaa1808e5
|
@ -71,6 +71,10 @@ class Controller(object):
|
|||
def attr_info(self):
|
||||
return self._attr_info
|
||||
|
||||
@property
|
||||
def member_actions(self):
|
||||
return self._member_actions
|
||||
|
||||
def __init__(self, plugin, collection, resource, attr_info,
|
||||
allow_bulk=False, member_actions=None, parent=None,
|
||||
allow_pagination=False, allow_sorting=False):
|
||||
|
|
|
@ -24,9 +24,6 @@ from neutron.api import extensions
|
|||
from neutron.api.v2 import attributes as attr
|
||||
from neutron.api.v2 import resource_helper
|
||||
from neutron.conf import quota
|
||||
from neutron import manager
|
||||
from neutron.pecan_wsgi import controllers
|
||||
from neutron.pecan_wsgi.controllers import utils as pecan_utils
|
||||
from neutron.plugins.common import constants
|
||||
|
||||
|
||||
|
@ -201,18 +198,6 @@ class L3(extensions.ExtensionDescriptor):
|
|||
super(L3, self).update_attributes_map(
|
||||
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
|
||||
|
||||
@classmethod
|
||||
def get_pecan_resources(cls):
|
||||
plugin = manager.NeutronManager.get_service_plugins()[
|
||||
constants.L3_ROUTER_NAT]
|
||||
router_controller = controllers.RoutersController()
|
||||
fip_controller = controllers.CollectionsController(
|
||||
FLOATINGIPS, FLOATINGIP)
|
||||
return [pecan_utils.PecanResourceExtension(
|
||||
ROUTERS, router_controller, plugin),
|
||||
pecan_utils.PecanResourceExtension(
|
||||
FLOATINGIPS, fip_controller, plugin)]
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return RESOURCE_ATTRIBUTE_MAP
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
|
||||
from neutron.pecan_wsgi.controllers import quota
|
||||
from neutron.pecan_wsgi.controllers import resource
|
||||
from neutron.pecan_wsgi.controllers import router
|
||||
|
||||
|
||||
CollectionsController = resource.CollectionsController
|
||||
QuotasController = quota.QuotasController
|
||||
RoutersController = router.RoutersController
|
||||
|
|
|
@ -27,10 +27,11 @@ LOG = logging.getLogger(__name__)
|
|||
class ItemController(utils.NeutronPecanController):
|
||||
|
||||
def __init__(self, resource, item, plugin=None, resource_info=None,
|
||||
parent_resource=None):
|
||||
parent_resource=None, member_actions=None):
|
||||
super(ItemController, self).__init__(None, resource, plugin=plugin,
|
||||
resource_info=resource_info,
|
||||
parent_resource=parent_resource)
|
||||
parent_resource=parent_resource,
|
||||
member_actions=member_actions)
|
||||
self.item = item
|
||||
|
||||
@utils.expose(generic=True)
|
||||
|
@ -84,11 +85,24 @@ class ItemController(utils.NeutronPecanController):
|
|||
controller = manager.NeutronManager.get_controller_for_resource(
|
||||
collection)
|
||||
if not controller:
|
||||
LOG.warning(_LW("No controller found for: %s - returning response "
|
||||
"code 404"), collection)
|
||||
pecan.abort(404)
|
||||
if collection not in self._member_actions:
|
||||
LOG.warning(_LW("No controller found for: %s - returning"
|
||||
"response code 404"), collection)
|
||||
pecan.abort(404)
|
||||
# collection is a member action, so we create a new controller
|
||||
# for it.
|
||||
method = self._member_actions[collection]
|
||||
kwargs = {'plugin': self.plugin,
|
||||
'resource_info': self.resource_info}
|
||||
if method == 'PUT':
|
||||
kwargs['update_action'] = collection
|
||||
elif method == 'GET':
|
||||
kwargs['show_action'] = collection
|
||||
controller = MemberActionController(
|
||||
self.resource, self.item, self, **kwargs)
|
||||
else:
|
||||
request.context['parent_id'] = request.context['resource_id']
|
||||
request.context['resource'] = controller.resource
|
||||
request.context['parent_id'] = request.context['resource_id']
|
||||
return controller, remainder
|
||||
|
||||
|
||||
|
@ -104,11 +118,10 @@ class CollectionsController(utils.NeutronPecanController):
|
|||
request.context['uri_identifiers'][uri_identifier] = item
|
||||
return (self.item_controller_class(
|
||||
self.resource, item, resource_info=self.resource_info,
|
||||
# NOTE(tonytan4ever): item needs to share the same
|
||||
# parent as collection
|
||||
parent_resource=self.parent
|
||||
),
|
||||
remainder)
|
||||
# NOTE(tonytan4ever): item needs to share the same
|
||||
# parent as collection
|
||||
parent_resource=self.parent,
|
||||
member_actions=self._member_actions), remainder)
|
||||
|
||||
@utils.expose(generic=True)
|
||||
def index(self, *args, **kwargs):
|
||||
|
@ -154,3 +167,60 @@ class CollectionsController(utils.NeutronPecanController):
|
|||
creator_args.append(request.context['parent_id'])
|
||||
creator_args.append(data)
|
||||
return {key: creator(*creator_args)}
|
||||
|
||||
|
||||
class MemberActionController(ItemController):
|
||||
@property
|
||||
def plugin_shower(self):
|
||||
# NOTE(blogan): Do an explicit check for the _show_action because
|
||||
# pecan will see the plugin_shower property as a possible custom route
|
||||
# and try to evaluate it, which causes the code block to be executed.
|
||||
# If _show_action is None, getattr throws an exception and fails a
|
||||
# request.
|
||||
if self._show_action:
|
||||
return getattr(self.plugin, self._show_action)
|
||||
|
||||
@property
|
||||
def plugin_updater(self):
|
||||
if self._update_action:
|
||||
return getattr(self.plugin, self._update_action)
|
||||
|
||||
def __init__(self, resource, item, parent_controller, plugin=None,
|
||||
resource_info=None, show_action=None, update_action=None):
|
||||
super(MemberActionController, self).__init__(
|
||||
resource, item, plugin=plugin, resource_info=resource_info)
|
||||
self._show_action = show_action
|
||||
self._update_action = update_action
|
||||
self.parent_controller = parent_controller
|
||||
|
||||
@utils.expose(generic=True)
|
||||
def index(self, *args, **kwargs):
|
||||
if not self._show_action:
|
||||
pecan.abort(405)
|
||||
neutron_context = request.context['neutron_context']
|
||||
fields = request.context['query_params'].get('fields')
|
||||
return self.plugin_shower(neutron_context, self.item, fields=fields)
|
||||
|
||||
@utils.when(index, method='PUT')
|
||||
def put(self, *args, **kwargs):
|
||||
if not self._update_action:
|
||||
LOG.debug("Action %(action)s is not defined on resource "
|
||||
"%(resource)s",
|
||||
{'action': self._update_action,
|
||||
'resource': self.resource})
|
||||
pecan.abort(405)
|
||||
neutron_context = request.context['neutron_context']
|
||||
LOG.debug("Processing member action %(action)s for resource "
|
||||
"%(resource)s identified by %(item)s",
|
||||
{'action': self._update_action,
|
||||
'resource': self.resource,
|
||||
'item': self.item})
|
||||
return self.plugin_updater(neutron_context, self.item,
|
||||
request.context['request_data'])
|
||||
|
||||
@utils.when(index, method='HEAD')
|
||||
@utils.when(index, method='POST')
|
||||
@utils.when(index, method='PATCH')
|
||||
@utils.when(index, method='DELETE')
|
||||
def not_supported(self):
|
||||
return super(MemberActionController, self).not_supported()
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
# Copyright (c) 2015 Taturiello Consulting, Meh.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron._i18n import _LE
|
||||
from oslo_log import log
|
||||
import pecan
|
||||
from pecan import request
|
||||
|
||||
from neutron import manager
|
||||
from neutron.pecan_wsgi.controllers import resource
|
||||
from neutron.pecan_wsgi.controllers import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class RouterController(resource.ItemController):
|
||||
"""Customize ResourceController for member actions"""
|
||||
|
||||
### Pecan generic controllers don't work very well with inheritance
|
||||
|
||||
@utils.expose(generic=True)
|
||||
def index(self, *args, **kwargs):
|
||||
return super(RouterController, self).index(*args, **kwargs)
|
||||
|
||||
@utils.when(index, method='HEAD')
|
||||
@utils.when(index, method='POST')
|
||||
@utils.when(index, method='PATCH')
|
||||
def not_supported(self):
|
||||
return super(RouterController, self).not_supported()
|
||||
|
||||
@utils.when(index, method='PUT')
|
||||
def put(self, *args, **kwargs):
|
||||
return super(RouterController, self).put(*args, **kwargs)
|
||||
|
||||
@utils.when(index, method='DELETE')
|
||||
def delete(self):
|
||||
return super(RouterController, self).delete()
|
||||
|
||||
@utils.expose()
|
||||
def _lookup(self, member_action, *remainder):
|
||||
# This check is mainly for the l3-agents resource. If there isn't
|
||||
# a controller for it then we'll just assume its a member action.
|
||||
controller = manager.NeutronManager.get_controller_for_resource(
|
||||
member_action)
|
||||
if not controller:
|
||||
controller = RouterMemberActionController(
|
||||
self.resource, self.item, member_action)
|
||||
return controller, remainder
|
||||
|
||||
|
||||
class RoutersController(resource.CollectionsController):
|
||||
|
||||
item_controller_class = RouterController
|
||||
|
||||
def __init__(self):
|
||||
super(RoutersController, self).__init__('routers', 'router')
|
||||
|
||||
|
||||
class RouterMemberActionController(resource.ItemController):
|
||||
|
||||
def __init__(self, resource, item, member_action):
|
||||
super(RouterMemberActionController, self).__init__(resource, item)
|
||||
self.member_action = member_action
|
||||
|
||||
@utils.expose(generic=True)
|
||||
def index(self, *args, **kwargs):
|
||||
pecan.abort(405)
|
||||
|
||||
@utils.when(index, method='HEAD')
|
||||
@utils.when(index, method='POST')
|
||||
@utils.when(index, method='PATCH')
|
||||
def not_supported(self):
|
||||
return super(RouterMemberActionController, self).not_supported()
|
||||
|
||||
@utils.when(index, method='PUT')
|
||||
def put(self, *args, **kwargs):
|
||||
neutron_context = request.context['neutron_context']
|
||||
LOG.debug("Processing member action %(action)s for resource "
|
||||
"%(resource)s identified by %(item)s",
|
||||
{'action': self.member_action,
|
||||
'resource': self.resource,
|
||||
'item': self.item})
|
||||
# NOTE(salv-orlando): The following simply verify that the plugin
|
||||
# has a method for a given action. It therefore enables plugins to
|
||||
# implement actions which are not part of the API specification.
|
||||
# Unfortunately the API extension descriptor does not do a good job
|
||||
# of sanctioning which actions are available on a given resource.
|
||||
# TODO(salv-orlando): prevent plugins from implementing actions
|
||||
# which are not part of the Neutron API spec
|
||||
try:
|
||||
member_action_method = getattr(self.plugin, self.member_action)
|
||||
return member_action_method(neutron_context, self.item,
|
||||
request.context['request_data'])
|
||||
except AttributeError:
|
||||
LOG.error(_LE("Action %(action)s is not defined on resource "
|
||||
"%(resource)s"),
|
||||
{'action': self.member_action,
|
||||
'resource': self.resource})
|
||||
pecan.abort(404)
|
|
@ -98,10 +98,11 @@ class NeutronPecanController(object):
|
|||
|
||||
def __init__(self, collection, resource, plugin=None, resource_info=None,
|
||||
allow_pagination=None, allow_sorting=None,
|
||||
parent_resource=None):
|
||||
parent_resource=None, member_actions=None):
|
||||
# Ensure dashes are always replaced with underscores
|
||||
self.collection = collection and collection.replace('-', '_')
|
||||
self.resource = resource and resource.replace('-', '_')
|
||||
self._member_actions = member_actions or {}
|
||||
self._resource_info = resource_info
|
||||
self._plugin = plugin
|
||||
# Controllers for some resources that are not mapped to anything in
|
||||
|
|
|
@ -23,6 +23,7 @@ from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
|
|||
from neutron.common import rpc as n_rpc
|
||||
from neutron import manager
|
||||
from neutron.pecan_wsgi import constants as pecan_constants
|
||||
from neutron.pecan_wsgi.hooks import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
@ -66,6 +67,8 @@ class NotifierHook(hooks.PecanHook):
|
|||
resource = state.request.context.get('resource')
|
||||
if not resource:
|
||||
return
|
||||
if utils.is_member_action(utils.get_controller(state)):
|
||||
return
|
||||
action = pecan_constants.ACTION_MAP.get(state.request.method)
|
||||
event = '%s.%s.start' % (resource, action)
|
||||
if action in ('create', 'update'):
|
||||
|
@ -92,6 +95,8 @@ class NotifierHook(hooks.PecanHook):
|
|||
if not action or action == 'get':
|
||||
LOG.debug("No notification will be sent for action: %s", action)
|
||||
return
|
||||
if utils.is_member_action(utils.get_controller(state)):
|
||||
return
|
||||
if state.response.status_int > 300:
|
||||
LOG.debug("No notification will be sent due to unsuccessful "
|
||||
"status code: %s", state.response.status_int)
|
||||
|
|
|
@ -26,6 +26,7 @@ from neutron.extensions import quotasv2
|
|||
from neutron import manager
|
||||
from neutron.pecan_wsgi import constants as pecan_constants
|
||||
from neutron.pecan_wsgi.controllers import quota
|
||||
from neutron.pecan_wsgi.hooks import utils
|
||||
from neutron import policy
|
||||
|
||||
|
||||
|
@ -35,10 +36,8 @@ def _custom_getter(resource, resource_id):
|
|||
return quota.get_tenant_quotas(resource_id)[quotasv2.RESOURCE_NAME]
|
||||
|
||||
|
||||
def fetch_resource(neutron_context, collection, resource, resource_id,
|
||||
def fetch_resource(neutron_context, controller, resource, resource_id,
|
||||
parent_id=None):
|
||||
controller = manager.NeutronManager.get_controller_for_resource(
|
||||
collection)
|
||||
attrs = controller.resource_info
|
||||
if not attrs:
|
||||
# this isn't a request for a normal resource. it could be
|
||||
|
@ -51,7 +50,10 @@ def fetch_resource(neutron_context, collection, resource, resource_id,
|
|||
value.get('primary_key') or 'default' not in value)]
|
||||
plugin = manager.NeutronManager.get_plugin_for_resource(resource)
|
||||
if plugin:
|
||||
getter = controller.plugin_shower
|
||||
if utils.is_member_action(controller):
|
||||
getter = controller.parent_controller.plugin_shower
|
||||
else:
|
||||
getter = controller.plugin_shower
|
||||
getter_args = [neutron_context, resource_id]
|
||||
if parent_id:
|
||||
getter_args.append(parent_id)
|
||||
|
@ -80,15 +82,14 @@ class PolicyHook(hooks.PecanHook):
|
|||
# policies
|
||||
if not resource:
|
||||
return
|
||||
controller = utils.get_controller(state)
|
||||
if not controller or utils.is_member_action(controller):
|
||||
return
|
||||
collection = state.request.context.get('collection')
|
||||
needs_prefetch = (state.request.method == 'PUT' or
|
||||
state.request.method == 'DELETE')
|
||||
policy.init()
|
||||
|
||||
# NOTE(tonytan4ever): needs to get the actual action from controller's
|
||||
# _plugin_handlers
|
||||
controller = manager.NeutronManager.get_controller_for_resource(
|
||||
collection)
|
||||
action = controller.plugin_handlers[
|
||||
pecan_constants.ACTION_MAP[state.request.method]]
|
||||
|
||||
|
@ -106,7 +107,7 @@ class PolicyHook(hooks.PecanHook):
|
|||
item = {}
|
||||
resource_id = state.request.context.get('resource_id')
|
||||
parent_id = state.request.context.get('parent_id')
|
||||
resource_obj = fetch_resource(neutron_context, collection,
|
||||
resource_obj = fetch_resource(neutron_context, controller,
|
||||
resource, resource_id,
|
||||
parent_id=parent_id)
|
||||
if resource_obj:
|
||||
|
@ -141,6 +142,7 @@ class PolicyHook(hooks.PecanHook):
|
|||
neutron_context = state.request.context.get('neutron_context')
|
||||
resource = state.request.context.get('resource')
|
||||
collection = state.request.context.get('collection')
|
||||
controller = utils.get_controller(state)
|
||||
if not resource:
|
||||
# can't filter a resource we don't recognize
|
||||
return
|
||||
|
@ -165,8 +167,8 @@ class PolicyHook(hooks.PecanHook):
|
|||
policy_method = policy.enforce if is_single else policy.check
|
||||
plugin = manager.NeutronManager.get_plugin_for_resource(resource)
|
||||
try:
|
||||
resp = [self._get_filtered_item(state.request, resource,
|
||||
collection, item)
|
||||
resp = [self._get_filtered_item(state.request, controller,
|
||||
resource, collection, item)
|
||||
for item in to_process
|
||||
if (state.request.method != 'GET' or
|
||||
policy_method(neutron_context, action, item,
|
||||
|
@ -182,10 +184,11 @@ class PolicyHook(hooks.PecanHook):
|
|||
resp = resp[0]
|
||||
state.response.json = {key: resp}
|
||||
|
||||
def _get_filtered_item(self, request, resource, collection, data):
|
||||
def _get_filtered_item(self, request, controller, resource, collection,
|
||||
data):
|
||||
neutron_context = request.context.get('neutron_context')
|
||||
to_exclude = self._exclude_attributes_by_policy(
|
||||
neutron_context, resource, collection, data)
|
||||
neutron_context, controller, resource, collection, data)
|
||||
return self._filter_attributes(request, data, to_exclude)
|
||||
|
||||
def _filter_attributes(self, request, data, fields_to_strip):
|
||||
|
@ -197,7 +200,7 @@ class PolicyHook(hooks.PecanHook):
|
|||
if (item[0] not in fields_to_strip and
|
||||
(not user_fields or item[0] in user_fields)))
|
||||
|
||||
def _exclude_attributes_by_policy(self, context, resource,
|
||||
def _exclude_attributes_by_policy(self, context, controller, resource,
|
||||
collection, data):
|
||||
"""Identifies attributes to exclude according to authZ policies.
|
||||
|
||||
|
@ -207,8 +210,6 @@ class PolicyHook(hooks.PecanHook):
|
|||
"""
|
||||
attributes_to_exclude = []
|
||||
for attr_name in data.keys():
|
||||
controller = manager.NeutronManager.get_controller_for_resource(
|
||||
collection)
|
||||
attr_data = controller.resource_info.get(attr_name)
|
||||
if attr_data and attr_data['is_visible']:
|
||||
if policy.check(
|
||||
|
|
|
@ -15,6 +15,7 @@ from pecan import hooks
|
|||
from neutron.api import api_common
|
||||
from neutron import manager
|
||||
from neutron.pecan_wsgi.hooks import policy_enforcement
|
||||
from neutron.pecan_wsgi.hooks import utils
|
||||
|
||||
|
||||
# TODO(blogan): ideally it'd be nice to get the pagination and sorting
|
||||
|
@ -93,8 +94,7 @@ class QueryParametersHook(hooks.PecanHook):
|
|||
collection = state.request.context.get('collection')
|
||||
if not collection:
|
||||
return
|
||||
controller = manager.NeutronManager.get_controller_for_resource(
|
||||
collection)
|
||||
controller = utils.get_controller(state)
|
||||
combined_fields, added_fields = _set_fields(state, controller)
|
||||
filters = _set_filters(state, controller)
|
||||
query_params = {'fields': combined_fields, 'filters': filters}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# Copyright (c) 2015 Taturiello Consulting, Meh.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.pecan_wsgi.controllers import resource
|
||||
from neutron.pecan_wsgi.controllers import utils as controller_utils
|
||||
|
||||
|
||||
def get_controller(state):
|
||||
if (state.arguments and state.arguments.args and
|
||||
isinstance(state.arguments.args[0],
|
||||
controller_utils.NeutronPecanController)):
|
||||
controller = state.arguments.args[0]
|
||||
return controller
|
||||
|
||||
|
||||
def is_member_action(controller):
|
||||
return isinstance(controller,
|
||||
resource.MemberActionController)
|
|
@ -70,6 +70,7 @@ def initialize_all():
|
|||
resource = legacy_controller.resource
|
||||
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',
|
||||
|
@ -80,7 +81,7 @@ def initialize_all():
|
|||
parent_resource = parent.get('member_name')
|
||||
new_controller = res_ctrl.CollectionsController(
|
||||
collection, resource, resource_info=attr_info,
|
||||
parent_resource=parent_resource)
|
||||
parent_resource=parent_resource, member_actions=member_actions)
|
||||
manager.NeutronManager.set_plugin_for_resource(resource, plugin)
|
||||
if path_prefix:
|
||||
manager.NeutronManager.add_resource_for_path_prefix(
|
||||
|
|
|
@ -802,3 +802,51 @@ class TestShimControllers(test_functional.PecanFunctionalTest):
|
|||
self.assertEqual(200, resp.status_int)
|
||||
self.assertEqual({sub_resource_collection: {'foo': temp_id}},
|
||||
resp.json)
|
||||
|
||||
|
||||
class TestMemberActionController(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(TestMemberActionController, self).setUp(
|
||||
service_plugins=plugins, extensions=new_extensions)
|
||||
hyphen_collection = pecan_utils.FakeExtension.HYPHENATED_COLLECTION
|
||||
self.collection = hyphen_collection.replace('_', '-')
|
||||
|
||||
def test_get_member_action_controller(self):
|
||||
url = '/v2.0/{}/something/boo_meh.json'.format(self.collection)
|
||||
resp = self.app.get(url)
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertEqual({'boo_yah': 'something'}, resp.json)
|
||||
|
||||
def test_put_member_action_controller(self):
|
||||
url = '/v2.0/{}/something/put_meh.json'.format(self.collection)
|
||||
resp = self.app.put_json(url, params={'it_matters_not': 'ok'})
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertEqual({'poo_yah': 'something'}, resp.json)
|
||||
|
||||
def test_get_member_action_does_not_exist(self):
|
||||
url = '/v2.0/{}/something/are_you_still_there.json'.format(
|
||||
self.collection)
|
||||
resp = self.app.get(url, expect_errors=True)
|
||||
self.assertEqual(404, resp.status_int)
|
||||
|
||||
def test_put_member_action_does_not_exist(self):
|
||||
url = '/v2.0/{}/something/are_you_still_there.json'.format(
|
||||
self.collection)
|
||||
resp = self.app.put_json(url, params={'it_matters_not': 'ok'},
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, resp.status_int)
|
||||
|
||||
def test_put_on_get_member_action(self):
|
||||
url = '/v2.0/{}/something/boo_meh.json'.format(self.collection)
|
||||
resp = self.app.put_json(url, params={'it_matters_not': 'ok'},
|
||||
expect_errors=True)
|
||||
self.assertEqual(405, resp.status_int)
|
||||
|
||||
def test_get_on_put_member_action(self):
|
||||
url = '/v2.0/{}/something/put_meh.json'.format(self.collection)
|
||||
resp = self.app.get(url, expect_errors=True)
|
||||
self.assertEqual(405, resp.status_int)
|
||||
|
|
|
@ -335,7 +335,9 @@ class TestNovaNotifierHook(test_functional.PecanFunctionalTest):
|
|||
# NOTE(kevinbenton): the original passed into the notifier does
|
||||
# not contain all of the fields of the object. Only those required
|
||||
# by the policy engine are included.
|
||||
orig = pe.fetch_resource(context.get_admin_context(), 'networks',
|
||||
controller = manager.NeutronManager.get_controller_for_resource(
|
||||
'networks')
|
||||
orig = pe.fetch_resource(context.get_admin_context(), controller,
|
||||
'network', network_id)
|
||||
response = self.app.put_json(
|
||||
'/v2.0/networks/%s.json' % network_id,
|
||||
|
@ -347,7 +349,7 @@ class TestNovaNotifierHook(test_functional.PecanFunctionalTest):
|
|||
orig, json_body)
|
||||
self.mock_notifier.reset_mock()
|
||||
|
||||
orig = pe.fetch_resource(context.get_admin_context(), 'networks',
|
||||
orig = pe.fetch_resource(context.get_admin_context(), controller,
|
||||
'network', network_id)
|
||||
response = self.app.delete(
|
||||
'/v2.0/networks/%s.json' % network_id, headers=req_headers)
|
||||
|
|
|
@ -150,14 +150,15 @@ class FakeExtension(extensions.ExtensionDescriptor):
|
|||
params = self.RAM.get(self.HYPHENATED_COLLECTION, {})
|
||||
attributes.PLURALS.update({self.HYPHENATED_COLLECTION:
|
||||
self.HYPHENATED_RESOURCE})
|
||||
member_actions = {'put_meh': 'PUT', 'boo_meh': 'GET'}
|
||||
fake_plugin = FakePlugin()
|
||||
controller = base.create_resource(
|
||||
collection, self.HYPHENATED_RESOURCE, FakePlugin(),
|
||||
params, allow_bulk=True, allow_pagination=True,
|
||||
allow_sorting=True)
|
||||
resources = [extensions.ResourceExtension(collection,
|
||||
controller,
|
||||
attr_map=params)]
|
||||
allow_sorting=True, member_actions=member_actions)
|
||||
resources = [extensions.ResourceExtension(
|
||||
collection, controller, attr_map=params,
|
||||
member_actions=member_actions)]
|
||||
for collection_name in self.SUB_RESOURCE_ATTRIBUTE_MAP:
|
||||
resource_name = collection_name
|
||||
parent = self.SUB_RESOURCE_ATTRIBUTE_MAP[collection_name].get(
|
||||
|
@ -204,3 +205,9 @@ class FakePlugin(object):
|
|||
def get_meh_meh_fake_subresources(self, context, id_, fields=None,
|
||||
filters=None):
|
||||
return {'foo': id_}
|
||||
|
||||
def put_meh(self, context, id_, data):
|
||||
return {'poo_yah': id_}
|
||||
|
||||
def boo_meh(self, context, id_, fields=None):
|
||||
return {'boo_yah': id_}
|
||||
|
|
Loading…
Reference in New Issue