Merge "[L3][QoS] Neutron server side router gateway IP QoS"

This commit is contained in:
Zuul 2018-12-04 00:26:55 +00:00 committed by Gerrit Code Review
commit 9b357c11e1
19 changed files with 578 additions and 16 deletions

View File

@ -119,3 +119,13 @@ class ProcessExecutionError(RuntimeError):
def __init__(self, message, returncode):
super(ProcessExecutionError, self).__init__(message)
self.returncode = returncode
class RouterQosBindingNotFound(exceptions.NotFound):
message = _("QoS binding for router %(router_id)s gateway and policy "
"%(policy_id)s could not be found.")
class RouterQosBindingError(exceptions.NeutronException):
message = _("QoS binding for router %(router_id)s gateway and policy "
"%(policy_id)s could not be created: %(db_error)s.")

View File

@ -0,0 +1,129 @@
# Copyright 2018 OpenStack Foundation
# Copyright 2017 Letv Cloud Computing
# 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 neutron_lib.api.definitions import l3 as l3_apidef
from neutron_lib.api.definitions import qos_gateway_ip
from neutron_lib.api import extensions
from neutron_lib.services.qos import constants as qos_consts
from oslo_log import log as logging
from neutron.db import _resource_extend as resource_extend
from neutron.db import l3_db
from neutron.db import l3_gwmode_db
from neutron.objects.qos import policy as policy_object
LOG = logging.getLogger(__name__)
@resource_extend.has_resource_extenders
class L3_gw_ip_qos_dbonly_mixin(l3_gwmode_db.L3_NAT_dbonly_mixin):
"""Mixin class to add router gateway IP's QoS extra attributes."""
_gw_ip_qos = None
@staticmethod
@resource_extend.extends([l3_apidef.ROUTERS])
def _extend_router_dict_gw_qos(router_res, router_db):
if router_db.gw_port_id and router_db.get('qos_policy_binding'):
policy_id = router_db.qos_policy_binding.policy_id
router_res[l3_apidef.EXTERNAL_GW_INFO].update(
{qos_consts.QOS_POLICY_ID: policy_id})
@property
def _is_gw_ip_qos_supported(self):
if self._gw_ip_qos is None:
# Check L3 service plugin
self._gw_ip_qos = extensions.is_extension_supported(
self, qos_gateway_ip.ALIAS)
return self._gw_ip_qos
def _create_gw_ip_qos_db(self, context, router_id, policy_id):
policy = policy_object.QosPolicy.get_policy_obj(context, policy_id)
policy.attach_router(router_id)
def _delete_gw_ip_qos_db(self, context, router_id, policy_id):
policy = policy_object.QosPolicy.get_policy_obj(context, policy_id)
policy.detach_router(router_id)
def _update_router_gw_info(self, context, router_id, info, router=None):
# Calls superclass, pass router db object for avoiding re-loading
router = super(L3_gw_ip_qos_dbonly_mixin,
self)._update_router_gw_info(
context, router_id, info, router)
if self._is_gw_ip_qos_supported and router.gw_port:
self._update_router_gw_qos_policy(context, router_id,
info, router)
return router
def _get_router_gateway_policy_binding(self, context, router_id):
router = self._get_router(context, router_id)
return router.qos_policy_binding
def _update_router_gw_qos_policy(self, context, router_id, info, router):
if not info or qos_consts.QOS_POLICY_ID not in info:
# An explicit 'None' for `qos_polcy_id` indicates to clear
# the router gateway IP policy. So if info does not have
# the key `qos_polcy_id`, we can not decide what behavior
# to be done, then directly return here.
return
new_qos_policy_id = info[qos_consts.QOS_POLICY_ID]
if router.qos_policy_binding:
old_qos_policy_id = router.qos_policy_binding.policy_id
if old_qos_policy_id == new_qos_policy_id:
return
if old_qos_policy_id:
self._delete_gw_ip_qos_db(context,
router_id,
old_qos_policy_id)
with context.session.begin(subtransactions=True):
context.session.refresh(router)
if new_qos_policy_id:
self._create_gw_ip_qos_db(
context, router_id, new_qos_policy_id)
def _build_routers_list(self, context, routers, gw_ports):
routers = super(L3_gw_ip_qos_dbonly_mixin,
self)._build_routers_list(
context, routers, gw_ports)
if not self._is_gw_ip_qos_supported:
return routers
for rtr in routers:
gw_port_id = rtr['gw_port_id']
# Collect gw ports only if available
if gw_port_id and gw_ports.get(gw_port_id):
rtr['gw_port'] = gw_ports[gw_port_id]
router_gateway_policy_binding = (
self._get_router_gateway_policy_binding(
context, rtr['id']))
qos_policy_id = None
if router_gateway_policy_binding:
qos_policy_id = router_gateway_policy_binding.policy_id
rtr['gw_port'][qos_consts.QOS_POLICY_ID] = qos_policy_id
return routers
class L3_gw_ip_qos_db_mixin(L3_gw_ip_qos_dbonly_mixin,
l3_db.L3_NAT_db_mixin):
pass

View File

@ -45,7 +45,7 @@ class L3_NAT_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin):
def _extend_router_dict_gw_mode(router_res, router_db):
if router_db.gw_port_id:
nw_id = router_db.gw_port['network_id']
router_res[l3_apidef.EXTERNAL_GW_INFO] = {
router_res[l3_apidef.EXTERNAL_GW_INFO].update({
'network_id': nw_id,
'enable_snat': router_db.enable_snat,
'external_fixed_ips': [
@ -53,7 +53,7 @@ class L3_NAT_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin):
'ip_address': ip["ip_address"]}
for ip in router_db.gw_port['fixed_ips']
]
}
})
def _update_router_gw_info(self, context, router_id, info, router=None):
# Load the router only if necessary

View File

@ -1 +1 @@
cada2437bf41
195176fb410d

View File

@ -0,0 +1,45 @@
# Copyright 2018 OpenStack Foundation
# Copyright 2017 Letv Cloud Computing
# 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.
#
"""router gateway IP QoS
Revision ID: 195176fb410d
Revises: cada2437bf41
Create Date: 2016-04-28 12:38:09.872706
"""
from alembic import op
import sqlalchemy as sa
from neutron_lib.db import constants as db_const
# revision identifiers, used by Alembic.
revision = '195176fb410d'
down_revision = 'cada2437bf41'
def upgrade():
op.create_table(
'qos_router_gw_policy_bindings',
sa.Column('policy_id',
sa.String(length=db_const.UUID_FIELD_SIZE),
sa.ForeignKey('qos_policies.id', ondelete='CASCADE'),
nullable=False, primary_key=True),
sa.Column('router_id',
sa.String(length=db_const.UUID_FIELD_SIZE),
sa.ForeignKey('routers.id', ondelete='CASCADE'),
nullable=False, unique=True, primary_key=True))

View File

@ -76,6 +76,26 @@ class QosFIPPolicyBinding(model_base.BASEV2):
cascade='delete', lazy='joined'))
class QosRouterGatewayIPPolicyBinding(model_base.BASEV2):
__tablename__ = 'qos_router_gw_policy_bindings'
policy_id = sa.Column(sa.String(db_const.UUID_FIELD_SIZE),
sa.ForeignKey('qos_policies.id',
ondelete='CASCADE'),
nullable=False,
primary_key=True)
router_id = sa.Column(sa.String(db_const.UUID_FIELD_SIZE),
sa.ForeignKey('routers.id',
ondelete='CASCADE'),
nullable=False,
unique=True,
primary_key=True)
revises_on_change = ('router', )
router = sa.orm.relationship(
l3.Router, load_on_pending=True,
backref=sa.orm.backref("qos_policy_binding", uselist=False,
cascade='delete', lazy='joined'))
class QosPortPolicyBinding(model_base.BASEV2):
__tablename__ = 'qos_port_policy_bindings'
policy_id = sa.Column(sa.String(36),

View File

@ -0,0 +1,24 @@
# Copyright 2018 OpenStack Foundation
# Copyright 2017 Letv Cloud Computing
# 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 neutron_lib.api.definitions import qos_gateway_ip as apidef
from neutron_lib.api import extensions
class Qos_gateway_ip(extensions.APIExtensionDescriptor):
"""Extension class supporting gateway IP rate limit in all router."""
api_definition = apidef

View File

@ -65,3 +65,19 @@ class QosPolicyFloatingIPBinding(base.NeutronDbObject):
primary_keys = ['policy_id', 'fip_id']
fields_no_update = ['policy_id', 'fip_id']
@base.NeutronObjectRegistry.register
class QosPolicyRouterGatewayIPBinding(base.NeutronDbObject):
# Version 1.0: Initial version
VERSION = '1.0'
db_model = qos_db_model.QosRouterGatewayIPPolicyBinding
fields = {
'policy_id': common_types.UUIDField(),
'router_id': common_types.UUIDField()
}
primary_keys = ['policy_id', 'router_id']
fields_no_update = ['policy_id', 'router_id']

View File

@ -58,7 +58,8 @@ class QosPolicy(rbac_db.NeutronRbacObject):
# Version 1.5: Direction for bandwidth limit rule added
# Version 1.6: Added "is_default" field
# Version 1.7: Added floating IP bindings
VERSION = '1.7'
# Version 1.8: Added router gateway QoS policy bindings
VERSION = '1.8'
# required by RbacNeutronMetaclass
rbac_db_cls = QosPolicyRBAC
@ -81,7 +82,8 @@ class QosPolicy(rbac_db.NeutronRbacObject):
binding_models = {'port': binding.QosPolicyPortBinding,
'network': binding.QosPolicyNetworkBinding,
'fip': binding.QosPolicyFloatingIPBinding}
'fip': binding.QosPolicyFloatingIPBinding,
'router': binding.QosPolicyRouterGatewayIPBinding}
def obj_load_attr(self, attrname):
if attrname == 'rules':
@ -201,6 +203,12 @@ class QosPolicy(rbac_db.NeutronRbacObject):
return cls._get_object_policy(
context, binding.QosPolicyFloatingIPBinding, fip_id=fip_id)
@classmethod
def get_router_policy(cls, context, router_id):
return cls._get_object_policy(
context, binding.QosPolicyRouterGatewayIPBinding,
router_id=router_id)
# TODO(QoS): Consider extending base to trigger registered methods for us
def create(self):
with self.db_context_writer(self.obj_context):
@ -265,6 +273,16 @@ class QosPolicy(rbac_db.NeutronRbacObject):
fip_id=fip_id,
db_error=e)
def attach_router(self, router_id):
router_binding_obj = binding.QosPolicyRouterGatewayIPBinding(
self.obj_context, policy_id=self.id, router_id=router_id)
try:
router_binding_obj.create()
except db_exc.DBReferenceError as e:
raise exceptions.RouterQosBindingError(policy_id=self.id,
router_id=router_id,
db_error=e)
def detach_network(self, network_id):
deleted = binding.QosPolicyNetworkBinding.delete_objects(
self.obj_context, network_id=network_id)
@ -286,6 +304,13 @@ class QosPolicy(rbac_db.NeutronRbacObject):
raise exceptions.FloatingIPQosBindingNotFound(fip_id=fip_id,
policy_id=self.id)
def detach_router(self, router_id):
deleted = binding.QosPolicyRouterGatewayIPBinding.delete_objects(
self.obj_context, router_id=router_id)
if not deleted:
raise exceptions.RouterQosBindingNotFound(router_id=router_id,
policy_id=self.id)
def set_default(self):
if not self.get_default():
qos_default_policy = QosPolicyDefault(self.obj_context,
@ -329,6 +354,13 @@ class QosPolicy(rbac_db.NeutronRbacObject):
self.obj_context, policy_id=self.id)
]
def get_bound_routers(self):
return [
rb.router_id
for rb in binding.QosPolicyRouterGatewayIPBinding.get_objects(
self.obj_context, policy_id=self.id)
]
@classmethod
def _get_bound_tenant_ids(cls, session, binding_db, bound_db,
binding_db_id_column, policy_id):
@ -349,6 +381,8 @@ class QosPolicy(rbac_db.NeutronRbacObject):
qosport = qos_db_model.QosPortPolicyBinding
fip = l3.FloatingIP
qosfip = qos_db_model.QosFIPPolicyBinding
router = l3.Router
qosrouter = qos_db_model.QosRouterGatewayIPPolicyBinding
bound_tenants = []
with cls.db_context_reader(context):
bound_tenants.extend(cls._get_bound_tenant_ids(
@ -359,6 +393,9 @@ class QosPolicy(rbac_db.NeutronRbacObject):
bound_tenants.extend(
cls._get_bound_tenant_ids(context.session, qosfip, fip,
qosfip.fip_id, policy_id))
bound_tenants.extend(
cls._get_bound_tenant_ids(context.session, qosrouter, router,
qosrouter.router_id, policy_id))
return set(bound_tenants)
def obj_make_compatible(self, primitive, target_version):

View File

@ -35,7 +35,7 @@ from neutron.db import l3_dvrscheduler_db
from neutron.db import l3_fip_pools_db
from neutron.db import l3_fip_port_details
from neutron.db import l3_fip_qos
from neutron.db import l3_gwmode_db
from neutron.db import l3_gateway_ip_qos
from neutron.db import l3_hamode_db
from neutron.db import l3_hascheduler_db
from neutron.db.models import l3 as l3_models
@ -54,11 +54,11 @@ def disable_dvr_extension_by_config(aliases):
aliases.remove('dvr')
def disable_qos_fip_extension_by_plugins(aliases):
def disable_l3_qos_extension_by_plugins(ext, aliases):
qos_class = 'neutron.services.qos.qos_plugin.QoSPlugin'
if all(p not in cfg.CONF.service_plugins for p in ['qos', qos_class]):
if 'qos-fip' in aliases:
aliases.remove('qos-fip')
if ext in aliases:
aliases.remove(ext)
@resource_extend.has_resource_extenders
@ -66,7 +66,7 @@ class L3RouterPlugin(service_base.ServicePluginBase,
common_db_mixin.CommonDbMixin,
extraroute_db.ExtraRoute_db_mixin,
l3_hamode_db.L3_HA_NAT_db_mixin,
l3_gwmode_db.L3_NAT_db_mixin,
l3_gateway_ip_qos.L3_gw_ip_qos_db_mixin,
l3_dvr_ha_scheduler_db.L3_DVR_HA_scheduler_db_mixin,
dns_db.DNSDbMixin,
l3_fip_qos.FloatingQoSDbMixin,
@ -86,7 +86,8 @@ class L3RouterPlugin(service_base.ServicePluginBase,
"extraroute", "l3_agent_scheduler",
"l3-ha", "router_availability_zone",
"l3-flavors", "qos-fip",
"fip-port-details", "floatingip-pools"]
"fip-port-details", "floatingip-pools",
"qos-gateway-ip"]
__native_pagination_support = True
__native_sorting_support = True
@ -116,7 +117,8 @@ class L3RouterPlugin(service_base.ServicePluginBase,
if not hasattr(self, '_aliases'):
aliases = self._supported_extension_aliases[:]
disable_dvr_extension_by_config(aliases)
disable_qos_fip_extension_by_plugins(aliases)
disable_l3_qos_extension_by_plugins('qos-fip', aliases)
disable_l3_qos_extension_by_plugins('qos-gateway-ip', aliases)
self._aliases = aliases
return self._aliases

View File

@ -105,6 +105,8 @@ def prepare_router_data(ip_version=lib_constants.IP_VERSION_4,
'subnets': subnets,
'extra_subnets': extra_subnets}
external_gateway_info = {"qos_policy_id": kwargs.get('qos_policy_id')}
routes = []
if extra_routes:
routes = [{'destination': '8.8.8.0/24', 'nexthop': '19.4.4.4'}]
@ -114,7 +116,8 @@ def prepare_router_data(ip_version=lib_constants.IP_VERSION_4,
'distributed': False,
lib_constants.INTERFACE_KEY: [],
'routes': routes,
'gw_port': ex_gw_port}
'gw_port': ex_gw_port,
'external_gateway_info': external_gateway_info}
router_fips = router.get(lib_constants.FLOATINGIP_KEY, [])
if enable_floating_ip:

View File

@ -38,6 +38,7 @@ NETWORK_API_EXTENSIONS+=",project-id"
NETWORK_API_EXTENSIONS+=",provider"
NETWORK_API_EXTENSIONS+=",qos"
NETWORK_API_EXTENSIONS+=",qos-fip"
NETWORK_API_EXTENSIONS+=",qos-gateway-ip"
NETWORK_API_EXTENSIONS+=",quotas"
NETWORK_API_EXTENSIONS+=",quota_details"
NETWORK_API_EXTENSIONS+=",rbac-policies"

View File

@ -395,13 +395,17 @@ class L3NatTestCaseMixin(object):
def _add_external_gateway_to_router(self, router_id, network_id,
expected_code=exc.HTTPOk.code,
neutron_context=None, ext_ips=None):
neutron_context=None, ext_ips=None,
**kwargs):
ext_ips = ext_ips or []
body = {'router':
{'external_gateway_info': {'network_id': network_id}}}
if ext_ips:
body['router']['external_gateway_info'][
'external_fixed_ips'] = ext_ips
if 'policy_id' in kwargs:
body['router']['external_gateway_info'][
'qos_policy_id'] = kwargs.get('policy_id')
return self._update('routers', router_id, body,
expected_code=expected_code,
neutron_context=neutron_context)

View File

@ -0,0 +1,233 @@
# Copyright 2018 OpenStack Foundation
# Copyright 2017 Letv Cloud Computing
# 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 neutron_lib.api.definitions import l3 as l3_apidef
from neutron_lib.api.definitions import qos_gateway_ip
from neutron_lib import context
from neutron_lib.services.qos import constants as qos_consts
from oslo_config import cfg
from oslo_utils import uuidutils
from neutron.conf.db import extraroute_db
from neutron.db import l3_gateway_ip_qos
from neutron.extensions import l3
from neutron.objects.qos import policy
from neutron.tests.unit.extensions import test_l3
class GatewayIPQoSTestExtensionManager(object):
def get_resources(self):
l3_apidef.RESOURCE_ATTRIBUTE_MAP['routers'].update(
qos_gateway_ip.RESOURCE_ATTRIBUTE_MAP['routers'])
return l3.L3.get_resources()
def get_actions(self):
return []
def get_request_extensions(self):
return []
class TestGatewayIPQoSIntPlugin(
test_l3.TestL3NatIntPlugin,
l3_gateway_ip_qos.L3_gw_ip_qos_db_mixin):
supported_extension_aliases = ["external-net",
"router",
"ext-gw-mode",
qos_gateway_ip.ALIAS]
class TestGatewayIPQoSL3NatServicePlugin(
test_l3.TestL3NatServicePlugin,
l3_gateway_ip_qos.L3_gw_ip_qos_db_mixin):
supported_extension_aliases = ["router",
"ext-gw-mode",
qos_gateway_ip.ALIAS]
class GatewayIPQoSDBTestCaseBase(object):
def test_create_router_gateway_with_qos_policy(self):
ctx = context.get_admin_context()
policy_obj = policy.QosPolicy(ctx,
id=uuidutils.generate_uuid(),
project_id='tenant', name='pol1',
rules=[])
policy_obj.create()
with self.subnet(cidr='11.0.0.0/24') as public_sub,\
self.router() as r:
self._set_net_external(public_sub['subnet']['network_id'])
res = self._add_external_gateway_to_router(
r['router']['id'],
public_sub['subnet']['network_id'],
policy_id=policy_obj.id)
self.assertEqual(
policy_obj.id,
res['router']['external_gateway_info'].get(
qos_consts.QOS_POLICY_ID))
def test_update_router_gateway_with_qos_policy(self):
ctx = context.get_admin_context()
policy_obj = policy.QosPolicy(ctx,
id=uuidutils.generate_uuid(),
project_id='tenant', name='pol1',
rules=[])
policy_obj.create()
with self.subnet(cidr='11.0.0.0/24') as public_sub,\
self.router() as r:
self._set_net_external(public_sub['subnet']['network_id'])
res = self._add_external_gateway_to_router(
r['router']['id'],
public_sub['subnet']['network_id'])
self.assertIsNone(
res['router']['external_gateway_info'].get(
qos_consts.QOS_POLICY_ID))
# update router gateway
res = self._add_external_gateway_to_router(
r['router']['id'],
public_sub['subnet']['network_id'],
policy_id=policy_obj.id)
self.assertEqual(
policy_obj.id,
res['router']['external_gateway_info'].get(
qos_consts.QOS_POLICY_ID))
def test_clear_router_gateway_and_create_with_old_qos_policy_implicitly(
self):
ctx = context.get_admin_context()
policy_obj = policy.QosPolicy(ctx,
id=uuidutils.generate_uuid(),
project_id='tenant', name='pol1',
rules=[])
policy_obj.create()
with self.subnet(cidr='11.0.0.0/24') as public_sub,\
self.router() as r:
self._set_net_external(public_sub['subnet']['network_id'])
res = self._add_external_gateway_to_router(
r['router']['id'],
public_sub['subnet']['network_id'],
policy_id=policy_obj.id)
self.assertEqual(
policy_obj.id,
res['router']['external_gateway_info'].get(
qos_consts.QOS_POLICY_ID))
# Clear router gateway
self._remove_external_gateway_from_router(
r['router']['id'],
public_sub['subnet']['network_id'],
external_gw_info={})
# Create router gateway again, then the qos policy binding will be
# reused here.
res = self._add_external_gateway_to_router(
r['router']['id'],
public_sub['subnet']['network_id'])
self.assertEqual(
policy_obj.id,
res['router']['external_gateway_info'].get(
qos_consts.QOS_POLICY_ID))
def test_clear_router_gateway_qos_policy(self):
ctx = context.get_admin_context()
policy_obj = policy.QosPolicy(ctx,
id=uuidutils.generate_uuid(),
project_id='tenant', name='pol1',
rules=[])
policy_obj.create()
with self.subnet(cidr='11.0.0.0/24') as public_sub,\
self.router() as r:
self._set_net_external(public_sub['subnet']['network_id'])
res = self._add_external_gateway_to_router(
r['router']['id'],
public_sub['subnet']['network_id'])
self.assertIsNone(
res['router']['external_gateway_info'].get(
qos_consts.QOS_POLICY_ID))
# update router gateway
res = self._add_external_gateway_to_router(
r['router']['id'],
public_sub['subnet']['network_id'],
policy_id=policy_obj.id)
self.assertEqual(
policy_obj.id,
res['router']['external_gateway_info'].get(
qos_consts.QOS_POLICY_ID))
# Explicitly clear router gateway qos policy binding
res = self._add_external_gateway_to_router(
r['router']['id'],
public_sub['subnet']['network_id'],
policy_id=None,
is_remove=True)
self.assertIsNone(
res['router']['external_gateway_info'].get(
qos_consts.QOS_POLICY_ID))
class GatewayIPQoSDBIntTestCase(test_l3.L3BaseForIntTests,
test_l3.L3NatTestCaseMixin,
GatewayIPQoSDBTestCaseBase):
def setUp(self, plugin=None):
if not plugin:
plugin = ('neutron.tests.unit.extensions.test_qos_gateway_ip.'
'TestGatewayIPQoSIntPlugin')
service_plugins = {'qos': 'neutron.services.qos.qos_plugin.QoSPlugin'}
extraroute_db.register_db_extraroute_opts()
# for these tests we need to enable overlapping ips
cfg.CONF.set_default('allow_overlapping_ips', True)
cfg.CONF.set_default('max_routes', 3)
ext_mgr = GatewayIPQoSTestExtensionManager()
super(test_l3.L3BaseForIntTests, self).setUp(
plugin=plugin,
ext_mgr=ext_mgr,
service_plugins=service_plugins)
self.setup_notification_driver()
class GatewayIPQoSDBSepTestCase(test_l3.L3BaseForSepTests,
test_l3.L3NatTestCaseMixin,
GatewayIPQoSDBTestCaseBase):
def setUp(self):
# the plugin without L3 support
plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin'
# the L3 service plugin
l3_plugin = ('neutron.tests.unit.extensions.test_qos_gateway_ip.'
'TestGatewayIPQoSL3NatServicePlugin')
service_plugins = {'l3_plugin_name': l3_plugin,
'qos': 'neutron.services.qos.qos_plugin.QoSPlugin'}
extraroute_db.register_db_extraroute_opts()
# for these tests we need to enable overlapping ips
cfg.CONF.set_default('allow_overlapping_ips', True)
cfg.CONF.set_default('max_routes', 3)
ext_mgr = GatewayIPQoSTestExtensionManager()
super(test_l3.L3BaseForSepTests, self).setUp(
plugin=plugin,
ext_mgr=ext_mgr,
service_plugins=service_plugins)
self.setup_notification_driver()

View File

@ -68,3 +68,22 @@ class QosPolicyFloatingIPBindingDbObjectTestCase(
for db_obj in self.db_objs:
self._create_test_qos_policy(id=db_obj['policy_id'])
self._create_test_fip_id(fip_id=db_obj['fip_id'])
class QosPolicyRouterGatewayIPBindingObjectTestCase(
test_base.BaseObjectIfaceTestCase):
_test_class = binding.QosPolicyRouterGatewayIPBinding
class QosPolicyRouterGatewayIPBindingDbObjectTestCase(
test_base.BaseDbObjectTestCase,
testlib_api.SqlTestCase):
_test_class = binding.QosPolicyRouterGatewayIPBinding
def setUp(self):
super(QosPolicyRouterGatewayIPBindingDbObjectTestCase, self).setUp()
for db_obj in self.db_objs:
self._create_test_qos_policy(id=db_obj['policy_id'])
self._create_test_router_id(router_id=db_obj['router_id'])

View File

@ -1542,10 +1542,12 @@ class BaseDbObjectTestCase(_BaseObjectTestCase,
segment.create()
return segment.id
def _create_test_router_id(self):
def _create_test_router_id(self, router_id=None):
attrs = {
'name': 'test_router',
}
if router_id:
attrs['id'] = router_id
self._router = router.Router(self.context, **attrs)
self._router.create()
return self._router['id']

View File

@ -78,9 +78,10 @@ object_data = {
'QosPolicyRBAC': '1.0-c8a67f39809c5a3c8c7f26f2f2c620b2',
'QosRuleType': '1.3-7286188edeb3a0386f9cf7979b9700fc',
'QosRuleTypeDriver': '1.0-7d8cb9f0ef661ac03700eae97118e3db',
'QosPolicy': '1.7-4adb0cde3102c10d8970ec9487fd7fe7',
'QosPolicy': '1.8-4adb0cde3102c10d8970ec9487fd7fe7',
'QosPolicyDefault': '1.0-59e5060eedb1f06dd0935a244d27d11c',
'QosPolicyFloatingIPBinding': '1.0-5625df4205a18778cd6aa40f99be024e',
'QosPolicyRouterGatewayIPBinding': '1.0-da064fbfe5ee18c950b905b483bf59e3',
'QosPolicyNetworkBinding': '1.0-df53a1e0f675aab8d27a1ccfed38dc42',
'QosPolicyPortBinding': '1.0-66cb364ac99aa64523ade07f9f868ea6',
'Quota': '1.0-6bb6a0f1bd5d66a2134ffa1a61873097',

View File

@ -0,0 +1,15 @@
---
features:
- |
A new attribute ``qos_policy_id`` is added to the L3 router
gateway.
* It enables users to associate QoS policies to L3 router gateways
to control the rate of transmission of the associated SNAT traffic.
* At the moment, only bandwidth limit rules are supported in the
QoS polices.
* To enable this feature, the ``qos`` service plugin has to be
configured in the Neutron server and the ``gateway_ip_qos``
extension has to be configured in the L3 agents. Please refer to
the ``QoS`` section of the ``OpenStack Networking Guide`` for more
specific details.

View File

@ -206,6 +206,7 @@ neutron.objects =
QosPolicyNetworkBinding = neutron.objects.qos.binding:QosPolicyNetworkBinding
QosPolicyPortBinding = neutron.objects.qos.binding:QosPolicyPortBinding
QosPolicyRBAC = neutron.objects.qos.policy:QosPolicyRBAC
QosPolicyRouterGatewayIPBinding = neutron.objects.qos.binding:QosPolicyRouterGatewayIPBinding
QosRule = neutron.objects.qos.rule:QosRule
QosRuleType = neutron.objects.qos.rule_type:QosRuleType
QosRuleTypeDriver = neutron.objects.qos.rule_type:QosRuleTypeDriver