summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2021-08-04 13:30:17 +0000
committerGerrit Code Review <review@openstack.org>2021-08-04 13:30:17 +0000
commit86de15d6327d99e31e143d8320fe80a0fb594230 (patch)
tree7fdf59f0d0ddd3caa67cfe84a03695cbd08a72a2
parentece64f3857cdffc4114a6c80dee8e94f48bc3af8 (diff)
parent8e30639452312019de17f3e1e8364f579752944e (diff)
downloadneutron-86de15d6327d99e31e143d8320fe80a0fb594230.tar.gz
Merge "[QoS] Add rule type packet per second (pps)"
-rw-r--r--neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD2
-rw-r--r--neutron/db/migration/alembic_migrations/versions/xena/expand/1bb3393de75d_add_qos_pps_rule.py55
-rw-r--r--neutron/db/qos/models.py26
-rw-r--r--neutron/extensions/qos.py9
-rw-r--r--neutron/extensions/qos_pps_rule.py60
-rw-r--r--neutron/objects/qos/policy.py15
-rw-r--r--neutron/objects/qos/rule.py23
-rw-r--r--neutron/objects/qos/rule_type.py7
-rw-r--r--neutron/services/qos/constants.py22
-rw-r--r--neutron/services/qos/drivers/manager.py3
-rw-r--r--neutron/services/qos/qos_plugin.py20
-rw-r--r--neutron/tests/unit/objects/qos/test_policy.py27
-rw-r--r--neutron/tests/unit/objects/qos/test_rule.py49
-rw-r--r--neutron/tests/unit/objects/qos/test_rule_type.py5
-rw-r--r--neutron/tests/unit/objects/test_objects.py11
-rw-r--r--neutron/tests/unit/services/qos/test_qos_plugin.py188
-rw-r--r--releasenotes/notes/qos_rule_type_pps-27254b90f26c10b6.yaml6
-rw-r--r--setup.cfg1
18 files changed, 499 insertions, 30 deletions
diff --git a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD
index d13d55f476..c31bc14c10 100644
--- a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD
+++ b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD
@@ -1 +1 @@
-8df53b0d2c0e
+1bb3393de75d
diff --git a/neutron/db/migration/alembic_migrations/versions/xena/expand/1bb3393de75d_add_qos_pps_rule.py b/neutron/db/migration/alembic_migrations/versions/xena/expand/1bb3393de75d_add_qos_pps_rule.py
new file mode 100644
index 0000000000..f84d7adc58
--- /dev/null
+++ b/neutron/db/migration/alembic_migrations/versions/xena/expand/1bb3393de75d_add_qos_pps_rule.py
@@ -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')
+ )
diff --git a/neutron/db/qos/models.py b/neutron/db/qos/models.py
index 8a64e6cd0b..7d2345fc11 100644
--- a/neutron/db/qos/models.py
+++ b/neutron/db/qos/models.py
@@ -192,3 +192,29 @@ class QosMinimumBandwidthRule(model_base.HasId, model_base.BASEV2):
name='qos_minimum_bandwidth_rules0qos_policy_id0direction'),
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__
+ )
diff --git a/neutron/extensions/qos.py b/neutron/extensions/qos.py
index 7764f6615d..82f2f15d0d 100644
--- a/neutron/extensions/qos.py
+++ b/neutron/extensions/qos.py
@@ -84,9 +84,12 @@ class QoSPluginBase(service_base.ServicePluginBase, metaclass=abc.ABCMeta):
path_prefix = apidef.API_PREFIX
# The rule object type to use for each incoming rule-related request.
- rule_objects = {'bandwidth_limit': rule_object.QosBandwidthLimitRule,
- 'dscp_marking': rule_object.QosDscpMarkingRule,
- 'minimum_bandwidth': rule_object.QosMinimumBandwidthRule}
+ rule_objects = {
+ 'bandwidth_limit': rule_object.QosBandwidthLimitRule,
+ '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
# method calls (see __getattr__ docstring, below).
diff --git a/neutron/extensions/qos_pps_rule.py b/neutron/extensions/qos_pps_rule.py
new file mode 100644
index 0000000000..03ef37b094
--- /dev/null
+++ b/neutron/extensions/qos_pps_rule.py
@@ -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
diff --git a/neutron/objects/qos/policy.py b/neutron/objects/qos/policy.py
index bb805873a0..bedcd2af85 100644
--- a/neutron/objects/qos/policy.py
+++ b/neutron/objects/qos/policy.py
@@ -66,7 +66,8 @@ class QosPolicy(rbac_db.NeutronRbacObject):
# Version 1.6: Added "is_default" field
# Version 1.7: Added floating IP bindings
# Version 1.8: Added router gateway QoS policy bindings
- VERSION = '1.8'
+ # Version 1.9: Added QosPacketRateLimitRule
+ VERSION = '1.9'
# required by RbacNeutronMetaclass
rbac_db_cls = QosPolicyRBAC
@@ -376,10 +377,22 @@ class QosPolicy(rbac_db.NeutronRbacObject):
return set(bound_tenants)
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)
if _target_version < (1, 8):
raise exception.IncompatibleObjectVersion(
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
diff --git a/neutron/objects/qos/rule.py b/neutron/objects/qos/rule.py
index 165c090100..049820217f 100644
--- a/neutron/objects/qos/rule.py
+++ b/neutron/objects/qos/rule.py
@@ -26,6 +26,7 @@ from oslo_versionedobjects import fields as obj_fields
from neutron.db.qos import models as qos_db_model
from neutron.objects import base
+from neutron.services.qos import constants as qos_constants
DSCP_MARK = 'dscp_mark'
@@ -36,7 +37,7 @@ def get_rules(obj_cls, context, qos_policy_id):
return all_rules
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 = getattr(sys.modules[__name__], rule_cls_name)
@@ -50,11 +51,12 @@ class QosRule(base.NeutronDbObject, metaclass=abc.ABCMeta):
# 1.1: Added DscpMarkingRule
# 1.2: Added QosMinimumBandwidthRule
# 1.3: Added direction for BandwidthLimitRule
+ # 1.4: Added PacketRateLimitRule
#
# NOTE(mangelajo): versions need to be handled from the top QosRule object
# because it's the only reference QosPolicy can make
# to them via obj_relationships version map
- VERSION = '1.3'
+ VERSION = '1.4'
fields = {
'id': common_types.UUIDField(),
@@ -167,3 +169,20 @@ class QosMinimumBandwidthRule(QosRule):
duplicates_compare_fields = ['direction']
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
diff --git a/neutron/objects/qos/rule_type.py b/neutron/objects/qos/rule_type.py
index 1a94fff09c..ced31370cd 100644
--- a/neutron/objects/qos/rule_type.py
+++ b/neutron/objects/qos/rule_type.py
@@ -13,17 +13,17 @@
from neutron_lib.objects import common_types
from neutron_lib.plugins import constants
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 neutron.objects import base
+from neutron.services.qos import constants as qos_constants
class RuleTypeField(obj_fields.BaseEnumField):
def __init__(self, **kwargs):
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)
@@ -33,7 +33,8 @@ class QosRuleType(base.NeutronObject):
# Version 1.1: Added QosDscpMarkingRule
# Version 1.2: Added QosMinimumBandwidthRule
# Version 1.3: Added drivers field
- VERSION = '1.3'
+ # Version 1.4: Added QosPacketRateLimitRule
+ VERSION = '1.4'
fields = {
'type': RuleTypeField(),
diff --git a/neutron/services/qos/constants.py b/neutron/services/qos/constants.py
new file mode 100644
index 0000000000..576c3f5441
--- /dev/null
+++ b/neutron/services/qos/constants.py
@@ -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]
diff --git a/neutron/services/qos/drivers/manager.py b/neutron/services/qos/drivers/manager.py
index 13818065d5..494cc9b093 100644
--- a/neutron/services/qos/drivers/manager.py
+++ b/neutron/services/qos/drivers/manager.py
@@ -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.handlers import resources_rpc
from neutron.objects.qos import policy as policy_object
+from neutron.services.qos import constants as qos_constants
LOG = logging.getLogger(__name__)
@@ -168,7 +169,7 @@ class QosServiceDriverManager(object):
if not self._drivers:
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
# types dynamically
diff --git a/neutron/services/qos/qos_plugin.py b/neutron/services/qos/qos_plugin.py
index 3ac3e99272..7c844b7997 100644
--- a/neutron/services/qos/qos_plugin.py
+++ b/neutron/services/qos/qos_plugin.py
@@ -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_default
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_rules_alias
from neutron_lib.callbacks import events as callbacks_events
@@ -64,14 +65,17 @@ class QoSPlugin(qos.QoSPluginBase):
service parameters over ports and networks.
"""
- supported_extension_aliases = [qos_apidef.ALIAS,
- qos_bw_limit_direction.ALIAS,
- qos_default.ALIAS,
- qos_rule_type_details.ALIAS,
- port_resource_request.ALIAS,
- qos_bw_minimum_ingress.ALIAS,
- qos_rules_alias.ALIAS,
- qos_port_network_policy.ALIAS]
+ supported_extension_aliases = [
+ qos_apidef.ALIAS,
+ qos_bw_limit_direction.ALIAS,
+ qos_default.ALIAS,
+ qos_rule_type_details.ALIAS,
+ port_resource_request.ALIAS,
+ qos_bw_minimum_ingress.ALIAS,
+ qos_rules_alias.ALIAS,
+ qos_port_network_policy.ALIAS,
+ qos_pps_rule.ALIAS,
+ ]
__native_pagination_support = True
__native_sorting_support = True
diff --git a/neutron/tests/unit/objects/qos/test_policy.py b/neutron/tests/unit/objects/qos/test_policy.py
index d4490bd5d1..8e1b7fb4ad 100644
--- a/neutron/tests/unit/objects/qos/test_policy.py
+++ b/neutron/tests/unit/objects/qos/test_policy.py
@@ -13,6 +13,7 @@
import random
from unittest import mock
+from neutron_lib import constants as lib_consts
from neutron_lib.exceptions import qos as qos_exc
from neutron_lib.services.qos import constants as qos_consts
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 policy
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 import testlib_api
@@ -32,6 +34,7 @@ RULE_OBJ_CLS = {
qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: rule.QosBandwidthLimitRule,
qos_consts.RULE_TYPE_DSCP_MARKING: rule.QosDscpMarkingRule,
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):
rule_fields = self.get_random_object_fields(obj_cls=obj_cls)
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):
rule_fields['direction'] = bwlimit_direction
rule_obj = obj_cls(self.context, **rule_fields)
@@ -184,6 +190,11 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
policy_obj.obj_load_attr('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):
obj = self._create_test_policy()
@@ -456,6 +467,20 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
self.assertRaises(exception.IncompatibleObjectVersion,
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')
def test_filter_by_shared(self, *mocks):
project_id = uuidutils.generate_uuid()
diff --git a/neutron/tests/unit/objects/qos/test_rule.py b/neutron/tests/unit/objects/qos/test_rule.py
index a8064137de..e225a9a335 100644
--- a/neutron/tests/unit/objects/qos/test_rule.py
+++ b/neutron/tests/unit/objects/qos/test_rule.py
@@ -18,6 +18,7 @@ from oslo_versionedobjects import exception
from neutron.objects.qos import policy
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.unit.objects import test_base
from neutron.tests.unit import testlib_api
@@ -241,3 +242,51 @@ class QosMinimumBandwidthRuleDbObjectTestCase(test_base.BaseDbObjectTestCase,
id=generated_qos_policy_id,
project_id=uuidutils.generate_uuid())
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()
diff --git a/neutron/tests/unit/objects/qos/test_rule_type.py b/neutron/tests/unit/objects/qos/test_rule_type.py
index ea7304618d..43d8807f08 100644
--- a/neutron/tests/unit/objects/qos/test_rule_type.py
+++ b/neutron/tests/unit/objects/qos/test_rule_type.py
@@ -22,6 +22,7 @@ from oslo_config import cfg
from neutron import manager
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.tests import base as test_base
@@ -78,11 +79,11 @@ class QosRuleTypeObjectTestCase(test_base.BaseTestCase):
def test_get_objects(self):
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',
new_callable=rule_types_mock):
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))
def test_wrong_type(self):
diff --git a/neutron/tests/unit/objects/test_objects.py b/neutron/tests/unit/objects/test_objects.py
index 0c7e975b9f..511b18e344 100644
--- a/neutron/tests/unit/objects/test_objects.py
+++ b/neutron/tests/unit/objects/test_objects.py
@@ -82,13 +82,14 @@ object_data = {
'PortUplinkStatusPropagation': '1.1-f0a4ca451a941910376c33616dea5de2',
'ProviderResourceAssociation': '1.0-05ab2d5a3017e5ce9dd381328f285f34',
'ProvisioningBlock': '1.0-c19d6d05bfa8143533471c1296066125',
- 'QosBandwidthLimitRule': '1.3-51b662b12a8d1dfa89288d826c6d26d3',
- 'QosDscpMarkingRule': '1.3-0313c6554b34fd10c753cb63d638256c',
- 'QosMinimumBandwidthRule': '1.3-314c3419f4799067cc31cc319080adff',
+ 'QosBandwidthLimitRule': '1.4-51b662b12a8d1dfa89288d826c6d26d3',
+ 'QosDscpMarkingRule': '1.4-0313c6554b34fd10c753cb63d638256c',
+ 'QosMinimumBandwidthRule': '1.4-314c3419f4799067cc31cc319080adff',
+ 'QosPacketRateLimitRule': '1.4-18411fa95f54602b8c8a5da2d3194b31',
'QosPolicyRBAC': '1.1-192845c5ed0718e1c54fac36936fcd7d',
- 'QosRuleType': '1.3-7286188edeb3a0386f9cf7979b9700fc',
+ 'QosRuleType': '1.4-a5b870dfa6f510a91f5cb0216873064e',
'QosRuleTypeDriver': '1.0-7d8cb9f0ef661ac03700eae97118e3db',
- 'QosPolicy': '1.8-4adb0cde3102c10d8970ec9487fd7fe7',
+ 'QosPolicy': '1.9-4adb0cde3102c10d8970ec9487fd7fe7',
'QosPolicyDefault': '1.0-59e5060eedb1f06dd0935a244d27d11c',
'QosPolicyFloatingIPBinding': '1.0-5625df4205a18778cd6aa40f99be024e',
'QosPolicyRouterGatewayIPBinding': '1.0-da064fbfe5ee18c950b905b483bf59e3',
diff --git a/neutron/tests/unit/services/qos/test_qos_plugin.py b/neutron/tests/unit/services/qos/test_qos_plugin.py
index 65b745bc4b..4ae2af3e1f 100644
--- a/neutron/tests/unit/services/qos/test_qos_plugin.py
+++ b/neutron/tests/unit/services/qos/test_qos_plugin.py
@@ -38,6 +38,7 @@ from neutron.objects import network as network_object
from neutron.objects import ports as ports_object
from neutron.objects.qos import policy as policy_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.tests.unit.db import test_db_base_plugin_v2
from neutron.tests.unit.services.qos import base
@@ -98,7 +99,11 @@ class TestQosPlugin(base.BaseQosTestCase):
'dscp_mark': 16},
'minimum_bandwidth_rule': {
'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.ctxt, **self.policy_data['policy'])
@@ -112,6 +117,9 @@ class TestQosPlugin(base.BaseQosTestCase):
self.min_rule = rule_object.QosMinimumBandwidthRule(
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):
call_args = self.qos_plugin.driver_manager.call.call_args[0]
self.assertTrue(self.qos_plugin.driver_manager.call.called)
@@ -1025,12 +1033,12 @@ class TestQosPlugin(base.BaseQosTestCase):
def test_get_rule_types(self):
rule_types_mock = mock.PropertyMock(
- return_value=qos_consts.VALID_RULE_TYPES)
+ return_value=qos_constants.VALID_RULE_TYPES)
filters = {'type': 'type_id'}
with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types',
new_callable=rule_types_mock):
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))
@mock.patch('neutron.objects.ports.Port')
@@ -1078,6 +1086,180 @@ class TestQosPlugin(base.BaseQosTestCase):
self.assertLess(
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):
diff --git a/releasenotes/notes/qos_rule_type_pps-27254b90f26c10b6.yaml b/releasenotes/notes/qos_rule_type_pps-27254b90f26c10b6.yaml
new file mode 100644
index 0000000000..ffc68e6996
--- /dev/null
+++ b/releasenotes/notes/qos_rule_type_pps-27254b90f26c10b6.yaml
@@ -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.
diff --git a/setup.cfg b/setup.cfg
index 5eade15341..e4fc932779 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -235,6 +235,7 @@ neutron.objects =
QosBandwidthLimitRule = neutron.objects.qos.rule:QosBandwidthLimitRule
QosDscpMarkingRule = neutron.objects.qos.rule:QosDscpMarkingRule
QosMinimumBandwidthRule = neutron.objects.qos.rule:QosMinimumBandwidthRule
+ QosPacketRateLimitRule = neutron.objects.qos.rule:QosPacketRateLimitRule
QosPolicy = neutron.objects.qos.policy:QosPolicy
QosPolicyDefault = neutron.objects.qos.policy:QosPolicyDefault
QosPolicyFloatingIPBinding = neutron.objects.qos.binding:QosPolicyFloatingIPBinding