rename neutron/api to tacker/api

Change-Id: I7dd1c76e2df0aefa618ba28064ce9100f1e0c651
This commit is contained in:
Isaku Yamahata 2014-06-26 17:08:54 +09:00
parent ea44fc81bc
commit 70be577f35
13 changed files with 135 additions and 478 deletions

View File

@ -1,115 +0,0 @@
# Copyright (c) 2012 OpenStack Foundation.
#
# 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 oslo.config import cfg
import routes as routes_mapper
import six.moves.urllib.parse as urlparse
import webob
import webob.dec
import webob.exc
from neutron.api import extensions
from neutron.api.v2 import attributes
from neutron.api.v2 import base
from neutron import manager
from neutron.openstack.common import log as logging
from neutron import wsgi
LOG = logging.getLogger(__name__)
RESOURCES = {'network': 'networks',
'subnet': 'subnets',
'port': 'ports'}
SUB_RESOURCES = {}
COLLECTION_ACTIONS = ['index', 'create']
MEMBER_ACTIONS = ['show', 'update', 'delete']
REQUIREMENTS = {'id': attributes.UUID_PATTERN, 'format': 'xml|json'}
class Index(wsgi.Application):
def __init__(self, resources):
self.resources = resources
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
metadata = {'application/xml': {'attributes': {
'resource': ['name', 'collection'],
'link': ['href', 'rel']}}}
layout = []
for name, collection in self.resources.iteritems():
href = urlparse.urljoin(req.path_url, collection)
resource = {'name': name,
'collection': collection,
'links': [{'rel': 'self',
'href': href}]}
layout.append(resource)
response = dict(resources=layout)
content_type = req.best_match_content_type()
body = wsgi.Serializer(metadata=metadata).serialize(response,
content_type)
return webob.Response(body=body, content_type=content_type)
class APIRouter(wsgi.Router):
@classmethod
def factory(cls, global_config, **local_config):
return cls(**local_config)
def __init__(self, **local_config):
mapper = routes_mapper.Mapper()
plugin = manager.NeutronManager.get_plugin()
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
ext_mgr.extend_resources("2.0", attributes.RESOURCE_ATTRIBUTE_MAP)
col_kwargs = dict(collection_actions=COLLECTION_ACTIONS,
member_actions=MEMBER_ACTIONS)
def _map_resource(collection, resource, params, parent=None):
allow_bulk = cfg.CONF.allow_bulk
allow_pagination = cfg.CONF.allow_pagination
allow_sorting = cfg.CONF.allow_sorting
controller = base.create_resource(
collection, resource, plugin, params, allow_bulk=allow_bulk,
parent=parent, allow_pagination=allow_pagination,
allow_sorting=allow_sorting)
path_prefix = None
if parent:
path_prefix = "/%s/{%s_id}/%s" % (parent['collection_name'],
parent['member_name'],
collection)
mapper_kwargs = dict(controller=controller,
requirements=REQUIREMENTS,
path_prefix=path_prefix,
**col_kwargs)
return mapper.collection(collection, resource,
**mapper_kwargs)
mapper.connect('index', '/', controller=Index(RESOURCES))
for resource in RESOURCES:
_map_resource(RESOURCES[resource], resource,
attributes.RESOURCE_ATTRIBUTE_MAP.get(
RESOURCES[resource], dict()))
for resource in SUB_RESOURCES:
_map_resource(SUB_RESOURCES[resource]['collection_name'], resource,
attributes.RESOURCE_ATTRIBUTE_MAP.get(
SUB_RESOURCES[resource]['collection_name'],
dict()),
SUB_RESOURCES[resource]['parent'])
super(APIRouter, self).__init__(mapper)

View File

@ -18,9 +18,9 @@ import urllib
from oslo.config import cfg from oslo.config import cfg
from webob import exc from webob import exc
from neutron.common import constants from tacker.common import constants
from neutron.common import exceptions from tacker.common import exceptions
from neutron.openstack.common import log as logging from tacker.openstack.common import log as logging
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -290,14 +290,14 @@ class NoSortingHelper(SortingHelper):
pass pass
class NeutronController(object): class TackerController(object):
"""Base controller class for Neutron API.""" """Base controller class for Tacker API."""
# _resource_name will be redefined in sub concrete controller # _resource_name will be redefined in sub concrete controller
_resource_name = None _resource_name = None
def __init__(self, plugin): def __init__(self, plugin):
self._plugin = plugin self._plugin = plugin
super(NeutronController, self).__init__() super(TackerController, self).__init__()
def _prepare_request_body(self, body, params): def _prepare_request_body(self, body, params):
"""Verifies required parameters are in request body. """Verifies required parameters are in request body.

View File

@ -16,7 +16,6 @@
import abc import abc
import imp import imp
import itertools
import os import os
from oslo.config import cfg from oslo.config import cfg
@ -25,13 +24,12 @@ import six
import webob.dec import webob.dec
import webob.exc import webob.exc
from neutron.api.v2 import attributes from tacker.api.v1 import attributes
from neutron.common import exceptions from tacker.common import exceptions
import neutron.extensions import tacker.extensions
from neutron import manager from tacker.openstack.common import log as logging
from neutron.openstack.common import log as logging from tacker import policy
from neutron import policy from tacker import wsgi
from neutron import wsgi
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -384,10 +382,10 @@ class ExtensionMiddleware(wsgi.Middleware):
return app return app
def plugin_aware_extension_middleware_factory(global_config, **local_config): def extension_middleware_factory(global_config, **local_config):
"""Paste factory.""" """Paste factory."""
def _factory(app): def _factory(app):
ext_mgr = PluginAwareExtensionManager.get_instance() ext_mgr = ExtensionManager.get_instance()
return ExtensionMiddleware(app, ext_mgr=ext_mgr) return ExtensionMiddleware(app, ext_mgr=ext_mgr)
return _factory return _factory
@ -399,6 +397,14 @@ class ExtensionManager(object):
example extension implementation. example extension implementation.
""" """
_instance = None
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls(get_extensions_path())
return cls._instance
def __init__(self, path): def __init__(self, path):
LOG.info(_('Initializing extension manager.')) LOG.info(_('Initializing extension manager.'))
self.path = path self.path = path
@ -539,7 +545,7 @@ class ExtensionManager(object):
def _load_all_extensions_from_path(self, path): def _load_all_extensions_from_path(self, path):
# Sorting the extension list makes the order in which they # Sorting the extension list makes the order in which they
# are loaded predictable across a cluster of load-balanced # are loaded predictable across a cluster of load-balanced
# Neutron Servers # Tacker Servers
for f in sorted(os.listdir(path)): for f in sorted(os.listdir(path)):
try: try:
LOG.debug(_('Loading extension file: %s'), f) LOG.debug(_('Loading extension file: %s'), f)
@ -574,68 +580,11 @@ class ExtensionManager(object):
self.extensions[alias] = ext self.extensions[alias] = ext
class PluginAwareExtensionManager(ExtensionManager):
_instance = None
def __init__(self, path, plugins):
self.plugins = plugins
super(PluginAwareExtensionManager, self).__init__(path)
self.check_if_plugin_extensions_loaded()
def _check_extension(self, extension):
"""Check if an extension is supported by any plugin."""
extension_is_valid = super(PluginAwareExtensionManager,
self)._check_extension(extension)
return (extension_is_valid and
self._plugins_support(extension) and
self._plugins_implement_interface(extension))
def _plugins_support(self, extension):
alias = extension.get_alias()
supports_extension = any((hasattr(plugin,
"supported_extension_aliases") and
alias in plugin.supported_extension_aliases)
for plugin in self.plugins.values())
if not supports_extension:
LOG.warn(_("Extension %s not supported by any of loaded plugins"),
alias)
return supports_extension
def _plugins_implement_interface(self, extension):
if(not hasattr(extension, "get_plugin_interface") or
extension.get_plugin_interface() is None):
return True
for plugin in self.plugins.values():
if isinstance(plugin, extension.get_plugin_interface()):
return True
LOG.warn(_("Loaded plugins do not implement extension %s interface"),
extension.get_alias())
return False
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls(get_extensions_path(),
manager.NeutronManager.get_service_plugins())
return cls._instance
def check_if_plugin_extensions_loaded(self):
"""Check if an extension supported by a plugin has been loaded."""
plugin_extensions = set(itertools.chain.from_iterable([
getattr(plugin, "supported_extension_aliases", [])
for plugin in self.plugins.values()]))
missing_aliases = plugin_extensions - set(self.extensions)
if missing_aliases:
raise exceptions.ExtensionsNotFound(
extensions=list(missing_aliases))
class RequestExtension(object): class RequestExtension(object):
"""Extend requests and responses of core Neutron OpenStack API controllers. """Extend requests and responses of core Tacker OpenStack API controllers.
Provide a way to add data to responses and handle custom request data Provide a way to add data to responses and handle custom request data
that is sent to core Neutron OpenStack API controllers. that is sent to core Tacker OpenStack API controllers.
""" """
def __init__(self, method, url_route, handler): def __init__(self, method, url_route, handler):
@ -646,7 +595,7 @@ class RequestExtension(object):
class ActionExtension(object): class ActionExtension(object):
"""Add custom actions to core Neutron OpenStack API controllers.""" """Add custom actions to core Tacker OpenStack API controllers."""
def __init__(self, collection, action_name, handler): def __init__(self, collection, action_name, handler):
self.collection = collection self.collection = collection
@ -655,7 +604,7 @@ class ActionExtension(object):
class ResourceExtension(object): class ResourceExtension(object):
"""Add top level resources to the OpenStack API in Neutron.""" """Add top level resources to the OpenStack API in Tacker."""
def __init__(self, collection, controller, parent=None, path_prefix="", def __init__(self, collection, controller, parent=None, path_prefix="",
collection_actions={}, member_actions={}, attr_map={}): collection_actions={}, member_actions={}, attr_map={}):
@ -669,9 +618,9 @@ class ResourceExtension(object):
# Returns the extension paths from a config entry and the __path__ # Returns the extension paths from a config entry and the __path__
# of neutron.extensions # of tacker.extensions
def get_extensions_path(): def get_extensions_path():
paths = ':'.join(neutron.extensions.__path__) paths = ':'.join(tacker.extensions.__path__)
if cfg.CONF.api_extensions_path: if cfg.CONF.api_extensions_path:
paths = ':'.join([cfg.CONF.api_extensions_path, paths]) paths = ':'.join([cfg.CONF.api_extensions_path, paths])

View File

@ -18,10 +18,10 @@
import netaddr import netaddr
import re import re
from neutron.common import constants from tacker.common import constants
from neutron.common import exceptions as n_exc from tacker.common import exceptions as n_exc
from neutron.openstack.common import log as logging from tacker.openstack.common import log as logging
from neutron.openstack.common import uuidutils from tacker.openstack.common import uuidutils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -580,12 +580,7 @@ validators = {'type:dict': _validate_dict,
'type:boolean': _validate_boolean} 'type:boolean': _validate_boolean}
# Define constants for base resource name # Define constants for base resource name
NETWORK = 'network'
NETWORKS = '%ss' % NETWORK
PORT = 'port'
PORTS = '%ss' % PORT
SUBNET = 'subnet'
SUBNETS = '%ss' % SUBNET
# Note: a default of ATTR_NOT_SPECIFIED indicates that an # Note: a default of ATTR_NOT_SPECIFIED indicates that an
# attribute is not required, but will be generated by the plugin # attribute is not required, but will be generated by the plugin
# if it is not specified. Particularly, a value of ATTR_NOT_SPECIFIED # if it is not specified. Particularly, a value of ATTR_NOT_SPECIFIED
@ -609,156 +604,11 @@ SUBNETS = '%ss' % SUBNET
# enforce_policy: the attribute is actively part of the policy enforcing # enforce_policy: the attribute is actively part of the policy enforcing
# mechanism, ie: there might be rules which refer to this attribute. # mechanism, ie: there might be rules which refer to this attribute.
RESOURCE_ATTRIBUTE_MAP = {
NETWORKS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'subnets': {'allow_post': False, 'allow_put': False,
'default': [],
'is_visible': True},
'admin_state_up': {'allow_post': True, 'allow_put': True,
'default': True,
'convert_to': convert_to_boolean,
'is_visible': True},
'status': {'allow_post': False, 'allow_put': False,
'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
SHARED: {'allow_post': True,
'allow_put': True,
'default': False,
'convert_to': convert_to_boolean,
'is_visible': True,
'required_by_policy': True,
'enforce_policy': True},
},
PORTS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True, 'default': '',
'validate': {'type:string': None},
'is_visible': True},
'network_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'validate': {'type:uuid': None},
'is_visible': True},
'admin_state_up': {'allow_post': True, 'allow_put': True,
'default': True,
'convert_to': convert_to_boolean,
'is_visible': True},
'mac_address': {'allow_post': True, 'allow_put': False,
'default': ATTR_NOT_SPECIFIED,
'validate': {'type:mac_address': None},
'enforce_policy': True,
'is_visible': True},
'fixed_ips': {'allow_post': True, 'allow_put': True,
'default': ATTR_NOT_SPECIFIED,
'convert_list_to': convert_kvp_list_to_dict,
'validate': {'type:fixed_ips': None},
'enforce_policy': True,
'is_visible': True},
'device_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '',
'is_visible': True},
'device_owner': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '',
'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'status': {'allow_post': False, 'allow_put': False,
'is_visible': True},
},
SUBNETS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True, 'default': '',
'validate': {'type:string': None},
'is_visible': True},
'ip_version': {'allow_post': True, 'allow_put': False,
'convert_to': convert_to_int,
'validate': {'type:values': [4, 6]},
'is_visible': True},
'network_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'validate': {'type:uuid': None},
'is_visible': True},
'cidr': {'allow_post': True, 'allow_put': False,
'validate': {'type:subnet': None},
'is_visible': True},
'gateway_ip': {'allow_post': True, 'allow_put': True,
'default': ATTR_NOT_SPECIFIED,
'validate': {'type:ip_address_or_none': None},
'is_visible': True},
'allocation_pools': {'allow_post': True, 'allow_put': True,
'default': ATTR_NOT_SPECIFIED,
'validate': {'type:ip_pools': None},
'is_visible': True},
'dns_nameservers': {'allow_post': True, 'allow_put': True,
'convert_to': convert_none_to_empty_list,
'default': ATTR_NOT_SPECIFIED,
'validate': {'type:nameservers': None},
'is_visible': True},
'host_routes': {'allow_post': True, 'allow_put': True,
'convert_to': convert_none_to_empty_list,
'default': ATTR_NOT_SPECIFIED,
'validate': {'type:hostroutes': None},
'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'enable_dhcp': {'allow_post': True, 'allow_put': True,
'default': True,
'convert_to': convert_to_boolean,
'is_visible': True},
'ipv6_ra_mode': {'allow_post': True, 'allow_put': True,
'default': ATTR_NOT_SPECIFIED,
'validate': {'type:values': constants.IPV6_MODES},
'is_visible': True},
'ipv6_address_mode': {'allow_post': True, 'allow_put': True,
'default': ATTR_NOT_SPECIFIED,
'validate': {'type:values':
constants.IPV6_MODES},
'is_visible': True},
SHARED: {'allow_post': False,
'allow_put': False,
'default': False,
'convert_to': convert_to_boolean,
'is_visible': False,
'required_by_policy': True,
'enforce_policy': True},
}
}
# Identify the attribute used by a resource to reference another resource # Identify the attribute used by a resource to reference another resource
RESOURCE_FOREIGN_KEYS = { RESOURCE_ATTRIBUTE_MAP = {}
NETWORKS: 'network_id'
}
PLURALS = {NETWORKS: NETWORK, PLURALS = {'extensions': 'extension'}
PORTS: PORT,
SUBNETS: SUBNET,
'dns_nameservers': 'dns_nameserver',
'host_routes': 'host_route',
'allocation_pools': 'allocation_pool',
'fixed_ips': 'fixed_ip',
'extensions': 'extension'}
EXT_NSES = {} EXT_NSES = {}
# Namespaces to be added for backward compatibility # Namespaces to be added for backward compatibility

View File

@ -15,22 +15,18 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import copy
import netaddr import netaddr
import webob.exc import webob.exc
from oslo.config import cfg from oslo.config import cfg
from neutron.api import api_common from tacker.api import api_common
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api from tacker.api.v1 import attributes
from neutron.api.v2 import attributes from tacker.api.v1 import resource as wsgi_resource
from neutron.api.v2 import resource as wsgi_resource from tacker.common import exceptions
from neutron.common import constants as const from tacker.common import rpc as n_rpc
from neutron.common import exceptions from tacker.openstack.common import log as logging
from neutron.common import rpc as n_rpc from tacker import policy
from neutron.openstack.common import log as logging
from neutron import policy
from neutron import quota
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -70,15 +66,9 @@ class Controller(object):
self._policy_attrs = [name for (name, info) in self._attr_info.items() self._policy_attrs = [name for (name, info) in self._attr_info.items()
if info.get('required_by_policy')] if info.get('required_by_policy')]
self._notifier = n_rpc.get_notifier('network') self._notifier = n_rpc.get_notifier('network')
# use plugin's dhcp notifier, if this is already instantiated # if cfg.CONF.notify_nova_on_port_data_changes:
agent_notifiers = getattr(plugin, 'agent_notifiers', {}) # from tacker.notifiers import nova
self._dhcp_agent_notifier = ( # self._nova_notifier = nova.Notifier()
agent_notifiers.get(const.AGENT_TYPE_DHCP) or
dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
)
if cfg.CONF.notify_nova_on_port_data_changes:
from neutron.notifiers import nova
self._nova_notifier = nova.Notifier()
self._member_actions = member_actions self._member_actions = member_actions
self._primary_key = self._get_primary_key() self._primary_key = self._get_primary_key()
if self._allow_pagination and self._native_pagination: if self._allow_pagination and self._native_pagination:
@ -153,7 +143,7 @@ class Controller(object):
def _view(self, context, data, fields_to_strip=None): def _view(self, context, data, fields_to_strip=None):
"""Build a view of an API resource. """Build a view of an API resource.
:param context: the neutron context :param context: the tacker context
:param data: the object for which a view is being created :param data: the object for which a view is being created
:param fields_to_strip: attributes to remove from the view :param fields_to_strip: attributes to remove from the view
@ -288,19 +278,6 @@ class Controller(object):
policy.enforce(request.context, action, obj) policy.enforce(request.context, action, obj)
return obj return obj
def _send_dhcp_notification(self, context, data, methodname):
if cfg.CONF.dhcp_agent_notification:
if self._collection in data:
for body in data[self._collection]:
item = {self._resource: body}
self._dhcp_agent_notifier.notify(context, item, methodname)
else:
self._dhcp_agent_notifier.notify(context, data, methodname)
def _send_nova_notification(self, action, orig, returned):
if hasattr(self, '_nova_notifier'):
self._nova_notifier.send_network_change(action, orig, returned)
def index(self, request, **kwargs): def index(self, request, **kwargs):
"""Returns a list of the requested entity.""" """Returns a list of the requested entity."""
parent_id = kwargs.get(self._parent_id_name) parent_id = kwargs.get(self._parent_id_name)
@ -383,46 +360,20 @@ class Controller(object):
if self._collection in body: if self._collection in body:
# Have to account for bulk create # Have to account for bulk create
items = body[self._collection] items = body[self._collection]
deltas = {}
bulk = True
else: else:
items = [body] items = [body]
bulk = False
# Ensure policy engine is initialized # Ensure policy engine is initialized
policy.init() policy.init()
for item in items: for item in items:
self._validate_network_tenant_ownership(request,
item[self._resource])
policy.enforce(request.context, policy.enforce(request.context,
action, action,
item[self._resource]) item[self._resource])
try:
tenant_id = item[self._resource]['tenant_id']
count = quota.QUOTAS.count(request.context, self._resource,
self._plugin, self._collection,
tenant_id)
if bulk:
delta = deltas.get(tenant_id, 0) + 1
deltas[tenant_id] = delta
else:
delta = 1
kwargs = {self._resource: count + delta}
except exceptions.QuotaResourceUnknown as e:
# We don't want to quota this resource
LOG.debug(e)
else:
quota.QUOTAS.limit_check(request.context,
item[self._resource]['tenant_id'],
**kwargs)
def notify(create_result): def notify(create_result):
notifier_method = self._resource + '.create.end' notifier_method = self._resource + '.create.end'
self._notifier.info(request.context, self._notifier.info(request.context,
notifier_method, notifier_method,
create_result) create_result)
self._send_dhcp_notification(request.context,
create_result,
notifier_method)
return create_result return create_result
kwargs = {self._parent_id_name: parent_id} if parent_id else {} kwargs = {self._parent_id_name: parent_id} if parent_id else {}
@ -447,8 +398,6 @@ class Controller(object):
else: else:
kwargs.update({self._resource: body}) kwargs.update({self._resource: body})
obj = obj_creator(request.context, **kwargs) obj = obj_creator(request.context, **kwargs)
self._send_nova_notification(action, {},
{self._resource: obj})
return notify({self._resource: self._view(request.context, return notify({self._resource: self._view(request.context,
obj)}) obj)})
@ -479,11 +428,6 @@ class Controller(object):
self._notifier.info(request.context, self._notifier.info(request.context,
notifier_method, notifier_method,
{self._resource + '_id': id}) {self._resource + '_id': id})
result = {self._resource: self._view(request.context, obj)}
self._send_nova_notification(action, {}, result)
self._send_dhcp_notification(request.context,
result,
notifier_method)
def update(self, request, id, body=None, **kwargs): def update(self, request, id, body=None, **kwargs):
"""Updates the specified entity's attributes.""" """Updates the specified entity's attributes."""
@ -512,7 +456,6 @@ class Controller(object):
policy.init() policy.init()
orig_obj = self._item(request, id, field_list=field_list, orig_obj = self._item(request, id, field_list=field_list,
parent_id=parent_id) parent_id=parent_id)
orig_object_copy = copy.copy(orig_obj)
orig_obj.update(body[self._resource]) orig_obj.update(body[self._resource])
try: try:
policy.enforce(request.context, policy.enforce(request.context,
@ -532,10 +475,6 @@ class Controller(object):
result = {self._resource: self._view(request.context, obj)} result = {self._resource: self._view(request.context, obj)}
notifier_method = self._resource + '.update.end' notifier_method = self._resource + '.update.end'
self._notifier.info(request.context, notifier_method, result) self._notifier.info(request.context, notifier_method, result)
self._send_dhcp_notification(request.context,
result,
notifier_method)
self._send_nova_notification(action, orig_object_copy, result)
return result return result
@staticmethod @staticmethod
@ -644,29 +583,6 @@ class Controller(object):
msg = _("Unrecognized attribute(s) '%s'") % ', '.join(extra_keys) msg = _("Unrecognized attribute(s) '%s'") % ', '.join(extra_keys)
raise webob.exc.HTTPBadRequest(msg) raise webob.exc.HTTPBadRequest(msg)
def _validate_network_tenant_ownership(self, request, resource_item):
# TODO(salvatore-orlando): consider whether this check can be folded
# in the policy engine
if (request.context.is_admin or
self._resource not in ('port', 'subnet')):
return
network = self._plugin.get_network(
request.context,
resource_item['network_id'])
# do not perform the check on shared networks
if network.get('shared'):
return
network_owner = network['tenant_id']
if network_owner != resource_item['tenant_id']:
msg = _("Tenant %(tenant_id)s not allowed to "
"create %(resource)s on this network")
raise webob.exc.HTTPForbidden(msg % {
"tenant_id": resource_item['tenant_id'],
"resource": self._resource,
})
def create_resource(collection, resource, plugin, params, allow_bulk=False, def create_resource(collection, resource, plugin, params, allow_bulk=False,
member_actions=None, parent=None, allow_pagination=False, member_actions=None, parent=None, allow_pagination=False,

View File

@ -24,11 +24,11 @@ import six
import webob.dec import webob.dec
import webob.exc import webob.exc
from neutron.api.v2 import attributes from tacker.api.v1 import attributes
from neutron.common import exceptions from tacker.common import exceptions
from neutron.openstack.common import gettextutils from tacker.openstack.common import gettextutils
from neutron.openstack.common import log as logging from tacker.openstack.common import log as logging
from neutron import wsgi from tacker import wsgi
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -85,7 +85,7 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
method = getattr(controller, action) method = getattr(controller, action)
result = method(request=request, **args) result = method(request=request, **args)
except (exceptions.NeutronException, except (exceptions.TackerException,
netaddr.AddrFormatError) as e: netaddr.AddrFormatError) as e:
for fault in faults: for fault in faults:
if isinstance(e, fault): if isinstance(e, fault):
@ -99,17 +99,17 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
else: else:
LOG.exception(_('%s failed'), action) LOG.exception(_('%s failed'), action)
e = translate(e, language) e = translate(e, language)
# following structure is expected by python-neutronclient # following structure is expected by python-tackerclient
err_data = {'type': e.__class__.__name__, err_data = {'type': e.__class__.__name__,
'message': e, 'detail': ''} 'message': e, 'detail': ''}
body = serializer.serialize({'NeutronError': err_data}) body = serializer.serialize({'TackerError': err_data})
kwargs = {'body': body, 'content_type': content_type} kwargs = {'body': body, 'content_type': content_type}
raise mapped_exc(**kwargs) raise mapped_exc(**kwargs)
except webob.exc.HTTPException as e: except webob.exc.HTTPException as e:
type_, value, tb = sys.exc_info() type_, value, tb = sys.exc_info()
LOG.exception(_('%s failed'), action) LOG.exception(_('%s failed'), action)
translate(e, language) translate(e, language)
value.body = serializer.serialize({'NeutronError': e}) value.body = serializer.serialize({'TackerError': e})
value.content_type = content_type value.content_type = content_type
six.reraise(type_, value, tb) six.reraise(type_, value, tb)
except NotImplementedError as e: except NotImplementedError as e:
@ -131,7 +131,7 @@ def Resource(controller, faults=None, deserializers=None, serializers=None):
msg = _('Request Failed: internal server error while ' msg = _('Request Failed: internal server error while '
'processing your request.') 'processing your request.')
msg = translate(msg, language) msg = translate(msg, language)
body = serializer.serialize({'NeutronError': msg}) body = serializer.serialize({'TackerError': msg})
kwargs = {'body': body, 'content_type': content_type} kwargs = {'body': body, 'content_type': content_type}
raise webob.exc.HTTPInternalServerError(**kwargs) raise webob.exc.HTTPInternalServerError(**kwargs)
@ -161,7 +161,7 @@ def translate(translatable, locale):
was not translated was not translated
""" """
localize = gettextutils.translate localize = gettextutils.translate
if isinstance(translatable, exceptions.NeutronException): if isinstance(translatable, exceptions.TackerException):
translatable.msg = localize(translatable.msg, locale) translatable.msg = localize(translatable.msg, locale)
elif isinstance(translatable, webob.exc.HTTPError): elif isinstance(translatable, webob.exc.HTTPError):
translatable.detail = localize(translatable.detail, locale) translatable.detail = localize(translatable.detail, locale)

View File

@ -16,11 +16,10 @@
from oslo.config import cfg from oslo.config import cfg
from neutron.api import extensions from tacker.api import extensions
from neutron.api.v2 import base from tacker.api.v1 import base
from neutron import manager from tacker import manager
from neutron.plugins.common import constants from tacker.plugins.common import constants
from neutron import quota
def build_plural_mappings(special_mappings, resource_map): def build_plural_mappings(special_mappings, resource_map):
@ -38,7 +37,7 @@ def build_plural_mappings(special_mappings, resource_map):
def build_resource_info(plural_mappings, resource_map, which_service, def build_resource_info(plural_mappings, resource_map, which_service,
action_map=None, register_quota=False, action_map=None,
translate_name=False, allow_bulk=False): translate_name=False, allow_bulk=False):
"""Build resources for advanced services. """Build resources for advanced services.
@ -55,8 +54,6 @@ def build_resource_info(plural_mappings, resource_map, which_service,
It can be set to None or "CORE"to create WSGI It can be set to None or "CORE"to create WSGI
resources for the the core plugin resources for the the core plugin
:param action_map: custom resource actions :param action_map: custom resource actions
:param register_quota: it can be set to True to register quotas for the
resource(s) being created
:param translate_name: replaces underscores with dashes :param translate_name: replaces underscores with dashes
:param allow_bulk: True if bulk create are allowed :param allow_bulk: True if bulk create are allowed
""" """
@ -65,17 +62,12 @@ def build_resource_info(plural_mappings, resource_map, which_service,
which_service = constants.CORE which_service = constants.CORE
if action_map is None: if action_map is None:
action_map = {} action_map = {}
if which_service != constants.CORE: plugin = manager.TackerManager.get_service_plugins()[which_service]
plugin = manager.NeutronManager.get_service_plugins()[which_service]
else:
plugin = manager.NeutronManager.get_plugin()
for collection_name in resource_map: for collection_name in resource_map:
resource_name = plural_mappings[collection_name] resource_name = plural_mappings[collection_name]
params = resource_map.get(collection_name, {}) params = resource_map.get(collection_name, {})
if translate_name: if translate_name:
collection_name = collection_name.replace('_', '-') collection_name = collection_name.replace('_', '-')
if register_quota:
quota.QUOTAS.register_resource_by_name(resource_name)
member_actions = action_map.get(resource_name, {}) member_actions = action_map.get(resource_name, {})
controller = base.create_resource( controller = base.create_resource(
collection_name, resource_name, plugin, params, collection_name, resource_name, plugin, params,

65
tacker/api/v1/router.py Normal file
View File

@ -0,0 +1,65 @@
# Copyright (c) 2012 OpenStack Foundation.
#
# 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.
import routes as routes_mapper
import six.moves.urllib.parse as urlparse
import webob
import webob.dec
import webob.exc
from tacker.api import extensions
from tacker.api.v1 import attributes
from tacker.openstack.common import log as logging
from tacker import wsgi
LOG = logging.getLogger(__name__)
class Index(wsgi.Application):
def __init__(self, resources):
self.resources = resources
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
metadata = {'application/xml': {'attributes': {
'resource': ['name', 'collection'],
'link': ['href', 'rel']}}}
layout = []
for name, collection in self.resources.iteritems():
href = urlparse.urljoin(req.path_url, collection)
resource = {'name': name,
'collection': collection,
'links': [{'rel': 'self',
'href': href}]}
layout.append(resource)
response = dict(resources=layout)
content_type = req.best_match_content_type()
body = wsgi.Serializer(metadata=metadata).serialize(response,
content_type)
return webob.Response(body=body, content_type=content_type)
class APIRouter(wsgi.Router):
@classmethod
def factory(cls, global_config, **local_config):
return cls(**local_config)
def __init__(self, **local_config):
mapper = routes_mapper.Mapper()
ext_mgr = extensions.ExtensionManager.get_instance()
ext_mgr.extend_resources("1.0", attributes.RESOURCE_ATTRIBUTE_MAP)
super(APIRouter, self).__init__(mapper)

View File

@ -15,10 +15,10 @@
import webob.dec import webob.dec
from neutron.api.views import versions as versions_view from tacker.api.views import versions as versions_view
from neutron.openstack.common import gettextutils from tacker.openstack.common import gettextutils
from neutron.openstack.common import log as logging from tacker.openstack.common import log as logging
from neutron import wsgi from tacker import wsgi
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -32,10 +32,10 @@ class Versions(object):
@webob.dec.wsgify(RequestClass=wsgi.Request) @webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req): def __call__(self, req):
"""Respond to a request for all Neutron API versions.""" """Respond to a request for all Tacker API versions."""
version_objs = [ version_objs = [
{ {
"id": "v2.0", "id": "v1.0",
"status": "CURRENT", "status": "CURRENT",
}, },
] ]