Add routed-service-insertion
Extend L3 router to add "service_type_id" attribute, and extend LB vip, pool, and health_monitor to add "router_id" attribute. Implements: API and DB model for blueprint routed-service-insertion Change-Id: I12f053e0b0e6cf0813ea88dd568aae49b09b8215
This commit is contained in:
parent
4298be01bf
commit
0f60b08483
88
quantum/db/routedserviceinsertion_db.py
Normal file
88
quantum/db/routedserviceinsertion_db.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2013 VMware, 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: Kaiwei Fan, VMware, Inc
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy import event
|
||||||
|
from sqlalchemy.orm import exc
|
||||||
|
|
||||||
|
from quantum.common import exceptions as qexception
|
||||||
|
from quantum.db import model_base
|
||||||
|
from quantum.extensions import routedserviceinsertion as rsi
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceRouterBinding(model_base.BASEV2):
|
||||||
|
resource_id = sa.Column(sa.String(36),
|
||||||
|
primary_key=True)
|
||||||
|
resource_type = sa.Column(sa.String(36),
|
||||||
|
primary_key=True)
|
||||||
|
router_id = sa.Column(sa.String(36),
|
||||||
|
sa.ForeignKey('routers.id'),
|
||||||
|
nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class AttributeException(qexception.QuantumException):
|
||||||
|
message = _("Resource type '%(resource_type)s' is longer "
|
||||||
|
"than %(maxlen)d characters")
|
||||||
|
|
||||||
|
|
||||||
|
@event.listens_for(ServiceRouterBinding.resource_type, 'set', retval=True)
|
||||||
|
def validate_resource_type(target, value, oldvalue, initiator):
|
||||||
|
"""Make sure the resource type fit the resource_type column."""
|
||||||
|
maxlen = ServiceRouterBinding.resource_type.property.columns[0].type.length
|
||||||
|
if len(value) > maxlen:
|
||||||
|
raise AttributeException(resource_type=value, maxlen=maxlen)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class RoutedServiceInsertionDbMixin(object):
|
||||||
|
"""Mixin class to add router service insertion."""
|
||||||
|
|
||||||
|
def _process_create_resource_router_id(self, context, resource, model):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
db = ServiceRouterBinding(
|
||||||
|
resource_id=resource['id'],
|
||||||
|
resource_type=model.__tablename__,
|
||||||
|
router_id=resource[rsi.ROUTER_ID])
|
||||||
|
context.session.add(db)
|
||||||
|
return self._make_resource_router_id_dict(db, model)
|
||||||
|
|
||||||
|
def _extend_resource_router_id_dict(self, context, resource, model):
|
||||||
|
binding = self._get_resource_router_id_binding(
|
||||||
|
context, resource['resource_id'], model)
|
||||||
|
resource[rsi.ROUTER_ID] = binding['router_id']
|
||||||
|
|
||||||
|
def _get_resource_router_id_binding(self, context, resource_id, model):
|
||||||
|
query = self._model_query(context, ServiceRouterBinding)
|
||||||
|
query = query.filter(
|
||||||
|
ServiceRouterBinding.resource_id == resource_id,
|
||||||
|
ServiceRouterBinding.resource_type == model.__tablename__)
|
||||||
|
return query.first()
|
||||||
|
|
||||||
|
def _make_resource_router_id_dict(self, resource_router_binding, model,
|
||||||
|
fields=None):
|
||||||
|
resource = {'resource_id': resource_router_binding['resource_id'],
|
||||||
|
'resource_type': model.__tablename__,
|
||||||
|
rsi.ROUTER_ID: resource_router_binding[rsi.ROUTER_ID]}
|
||||||
|
return self._fields(resource, fields)
|
||||||
|
|
||||||
|
def _delete_resource_router_id_binding(self, context, resource_id, model):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
binding = self._get_resource_router_id_binding(
|
||||||
|
context, resource_id, model)
|
||||||
|
if binding:
|
||||||
|
context.session.delete(binding)
|
61
quantum/db/routerservicetype_db.py
Normal file
61
quantum/db/routerservicetype_db.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2013 VMware, 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: Kaiwei Fan, VMware, Inc
|
||||||
|
|
||||||
|
from sqlalchemy.orm import exc
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from quantum.db import model_base
|
||||||
|
from quantum.extensions import routerservicetype as rst
|
||||||
|
|
||||||
|
|
||||||
|
class RouterServiceTypeBinding(model_base.BASEV2):
|
||||||
|
router_id = sa.Column(sa.String(36),
|
||||||
|
sa.ForeignKey('routers.id', ondelete="CASCADE"),
|
||||||
|
primary_key=True)
|
||||||
|
service_type_id = sa.Column(sa.String(36),
|
||||||
|
sa.ForeignKey('servicetypes.id'),
|
||||||
|
nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class RouterServiceTypeDbMixin(object):
|
||||||
|
"""Mixin class to add router service type."""
|
||||||
|
|
||||||
|
def _process_create_router_service_type_id(self, context, router):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
db = RouterServiceTypeBinding(
|
||||||
|
router_id=router['id'],
|
||||||
|
service_type_id=router[rst.SERVICE_TYPE_ID])
|
||||||
|
context.session.add(db)
|
||||||
|
return self._make_router_service_type_id_dict(db)
|
||||||
|
|
||||||
|
def _extend_router_service_type_id_dict(self, context, router):
|
||||||
|
rsbind = self._get_router_service_type_id_binding(
|
||||||
|
context, router['id'])
|
||||||
|
if rsbind:
|
||||||
|
router[rst.SERVICE_TYPE_ID] = rsbind['service_type_id']
|
||||||
|
|
||||||
|
def _get_router_service_type_id_binding(self, context, router_id):
|
||||||
|
query = self._model_query(context, RouterServiceTypeBinding)
|
||||||
|
query = query.filter(
|
||||||
|
RouterServiceTypeBinding.router_id == router_id)
|
||||||
|
return query.first()
|
||||||
|
|
||||||
|
def _make_router_service_type_id_dict(self, router_service_type):
|
||||||
|
res = {'router_id': router_service_type['router_id'],
|
||||||
|
'service_type_id': router_service_type[rst.SERVICE_TYPE_ID]}
|
||||||
|
return self._fields(res, None)
|
@ -214,9 +214,14 @@ class L3(extensions.ExtensionDescriptor):
|
|||||||
|
|
||||||
return exts
|
return exts
|
||||||
|
|
||||||
|
def update_attributes_map(self, attributes):
|
||||||
|
super(L3, self).update_attributes_map(
|
||||||
|
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
|
||||||
|
|
||||||
def get_extended_resources(self, version):
|
def get_extended_resources(self, version):
|
||||||
if version == "2.0":
|
if version == "2.0":
|
||||||
return EXTENDED_ATTRIBUTES_2_0
|
return dict(EXTENDED_ATTRIBUTES_2_0.items() +
|
||||||
|
RESOURCE_ATTRIBUTE_MAP.items())
|
||||||
else:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -318,6 +318,16 @@ class Loadbalancer(extensions.ExtensionDescriptor):
|
|||||||
def get_plugin_interface(cls):
|
def get_plugin_interface(cls):
|
||||||
return LoadBalancerPluginBase
|
return LoadBalancerPluginBase
|
||||||
|
|
||||||
|
def update_attributes_map(self, attributes):
|
||||||
|
super(Loadbalancer, self).update_attributes_map(
|
||||||
|
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
|
||||||
|
|
||||||
|
def get_extended_resources(self, version):
|
||||||
|
if version == "2.0":
|
||||||
|
return RESOURCE_ATTRIBUTE_MAP
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class LoadBalancerPluginBase(ServicePluginBase):
|
class LoadBalancerPluginBase(ServicePluginBase):
|
||||||
__metaclass__ = abc.ABCMeta
|
__metaclass__ = abc.ABCMeta
|
||||||
|
67
quantum/extensions/routedserviceinsertion.py
Normal file
67
quantum/extensions/routedserviceinsertion.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2013 VMware, 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: Kaiwei Fan, VMware, Inc
|
||||||
|
|
||||||
|
|
||||||
|
ROUTER_ID = 'router_id'
|
||||||
|
EXTENDED_ATTRIBUTES_2_0 = {
|
||||||
|
'vips': {
|
||||||
|
ROUTER_ID: {'allow_post': True, 'allow_put': False,
|
||||||
|
'validate': {'type:uuid_or_none': None},
|
||||||
|
'default': None, 'is_visible': True},
|
||||||
|
},
|
||||||
|
'pools': {
|
||||||
|
ROUTER_ID: {'allow_post': True, 'allow_put': False,
|
||||||
|
'validate': {'type:uuid_or_none': None},
|
||||||
|
'default': None, 'is_visible': True},
|
||||||
|
},
|
||||||
|
'health_monitors': {
|
||||||
|
ROUTER_ID: {'allow_post': True, 'allow_put': False,
|
||||||
|
'validate': {'type:uuid_or_none': None},
|
||||||
|
'default': None, 'is_visible': True},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Routedserviceinsertion(object):
|
||||||
|
"""Extension class supporting routed service type."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_name(cls):
|
||||||
|
return "Routed Service Insertion"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_alias(cls):
|
||||||
|
return "routed-service-insertion"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_description(cls):
|
||||||
|
return "Provides routed service type"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_namespace(cls):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_updated(cls):
|
||||||
|
return "2013-01-29T00:00:00-00:00"
|
||||||
|
|
||||||
|
def get_extended_resources(self, version):
|
||||||
|
if version == "2.0":
|
||||||
|
return EXTENDED_ATTRIBUTES_2_0
|
||||||
|
else:
|
||||||
|
return {}
|
57
quantum/extensions/routerservicetype.py
Normal file
57
quantum/extensions/routerservicetype.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
#
|
||||||
|
# Copyright 2013 VMware, 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: Kaiwei Fan, VMware, Inc
|
||||||
|
|
||||||
|
|
||||||
|
SERVICE_TYPE_ID = 'service_type_id'
|
||||||
|
EXTENDED_ATTRIBUTES_2_0 = {
|
||||||
|
'routers': {
|
||||||
|
SERVICE_TYPE_ID: {'allow_post': True, 'allow_put': False,
|
||||||
|
'validate': {'type:uuid_or_none': None},
|
||||||
|
'default': None, 'is_visible': True},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Routerservicetype(object):
|
||||||
|
"""Extension class supporting router service type."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_name(cls):
|
||||||
|
return "Router Service Type"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_alias(cls):
|
||||||
|
return "router-service-type"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_description(cls):
|
||||||
|
return "Provides router service type"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_namespace(cls):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_updated(cls):
|
||||||
|
return "2013-01-29T00:00:00-00:00"
|
||||||
|
|
||||||
|
def get_extended_resources(self, version):
|
||||||
|
if version == "2.0":
|
||||||
|
return EXTENDED_ATTRIBUTES_2_0
|
||||||
|
else:
|
||||||
|
return {}
|
435
quantum/tests/unit/test_routerserviceinsertion.py
Normal file
435
quantum/tests/unit/test_routerserviceinsertion.py
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2013 VMware, 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 unittest2 as unittest
|
||||||
|
import webob.exc as webexc
|
||||||
|
|
||||||
|
import quantum
|
||||||
|
from quantum.api import extensions
|
||||||
|
from quantum.api.v2 import router
|
||||||
|
from quantum.common import config
|
||||||
|
from quantum.db.loadbalancer import loadbalancer_db as lb_db
|
||||||
|
from quantum.db import db_base_plugin_v2
|
||||||
|
from quantum.db import l3_db
|
||||||
|
from quantum.db import routedserviceinsertion_db as rsi_db
|
||||||
|
from quantum.db import routerservicetype_db as rst_db
|
||||||
|
from quantum.db import servicetype_db as st_db
|
||||||
|
from quantum.extensions import routedserviceinsertion as rsi
|
||||||
|
from quantum.extensions import routerservicetype as rst
|
||||||
|
from quantum.openstack.common import cfg
|
||||||
|
from quantum.plugins.common import constants
|
||||||
|
from quantum.tests.unit import test_api_v2
|
||||||
|
from quantum.tests.unit import testlib_api
|
||||||
|
from quantum import wsgi
|
||||||
|
|
||||||
|
_uuid = test_api_v2._uuid
|
||||||
|
_get_path = test_api_v2._get_path
|
||||||
|
extensions_path = ':'.join(quantum.extensions.__path__)
|
||||||
|
|
||||||
|
|
||||||
|
class RouterServiceInsertionTestPlugin(
|
||||||
|
rst_db.RouterServiceTypeDbMixin,
|
||||||
|
rsi_db.RoutedServiceInsertionDbMixin,
|
||||||
|
st_db.ServiceTypeManager,
|
||||||
|
lb_db.LoadBalancerPluginDb,
|
||||||
|
l3_db.L3_NAT_db_mixin,
|
||||||
|
db_base_plugin_v2.QuantumDbPluginV2):
|
||||||
|
|
||||||
|
supported_extension_aliases = [
|
||||||
|
"router", "router-service-type", "routed-service-insertion",
|
||||||
|
"service-type", "lbaas"
|
||||||
|
]
|
||||||
|
|
||||||
|
def create_router(self, context, router):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
r = super(RouterServiceInsertionTestPlugin, self).create_router(
|
||||||
|
context, router)
|
||||||
|
service_type_id = router['router'].get(rst.SERVICE_TYPE_ID)
|
||||||
|
if service_type_id is not None:
|
||||||
|
r[rst.SERVICE_TYPE_ID] = service_type_id
|
||||||
|
self._process_create_router_service_type_id(
|
||||||
|
context, r)
|
||||||
|
return r
|
||||||
|
|
||||||
|
def get_router(self, context, id, fields=None):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
r = super(RouterServiceInsertionTestPlugin, self).get_router(
|
||||||
|
context, id, fields)
|
||||||
|
rsbind = self._get_router_service_type_id_binding(context, id)
|
||||||
|
if rsbind:
|
||||||
|
r[rst.SERVICE_TYPE_ID] = rsbind['service_type_id']
|
||||||
|
return r
|
||||||
|
|
||||||
|
def delete_router(self, context, id):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
super(RouterServiceInsertionTestPlugin, self).delete_router(
|
||||||
|
context, id)
|
||||||
|
rsbind = self._get_router_service_type_id_binding(context, id)
|
||||||
|
if rsbind:
|
||||||
|
raise Exception('Router service-type binding is not deleted')
|
||||||
|
|
||||||
|
def create_resource(self, res, context, resource, model):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
method_name = "create_{0}".format(res)
|
||||||
|
method = getattr(super(RouterServiceInsertionTestPlugin, self),
|
||||||
|
method_name)
|
||||||
|
o = method(context, resource)
|
||||||
|
router_id = resource[res].get(rsi.ROUTER_ID)
|
||||||
|
if router_id is not None:
|
||||||
|
o[rsi.ROUTER_ID] = router_id
|
||||||
|
self._process_create_resource_router_id(
|
||||||
|
context, o, model)
|
||||||
|
return o
|
||||||
|
|
||||||
|
def get_resource(self, res, context, id, fields, model):
|
||||||
|
method_name = "get_{0}".format(res)
|
||||||
|
method = getattr(super(RouterServiceInsertionTestPlugin, self),
|
||||||
|
method_name)
|
||||||
|
o = method(context, id, fields)
|
||||||
|
if fields is None or rsi.ROUTER_ID in fields:
|
||||||
|
rsbind = self._get_resource_router_id_binding(
|
||||||
|
context, id, model)
|
||||||
|
if rsbind:
|
||||||
|
o[rsi.ROUTER_ID] = rsbind['router_id']
|
||||||
|
return o
|
||||||
|
|
||||||
|
def delete_resource(self, res, context, id, model):
|
||||||
|
method_name = "delete_{0}".format(res)
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
method = getattr(super(RouterServiceInsertionTestPlugin, self),
|
||||||
|
method_name)
|
||||||
|
method(context, id)
|
||||||
|
self._delete_resource_router_id_binding(context, id, model)
|
||||||
|
if self._get_resource_router_id_binding(context, id, model):
|
||||||
|
raise Exception("{0}-router binding is not deleted".format(res))
|
||||||
|
|
||||||
|
def create_pool(self, context, pool):
|
||||||
|
return self.create_resource('pool', context, pool, lb_db.Pool)
|
||||||
|
|
||||||
|
def get_pool(self, context, id, fields=None):
|
||||||
|
return self.get_resource('pool', context, id, fields, lb_db.Pool)
|
||||||
|
|
||||||
|
def delete_pool(self, context, id):
|
||||||
|
return self.delete_resource('pool', context, id, lb_db.Pool)
|
||||||
|
|
||||||
|
def create_health_monitor(self, context, health_monitor):
|
||||||
|
return self.create_resource('health_monitor', context, health_monitor,
|
||||||
|
lb_db.HealthMonitor)
|
||||||
|
|
||||||
|
def get_health_monitor(self, context, id, fields=None):
|
||||||
|
return self.get_resource('health_monitor', context, id, fields,
|
||||||
|
lb_db.HealthMonitor)
|
||||||
|
|
||||||
|
def delete_health_monitor(self, context, id):
|
||||||
|
return self.delete_resource('health_monitor', context, id,
|
||||||
|
lb_db.HealthMonitor)
|
||||||
|
|
||||||
|
def create_vip(self, context, vip):
|
||||||
|
return self.create_resource('vip', context, vip, lb_db.Vip)
|
||||||
|
|
||||||
|
def get_vip(self, context, id, fields=None):
|
||||||
|
return self.get_resource(
|
||||||
|
'vip', context, id, fields, lb_db.Vip)
|
||||||
|
|
||||||
|
def delete_vip(self, context, id):
|
||||||
|
return self.delete_resource('vip', context, id, lb_db.Vip)
|
||||||
|
|
||||||
|
def stats(self, context, pool_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RouterServiceInsertionTestCase(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
plugin = (
|
||||||
|
"quantum.tests.unit.test_routerserviceinsertion."
|
||||||
|
"RouterServiceInsertionTestPlugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
# point config file to: quantum/tests/etc/quantum.conf.test
|
||||||
|
args = ['--config-file', test_api_v2.etcdir('quantum.conf.test')]
|
||||||
|
config.parse(args=args)
|
||||||
|
|
||||||
|
#just stubbing core plugin with LoadBalancer plugin
|
||||||
|
cfg.CONF.set_override('core_plugin', plugin)
|
||||||
|
cfg.CONF.set_override('service_plugins', [plugin])
|
||||||
|
cfg.CONF.set_override('quota_router', -1, group='QUOTAS')
|
||||||
|
|
||||||
|
# Ensure 'stale' patched copies of the plugin are never returned
|
||||||
|
quantum.manager.QuantumManager._instance = None
|
||||||
|
# Ensure existing ExtensionManager is not used
|
||||||
|
|
||||||
|
ext_mgr = extensions.PluginAwareExtensionManager(
|
||||||
|
extensions_path,
|
||||||
|
{constants.LOADBALANCER: RouterServiceInsertionTestPlugin()}
|
||||||
|
)
|
||||||
|
extensions.PluginAwareExtensionManager._instance = ext_mgr
|
||||||
|
router.APIRouter()
|
||||||
|
|
||||||
|
app = config.load_paste_app('extensions_test_app')
|
||||||
|
self._api = extensions.ExtensionMiddleware(app, ext_mgr=ext_mgr)
|
||||||
|
|
||||||
|
self._tenant_id = "8c70909f-b081-452d-872b-df48e6c355d1"
|
||||||
|
|
||||||
|
res = self._do_request('GET', _get_path('service-types'))
|
||||||
|
self._service_type_id = res['service_types'][0]['id']
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self._api = None
|
||||||
|
cfg.CONF.reset()
|
||||||
|
|
||||||
|
def _do_request(self, method, path, data=None, params=None, action=None):
|
||||||
|
content_type = 'application/json'
|
||||||
|
body = None
|
||||||
|
if data is not None: # empty dict is valid
|
||||||
|
body = wsgi.Serializer().serialize(data, content_type)
|
||||||
|
|
||||||
|
req = testlib_api.create_request(
|
||||||
|
path, body, content_type,
|
||||||
|
method, query_string=params)
|
||||||
|
res = req.get_response(self._api)
|
||||||
|
if res.status_code >= 400:
|
||||||
|
raise webexc.HTTPClientError(detail=res.body, code=res.status_code)
|
||||||
|
if res.status_code != webexc.HTTPNoContent.code:
|
||||||
|
return res.json
|
||||||
|
|
||||||
|
def _router_create(self, service_type_id=None):
|
||||||
|
data = {
|
||||||
|
"router": {
|
||||||
|
"tenant_id": self._tenant_id,
|
||||||
|
"name": "test",
|
||||||
|
"admin_state_up": True,
|
||||||
|
"service_type_id": service_type_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = self._do_request('POST', _get_path('routers'), data)
|
||||||
|
return res['router']
|
||||||
|
|
||||||
|
def test_router_create_no_service_type_id(self):
|
||||||
|
router = self._router_create()
|
||||||
|
self.assertEqual(router.get('service_type_id'), None)
|
||||||
|
|
||||||
|
def test_router_create_with_service_type_id(self):
|
||||||
|
router = self._router_create(self._service_type_id)
|
||||||
|
self.assertEqual(router['service_type_id'], self._service_type_id)
|
||||||
|
|
||||||
|
def test_router_get(self):
|
||||||
|
router = self._router_create(self._service_type_id)
|
||||||
|
res = self._do_request('GET',
|
||||||
|
_get_path('routers/{0}'.format(router['id'])))
|
||||||
|
self.assertEqual(res['router']['service_type_id'],
|
||||||
|
self._service_type_id)
|
||||||
|
|
||||||
|
def _test_router_update(self, update_service_type_id):
|
||||||
|
router = self._router_create(self._service_type_id)
|
||||||
|
router_id = router['id']
|
||||||
|
new_name = _uuid()
|
||||||
|
data = {
|
||||||
|
"router": {
|
||||||
|
"name": new_name,
|
||||||
|
"admin_state_up": router['admin_state_up'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if update_service_type_id:
|
||||||
|
data["router"]["service_type_id"] = _uuid()
|
||||||
|
with self.assertRaises(webexc.HTTPClientError) as ctx_manager:
|
||||||
|
res = self._do_request(
|
||||||
|
'PUT', _get_path('routers/{0}'.format(router_id)), data)
|
||||||
|
self.assertEqual(ctx_manager.exception.code, 400)
|
||||||
|
else:
|
||||||
|
res = self._do_request(
|
||||||
|
'PUT', _get_path('routers/{0}'.format(router_id)), data)
|
||||||
|
res = self._do_request(
|
||||||
|
'GET', _get_path('routers/{0}'.format(router['id'])))
|
||||||
|
self.assertEqual(res['router']['name'], new_name)
|
||||||
|
|
||||||
|
def test_router_update_with_service_type_id(self):
|
||||||
|
self._test_router_update(True)
|
||||||
|
|
||||||
|
def test_router_update_without_service_type_id(self):
|
||||||
|
self._test_router_update(False)
|
||||||
|
|
||||||
|
def test_router_delete(self):
|
||||||
|
router = self._router_create(self._service_type_id)
|
||||||
|
self._do_request(
|
||||||
|
'DELETE', _get_path('routers/{0}'.format(router['id'])))
|
||||||
|
|
||||||
|
def _test_lb_setup(self):
|
||||||
|
self._subnet_id = _uuid()
|
||||||
|
router = self._router_create(self._service_type_id)
|
||||||
|
self._router_id = router['id']
|
||||||
|
|
||||||
|
def _test_pool_setup(self):
|
||||||
|
self._test_lb_setup()
|
||||||
|
|
||||||
|
def _test_health_monitor_setup(self):
|
||||||
|
self._test_lb_setup()
|
||||||
|
|
||||||
|
def _test_vip_setup(self):
|
||||||
|
self._test_pool_setup()
|
||||||
|
pool = self._pool_create(self._router_id)
|
||||||
|
self._pool_id = pool['id']
|
||||||
|
|
||||||
|
def _create_resource(self, res, data):
|
||||||
|
resp = self._do_request('POST', _get_path('lb/{0}s'.format(res)), data)
|
||||||
|
return resp[res]
|
||||||
|
|
||||||
|
def _pool_create(self, router_id=None):
|
||||||
|
data = {
|
||||||
|
"pool": {
|
||||||
|
"tenant_id": self._tenant_id,
|
||||||
|
"name": "test",
|
||||||
|
"protocol": "HTTP",
|
||||||
|
"subnet_id": self._subnet_id,
|
||||||
|
"lb_method": "ROUND_ROBIN",
|
||||||
|
"router_id": router_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self._create_resource('pool', data)
|
||||||
|
|
||||||
|
def _pool_update_attrs(self, pool):
|
||||||
|
uattr = {}
|
||||||
|
fields = [
|
||||||
|
'name', 'description', 'lb_method',
|
||||||
|
'health_monitors', 'admin_state_up'
|
||||||
|
]
|
||||||
|
for field in fields:
|
||||||
|
uattr[field] = pool[field]
|
||||||
|
return uattr
|
||||||
|
|
||||||
|
def _health_monitor_create(self, router_id=None):
|
||||||
|
data = {
|
||||||
|
"health_monitor": {
|
||||||
|
"tenant_id": self._tenant_id,
|
||||||
|
"type": "HTTP",
|
||||||
|
"delay": 1,
|
||||||
|
"timeout": 1,
|
||||||
|
"max_retries": 1,
|
||||||
|
"router_id": router_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self._create_resource('health_monitor', data)
|
||||||
|
|
||||||
|
def _health_monitor_update_attrs(self, hm):
|
||||||
|
uattr = {}
|
||||||
|
fields = ['delay', 'timeout', 'max_retries']
|
||||||
|
for field in fields:
|
||||||
|
uattr[field] = hm[field]
|
||||||
|
return uattr
|
||||||
|
|
||||||
|
def _vip_create(self, router_id=None):
|
||||||
|
data = {
|
||||||
|
"vip": {
|
||||||
|
"tenant_id": self._tenant_id,
|
||||||
|
"name": "test",
|
||||||
|
"protocol": "HTTP",
|
||||||
|
"port": 80,
|
||||||
|
"subnet_id": self._subnet_id,
|
||||||
|
"pool_id": self._pool_id,
|
||||||
|
"address": "192.168.1.101",
|
||||||
|
"connection_limit": 100,
|
||||||
|
"admin_state_up": True,
|
||||||
|
"router_id": router_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self._create_resource('vip', data)
|
||||||
|
|
||||||
|
def _vip_update_attrs(self, vip):
|
||||||
|
uattr = {}
|
||||||
|
fields = [
|
||||||
|
'name', 'description', 'pool_id', 'connection_limit',
|
||||||
|
'admin_state_up'
|
||||||
|
]
|
||||||
|
for field in fields:
|
||||||
|
uattr[field] = vip[field]
|
||||||
|
return uattr
|
||||||
|
|
||||||
|
def _test_resource_create(self, res):
|
||||||
|
getattr(self, "_test_{0}_setup".format(res))()
|
||||||
|
obj = getattr(self, "_{0}_create".format(res))()
|
||||||
|
obj = getattr(self, "_{0}_create".format(res))(self._router_id)
|
||||||
|
self.assertEqual(obj['router_id'], self._router_id)
|
||||||
|
|
||||||
|
def _test_resource_update(self, res, update_router_id,
|
||||||
|
update_attr, update_value):
|
||||||
|
getattr(self, "_test_{0}_setup".format(res))()
|
||||||
|
obj = getattr(self, "_{0}_create".format(res))(self._router_id)
|
||||||
|
uattrs = getattr(self, "_{0}_update_attrs".format(res))(obj)
|
||||||
|
uattrs[update_attr] = update_value
|
||||||
|
data = {res: uattrs}
|
||||||
|
if update_router_id:
|
||||||
|
uattrs['router_id'] = self._router_id
|
||||||
|
with self.assertRaises(webexc.HTTPClientError) as ctx_manager:
|
||||||
|
newobj = self._do_request(
|
||||||
|
'PUT',
|
||||||
|
_get_path('lb/{0}s/{1}'.format(res, obj['id'])), data)
|
||||||
|
self.assertEqual(ctx_manager.exception.code, 400)
|
||||||
|
else:
|
||||||
|
newobj = self._do_request(
|
||||||
|
'PUT',
|
||||||
|
_get_path('lb/{0}s/{1}'.format(res, obj['id'])), data)
|
||||||
|
updated = self._do_request(
|
||||||
|
'GET',
|
||||||
|
_get_path('lb/{0}s/{1}'.format(res, obj['id'])))
|
||||||
|
self.assertEqual(updated[res][update_attr], update_value)
|
||||||
|
|
||||||
|
def _test_resource_delete(self, res):
|
||||||
|
getattr(self, "_test_{0}_setup".format(res))()
|
||||||
|
obj = getattr(self, "_{0}_create".format(res))()
|
||||||
|
self._do_request(
|
||||||
|
'DELETE', _get_path('lb/{0}s/{1}'.format(res, obj['id'])))
|
||||||
|
obj = getattr(self, "_{0}_create".format(res))(self._router_id)
|
||||||
|
self._do_request(
|
||||||
|
'DELETE', _get_path('lb/{0}s/{1}'.format(res, obj['id'])))
|
||||||
|
|
||||||
|
def test_pool_create(self):
|
||||||
|
self._test_resource_create('pool')
|
||||||
|
|
||||||
|
def test_pool_update_with_router_id(self):
|
||||||
|
self._test_resource_update('pool', True, 'name', _uuid())
|
||||||
|
|
||||||
|
def test_pool_update_without_router_id(self):
|
||||||
|
self._test_resource_update('pool', False, 'name', _uuid())
|
||||||
|
|
||||||
|
def test_pool_delete(self):
|
||||||
|
self._test_resource_delete('pool')
|
||||||
|
|
||||||
|
def test_health_monitor_create(self):
|
||||||
|
self._test_resource_create('health_monitor')
|
||||||
|
|
||||||
|
def test_health_monitor_update_with_router_id(self):
|
||||||
|
self._test_resource_update('health_monitor', True, 'timeout', 2)
|
||||||
|
|
||||||
|
def test_health_monitor_update_without_router_id(self):
|
||||||
|
self._test_resource_update('health_monitor', False, 'timeout', 2)
|
||||||
|
|
||||||
|
def test_health_monitor_delete(self):
|
||||||
|
self._test_resource_delete('health_monitor')
|
||||||
|
|
||||||
|
def test_vip_create(self):
|
||||||
|
self._test_resource_create('vip')
|
||||||
|
|
||||||
|
def test_vip_update_with_router_id(self):
|
||||||
|
self._test_resource_update('vip', True, 'name', _uuid())
|
||||||
|
|
||||||
|
def test_vip_update_without_router_id(self):
|
||||||
|
self._test_resource_update('vip', False, 'name', _uuid())
|
||||||
|
|
||||||
|
def test_vip_delete(self):
|
||||||
|
self._test_resource_delete('vip')
|
Loading…
Reference in New Issue
Block a user