Merge "[QoS] Add rule type packet per second (pps)"

This commit is contained in:
Zuul 2021-08-04 13:30:17 +00:00 committed by Gerrit Code Review
commit 86de15d632
18 changed files with 499 additions and 30 deletions

View File

@ -1 +1 @@
8df53b0d2c0e 1bb3393de75d

View File

@ -0,0 +1,55 @@
# Copyright (c) 2021 China Unicom Cloud Data Co.,Ltd.
# 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.
#
"""add qos policy rule Packet Rate Limit
Revision ID: 1bb3393de75d
Revises: 8df53b0d2c0e
Create Date: 2021-01-22 17:00:03.085196
"""
from alembic import op
import sqlalchemy as sa
from neutron_lib import constants
# revision identifiers, used by Alembic.
revision = '1bb3393de75d'
down_revision = '8df53b0d2c0e'
direction_enum = sa.Enum(
constants.EGRESS_DIRECTION, constants.INGRESS_DIRECTION,
name='qos_packet_rate_limit_rules_directions'
)
def upgrade():
op.create_table(
'qos_packet_rate_limit_rules',
sa.Column('id', sa.String(36), primary_key=True),
sa.Column('qos_policy_id',
sa.String(length=36),
sa.ForeignKey('qos_policies.id', ondelete='CASCADE'),
nullable=False, index=True),
sa.Column('max_kpps', sa.Integer()),
sa.Column('max_burst_kpps', sa.Integer()),
sa.Column('direction', direction_enum,
nullable=False,
server_default=constants.EGRESS_DIRECTION),
sa.UniqueConstraint('qos_policy_id', 'direction',
name='qos_packet_rate_limit_rules0qos_policy_id0direction')
)

View File

@ -192,3 +192,29 @@ class QosMinimumBandwidthRule(model_base.HasId, model_base.BASEV2):
name='qos_minimum_bandwidth_rules0qos_policy_id0direction'), name='qos_minimum_bandwidth_rules0qos_policy_id0direction'),
model_base.BASEV2.__table_args__ model_base.BASEV2.__table_args__
) )
class QosPacketRateLimitRule(model_base.HasId, model_base.BASEV2):
__tablename__ = 'qos_packet_rate_limit_rules'
qos_policy_id = sa.Column(sa.String(36),
sa.ForeignKey('qos_policies.id',
ondelete='CASCADE'),
nullable=False,
index=True)
max_kpps = sa.Column(sa.Integer)
max_burst_kpps = sa.Column(sa.Integer)
revises_on_change = ('qos_policy',)
qos_policy = sa.orm.relationship(QosPolicy, load_on_pending=True)
direction = sa.Column(
sa.Enum(constants.EGRESS_DIRECTION,
constants.INGRESS_DIRECTION,
name="qos_packet_rate_limit_rules_directions"),
default=constants.EGRESS_DIRECTION,
server_default=constants.EGRESS_DIRECTION,
nullable=False)
__table_args__ = (
sa.UniqueConstraint(
qos_policy_id, direction,
name="qos_packet_rate_limit_rules0qos_policy_id0direction"),
model_base.BASEV2.__table_args__
)

View File

@ -84,9 +84,12 @@ class QoSPluginBase(service_base.ServicePluginBase, metaclass=abc.ABCMeta):
path_prefix = apidef.API_PREFIX path_prefix = apidef.API_PREFIX
# The rule object type to use for each incoming rule-related request. # The rule object type to use for each incoming rule-related request.
rule_objects = {'bandwidth_limit': rule_object.QosBandwidthLimitRule, rule_objects = {
'dscp_marking': rule_object.QosDscpMarkingRule, 'bandwidth_limit': rule_object.QosBandwidthLimitRule,
'minimum_bandwidth': rule_object.QosMinimumBandwidthRule} 'dscp_marking': rule_object.QosDscpMarkingRule,
'minimum_bandwidth': rule_object.QosMinimumBandwidthRule,
'packet_rate_limit': rule_object.QosPacketRateLimitRule,
}
# Patterns used to call method proxies for all policy-rule-specific # Patterns used to call method proxies for all policy-rule-specific
# method calls (see __getattr__ docstring, below). # method calls (see __getattr__ docstring, below).

View File

@ -0,0 +1,60 @@
# Copyright (c) 2021 China Unicom Cloud Data Co.,Ltd.
# 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_bw_minimum_ingress
from neutron_lib.api.definitions import qos_pps_rule as apidef
from neutron_lib.api import extensions as api_extensions
from neutron_lib.plugins import constants
from neutron_lib.plugins import directory
from neutron.api import extensions
from neutron.api.v2 import base
COLLECTION_NAME = 'packet_rate_limit_rules'
RESOURCE_NAME = 'packet_rate_limit_rule'
# A quick align for subresource minimum bandwidth ingress direction.
# TODO(liuyulong): Move to neutron-lib
apidef.SUB_RESOURCE_ATTRIBUTE_MAP.update(
qos_bw_minimum_ingress.SUB_RESOURCE_ATTRIBUTE_MAP)
class Qos_pps_rule(api_extensions.APIExtensionDescriptor):
api_definition = apidef
@classmethod
def get_resources(cls):
plugin = directory.get_plugin(constants.QOS)
params = apidef.SUB_RESOURCE_ATTRIBUTE_MAP[
COLLECTION_NAME]['parameters']
parent = apidef.SUB_RESOURCE_ATTRIBUTE_MAP[
COLLECTION_NAME]['parent']
controller = base.create_resource(
COLLECTION_NAME,
RESOURCE_NAME,
plugin,
params,
parent=parent,
allow_pagination=True,
allow_sorting=True)
exts = [
extensions.ResourceExtension(
COLLECTION_NAME,
controller,
parent,
attr_map=params)
]
return exts

View File

@ -66,7 +66,8 @@ class QosPolicy(rbac_db.NeutronRbacObject):
# Version 1.6: Added "is_default" field # Version 1.6: Added "is_default" field
# Version 1.7: Added floating IP bindings # Version 1.7: Added floating IP bindings
# Version 1.8: Added router gateway QoS policy bindings # Version 1.8: Added router gateway QoS policy bindings
VERSION = '1.8' # Version 1.9: Added QosPacketRateLimitRule
VERSION = '1.9'
# required by RbacNeutronMetaclass # required by RbacNeutronMetaclass
rbac_db_cls = QosPolicyRBAC rbac_db_cls = QosPolicyRBAC
@ -376,10 +377,22 @@ class QosPolicy(rbac_db.NeutronRbacObject):
return set(bound_tenants) return set(bound_tenants)
def obj_make_compatible(self, primitive, target_version): def obj_make_compatible(self, primitive, target_version):
def filter_rules(obj_names, rules):
return [rule for rule in rules if
rule['versioned_object.name'] in obj_names]
_target_version = versionutils.convert_version_to_tuple(target_version) _target_version = versionutils.convert_version_to_tuple(target_version)
if _target_version < (1, 8): if _target_version < (1, 8):
raise exception.IncompatibleObjectVersion( raise exception.IncompatibleObjectVersion(
objver=target_version, objname=self.__class__.__name__) objver=target_version, objname=self.__class__.__name__)
names = [
rule_obj_impl.QosBandwidthLimitRule.obj_name(),
rule_obj_impl.QosDscpMarkingRule.obj_name(),
rule_obj_impl.QosMinimumBandwidthRule.obj_name(),
]
if _target_version >= (1, 9):
names.append(rule_obj_impl.QosPacketRateLimitRule.obj_name())
if 'rules' in primitive and names:
primitive['rules'] = filter_rules(names, primitive['rules'])
@base_db.NeutronObjectRegistry.register @base_db.NeutronObjectRegistry.register

View File

@ -26,6 +26,7 @@ from oslo_versionedobjects import fields as obj_fields
from neutron.db.qos import models as qos_db_model from neutron.db.qos import models as qos_db_model
from neutron.objects import base from neutron.objects import base
from neutron.services.qos import constants as qos_constants
DSCP_MARK = 'dscp_mark' DSCP_MARK = 'dscp_mark'
@ -36,7 +37,7 @@ def get_rules(obj_cls, context, qos_policy_id):
return all_rules return all_rules
with obj_cls.db_context_reader(context): with obj_cls.db_context_reader(context):
for rule_type in qos_consts.VALID_RULE_TYPES: for rule_type in qos_constants.VALID_RULE_TYPES:
rule_cls_name = 'Qos%sRule' % helpers.camelize(rule_type) rule_cls_name = 'Qos%sRule' % helpers.camelize(rule_type)
rule_cls = getattr(sys.modules[__name__], rule_cls_name) rule_cls = getattr(sys.modules[__name__], rule_cls_name)
@ -50,11 +51,12 @@ class QosRule(base.NeutronDbObject, metaclass=abc.ABCMeta):
# 1.1: Added DscpMarkingRule # 1.1: Added DscpMarkingRule
# 1.2: Added QosMinimumBandwidthRule # 1.2: Added QosMinimumBandwidthRule
# 1.3: Added direction for BandwidthLimitRule # 1.3: Added direction for BandwidthLimitRule
# 1.4: Added PacketRateLimitRule
# #
# NOTE(mangelajo): versions need to be handled from the top QosRule object # NOTE(mangelajo): versions need to be handled from the top QosRule object
# because it's the only reference QosPolicy can make # because it's the only reference QosPolicy can make
# to them via obj_relationships version map # to them via obj_relationships version map
VERSION = '1.3' VERSION = '1.4'
fields = { fields = {
'id': common_types.UUIDField(), 'id': common_types.UUIDField(),
@ -167,3 +169,20 @@ class QosMinimumBandwidthRule(QosRule):
duplicates_compare_fields = ['direction'] duplicates_compare_fields = ['direction']
rule_type = qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH rule_type = qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH
@base.NeutronObjectRegistry.register
class QosPacketRateLimitRule(QosRule):
db_model = qos_db_model.QosPacketRateLimitRule
fields = {
'max_kpps': obj_fields.IntegerField(nullable=True),
'max_burst_kpps': obj_fields.IntegerField(nullable=True),
'direction': common_types.FlowDirectionEnumField(
default=constants.EGRESS_DIRECTION)
}
duplicates_compare_fields = ['direction']
rule_type = qos_constants.RULE_TYPE_PACKET_RATE_LIMIT

View File

@ -13,17 +13,17 @@
from neutron_lib.objects import common_types from neutron_lib.objects import common_types
from neutron_lib.plugins import constants from neutron_lib.plugins import constants
from neutron_lib.plugins import directory from neutron_lib.plugins import directory
from neutron_lib.services.qos import constants as qos_consts
from oslo_versionedobjects import fields as obj_fields from oslo_versionedobjects import fields as obj_fields
from neutron.objects import base from neutron.objects import base
from neutron.services.qos import constants as qos_constants
class RuleTypeField(obj_fields.BaseEnumField): class RuleTypeField(obj_fields.BaseEnumField):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.AUTO_TYPE = obj_fields.Enum( self.AUTO_TYPE = obj_fields.Enum(
valid_values=qos_consts.VALID_RULE_TYPES) valid_values=qos_constants.VALID_RULE_TYPES)
super(RuleTypeField, self).__init__(**kwargs) super(RuleTypeField, self).__init__(**kwargs)
@ -33,7 +33,8 @@ class QosRuleType(base.NeutronObject):
# Version 1.1: Added QosDscpMarkingRule # Version 1.1: Added QosDscpMarkingRule
# Version 1.2: Added QosMinimumBandwidthRule # Version 1.2: Added QosMinimumBandwidthRule
# Version 1.3: Added drivers field # Version 1.3: Added drivers field
VERSION = '1.3' # Version 1.4: Added QosPacketRateLimitRule
VERSION = '1.4'
fields = { fields = {
'type': RuleTypeField(), 'type': RuleTypeField(),

View File

@ -0,0 +1,22 @@
#
# 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.services.qos import constants as qos_consts
# TODO(liuyulong): Because of the development sequence, the rule must
# be implemented in Neutron first. Then the following can be moved
# to neutron-lib after neutron has the new rule.
# Add qos rule packet rate limit
RULE_TYPE_PACKET_RATE_LIMIT = 'packet_rate_limit'
VALID_RULE_TYPES = qos_consts.VALID_RULE_TYPES + [RULE_TYPE_PACKET_RATE_LIMIT]

View File

@ -24,6 +24,7 @@ from neutron.api.rpc.callbacks.producer import registry as rpc_registry
from neutron.api.rpc.callbacks import resources from neutron.api.rpc.callbacks import resources
from neutron.api.rpc.handlers import resources_rpc from neutron.api.rpc.handlers import resources_rpc
from neutron.objects.qos import policy as policy_object from neutron.objects.qos import policy as policy_object
from neutron.services.qos import constants as qos_constants
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -168,7 +169,7 @@ class QosServiceDriverManager(object):
if not self._drivers: if not self._drivers:
return [] return []
rule_types = set(qos_consts.VALID_RULE_TYPES) rule_types = set(qos_constants.VALID_RULE_TYPES)
# Recalculate on every call to allow drivers determine supported rule # Recalculate on every call to allow drivers determine supported rule
# types dynamically # types dynamically

View File

@ -22,6 +22,7 @@ from neutron_lib.api.definitions import qos_bw_limit_direction
from neutron_lib.api.definitions import qos_bw_minimum_ingress from neutron_lib.api.definitions import qos_bw_minimum_ingress
from neutron_lib.api.definitions import qos_default from neutron_lib.api.definitions import qos_default
from neutron_lib.api.definitions import qos_port_network_policy from neutron_lib.api.definitions import qos_port_network_policy
from neutron_lib.api.definitions import qos_pps_rule
from neutron_lib.api.definitions import qos_rule_type_details from neutron_lib.api.definitions import qos_rule_type_details
from neutron_lib.api.definitions import qos_rules_alias from neutron_lib.api.definitions import qos_rules_alias
from neutron_lib.callbacks import events as callbacks_events from neutron_lib.callbacks import events as callbacks_events
@ -64,14 +65,17 @@ class QoSPlugin(qos.QoSPluginBase):
service parameters over ports and networks. service parameters over ports and networks.
""" """
supported_extension_aliases = [qos_apidef.ALIAS, supported_extension_aliases = [
qos_bw_limit_direction.ALIAS, qos_apidef.ALIAS,
qos_default.ALIAS, qos_bw_limit_direction.ALIAS,
qos_rule_type_details.ALIAS, qos_default.ALIAS,
port_resource_request.ALIAS, qos_rule_type_details.ALIAS,
qos_bw_minimum_ingress.ALIAS, port_resource_request.ALIAS,
qos_rules_alias.ALIAS, qos_bw_minimum_ingress.ALIAS,
qos_port_network_policy.ALIAS] qos_rules_alias.ALIAS,
qos_port_network_policy.ALIAS,
qos_pps_rule.ALIAS,
]
__native_pagination_support = True __native_pagination_support = True
__native_sorting_support = True __native_sorting_support = True

View File

@ -13,6 +13,7 @@
import random import random
from unittest import mock from unittest import mock
from neutron_lib import constants as lib_consts
from neutron_lib.exceptions import qos as qos_exc from neutron_lib.exceptions import qos as qos_exc
from neutron_lib.services.qos import constants as qos_consts from neutron_lib.services.qos import constants as qos_consts
from oslo_utils import uuidutils from oslo_utils import uuidutils
@ -24,6 +25,7 @@ from neutron.objects import ports as port_obj
from neutron.objects.qos import binding from neutron.objects.qos import binding
from neutron.objects.qos import policy from neutron.objects.qos import policy
from neutron.objects.qos import rule from neutron.objects.qos import rule
from neutron.services.qos import constants as q_consts
from neutron.tests.unit.objects import test_base from neutron.tests.unit.objects import test_base
from neutron.tests.unit import testlib_api from neutron.tests.unit import testlib_api
@ -32,6 +34,7 @@ RULE_OBJ_CLS = {
qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: rule.QosBandwidthLimitRule, qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: rule.QosBandwidthLimitRule,
qos_consts.RULE_TYPE_DSCP_MARKING: rule.QosDscpMarkingRule, qos_consts.RULE_TYPE_DSCP_MARKING: rule.QosDscpMarkingRule,
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: rule.QosMinimumBandwidthRule, qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: rule.QosMinimumBandwidthRule,
q_consts.RULE_TYPE_PACKET_RATE_LIMIT: rule.QosPacketRateLimitRule,
} }
@ -173,7 +176,10 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
for rule_type in rule_type): for rule_type in rule_type):
rule_fields = self.get_random_object_fields(obj_cls=obj_cls) rule_fields = self.get_random_object_fields(obj_cls=obj_cls)
rule_fields['qos_policy_id'] = policy_obj.id rule_fields['qos_policy_id'] = policy_obj.id
if (obj_cls.rule_type == qos_consts.RULE_TYPE_BANDWIDTH_LIMIT and if (obj_cls.rule_type in [
qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH,
q_consts.RULE_TYPE_PACKET_RATE_LIMIT] and
bwlimit_direction is not None): bwlimit_direction is not None):
rule_fields['direction'] = bwlimit_direction rule_fields['direction'] = bwlimit_direction
rule_obj = obj_cls(self.context, **rule_fields) rule_obj = obj_cls(self.context, **rule_fields)
@ -184,6 +190,11 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
policy_obj.obj_load_attr('rules') policy_obj.obj_load_attr('rules')
return policy_obj, rules return policy_obj, rules
@staticmethod
def _policy_through_version(obj, version):
primitive = obj.obj_to_primitive(target_version=version)
return policy.QosPolicy.clean_obj_from_primitive(primitive)
def test_attach_network_get_network_policy(self): def test_attach_network_get_network_policy(self):
obj = self._create_test_policy() obj = self._create_test_policy()
@ -456,6 +467,20 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
self.assertRaises(exception.IncompatibleObjectVersion, self.assertRaises(exception.IncompatibleObjectVersion,
policy_obj.obj_to_primitive, '1.7') policy_obj.obj_to_primitive, '1.7')
def test_object_version_degradation_less_than_1_9(self):
policy_obj, rule_objs = self._create_test_policy_with_rules(
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
qos_consts.RULE_TYPE_DSCP_MARKING,
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH,
q_consts.RULE_TYPE_PACKET_RATE_LIMIT], reload_rules=True,
bwlimit_direction=lib_consts.INGRESS_DIRECTION)
policy_obj_v1_8 = self._policy_through_version(policy_obj, '1.8')
self.assertIn(rule_objs[0], policy_obj_v1_8.rules)
self.assertIn(rule_objs[1], policy_obj_v1_8.rules)
self.assertIn(rule_objs[2], policy_obj_v1_8.rules)
self.assertNotIn(rule_objs[3], policy_obj_v1_8.rules)
@mock.patch.object(policy.QosPolicy, 'unset_default') @mock.patch.object(policy.QosPolicy, 'unset_default')
def test_filter_by_shared(self, *mocks): def test_filter_by_shared(self, *mocks):
project_id = uuidutils.generate_uuid() project_id = uuidutils.generate_uuid()

View File

@ -18,6 +18,7 @@ from oslo_versionedobjects import exception
from neutron.objects.qos import policy from neutron.objects.qos import policy
from neutron.objects.qos import rule from neutron.objects.qos import rule
from neutron.services.qos import constants as qos_constants
from neutron.tests import base as neutron_test_base from neutron.tests import base as neutron_test_base
from neutron.tests.unit.objects import test_base from neutron.tests.unit.objects import test_base
from neutron.tests.unit import testlib_api from neutron.tests.unit import testlib_api
@ -241,3 +242,51 @@ class QosMinimumBandwidthRuleDbObjectTestCase(test_base.BaseDbObjectTestCase,
id=generated_qos_policy_id, id=generated_qos_policy_id,
project_id=uuidutils.generate_uuid()) project_id=uuidutils.generate_uuid())
policy_obj.create() policy_obj.create()
class QosPacketRateLimitRuleObjectTestCase(test_base.BaseObjectIfaceTestCase):
_test_class = rule.QosPacketRateLimitRule
def test_to_dict_returns_type(self):
obj = rule.QosPacketRateLimitRule(self.context, **self.db_objs[0])
dict_ = obj.to_dict()
self.assertEqual(qos_constants.RULE_TYPE_PACKET_RATE_LIMIT,
dict_['type'])
def test_duplicate_rules(self):
policy_id = uuidutils.generate_uuid()
ingress_rule_1 = rule.QosPacketRateLimitRule(
self.context, qos_policy_id=policy_id,
max_kpps=2000, max_burst=800,
direction=constants.INGRESS_DIRECTION)
ingress_rule_2 = rule.QosPacketRateLimitRule(
self.context, qos_policy_id=policy_id,
max_kpps=3000, max_burst=200,
direction=constants.INGRESS_DIRECTION)
egress_rule = rule.QosPacketRateLimitRule(
self.context, qos_policy_id=policy_id,
max_kpps=1000, max_burst=500,
direction=constants.EGRESS_DIRECTION)
dscp_rule = rule.QosDscpMarkingRule(
self.context, qos_policy_id=policy_id, dscp_mark=16)
self.assertTrue(ingress_rule_1.duplicates(ingress_rule_2))
self.assertFalse(ingress_rule_1.duplicates(egress_rule))
self.assertFalse(ingress_rule_1.duplicates(dscp_rule))
class QosPacketRateLimitRuleDbObjectTestCase(test_base.BaseDbObjectTestCase,
testlib_api.SqlTestCase):
_test_class = rule.QosPacketRateLimitRule
def setUp(self):
super(QosPacketRateLimitRuleDbObjectTestCase, self).setUp()
# Prepare policy to be able to insert a rule
for obj in self.db_objs:
generated_qos_policy_id = obj['qos_policy_id']
policy_obj = policy.QosPolicy(self.context,
id=generated_qos_policy_id,
project_id=uuidutils.generate_uuid())
policy_obj.create()

View File

@ -22,6 +22,7 @@ from oslo_config import cfg
from neutron import manager from neutron import manager
from neutron.objects.qos import rule_type from neutron.objects.qos import rule_type
from neutron.services.qos import constants as qos_constants
from neutron.services.qos import qos_plugin from neutron.services.qos import qos_plugin
from neutron.tests import base as test_base from neutron.tests import base as test_base
@ -78,11 +79,11 @@ class QosRuleTypeObjectTestCase(test_base.BaseTestCase):
def test_get_objects(self): def test_get_objects(self):
rule_types_mock = mock.PropertyMock( rule_types_mock = mock.PropertyMock(
return_value=set(qos_consts.VALID_RULE_TYPES)) return_value=set(qos_constants.VALID_RULE_TYPES))
with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types', with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types',
new_callable=rule_types_mock): new_callable=rule_types_mock):
types = rule_type.QosRuleType.get_objects() types = rule_type.QosRuleType.get_objects()
self.assertEqual(sorted(qos_consts.VALID_RULE_TYPES), self.assertEqual(sorted(qos_constants.VALID_RULE_TYPES),
sorted(type_['type'] for type_ in types)) sorted(type_['type'] for type_ in types))
def test_wrong_type(self): def test_wrong_type(self):

View File

@ -82,13 +82,14 @@ object_data = {
'PortUplinkStatusPropagation': '1.1-f0a4ca451a941910376c33616dea5de2', 'PortUplinkStatusPropagation': '1.1-f0a4ca451a941910376c33616dea5de2',
'ProviderResourceAssociation': '1.0-05ab2d5a3017e5ce9dd381328f285f34', 'ProviderResourceAssociation': '1.0-05ab2d5a3017e5ce9dd381328f285f34',
'ProvisioningBlock': '1.0-c19d6d05bfa8143533471c1296066125', 'ProvisioningBlock': '1.0-c19d6d05bfa8143533471c1296066125',
'QosBandwidthLimitRule': '1.3-51b662b12a8d1dfa89288d826c6d26d3', 'QosBandwidthLimitRule': '1.4-51b662b12a8d1dfa89288d826c6d26d3',
'QosDscpMarkingRule': '1.3-0313c6554b34fd10c753cb63d638256c', 'QosDscpMarkingRule': '1.4-0313c6554b34fd10c753cb63d638256c',
'QosMinimumBandwidthRule': '1.3-314c3419f4799067cc31cc319080adff', 'QosMinimumBandwidthRule': '1.4-314c3419f4799067cc31cc319080adff',
'QosPacketRateLimitRule': '1.4-18411fa95f54602b8c8a5da2d3194b31',
'QosPolicyRBAC': '1.1-192845c5ed0718e1c54fac36936fcd7d', 'QosPolicyRBAC': '1.1-192845c5ed0718e1c54fac36936fcd7d',
'QosRuleType': '1.3-7286188edeb3a0386f9cf7979b9700fc', 'QosRuleType': '1.4-a5b870dfa6f510a91f5cb0216873064e',
'QosRuleTypeDriver': '1.0-7d8cb9f0ef661ac03700eae97118e3db', 'QosRuleTypeDriver': '1.0-7d8cb9f0ef661ac03700eae97118e3db',
'QosPolicy': '1.8-4adb0cde3102c10d8970ec9487fd7fe7', 'QosPolicy': '1.9-4adb0cde3102c10d8970ec9487fd7fe7',
'QosPolicyDefault': '1.0-59e5060eedb1f06dd0935a244d27d11c', 'QosPolicyDefault': '1.0-59e5060eedb1f06dd0935a244d27d11c',
'QosPolicyFloatingIPBinding': '1.0-5625df4205a18778cd6aa40f99be024e', 'QosPolicyFloatingIPBinding': '1.0-5625df4205a18778cd6aa40f99be024e',
'QosPolicyRouterGatewayIPBinding': '1.0-da064fbfe5ee18c950b905b483bf59e3', 'QosPolicyRouterGatewayIPBinding': '1.0-da064fbfe5ee18c950b905b483bf59e3',

View File

@ -38,6 +38,7 @@ from neutron.objects import network as network_object
from neutron.objects import ports as ports_object from neutron.objects import ports as ports_object
from neutron.objects.qos import policy as policy_object from neutron.objects.qos import policy as policy_object
from neutron.objects.qos import rule as rule_object from neutron.objects.qos import rule as rule_object
from neutron.services.qos import constants as qos_constants
from neutron.services.qos import qos_plugin from neutron.services.qos import qos_plugin
from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.db import test_db_base_plugin_v2
from neutron.tests.unit.services.qos import base from neutron.tests.unit.services.qos import base
@ -98,7 +99,11 @@ class TestQosPlugin(base.BaseQosTestCase):
'dscp_mark': 16}, 'dscp_mark': 16},
'minimum_bandwidth_rule': { 'minimum_bandwidth_rule': {
'id': uuidutils.generate_uuid(), 'id': uuidutils.generate_uuid(),
'min_kbps': 10}} 'min_kbps': 10},
'packet_rate_limit_rule': {
'id': uuidutils.generate_uuid(),
'max_kpps': 20,
'max_burst_kpps': 130}}
self.policy = policy_object.QosPolicy( self.policy = policy_object.QosPolicy(
self.ctxt, **self.policy_data['policy']) self.ctxt, **self.policy_data['policy'])
@ -112,6 +117,9 @@ class TestQosPlugin(base.BaseQosTestCase):
self.min_rule = rule_object.QosMinimumBandwidthRule( self.min_rule = rule_object.QosMinimumBandwidthRule(
self.ctxt, **self.rule_data['minimum_bandwidth_rule']) self.ctxt, **self.rule_data['minimum_bandwidth_rule'])
self.pps_rule = rule_object.QosPacketRateLimitRule(
self.ctxt, **self.rule_data['packet_rate_limit_rule'])
def _validate_driver_params(self, method_name, ctxt): def _validate_driver_params(self, method_name, ctxt):
call_args = self.qos_plugin.driver_manager.call.call_args[0] call_args = self.qos_plugin.driver_manager.call.call_args[0]
self.assertTrue(self.qos_plugin.driver_manager.call.called) self.assertTrue(self.qos_plugin.driver_manager.call.called)
@ -1025,12 +1033,12 @@ class TestQosPlugin(base.BaseQosTestCase):
def test_get_rule_types(self): def test_get_rule_types(self):
rule_types_mock = mock.PropertyMock( rule_types_mock = mock.PropertyMock(
return_value=qos_consts.VALID_RULE_TYPES) return_value=qos_constants.VALID_RULE_TYPES)
filters = {'type': 'type_id'} filters = {'type': 'type_id'}
with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types', with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types',
new_callable=rule_types_mock): new_callable=rule_types_mock):
types = self.qos_plugin.get_rule_types(self.ctxt, filters=filters) types = self.qos_plugin.get_rule_types(self.ctxt, filters=filters)
self.assertEqual(sorted(qos_consts.VALID_RULE_TYPES), self.assertEqual(sorted(qos_constants.VALID_RULE_TYPES),
sorted(type_['type'] for type_ in types)) sorted(type_['type'] for type_ in types))
@mock.patch('neutron.objects.ports.Port') @mock.patch('neutron.objects.ports.Port')
@ -1078,6 +1086,180 @@ class TestQosPlugin(base.BaseQosTestCase):
self.assertLess( self.assertLess(
action_index, mock_manager.mock_calls.index(driver_mock_call)) action_index, mock_manager.mock_calls.index(driver_mock_call))
def test_create_policy_packet_rate_limit_rule(self):
_policy = policy_object.QosPolicy(
self.ctxt, **self.policy_data['policy'])
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=_policy):
setattr(_policy, "rules", [self.pps_rule])
self.qos_plugin.create_policy_packet_rate_limit_rule(
self.ctxt, self.policy.id, self.rule_data)
self._validate_driver_params('update_policy', self.ctxt)
def test_create_policy_pps_rule_duplicates(self):
_policy = self._get_policy()
setattr(_policy, "rules", [self.pps_rule])
new_rule_data = {
'packet_rate_limit_rule': {
'max_kpps': 400,
'direction': self.pps_rule.direction
}
}
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=_policy) as mock_qos_get_obj:
self.assertRaises(
qos_exc.QoSRulesConflict,
self.qos_plugin.create_policy_packet_rate_limit_rule,
self.ctxt, _policy.id, new_rule_data)
mock_qos_get_obj.assert_called_once_with(self.ctxt, id=_policy.id)
def test_update_policy_packet_rate_limit_rule(self):
_policy = policy_object.QosPolicy(
self.ctxt, **self.policy_data['policy'])
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=_policy):
setattr(_policy, "rules", [self.pps_rule])
self.qos_plugin.update_policy_packet_rate_limit_rule(
self.ctxt, self.pps_rule.id, self.policy.id, self.rule_data)
self._validate_driver_params('update_policy', self.ctxt)
def test_update_policy_pps_rule_bad_policy(self):
_policy = policy_object.QosPolicy(
self.ctxt, **self.policy_data['policy'])
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=_policy):
setattr(_policy, "rules", [])
self.assertRaises(
qos_exc.QosRuleNotFound,
self.qos_plugin.update_policy_packet_rate_limit_rule,
self.ctxt, self.pps_rule.id, self.policy.id,
self.rule_data)
def test_delete_policy_packet_rate_limit_rule(self):
_policy = policy_object.QosPolicy(
self.ctxt, **self.policy_data['policy'])
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=_policy):
setattr(_policy, "rules", [self.pps_rule])
self.qos_plugin.delete_policy_packet_rate_limit_rule(
self.ctxt, self.pps_rule.id, self.policy.id)
self._validate_driver_params('update_policy', self.ctxt)
def test_delete_policy_pps_rule_bad_policy(self):
_policy = policy_object.QosPolicy(
self.ctxt, **self.policy_data['policy'])
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=_policy):
setattr(_policy, "rules", [])
self.assertRaises(
qos_exc.QosRuleNotFound,
self.qos_plugin.delete_policy_packet_rate_limit_rule,
self.ctxt, self.pps_rule.id, _policy.id)
def test_get_policy_packet_rate_limit_rule(self):
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=self.policy):
with mock.patch('neutron.objects.qos.rule.'
'QosPacketRateLimitRule.'
'get_object') as get_object_mock:
self.qos_plugin.get_policy_packet_rate_limit_rule(
self.ctxt, self.pps_rule.id, self.policy.id)
get_object_mock.assert_called_once_with(self.ctxt,
id=self.pps_rule.id)
def test_get_policy_packet_rate_limit_rules_for_policy(self):
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=self.policy):
with mock.patch('neutron.objects.qos.rule.'
'QosPacketRateLimitRule.'
'get_objects') as get_objects_mock:
self.qos_plugin.get_policy_packet_rate_limit_rules(
self.ctxt, self.policy.id)
get_objects_mock.assert_called_once_with(
self.ctxt, _pager=mock.ANY, qos_policy_id=self.policy.id)
def test_get_policy_packet_rate_limit_rules_for_policy_with_filters(self):
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=self.policy):
with mock.patch('neutron.objects.qos.rule.'
'QosPacketRateLimitRule.'
'get_objects') as get_objects_mock:
filters = {'filter': 'filter_id'}
self.qos_plugin.get_policy_packet_rate_limit_rules(
self.ctxt, self.policy.id, filters=filters)
get_objects_mock.assert_called_once_with(
self.ctxt, _pager=mock.ANY,
qos_policy_id=self.policy.id,
filter='filter_id')
def test_get_policy_packet_rate_limit_rule_for_nonexistent_policy(self):
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=None):
self.assertRaises(
qos_exc.QosPolicyNotFound,
self.qos_plugin.get_policy_packet_rate_limit_rule,
self.ctxt, self.pps_rule.id, self.policy.id)
def test_get_policy_packet_rate_limit_rules_for_nonexistent_policy(self):
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=None):
self.assertRaises(
qos_exc.QosPolicyNotFound,
self.qos_plugin.get_policy_packet_rate_limit_rules,
self.ctxt, self.policy.id)
def test_create_policy_pps_rule_for_nonexistent_policy(self):
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=None):
self.assertRaises(
qos_exc.QosPolicyNotFound,
self.qos_plugin.create_policy_packet_rate_limit_rule,
self.ctxt, self.policy.id, self.rule_data)
def test_update_policy_pps_rule_for_nonexistent_policy(self):
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=None):
self.assertRaises(
qos_exc.QosPolicyNotFound,
self.qos_plugin.update_policy_packet_rate_limit_rule,
self.ctxt, self.pps_rule.id, self.policy.id, self.rule_data)
def test_delete_policy_pps_rule_for_nonexistent_policy(self):
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=None):
self.assertRaises(
qos_exc.QosPolicyNotFound,
self.qos_plugin.delete_policy_packet_rate_limit_rule,
self.ctxt, self.pps_rule.id, self.policy.id)
def test_get_pps_rule_type(self):
admin_ctxt = context.get_admin_context()
drivers_details = [{
'name': 'fake-driver',
'supported_parameters': [{
'parameter_name': 'max_kpps',
'parameter_type': lib_constants.VALUES_TYPE_RANGE,
'parameter_range': {'start': 0, 'end': 100}
}]
}]
with mock.patch.object(
qos_plugin.QoSPlugin, "supported_rule_type_details",
return_value=drivers_details
):
rule_type_details = self.qos_plugin.get_rule_type(
admin_ctxt, qos_constants.RULE_TYPE_PACKET_RATE_LIMIT)
self.assertEqual(
qos_constants.RULE_TYPE_PACKET_RATE_LIMIT,
rule_type_details['type'])
self.assertEqual(
drivers_details, rule_type_details['drivers'])
def test_get_pps_rule_type_as_user(self):
self.assertRaises(
lib_exc.NotAuthorized,
self.qos_plugin.get_rule_type,
self.ctxt, qos_constants.RULE_TYPE_PACKET_RATE_LIMIT)
class QoSRuleAliasTestExtensionManager(object): class QoSRuleAliasTestExtensionManager(object):

View File

@ -0,0 +1,6 @@
---
features:
- |
Added new API extension to QoS service plugin
to support CRUD actions for packet rate limit (packet per
second) rule in Neutron server side.

View File

@ -235,6 +235,7 @@ neutron.objects =
QosBandwidthLimitRule = neutron.objects.qos.rule:QosBandwidthLimitRule QosBandwidthLimitRule = neutron.objects.qos.rule:QosBandwidthLimitRule
QosDscpMarkingRule = neutron.objects.qos.rule:QosDscpMarkingRule QosDscpMarkingRule = neutron.objects.qos.rule:QosDscpMarkingRule
QosMinimumBandwidthRule = neutron.objects.qos.rule:QosMinimumBandwidthRule QosMinimumBandwidthRule = neutron.objects.qos.rule:QosMinimumBandwidthRule
QosPacketRateLimitRule = neutron.objects.qos.rule:QosPacketRateLimitRule
QosPolicy = neutron.objects.qos.policy:QosPolicy QosPolicy = neutron.objects.qos.policy:QosPolicy
QosPolicyDefault = neutron.objects.qos.policy:QosPolicyDefault QosPolicyDefault = neutron.objects.qos.policy:QosPolicyDefault
QosPolicyFloatingIPBinding = neutron.objects.qos.binding:QosPolicyFloatingIPBinding QosPolicyFloatingIPBinding = neutron.objects.qos.binding:QosPolicyFloatingIPBinding