Merge "[L3][QoS] Neutron server side router gateway IP QoS"
This commit is contained in:
commit
9b357c11e1
|
@ -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.")
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
cada2437bf41
|
||||
195176fb410d
|
||||
|
|
|
@ -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))
|
|
@ -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),
|
||||
|
|
|
@ -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
|
|
@ -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']
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
|
@ -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'])
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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.
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue