Remove v1 code from quantum-server

bp remove-v1-related-code

Removes API, DB, and test code that is used only for Quantum v1 API,
which is no longer supported.

Includes removal of v1 code for sample, ovs, linuxbridge and ryu plugin.

Nicira and Cisco plugins will be handled outside of this patchset.

Change-Id: Id34dc7229bb7b399b5cfd4602dbc8d5ee4e8de61
changes/98/10998/6
Dan Wendlandt 2012-08-10 10:37:26 -07:00
parent 2a2d7f2e3a
commit 77573d7338
50 changed files with 87 additions and 5956 deletions

View File

@ -1,20 +1,8 @@
[composite:quantum]
use = egg:Paste#urlmap
/: quantumversions
/v1.0: quantumapi_v1_0
/v1.1: quantumapi_v1_1
/v2.0: quantumapi_v2_0
[composite:quantumapi_v1_0]
use = call:quantum.auth:pipeline_factory
noauth = extensions quantumapiapp_v1_0
keystone = authtoken keystonecontext extensions quantumapiapp_v1_0
[composite:quantumapi_v1_1]
use = call:quantum.auth:pipeline_factory
noauth = extensions quantumapiapp_v1_1
keystone = authtoken keystonecontext extensions quantumapiapp_v1_1
[composite:quantumapi_v2_0]
use = call:quantum.auth:pipeline_factory
noauth = extensions quantumapiapp_v2_0
@ -38,11 +26,5 @@ paste.filter_factory = quantum.extensions.extensions:plugin_aware_extension_midd
[app:quantumversions]
paste.app_factory = quantum.api.versions:Versions.factory
[app:quantumapiapp_v1_0]
paste.app_factory = quantum.api:APIRouterV10.factory
[app:quantumapiapp_v1_1]
paste.app_factory = quantum.api:APIRouterV11.factory
[app:quantumapiapp_v2_0]
paste.app_factory = quantum.api.v2.router:APIRouter.factory

View File

@ -1,108 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Citrix Systems
# 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.
# @author: Salvatore Orlando, Citrix Systems
"""
Quantum API controllers.
"""
import logging
import routes
import webob.dec
import webob.exc
from quantum.api import attachments
from quantum.api import networks
from quantum.api import ports
from quantum.common import flags
from quantum import manager
from quantum import wsgi
LOG = logging.getLogger('quantum.api')
FLAGS = flags.FLAGS
class APIRouter(wsgi.Router):
"""
Base class for Quantum API routes.
"""
_version = None
def __init__(self):
mapper = self._mapper()
self._setup_routes(mapper)
super(APIRouter, self).__init__(mapper)
def _mapper(self):
return routes.Mapper()
def _setup_routes(self, mapper):
self._setup_base_routes(mapper, self._version)
def _setup_base_routes(self, mapper, version):
"""Routes common to all versions."""
# Loads the quantum plugin
# Note(salvatore-orlando): Should the plugin be versioned
# I don't think so
plugin = manager.QuantumManager.get_plugin()
uri_prefix = '/tenants/{tenant_id}/'
attachment_path = (
'%snetworks/{network_id}/ports/{id}/attachment{.format}' %
uri_prefix)
mapper.resource('network', 'networks',
controller=networks.create_resource(plugin, version),
collection={'detail': 'GET'},
member={'detail': 'GET'},
path_prefix=uri_prefix)
mapper.resource('port', 'ports',
controller=ports.create_resource(plugin, version),
collection={'detail': 'GET'},
member={'detail': 'GET'},
parent_resource=dict(
member_name='network',
collection_name='%snetworks' % uri_prefix))
attachments_ctrl = attachments.create_resource(plugin, version)
mapper.connect("get_resource",
attachment_path,
controller=attachments_ctrl,
action="get_resource",
conditions=dict(method=['GET']))
mapper.connect("attach_resource",
attachment_path,
controller=attachments_ctrl,
action="attach_resource",
conditions=dict(method=['PUT']))
mapper.connect("detach_resource",
attachment_path,
controller=attachments_ctrl,
action="detach_resource",
conditions=dict(method=['DELETE']))
class APIRouterV10(APIRouter):
"""
API routes mappings for Quantum API v1.0
"""
_version = '1.0'
class APIRouterV11(APIRouter):
"""
API routes mappings for Quantum API v1.1
"""
_version = '1.1'

View File

@ -26,143 +26,6 @@ from quantum import wsgi
LOG = logging.getLogger(__name__)
XML_NS_V10 = 'http://openstack.org/quantum/api/v1.0'
XML_NS_V11 = 'http://openstack.org/quantum/api/v1.1'
class OperationalStatus:
""" Enumeration for operational status
UP : the resource is available (operationall up)
DOWN : the resource is not operational; this might indicate
a failure in the underlying switching fabric.
PROVISIONING: the plugin is creating or updating the resource
in the underlying switching fabric
UNKNOWN: the plugin does not support the operational status concept.
"""
UP = "UP"
DOWN = "DOWN"
PROVISIONING = "PROVISIONING"
UNKNOWN = "UNKNOWN"
def create_resource(version, controller_dict):
"""
Generic function for creating a wsgi resource
The function takes as input:
- desired version
- controller and metadata dictionary
e.g.: {'1.0': [ctrl_v10, meta_v10, xml_ns],
'1.1': [ctrl_v11, meta_v11, xml_ns]}
"""
# the first element of the iterable is expected to be the controller
controller = controller_dict[version][0]
# the second element should be the metadata
metadata = controller_dict[version][1]
# and the third element the xml namespace
xmlns = controller_dict[version][2]
# and also the function for building the fault body
fault_body_function = faults.fault_body_function(version)
headers_serializers = {
'1.0': HeaderSerializer10(),
'1.1': HeaderSerializer11()
}
xml_serializer = wsgi.XMLDictSerializer(metadata, xmlns)
json_serializer = wsgi.JSONDictSerializer()
xml_deserializer = wsgi.XMLDeserializer(metadata)
json_deserializer = wsgi.JSONDeserializer()
body_serializers = {
'application/xml': xml_serializer,
'application/json': json_serializer,
}
body_deserializers = {
'application/xml': xml_deserializer,
'application/json': json_deserializer,
}
serializer = wsgi.ResponseSerializer(body_serializers,
headers_serializers[version])
deserializer = wsgi.RequestDeserializer(body_deserializers)
return wsgi.Resource(controller,
fault_body_function,
deserializer,
serializer)
def APIFaultWrapper(errors=None):
quantum_error_dict = {
'1.0': faults.Quantum10HTTPError,
'1.1': faults.Quantum11HTTPError
}
def wrapper(func, **kwargs):
def the_func(*args, **kwargs):
try:
# Grab API version from type of controller
controller = args[0]
version = controller.version
return func(*args, **kwargs)
except Exception as e:
if errors is not None and type(e) in errors:
# Version-specific behaviour
quantum_error_class = quantum_error_dict[version]
raise quantum_error_class(e)
# otherwise just re-raise
raise
the_func.__name__ = func.__name__
return the_func
return wrapper
class HeaderSerializer10(wsgi.ResponseHeaderSerializer):
"""
Defines default respone status codes for Quantum API 1.0 operations
create - 200 OK
update - 204 NOCONTENT
delete - 204 NOCONTENT
others - 200 OK (defined in base class)
"""
def create(self, response, data):
response.status_int = 200
def delete(self, response, data):
response.status_int = 204
def update(self, response, data):
response.status_int = 204
def attach_resource(self, response, data):
response.status_int = 204
def detach_resource(self, response, data):
response.status_int = 204
class HeaderSerializer11(HeaderSerializer10):
"""
Defines default respone status codes for Quantum API 1.0 operations
create - 202 ACCEPTED
update - 204 NOCONTENT
delete - 204 NOCONTENT
others - 200 OK (defined in base class)
"""
def create(self, response, data):
response.status_int = 202
class QuantumController(object):
""" Base controller class for Quantum API """
# _resource_name will be redefined in sub concrete controller

View File

@ -1,89 +0,0 @@
# Copyright 2011 Citrix Systems.
# 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.
import logging
from quantum.api import api_common as common
from quantum.api.views import attachments as attachments_view
from quantum.common import exceptions as exception
LOG = logging.getLogger(__name__)
def create_resource(plugin, version):
controller_dict = {
'1.0': [ControllerV10(plugin),
ControllerV10._serialization_metadata,
common.XML_NS_V10],
'1.1': [ControllerV11(plugin),
ControllerV11._serialization_metadata,
common.XML_NS_V11],
}
return common.create_resource(version, controller_dict)
class Controller(common.QuantumController):
""" Port API controller for Quantum API """
_resource_name = 'attachment'
# version will be redefined by in child class
version = None
_attachment_ops_param_list = [
{
'param-name': 'id',
'required': True,
},
]
_serialization_metadata = {
"application/xml": {
"attributes": {
"attachment": ["id"],
},
},
}
@common.APIFaultWrapper([exception.NetworkNotFound,
exception.PortNotFound])
def get_resource(self, request, tenant_id, network_id, id):
att_data = self._plugin.get_port_details(tenant_id, network_id, id)
builder = attachments_view.get_view_builder(request)
result = builder.build(att_data)['attachment']
return dict(attachment=result)
@common.APIFaultWrapper([exception.NetworkNotFound,
exception.PortNotFound,
exception.PortInUse,
exception.AlreadyAttached])
def attach_resource(self, request, tenant_id, network_id, id, body):
body = self._prepare_request_body(body,
self._attachment_ops_param_list)
self._plugin.plug_interface(tenant_id, network_id, id,
body['attachment']['id'])
@common.APIFaultWrapper([exception.NetworkNotFound,
exception.PortNotFound])
def detach_resource(self, request, tenant_id, network_id, id):
self._plugin.unplug_interface(tenant_id, network_id, id)
class ControllerV10(Controller):
"""Attachment resources controller for Quantum v1.0 API"""
version = "1.0"
class ControllerV11(Controller):
"""Attachment resources controller for Quantum v1.1 API"""
version = "1.1"

View File

@ -1,187 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Citrix Systems.
# 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.
import webob.exc
from quantum.common import exceptions
_NETNOTFOUND_EXPL = 'Unable to find a network with the specified identifier.'
_NETINUSE_EXPL = 'Unable to remove the network: attachments still plugged.'
_PORTNOTFOUND_EXPL = 'Unable to find a port with the specified identifier.'
_STATEINVALID_EXPL = 'Unable to update port state with specified value.'
_PORTINUSE_EXPL = 'A resource is currently attached to the logical port'
_ALREADYATTACHED_EXPL = 'The resource is already attached to another port'
_NOTIMPLEMENTED_EXPL = 'Not implemented'
def fault_body_function_v10(wrapped_exc):
""" This function creates the contents of the body for a fault
response for Quantum API v1.0.
:param wrapped_exc: Exception thrown by the Quantum service
:type wrapped_exc: quantum.common.exceptions.QuantumException
:returns: response body contents and serialization metadata
:rtype: tuple
"""
code = wrapped_exc.status_int
fault_name = (hasattr(wrapped_exc, 'title') and
wrapped_exc.title or "quantumServiceFault")
fault_data = {
fault_name: {
'code': code,
'message': wrapped_exc.explanation,
'detail': str(wrapped_exc.detail),
},
}
metadata = {'attributes': {fault_name: ['code']}}
return fault_data, metadata
def fault_body_function_v11(wrapped_exc):
""" This function creates the contents of the body for a fault
response for Quantum API v1.1.
:param wrapped_exc: Exception thrown by the Quantum service
:type wrapped_exc: quantum.common.exceptions.QuantumException
:returns: response body contents and serialization metadata
:rtype: tuple
"""
fault_name = (hasattr(wrapped_exc, 'type') and
wrapped_exc.type or "QuantumServiceFault")
# Ensure first letter is capital
fault_name = fault_name[0].upper() + fault_name[1:]
fault_data = {
'QuantumError': {
'type': fault_name,
'message': wrapped_exc.explanation,
'detail': str(wrapped_exc.detail),
},
}
# Metadata not required for v11
return fault_data, None
def fault_body_function(version):
# dict mapping API version to functions for building the
# fault response body
fault_body_function_dict = {
'1.0': fault_body_function_v10,
'1.1': fault_body_function_v11
}
return fault_body_function_dict.get(version, None)
class Quantum10HTTPError(webob.exc.HTTPClientError):
_fault_dict = {
exceptions.NetworkNotFound: {
'code': 420,
'title': 'networkNotFound',
'explanation': _NETNOTFOUND_EXPL
},
exceptions.NetworkInUse: {
'code': 421,
'title': 'networkInUse',
'explanation': _NETINUSE_EXPL
},
exceptions.PortNotFound: {
'code': 430,
'title': 'portNotFound',
'explanation': _PORTNOTFOUND_EXPL
},
exceptions.StateInvalid: {
'code': 431,
'title': 'requestedStateInvalid',
'explanation': _STATEINVALID_EXPL
},
exceptions.PortInUse: {
'code': 432,
'title': 'portInUse',
'explanation': _PORTINUSE_EXPL
},
exceptions.AlreadyAttached: {
'code': 440,
'title': 'alreadyAttached',
'explanation': _ALREADYATTACHED_EXPL
},
exceptions.NotImplementedError: {
'code': 501,
'title': 'notImplemented',
'explanation': _NOTIMPLEMENTED_EXPL
}
}
def __init__(self, inner_exc):
_fault_data = self._fault_dict.get(type(inner_exc), None)
if _fault_data:
self.code = _fault_data['code']
self.title = _fault_data['title']
self.explanation = _fault_data['explanation']
super(webob.exc.HTTPClientError, self).__init__(inner_exc)
class Quantum11HTTPError(webob.exc.HTTPClientError):
_fault_dict = {
exceptions.NetworkNotFound: {
'code': webob.exc.HTTPNotFound.code,
'title': webob.exc.HTTPNotFound.title,
'type': 'NetworkNotFound',
'explanation': _NETNOTFOUND_EXPL
},
exceptions.NetworkInUse: {
'code': webob.exc.HTTPConflict.code,
'title': webob.exc.HTTPConflict.title,
'type': 'NetworkInUse',
'explanation': _NETINUSE_EXPL
},
exceptions.PortNotFound: {
'code': webob.exc.HTTPNotFound.code,
'title': webob.exc.HTTPNotFound.title,
'type': 'PortNotFound',
'explanation': _PORTNOTFOUND_EXPL
},
exceptions.StateInvalid: {
'code': webob.exc.HTTPBadRequest.code,
'title': webob.exc.HTTPBadRequest.title,
'type': 'RequestedStateInvalid',
'explanation': _STATEINVALID_EXPL
},
exceptions.PortInUse: {
'code': webob.exc.HTTPConflict.code,
'title': webob.exc.HTTPConflict.title,
'type': 'PortInUse',
'explanation': _PORTINUSE_EXPL
},
exceptions.AlreadyAttached: {
'code': webob.exc.HTTPConflict.code,
'title': webob.exc.HTTPConflict.title,
'type': 'AlreadyAttached',
'explanation': _ALREADYATTACHED_EXPL
}
}
def __init__(self, inner_exc):
_fault_data = self._fault_dict.get(type(inner_exc), None)
if _fault_data:
self.code = _fault_data['code']
self.title = _fault_data['title']
self.explanation = _fault_data['explanation']
self.type = _fault_data['type']
super(webob.exc.HTTPClientError, self).__init__(inner_exc)

View File

@ -1,188 +0,0 @@
# Copyright 2011 Citrix Systems.
# 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.
import logging
from quantum.api import api_common as common
from quantum.api.views import filters
from quantum.api.views import networks as networks_view
from quantum.common import exceptions as exception
LOG = logging.getLogger(__name__)
def create_resource(plugin, version):
controller_dict = {
'1.0': [ControllerV10(plugin),
ControllerV10._serialization_metadata,
common.XML_NS_V10],
'1.1': [ControllerV11(plugin),
ControllerV11._serialization_metadata,
common.XML_NS_V11],
}
return common.create_resource(version, controller_dict)
class Controller(common.QuantumController):
""" Network API controller for Quantum API """
_resource_name = 'network'
# version will be redefined in child class
version = None
_network_ops_param_list = [
{'param-name': 'name', 'required': True},
]
def _item(self, request, tenant_id, network_id,
net_details=True, port_details=False):
# We expect get_network_details to return information
# concerning logical ports as well.
network = self._plugin.get_network_details(tenant_id, network_id)
# Doing this in the API is inefficient
# TODO(salvatore-orlando): This should be fixed with Bug #834012
# Don't pass filter options
ports_data = None
if port_details:
port_list = self._plugin.get_all_ports(tenant_id, network_id)
ports_data = [
self._plugin.get_port_details(tenant_id, network_id,
port['port-id'])
for port in port_list]
builder = networks_view.get_view_builder(request, self.version)
result = builder.build(network, net_details,
ports_data, port_details)['network']
return dict(network=result)
def _items(self, request, tenant_id, net_details=False):
""" Returns a list of networks.
Ideally, the plugin would perform filtering,
returning only the items matching filters specified
on the request query string.
However, plugins are not required to support filtering.
In this case, this function will filter the complete list
of networks returned by the plugin
"""
filter_opts = {}
filter_opts.update(request.GET)
networks = self._plugin.get_all_networks(tenant_id,
filter_opts=filter_opts)
# Inefficient, API-layer filtering
# will be performed only for the filters not implemented by the plugin
# NOTE(salvatore-orlando): the plugin is supposed to leave only filters
# it does not implement in filter_opts
networks = filters.filter_networks(networks,
self._plugin,
tenant_id,
filter_opts)
builder = networks_view.get_view_builder(request, self.version)
result = [builder.build(network, net_details)['network']
for network in networks]
return dict(networks=result)
@common.APIFaultWrapper()
def index(self, request, tenant_id):
""" Returns a list of network ids """
return self._items(request, tenant_id)
@common.APIFaultWrapper([exception.NetworkNotFound])
def show(self, request, tenant_id, id):
""" Returns network details for the given network id """
return self._item(request, tenant_id, id,
net_details=True, port_details=False)
@common.APIFaultWrapper([exception.NetworkNotFound])
def detail(self, request, **kwargs):
tenant_id = kwargs.get('tenant_id')
network_id = kwargs.get('id')
if network_id:
# show details for a given network
return self._item(request, tenant_id, network_id,
net_details=True, port_details=True)
else:
# show details for all networks
return self._items(request, tenant_id, net_details=True)
@common.APIFaultWrapper()
def create(self, request, tenant_id, body):
""" Creates a new network for a given tenant """
# NOTE(bgh): We're currently passing both request_params['name'] and
# the entire request_params dict because their may be pieces of
# information (data extensions) inside the request params that the
# actual plugin will want to parse. We could just pass only
# request_params but that would mean all the plugins would need to
# change.
body = self._prepare_request_body(body, self._network_ops_param_list)
network = self._plugin.create_network(tenant_id,
body['network']['name'],
**body)
builder = networks_view.get_view_builder(request, self.version)
result = builder.build(network)['network']
return dict(network=result)
@common.APIFaultWrapper([exception.NetworkNotFound])
def update(self, request, tenant_id, id, body):
""" Updates the name for the network with the given id """
body = self._prepare_request_body(body, self._network_ops_param_list)
self._plugin.update_network(tenant_id, id, **body['network'])
@common.APIFaultWrapper([exception.NetworkNotFound,
exception.NetworkInUse])
def delete(self, request, tenant_id, id):
""" Destroys the network with the given id """
self._plugin.delete_network(tenant_id, id)
class ControllerV10(Controller):
"""Network resources controller for Quantum v1.0 API"""
_serialization_metadata = {
"attributes": {
"network": ["id", "name"],
"port": ["id", "state"],
"attachment": ["id"],
},
"plurals": {
"networks": "network",
"ports": "port",
},
}
version = "1.0"
class ControllerV11(Controller):
"""Network resources controller for Quantum v1.1 API
Note: at this state this class only adds serialization
metadata for the operational status concept.
API filters, pagination, and atom links will be handled by
this class as well.
"""
_serialization_metadata = {
"attributes": {
"network": ["id", "name", "op-status"],
"port": ["id", "state", "op-status"],
"attachment": ["id"],
},
"plurals": {
"networks": "network",
"ports": "port",
},
}
version = "1.1"

View File

@ -1,185 +0,0 @@
# Copyright 2011 Citrix Systems.
# 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.
import logging
from quantum.api import api_common as common
from quantum.api.views import filters
from quantum.api.views import ports as ports_view
from quantum.common import exceptions as exception
LOG = logging.getLogger(__name__)
def create_resource(plugin, version):
controller_dict = {
'1.0': [ControllerV10(plugin),
ControllerV10._serialization_metadata,
common.XML_NS_V10],
'1.1': [ControllerV11(plugin),
ControllerV11._serialization_metadata,
common.XML_NS_V11],
}
return common.create_resource(version, controller_dict)
class Controller(common.QuantumController):
""" Port API controller for Quantum API """
_resource_name = 'port'
# version will be redefined in child class
version = None
_port_ops_param_list = [
{'param-name': 'state', 'default-value': 'DOWN', 'required': False},
]
def _items(self, request, tenant_id, network_id,
port_details=False):
""" Returns a list of ports.
Ideally, the plugin would perform filtering,
returning only the items matching filters specified
on the request query string.
However, plugins are not required to support filtering.
In this case, this function will filter the complete list
of ports returned by the plugin
"""
filter_opts = {}
filter_opts.update(request.GET)
port_list = self._plugin.get_all_ports(tenant_id,
network_id,
filter_opts=filter_opts)
builder = ports_view.get_view_builder(request, self.version)
# Load extra data for ports if required.
# This can be inefficient.
# TODO(salvatore-orlando): the fix for bug #834012 should deal with it
if port_details:
port_list_detail = [
self._plugin.get_port_details(tenant_id, network_id,
port['port-id'])
for port in port_list]
port_list = port_list_detail
# Perform manual filtering if not supported by plugin
# Inefficient, API-layer filtering
# will be performed only if the plugin does
# not support filtering
# NOTE(salvatore-orlando): the plugin is supposed to leave only filters
# it does not implement in filter_opts
port_list = filters.filter_ports(port_list, self._plugin,
tenant_id, network_id,
filter_opts)
result = [builder.build(port, port_details)['port']
for port in port_list]
return dict(ports=result)
def _item(self, request, tenant_id, network_id, port_id,
att_details=False):
""" Returns a specific port. """
port = self._plugin.get_port_details(tenant_id, network_id, port_id)
builder = ports_view.get_view_builder(request, self.version)
result = builder.build(port, port_details=True,
att_details=att_details)['port']
return dict(port=result)
@common.APIFaultWrapper([exception.NetworkNotFound])
def index(self, request, tenant_id, network_id):
""" Returns a list of port ids for a given network """
return self._items(request, tenant_id, network_id, port_details=False)
@common.APIFaultWrapper([exception.NetworkNotFound,
exception.PortNotFound])
def show(self, request, tenant_id, network_id, id):
""" Returns port details for given port and network """
return self._item(request, tenant_id, network_id, id)
@common.APIFaultWrapper([exception.NetworkNotFound,
exception.PortNotFound])
def detail(self, request, **kwargs):
tenant_id = kwargs.get('tenant_id')
network_id = kwargs.get('network_id')
port_id = kwargs.get('id')
if port_id:
# show details for a given network
return self._item(request, tenant_id,
network_id, port_id, att_details=True)
else:
# show details for all port
return self._items(request, tenant_id,
network_id, port_details=True)
@common.APIFaultWrapper([exception.NetworkNotFound,
exception.StateInvalid])
def create(self, request, tenant_id, network_id, body=None):
""" Creates a new port for a given network
The request body is optional for a port object.
"""
body = self._prepare_request_body(body, self._port_ops_param_list)
port = self._plugin.create_port(tenant_id,
network_id, body['port']['state'],
**body)
builder = ports_view.get_view_builder(request, self.version)
result = builder.build(port)['port']
return dict(port=result)
@common.APIFaultWrapper([exception.NetworkNotFound,
exception.PortNotFound,
exception.StateInvalid])
def update(self, request, tenant_id, network_id, id, body):
""" Updates the state of a port for a given network """
body = self._prepare_request_body(body, self._port_ops_param_list)
self._plugin.update_port(tenant_id, network_id, id, **body['port'])
@common.APIFaultWrapper([exception.NetworkNotFound,
exception.PortNotFound,
exception.PortInUse])
def delete(self, request, tenant_id, network_id, id):
""" Destroys the port with the given id """
self._plugin.delete_port(tenant_id, network_id, id)
class ControllerV10(Controller):
"""Port resources controller for Quantum v1.0 API"""
_serialization_metadata = {
"attributes": {
"port": ["id", "state"],
"attachment": ["id"],
},
"plurals": {
"ports": "port",
},
}
version = "1.0"
class ControllerV11(Controller):
"""Port resources controller for Quantum v1.1 API"""
_serialization_metadata = {
"attributes": {
"port": ["id", "state", "op-status"],
"attachment": ["id"],
},
"plurals": {
"ports": "port",
},
}
version = "1.1"

View File

@ -1,37 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Citrix Systems
# 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.
def get_view_builder(req):
base_url = req.application_url
return ViewBuilder(base_url)
class ViewBuilder(object):
def __init__(self, base_url):
"""
:param base_url: url of the root wsgi application
"""
self.base_url = base_url
def build(self, attachment_data):
"""Generic method used to generate an attachment entity."""
if attachment_data['attachment']:
return dict(attachment=dict(id=attachment_data['attachment']))
else:
return dict(attachment={})

View File

@ -1,160 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Citrix Systems
# 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.
import logging
LOG = logging.getLogger(__name__)
def _load_network_ports_details(network, **kwargs):
plugin = kwargs.get('plugin', None)
tenant_id = kwargs.get('tenant_id', None)
#load network details only if required
if not 'net-ports' in network:
# Don't pass filter options, don't care about unused filters
port_list = plugin.get_all_ports(tenant_id, network['net-id'])
ports_data = [plugin.get_port_details(
tenant_id, network['net-id'],
port['port-id'])
for port in port_list]
network['net-ports'] = ports_data
def _filter_network_by_name(network, name, **kwargs):
return network.get('net-name', None) == name
def _filter_network_with_operational_port(network, port_op_status,
**kwargs):
_load_network_ports_details(network, **kwargs)
return any([port['port-op-status'] == port_op_status
for port in network['net-ports']])
def _filter_network_with_active_port(network, port_state, **kwargs):
_load_network_ports_details(network, **kwargs)
return any([port['port-state'] == port_state
for port in network['net-ports']])
def _filter_network_has_interface(network, has_interface, **kwargs):
_load_network_ports_details(network, **kwargs)
# convert to bool
match_has_interface = has_interface.lower() == 'true'
really_has_interface = any([port['attachment'] is not None
for port in network['net-ports']])
return match_has_interface == really_has_interface
def _filter_network_by_port(network, port_id, **kwargs):
_load_network_ports_details(network, **kwargs)
return any([port['port-id'] == port_id
for port in network['net-ports']])
def _filter_network_by_interface(network, interface_id, **kwargs):
_load_network_ports_details(network, **kwargs)
return any([port.get('attachment', None) == interface_id
for port in network['net-ports']])
def _filter_port_by_state(port, state, **kwargs):
return port.get('port-state', None) == state
def _filter_network_by_op_status(network, op_status, **kwargs):
return network.get('net-op-status', None) == op_status
def _filter_port_by_op_status(port, op_status, **kwargs):
return port.get('port-op-status', None) == op_status
def _filter_port_by_interface(port, interface_id, **kwargs):
return port.get('attachment', None) == interface_id
def _filter_port_has_interface(port, has_interface, **kwargs):
# convert to bool
match_has_interface = has_interface.lower() == 'true'
really_has_interface = ('attachment' in port and
port['attachment'] is not None)
return match_has_interface == really_has_interface
def _do_filtering(items, filters, filter_opts, plugin,
tenant_id, network_id=None):
filtered_items = []
for item in items:
is_filter_match = False
for flt in filters:
if flt in filter_opts:
is_filter_match = filters[flt](item,
filter_opts[flt],
plugin=plugin,
tenant_id=tenant_id,
network_id=network_id)
if not is_filter_match:
break
if is_filter_match:
filtered_items.append(item)
return filtered_items
def filter_networks(networks, plugin, tenant_id, filter_opts):
# Do filtering only if the plugin supports it
# and if filtering options have been specific
if len(filter_opts) == 0:
return networks
# load filter functions
filters = {
'name': _filter_network_by_name,
'op-status': _filter_network_by_op_status,
'port-op-status': _filter_network_with_operational_port,
'port-state': _filter_network_with_active_port,
'has-attachment': _filter_network_has_interface,
'attachment': _filter_network_by_interface,
'port': _filter_network_by_port}
# filter networks
return _do_filtering(networks, filters, filter_opts, plugin, tenant_id)
def filter_ports(ports, plugin, tenant_id, network_id, filter_opts):
# Do filtering only if the plugin supports it
# and if filtering options have been specific
if len(filter_opts) == 0:
return ports
# load filter functions
filters = {
'state': _filter_port_by_state,
'op-status': _filter_port_by_op_status,
'has-attachment': _filter_port_has_interface,
'attachment': _filter_port_by_interface}
# port details are need for filtering
ports = [plugin.get_port_details(tenant_id, network_id,
port['port-id']) for port in ports]
# filter ports
return _do_filtering(ports,
filters,
filter_opts,
plugin,
tenant_id,
network_id)

View File

@ -1,91 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Citrix Systems
# 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 quantum.api.api_common import OperationalStatus
def get_view_builder(req, version):
base_url = req.application_url
view_builder = {
'1.0': ViewBuilder10,
'1.1': ViewBuilder11,
}[version](base_url)
return view_builder
class ViewBuilder10(object):
def __init__(self, base_url=None):
"""
:param base_url: url of the root wsgi application
"""
self.base_url = base_url
def build(self, network_data, net_detail=False,
ports_data=None, port_detail=False):
"""Generic method used to generate a network entity."""
if net_detail:
network = self._build_detail(network_data)
else:
network = self._build_simple(network_data)
if port_detail:
ports = [self._build_port(port_data) for port_data in ports_data]
network['network']['ports'] = ports
return network
def _build_simple(self, network_data):
"""Return a simple model of a network."""
return dict(network=dict(id=network_data['net-id']))
def _build_detail(self, network_data):
"""Return a detailed model of a network."""
return dict(network=dict(id=network_data['net-id'],
name=network_data['net-name']))
def _build_port(self, port_data):
"""Return details about a specific logical port."""
port_dict = dict(id=port_data['port-id'],
state=port_data['port-state'])
if port_data['attachment']:
port_dict['attachment'] = dict(id=port_data['attachment'])
return port_dict
class ViewBuilder11(ViewBuilder10):
def _build_simple(self, network_data):
"""Return a simple model of a network."""
return dict(network=dict(id=network_data['net-id']))
def _build_detail(self, network_data):
"""Return a detailed model of a network. """
op_status = network_data.get('net-op-status',
OperationalStatus.UNKNOWN)
return dict(network={'id': network_data['net-id'],
'name': network_data['net-name'],
'op-status': op_status})
def _build_port(self, port_data):
"""Return details about a specific logical port."""
op_status = port_data.get('port-op-status',
OperationalStatus.UNKNOWN)
port_dict = {'id': port_data['port-id'],
'state': port_data['port-state'],
'op-status': op_status}
if port_data['attachment']:
port_dict['attachment'] = dict(id=port_data['attachment'])
return port_dict

View File

@ -1,60 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Citrix Systems
# 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 quantum.api.api_common import OperationalStatus
def get_view_builder(req, version):
base_url = req.application_url
view_builder = {
'1.0': ViewBuilder10,
'1.1': ViewBuilder11,
}[version](base_url)
return view_builder
class ViewBuilder10(object):
def __init__(self, base_url=None):
"""
:param base_url: url of the root wsgi application
"""
self.base_url = base_url
def build(self, port_data, port_details=False, att_details=False):
"""Generic method used to generate a port entity."""
port = dict(port=dict(id=port_data['port-id']))
if port_details:
port['port']['state'] = port_data['port-state']
if att_details and port_data['attachment']:
port['port']['attachment'] = dict(id=port_data['attachment'])
return port
class ViewBuilder11(ViewBuilder10):
def build(self, port_data, port_details=False, att_details=False):
"""Generates a port entity with operation status info"""
port = dict(port=dict(id=port_data['port-id']))
if port_details:
port['port']['state'] = port_data['port-state']
port['port']['op-status'] = port_data.get('port-op-status',
OperationalStatus.
UNKNOWN)
if att_details and port_data['attachment']:
port['port']['attachment'] = dict(id=port_data['attachment'])
return port

View File

@ -25,10 +25,8 @@ from sqlalchemy import create_engine
from sqlalchemy.exc import DisconnectionError
from sqlalchemy.orm import sessionmaker, exc
from quantum.api.api_common import OperationalStatus
from quantum.common import exceptions as q_exc
from quantum.db import model_base, models
from quantum.db import model_base
LOG = logging.getLogger(__name__)
@ -138,197 +136,3 @@ def unregister_models(base=BASE):
global _ENGINE
assert _ENGINE
base.metadata.drop_all(_ENGINE)
def network_create(tenant_id, name, op_status=OperationalStatus.UNKNOWN):
session = get_session()
with session.begin():
net = models.Network(tenant_id, name, op_status)
session.add(net)
session.flush()
return net
def network_all_tenant_list():
session = get_session()
return session.query(models.Network).all()
def network_list(tenant_id):
session = get_session()
return (session.query(models.Network).
filter_by(tenant_id=tenant_id).
all())
def network_get(net_id):
session = get_session()
try:
return (session.query(models.Network).
filter_by(uuid=net_id).
one())
except exc.NoResultFound:
raise q_exc.NetworkNotFound(net_id=net_id)
def network_update(net_id, tenant_id, **kwargs):
session = get_session()
net = network_get(net_id)
for key in kwargs.keys():
net[key] = kwargs[key]
session.merge(net)
session.flush()
return net
def network_destroy(net_id):
session = get_session()
try:
net = (session.query(models.Network).
filter_by(uuid=net_id).
one())
ports = (session.query(models.Port).
filter_by(network_id=net_id).
all())
for p in ports:
session.delete(p)
session.delete(net)
session.flush()
return net
except exc.NoResultFound:
raise q_exc.NetworkNotFound(net_id=net_id)
def validate_network_ownership(tenant_id, net_id):
session = get_session()
try:
return (session.query(models.Network).
filter_by(uuid=net_id).
filter_by(tenant_id=tenant_id).
one())
except exc.NoResultFound:
raise q_exc.NetworkNotFound(net_id=net_id)
def port_create(net_id, state=None, op_status=OperationalStatus.UNKNOWN):
# confirm network exists
network_get(net_id)
session = get_session()
with session.begin():
port = models.Port(net_id, op_status)
if state is None:
state = 'DOWN'
elif state not in ('ACTIVE', 'DOWN'):
raise q_exc.StateInvalid(port_state=state)
port['state'] = state
session.add(port)
session.flush()
return port
def port_list(net_id):
# confirm network exists
network_get(net_id)
session = get_session()
return (session.query(models.Port).
filter_by(network_id=net_id).
all())
def port_get(port_id, net_id, session=None):
# confirm network exists
network_get(net_id)
if not session:
session = get_session()
try:
return (session.query(models.Port).
filter_by(uuid=port_id).
filter_by(network_id=net_id).
one())
except exc.NoResultFound:
raise q_exc.PortNotFound(net_id=net_id, port_id=port_id)
def port_update(port_id, net_id, **kwargs):
# confirm network exists
network_get(net_id)
port = port_get(port_id, net_id)
session = get_session()
for key in kwargs:
if key == "state":
if kwargs[key] not in ('ACTIVE', 'DOWN'):
raise q_exc.StateInvalid(port_state=kwargs[key])
port[key] = kwargs[key]
session.merge(port)
session.flush()
return port
def port_set_attachment(port_id, net_id, new_interface_id):
# confirm network exists
network_get(net_id)
session = get_session()
port = port_get(port_id, net_id)
if new_interface_id != "":
# We are setting, not clearing, the attachment-id
if port['interface_id']:
raise q_exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port['interface_id'])
try:
port = (session.query(models.Port).
filter_by(interface_id=new_interface_id).
one())
raise q_exc.AlreadyAttached(net_id=net_id,
port_id=port_id,
att_id=new_interface_id,
att_port_id=port['uuid'])
except exc.NoResultFound:
# this is what should happen
pass
port.interface_id = new_interface_id
session.merge(port)
session.flush()
return port
def port_unset_attachment(port_id, net_id):