diff --git a/etc/api-paste.ini b/etc/api-paste.ini index 2a42529e05..a028d5bf87 100644 --- a/etc/api-paste.ini +++ b/etc/api-paste.ini @@ -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 diff --git a/quantum/api/__init__.py b/quantum/api/__init__.py index 2cec49a81f..e69de29bb2 100644 --- a/quantum/api/__init__.py +++ b/quantum/api/__init__.py @@ -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' diff --git a/quantum/api/api_common.py b/quantum/api/api_common.py index 4e0880232a..8debe36f49 100644 --- a/quantum/api/api_common.py +++ b/quantum/api/api_common.py @@ -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 diff --git a/quantum/api/attachments.py b/quantum/api/attachments.py deleted file mode 100644 index b9cb2ec455..0000000000 --- a/quantum/api/attachments.py +++ /dev/null @@ -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" diff --git a/quantum/api/faults.py b/quantum/api/faults.py deleted file mode 100644 index 3497540c44..0000000000 --- a/quantum/api/faults.py +++ /dev/null @@ -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) diff --git a/quantum/api/networks.py b/quantum/api/networks.py deleted file mode 100644 index 74ef3dd616..0000000000 --- a/quantum/api/networks.py +++ /dev/null @@ -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" diff --git a/quantum/api/ports.py b/quantum/api/ports.py deleted file mode 100644 index 3f423a0d08..0000000000 --- a/quantum/api/ports.py +++ /dev/null @@ -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" diff --git a/quantum/api/views/attachments.py b/quantum/api/views/attachments.py deleted file mode 100644 index 31351a1e0f..0000000000 --- a/quantum/api/views/attachments.py +++ /dev/null @@ -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={}) diff --git a/quantum/api/views/filters.py b/quantum/api/views/filters.py deleted file mode 100644 index 349088cc17..0000000000 --- a/quantum/api/views/filters.py +++ /dev/null @@ -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) diff --git a/quantum/api/views/networks.py b/quantum/api/views/networks.py deleted file mode 100644 index fecf467f81..0000000000 --- a/quantum/api/views/networks.py +++ /dev/null @@ -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 diff --git a/quantum/api/views/ports.py b/quantum/api/views/ports.py deleted file mode 100644 index 0b210778fa..0000000000 --- a/quantum/api/views/ports.py +++ /dev/null @@ -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 diff --git a/quantum/db/api.py b/quantum/db/api.py index 3967fc1bf0..7479169606 100644 --- a/quantum/db/api.py +++ b/quantum/db/api.py @@ -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): - # confirm network exists - network_get(net_id) - - session = get_session() - port = port_get(port_id, net_id, session) - port.interface_id = None - session.add(port) - session.flush() - - -def port_destroy(port_id, net_id): - # confirm network exists - network_get(net_id) - - session = get_session() - try: - port = (session.query(models.Port). - filter_by(uuid=port_id). - filter_by(network_id=net_id). - one()) - if port['interface_id']: - raise q_exc.PortInUse(net_id=net_id, port_id=port_id, - att_id=port['interface_id']) - session.delete(port) - session.flush() - return port - except exc.NoResultFound: - raise q_exc.PortNotFound(port_id=port_id) - - -def validate_port_ownership(tenant_id, net_id, port_id, session=None): - validate_network_ownership(tenant_id, net_id) - port_get(port_id, net_id) diff --git a/quantum/db/models.py b/quantum/db/models.py deleted file mode 100644 index 177cba3cf0..0000000000 --- a/quantum/db/models.py +++ /dev/null @@ -1,77 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2011 Nicira Networks, Inc. -# 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: Somik Behera, Nicira Networks, Inc. -# @author: Brad Hall, Nicira Networks, Inc. -# @author: Dan Wendlandt, Nicira Networks, Inc. -# @author: Salvatore Orlando, Citrix Systems - -import uuid - -from sqlalchemy import Column, String, ForeignKey -from sqlalchemy.orm import relation - -from quantum.api import api_common as common -from quantum.db import model_base - - -BASE = model_base.BASE - - -class Port(model_base.BASE): - """Represents a port on a quantum network""" - __tablename__ = 'ports' - - uuid = Column(String(255), primary_key=True) - network_id = Column(String(255), ForeignKey("networks.uuid"), - nullable=False) - interface_id = Column(String(255), nullable=True) - # Port state - Hardcoding string value at the moment - state = Column(String(8)) - op_status = Column(String(16)) - - def __init__(self, network_id, op_status=common.OperationalStatus.UNKNOWN): - self.uuid = str(uuid.uuid4()) - self.network_id = network_id - self.interface_id = None - self.state = "DOWN" - self.op_status = op_status - - def __repr__(self): - return "" % (self.uuid, self.network_id, - self.state, self.op_status, - self.interface_id) - - -class Network(model_base.BASE): - """Represents a quantum network""" - __tablename__ = 'networks' - - uuid = Column(String(255), primary_key=True) - tenant_id = Column(String(255), nullable=False) - name = Column(String(255)) - ports = relation(Port, order_by=Port.uuid, backref="network") - op_status = Column(String(16)) - - def __init__(self, tenant_id, name, - op_status=common.OperationalStatus.UNKNOWN): - self.uuid = str(uuid.uuid4()) - self.tenant_id = tenant_id - self.name = name - self.op_status = op_status - - def __repr__(self): - return "" % (self.uuid, self.name, - self.op_status, self.tenant_id) diff --git a/quantum/extensions/_portstats_view.py b/quantum/extensions/_portstats_view.py deleted file mode 100644 index d67b9147fa..0000000000 --- a/quantum/extensions/_portstats_view.py +++ /dev/null @@ -1,49 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 Nicira Networks, Inc. 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: Brad Hall, Nicira Networks, Inc -# - - -def get_view_builder(req): - """get view builder""" - base_url = req.application_url - return ViewBuilder(base_url) - - -class ViewBuilder(object): - """ - ViewBuilder for Port statistics. - - Port stats coming back from the plugin will look like this: - { - "rx_packets": 0, - "rx_bytes": 0, - "tx_errors": 0, - "rx_errors": 0, - "tx_bytes": 0, - "tx_packets": 0 - } - """ - def __init__(self, base_url): - self.base_url = base_url - - def build(self, portstat_data, is_detail=True): - # We just ignore is_detail -- it doesn't make sense in this context. - return self._build(portstat_data) - - def _build(self, portstat_data): - return portstat_data diff --git a/quantum/extensions/portstats.py b/quantum/extensions/portstats.py deleted file mode 100644 index ff87b29531..0000000000 --- a/quantum/extensions/portstats.py +++ /dev/null @@ -1,96 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 Nicira Networks, Inc. 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: Brad Hall, Nicira Networks, Inc - -import logging - -from quantum.api import faults -from quantum.common import exceptions as qexception -from quantum.common import extensions -from quantum.extensions import _portstats_view as portstats_view -from quantum.manager import QuantumManager -from quantum import wsgi - - -LOG = logging.getLogger("quantum.api.portstats") - - -class Portstats(object): - def __init__(self): - pass - - @classmethod - def get_name(cls): - return "Port Statistics" - - @classmethod - def get_alias(cls): - return "portstats" - - @classmethod - def get_description(cls): - return "Port Statistics" - - @classmethod - def get_namespace(cls): - return "http://docs.openstack.org/ext/portstats/api/v1.0" - - @classmethod - def get_updated(cls): - return "2011-12-20T10:00:00-00:00" - - @classmethod - def get_resources(cls): - """ Returns all defined resources """ - controller = StatsController(QuantumManager.get_plugin()) - parent_resource = dict(member_name="port", - collection_name="extensions/ovs/tenants/" - ":(tenant_id)/ networks/:(network_id)/ports") - return [extensions.ResourceExtension('stats', controller, - parent=parent_resource)] - - -class StatsController(wsgi.Controller): - _serialization_metadata = { - "application/xml": { - "attributes": { - "stats": ["rx_bytes", "rx_packets", "rx_errors", - "tx_bytes", "tx_packets", "tx_errors"] - } - } - } - - def __init__(self, plugin): - self._resource_name = 'stats' - self._plugin = plugin - - def _show(self, request, tenant_id, network_id, port_id): - """Returns port statistics for a given port""" - if not hasattr(self._plugin, "get_port_stats"): - return faults.QuantumHTTPError( - qexception.NotImplementedError("get_port_stats")) - - stats = self._plugin.get_port_stats(tenant_id, network_id, port_id) - builder = portstats_view.get_view_builder(request) - result = builder.build(stats, True) - return dict(stats=result) - - def index(self, request, tenant_id, network_id, port_id): - return self._show(request, tenant_id, network_id, port_id) - - def show(self, request, tenant_id, network_id, port_id, id): - return self._show(request, tenant_id, network_id, port_id) diff --git a/quantum/plugins/linuxbridge/LinuxBridgePlugin.py b/quantum/plugins/linuxbridge/LinuxBridgePlugin.py deleted file mode 100644 index 0dbaa7237e..0000000000 --- a/quantum/plugins/linuxbridge/LinuxBridgePlugin.py +++ /dev/null @@ -1,258 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012, Cisco Systems, Inc. -# -# 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: Sumit Naiksatam, Cisco Systems, Inc. - -import logging - -from quantum.api.api_common import OperationalStatus -from quantum.common import exceptions as exc -from quantum.db import api as db -from quantum.plugins.linuxbridge.common import constants as const -from quantum.plugins.linuxbridge.common import utils as cutil -from quantum.plugins.linuxbridge.db import l2network_db as cdb -from quantum.quantum_plugin_base import QuantumPluginBase - - -LOG = logging.getLogger(__name__) - - -class LinuxBridgePlugin(QuantumPluginBase): - """ - LinuxBridgePlugin provides support for Quantum abstractions - using LinuxBridge. A new VLAN is created for each network. - It relies on an agent to perform the actual bridge configuration - on each host. - """ - - def __init__(self, configfile=None): - cdb.initialize() - LOG.debug("Linux Bridge Plugin initialization done successfully") - - def _get_vlan_for_tenant(self, tenant_id, **kwargs): - """Get an available VLAN ID""" - try: - return cdb.reserve_vlanid() - except: - raise Exception("Failed to reserve VLAN ID for network") - - def _release_vlan_for_tenant(self, tenant_id, net_id, **kwargs): - """Release the ID""" - vlan_binding = cdb.get_vlan_binding(net_id) - return cdb.release_vlanid(vlan_binding[const.VLANID]) - - def _validate_port_state(self, port_state): - if port_state.upper() not in ('ACTIVE', 'DOWN'): - raise exc.StateInvalid(port_state=port_state) - return True - - def get_all_networks(self, tenant_id, **kwargs): - """ - Returns a dictionary containing all - for - the specified tenant. - """ - LOG.debug("LinuxBridgePlugin.get_all_networks() called") - networks_list = db.network_list(tenant_id) - new_networks_list = [] - for network in networks_list: - new_network_dict = cutil.make_net_dict(network[const.UUID], - network[const.NETWORKNAME], - [], network[const.OPSTATUS]) - new_networks_list.append(new_network_dict) - - # This plugin does not perform filtering at the moment - return new_networks_list - - def get_network_details(self, tenant_id, net_id): - """ - retrieved a list of all the remote vifs that - are attached to the network - """ - LOG.debug("LinuxBridgePlugin.get_network_details() called") - db.validate_network_ownership(tenant_id, net_id) - network = db.network_get(net_id) - ports_list = db.port_list(net_id) - ports_on_net = [] - for port in ports_list: - new_port = cutil.make_port_dict(port) - ports_on_net.append(new_port) - - new_network = cutil.make_net_dict(network[const.UUID], - network[const.NETWORKNAME], - ports_on_net, - network[const.OPSTATUS]) - - return new_network - - def create_network(self, tenant_id, net_name, **kwargs): - """ - Creates a new Virtual Network, and assigns it - a symbolic name. - """ - LOG.debug("LinuxBridgePlugin.create_network() called") - new_network = db.network_create(tenant_id, net_name, - op_status=OperationalStatus.UP) - new_net_id = new_network[const.UUID] - vlan_id = self._get_vlan_for_tenant(tenant_id) - cdb.add_vlan_binding(vlan_id, new_net_id) - new_net_dict = { - const.NET_ID: new_net_id, - const.NET_NAME: net_name, - const.NET_PORTS: [], - const.NET_OP_STATUS: new_network[const.OPSTATUS], - } - return new_net_dict - - def delete_network(self, tenant_id, net_id): - """ - Deletes the network with the specified network identifier - belonging to the specified tenant. - """ - LOG.debug("LinuxBridgePlugin.delete_network() called") - db.validate_network_ownership(tenant_id, net_id) - net = db.network_get(net_id) - if net: - ports_on_net = db.port_list(net_id) - if len(ports_on_net) > 0: - for port in ports_on_net: - if port[const.INTERFACEID]: - raise exc.NetworkInUse(net_id=net_id) - for port in ports_on_net: - self.delete_port(tenant_id, net_id, port[const.UUID]) - - net_dict = cutil.make_net_dict(net[const.UUID], - net[const.NETWORKNAME], - [], net[const.OPSTATUS]) - try: - self._release_vlan_for_tenant(tenant_id, net_id) - cdb.remove_vlan_binding(net_id) - except Exception as excp: - LOG.warning("Exception: %s" % excp) - db.network_update(net_id, tenant_id, {const.OPSTATUS: - OperationalStatus.DOWN}) - db.network_destroy(net_id) - return net_dict - # Network not found - raise exc.NetworkNotFound(net_id=net_id) - - def update_network(self, tenant_id, net_id, **kwargs): - """ - Updates the attributes of a particular Virtual Network. - """ - LOG.debug("LinuxBridgePlugin.update_network() called") - db.validate_network_ownership(tenant_id, net_id) - network = db.network_update(net_id, tenant_id, **kwargs) - net_dict = cutil.make_net_dict(network[const.UUID], - network[const.NETWORKNAME], - [], network[const.OPSTATUS]) - return net_dict - - def get_all_ports(self, tenant_id, net_id, **kwargs): - """ - Retrieves all port identifiers belonging to the - specified Virtual Network. - """ - LOG.debug("LinuxBridgePlugin.get_all_ports() called") - db.validate_network_ownership(tenant_id, net_id) - ports_list = db.port_list(net_id) - ports_on_net = [] - for port in ports_list: - new_port = cutil.make_port_dict(port) - ports_on_net.append(new_port) - - # This plugin does not perform filtering at the moment - return ports_on_net - - def get_port_details(self, tenant_id, net_id, port_id): - """ - This method allows the user to retrieve a remote interface - that is attached to this particular port. - """ - LOG.debug("LinuxBridgePlugin.get_port_details() called") - db.validate_port_ownership(tenant_id, net_id, port_id) - port = db.port_get(port_id, net_id) - new_port_dict = cutil.make_port_dict(port) - return new_port_dict - - def create_port(self, tenant_id, net_id, port_state=None, **kwargs): - """ - Creates a port on the specified Virtual Network. - """ - LOG.debug("LinuxBridgePlugin.create_port() called") - db.validate_network_ownership(tenant_id, net_id) - port = db.port_create(net_id, port_state, - op_status=OperationalStatus.DOWN) - new_port_dict = cutil.make_port_dict(port) - return new_port_dict - - def update_port(self, tenant_id, net_id, port_id, **kwargs): - """ - Updates the attributes of a port on the specified Virtual Network. - """ - LOG.debug("LinuxBridgePlugin.update_port() called") - db.validate_port_ownership(tenant_id, net_id, port_id) - self._validate_port_state(kwargs["state"]) - port = db.port_update(port_id, net_id, **kwargs) - - new_port_dict = cutil.make_port_dict(port) - return new_port_dict - - def delete_port(self, tenant_id, net_id, port_id): - """ - Deletes a port on a specified Virtual Network, - if the port contains a remote interface attachment, - the remote interface is first un-plugged and then the port - is deleted. - """ - LOG.debug("LinuxBridgePlugin.delete_port() called") - db.validate_port_ownership(tenant_id, net_id, port_id) - port = db.port_get(port_id, net_id) - attachment_id = port[const.INTERFACEID] - if not attachment_id: - db.port_destroy(port_id, net_id) - new_port_dict = cutil.make_port_dict(port) - return new_port_dict - else: - raise exc.PortInUse(port_id=port_id, net_id=net_id, - att_id=attachment_id) - - def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id): - """ - Attaches a remote interface to the specified port on the - specified Virtual Network. - """ - LOG.debug("LinuxBridgePlugin.plug_interface() called") - db.validate_port_ownership(tenant_id, net_id, port_id) - port = db.port_get(port_id, net_id) - attachment_id = port[const.INTERFACEID] - if attachment_id: - raise exc.PortInUse(port_id=port_id, net_id=net_id, - att_id=attachment_id) - db.port_set_attachment(port_id, net_id, remote_interface_id) - - def unplug_interface(self, tenant_id, net_id, port_id): - """ - Detaches a remote interface from the specified port on the - specified Virtual Network. - """ - LOG.debug("LinuxBridgePlugin.unplug_interface() called") - db.validate_port_ownership(tenant_id, net_id, port_id) - port = db.port_get(port_id, net_id) - attachment_id = port[const.INTERFACEID] - if attachment_id is None: - return - db.port_unset_attachment(port_id, net_id) - db.port_update(port_id, net_id, op_status=OperationalStatus.DOWN) diff --git a/quantum/plugins/linuxbridge/common/utils.py b/quantum/plugins/linuxbridge/common/utils.py deleted file mode 100644 index f14e8ea7b4..0000000000 --- a/quantum/plugins/linuxbridge/common/utils.py +++ /dev/null @@ -1,53 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 Cisco Systems, Inc. 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: Sumit Naiksatam, Cisco Systems, Inc. - -import logging - -from quantum.api.api_common import OperationalStatus -from quantum.plugins.linuxbridge.common import constants as const - - -LOG = logging.getLogger(__name__) - - -def make_net_dict(net_id, net_name, ports, op_status): - """Helper funciton""" - res = { - const.NET_ID: net_id, - const.NET_NAME: net_name, - const.NET_OP_STATUS: op_status, - } - if ports: - res[const.NET_PORTS] = ports - return res - - -def make_port_dict(port): - """Helper funciton""" - if port[const.PORTSTATE] == const.PORT_UP: - op_status = port[const.OPSTATUS] - else: - op_status = OperationalStatus.DOWN - - return { - const.PORT_ID: str(port[const.UUID]), - const.PORT_STATE: port[const.PORTSTATE], - const.PORT_OP_STATUS: op_status, - const.NET_ID: port[const.NETWORKID], - const.ATTACHMENT: port[const.INTERFACEID], - } diff --git a/quantum/plugins/linuxbridge/db/l2network_db.py b/quantum/plugins/linuxbridge/db/l2network_db.py index a0ca30f75e..25c3aa02f6 100644 --- a/quantum/plugins/linuxbridge/db/l2network_db.py +++ b/quantum/plugins/linuxbridge/db/l2network_db.py @@ -27,13 +27,12 @@ from quantum.db import models_v2 from quantum.openstack.common import cfg from quantum.plugins.linuxbridge.common import config from quantum.plugins.linuxbridge.common import exceptions as c_exc -from quantum.plugins.linuxbridge.db import l2network_models from quantum.plugins.linuxbridge.db import l2network_models_v2 LOG = logging.getLogger(__name__) # The global variable for the database version model -L2_MODEL = l2network_models +L2_MODEL = l2network_models_v2 def initialize(base=None): @@ -44,7 +43,6 @@ def initialize(base=None): cfg.CONF.DATABASE.reconnect_interval}) if base: options.update({"base": base}) - L2_MODEL = l2network_models_v2 db.configure_db(options) create_vlanids() @@ -182,7 +180,7 @@ def reserve_specific_vlanid(vlan_id): raise q_exc.InvalidInput(error_message=msg) session = db.get_session() try: - rvlanid = (session.query(l2network_models.VlanID). + rvlanid = (session.query(l2network_models_v2.VlanID). filter_by(vlan_id=vlan_id). one()) if rvlanid["vlan_used"]: @@ -191,7 +189,7 @@ def reserve_specific_vlanid(vlan_id): rvlanid["vlan_used"] = True session.merge(rvlanid) except exc.NoResultFound: - rvlanid = l2network_models.VlanID(vlan_id) + rvlanid = l2network_models_v2.VlanID(vlan_id) LOG.debug("reserving non-dynamic vlanid %s" % vlan_id) rvlanid["vlan_used"] = True session.add(rvlanid) diff --git a/quantum/plugins/linuxbridge/db/l2network_models.py b/quantum/plugins/linuxbridge/db/l2network_models.py deleted file mode 100644 index 540c93fb46..0000000000 --- a/quantum/plugins/linuxbridge/db/l2network_models.py +++ /dev/null @@ -1,50 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012, Cisco Systems, Inc. -# -# 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: Rohit Agarwalla, Cisco Systems, Inc. - -from sqlalchemy import Column, Integer, String, Boolean - -from quantum.db.models import BASE - - -class VlanID(BASE): - """Represents a vlan_id usage""" - __tablename__ = 'vlan_ids' - - vlan_id = Column(Integer, primary_key=True) - vlan_used = Column(Boolean) - - def __init__(self, vlan_id): - self.vlan_id = vlan_id - self.vlan_used = False - - def __repr__(self): - return "" % (self.vlan_id, self.vlan_used) - - -class VlanBinding(BASE): - """Represents a binding of vlan_id to network_id""" - __tablename__ = 'vlan_bindings' - - vlan_id = Column(Integer, primary_key=True) - network_id = Column(String(255), nullable=False) - - def __init__(self, vlan_id, network_id): - self.vlan_id = vlan_id - self.network_id = network_id - - def __repr__(self): - return "" % (self.vlan_id, self.network_id) diff --git a/quantum/plugins/linuxbridge/run_tests.py b/quantum/plugins/linuxbridge/run_tests.py index 7211045213..44f571e8d2 100644 --- a/quantum/plugins/linuxbridge/run_tests.py +++ b/quantum/plugins/linuxbridge/run_tests.py @@ -35,7 +35,6 @@ sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(__file__)) -from quantum.api.api_common import OperationalStatus from quantum.common.test_lib import run_tests, test_config import quantum.tests.unit @@ -47,10 +46,7 @@ if __name__ == '__main__': # we should only invoked the tests once invoke_once = len(sys.argv) > 1 - test_config['plugin_name'] = "LinuxBridgePlugin.LinuxBridgePlugin" test_config['plugin_name_v2'] = "lb_quantum_plugin.LinuxBridgePluginV2" - test_config['default_net_op_status'] = OperationalStatus.UP - test_config['default_port_op_status'] = OperationalStatus.DOWN cwd = os.getcwd() c = config.Config(stream=sys.stdout, diff --git a/quantum/plugins/linuxbridge/tests/unit/test_database.py b/quantum/plugins/linuxbridge/tests/unit/test_database.py deleted file mode 100644 index af574bd4bf..0000000000 --- a/quantum/plugins/linuxbridge/tests/unit/test_database.py +++ /dev/null @@ -1,289 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012, Cisco Systems, Inc. -# -# 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: Rohit Agarwalla, Cisco Systems, Inc. - -""" -test_database.py is an independent test suite -that tests the database api method calls -""" - -import logging -import unittest2 as unittest - -import quantum.db.api as db -from quantum.openstack.common import cfg -import quantum.plugins.linuxbridge.common.exceptions as c_exc -import quantum.plugins.linuxbridge.db.l2network_db as l2network_db - - -LOG = logging.getLogger(__name__) - - -class L2networkDB(object): - - """Class conisting of methods to call L2network db methods""" - def get_all_vlan_bindings(self): - """Get all vlan binding into a list of dict""" - vlans = [] - try: - for vlan_bind in l2network_db.get_all_vlan_bindings(): - LOG.debug("Getting vlan bindings for vlan: %s" % - vlan_bind.vlan_id) - vlan_dict = {} - vlan_dict["vlan-id"] = str(vlan_bind.vlan_id) - vlan_dict["net-id"] = str(vlan_bind.network_id) - vlans.append(vlan_dict) - except Exception, exc: - LOG.error("Failed to get all vlan bindings: %s" % str(exc)) - return vlans - - def get_vlan_binding(self, network_id): - """Get a vlan binding""" - vlan = [] - try: - for vlan_bind in l2network_db.get_vlan_binding(network_id): - LOG.debug("Getting vlan binding for vlan: %s" % - vlan_bind.vlan_id) - vlan_dict = {} - vlan_dict["vlan-id"] = str(vlan_bind.vlan_id) - vlan_dict["net-id"] = str(vlan_bind.network_id) - vlan.append(vlan_dict) - except Exception, exc: - LOG.error("Failed to get vlan binding: %s" % str(exc)) - return vlan - - def create_vlan_binding(self, vlan_id, network_id): - """Create a vlan binding""" - vlan_dict = {} - try: - res = l2network_db.add_vlan_binding(vlan_id, network_id) - LOG.debug("Created vlan binding for vlan: %s" % res.vlan_id) - vlan_dict["vlan-id"] = str(res.vlan_id) - vlan_dict["net-id"] = str(res.network_id) - return vlan_dict - except Exception, exc: - LOG.error("Failed to create vlan binding: %s" % str(exc)) - - def delete_vlan_binding(self, network_id): - """Delete a vlan binding""" - try: - res = l2network_db.remove_vlan_binding(network_id) - LOG.debug("Deleted vlan binding for vlan: %s" % res.vlan_id) - vlan_dict = {} - vlan_dict["vlan-id"] = str(res.vlan_id) - return vlan_dict - except Exception, exc: - raise Exception("Failed to delete vlan binding: %s" % str(exc)) - - def update_vlan_binding(self, network_id, vlan_id): - """Update a vlan binding""" - try: - res = l2network_db.update_vlan_binding(network_id, vlan_id) - LOG.debug("Updating vlan binding for vlan: %s" % res.vlan_id) - vlan_dict = {} - vlan_dict["vlan-id"] = str(res.vlan_id) - vlan_dict["net-id"] = str(res.network_id) - return vlan_dict - except Exception, exc: - raise Exception("Failed to update vlan binding: %s" % str(exc)) - - -class QuantumDB(object): - """Class conisting of methods to call Quantum db methods""" - def get_all_networks(self, tenant_id): - """Get all networks""" - nets = [] - try: - for net in db.network_list(tenant_id): - LOG.debug("Getting network: %s" % net.uuid) - net_dict = {} - net_dict["tenant-id"] = net.tenant_id - net_dict["net-id"] = str(net.uuid) - net_dict["net-name"] = net.name - nets.append(net_dict) - except Exception, exc: - LOG.error("Failed to get all networks: %s" % str(exc)) - return nets - - def create_network(self, tenant_id, net_name): - """Create a network""" - net_dict = {} - try: - res = db.network_create(tenant_id, - net_name, - op_status="UP") - LOG.debug("Created network: %s" % res.uuid) - net_dict["tenant-id"] = res.tenant_id - net_dict["net-id"] = str(res.uuid) - net_dict["net-name"] = res.name - return net_dict - except Exception, exc: - LOG.error("Failed to create network: %s" % str(exc)) - - def delete_network(self, net_id): - """Delete a network""" - try: - net = db.network_destroy(net_id) - LOG.debug("Deleted network: %s" % net.uuid) - net_dict = {} - net_dict["net-id"] = str(net.uuid) - return net_dict - except Exception, exc: - raise Exception("Failed to delete port: %s" % str(exc)) - - -class L2networkDBTest(unittest.TestCase): - """Class conisting of L2network DB unit tests""" - def setUp(self): - """Setup for tests""" - l2network_db.initialize() - l2network_db.create_vlanids() - self.dbtest = L2networkDB() - self.quantum = QuantumDB() - LOG.debug("Setup") - - def tearDown(self): - """Tear Down""" - db.clear_db() - - def test_create_vlanbinding(self): - net1 = self.quantum.create_network("t1", "netid1") - vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"]) - self.assertTrue(vlan1["vlan-id"] == "10") - self.teardown_vlanbinding() - self.teardown_network() - - def test_getall_vlanbindings(self): - net1 = self.quantum.create_network("t1", "netid1") - net2 = self.quantum.create_network("t1", "netid2") - vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"]) - self.assertTrue(vlan1["vlan-id"] == "10") - vlan2 = self.dbtest.create_vlan_binding(20, net2["net-id"]) - self.assertTrue(vlan2["vlan-id"] == "20") - vlans = self.dbtest.get_all_vlan_bindings() - self.assertTrue(len(vlans) == 2) - self.teardown_vlanbinding() - self.teardown_network() - - def test_delete_vlanbinding(self): - net1 = self.quantum.create_network("t1", "netid1") - vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"]) - self.assertTrue(vlan1["vlan-id"] == "10") - self.dbtest.delete_vlan_binding(net1["net-id"]) - vlans = self.dbtest.get_all_vlan_bindings() - count = 0 - for vlan in vlans: - if vlan["vlan-id"] is "10": - count += 1 - self.assertTrue(count == 0) - self.teardown_vlanbinding() - self.teardown_network() - - def test_update_vlanbinding(self): - net1 = self.quantum.create_network("t1", "netid1") - vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"]) - self.assertTrue(vlan1["vlan-id"] == "10") - vlan1 = self.dbtest.update_vlan_binding(net1["net-id"], 11) - self.assertTrue(vlan1["vlan-id"] == "11") - self.teardown_vlanbinding() - self.teardown_network() - - def test_vlanids(self): - l2network_db.create_vlanids() - vlanids = l2network_db.get_all_vlanids() - self.assertGreater(len(vlanids), 0) - vlanid = l2network_db.reserve_vlanid() - used = l2network_db.is_vlanid_used(vlanid) - self.assertTrue(used) - used = l2network_db.release_vlanid(vlanid) - self.assertFalse(used) - self.teardown_vlanbinding() - self.teardown_network() - - def test_specific_vlanid_outside(self): - l2network_db.create_vlanids() - orig_count = len(l2network_db.get_all_vlanids()) - self.assertGreater(orig_count, 0) - vlan_id = 7 # outside range dynamically allocated - with self.assertRaises(c_exc.VlanIDNotFound): - l2network_db.is_vlanid_used(vlan_id) - l2network_db.reserve_specific_vlanid(vlan_id) - self.assertTrue(l2network_db.is_vlanid_used(vlan_id)) - count = len(l2network_db.get_all_vlanids()) - self.assertEqual(count, orig_count + 1) - used = l2network_db.release_vlanid(vlan_id) - self.assertFalse(used) - with self.assertRaises(c_exc.VlanIDNotFound): - l2network_db.is_vlanid_used(vlan_id) - count = len(l2network_db.get_all_vlanids()) - self.assertEqual(count, orig_count) - self.teardown_vlanbinding() - self.teardown_network() - - def test_specific_vlanid_inside(self): - l2network_db.create_vlanids() - orig_count = len(l2network_db.get_all_vlanids()) - self.assertGreater(orig_count, 0) - vlan_id = 1007 # inside range dynamically allocated - self.assertFalse(l2network_db.is_vlanid_used(vlan_id)) - l2network_db.reserve_specific_vlanid(vlan_id) - self.assertTrue(l2network_db.is_vlanid_used(vlan_id)) - count = len(l2network_db.get_all_vlanids()) - self.assertEqual(count, orig_count) - used = l2network_db.release_vlanid(vlan_id) - self.assertFalse(used) - self.assertFalse(l2network_db.is_vlanid_used(vlan_id)) - count = len(l2network_db.get_all_vlanids()) - self.assertEqual(count, orig_count) - self.teardown_vlanbinding() - self.teardown_network() - - def teardown_network(self): - """tearDown Network table""" - LOG.debug("Tearing Down Network") - nets = self.quantum.get_all_networks("t1") - for net in nets: - netid = net["net-id"] - self.quantum.delete_network(netid) - - def teardown_vlanbinding(self): - """tearDown VlanBinding table""" - LOG.debug("Tearing Down Vlan Binding") - vlans = self.dbtest.get_all_vlan_bindings() - for vlan in vlans: - netid = vlan["net-id"] - self.dbtest.delete_vlan_binding(netid) - - -class ConfigurationTest(unittest.TestCase): - - def test_defaults(self): - self.assertEqual('sqlite://', - cfg.CONF.DATABASE.sql_connection) - self.assertEqual(-1, - cfg.CONF.DATABASE.sql_max_retries) - self.assertEqual(2, - cfg.CONF.DATABASE.reconnect_interval) - self.assertEqual(2, - cfg.CONF.AGENT.polling_interval) - self.assertEqual('sudo', - cfg.CONF.AGENT.root_helper) - self.assertEqual(1000, - cfg.CONF.VLANS.vlan_start) - self.assertEqual(3000, - cfg.CONF.VLANS.vlan_end) - self.assertEqual('eth1', - cfg.CONF.LINUX_BRIDGE.physical_interface) diff --git a/quantum/plugins/openvswitch/ovs_db.py b/quantum/plugins/openvswitch/ovs_db.py deleted file mode 100644 index 49adade448..0000000000 --- a/quantum/plugins/openvswitch/ovs_db.py +++ /dev/null @@ -1,56 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2011 Nicira Networks, Inc. -# 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: Somik Behera, Nicira Networks, Inc. -# @author: Brad Hall, Nicira Networks, Inc. -# @author: Dan Wendlandt, Nicira Networks, Inc. - -from sqlalchemy.orm import exc - -import quantum.db.api as db -from quantum.plugins.openvswitch import ovs_models - - -def get_vlans(): - session = db.get_session() - try: - bindings = (session.query(ovs_models.VlanBinding). - all()) - except exc.NoResultFound: - return [] - res = [] - for x in bindings: - res.append((x.vlan_id, x.network_id)) - return res - - -def add_vlan_binding(vlanid, netid): - session = db.get_session() - binding = ovs_models.VlanBinding(vlanid, netid) - session.add(binding) - session.flush() - return binding.vlan_id - - -def remove_vlan_binding(netid): - session = db.get_session() - try: - binding = (session.query(ovs_models.VlanBinding). - filter_by(network_id=netid). - one()) - session.delete(binding) - except exc.NoResultFound: - pass - session.flush() diff --git a/quantum/plugins/openvswitch/ovs_models.py b/quantum/plugins/openvswitch/ovs_models.py deleted file mode 100644 index 52555e1750..0000000000 --- a/quantum/plugins/openvswitch/ovs_models.py +++ /dev/null @@ -1,50 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2011 Nicira Networks, Inc. -# 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: Somik Behera, Nicira Networks, Inc. -# @author: Brad Hall, Nicira Networks, Inc. -# @author: Dan Wendlandt, Nicira Networks, Inc. - -from sqlalchemy import Column, Integer, String - -from quantum.db.models import BASE - - -class VlanBinding(BASE): - """Represents a binding of network_id, vlan_id""" - __tablename__ = 'vlan_bindings' - - vlan_id = Column(Integer, primary_key=True) - network_id = Column(String(255)) - - def __init__(self, vlan_id, network_id): - self.network_id = network_id - self.vlan_id = vlan_id - - def __repr__(self): - return "" % (self.vlan_id, self.network_id) - - -class TunnelIP(BASE): - """Represents a remote IP in tunnel mode""" - __tablename__ = 'tunnel_ips' - - ip_address = Column(String(255), primary_key=True) - - def __init__(self, ip_address): - self.ip_address = ip_address - - def __repr__(self): - return "" % (self.ip_address) diff --git a/quantum/plugins/openvswitch/ovs_quantum_plugin.py b/quantum/plugins/openvswitch/ovs_quantum_plugin.py index a0156c81ba..aa24cbdcaa 100644 --- a/quantum/plugins/openvswitch/ovs_quantum_plugin.py +++ b/quantum/plugins/openvswitch/ovs_quantum_plugin.py @@ -23,7 +23,6 @@ import logging import os -from quantum.api.api_common import OperationalStatus from quantum.api.v2 import attributes from quantum.common import exceptions as q_exc from quantum.common.utils import find_config_file @@ -32,223 +31,13 @@ from quantum.db import db_base_plugin_v2 from quantum.db import models_v2 from quantum.openstack.common import cfg from quantum.plugins.openvswitch.common import config -from quantum.plugins.openvswitch import ovs_db from quantum.plugins.openvswitch import ovs_db_v2 -from quantum.quantum_plugin_base import QuantumPluginBase from quantum import policy LOG = logging.getLogger("ovs_quantum_plugin") -# Exception thrown if no more VLANs are available -class NoFreeVLANException(Exception): - # TODO(rkukura) Remove this class when removing V1 API - pass - - -class VlanMap(object): - # TODO(rkukura) Remove this class when removing V1 API - vlans = {} - net_ids = {} - free_vlans = set() - - def __init__(self, vlan_min=1, vlan_max=4094): - if vlan_min > vlan_max: - LOG.warn("Using default VLAN values! vlan_min = %s is larger" - " than vlan_max = %s!" % (vlan_min, vlan_max)) - vlan_min = 1 - vlan_max = 4094 - - self.vlan_min = vlan_min - self.vlan_max = vlan_max - self.vlans.clear() - self.net_ids.clear() - self.free_vlans = set(xrange(self.vlan_min, self.vlan_max + 1)) - - def already_used(self, vlan_id, network_id): - self.free_vlans.remove(vlan_id) - self.set_vlan(vlan_id, network_id) - - def set_vlan(self, vlan_id, network_id): - self.vlans[vlan_id] = network_id - self.net_ids[network_id] = vlan_id - - def acquire(self, network_id): - if len(self.free_vlans): - vlan = self.free_vlans.pop() - self.set_vlan(vlan, network_id) - LOG.debug("Allocated VLAN %s for network %s" % (vlan, network_id)) - return vlan - else: - raise NoFreeVLANException("No VLAN free for network %s" % - network_id) - - def acquire_specific(self, vlan_id, network_id): - LOG.debug("Allocating specific VLAN %s for network %s" - % (vlan_id, network_id)) - if vlan_id < 1 or vlan_id > 4094: - msg = _("Specified VLAN %s outside legal range (1-4094)") % vlan_id - raise q_exc.InvalidInput(error_message=msg) - if self.vlans.get(vlan_id): - raise q_exc.VlanIdInUse(vlan_id=vlan_id) - self.free_vlans.discard(vlan_id) - self.set_vlan(vlan_id, network_id) - - def release(self, network_id): - vlan = self.net_ids.get(network_id, None) - if vlan is not None: - if vlan >= self.vlan_min and vlan <= self.vlan_max: - self.free_vlans.add(vlan) - del self.vlans[vlan] - del self.net_ids[network_id] - LOG.debug("Deallocated VLAN %s (used by network %s)" % - (vlan, network_id)) - else: - LOG.error("No vlan found with network \"%s\"", network_id) - - def populate_already_used(self, vlans): - for vlan_id, network_id in vlans: - LOG.debug("Adding already populated vlan %s -> %s" % - (vlan_id, network_id)) - self.already_used(vlan_id, network_id) - - -class OVSQuantumPlugin(QuantumPluginBase): - # TODO(rkukura) Remove this class when removing V1 API - - def __init__(self, configfile=None): - options = {"sql_connection": cfg.CONF.DATABASE.sql_connection} - sql_max_retries = cfg.CONF.DATABASE.sql_max_retries - options.update({"sql_max_retries": sql_max_retries}) - reconnect_interval = cfg.CONF.DATABASE.reconnect_interval - options.update({"reconnect_interval": reconnect_interval}) - db.configure_db(options) - - self.vmap = VlanMap(cfg.CONF.OVS.vlan_min, cfg.CONF.OVS.vlan_max) - # Populate the map with anything that is already present in the - # database - self.vmap.populate_already_used(ovs_db.get_vlans()) - - def get_all_networks(self, tenant_id, **kwargs): - nets = [] - for x in db.network_list(tenant_id): - LOG.debug("Adding network: %s" % x.uuid) - nets.append(self._make_net_dict(str(x.uuid), x.name, - None, x.op_status)) - return nets - - def _make_net_dict(self, net_id, net_name, ports, op_status): - res = { - 'net-id': net_id, - 'net-name': net_name, - 'net-op-status': op_status, - } - if ports: - res['net-ports'] = ports - return res - - def create_network(self, tenant_id, net_name, **kwargs): - net = db.network_create(tenant_id, net_name, - op_status=OperationalStatus.UP) - try: - vlan_id = self.vmap.acquire(str(net.uuid)) - except NoFreeVLANException: - db.network_destroy(net.uuid) - raise - - LOG.debug("Created network: %s" % net) - ovs_db.add_vlan_binding(vlan_id, str(net.uuid)) - return self._make_net_dict(str(net.uuid), net.name, [], net.op_status) - - def delete_network(self, tenant_id, net_id): - db.validate_network_ownership(tenant_id, net_id) - net = db.network_get(net_id) - - # Verify that no attachments are plugged into the network - for port in db.port_list(net_id): - if port.interface_id: - raise q_exc.NetworkInUse(net_id=net_id) - net = db.network_destroy(net_id) - ovs_db.remove_vlan_binding(net_id) - self.vmap.release(net_id) - return self._make_net_dict(str(net.uuid), net.name, [], net.op_status) - - def get_network_details(self, tenant_id, net_id): - db.validate_network_ownership(tenant_id, net_id) - net = db.network_get(net_id) - ports = self.get_all_ports(tenant_id, net_id) - return self._make_net_dict(str(net.uuid), net.name, - ports, net.op_status) - - def update_network(self, tenant_id, net_id, **kwargs): - db.validate_network_ownership(tenant_id, net_id) - net = db.network_update(net_id, tenant_id, **kwargs) - return self._make_net_dict(str(net.uuid), net.name, - None, net.op_status) - - def _make_port_dict(self, port): - if port.state == "ACTIVE": - op_status = port.op_status - else: - op_status = OperationalStatus.DOWN - - return { - 'port-id': str(port.uuid), - 'port-state': port.state, - 'port-op-status': op_status, - 'net-id': port.network_id, - 'attachment': port.interface_id, - } - - def get_all_ports(self, tenant_id, net_id, **kwargs): - ids = [] - db.validate_network_ownership(tenant_id, net_id) - ports = db.port_list(net_id) - # This plugin does not perform filtering at the moment - return [{'port-id': str(p.uuid)} for p in ports] - - def create_port(self, tenant_id, net_id, port_state=None, **kwargs): - LOG.debug("Creating port with network_id: %s" % net_id) - db.validate_network_ownership(tenant_id, net_id) - port = db.port_create(net_id, port_state, - op_status=OperationalStatus.DOWN) - return self._make_port_dict(port) - - def delete_port(self, tenant_id, net_id, port_id): - db.validate_port_ownership(tenant_id, net_id, port_id) - port = db.port_destroy(port_id, net_id) - return self._make_port_dict(port) - - def update_port(self, tenant_id, net_id, port_id, **kwargs): - """ - Updates the state of a port on the specified Virtual Network. - """ - db.validate_port_ownership(tenant_id, net_id, port_id) - port = db.port_get(port_id, net_id) - db.port_update(port_id, net_id, **kwargs) - return self._make_port_dict(port) - - def get_port_details(self, tenant_id, net_id, port_id): - db.validate_port_ownership(tenant_id, net_id, port_id) - port = db.port_get(port_id, net_id) - return self._make_port_dict(port) - - def plug_interface(self, tenant_id, net_id, port_id, remote_iface_id): - db.validate_port_ownership(tenant_id, net_id, port_id) - db.port_set_attachment(port_id, net_id, remote_iface_id) - - def unplug_interface(self, tenant_id, net_id, port_id): - db.validate_port_ownership(tenant_id, net_id, port_id) - db.port_set_attachment(port_id, net_id, "") - db.port_update(port_id, net_id, op_status=OperationalStatus.DOWN) - - def get_interface_details(self, tenant_id, net_id, port_id): - db.validate_port_ownership(tenant_id, net_id, port_id) - res = db.port_get(port_id, net_id) - return res.interface_id - - class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2): """Implement the Quantum abstractions using Open vSwitch. diff --git a/quantum/plugins/openvswitch/run_tests.py b/quantum/plugins/openvswitch/run_tests.py index 84c99e937f..8496c4a257 100755 --- a/quantum/plugins/openvswitch/run_tests.py +++ b/quantum/plugins/openvswitch/run_tests.py @@ -34,7 +34,6 @@ from nose import core sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(__file__)) -from quantum.api.api_common import OperationalStatus from quantum.common.test_lib import run_tests, test_config import quantum.tests.unit @@ -46,10 +45,7 @@ if __name__ == '__main__': # we should only invoked the tests once invoke_once = len(sys.argv) > 1 - test_config['plugin_name'] = "ovs_quantum_plugin.OVSQuantumPlugin" test_config['plugin_name_v2'] = "ovs_quantum_plugin.OVSQuantumPluginV2" - test_config['default_net_op_status'] = OperationalStatus.UP - test_config['default_port_op_status'] = OperationalStatus.DOWN cwd = os.getcwd() c = config.Config(stream=sys.stdout, diff --git a/quantum/plugins/openvswitch/tests/unit/test_vlan_map.py b/quantum/plugins/openvswitch/tests/unit/test_vlan_map.py deleted file mode 100644 index baa5e63211..0000000000 --- a/quantum/plugins/openvswitch/tests/unit/test_vlan_map.py +++ /dev/null @@ -1,67 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2011 Nicira Networks, Inc. -# 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 unittest - -from quantum.plugins.openvswitch.ovs_quantum_plugin import ( - NoFreeVLANException, - VlanMap, -) - - -class VlanMapTest(unittest.TestCase): - # TODO(rkukura) Remove this class when removing V1 API - - def setUp(self): - self.vmap = VlanMap() - - def tearDown(self): - pass - - def testAddVlan(self): - vlan_id = self.vmap.acquire("foobar") - self.assertTrue(vlan_id >= self.vmap.vlan_min) - self.assertTrue(vlan_id <= self.vmap.vlan_max) - - def testReleaseVlan(self): - vlan_id = self.vmap.acquire("foobar") - self.vmap.release("foobar") - - def testAddRelease4kVlans(self): - vlan_id = None - num_vlans = self.vmap.vlan_max - self.vmap.vlan_min - for id in xrange(num_vlans): - vlan_id = self.vmap.acquire("net-%s" % id) - self.assertTrue(vlan_id >= self.vmap.vlan_min) - self.assertTrue(vlan_id <= self.vmap.vlan_max) - for id in xrange(num_vlans): - self.vmap.release("net-%s" % id) - - def testAlreadyUsed(self): - existing_vlan = 2 - self.vmap.already_used(existing_vlan, "net1") - try: - # this value is high enough that we will exhaust - # all VLANs. We want to make sure 'existing_vlan' - # is never reallocated. - num_vlans = self.vmap.vlan_max - self.vmap.vlan_min + 1 - for x in xrange(num_vlans): - vlan_id = self.vmap.acquire("net-%x" % x) - self.assertTrue(vlan_id != existing_vlan) - - self.fail("Did not run out of VLANs as expected") - except NoFreeVLANException: - pass # Expected exit diff --git a/quantum/plugins/ryu/agent/ryu_quantum_agent.py b/quantum/plugins/ryu/agent/ryu_quantum_agent.py index 65e1c757c6..dc1ad45804 100755 --- a/quantum/plugins/ryu/agent/ryu_quantum_agent.py +++ b/quantum/plugins/ryu/agent/ryu_quantum_agent.py @@ -130,13 +130,12 @@ def check_ofp_mode(db): class OVSQuantumOFPRyuAgent: - def __init__(self, integ_br, db, root_helper, target_v2_api=False): + def __init__(self, integ_br, db, root_helper): self.root_helper = root_helper (ofp_controller_addr, ofp_rest_api_addr) = check_ofp_mode(db) self.nw_id_external = rest_nw_id.NW_ID_EXTERNAL self.api = OFPClient(ofp_rest_api_addr) - self.target_v2_api = target_v2_api self._setup_integration_br(integ_br, ofp_controller_addr) def _setup_integration_br(self, integ_br, ofp_controller_addr): @@ -151,16 +150,10 @@ class OVSQuantumOFPRyuAgent: def _all_bindings(self, db): """return interface id -> port which include network id bindings""" - if self.target_v2_api: - return dict((port.device_id, port) for port in db.ports.all()) - else: - return dict((port.interface_id, port) for port in db.ports.all()) + return dict((port.device_id, port) for port in db.ports.all()) def _set_port_status(self, port, status): - if self.target_v2_api: - port.status = status - else: - port.op_status = status + port.status = status def daemon_loop(self, db): # on startup, register all existing ports @@ -232,13 +225,12 @@ def main(): integ_br = cfg.CONF.OVS.integration_bridge root_helper = cfg.CONF.AGENT.root_helper - target_v2_api = cfg.CONF.AGENT.target_v2_api options = {"sql_connection": cfg.CONF.DATABASE.sql_connection} db = SqlSoup(options["sql_connection"]) LOG.info("Connecting to database \"%s\" on %s", db.engine.url.database, db.engine.url.host) - plugin = OVSQuantumOFPRyuAgent(integ_br, db, root_helper, target_v2_api) + plugin = OVSQuantumOFPRyuAgent(integ_br, db, root_helper) plugin.daemon_loop(db) sys.exit(0) diff --git a/quantum/plugins/ryu/common/config.py b/quantum/plugins/ryu/common/config.py index 66f2656f1a..280560d67b 100644 --- a/quantum/plugins/ryu/common/config.py +++ b/quantum/plugins/ryu/common/config.py @@ -30,7 +30,6 @@ ovs_opts = [ ] agent_opts = [ - cfg.BoolOpt('target_v2_api', default=True), cfg.IntOpt('polling_interval', default=2), cfg.StrOpt('root_helper', default='sudo'), ] diff --git a/quantum/plugins/ryu/db/api.py b/quantum/plugins/ryu/db/api.py deleted file mode 100644 index caa8b57385..0000000000 --- a/quantum/plugins/ryu/db/api.py +++ /dev/null @@ -1,27 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2012 Isaku Yamahata -# 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 quantum.db.api as db -from quantum.plugins.ryu.db import models - - -def set_ofp_servers(hosts): - session = db.get_session() - session.query(models.OFPServer).delete() - for (host_address, host_type) in hosts: - host = models.OFPServer(host_address, host_type) - session.add(host) - session.flush() diff --git a/quantum/plugins/ryu/db/models.py b/quantum/plugins/ryu/db/models.py deleted file mode 100644 index e40f9cac59..0000000000 --- a/quantum/plugins/ryu/db/models.py +++ /dev/null @@ -1,37 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2012 Isaku Yamahata -# 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 sqlalchemy import Column, Integer, String - -from quantum.db.models import BASE - - -class OFPServer(BASE): - """Openflow Server/API address""" - __tablename__ = 'ofp_server' - - id = Column(Integer, primary_key=True, autoincrement=True) - address = Column(String(255)) # netloc : - host_type = Column(String(255)) # server type - # Controller, REST_API - - def __init__(self, address, host_type): - self.address = address - self.host_type = host_type - - def __repr__(self): - return "" % (self.id, self.address, - self.host_type) diff --git a/quantum/plugins/ryu/ovs_quantum_plugin_base.py b/quantum/plugins/ryu/ovs_quantum_plugin_base.py deleted file mode 100644 index da941799d4..0000000000 --- a/quantum/plugins/ryu/ovs_quantum_plugin_base.py +++ /dev/null @@ -1,173 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 Isaku Yamahata -# 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: Isaku Yamahata - -from abc import ABCMeta, abstractmethod -import logging as LOG -import os - -from quantum.api.api_common import OperationalStatus -from quantum.common import exceptions as q_exc -import quantum.db.api as db -from quantum.openstack.common import cfg -from quantum.plugins.ryu.common import config -from quantum.quantum_plugin_base import QuantumPluginBase - - -LOG.getLogger(__name__) - - -class OVSQuantumPluginDriverBase(object): - """ - Base class for OVS quantum plugin driver - """ - __metaclass__ = ABCMeta - - @abstractmethod - def create_network(self, net): - pass - - @abstractmethod - def delete_network(self, net): - pass - - -class OVSQuantumPluginBase(QuantumPluginBase): - """ - Base class for OVS-based plugin which referes to a subclass of - OVSQuantumPluginDriverBase which is defined above. - Subclass of OVSQuantumPluginBase must set self.driver to a subclass of - OVSQuantumPluginDriverBase. - """ - def __init__(self, conf_file, mod_file, configfile=None): - super(OVSQuantumPluginBase, self).__init__() - options = {"sql_connection": cfg.CONF.DATABASE.sql_connection} - sql_max_retries = cfg.CONF.DATABASE.sql_max_retries - options.update({"sql_max_retries": sql_max_retries}) - reconnect_interval = cfg.CONF.DATABASE.reconnect_interval - options.update({"reconnect_interval": reconnect_interval}) - db.configure_db(options) - - self.conf = cfg.CONF - # Subclass must set self.driver to its own OVSQuantumPluginDriverBase - self.driver = None - - def get_all_networks(self, tenant_id, **kwargs): - nets = [] - for net in db.network_list(tenant_id): - LOG.debug("Adding network: %s", net.uuid) - nets.append(self._make_net_dict(str(net.uuid), net.name, - None, net.op_status)) - return nets - - def _make_net_dict(self, net_id, net_name, ports, op_status): - res = {'net-id': net_id, - 'net-name': net_name, - 'net-op-status': op_status} - if ports: - res['net-ports'] = ports - return res - - def create_network(self, tenant_id, net_name, **kwargs): - net = db.network_create(tenant_id, net_name, - op_status=OperationalStatus.UP) - LOG.debug("Created network: %s", net) - self.driver.create_network(net) - return self._make_net_dict(str(net.uuid), net.name, [], net.op_status) - - def delete_network(self, tenant_id, net_id): - db.validate_network_ownership(tenant_id, net_id) - net = db.network_get(net_id) - - # Verify that no attachments are plugged into the network - for port in db.port_list(net_id): - if port.interface_id: - raise q_exc.NetworkInUse(net_id=net_id) - net = db.network_destroy(net_id) - self.driver.delete_network(net) - return self._make_net_dict(str(net.uuid), net.name, [], net.op_status) - - def get_network_details(self, tenant_id, net_id): - db.validate_network_ownership(tenant_id, net_id) - net = db.network_get(net_id) - ports = self.get_all_ports(tenant_id, net_id) - return self._make_net_dict(str(net.uuid), net.name, - ports, net.op_status) - - def update_network(self, tenant_id, net_id, **kwargs): - db.validate_network_ownership(tenant_id, net_id) - net = db.network_update(net_id, tenant_id, **kwargs) - return self._make_net_dict(str(net.uuid), net.name, - None, net.op_status) - - def _make_port_dict(self, port): - if port.state == "ACTIVE": - op_status = port.op_status - else: - op_status = OperationalStatus.DOWN - - return {'port-id': str(port.uuid), - 'port-state': port.state, - 'port-op-status': op_status, - 'net-id': port.network_id, - 'attachment': port.interface_id} - - def get_all_ports(self, tenant_id, net_id, **kwargs): - db.validate_network_ownership(tenant_id, net_id) - ports = db.port_list(net_id) - # This plugin does not perform filtering at the moment - return [{'port-id': str(port.uuid)} for port in ports] - - def create_port(self, tenant_id, net_id, port_state=None, **kwargs): - LOG.debug("Creating port with network_id: %s", net_id) - port = db.port_create(net_id, port_state, - op_status=OperationalStatus.DOWN) - return self._make_port_dict(port) - - def delete_port(self, tenant_id, net_id, port_id): - db.validate_port_ownership(tenant_id, net_id, port_id) - port = db.port_destroy(port_id, net_id) - return self._make_port_dict(port) - - def update_port(self, tenant_id, net_id, port_id, **kwargs): - """ - Updates the state of a port on the specified Virtual Network. - """ - LOG.debug("update_port() called\n") - db.validate_port_ownership(tenant_id, net_id, port_id) - port = db.port_get(port_id, net_id) - db.port_update(port_id, net_id, **kwargs) - return self._make_port_dict(port) - - def get_port_details(self, tenant_id, net_id, port_id): - db.validate_port_ownership(tenant_id, net_id, port_id) - port = db.port_get(port_id, net_id) - return self._make_port_dict(port) - - def plug_interface(self, tenant_id, net_id, port_id, remote_iface_id): - db.validate_port_ownership(tenant_id, net_id, port_id) - db.port_set_attachment(port_id, net_id, remote_iface_id) - - def unplug_interface(self, tenant_id, net_id, port_id): - db.validate_port_ownership(tenant_id, net_id, port_id) - db.port_set_attachment(port_id, net_id, "") - db.port_update(port_id, net_id, op_status=OperationalStatus.DOWN) - - def get_interface_details(self, tenant_id, net_id, port_id): - db.validate_port_ownership(tenant_id, net_id, port_id) - res = db.port_get(port_id, net_id) - return res.interface_id diff --git a/quantum/plugins/ryu/run_tests.py b/quantum/plugins/ryu/run_tests.py index b0ffe758ba..c3628e5408 100644 --- a/quantum/plugins/ryu/run_tests.py +++ b/quantum/plugins/ryu/run_tests.py @@ -34,7 +34,6 @@ from nose import core sys.path.append(os.getcwd()) sys.path.append(os.path.dirname(__file__)) -from quantum.api.api_common import OperationalStatus from quantum.common.test_lib import run_tests, test_config from quantum.plugins.ryu.tests.unit.utils import patch_fake_ryu_client import quantum.tests.unit @@ -47,10 +46,7 @@ if __name__ == '__main__': # we should only invoked the tests once invoke_once = len(sys.argv) > 1 - test_config['plugin_name'] = "ryu_quantum_plugin.RyuQuantumPlugin" test_config['plugin_name_v2'] = "ryu_quantum_plugin.RyuQuantumPluginV2" - test_config['default_net_op_status'] = OperationalStatus.UP - test_config['default_port_op_status'] = OperationalStatus.DOWN cwd = os.getcwd() # patch modules for ryu.app.client and ryu.app.rest_nw_id diff --git a/quantum/plugins/ryu/ryu_quantum_plugin.py b/quantum/plugins/ryu/ryu_quantum_plugin.py index 2a8ad3068b..07dd6107bf 100644 --- a/quantum/plugins/ryu/ryu_quantum_plugin.py +++ b/quantum/plugins/ryu/ryu_quantum_plugin.py @@ -26,52 +26,13 @@ from quantum.db import api as db from quantum.db import db_base_plugin_v2 from quantum.db import models_v2 from quantum.openstack.common import cfg -from quantum.plugins.ryu.db import api as db_api from quantum.plugins.ryu.db import api_v2 as db_api_v2 from quantum.plugins.ryu import ofp_service_type -from quantum.plugins.ryu import ovs_quantum_plugin_base from quantum.plugins.ryu.common import config LOG = logging.getLogger(__name__) -class OFPRyuDriver(ovs_quantum_plugin_base.OVSQuantumPluginDriverBase): - def __init__(self, conf): - super(OFPRyuDriver, self).__init__() - ofp_con_host = conf.OVS.openflow_controller - ofp_api_host = conf.OVS.openflow_rest_api - - if ofp_con_host is None or ofp_api_host is None: - raise q_exc.Invalid("invalid configuration. check ryu.ini") - - hosts = [(ofp_con_host, ofp_service_type.CONTROLLER), - (ofp_api_host, ofp_service_type.REST_API)] - db_api.set_ofp_servers(hosts) - - self.client = client.OFPClient(ofp_api_host) - self.client.update_network(rest_nw_id.NW_ID_EXTERNAL) - - # register known all network list on startup - self._create_all_tenant_network() - - def _create_all_tenant_network(self): - networks = db.network_all_tenant_list() - for net in networks: - self.client.update_network(net.uuid) - - def create_network(self, net): - self.client.create_network(net.uuid) - - def delete_network(self, net): - self.client.delete_network(net.uuid) - - -class RyuQuantumPlugin(ovs_quantum_plugin_base.OVSQuantumPluginBase): - def __init__(self, configfile=None): - super(RyuQuantumPlugin, self).__init__(__file__, configfile) - self.driver = OFPRyuDriver(self.conf) - - class RyuQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2): def __init__(self, configfile=None): options = {"sql_connection": cfg.CONF.DATABASE.sql_connection} diff --git a/quantum/plugins/ryu/tests/unit/basetest.py b/quantum/plugins/ryu/tests/unit/basetest.py deleted file mode 100644 index 5dbd39a1cd..0000000000 --- a/quantum/plugins/ryu/tests/unit/basetest.py +++ /dev/null @@ -1,42 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 Isaku Yamahata -# 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 mox -import stubout -import unittest - -import quantum.db.api as db -from quantum.plugins.ryu.tests.unit import utils - - -class BaseRyuTest(unittest.TestCase): - """base test class for Ryu unit tests""" - def setUp(self): - config = utils.get_config() - options = {"sql_connection": config.get("DATABASE", "sql_connection")} - db.configure_db(options) - - self.config = config - self.mox = mox.Mox() - self.stubs = stubout.StubOutForTesting() - - def tearDown(self): - self.mox.UnsetStubs() - self.stubs.UnsetAll() - self.stubs.SmartUnsetAll() - self.mox.VerifyAll() - db.clear_db() diff --git a/quantum/plugins/ryu/tests/unit/fake_plugin.py b/quantum/plugins/ryu/tests/unit/fake_plugin.py deleted file mode 100644 index 3d521b4f77..0000000000 --- a/quantum/plugins/ryu/tests/unit/fake_plugin.py +++ /dev/null @@ -1,37 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2012 Isaku Yamahata -# -# 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.plugins.ryu import ovs_quantum_plugin_base - - -class FakePluginDriver(ovs_quantum_plugin_base.OVSQuantumPluginDriverBase): - def __init__(self, config): - super(FakePluginDriver, self).__init__() - conf = config.parse(config) - self.conf = conf - - def create_network(self, net): - pass - - def delete_network(self, net): - pass - - -class FakePlugin(ovs_quantum_plugin_base.OVSQuantumPluginBase): - def __init__(self, configfile=None): - super(FakePlugin, self).__init__(None, __file__, configfile) - self.driver = FakePluginDriver(self.conf) diff --git a/quantum/plugins/ryu/tests/unit/fake_rest_nw_id.py b/quantum/plugins/ryu/tests/unit/fake_rest_nw_id.py deleted file mode 100644 index 5a682cc29d..0000000000 --- a/quantum/plugins/ryu/tests/unit/fake_rest_nw_id.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. -# Copyright (C) 2011 Isaku Yamahata -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -NW_ID_EXTERNAL = '__NW_ID_EXTERNAL__' -NW_ID_UNKNOWN = '__NW_ID_UNKNOWN__' diff --git a/quantum/plugins/ryu/tests/unit/fake_ryu_client.py b/quantum/plugins/ryu/tests/unit/fake_ryu_client.py deleted file mode 100644 index 763b86cf41..0000000000 --- a/quantum/plugins/ryu/tests/unit/fake_ryu_client.py +++ /dev/null @@ -1,46 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 Isaku Yamahata -# 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. - - -class OFPClient(object): - def __init__(self, address): - super(OFPClient, self).__init__() - self.address = address - - def get_networks(self): - pass - - def create_network(self, network_id): - pass - - def update_network(self, network_id): - pass - - def delete_network(self, network_id): - pass - - def get_ports(self, network_id): - pass - - def create_port(self, network_id, dpid, port): - pass - - def update_port(self, network_id, dpid, port): - pass - - def delete_port(self, network_id, dpid, port): - pass diff --git a/quantum/plugins/ryu/tests/unit/test_plugin_base.py b/quantum/plugins/ryu/tests/unit/test_defaults.py similarity index 50% rename from quantum/plugins/ryu/tests/unit/test_plugin_base.py rename to quantum/plugins/ryu/tests/unit/test_defaults.py index c9391877a4..dae4cc4220 100644 --- a/quantum/plugins/ryu/tests/unit/test_plugin_base.py +++ b/quantum/plugins/ryu/tests/unit/test_defaults.py @@ -15,46 +15,14 @@ # License for the specific language governing permissions and limitations # under the License. -import os - -import mox +import unittest2 from quantum.openstack.common import cfg -from quantum.plugins.ryu.tests.unit.basetest import BaseRyuTest -from quantum.plugins.ryu.tests.unit import fake_plugin -from quantum.plugins.ryu.tests.unit import utils +from quantum.plugins.ryu.common import config -class PluginBaseTest(BaseRyuTest): - """Class conisting of OVSQuantumPluginBase unit tests""" - def setUp(self): - super(PluginBaseTest, self).setUp() - self.ini_file = utils.create_fake_ryu_ini() - - def tearDown(self): - os.unlink(self.ini_file) - super(PluginBaseTest, self).tearDown() - - def test_create_delete_network(self): - # mox.StubOutClassWithMocks can't be used for class with metaclass - # overrided - driver_mock = self.mox.CreateMock(fake_plugin.FakePluginDriver) - self.mox.StubOutWithMock(fake_plugin, 'FakePluginDriver', - use_mock_anything=True) - - fake_plugin.FakePluginDriver(mox.IgnoreArg()).AndReturn(driver_mock) - driver_mock.create_network(mox.IgnoreArg()) - driver_mock.delete_network(mox.IgnoreArg()) - self.mox.ReplayAll() - plugin = fake_plugin.FakePlugin(configfile=self.ini_file) - - tenant_id = 'tenant_id' - net_name = 'net_name' - ret = plugin.create_network(tenant_id, net_name) - - plugin.delete_network(tenant_id, ret['net-id']) - self.mox.VerifyAll() - +class ConfigurationTest(unittest2.TestCase): + """Configuration file Tests""" def test_defaults(self): self.assertEqual('br-int', cfg.CONF.OVS.integration_bridge) self.assertEqual('sqlite://', cfg.CONF.DATABASE.sql_connection) diff --git a/quantum/plugins/ryu/tests/unit/test_ryu_db.py b/quantum/plugins/ryu/tests/unit/test_ryu_db.py new file mode 100644 index 0000000000..c12849c042 --- /dev/null +++ b/quantum/plugins/ryu/tests/unit/test_ryu_db.py @@ -0,0 +1,52 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2012 Isaku Yamahata +# 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 unittest2 + +from quantum.db import api as db +from quantum.db import models_v2 +from quantum.openstack.common import cfg +from quantum.plugins.ryu.db import api_v2 as db_api_v2 +from quantum.plugins.ryu.db import models_v2 as ryu_models_v2 +from quantum.plugins.ryu import ofp_service_type + + +class RyuDBTest(unittest2.TestCase): + def setUp(self): + options = {"sql_connection": cfg.CONF.DATABASE.sql_connection} + options.update({'base': models_v2.model_base.BASEV2}) + reconnect_interval = cfg.CONF.DATABASE.reconnect_interval + options.update({"reconnect_interval": reconnect_interval}) + db.configure_db(options) + + self.hosts = [(cfg.CONF.OVS.openflow_controller, + ofp_service_type.CONTROLLER), + (cfg.CONF.OVS.openflow_rest_api, + ofp_service_type.REST_API)] + db_api_v2.set_ofp_servers(self.hosts) + + def tearDown(self): + db.clear_db() + cfg.CONF.reset() + + def test_ofp_server(self): + session = db.get_session() + servers = session.query(ryu_models_v2.OFPServer).all() + print servers + self.assertEqual(len(servers), 2) + for s in servers: + self.assertTrue((s.address, s.host_type) in self.hosts) diff --git a/quantum/plugins/ryu/tests/unit/test_ryu_driver.py b/quantum/plugins/ryu/tests/unit/test_ryu_driver.py deleted file mode 100644 index dde741d65c..0000000000 --- a/quantum/plugins/ryu/tests/unit/test_ryu_driver.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2012 Isaku Yamahata -# 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 uuid - -import quantum.db.api as db -from quantum.openstack.common import cfg -from quantum.plugins.ryu.common import config -from quantum.plugins.ryu.tests.unit.basetest import BaseRyuTest -from quantum.plugins.ryu.tests.unit import utils -from quantum.plugins.ryu.tests.unit.utils import patch_fake_ryu_client - - -class RyuDriverTest(BaseRyuTest): - """Class conisting of OFPRyuDriver unit tests""" - def setUp(self): - super(RyuDriverTest, self).setUp() - self.conf = cfg.CONF - # fake up ryu.app.client and ryu.app.rest_nw_id - # With those, plugin can be tested without ryu installed - self.module_patcher = patch_fake_ryu_client() - self.module_patcher.start() - - def tearDown(self): - self.module_patcher.stop() - super(RyuDriverTest, self).tearDown() - - def test_ryu_driver(self): - from ryu.app import client as client_mod - from ryu.app import rest_nw_id as rest_nw_id_mod - - self.mox.StubOutClassWithMocks(client_mod, 'OFPClient') - client_mock = client_mod.OFPClient(utils.FAKE_REST_ADDR) - - self.mox.StubOutWithMock(client_mock, 'update_network') - self.mox.StubOutWithMock(client_mock, 'create_network') - self.mox.StubOutWithMock(client_mock, 'delete_network') - client_mock.update_network(rest_nw_id_mod.NW_ID_EXTERNAL) - uuid0 = '01234567-89ab-cdef-0123-456789abcdef' - - def fake_uuid4(): - return uuid0 - - self.stubs.Set(uuid, 'uuid4', fake_uuid4) - uuid1 = '12345678-9abc-def0-1234-56789abcdef0' - net1 = utils.Net(uuid1) - - client_mock.update_network(uuid0) - client_mock.create_network(uuid1) - client_mock.delete_network(uuid1) - self.mox.ReplayAll() - - db.network_create('test', uuid0) - - from quantum.plugins.ryu import ryu_quantum_plugin - ryu_driver = ryu_quantum_plugin.OFPRyuDriver(self.conf) - ryu_driver.create_network(net1) - ryu_driver.delete_network(net1) - self.mox.VerifyAll() - - db.network_destroy(uuid0) diff --git a/quantum/plugins/ryu/tests/unit/utils.py b/quantum/plugins/ryu/tests/unit/utils.py index de7ec20f46..eaa5541ebe 100644 --- a/quantum/plugins/ryu/tests/unit/utils.py +++ b/quantum/plugins/ryu/tests/unit/utils.py @@ -15,60 +15,18 @@ # License for the specific language governing permissions and limitations # under the License. -import ConfigParser -import imp -import os -from StringIO import StringIO -import tempfile - import mock -from quantum.plugins.ryu.tests.unit import fake_rest_nw_id -from quantum.plugins.ryu.tests.unit import fake_ryu_client - - -FAKE_CONTROLLER_ADDR = '127.0.0.1:6633' -FAKE_REST_ADDR = '127.0.0.1:8080' -FAKE_RYU_INI_TEMPLATE = """ -[DATABASE] -sql_connection = sqlite:///:memory: - -[OVS] -integration-bridge = br-int -openflow-controller = %s -openflow-rest-api = %s -""" % (FAKE_CONTROLLER_ADDR, FAKE_REST_ADDR) - - -def create_fake_ryu_ini(): - fd, file_name = tempfile.mkstemp(suffix='.ini') - tmp_file = os.fdopen(fd, 'w') - tmp_file.write(FAKE_RYU_INI_TEMPLATE) - tmp_file.close() - return file_name - - -def get_config(): - config = ConfigParser.ConfigParser() - buf_file = StringIO(FAKE_RYU_INI_TEMPLATE) - config.readfp(buf_file) - buf_file.close() - return config - def patch_fake_ryu_client(): - ryu_mod = imp.new_module('ryu') - ryu_app_mod = imp.new_module('ryu.app') - ryu_mod.app = ryu_app_mod - ryu_app_mod.client = fake_ryu_client - ryu_app_mod.rest_nw_id = fake_rest_nw_id + ryu_mod = mock.Mock() + ryu_app_mod = ryu_mod.app + ryu_app_client = ryu_app_mod.client + rest_nw_id = ryu_app_mod.rest_nw_id + rest_nw_id.NW_ID_EXTERNAL = '__NW_ID_EXTERNAL__' + rest_nw_id.NW_ID_UNKNOWN = '__NW_ID_UNKNOWN__' return mock.patch.dict('sys.modules', {'ryu': ryu_mod, 'ryu.app': ryu_app_mod, - 'ryu.app.client': fake_ryu_client, - 'ryu.app.rest_nw_id': fake_rest_nw_id}) - - -class Net(object): - def __init__(self, uuid): - self.uuid = uuid + 'ryu.app.client': ryu_app_client, + 'ryu.app.rest_nw_id': rest_nw_id}) diff --git a/quantum/plugins/sample/SamplePlugin.py b/quantum/plugins/sample/SamplePlugin.py deleted file mode 100644 index 07d59cff04..0000000000 --- a/quantum/plugins/sample/SamplePlugin.py +++ /dev/null @@ -1,344 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011, Nicira Networks, Inc. -# -# 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: Somik Behera, Nicira Networks, Inc. -# @author: Salvatore Orlando, Citrix - -import logging - -from quantum.api.api_common import OperationalStatus -from quantum.common import exceptions as exc -from quantum.db import api as db - - -LOG = logging.getLogger('quantum.plugins.sample.SamplePlugin') - - -class QuantumEchoPlugin(object): - - """ - QuantumEchoPlugin is a demo plugin that doesn't - do anything but demonstrated the concept of a - concrete Quantum Plugin. Any call to this plugin - will result in just a "print" to std. out with - the name of the method that was called. - """ - - def get_all_networks(self, tenant_id): - """ - Returns a dictionary containing all - for - the specified tenant. - """ - print("get_all_networks() called\n") - - def create_network(self, tenant_id, net_name, **kwargs): - """ - Creates a new Virtual Network, and assigns it - a symbolic name. - """ - print("create_network() called\n") - - def delete_network(self, tenant_id, net_id): - """ - Deletes the network with the specified network identifier - belonging to the specified tenant. - """ - print("delete_network() called\n") - - def get_network_details(self, tenant_id, net_id): - """ - Deletes the Virtual Network belonging to a the - spec - """ - print("get_network_details() called\n") - - def update_network(self, tenant_id, net_id, **kwargs): - print("update_network() called") - - def get_all_ports(self, tenant_id, net_id): - """ - Retrieves all port identifiers belonging to the - specified Virtual Network. - """ - print("get_all_ports() called\n") - - def create_port(self, tenant_id, net_id, **kwargs): - """ - Creates a port on the specified Virtual Network. - """ - print("create_port() called\n") - - def delete_port(self, tenant_id, net_id, port_id): - """ - Deletes a port on a specified Virtual Network, - if the port contains a remote interface attachment, - the remote interface is first un-plugged and then the port - is deleted. - """ - print("delete_port() called\n") - - def update_port(self, tenant_id, net_id, port_id, **kwargs): - """ - Updates the attributes of a port on the specified Virtual Network. - """ - print("update_port() called\n") - - def get_port_details(self, tenant_id, net_id, port_id): - """ - This method allows the user to retrieve a remote interface - that is attached to this particular port. - """ - print("get_port_details() called\n") - - def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id): - """ - Attaches a remote interface to the specified port on the - specified Virtual Network. - """ - print("plug_interface() called\n") - - def unplug_interface(self, tenant_id, net_id, port_id): - """ - Detaches a remote interface from the specified port on the - specified Virtual Network. - """ - print("unplug_interface() called\n") - - supported_extension_aliases = ["FOXNSOX"] - - def method_to_support_foxnsox_extension(self): - print("method_to_support_foxnsox_extension() called\n") - - -class FakePlugin(object): - """ - FakePlugin is a demo plugin that provides - in-memory data structures to aid in quantum - client/cli/api development - """ - - def __init__(self): - db.configure_db({'sql_connection': 'sqlite:///:memory:'}) - FakePlugin._net_counter = 0 - - def _get_network(self, tenant_id, network_id): - - db.validate_network_ownership(tenant_id, network_id) - try: - network = db.network_get(network_id) - except: - raise exc.NetworkNotFound(net_id=network_id) - return network - - def _get_port(self, tenant_id, network_id, port_id): - - db.validate_port_ownership(tenant_id, network_id, port_id) - net = self._get_network(tenant_id, network_id) - try: - port = db.port_get(port_id, network_id) - except: - raise exc.PortNotFound(net_id=network_id, port_id=port_id) - # Port must exist and belong to the appropriate network. - if port['network_id'] != net['uuid']: - raise exc.PortNotFound(net_id=network_id, port_id=port_id) - return port - - def _validate_port_state(self, port_state): - if port_state.upper() not in ('ACTIVE', 'DOWN'): - raise exc.StateInvalid(port_state=port_state) - return True - - def _validate_attachment(self, tenant_id, network_id, port_id, - remote_interface_id): - for port in db.port_list(network_id): - if port['interface_id'] == remote_interface_id: - raise exc.AlreadyAttached(net_id=network_id, - port_id=port_id, - att_id=port['interface_id'], - att_port_id=port['uuid']) - - def get_all_networks(self, tenant_id, **kwargs): - """ - Returns a dictionary containing all - for - the specified tenant. - """ - LOG.debug("FakePlugin.get_all_networks() called") - filter_opts = kwargs.get('filter_opts', None) - if not filter_opts is None and len(filter_opts) > 0: - LOG.debug("filtering options were passed to the plugin" - "but the Fake plugin does not support them") - nets = [] - for net in db.network_list(tenant_id): - net_item = {'net-id': str(net.uuid), - 'net-name': net.name, - 'net-op-status': net.op_status} - nets.append(net_item) - return nets - - def get_network_details(self, tenant_id, net_id): - """ - retrieved a list of all the remote vifs that - are attached to the network - """ - LOG.debug("FakePlugin.get_network_details() called") - net = self._get_network(tenant_id, net_id) - # Retrieves ports for network - ports = self.get_all_ports(tenant_id, net_id) - return {'net-id': str(net.uuid), - 'net-name': net.name, - 'net-op-status': net.op_status, - 'net-ports': ports} - - def create_network(self, tenant_id, net_name, **kwargs): - """ - Creates a new Virtual Network, and assigns it - a symbolic name. - """ - LOG.debug("FakePlugin.create_network() called") - new_net = db.network_create(tenant_id, net_name) - # Put operational status UP - db.network_update(new_net.uuid, net_name, - op_status=OperationalStatus.UP) - # Return uuid for newly created network as net-id. - return {'net-id': new_net.uuid} - - def delete_network(self, tenant_id, net_id): - """ - Deletes the network with the specified network identifier - belonging to the specified tenant. - """ - LOG.debug("FakePlugin.delete_network() called") - net = self._get_network(tenant_id, net_id) - # Verify that no attachments are plugged into the network - if net: - for port in db.port_list(net_id): - if port['interface_id']: - raise exc.NetworkInUse(net_id=net_id) - db.network_destroy(net_id) - return net - # Network not found - raise exc.NetworkNotFound(net_id=net_id) - - def update_network(self, tenant_id, net_id, **kwargs): - """ - Updates the attributes of a particular Virtual Network. - """ - LOG.debug("FakePlugin.update_network() called") - net = db.network_update(net_id, tenant_id, **kwargs) - return net - - def get_all_ports(self, tenant_id, net_id, **kwargs): - """ - Retrieves all port identifiers belonging to the - specified Virtual Network. - """ - LOG.debug("FakePlugin.get_all_ports() called") - db.validate_network_ownership(tenant_id, net_id) - filter_opts = kwargs.get('filter_opts') - if filter_opts: - LOG.debug("filtering options were passed to the plugin" - "but the Fake plugin does not support them") - port_ids = [] - ports = db.port_list(net_id) - for x in ports: - d = {'port-id': str(x.uuid)} - port_ids.append(d) - return port_ids - - def get_port_details(self, tenant_id, net_id, port_id): - """ - This method allows the user to retrieve a remote interface - that is attached to this particular port. - """ - LOG.debug("FakePlugin.get_port_details() called") - port = self._get_port(tenant_id, net_id, port_id) - return {'port-id': str(port.uuid), - 'attachment': port.interface_id, - 'port-state': port.state, - 'port-op-status': port.op_status} - - def create_port(self, tenant_id, net_id, port_state=None, **kwargs): - """ - Creates a port on the specified Virtual Network. - """ - LOG.debug("FakePlugin.create_port() called") - # verify net_id - self._get_network(tenant_id, net_id) - port = db.port_create(net_id, port_state) - # Put operational status UP - db.port_update(port.uuid, net_id, op_status=OperationalStatus.UP) - port_item = {'port-id': str(port.uuid)} - return port_item - - def update_port(self, tenant_id, net_id, port_id, **kwargs): - """ - Updates the attributes of a port on the specified Virtual Network. - """ - LOG.debug("FakePlugin.update_port() called") - #validate port and network ids - self._get_network(tenant_id, net_id) - self._get_port(tenant_id, net_id, port_id) - port = db.port_update(port_id, net_id, **kwargs) - port_item = {'port-id': port_id, 'port-state': port['state']} - return port_item - - def delete_port(self, tenant_id, net_id, port_id): - """ - Deletes a port on a specified Virtual Network, - if the port contains a remote interface attachment, - the remote interface is first un-plugged and then the port - is deleted. - """ - LOG.debug("FakePlugin.delete_port() called") - net = self._get_network(tenant_id, net_id) - port = self._get_port(tenant_id, net_id, port_id) - if port['interface_id']: - raise exc.PortInUse(net_id=net_id, port_id=port_id, - att_id=port['interface_id']) - try: - port = db.port_destroy(port_id, net_id) - except Exception, e: - raise Exception("Failed to delete port: %s" % str(e)) - d = {} - d["port-id"] = str(port.uuid) - return d - - def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id): - """ - Attaches a remote interface to the specified port on the - specified Virtual Network. - """ - LOG.debug("FakePlugin.plug_interface() called") - port = self._get_port(tenant_id, net_id, port_id) - # Validate attachment - self._validate_attachment(tenant_id, net_id, port_id, - remote_interface_id) - if port['interface_id']: - raise exc.PortInUse(net_id=net_id, port_id=port_id, - att_id=port['interface_id']) - db.port_set_attachment(port_id, net_id, remote_interface_id) - - def unplug_interface(self, tenant_id, net_id, port_id): - """ - Detaches a remote interface from the specified port on the - specified Virtual Network. - """ - LOG.debug("FakePlugin.unplug_interface() called") - self._get_port(tenant_id, net_id, port_id) - # TODO(salvatore-orlando): - # Should unplug on port without attachment raise an Error? - db.port_unset_attachment(port_id, net_id) diff --git a/quantum/plugins/sample/SamplePluginV2.py b/quantum/plugins/sample/SamplePluginV2.py deleted file mode 100644 index 5a6f9b94f1..0000000000 --- a/quantum/plugins/sample/SamplePluginV2.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (c) 2012 OpenStack, LLC. -# -# 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 -import uuid - -from quantum import quantum_plugin_base_v2 - - -LOG = logging.getLogger(__name__) - - -class QuantumEchoPlugin(quantum_plugin_base_v2.QuantumPluginBaseV2): - - """ - QuantumEchoPlugin is a demo plugin that doesn't - do anything but demonstrate the concept of a - concrete Quantum Plugin. Any call to this plugin - will result in just a log statement with the name - method that was called and its arguments. - """ - - def _log(self, name, context, **kwargs): - kwarg_msg = ' '.join([('%s: |%s|' % (str(key), kwargs[key])) - for key in kwargs]) - - # TODO(anyone) Add a nice __repr__ and __str__ to context - #LOG.debug('%s context: %s %s' % (name, context, kwarg_msg)) - LOG.debug('%s %s' % (name, kwarg_msg)) - - def create_subnet(self, context, subnet): - self._log("create_subnet", context, subnet=subnet) - res = {"id": str(uuid.uuid4())} - res.update(subnet) - return res - - def update_subnet(self, context, id, subnet): - self._log("update_subnet", context, id=id, subnet=subnet) - res = {"id": id} - res.update(subnet) - return res - - def get_subnet(self, context, id, show=None, verbose=None): - self._log("get_subnet", context, id=id, show=show, - verbose=verbose) - return {"id": id} - - def delete_subnet(self, context, id): - self._log("delete_subnet", context, id=id) - - def get_subnets(self, context, filters=None, show=None, verbose=None): - self._log("get_subnets", context, filters=filters, show=show, - verbose=verbose) - return [] - - def create_network(self, context, network): - self._log("create_network", context, network=network) - res = {"id": str(uuid.uuid4())} - res.update(network) - return res - - def update_network(self, context, id, network): - self._log("update_network", context, id=id, network=network) - res = {"id": id} - res.update(network) - return res - - def get_network(self, context, id, show=None, verbose=None): - self._log("get_network", context, id=id, show=show, - verbose=verbose) - return {"id": id} - - def delete_network(self, context, id): - self._log("delete_network", context, id=id) - - def get_networks(self, context, filters=None, show=None, verbose=None): - self._log("get_networks", context, filters=filters, show=show, - verbose=verbose) - return [] - - def create_port(self, context, port): - self._log("create_port", context, port=port) - res = {"id": str(uuid.uuid4())} - res.update(port) - return res - - def update_port(self, context, id, port): - self._log("update_port", context, id=id, port=port) - res = {"id": id} - res.update(port) - return res - - def get_port(self, context, id, show=None, verbose=None): - self._log("get_port", context, id=id, show=show, - verbose=verbose) - return {"id": id} - - def delete_port(self, context, id): - self._log("delete_port", context, id=id) - - def get_ports(self, context, filters=None, show=None, verbose=None): - self._log("get_ports", context, filters=filters, show=show, - verbose=verbose) - return [] - - supported_extension_aliases = ["FOXNSOX"] - - def method_to_support_foxnsox_extension(self, context): - self._log("method_to_support_foxnsox_extension", context) diff --git a/quantum/plugins/sample/__init__.py b/quantum/plugins/sample/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/quantum/quantum_plugin_base.py b/quantum/quantum_plugin_base.py deleted file mode 100644 index 69f91dc5a7..0000000000 --- a/quantum/quantum_plugin_base.py +++ /dev/null @@ -1,270 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2011 Nicira Networks, Inc. -# 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: Somik Behera, Nicira Networks, Inc. - -""" -Quantum Plug-in API specification. - -QuantumPluginBase provides the definition of minimum set of -methods that needs to be implemented by a Quantum Plug-in. -""" - -from abc import ABCMeta, abstractmethod -import inspect - - -class QuantumPluginBase(object): - - __metaclass__ = ABCMeta - - @abstractmethod - def get_all_networks(self, tenant_id, **kwargs): - """ - Returns a dictionary containing all - for - the specified tenant. - :param tenant_id: unique identifier for the tenant whose networks - are being retrieved by this method - :param **kwargs: options to be passed to the plugin. The following - keywork based-options can be specified: - filter_opts - options for filtering network list - :returns: a list of mapping sequences with the following signature: - [ {'net-id': uuid that uniquely identifies - the particular quantum network, - 'net-name': a human-readable name associated - with network referenced by net-id - }, - .... - {'net-id': uuid that uniquely identifies the - particular quantum network, - 'net-name': a human-readable name associated - with network referenced by net-id - } - ] - :raises: None - """ - pass - - @abstractmethod - def create_network(self, tenant_id, net_name, **kwargs): - """ - Creates a new Virtual Network, and assigns it - a symbolic name. - - :returns: a sequence of mappings with the following signature: - {'net-id': uuid that uniquely identifies the - particular quantum network, - 'net-name': a human-readable name associated - with network referenced by net-id - } - :raises: - """ - pass - - @abstractmethod - def delete_network(self, tenant_id, net_id): - """ - Deletes the network with the specified network identifier - belonging to the specified tenant. - - :returns: a sequence of mappings with the following signature: - {'net-id': uuid that uniquely identifies the - particular quantum network - } - :raises: exception.NetworkInUse - :raises: exception.NetworkNotFound - """ - pass - - @abstractmethod - def get_network_details(self, tenant_id, net_id): - """ - Retrieves a list of all the remote vifs that - are attached to the network. - - :returns: a sequence of mappings with the following signature: - {'net-id': uuid that uniquely identifies the - particular quantum network - 'net-name': a human-readable name associated - with network referenced by net-id - 'net-ifaces': ['vif1_on_network_uuid', - 'vif2_on_network_uuid',...,'vifn_uuid'] - } - :raises: exception.NetworkNotFound - """ - pass - - @abstractmethod - def update_network(self, tenant_id, net_id, **kwargs): - """ - Updates the attributes of a particular Virtual Network. - - :returns: a sequence of mappings representing the new network - attributes, with the following signature: - {'net-id': uuid that uniquely identifies the - particular quantum network - 'net-name': the new human-readable name - associated with network referenced by net-id - } - :raises: exception.NetworkNotFound - """ - pass - - @abstractmethod - def get_all_ports(self, tenant_id, net_id, **kwargs): - """ - Retrieves all port identifiers belonging to the - specified Virtual Network. - :param tenant_id: unique identifier for the tenant for which this - method is going to retrieve ports - :param net_id: unique identifiers for the network whose ports are - about to be retrieved - :param **kwargs: options to be passed to the plugin. The following - keywork based-options can be specified: - filter_opts - options for filtering network list - :returns: a list of mapping sequences with the following signature: - [ {'port-id': uuid representing a particular port - on the specified quantum network - }, - .... - {'port-id': uuid representing a particular port - on the specified quantum network - } - ] - :raises: exception.NetworkNotFound - """ - pass - - @abstractmethod - def create_port(self, tenant_id, net_id, port_state=None, **kwargs): - """ - Creates a port on the specified Virtual Network. - - :returns: a mapping sequence with the following signature: - {'port-id': uuid representing the created port - on specified quantum network - } - :raises: exception.NetworkNotFound - :raises: exception.StateInvalid - """ - pass - - @abstractmethod - def update_port(self, tenant_id, net_id, port_id, **kwargs): - """ - Updates the attributes of a specific port on the - specified Virtual Network. - - :returns: a mapping sequence with the following signature: - {'port-id': uuid representing the - updated port on specified quantum network - 'port-state': update port state( UP or DOWN) - } - :raises: exception.StateInvalid - :raises: exception.PortNotFound - """ - pass - - @abstractmethod - def delete_port(self, tenant_id, net_id, port_id): - """ - Deletes a port on a specified Virtual Network, - if the port contains a remote interface attachment, - the remote interface is first un-plugged and then the port - is deleted. - - :returns: a mapping sequence with the following signature: - {'port-id': uuid representing the deleted port - on specified quantum network - } - :raises: exception.PortInUse - :raises: exception.PortNotFound - :raises: exception.NetworkNotFound - """ - pass - - @abstractmethod - def get_port_details(self, tenant_id, net_id, port_id): - """ - This method allows the user to retrieve a remote interface - that is attached to this particular port. - - :returns: a mapping sequence with the following signature: - {'port-id': uuid representing the port on - specified quantum network - 'net-id': uuid representing the particular - quantum network - 'attachment': uuid of the virtual interface - bound to the port, None otherwise - } - :raises: exception.PortNotFound - :raises: exception.NetworkNotFound - """ - pass - - @abstractmethod - def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id): - """ - Attaches a remote interface to the specified port on the - specified Virtual Network. - - :returns: None - :raises: exception.NetworkNotFound - :raises: exception.PortNotFound - :raises: exception.AlreadyAttached - (? should the network automatically unplug/replug) - """ - pass - - @abstractmethod - def unplug_interface(self, tenant_id, net_id, port_id): - """ - Detaches a remote interface from the specified port on the - specified Virtual Network. - - :returns: None - :raises: exception.NetworkNotFound - :raises: exception.PortNotFound - """ - pass - - @classmethod - def __subclasshook__(cls, klass): - """ - The __subclasshook__ method is a class method - that will be called everytime a class is tested - using issubclass(klass, Plugin). - In that case, it will check that every method - marked with the abstractmethod decorator is - provided by the plugin class. - """ - if cls is QuantumPluginBase: - for method in cls.__abstractmethods__: - method_ok = False - for base in klass.__mro__: - if method in base.__dict__: - fn_obj = base.__dict__[method] - if inspect.isfunction(fn_obj): - abstract_fn_obj = cls.__dict__[method] - arg_count = fn_obj.func_code.co_argcount - expected_arg_count = ( - abstract_fn_obj.func_code.co_argcount) - method_ok = arg_count == expected_arg_count - if method_ok: - continue - return NotImplemented - return True - return NotImplemented diff --git a/quantum/tests/unit/_test_api.py b/quantum/tests/unit/_test_api.py deleted file mode 100644 index 26ac1fadef..0000000000 --- a/quantum/tests/unit/_test_api.py +++ /dev/null @@ -1,1227 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010-2011 ???? -# 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: Brad Hall, Nicira Networks -# @author: Salvatore Orlando, Citrix Systems - -import logging -import unittest2 as unittest - -import mock -import os - -from quantum.api.api_common import APIFaultWrapper -from quantum.api.networks import Controller -from quantum.common import config -from quantum.common.test_lib import test_config -from quantum.db import api as db -from quantum.openstack.common import cfg -from quantum.openstack.common import importutils -import quantum.tests.unit.testlib_api as testlib -from quantum.wsgi import XMLDeserializer, JSONDeserializer - - -LOG = logging.getLogger('quantum.tests.test_api') - - -ROOTDIR = os.path.dirname(os.path.dirname(__file__)) -ETCDIR = os.path.join(ROOTDIR, 'etc') - - -def etcdir(*p): - return os.path.join(ETCDIR, *p) - - -NETS = "networks" -PORTS = "ports" -ATTS = "attachments" - - -class AbstractAPITest(unittest.TestCase): - """ Base class definiting some methods for API tests """ - - def _deserialize_net_response(self, content_type, response): - network_data = (self._net_deserializers[content_type]. - deserialize(response.body)['body']) - # do not taint assertions with xml namespace - if 'xmlns' in network_data['network']: - del network_data['network']['xmlns'] - return network_data - - def _deserialize_port_response(self, content_type, response): - port_data = (self._port_deserializers[content_type]. - deserialize(response.body)['body']) - # do not taint assertions with xml namespace - if 'xmlns' in port_data['port']: - del port_data['port']['xmlns'] - return port_data - - def _create_network(self, fmt, name=None, custom_req_body=None, - expected_res_status=None): - LOG.debug("Creating network") - content_type = "application/" + fmt - if name: - net_name = name - else: - net_name = self.network_name - network_req = testlib.new_network_request(self.tenant_id, - net_name, fmt, - custom_req_body) - network_res = network_req.get_response(self.api) - expected_res_status = (expected_res_status or - self._successful_create_code) - self.assertEqual(network_res.status_int, expected_res_status) - if expected_res_status in (200, 202): - network_data = self._deserialize_net_response(content_type, - network_res) - return network_data['network']['id'] - - def _create_port(self, network_id, port_state, fmt, custom_req_body=None, - expected_res_status=None): - LOG.debug("Creating port for network %s", network_id) - content_type = "application/%s" % fmt - port_req = testlib.new_port_request(self.tenant_id, network_id, - port_state, fmt, - custom_req_body) - port_res = port_req.get_response(self.api) - expected_res_status = (expected_res_status or - self._successful_create_code) - self.assertEqual(port_res.status_int, expected_res_status) - if expected_res_status in (200, 202): - port_data = self._deserialize_port_response(content_type, - port_res) - return port_data['port']['id'] - - def _set_attachment(self, network_id, port_id, interface_id, fmt, - expected_res_status=204): - put_attachment_req = testlib.put_attachment_request(self.tenant_id, - network_id, - port_id, - interface_id, - fmt) - put_attachment_res = put_attachment_req.get_response(self.api) - self.assertEqual(put_attachment_res.status_int, expected_res_status) - - def setUp(self, api_router_klass, xml_metadata_dict): - # Create the default configurations - args = ['--config-file', etcdir('quantum.conf.test')] - config.parse(args=args) - # Update the plugin - cfg.CONF.set_override('core_plugin', test_config['plugin_name']) - api_router_cls = importutils.import_class(api_router_klass) - self.api = api_router_cls() - self.tenant_id = "test_tenant" - self.network_name = "test_network" - - # Prepare XML & JSON deserializers - net_xml_deserializer = XMLDeserializer(xml_metadata_dict[NETS]) - port_xml_deserializer = XMLDeserializer(xml_metadata_dict[PORTS]) - att_xml_deserializer = XMLDeserializer(xml_metadata_dict[ATTS]) - - json_deserializer = JSONDeserializer() - - self._net_deserializers = { - 'application/xml': net_xml_deserializer, - 'application/json': json_deserializer, - } - self._port_deserializers = { - 'application/xml': port_xml_deserializer, - 'application/json': json_deserializer, - } - self._att_deserializers = { - 'application/xml': att_xml_deserializer, - 'application/json': json_deserializer, - } - - def tearDown(self): - """Clear the test environment""" - # Remove database contents - db.clear_db() - cfg.CONF.reset() - - -class BaseAPIOperationsTest(AbstractAPITest): - """Abstract base class for Quantum API unit tests - Defined according to operations defined for Quantum API v1.0 - """ - - def _test_create_network(self, fmt): - LOG.debug("_test_create_network - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - network_id = self._create_network(fmt) - show_network_req = testlib.show_network_request(self.tenant_id, - network_id, - fmt) - show_network_res = show_network_req.get_response(self.api) - self.assertEqual(show_network_res.status_int, 200) - network_data = (self._net_deserializers[content_type]. - deserialize(show_network_res.body)['body']) - self.assertEqual(network_id, network_data['network']['id']) - LOG.debug("_test_create_network - fmt:%s - END", fmt) - - def _test_create_network_badrequest(self, fmt): - LOG.debug("_test_create_network_badrequest - fmt:%s - START", fmt) - bad_body = {'network': {'bad-attribute': 'very-bad'}} - self._create_network(fmt, custom_req_body=bad_body, - expected_res_status=400) - LOG.debug("_test_create_network_badrequest - fmt:%s - END", fmt) - - def _test_list_networks(self, fmt): - LOG.debug("_test_list_networks - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - self._create_network(fmt, "net_1") - self._create_network(fmt, "net_2") - list_network_req = testlib.network_list_request(self.tenant_id, - fmt) - list_network_res = list_network_req.get_response(self.api) - self.assertEqual(list_network_res.status_int, 200) - network_data = (self._net_deserializers[content_type]. - deserialize(list_network_res.body)['body']) - # Check network count: should return 2 - self.assertEqual(len(network_data['networks']), 2) - LOG.debug("_test_list_networks - fmt:%s - END", fmt) - - def _test_list_networks_detail(self, fmt): - LOG.debug("_test_list_networks_detail - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - self._create_network(fmt, "net_1") - self._create_network(fmt, "net_2") - list_network_req = testlib.network_list_detail_request(self.tenant_id, - fmt) - list_network_res = list_network_req.get_response(self.api) - self.assertEqual(list_network_res.status_int, 200) - network_data = (self._net_deserializers[content_type]. - deserialize(list_network_res.body)['body']) - # Check network count: should return 2 - self.assertEqual(len(network_data['networks']), 2) - # Check contents - id & name for each network - for network in network_data['networks']: - self.assertTrue('id' in network and 'name' in network) - self.assertTrue(network['id'] and network['name']) - LOG.debug("_test_list_networks_detail - fmt:%s - END", fmt) - - def _test_show_network(self, fmt): - LOG.debug("_test_show_network - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - network_id = self._create_network(fmt) - show_network_req = testlib.show_network_request(self.tenant_id, - network_id, - fmt) - show_network_res = show_network_req.get_response(self.api) - self.assertEqual(show_network_res.status_int, 200) - network_data = self._deserialize_net_response(content_type, - show_network_res) - self.assert_network(id=network_id, name=self.network_name, - network_data=network_data['network']) - LOG.debug("_test_show_network - fmt:%s - END", fmt) - - def _test_show_network_detail(self, fmt): - LOG.debug("_test_show_network_detail - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - # Create a network and a port - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, "ACTIVE", fmt) - show_network_req = testlib.show_network_detail_request(self.tenant_id, - network_id, - fmt) - show_network_res = show_network_req.get_response(self.api) - self.assertEqual(show_network_res.status_int, 200) - network_data = self._deserialize_net_response(content_type, - show_network_res) - self.assert_network_details(id=network_id, name=self.network_name, - port_id=port_id, port_state='ACTIVE', - network_data=network_data['network']) - LOG.debug("_test_show_network_detail - fmt:%s - END", fmt) - - def _test_show_network_not_found(self, fmt): - LOG.debug("_test_show_network_not_found - fmt:%s - START", fmt) - show_network_req = testlib.show_network_request(self.tenant_id, - "A_BAD_ID", - fmt) - show_network_res = show_network_req.get_response(self.api) - self.assertEqual(show_network_res.status_int, - self._network_not_found_code) - LOG.debug("_test_show_network_not_found - fmt:%s - END", fmt) - - def _test_rename_network(self, fmt): - LOG.debug("_test_rename_network - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - new_name = 'new_network_name' - network_id = self._create_network(fmt) - update_network_req = testlib.update_network_request(self.tenant_id, - network_id, - new_name, - fmt) - update_network_res = update_network_req.get_response(self.api) - self.assertEqual(update_network_res.status_int, 204) - show_network_req = testlib.show_network_request(self.tenant_id, - network_id, - fmt) - show_network_res = show_network_req.get_response(self.api) - self.assertEqual(show_network_res.status_int, 200) - network_data = self._deserialize_net_response(content_type, - show_network_res) - self.assert_network(id=network_id, name=new_name, - network_data=network_data['network']) - LOG.debug("_test_rename_network - fmt:%s - END", fmt) - - def _test_rename_network_badrequest(self, fmt): - LOG.debug("_test_rename_network_badrequest - fmt:%s - START", fmt) - network_id = self._create_network(fmt) - bad_body = {'network': {'bad-attribute': 'very-bad'}} - update_network_req = testlib.update_network_request( - self.tenant_id, - network_id, fmt, - custom_req_body=bad_body) - update_network_res = update_network_req.get_response(self.api) - self.assertEqual(update_network_res.status_int, 400) - LOG.debug("_test_rename_network_badrequest - fmt:%s - END", fmt) - - def _test_rename_network_not_found(self, fmt): - LOG.debug("_test_rename_network_not_found - fmt:%s - START", fmt) - new_name = 'new_network_name' - update_network_req = testlib.update_network_request(self.tenant_id, - "A BAD ID", - new_name, - fmt) - update_network_res = update_network_req.get_response(self.api) - self.assertEqual(update_network_res.status_int, - self._network_not_found_code) - LOG.debug("_test_rename_network_not_found - fmt:%s - END", fmt) - - def _test_delete_network(self, fmt): - LOG.debug("_test_delete_network - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - network_id = self._create_network(fmt) - LOG.debug("Deleting network %s of tenant %s" % - (network_id, self.tenant_id)) - delete_network_req = testlib.network_delete_request(self.tenant_id, - network_id, - fmt) - delete_network_res = delete_network_req.get_response(self.api) - self.assertEqual(delete_network_res.status_int, 204) - list_network_req = testlib.network_list_request(self.tenant_id, - fmt) - list_network_res = list_network_req.get_response(self.api) - network_list_data = (self._net_deserializers[content_type]. - deserialize(list_network_res.body)['body']) - network_count = len(network_list_data['networks']) - self.assertEqual(network_count, 0) - LOG.debug("_test_delete_network - fmt:%s - END", fmt) - - def _test_delete_network_in_use(self, fmt): - LOG.debug("_test_delete_network_in_use - fmt:%s - START", fmt) - port_state = "ACTIVE" - attachment_id = "test_attachment" - network_id = self._create_network(fmt) - LOG.debug("Deleting network %s of tenant %s" % - (network_id, self.tenant_id)) - port_id = self._create_port(network_id, port_state, fmt) - #plug an attachment into the port - LOG.debug("Putting attachment into port %s", port_id) - attachment_req = testlib.put_attachment_request(self.tenant_id, - network_id, - port_id, - attachment_id) - attachment_res = attachment_req.get_response(self.api) - self.assertEquals(attachment_res.status_int, 204) - - LOG.debug("Deleting network %s of tenant %s" % - (network_id, self.tenant_id)) - delete_network_req = testlib.network_delete_request(self.tenant_id, - network_id, - fmt) - delete_network_res = delete_network_req.get_response(self.api) - self.assertEqual(delete_network_res.status_int, - self._network_in_use_code) - LOG.debug("_test_delete_network_in_use - fmt:%s - END", fmt) - - def _test_delete_network_with_unattached_port(self, fmt): - LOG.debug("_test_delete_network_with_unattached_port " - "- fmt:%s - START", fmt) - port_state = "ACTIVE" - network_id = self._create_network(fmt) - LOG.debug("Deleting network %s of tenant %s" % - (network_id, self.tenant_id)) - self._create_port(network_id, port_state, fmt) - - LOG.debug("Deleting network %s of tenant %s" % - (network_id, self.tenant_id)) - delete_network_req = testlib.network_delete_request(self.tenant_id, - network_id, - fmt) - delete_network_res = delete_network_req.get_response(self.api) - self.assertEqual(delete_network_res.status_int, 204) - LOG.debug("_test_delete_network_with_unattached_port - fmt:%s - END", - fmt) - - def _test_list_ports(self, fmt): - LOG.debug("_test_list_ports - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - port_state = "ACTIVE" - network_id = self._create_network(fmt) - self._create_port(network_id, port_state, fmt) - self._create_port(network_id, port_state, fmt) - list_port_req = testlib.port_list_request(self.tenant_id, - network_id, fmt) - list_port_res = list_port_req.get_response(self.api) - self.assertEqual(list_port_res.status_int, 200) - port_data = (self._port_deserializers[content_type]. - deserialize(list_port_res.body)['body']) - # Check port count: should return 2 - self.assertEqual(len(port_data['ports']), 2) - LOG.debug("_test_list_ports - fmt:%s - END", fmt) - - def _test_list_ports_networknotfound(self, fmt): - LOG.debug("_test_list_ports_networknotfound" - " - fmt:%s - START", fmt) - list_port_req = testlib.port_list_request(self.tenant_id, - "A_BAD_ID", fmt) - list_port_res = list_port_req.get_response(self.api) - self.assertEqual(list_port_res.status_int, - self._network_not_found_code) - LOG.debug("_test_list_ports_networknotfound - fmt:%s - END", fmt) - - def _test_list_ports_detail(self, fmt): - LOG.debug("_test_list_ports_detail - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - port_state = "ACTIVE" - network_id = self._create_network(fmt) - self._create_port(network_id, port_state, fmt) - self._create_port(network_id, port_state, fmt) - list_port_req = testlib.port_list_detail_request(self.tenant_id, - network_id, fmt) - list_port_res = list_port_req.get_response(self.api) - self.assertEqual(list_port_res.status_int, 200) - port_data = (self._port_deserializers[content_type]. - deserialize(list_port_res.body)['body']) - # Check port count: should return 2 - self.assertEqual(len(port_data['ports']), 2) - # Check contents - id & name for each network - for port in port_data['ports']: - self.assertTrue('id' in port and 'state' in port) - self.assertTrue(port['id'] and port['state']) - LOG.debug("_test_list_ports_detail - fmt:%s - END", fmt) - - def _test_show_port(self, fmt): - LOG.debug("_test_show_port - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - port_state = "ACTIVE" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - show_port_req = testlib.show_port_request(self.tenant_id, - network_id, port_id, - fmt) - show_port_res = show_port_req.get_response(self.api) - self.assertEqual(show_port_res.status_int, 200) - port_data = self._deserialize_port_response(content_type, - show_port_res) - self.assert_port(id=port_id, state=port_state, - port_data=port_data['port']) - LOG.debug("_test_show_port - fmt:%s - END", fmt) - - def _test_show_port_detail(self, fmt): - LOG.debug("_test_show_port - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - port_state = "ACTIVE" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - - # Part 1 - no attachment - show_port_req = testlib.show_port_detail_request( - self.tenant_id, network_id, port_id, fmt) - show_port_res = show_port_req.get_response(self.api) - self.assertEqual(show_port_res.status_int, 200) - port_data = self._deserialize_port_response(content_type, - show_port_res) - self.assert_port(id=port_id, state=port_state, - port_data=port_data['port']) - - # Part 2 - plug attachment into port - interface_id = "test_interface" - put_attachment_req = testlib.put_attachment_request(self.tenant_id, - network_id, - port_id, - interface_id, - fmt) - put_attachment_res = put_attachment_req.get_response(self.api) - self.assertEqual(put_attachment_res.status_int, 204) - show_port_req = testlib.show_port_detail_request( - self.tenant_id, network_id, port_id, fmt) - show_port_res = show_port_req.get_response(self.api) - self.assertEqual(show_port_res.status_int, 200) - port_data = self._deserialize_port_response(content_type, - show_port_res) - self.assert_port_attachment(id=port_id, state=port_state, - interface_id=interface_id, - port_data=port_data['port']) - - LOG.debug("_test_show_port_detail - fmt:%s - END", fmt) - - def _test_show_port_networknotfound(self, fmt): - LOG.debug("_test_show_port_networknotfound - fmt:%s - START", fmt) - port_state = "ACTIVE" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - show_port_req = testlib.show_port_request(self.tenant_id, - "A_BAD_ID", port_id, - fmt) - show_port_res = show_port_req.get_response(self.api) - self.assertEqual(show_port_res.status_int, - self._network_not_found_code) - LOG.debug("_test_show_port_networknotfound - fmt:%s - END", fmt) - - def _test_show_port_portnotfound(self, fmt): - LOG.debug("_test_show_port_portnotfound - fmt:%s - START", fmt) - network_id = self._create_network(fmt) - show_port_req = testlib.show_port_request(self.tenant_id, - network_id, - "A_BAD_ID", - fmt) - show_port_res = show_port_req.get_response(self.api) - self.assertEqual(show_port_res.status_int, - self._port_not_found_code) - LOG.debug("_test_show_port_portnotfound - fmt:%s - END", fmt) - - def _test_create_port_noreqbody(self, fmt): - LOG.debug("_test_create_port_noreqbody - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, None, fmt, - custom_req_body='') - show_port_req = testlib.show_port_request(self.tenant_id, - network_id, port_id, fmt) - show_port_res = show_port_req.get_response(self.api) - self.assertEqual(show_port_res.status_int, 200) - port_data = (self._port_deserializers[content_type]. - deserialize(show_port_res.body)['body']) - self.assertEqual(port_id, port_data['port']['id']) - LOG.debug("_test_create_port_noreqbody - fmt:%s - END", fmt) - - def _test_create_port(self, fmt): - LOG.debug("_test_create_port - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - port_state = "ACTIVE" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - show_port_req = testlib.show_port_request(self.tenant_id, - network_id, port_id, fmt) - show_port_res = show_port_req.get_response(self.api) - self.assertEqual(show_port_res.status_int, 200) - port_data = (self._port_deserializers[content_type]. - deserialize(show_port_res.body)['body']) - self.assertEqual(port_id, port_data['port']['id']) - LOG.debug("_test_create_port - fmt:%s - END", fmt) - - def _test_create_port_networknotfound(self, fmt): - LOG.debug("_test_create_port_networknotfound - fmt:%s - START", fmt) - port_state = "ACTIVE" - self._create_port("A_BAD_ID", port_state, fmt, - expected_res_status=self._network_not_found_code) - LOG.debug("_test_create_port_networknotfound - fmt:%s - END", fmt) - - def _test_create_port_badrequest(self, fmt): - LOG.debug("_test_create_port_badrequest - fmt:%s - START", fmt) - bad_body = {'bad-resource': {'bad-attribute': 'bad-value'}} - network_id = self._create_network(fmt) - port_state = "ACTIVE" - self._create_port(network_id, port_state, fmt, - custom_req_body=bad_body, expected_res_status=400) - LOG.debug("_test_create_port_badrequest - fmt:%s - END", fmt) - - def _test_create_port_badportstate(self, fmt): - LOG.debug("_test_create_port_badportstate - fmt:%s - START", fmt) - network_id = self._create_network(fmt) - port_state = "BADSTATE" - self._create_port(network_id, port_state, fmt, - expected_res_status=self._port_state_invalid_code) - LOG.debug("_test_create_port_badportstate - fmt:%s - END", fmt) - - def _test_delete_port(self, fmt): - LOG.debug("_test_delete_port - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - port_state = "ACTIVE" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - LOG.debug("Deleting port %s for network %s of tenant %s" % - (port_id, network_id, self.tenant_id)) - delete_port_req = testlib.port_delete_request(self.tenant_id, - network_id, port_id, - fmt) - delete_port_res = delete_port_req.get_response(self.api) - self.assertEqual(delete_port_res.status_int, 204) - list_port_req = testlib.port_list_request(self.tenant_id, network_id, - fmt) - list_port_res = list_port_req.get_response(self.api) - port_list_data = (self._port_deserializers[content_type]. - deserialize(list_port_res.body)['body']) - port_count = len(port_list_data['ports']) - self.assertEqual(port_count, 0) - LOG.debug("_test_delete_port - fmt:%s - END", fmt) - - def _test_delete_port_in_use(self, fmt): - LOG.debug("_test_delete_port_in_use - fmt:%s - START", fmt) - port_state = "ACTIVE" - attachment_id = "test_attachment" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - #plug an attachment into the port - LOG.debug("Putting attachment into port %s", port_id) - attachment_req = testlib.put_attachment_request(self.tenant_id, - network_id, - port_id, - attachment_id) - attachment_res = attachment_req.get_response(self.api) - self.assertEquals(attachment_res.status_int, 204) - LOG.debug("Deleting port %s for network %s of tenant %s" % - (port_id, network_id, self.tenant_id)) - delete_port_req = testlib.port_delete_request(self.tenant_id, - network_id, port_id, - fmt) - delete_port_res = delete_port_req.get_response(self.api) - self.assertEqual(delete_port_res.status_int, - self._port_in_use_code) - LOG.debug("_test_delete_port_in_use - fmt:%s - END", fmt) - - def _test_delete_port_with_bad_id(self, fmt): - LOG.debug("_test_delete_port_with_bad_id - fmt:%s - START", fmt) - port_state = "ACTIVE" - network_id = self._create_network(fmt) - self._create_port(network_id, port_state, fmt) - # Test for portnotfound - delete_port_req = testlib.port_delete_request(self.tenant_id, - network_id, "A_BAD_ID", - fmt) - delete_port_res = delete_port_req.get_response(self.api) - self.assertEqual(delete_port_res.status_int, - self._port_not_found_code) - LOG.debug("_test_delete_port_with_bad_id - fmt:%s - END", fmt) - - def _test_delete_port_networknotfound(self, fmt): - LOG.debug("_test_delete_port_networknotfound - fmt:%s - START", fmt) - port_state = "ACTIVE" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - delete_port_req = testlib.port_delete_request(self.tenant_id, - "A_BAD_ID", port_id, - fmt) - delete_port_res = delete_port_req.get_response(self.api) - self.assertEqual(delete_port_res.status_int, - self._network_not_found_code) - LOG.debug("_test_delete_port_networknotfound - fmt:%s - END", fmt) - - def _test_set_port_state(self, fmt): - LOG.debug("_test_set_port_state - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - port_state = 'DOWN' - new_port_state = 'ACTIVE' - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - update_port_req = testlib.update_port_request(self.tenant_id, - network_id, port_id, - new_port_state, - fmt) - update_port_res = update_port_req.get_response(self.api) - self.assertEqual(update_port_res.status_int, 204) - show_port_req = testlib.show_port_request(self.tenant_id, - network_id, port_id, - fmt) - show_port_res = show_port_req.get_response(self.api) - self.assertEqual(show_port_res.status_int, 200) - port_data = self._deserialize_port_response(content_type, - show_port_res) - self.assert_port(id=port_id, state=new_port_state, - port_data=port_data['port']) - # now set it back to the original value - update_port_req = testlib.update_port_request(self.tenant_id, - network_id, port_id, - port_state, - fmt) - update_port_res = update_port_req.get_response(self.api) - self.assertEqual(update_port_res.status_int, 204) - show_port_req = testlib.show_port_request(self.tenant_id, - network_id, port_id, - fmt) - show_port_res = show_port_req.get_response(self.api) - self.assertEqual(show_port_res.status_int, 200) - port_data = self._deserialize_port_response(content_type, - show_port_res) - self.assert_port(id=port_id, state=port_state, - port_data=port_data['port']) - LOG.debug("_test_set_port_state - fmt:%s - END", fmt) - - def _test_set_port_state_networknotfound(self, fmt): - LOG.debug("_test_set_port_state_networknotfound - fmt:%s - START", fmt) - port_state = 'DOWN' - new_port_state = 'ACTIVE' - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - update_port_req = testlib.update_port_request(self.tenant_id, - "A_BAD_ID", port_id, - new_port_state, - fmt) - update_port_res = update_port_req.get_response(self.api) - self.assertEqual(update_port_res.status_int, - self._network_not_found_code) - LOG.debug("_test_set_port_state_networknotfound - fmt:%s - END", fmt) - - def _test_set_port_state_portnotfound(self, fmt): - LOG.debug("_test_set_port_state_portnotfound - fmt:%s - START", fmt) - port_state = 'DOWN' - new_port_state = 'ACTIVE' - network_id = self._create_network(fmt) - self._create_port(network_id, port_state, fmt) - update_port_req = testlib.update_port_request(self.tenant_id, - network_id, - "A_BAD_ID", - new_port_state, - fmt) - update_port_res = update_port_req.get_response(self.api) - self.assertEqual(update_port_res.status_int, - self._port_not_found_code) - LOG.debug("_test_set_port_state_portnotfound - fmt:%s - END", fmt) - - def _test_set_port_state_stateinvalid(self, fmt): - LOG.debug("_test_set_port_state_stateinvalid - fmt:%s - START", fmt) - port_state = 'DOWN' - new_port_state = 'A_BAD_STATE' - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - update_port_req = testlib.update_port_request(self.tenant_id, - network_id, port_id, - new_port_state, - fmt) - update_port_res = update_port_req.get_response(self.api) - self.assertEqual(update_port_res.status_int, - self._port_state_invalid_code) - LOG.debug("_test_set_port_state_stateinvalid - fmt:%s - END", fmt) - - def _test_show_attachment(self, fmt): - LOG.debug("_test_show_attachment - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - port_state = "ACTIVE" - network_id = self._create_network(fmt) - interface_id = "test_interface" - port_id = self._create_port(network_id, port_state, fmt) - put_attachment_req = testlib.put_attachment_request(self.tenant_id, - network_id, - port_id, - interface_id, - fmt) - put_attachment_res = put_attachment_req.get_response(self.api) - self.assertEqual(put_attachment_res.status_int, 204) - get_attachment_req = testlib.get_attachment_request(self.tenant_id, - network_id, - port_id, - fmt) - get_attachment_res = get_attachment_req.get_response(self.api) - attachment_data = (self._att_deserializers[content_type]. - deserialize(get_attachment_res.body)['body']) - self.assertEqual(attachment_data['attachment']['id'], interface_id) - LOG.debug("_test_show_attachment - fmt:%s - END", fmt) - - def _test_show_attachment_none_set(self, fmt): - LOG.debug("_test_show_attachment_none_set - fmt:%s - START", fmt) - content_type = "application/%s" % fmt - port_state = "ACTIVE" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - get_attachment_req = testlib.get_attachment_request(self.tenant_id, - network_id, - port_id, - fmt) - get_attachment_res = get_attachment_req.get_response(self.api) - attachment_data = (self._att_deserializers[content_type]. - deserialize(get_attachment_res.body)['body']) - self.assertTrue('id' not in attachment_data['attachment']) - LOG.debug("_test_show_attachment_none_set - fmt:%s - END", fmt) - - def _test_show_attachment_networknotfound(self, fmt): - LOG.debug("_test_show_attachment_networknotfound - fmt:%s - START", - fmt) - port_state = "ACTIVE" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - get_attachment_req = testlib.get_attachment_request(self.tenant_id, - "A_BAD_ID", - port_id, - fmt) - get_attachment_res = get_attachment_req.get_response(self.api) - self.assertEqual(get_attachment_res.status_int, - self._network_not_found_code) - LOG.debug("_test_show_attachment_networknotfound - fmt:%s - END", fmt) - - def _test_show_attachment_portnotfound(self, fmt): - LOG.debug("_test_show_attachment_portnotfound - fmt:%s - START", fmt) - port_state = "ACTIVE" - network_id = self._create_network(fmt) - self._create_port(network_id, port_state, fmt) - get_attachment_req = testlib.get_attachment_request(self.tenant_id, - network_id, - "A_BAD_ID", - fmt) - get_attachment_res = get_attachment_req.get_response(self.api) - self.assertEqual(get_attachment_res.status_int, - self._port_not_found_code) - LOG.debug("_test_show_attachment_portnotfound - fmt:%s - END", fmt) - - def _test_put_attachment(self, fmt): - LOG.debug("_test_put_attachment - fmt:%s - START", fmt) - port_state = "ACTIVE" - network_id = self._create_network(fmt) - interface_id = "test_interface" - port_id = self._create_port(network_id, port_state, fmt) - put_attachment_req = testlib.put_attachment_request(self.tenant_id, - network_id, - port_id, - interface_id, - fmt) - put_attachment_res = put_attachment_req.get_response(self.api) - self.assertEqual(put_attachment_res.status_int, 204) - LOG.debug("_test_put_attachment - fmt:%s - END", fmt) - - def _test_put_attachment_networknotfound(self, fmt): - LOG.debug("_test_put_attachment_networknotfound - fmt:%s - START", fmt) - port_state = 'DOWN' - interface_id = "test_interface" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - put_attachment_req = testlib.put_attachment_request(self.tenant_id, - "A_BAD_ID", - port_id, - interface_id, - fmt) - put_attachment_res = put_attachment_req.get_response(self.api) - self.assertEqual(put_attachment_res.status_int, - self._network_not_found_code) - LOG.debug("_test_put_attachment_networknotfound - fmt:%s - END", fmt) - - def _test_put_attachment_portnotfound(self, fmt): - LOG.debug("_test_put_attachment_portnotfound - fmt:%s - START", fmt) - port_state = 'DOWN' - interface_id = "test_interface" - network_id = self._create_network(fmt) - self._create_port(network_id, port_state, fmt) - put_attachment_req = testlib.put_attachment_request(self.tenant_id, - network_id, - "A_BAD_ID", - interface_id, - fmt) - put_attachment_res = put_attachment_req.get_response(self.api) - self.assertEqual(put_attachment_res.status_int, - self._port_not_found_code) - LOG.debug("_test_put_attachment_portnotfound - fmt:%s - END", fmt) - - def _test_delete_attachment(self, fmt): - LOG.debug("_test_delete_attachment - fmt:%s - START", fmt) - port_state = "ACTIVE" - network_id = self._create_network(fmt) - interface_id = "test_interface" - port_id = self._create_port(network_id, port_state, fmt) - put_attachment_req = testlib.put_attachment_request(self.tenant_id, - network_id, - port_id, - interface_id, - fmt) - put_attachment_res = put_attachment_req.get_response(self.api) - self.assertEqual(put_attachment_res.status_int, 204) - del_attachment_req = testlib.delete_attachment_request(self.tenant_id, - network_id, - port_id, - fmt) - del_attachment_res = del_attachment_req.get_response(self.api) - self.assertEqual(del_attachment_res.status_int, 204) - LOG.debug("_test_delete_attachment - fmt:%s - END", fmt) - - def _test_delete_attachment_networknotfound(self, fmt): - LOG.debug("_test_delete_attachment_networknotfound - fmt:%s - START", - fmt) - port_state = "ACTIVE" - network_id = self._create_network(fmt) - port_id = self._create_port(network_id, port_state, fmt) - del_attachment_req = testlib.delete_attachment_request(self.tenant_id, - "A_BAD_ID", - port_id, - fmt) - del_attachment_res = del_attachment_req.get_response(self.api) - self.assertEqual(del_attachment_res.status_int, - self._network_not_found_code) - LOG.debug("_test_delete_attachment_networknotfound - fmt:%s - END", - fmt) - - def _test_delete_attachment_portnotfound(self, fmt): - LOG.debug("_test_delete_attachment_portnotfound - fmt:%s - START", fmt) - port_state = "ACTIVE" - network_id = self._create_network(fmt) - self._create_port(network_id, port_state, fmt) - del_attachment_req = testlib.delete_attachment_request(self.tenant_id, - network_id, - "A_BAD_ID", - fmt) - del_attachment_res = del_attachment_req.get_response(self.api) - self.assertEqual(del_attachment_res.status_int, - self._port_not_found_code) - LOG.debug("_test_delete_attachment_portnotfound - fmt:%s - END", fmt) - - def _test_unparsable_data(self, fmt): - LOG.debug("_test_unparsable_data - fmt:%s - START", fmt) - - data = "this is not json or xml" - method = 'POST' - content_type = "application/%s" % fmt - tenant_id = self.tenant_id - path = "/tenants/%(tenant_id)s/networks.%(fmt)s" % locals() - network_req = testlib.create_request(path, data, content_type, method) - network_res = network_req.get_response(self.api) - self.assertEqual(network_res.status_int, 400) - - LOG.debug("_test_unparsable_data - fmt:%s - END", fmt) - - def _test_multitenancy(self, fmt): - LOG.debug("_test_multitenancy - fmt:%s - START", fmt) - - # creates a network for tenant self.tenant_id - net_id = self._create_network(fmt) - port_id = self._create_port(net_id, "ACTIVE", fmt) - - invalid_tenant = self.tenant_id + "-invalid" - - def assert_net_not_found(base_path, method, fmt): - content_type = "application/%s" % fmt - full_path = "%s.%s" % (base_path, fmt) - req = testlib.create_request(full_path, None, content_type) - res = req.get_response(self.api) - self.assertEqual(res.status_int, self._network_not_found_code) - - # new tenant should NOT see this network UUID - net_path = "/tenants/%(invalid_tenant)s/networks/%(net_id)s" % locals() - net_detail_path = net_path + "/detail" - - assert_net_not_found(net_path, 'GET', fmt) - assert_net_not_found(net_path, 'PUT', fmt) - assert_net_not_found(net_path, 'DELETE', fmt) - assert_net_not_found(net_detail_path, 'GET', fmt) - - # new tenant should NOT see this network + port UUID - port_all_path = net_path + "/ports" - port_path = "%s/%s" % (port_all_path, port_id) - port_detail_path = port_path + "/detail" - - # NOTE: we actually still check for a network not found - # error here, as both the network and port in the URL are - # invalid. This is consistent with the test - # _test_show_port_networknotfound - assert_net_not_found(port_all_path, 'POST', fmt) - assert_net_not_found(port_all_path, 'GET', fmt) - assert_net_not_found(port_path, 'GET', fmt) - assert_net_not_found(port_path, 'PUT', fmt) - assert_net_not_found(port_path, 'DELETE', fmt) - assert_net_not_found(port_detail_path, 'GET', fmt) - - attach_path = port_path + "/attachment" - assert_net_not_found(attach_path, 'GET', fmt) - assert_net_not_found(attach_path, 'PUT', fmt) - assert_net_not_found(attach_path, 'DELETE', fmt) - - LOG.debug("_test_multitenancy - fmt:%s - END", fmt) - - def test_list_networks_json(self): - self._test_list_networks('json') - - def test_list_networks_xml(self): - self._test_list_networks('xml') - - def test_list_networks_detail_json(self): - self._test_list_networks_detail('json') - - def test_list_networks_detail_xml(self): - self._test_list_networks_detail('xml') - - def test_create_network_json(self): - self._test_create_network('json') - - def test_create_network_xml(self): - self._test_create_network('xml') - - def test_create_network_badrequest_json(self): - self._test_create_network_badrequest('json') - - def test_create_network_badrequest_xml(self): - self._test_create_network_badrequest('xml') - - def test_show_network_not_found_json(self): - self._test_show_network_not_found('json') - - def test_show_network_not_found_xml(self): - self._test_show_network_not_found('xml') - - def test_show_network_json(self): - self._test_show_network('json') - - def test_show_network_xml(self): - self._test_show_network('xml') - - def test_show_network_detail_json(self): - self._test_show_network_detail('json') - - def test_show_network_detail_xml(self): - self._test_show_network_detail('xml') - - def test_delete_network_json(self): - self._test_delete_network('json') - - def test_delete_network_xml(self): - self._test_delete_network('xml') - - def test_rename_network_json(self): - self._test_rename_network('json') - - def test_rename_network_xml(self): - self._test_rename_network('xml') - - def test_rename_network_badrequest_json(self): - self._test_rename_network_badrequest('json') - - def test_rename_network_badrequest_xml(self): - self._test_rename_network_badrequest('xml') - - def test_rename_network_not_found_json(self): - self._test_rename_network_not_found('json') - - def test_rename_network_not_found_xml(self): - self._test_rename_network_not_found('xml') - - def test_delete_network_in_use_json(self): - self._test_delete_network_in_use('json') - - def test_delete_network_in_use_xml(self): - self._test_delete_network_in_use('xml') - - def test_delete_network_with_unattached_port_xml(self): - self._test_delete_network_with_unattached_port('xml') - - def test_delete_network_with_unattached_port_json(self): - self._test_delete_network_with_unattached_port('json') - - def test_list_ports_json(self): - self._test_list_ports('json') - - def test_list_ports_xml(self): - self._test_list_ports('xml') - - def test_list_ports_networknotfound_json(self): - self._test_list_ports_networknotfound('json') - - def test_list_ports_networknotfound_xml(self): - self._test_list_ports_networknotfound('xml') - - def test_list_ports_detail_json(self): - self._test_list_ports_detail('json') - - def test_list_ports_detail_xml(self): - self._test_list_ports_detail('xml') - - def test_show_port_json(self): - self._test_show_port('json') - - def test_show_port_xml(self): - self._test_show_port('xml') - - def test_show_port_detail_json(self): - self._test_show_port_detail('json') - - def test_show_port_detail_xml(self): - self._test_show_port_detail('xml') - - def test_show_port_networknotfound_json(self): - self._test_show_port_networknotfound('json') - - def test_show_port_networknotfound_xml(self): - self._test_show_port_networknotfound('xml') - - def test_show_port_portnotfound_json(self): - self._test_show_port_portnotfound('json') - - def test_show_port_portnotfound_xml(self): - self._test_show_port_portnotfound('xml') - - def test_create_port_json(self): - self._test_create_port('json') - - def test_create_port_xml(self): - self._test_create_port('xml') - - def test_create_port_noreqbody_json(self): - self._test_create_port_noreqbody('json') - - def test_create_port_noreqbody_xml(self): - self._test_create_port_noreqbody('xml') - - def test_create_port_networknotfound_json(self): - self._test_create_port_networknotfound('json') - - def test_create_port_networknotfound_xml(self): - self._test_create_port_networknotfound('xml') - - def test_create_port_badrequest_json(self): - self._test_create_port_badrequest('json') - - def test_create_port_badrequest_xml(self): - self._test_create_port_badrequest('xml') - - def test_create_port_badportstate_json(self): - self._test_create_port_badportstate('json') - - def test_create_port_badportstate_xml(self): - self._test_create_port_badportstate('xml') - - def test_delete_port_xml(self): - self._test_delete_port('xml') - - def test_delete_port_json(self): - self._test_delete_port('json') - - def test_delete_port_in_use_xml(self): - self._test_delete_port_in_use('xml') - - def test_delete_port_in_use_json(self): - self._test_delete_port_in_use('json') - - def test_delete_port_networknotfound_xml(self): - self._test_delete_port_networknotfound('xml') - - def test_delete_port_networknotfound_json(self): - self._test_delete_port_networknotfound('json') - - def test_delete_port_with_bad_id_xml(self): - self._test_delete_port_with_bad_id('xml') - - def test_delete_port_with_bad_id_json(self): - self._test_delete_port_with_bad_id('json') - - def test_set_port_state_xml(self): - self._test_set_port_state('xml') - - def test_set_port_state_json(self): - self._test_set_port_state('json') - - def test_set_port_state_networknotfound_xml(self): - self._test_set_port_state_networknotfound('xml') - - def test_set_port_state_networknotfound_json(self): - self._test_set_port_state_networknotfound('json') - - def test_set_port_state_portnotfound_xml(self): - self._test_set_port_state_portnotfound('xml') - - def test_set_port_state_portnotfound_json(self): - self._test_set_port_state_portnotfound('json') - - def test_set_port_state_stateinvalid_xml(self): - self._test_set_port_state_stateinvalid('xml') - - def test_set_port_state_stateinvalid_json(self): - self._test_set_port_state_stateinvalid('json') - - def test_show_attachment_xml(self): - self._test_show_attachment('xml') - - def test_show_attachment_json(self): - self._test_show_attachment('json') - - def test_show_attachment_none_set_xml(self): - self._test_show_attachment_none_set('xml') - - def test_show_attachment_none_set_json(self): - self._test_show_attachment_none_set('json') - - def test_show_attachment_networknotfound_xml(self): - self._test_show_attachment_networknotfound('xml') - - def test_show_attachment_networknotfound_json(self): - self._test_show_attachment_networknotfound('json') - - def test_show_attachment_portnotfound_xml(self): - self._test_show_attachment_portnotfound('xml') - - def test_show_attachment_portnotfound_json(self): - self._test_show_attachment_portnotfound('json') - - def test_put_attachment_xml(self): - self._test_put_attachment('xml') - - def test_put_attachment_json(self): - self._test_put_attachment('json') - - def test_put_attachment_networknotfound_xml(self): - self._test_put_attachment_networknotfound('xml') - - def test_put_attachment_networknotfound_json(self): - self._test_put_attachment_networknotfound('json') - - def test_put_attachment_portnotfound_xml(self): - self._test_put_attachment_portnotfound('xml') - - def test_put_attachment_portnotfound_json(self): - self._test_put_attachment_portnotfound('json') - - def test_delete_attachment_xml(self): - self._test_delete_attachment('xml') - - def test_delete_attachment_json(self): - self._test_delete_attachment('json') - - def test_delete_attachment_networknotfound_xml(self): - self._test_delete_attachment_networknotfound('xml') - - def test_delete_attachment_networknotfound_json(self): - self._test_delete_attachment_networknotfound('json') - - def test_delete_attachment_portnotfound_xml(self): - self._test_delete_attachment_portnotfound('xml') - - def test_delete_attachment_portnotfound_json(self): - self._test_delete_attachment_portnotfound('json') - - def test_unparsable_data_xml(self): - self._test_unparsable_data('xml') - - def test_unparsable_data_json(self): - self._test_unparsable_data('json') - - def test_multitenancy_xml(self): - self._test_multitenancy('xml') - - def test_multitenancy_json(self): - self._test_multitenancy('json') - - def test_internal_error(self): - """Check that internal errors do not leak. - - Any internal, unexpected error should be turned into a 500 response - without any traces of the original exception. - """ - orig_exception_msg = "An exception with a traceback" - - @APIFaultWrapper() - def raise_exception(self, *args, **kwargs): - raise Exception(orig_exception_msg) - - list_network_req = testlib.network_list_request(self.tenant_id, "json") - with mock.patch.object(Controller, 'index', new=raise_exception): - list_network_res = list_network_req.get_response(self.api) - self.assertEqual(list_network_res.status_int, 500) - self.assertNotIn(orig_exception_msg, list_network_res.body) diff --git a/quantum/tests/unit/test_api.py b/quantum/tests/unit/test_api.py deleted file mode 100644 index 82aef8f854..0000000000 --- a/quantum/tests/unit/test_api.py +++ /dev/null @@ -1,372 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010-2012 ???? -# 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 - -import unittest - -from lxml import etree -from webob import exc - -import quantum.api.attachments as atts -import quantum.api.networks as nets -import quantum.api.ports as ports -import quantum.api.versions as versions -from quantum.common.test_lib import test_config -from quantum.openstack.common import jsonutils -import quantum.tests.unit._test_api as test_api -import quantum.tests.unit.testlib_api as testlib - - -class APITestV10(test_api.BaseAPIOperationsTest): - - def assert_network(self, **kwargs): - self.assertEqual({'id': kwargs['id'], - 'name': kwargs['name']}, - kwargs['network_data']) - - def assert_network_details(self, **kwargs): - self.assertEqual({'id': kwargs['id'], - 'name': kwargs['name'], - 'ports': [{'id': kwargs['port_id'], - 'state': 'ACTIVE'}]}, - kwargs['network_data']) - - def assert_port(self, **kwargs): - self.assertEqual({'id': kwargs['id'], - 'state': kwargs['state']}, - kwargs['port_data']) - - def assert_port_attachment(self, **kwargs): - self.assertEqual({'id': kwargs['id'], 'state': kwargs['state'], - 'attachment': {'id': kwargs['interface_id']}}, - kwargs['port_data']) - - def setUp(self): - super(APITestV10, self).setUp( - 'quantum.api.APIRouterV10', - { - test_api.NETS: nets.ControllerV10._serialization_metadata, - test_api.PORTS: ports.ControllerV10._serialization_metadata, - test_api.ATTS: atts.ControllerV10._serialization_metadata, - } - ) - self._successful_create_code = exc.HTTPOk.code - self._network_not_found_code = 420 - self._network_in_use_code = 421 - self._port_not_found_code = 430 - self._port_state_invalid_code = 431 - self._port_in_use_code = 432 - self._already_attached_code = 440 - - -class APITestV11(test_api.BaseAPIOperationsTest): - - def assert_network(self, **kwargs): - self.assertEqual({'id': kwargs['id'], - 'name': kwargs['name'], - 'op-status': self.net_op_status}, - kwargs['network_data']) - - def assert_network_details(self, **kwargs): - self.assertEqual({'id': kwargs['id'], - 'name': kwargs['name'], - 'op-status': self.net_op_status, - 'ports': [{'id': kwargs['port_id'], - 'state': 'ACTIVE', - 'op-status': self.port_op_status}]}, - kwargs['network_data']) - - def assert_port(self, **kwargs): - self.assertEqual({'id': kwargs['id'], - 'state': kwargs['state'], - 'op-status': self.port_op_status}, - kwargs['port_data']) - - def assert_port_attachment(self, **kwargs): - self.assertEqual({'id': kwargs['id'], 'state': kwargs['state'], - 'op-status': self.port_op_status, - 'attachment': {'id': kwargs['interface_id']}}, - kwargs['port_data']) - - def setUp(self): - self.net_op_status = test_config.get('default_net_op_status', - 'UNKNOWN') - self.port_op_status = test_config.get('default_port_op_status', - 'UNKNOWN') - super(APITestV11, self).setUp( - 'quantum.api.APIRouterV11', - { - test_api.NETS: nets.ControllerV11._serialization_metadata, - test_api.PORTS: ports.ControllerV11._serialization_metadata, - test_api.ATTS: atts.ControllerV11._serialization_metadata, - } - ) - self._successful_create_code = exc.HTTPAccepted.code - self._network_not_found_code = exc.HTTPNotFound.code - self._network_in_use_code = exc.HTTPConflict.code - self._port_not_found_code = exc.HTTPNotFound.code - self._port_state_invalid_code = exc.HTTPBadRequest.code - self._port_in_use_code = exc.HTTPConflict.code - self._already_attached_code = exc.HTTPConflict.code - - -class APIFiltersTest(test_api.AbstractAPITest): - """ Test case for API filters. - Uses controller for API v1.1 - """ - - def _do_filtered_network_list_request(self, flt): - list_network_req = testlib.network_list_request(self.tenant_id, - self.fmt, - query_string=flt) - list_network_res = list_network_req.get_response(self.api) - self.assertEqual(list_network_res.status_int, 200) - network_data = (self._net_deserializers[self.content_type]. - deserialize(list_network_res.body)['body']) - return network_data - - def _do_filtered_port_list_request(self, flt, network_id): - list_port_req = testlib.port_list_request(self.tenant_id, - network_id, - self.fmt, - query_string=flt) - list_port_res = list_port_req.get_response(self.api) - self.assertEqual(list_port_res.status_int, 200) - port_data = (self._port_deserializers[self.content_type]. - deserialize(list_port_res.body)['body']) - return port_data - - def setUp(self): - super(APIFiltersTest, self).setUp( - 'quantum.api.APIRouterV11', - { - test_api.NETS: nets.ControllerV11._serialization_metadata, - test_api.PORTS: ports.ControllerV11._serialization_metadata, - test_api.ATTS: atts.ControllerV11._serialization_metadata, - } - ) - self._successful_create_code = exc.HTTPAccepted.code - self.net_op_status = test_config.get('default_net_op_status', - 'UNKNOWN') - self.port_op_status = test_config.get('default_port_op_status', - 'UNKNOWN') - self.fmt = "xml" - self.content_type = "application/%s" % self.fmt - # create data for validating filters - # Create network "test-1" - self.net1_id = self._create_network(self.fmt, name="test-1") - # Add 2 ports, 1 ACTIVE, 1 DOWN - self.port11_id = self._create_port(self.net1_id, "ACTIVE", self.fmt) - self.port12_id = self._create_port(self.net1_id, "DOWN", self.fmt) - # Put attachment "test-1-att" in active port - self._set_attachment(self.net1_id, - self.port11_id, - "test-1-att", - self.fmt) - # Create network "test-2" - # Add 2 ports, 2 ACTIVE, 0 DOWN - self.net2_id = self._create_network(self.fmt, name="test-2") - self.port21_id = self._create_port(self.net2_id, "ACTIVE", self.fmt) - self.port22_id = self._create_port(self.net2_id, "ACTIVE", self.fmt) - - def test_network_name_filter(self): - flt = "name=test-1" - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 1 - self.assertEqual(len(network_data['networks']), 1) - self.assertEqual(network_data['networks'][0]['id'], self.net1_id) - - flt = "name=non-existent" - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 0 - self.assertEqual(len(network_data['networks']), 0) - - def test_network_op_status_filter(self): - # First filter for networks in default status - flt = "op-status=%s" % self.net_op_status - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 2 - self.assertEqual(len(network_data['networks']), 2) - - # And then for networks in 'DOWN' status - flt = "op-status=DOWN" - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 0 - self.assertEqual(len(network_data['networks']), 0) - - def test_network_port_op_status_filter(self): - # First filter for networks with ports in default op status - flt = "port-op-status=%s" % self.port_op_status - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 2 - self.assertEqual(len(network_data['networks']), 2) - - def test_network_port_state_filter(self): - # First filter for networks with ports 'ACTIVE' - flt = "port-state=ACTIVE" - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 2 - self.assertEqual(len(network_data['networks']), 2) - - # And then for networks with ports in 'DOWN' admin state - flt = "port-state=DOWN" - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 1 - self.assertEqual(len(network_data['networks']), 1) - - def test_network_has_attachment_filter(self): - # First filter for networks with ports 'ACTIVE' - flt = "has-attachment=True" - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 1 - self.assertEqual(len(network_data['networks']), 1) - - # And then for networks with ports in 'DOWN' admin state - flt = "has-attachment=False" - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 1 - self.assertEqual(len(network_data['networks']), 1) - - def test_network_port_filter(self): - flt = "port=%s" % self.port11_id - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 1 - self.assertEqual(len(network_data['networks']), 1) - self.assertEqual(network_data['networks'][0]['id'], self.net1_id) - - flt = "port=%s" % self.port21_id - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 1 - self.assertEqual(len(network_data['networks']), 1) - self.assertEqual(network_data['networks'][0]['id'], self.net2_id) - - def test_network_attachment_filter(self): - flt = "attachment=test-1-att" - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 1 - self.assertEqual(len(network_data['networks']), 1) - self.assertEqual(network_data['networks'][0]['id'], self.net1_id) - - flt = "attachment=non-existent" - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 0 - self.assertEqual(len(network_data['networks']), 0) - - def test_network_multiple_filters(self): - # Add some data for having more fun - another_net_id = self._create_network(self.fmt, name="test-1") - # Add 1 ACTIVE port - self._create_port(another_net_id, "ACTIVE", self.fmt) - # Do the filtering - flt = "name=test-1&port-state=ACTIVE&attachment=test-1-att" - network_data = self._do_filtered_network_list_request(flt) - # Check network count: should return 1 - self.assertEqual(len(network_data['networks']), 1) - self.assertEqual(network_data['networks'][0]['id'], self.net1_id) - - def test_port_state_filter(self): - # First filter for 'ACTIVE' ports in 1st network - flt = "state=ACTIVE" - port_data = self._do_filtered_port_list_request(flt, self.net1_id) - # Check port count: should return 1 - self.assertEqual(len(port_data['ports']), 1) - - # And then in 2nd network - port_data = self._do_filtered_port_list_request(flt, self.net2_id) - # Check port count: should return 2 - self.assertEqual(len(port_data['ports']), 2) - - def test_port_op_status_filter(self): - # First filter for 'UP' ports in 1st network - flt = "op-status=%s" % self.port_op_status - port_data = self._do_filtered_port_list_request(flt, self.net1_id) - # Check port count: should return 2 - self.assertEqual(len(port_data['ports']), 2) - - def test_port_has_attachment_filter(self): - # First search for ports with attachments in 1st network - flt = "has-attachment=True" - port_data = self._do_filtered_port_list_request(flt, self.net1_id) - # Check port count: should return 1 - self.assertEqual(len(port_data['ports']), 1) - self.assertEqual(port_data['ports'][0]['id'], self.port11_id) - - # And then for ports without attachment in 2nd network - flt = "has-attachment=False" - port_data = self._do_filtered_port_list_request(flt, self.net2_id) - # Check port count: should return 2 - self.assertEqual(len(port_data['ports']), 2) - - def test_port_attachment_filter(self): - # First search for ports with attachments in 1st network - flt = "attachment=test-1-att" - port_data = self._do_filtered_port_list_request(flt, self.net1_id) - # Check port count: should return 1 - self.assertEqual(len(port_data['ports']), 1) - self.assertEqual(port_data['ports'][0]['id'], self.port11_id) - - # And then for a non-existent attachment in 2nd network - flt = "attachment=non-existent" - port_data = self._do_filtered_port_list_request(flt, self.net2_id) - # Check port count: should return 0 - self.assertEqual(len(port_data['ports']), 0) - - def test_port_multiple_filters(self): - flt = "op-status=%s&state=DOWN" % self.port_op_status - port_data = self._do_filtered_port_list_request(flt, self.net1_id) - # Check port count: should return 1 - self.assertEqual(len(port_data['ports']), 1) - self.assertEqual(port_data['ports'][0]['id'], self.port12_id) - - flt = "state=ACTIVE&attachment=test-1-att" - port_data = self._do_filtered_port_list_request(flt, self.net1_id) - # Check port count: should return 1 - self.assertEqual(len(port_data['ports']), 1) - self.assertEqual(port_data['ports'][0]['id'], self.port11_id) - - flt = "state=ACTIVE&has-attachment=False" - port_data = self._do_filtered_port_list_request(flt, self.net2_id) - # Check port count: should return 2 - self.assertEqual(len(port_data['ports']), 2) - - -class APIRootTest(unittest.TestCase): - def setUp(self): - self.app = versions.Versions() - - def _test_root_responds_with_versions(self, content_type): - req = testlib.create_request('/', '', content_type) - response = self.app(req) - self.assertEquals(response.status_int, 200) - return response.body - - def test_root_responds_with_versions_json(self): - body = self._test_root_responds_with_versions('application/json') - data = jsonutils.loads(body) - self.assertEquals('versions', data.keys()[0]) - - def test_root_responds_with_versions_xml(self): - body = self._test_root_responds_with_versions('application/xml') - root = etree.fromstring(body) - self.assertEquals(root.tag, 'versions') - - def test_invalid_version(self): - req = testlib.create_request('/v99.99/tenants/tenantX/networks', - '', - 'application/json') - response = self.app(req) - self.assertEquals(response.status_int, 404) diff --git a/quantum/tests/unit/test_database.py b/quantum/tests/unit/test_database.py deleted file mode 100644 index 1e883a6a7a..0000000000 --- a/quantum/tests/unit/test_database.py +++ /dev/null @@ -1,127 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011, Cisco Systems, Inc. -# -# 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: Rohit Agarwalla, Cisco Systems, Inc. - -""" -test_database.py is an independent test suite -that tests the database api method calls -""" - -import unittest - -from quantum.db import api as db -from quantum.tests.unit import database_stubs as db_stubs - - -class QuantumDBTest(unittest.TestCase): - """Class consisting of Quantum DB unit tests""" - def setUp(self): - """Setup for tests""" - db.configure_db({'sql_connection': 'sqlite:///:memory:'}) - self.dbtest = db_stubs.QuantumDB() - self.tenant_id = "t1" - - def tearDown(self): - """Tear Down""" - db.clear_db() - - def testa_create_network(self): - """test to create network""" - net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1") - self.assertTrue(net1["name"] == "plugin_test1") - - def testb_get_networks(self): - """test to get all networks""" - net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1") - self.assertTrue(net1["name"] == "plugin_test1") - net2 = self.dbtest.create_network(self.tenant_id, "plugin_test2") - self.assertTrue(net2["name"] == "plugin_test2") - nets = self.dbtest.get_all_networks(self.tenant_id) - count = 0 - for net in nets: - if "plugin_test" in net["name"]: - count += 1 - self.assertTrue(count == 2) - - def testc_delete_network(self): - """test to delete network""" - net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1") - self.assertTrue(net1["name"] == "plugin_test1") - self.dbtest.delete_network(net1["id"]) - nets = self.dbtest.get_all_networks(self.tenant_id) - count = len(nets) - self.assertTrue(count == 0) - - def testd_update_network(self): - """test to rename network""" - net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1") - self.assertTrue(net1["name"] == "plugin_test1") - net = self.dbtest.update_network(self.tenant_id, net1["id"], - {'name': "plugin_test1_renamed"}) - print net - self.assertTrue(net["name"] == "plugin_test1_renamed") - - def teste_create_port(self): - """test to create port""" - net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1") - port = self.dbtest.create_port(net1["id"]) - self.assertTrue(port["net-id"] == net1["id"]) - - def testf_get_ports(self): - """test to get ports""" - net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1") - port = self.dbtest.create_port(net1["id"]) - self.assertTrue(port["net-id"] == net1["id"]) - ports = self.dbtest.get_all_ports(net1["id"]) - count = len(ports) - self.assertTrue(count == 1) - - def testf_update_port(self): - """test to update port""" - net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1") - port = self.dbtest.create_port(net1["id"]) - self.dbtest.update_port(port["net-id"], - port['id'], - state='ACTIVE', - interface_id='interface_id1') - self.assertTrue(port["net-id"] == net1["id"]) - ports = self.dbtest.get_all_ports(net1["id"]) - new_port = ports[0] - self.assertEqual('ACTIVE', new_port['state']) - self.assertEqual('interface_id1', new_port['attachment']) - - def testf_delete_port(self): - """test to delete port""" - net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1") - port = self.dbtest.create_port(net1["id"]) - self.assertTrue(port["net-id"] == net1["id"]) - ports = self.dbtest.get_all_ports(net1["id"]) - for por in ports: - self.dbtest.delete_port(net1["id"], por["id"]) - ports = self.dbtest.get_all_ports(net1["id"]) - count = len(ports) - self.assertTrue(count == 0) - - def testg_plug_unplug_interface(self): - """test to plug/unplug interface""" - net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1") - port1 = self.dbtest.create_port(net1["id"]) - self.dbtest.plug_interface(net1["id"], port1["id"], "vif1.1") - port = self.dbtest.get_port(net1["id"], port1["id"]) - self.assertTrue(port[0]["attachment"] == "vif1.1") - self.dbtest.unplug_interface(net1["id"], port1["id"]) - port = self.dbtest.get_port(net1["id"], port1["id"]) - self.assertTrue(port[0]["attachment"] is None) diff --git a/quantum/tests/unit/test_extensions.py b/quantum/tests/unit/test_extensions.py index ed7efdf92a..777c745f4d 100644 --- a/quantum/tests/unit/test_extensions.py +++ b/quantum/tests/unit/test_extensions.py @@ -19,10 +19,10 @@ import os import unittest import routes +import webob from webtest import AppError from webtest import TestApp -from quantum.api import faults from quantum.common import config from quantum.common import exceptions from quantum.extensions import extensions @@ -32,7 +32,7 @@ from quantum.extensions.extensions import ( PluginAwareExtensionManager, ) from quantum.openstack.common import jsonutils -from quantum.plugins.sample.SamplePlugin import QuantumEchoPlugin +from quantum.db.db_base_plugin_v2 import QuantumDbPluginV2 from quantum.tests.unit import BaseTest from quantum.tests.unit.extension_stubs import ( ExtensionExpectingPluginInterface, @@ -65,6 +65,15 @@ class ExtensionsTestApp(wsgi.Router): super(ExtensionsTestApp, self).__init__(mapper) +class FakePluginWithExtension(QuantumDbPluginV2): + """A fake plugin used only for extension testing in this file.""" + + supported_extension_aliases = ["FOXNSOX"] + + def method_to_support_foxnsox_extension(self, context): + self._log("method_to_support_foxnsox_extension", context) + + class ResourceExtensionTest(unittest.TestCase): class ResourceExtensionController(wsgi.Controller): @@ -76,8 +85,7 @@ class ResourceExtensionTest(unittest.TestCase): return {'data': {'id': id}} def notimplemented_function(self, request, id): - return faults.Quantum10HTTPError( - exceptions.NotImplementedError("notimplemented_function")) + return webob.exc.HTTPClientError(NotImplementedError()) def custom_member_action(self, request, id): return {'member_action': 'value'} @@ -473,8 +481,9 @@ def setup_base_app(): def setup_extensions_middleware(extension_manager=None): extension_manager = (extension_manager or - PluginAwareExtensionManager(extensions_path, - QuantumEchoPlugin())) + PluginAwareExtensionManager( + extensions_path, + FakePluginWithExtension())) config_file = 'quantum.conf.test' args = ['--config-file', etcdir(config_file)] config.parse(args=args) diff --git a/quantum/tests/unit/testlib_api.py b/quantum/tests/unit/testlib_api.py index 341a9acb8a..2023bf49eb 100644 --- a/quantum/tests/unit/testlib_api.py +++ b/quantum/tests/unit/testlib_api.py @@ -28,170 +28,3 @@ def create_request(path, body, content_type, method='GET', query_string=None): req.headers['Accept'] = content_type req.body = body return req - - -def _network_list_request(tenant_id, format='xml', detail=False, - query_string=None): - method = 'GET' - detail_str = detail and '/detail' or '' - path = ("/tenants/%(tenant_id)s/networks" - "%(detail_str)s.%(format)s") % locals() - content_type = "application/%s" % format - return create_request(path, None, content_type, method, query_string) - - -def network_list_request(tenant_id, format='xml', query_string=None): - return _network_list_request(tenant_id, format, query_string=query_string) - - -def network_list_detail_request(tenant_id, format='xml'): - return _network_list_request(tenant_id, format, detail=True) - - -def _show_network_request(tenant_id, network_id, format='xml', detail=False): - method = 'GET' - detail_str = detail and '/detail' or '' - path = ("/tenants/%(tenant_id)s/networks" - "/%(network_id)s%(detail_str)s.%(format)s") % locals() - content_type = "application/%s" % format - return create_request(path, None, content_type, method) - - -def show_network_request(tenant_id, network_id, format='xml'): - return _show_network_request(tenant_id, network_id, format) - - -def show_network_detail_request(tenant_id, network_id, format='xml'): - return _show_network_request(tenant_id, network_id, format, detail=True) - - -def new_network_request(tenant_id, network_name='new_name', - format='xml', custom_req_body=None): - method = 'POST' - path = "/tenants/%(tenant_id)s/networks.%(format)s" % locals() - data = custom_req_body or {'network': {'name': '%s' % network_name}} - content_type = "application/%s" % format - body = Serializer().serialize(data, content_type) - return create_request(path, body, content_type, method) - - -def update_network_request(tenant_id, network_id, network_name, format='xml', - custom_req_body=None): - method = 'PUT' - path = ("/tenants/%(tenant_id)s/networks" - "/%(network_id)s.%(format)s") % locals() - data = custom_req_body or {'network': {'name': '%s' % network_name}} - content_type = "application/%s" % format - body = Serializer().serialize(data, content_type) - return create_request(path, body, content_type, method) - - -def network_delete_request(tenant_id, network_id, format='xml'): - method = 'DELETE' - path = ("/tenants/%(tenant_id)s/networks/" - "%(network_id)s.%(format)s") % locals() - content_type = "application/%s" % format - return create_request(path, None, content_type, method) - - -def _port_list_request(tenant_id, network_id, format='xml', - detail=False, query_string=None): - method = 'GET' - detail_str = detail and '/detail' or '' - path = ("/tenants/%(tenant_id)s/networks/" - "%(network_id)s/ports%(detail_str)s.%(format)s") % locals() - content_type = "application/%s" % format - return create_request(path, None, content_type, method, query_string) - - -def port_list_request(tenant_id, network_id, format='xml', query_string=None): - return _port_list_request(tenant_id, - network_id, - format, - query_string=query_string) - - -def port_list_detail_request(tenant_id, network_id, format='xml'): - return _port_list_request(tenant_id, network_id, - format, detail=True) - - -def _show_port_request(tenant_id, network_id, port_id, - format='xml', detail=False): - method = 'GET' - detail_str = detail and '/detail' or '' - path = ("/tenants/%(tenant_id)s/networks/%(network_id)s" - "/ports/%(port_id)s%(detail_str)s.%(format)s") % locals() - content_type = "application/%s" % format - return create_request(path, None, content_type, method) - - -def show_port_request(tenant_id, network_id, port_id, format='xml'): - return _show_port_request(tenant_id, network_id, port_id, format) - - -def show_port_detail_request(tenant_id, network_id, port_id, format='xml'): - return _show_port_request(tenant_id, network_id, port_id, - format, detail=True) - - -def new_port_request(tenant_id, network_id, port_state, - format='xml', custom_req_body=None): - method = 'POST' - path = ("/tenants/%(tenant_id)s/networks/" - "%(network_id)s/ports.%(format)s") % locals() - data = (custom_req_body or port_state and - {'port': {'state': '%s' % port_state}}) - content_type = "application/%s" % format - body = data and Serializer().serialize(data, content_type) - return create_request(path, body, content_type, method) - - -def port_delete_request(tenant_id, network_id, port_id, format='xml'): - method = 'DELETE' - path = ("/tenants/%(tenant_id)s/networks/" - "%(network_id)s/ports/%(port_id)s.%(format)s") % locals() - content_type = "application/%s" % format - return create_request(path, None, content_type, method) - - -def update_port_request(tenant_id, network_id, port_id, port_state, - format='xml', custom_req_body=None): - method = 'PUT' - path = ("/tenants/%(tenant_id)s/networks" - "/%(network_id)s/ports/%(port_id)s.%(format)s") % locals() - data = custom_req_body or {'port': {'state': '%s' % port_state}} - content_type = "application/%s" % format - body = Serializer().serialize(data, content_type) - return create_request(path, body, content_type, method) - - -def get_attachment_request(tenant_id, network_id, port_id, format='xml'): - method = 'GET' - path = ("/tenants/%(tenant_id)s/networks/" - "%(network_id)s/ports/%(port_id)s/" - "attachment.%(format)s") % locals() - content_type = "application/%s" % format - return create_request(path, None, content_type, method) - - -def put_attachment_request(tenant_id, network_id, port_id, - attachment_id, format='xml'): - method = 'PUT' - path = ("/tenants/%(tenant_id)s/networks/" - "%(network_id)s/ports/%(port_id)s/" - "attachment.%(format)s") % locals() - data = {'attachment': {'id': attachment_id}} - content_type = "application/%s" % format - body = Serializer().serialize(data, content_type) - return create_request(path, body, content_type, method) - - -def delete_attachment_request(tenant_id, network_id, port_id, - attachment_id, format='xml'): - method = 'DELETE' - path = ("/tenants/%(tenant_id)s/networks/" - "%(network_id)s/ports/%(port_id)s/" - "attachment.%(format)s") % locals() - content_type = "application/%s" % format - return create_request(path, None, content_type, method)