rename neutron/api to tacker/api
Change-Id: I7dd1c76e2df0aefa618ba28064ce9100f1e0c651
This commit is contained in:
parent
ea44fc81bc
commit
70be577f35
@ -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)
|
|
@ -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.
|
@ -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])
|
||||||
|
|
@ -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
|
@ -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,
|
@ -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)
|
@ -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
65
tacker/api/v1/router.py
Normal 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)
|
@ -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",
|
||||||
},
|
},
|
||||||
]
|
]
|
Loading…
Reference in New Issue
Block a user