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

This patch adds new API extension to QoS service plugin
to allow CURD actions for packet rate limit (packet per
second) rule in Neutron server side.

NOTE: This patch will NOT implement the real functionality
in L2/L3 backend to limit the pps.

Co-Authored-By:  NANALI <lin203@chinaunicom.cn>

Closes-bug: #1912460
Change-Id: Icc88accb88d9cec40c960c56f032c3c27317b42e
This commit is contained in:
LIU Yulong 2021-06-02 15:38:02 +08:00
parent b7befc9811
commit 8e30639452
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'),
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
# 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).

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.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

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.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

View File

@ -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(),

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.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

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_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

View File

@ -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()

View File

@ -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()

View File

@ -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):

View File

@ -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',

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.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):

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

@ -234,6 +234,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