1 # Copyright (c) 2015 Mirantis, Inc.
2 # Copyright (c) 2015 Rackspace, Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 # not use this file except in compliance with the License. You may obtain
7 # a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 # License for the specific language governing permissions and limitations
17 from oslo_log import log
19 from pecan import request
21 from neutron._i18n import _, _LW
22 from neutron.api import extensions
23 from neutron.api.views import versions as versions_view
24 from neutron import manager
25 from neutron.pecan_wsgi.controllers import utils
27 LOG = log.getLogger(__name__)
31 def _load_version_info(version_info):
32 assert version_info['id'] not in _VERSION_INFO
33 _VERSION_INFO[version_info['id']] = version_info
36 def _get_version_info():
37 return _VERSION_INFO.values()
40 class RootController(object):
42 @utils.expose(generic=True)
44 builder = versions_view.get_view_builder(pecan.request)
45 versions = [builder.build(version) for version in _get_version_info()]
46 return dict(versions=versions)
48 @utils.when(index, method='HEAD')
49 @utils.when(index, method='POST')
50 @utils.when(index, method='PATCH')
51 @utils.when(index, method='PUT')
52 @utils.when(index, method='DELETE')
53 def not_supported(self):
57 class ExtensionsController(object):
60 def _lookup(self, alias, *remainder):
61 return ExtensionController(alias), remainder
65 ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
66 exts = [extensions.ExtensionController._translate(ext)
67 for ext in ext_mgr.extensions.values()]
68 return {'extensions': exts}
71 class V2Controller(object):
73 # Same data structure as neutron.api.versions.Versions for API backward
79 _load_version_info(version_info)
81 extensions = ExtensionsController()
83 @utils.expose(generic=True)
85 builder = versions_view.get_view_builder(pecan.request)
86 return dict(version=builder.build(self.version_info))
88 @utils.when(index, method='HEAD')
89 @utils.when(index, method='POST')
90 @utils.when(index, method='PATCH')
91 @utils.when(index, method='PUT')
92 @utils.when(index, method='DELETE')
93 def not_supported(self):
97 def _lookup(self, collection, *remainder):
98 # if collection exists in the extension to service plugins map then
99 # we are assuming that collection is the service plugin and
100 # needs to be remapped.
101 # Example: https://neutron.endpoint/v2.0/lbaas/loadbalancers
103 manager.NeutronManager.get_service_plugin_by_path_prefix(
105 collection = remainder[0]
106 remainder = remainder[1:]
107 controller = manager.NeutronManager.get_controller_for_resource(
110 LOG.warn(_LW("No controller found for: %s - returning response "
111 "code 404"), collection)
113 # Store resource and collection names in pecan request context so that
114 # hooks can leverage them if necessary
115 request.context['resource'] = controller.resource
116 request.context['collection'] = collection
117 return controller, remainder
120 # This controller cannot be specified directly as a member of RootController
121 # as its path is not a valid python identifier
122 pecan.route(RootController, 'v2.0', V2Controller())
125 class ExtensionController(object):
127 def __init__(self, alias):
132 ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
133 ext = ext_mgr.extensions.get(self.alias, None)
136 404, detail=_("Extension with alias %s "
137 "does not exist") % self.alias)
138 return {'extension': extensions.ExtensionController._translate(ext)}
141 class CollectionsController(utils.NeutronPecanController):
144 def _lookup(self, item, *remainder):
145 # Store resource identifier in request context
146 request.context['resource_id'] = item
147 return ItemController(self.resource, item), remainder
149 @utils.expose(generic=True)
150 def index(self, *args, **kwargs):
151 return self.get(*args, **kwargs)
153 def get(self, *args, **kwargs):
155 # TODO(kevinbenton): use user-provided fields in call to plugin
156 # after making sure policy enforced fields remain
157 kwargs.pop('fields', None)
158 _listify = lambda x: x if isinstance(x, list) else [x]
159 filters = {k: _listify(v) for k, v in kwargs.items()}
160 # TODO(kevinbenton): convert these using api_common.get_filters
161 lister = getattr(self.plugin, 'get_%s' % self.collection)
162 neutron_context = request.context['neutron_context']
163 return {self.collection: lister(neutron_context, filters=filters)}
165 @utils.when(index, method='HEAD')
166 @utils.when(index, method='PATCH')
167 @utils.when(index, method='PUT')
168 @utils.when(index, method='DELETE')
169 def not_supported(self):
172 @utils.when(index, method='POST')
173 def post(self, *args, **kwargs):
174 # TODO(kevinbenton): emulated bulk!
175 resources = request.context['resources']
176 pecan.response.status = 201
177 return self.create(resources)
179 def create(self, resources):
180 if len(resources) > 1:
182 method = 'create_%s_bulk' % self.resource
183 key = self.collection
184 data = {key: [{self.resource: res} for res in resources]}
186 method = 'create_%s' % self.resource
188 data = {key: resources[0]}
189 creator = getattr(self.plugin, method)
190 neutron_context = request.context['neutron_context']
191 return {key: creator(neutron_context, data)}
194 class ItemController(utils.NeutronPecanController):
196 def __init__(self, resource, item):
197 super(ItemController, self).__init__(None, resource)
200 @utils.expose(generic=True)
201 def index(self, *args, **kwargs):
204 def get(self, *args, **kwargs):
205 getter = getattr(self.plugin, 'get_%s' % self.resource)
206 neutron_context = request.context['neutron_context']
207 return {self.resource: getter(neutron_context, self.item)}
209 @utils.when(index, method='HEAD')
210 @utils.when(index, method='POST')
211 @utils.when(index, method='PATCH')
212 def not_supported(self):
215 @utils.when(index, method='PUT')
216 def put(self, *args, **kwargs):
217 neutron_context = request.context['neutron_context']
218 resources = request.context['resources']
219 if request.member_action:
220 member_action_method = getattr(self.plugin,
221 request.member_action)
222 return member_action_method(neutron_context, self.item,
224 # TODO(kevinbenton): bulk?
225 updater = getattr(self.plugin, 'update_%s' % self.resource)
226 # Bulk update is not supported, 'resources' always contains a single
228 data = {self.resource: resources[0]}
229 return updater(neutron_context, self.item, data)
231 @utils.when(index, method='DELETE')
233 # TODO(kevinbenton): setting code could be in a decorator
234 pecan.response.status = 204
235 neutron_context = request.context['neutron_context']
236 deleter = getattr(self.plugin, 'delete_%s' % self.resource)
237 return deleter(neutron_context, self.item)