Merge "Remove v1 code from quantum-server"
This commit is contained in:
commit
ab9d1b8c7a
@ -1,20 +1,8 @@
|
|||||||
[composite:quantum]
|
[composite:quantum]
|
||||||
use = egg:Paste#urlmap
|
use = egg:Paste#urlmap
|
||||||
/: quantumversions
|
/: quantumversions
|
||||||
/v1.0: quantumapi_v1_0
|
|
||||||
/v1.1: quantumapi_v1_1
|
|
||||||
/v2.0: quantumapi_v2_0
|
/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]
|
[composite:quantumapi_v2_0]
|
||||||
use = call:quantum.auth:pipeline_factory
|
use = call:quantum.auth:pipeline_factory
|
||||||
noauth = extensions quantumapiapp_v2_0
|
noauth = extensions quantumapiapp_v2_0
|
||||||
@ -38,11 +26,5 @@ paste.filter_factory = quantum.extensions.extensions:plugin_aware_extension_midd
|
|||||||
[app:quantumversions]
|
[app:quantumversions]
|
||||||
paste.app_factory = quantum.api.versions:Versions.factory
|
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]
|
[app:quantumapiapp_v2_0]
|
||||||
paste.app_factory = quantum.api.v2.router:APIRouter.factory
|
paste.app_factory = quantum.api.v2.router:APIRouter.factory
|
||||||
|
@ -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'
|
|
@ -26,143 +26,6 @@ from quantum import wsgi
|
|||||||
LOG = logging.getLogger(__name__)
|
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):
|
class QuantumController(object):
|
||||||
""" Base controller class for Quantum API """
|
""" Base controller class for Quantum API """
|
||||||
# _resource_name will be redefined in sub concrete controller
|
# _resource_name will be redefined in sub concrete controller
|
||||||
|
@ -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"
|
|
@ -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)
|
|
@ -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"
|
|
@ -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"
|
|
@ -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={})
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -25,10 +25,8 @@ from sqlalchemy import create_engine
|
|||||||
from sqlalchemy.exc import DisconnectionError
|
from sqlalchemy.exc import DisconnectionError
|
||||||
from sqlalchemy.orm import sessionmaker, exc
|
from sqlalchemy.orm import sessionmaker, exc
|
||||||
|
|
||||||
from quantum.api.api_common import OperationalStatus
|
|
||||||
from quantum.common import exceptions as q_exc
|
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__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -138,197 +136,3 @@ def unregister_models(base=BASE):
|
|||||||
global _ENGINE
|
global _ENGINE
|
||||||
assert _ENGINE
|
assert _ENGINE
|
||||||
base.metadata.drop_all(_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)
|
|
||||||
|
@ -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 "<Port(%s,%s,%s,%s,%s)>" % (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 "<Network(%s,%s,%s,%s)>" % (self.uuid, self.name,
|
|
||||||
self.op_status, self.tenant_id)
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
||||||
<network_uuid, network_name> 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)
|
|
@ -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],
|
|
||||||
}
|
|
@ -27,13 +27,12 @@ from quantum.db import models_v2
|
|||||||
from quantum.openstack.common import cfg
|
from quantum.openstack.common import cfg
|
||||||
from quantum.plugins.linuxbridge.common import config
|
from quantum.plugins.linuxbridge.common import config
|
||||||
from quantum.plugins.linuxbridge.common import exceptions as c_exc
|
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
|
from quantum.plugins.linuxbridge.db import l2network_models_v2
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
# The global variable for the database version model
|
# The global variable for the database version model
|
||||||
L2_MODEL = l2network_models
|
L2_MODEL = l2network_models_v2
|
||||||
|
|
||||||
|
|
||||||
def initialize(base=None):
|
def initialize(base=None):
|
||||||
@ -44,7 +43,6 @@ def initialize(base=None):
|
|||||||
cfg.CONF.DATABASE.reconnect_interval})
|
cfg.CONF.DATABASE.reconnect_interval})
|
||||||
if base:
|
if base:
|
||||||
options.update({"base": base})
|
options.update({"base": base})
|
||||||
L2_MODEL = l2network_models_v2
|
|
||||||
db.configure_db(options)
|
db.configure_db(options)
|
||||||
create_vlanids()
|
create_vlanids()
|
||||||
|
|
||||||
@ -182,7 +180,7 @@ def reserve_specific_vlanid(vlan_id):
|
|||||||
raise q_exc.InvalidInput(error_message=msg)
|
raise q_exc.InvalidInput(error_message=msg)
|
||||||
session = db.get_session()
|
session = db.get_session()
|
||||||
try:
|
try:
|
||||||
rvlanid = (session.query(l2network_models.VlanID).
|
rvlanid = (session.query(l2network_models_v2.VlanID).
|
||||||
filter_by(vlan_id=vlan_id).
|
filter_by(vlan_id=vlan_id).
|
||||||
one())
|
one())
|
||||||
if rvlanid["vlan_used"]:
|
if rvlanid["vlan_used"]:
|
||||||
@ -191,7 +189,7 @@ def reserve_specific_vlanid(vlan_id):
|
|||||||
rvlanid["vlan_used"] = True
|
rvlanid["vlan_used"] = True
|
||||||
session.merge(rvlanid)
|
session.merge(rvlanid)
|
||||||
except exc.NoResultFound:
|
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)
|
LOG.debug("reserving non-dynamic vlanid %s" % vlan_id)
|
||||||
rvlanid["vlan_used"] = True
|
rvlanid["vlan_used"] = True
|
||||||
session.add(rvlanid)
|
session.add(rvlanid)
|
||||||
|
@ -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 "<VlanID(%d,%s)>" % (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 "<VlanBinding(%d,%s)>" % (self.vlan_id, self.network_id)
|
|
@ -35,7 +35,6 @@ sys.path.append(os.getcwd())
|
|||||||
sys.path.append(os.path.dirname(__file__))
|
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.common.test_lib import run_tests, test_config
|
||||||
import quantum.tests.unit
|
import quantum.tests.unit
|
||||||
|
|
||||||
@ -47,10 +46,7 @@ if __name__ == '__main__':
|
|||||||
# we should only invoked the tests once
|
# we should only invoked the tests once
|
||||||
invoke_once = len(sys.argv) > 1
|
invoke_once = len(sys.argv) > 1
|
||||||
|
|
||||||
test_config['plugin_name'] = "LinuxBridgePlugin.LinuxBridgePlugin"
|
|
||||||
test_config['plugin_name_v2'] = "lb_quantum_plugin.LinuxBridgePluginV2"
|
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()
|
cwd = os.getcwd()
|
||||||
c = config.Config(stream=sys.stdout,
|
c = config.Config(stream=sys.stdout,
|
||||||
|
@ -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)
|
|
@ -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()
|
|
@ -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 "<VlanBinding(%s,%s)>" % (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 "<TunnelIP(%s)>" % (self.ip_address)
|
|
@ -23,7 +23,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from quantum.api.api_common import OperationalStatus
|
|
||||||
from quantum.api.v2 import attributes
|
from quantum.api.v2 import attributes
|
||||||
from quantum.common import exceptions as q_exc
|
from quantum.common import exceptions as q_exc
|
||||||
from quantum.common.utils import find_config_file
|
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.db import models_v2
|
||||||
from quantum.openstack.common import cfg
|
from quantum.openstack.common import cfg
|
||||||
from quantum.plugins.openvswitch.common import config
|
from quantum.plugins.openvswitch.common import config
|
||||||
from quantum.plugins.openvswitch import ovs_db
|
|
||||||
from quantum.plugins.openvswitch import ovs_db_v2
|
from quantum.plugins.openvswitch import ovs_db_v2
|
||||||
from quantum.quantum_plugin_base import QuantumPluginBase
|
|
||||||
from quantum import policy
|
from quantum import policy
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger("ovs_quantum_plugin")
|
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):
|
class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||||
"""Implement the Quantum abstractions using Open vSwitch.
|
"""Implement the Quantum abstractions using Open vSwitch.
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ from nose import core
|
|||||||
sys.path.append(os.getcwd())
|
sys.path.append(os.getcwd())
|
||||||
sys.path.append(os.path.dirname(__file__))
|
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.common.test_lib import run_tests, test_config
|
||||||
import quantum.tests.unit
|
import quantum.tests.unit
|
||||||
|
|
||||||
@ -46,10 +45,7 @@ if __name__ == '__main__':
|
|||||||
# we should only invoked the tests once
|
# we should only invoked the tests once
|
||||||
invoke_once = len(sys.argv) > 1
|
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['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()
|
cwd = os.getcwd()
|
||||||
c = config.Config(stream=sys.stdout,
|
c = config.Config(stream=sys.stdout,
|
||||||
|
@ -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
|
|
@ -130,13 +130,12 @@ def check_ofp_mode(db):
|
|||||||
|
|
||||||
|
|
||||||
class OVSQuantumOFPRyuAgent:
|
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
|
self.root_helper = root_helper
|
||||||
(ofp_controller_addr, ofp_rest_api_addr) = check_ofp_mode(db)
|
(ofp_controller_addr, ofp_rest_api_addr) = check_ofp_mode(db)
|
||||||
|
|
||||||
self.nw_id_external = rest_nw_id.NW_ID_EXTERNAL
|
self.nw_id_external = rest_nw_id.NW_ID_EXTERNAL
|
||||||
self.api = OFPClient(ofp_rest_api_addr)
|
self.api = OFPClient(ofp_rest_api_addr)
|
||||||
self.target_v2_api = target_v2_api
|
|
||||||
self._setup_integration_br(integ_br, ofp_controller_addr)
|
self._setup_integration_br(integ_br, ofp_controller_addr)
|
||||||
|
|
||||||
def _setup_integration_br(self, 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):
|
def _all_bindings(self, db):
|
||||||
"""return interface id -> port which include network id bindings"""
|
"""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())
|
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())
|
|
||||||
|
|
||||||
def _set_port_status(self, port, status):
|
def _set_port_status(self, port, status):
|
||||||
if self.target_v2_api:
|
|
||||||
port.status = status
|
port.status = status
|
||||||
else:
|
|
||||||
port.op_status = status
|
|
||||||
|
|
||||||
def daemon_loop(self, db):
|
def daemon_loop(self, db):
|
||||||
# on startup, register all existing ports
|
# on startup, register all existing ports
|
||||||
@ -232,13 +225,12 @@ def main():
|
|||||||
|
|
||||||
integ_br = cfg.CONF.OVS.integration_bridge
|
integ_br = cfg.CONF.OVS.integration_bridge
|
||||||
root_helper = cfg.CONF.AGENT.root_helper
|
root_helper = cfg.CONF.AGENT.root_helper
|
||||||
target_v2_api = cfg.CONF.AGENT.target_v2_api
|
|
||||||
options = {"sql_connection": cfg.CONF.DATABASE.sql_connection}
|
options = {"sql_connection": cfg.CONF.DATABASE.sql_connection}
|
||||||
db = SqlSoup(options["sql_connection"])
|
db = SqlSoup(options["sql_connection"])
|
||||||
|
|
||||||
LOG.info("Connecting to database \"%s\" on %s",
|
LOG.info("Connecting to database \"%s\" on %s",
|
||||||
db.engine.url.database, db.engine.url.host)
|
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)
|
plugin.daemon_loop(db)
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
@ -30,7 +30,6 @@ ovs_opts = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
agent_opts = [
|
agent_opts = [
|
||||||
cfg.BoolOpt('target_v2_api', default=True),
|
|
||||||
cfg.IntOpt('polling_interval', default=2),
|
cfg.IntOpt('polling_interval', default=2),
|
||||||
cfg.StrOpt('root_helper', default='sudo'),
|
cfg.StrOpt('root_helper', default='sudo'),
|
||||||
]
|
]
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
# Copyright 2012 Isaku Yamahata <yamahata at private email ne jp>
|
|
||||||
# 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()
|
|
@ -1,37 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
# Copyright 2012 Isaku Yamahata <yamahata at private email ne jp>
|
|
||||||
# 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 ip address>:<port>
|
|
||||||
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 "<OFPServer(%s,%s,%s)>" % (self.id, self.address,
|
|
||||||
self.host_type)
|
|
@ -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
|
|
@ -34,7 +34,6 @@ from nose import core
|
|||||||
sys.path.append(os.getcwd())
|
sys.path.append(os.getcwd())
|
||||||
sys.path.append(os.path.dirname(__file__))
|
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.common.test_lib import run_tests, test_config
|
||||||
from quantum.plugins.ryu.tests.unit.utils import patch_fake_ryu_client
|
from quantum.plugins.ryu.tests.unit.utils import patch_fake_ryu_client
|
||||||
import quantum.tests.unit
|
import quantum.tests.unit
|
||||||
@ -47,10 +46,7 @@ if __name__ == '__main__':
|
|||||||
# we should only invoked the tests once
|
# we should only invoked the tests once
|
||||||
invoke_once = len(sys.argv) > 1
|
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['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()
|
cwd = os.getcwd()
|
||||||
# patch modules for ryu.app.client and ryu.app.rest_nw_id
|
# patch modules for ryu.app.client and ryu.app.rest_nw_id
|
||||||
|
@ -26,52 +26,13 @@ from quantum.db import api as db
|
|||||||
from quantum.db import db_base_plugin_v2
|
from quantum.db import db_base_plugin_v2
|
||||||
from quantum.db import models_v2
|
from quantum.db import models_v2
|
||||||
from quantum.openstack.common import cfg
|
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.db import api_v2 as db_api_v2
|
||||||
from quantum.plugins.ryu import ofp_service_type
|
from quantum.plugins.ryu import ofp_service_type
|
||||||
from quantum.plugins.ryu import ovs_quantum_plugin_base
|
|
||||||
from quantum.plugins.ryu.common import config
|
from quantum.plugins.ryu.common import config
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
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):
|
class RyuQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||||
def __init__(self, configfile=None):
|
def __init__(self, configfile=None):
|
||||||
options = {"sql_connection": cfg.CONF.DATABASE.sql_connection}
|
options = {"sql_connection": cfg.CONF.DATABASE.sql_connection}
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
#
|
|
||||||
# Copyright 2012 Isaku Yamahata <yamahata at private email ne jp>
|
|
||||||
# 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()
|
|
@ -1,37 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
# Copyright 2012 Isaku Yamahata <yamahata at private email ne jp>
|
|
||||||
# <yamahata at valinux co jp>
|
|
||||||
# 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)
|
|
@ -1,17 +0,0 @@
|
|||||||
# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
|
|
||||||
# Copyright (C) 2011 Isaku Yamahata <yamahata at valinux co jp>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
NW_ID_EXTERNAL = '__NW_ID_EXTERNAL__'
|
|
||||||
NW_ID_UNKNOWN = '__NW_ID_UNKNOWN__'
|
|
@ -1,46 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
#
|
|
||||||
# Copyright 2012 Isaku Yamahata <yamahata at private email ne jp>
|
|
||||||
# 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
|
|
@ -15,46 +15,14 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import os
|
import unittest2
|
||||||
|
|
||||||
import mox
|
|
||||||
|
|
||||||
from quantum.openstack.common import cfg
|
from quantum.openstack.common import cfg
|
||||||
from quantum.plugins.ryu.tests.unit.basetest import BaseRyuTest
|
from quantum.plugins.ryu.common import config
|
||||||
from quantum.plugins.ryu.tests.unit import fake_plugin
|
|
||||||
from quantum.plugins.ryu.tests.unit import utils
|
|
||||||
|
|
||||||
|
|
||||||
class PluginBaseTest(BaseRyuTest):
|
class ConfigurationTest(unittest2.TestCase):
|
||||||
"""Class conisting of OVSQuantumPluginBase unit tests"""
|
"""Configuration file 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()
|
|
||||||
|
|
||||||
def test_defaults(self):
|
def test_defaults(self):
|
||||||
self.assertEqual('br-int', cfg.CONF.OVS.integration_bridge)
|
self.assertEqual('br-int', cfg.CONF.OVS.integration_bridge)
|
||||||
self.assertEqual('sqlite://', cfg.CONF.DATABASE.sql_connection)
|
self.assertEqual('sqlite://', cfg.CONF.DATABASE.sql_connection)
|
52
quantum/plugins/ryu/tests/unit/test_ryu_db.py
Normal file
52
quantum/plugins/ryu/tests/unit/test_ryu_db.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2012 Isaku Yamahata <yamahata at private email ne jp>
|
||||||
|
# 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)
|
@ -1,75 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
#
|
|
||||||
# Copyright 2012 Isaku Yamahata <yamahata at private email ne jp>
|
|
||||||
# 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)
|
|
@ -15,60 +15,18 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import ConfigParser
|
|
||||||
import imp
|
|
||||||
import os
|
|
||||||
from StringIO import StringIO
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import mock
|
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():
|
def patch_fake_ryu_client():
|
||||||
ryu_mod = imp.new_module('ryu')
|
ryu_mod = mock.Mock()
|
||||||
ryu_app_mod = imp.new_module('ryu.app')
|
ryu_app_mod = ryu_mod.app
|
||||||
ryu_mod.app = ryu_app_mod
|
ryu_app_client = ryu_app_mod.client
|
||||||
ryu_app_mod.client = fake_ryu_client
|
rest_nw_id = ryu_app_mod.rest_nw_id
|
||||||
ryu_app_mod.rest_nw_id = fake_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',
|
return mock.patch.dict('sys.modules',
|
||||||
{'ryu': ryu_mod,
|
{'ryu': ryu_mod,
|
||||||
'ryu.app': ryu_app_mod,
|
'ryu.app': ryu_app_mod,
|
||||||
'ryu.app.client': fake_ryu_client,
|
'ryu.app.client': ryu_app_client,
|
||||||
'ryu.app.rest_nw_id': fake_rest_nw_id})
|
'ryu.app.rest_nw_id': rest_nw_id})
|
||||||
|
|
||||||
|
|
||||||
class Net(object):
|
|
||||||
def __init__(self, uuid):
|
|
||||||
self.uuid = uuid
|
|
||||||
|
@ -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
|
|
||||||
<network_uuid, network_name> 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
|
|
||||||
<network_uuid, network_name> 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)
|
|
@ -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)
|
|
@ -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
|
|
||||||
<network_uuid, network_name> 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
|
|
File diff suppressed because it is too large
Load Diff
@ -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)
|
|
@ -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)
|
|
@ -19,10 +19,10 @@ import os
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import routes
|
import routes
|
||||||
|
import webob
|
||||||
from webtest import AppError
|
from webtest import AppError
|
||||||
from webtest import TestApp
|
from webtest import TestApp
|
||||||
|
|
||||||
from quantum.api import faults
|
|
||||||
from quantum.common import config
|
from quantum.common import config
|
||||||
from quantum.common import exceptions
|
from quantum.common import exceptions
|
||||||
from quantum.extensions import extensions
|
from quantum.extensions import extensions
|
||||||
@ -32,7 +32,7 @@ from quantum.extensions.extensions import (
|
|||||||
PluginAwareExtensionManager,
|
PluginAwareExtensionManager,
|
||||||
)
|
)
|
||||||
from quantum.openstack.common import jsonutils
|
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 import BaseTest
|
||||||
from quantum.tests.unit.extension_stubs import (
|
from quantum.tests.unit.extension_stubs import (
|
||||||
ExtensionExpectingPluginInterface,
|
ExtensionExpectingPluginInterface,
|
||||||
@ -65,6 +65,15 @@ class ExtensionsTestApp(wsgi.Router):
|
|||||||
super(ExtensionsTestApp, self).__init__(mapper)
|
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 ResourceExtensionTest(unittest.TestCase):
|
||||||
|
|
||||||
class ResourceExtensionController(wsgi.Controller):
|
class ResourceExtensionController(wsgi.Controller):
|
||||||
@ -76,8 +85,7 @@ class ResourceExtensionTest(unittest.TestCase):
|
|||||||
return {'data': {'id': id}}
|
return {'data': {'id': id}}
|
||||||
|
|
||||||
def notimplemented_function(self, request, id):
|
def notimplemented_function(self, request, id):
|
||||||
return faults.Quantum10HTTPError(
|
return webob.exc.HTTPClientError(NotImplementedError())
|
||||||
exceptions.NotImplementedError("notimplemented_function"))
|
|
||||||
|
|
||||||
def custom_member_action(self, request, id):
|
def custom_member_action(self, request, id):
|
||||||
return {'member_action': 'value'}
|
return {'member_action': 'value'}
|
||||||
@ -473,8 +481,9 @@ def setup_base_app():
|
|||||||
|
|
||||||
def setup_extensions_middleware(extension_manager=None):
|
def setup_extensions_middleware(extension_manager=None):
|
||||||
extension_manager = (extension_manager or
|
extension_manager = (extension_manager or
|
||||||
PluginAwareExtensionManager(extensions_path,
|
PluginAwareExtensionManager(
|
||||||
QuantumEchoPlugin()))
|
extensions_path,
|
||||||
|
FakePluginWithExtension()))
|
||||||
config_file = 'quantum.conf.test'
|
config_file = 'quantum.conf.test'
|
||||||
args = ['--config-file', etcdir(config_file)]
|
args = ['--config-file', etcdir(config_file)]
|
||||||
config.parse(args=args)
|
config.parse(args=args)
|
||||||
|
@ -28,170 +28,3 @@ def create_request(path, body, content_type, method='GET', query_string=None):
|
|||||||
req.headers['Accept'] = content_type
|
req.headers['Accept'] = content_type
|
||||||
req.body = body
|
req.body = body
|
||||||
return req
|
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)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user