Add QoS bandwidth limit for instance ingress traffic
This patch introduces the new parameter "direction" to the QoS bandwidth limit rule. It will allow the creation of bandwidth limit rules for either ingress or egress traffic. For backwards compatibility the default direction will be egress. DocImpact: Ingress bandwidth limit available for QoS APIImpact: New type of parameter for QoS rule in neutron API Change-Id: Ia13568879c2b6f80fb190ccafe7e19ca05b0c6a8 Partial-Bug: #1560961
This commit is contained in:
parent
39c05533a3
commit
c29f3aaa7c
@ -1 +1 @@
|
||||
804a3c76314c
|
||||
2b42d90729da
|
||||
|
@ -0,0 +1,81 @@
|
||||
# Copyright 2017 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""qos add direction to bw_limit_rule table
|
||||
|
||||
Revision ID: 2b42d90729da
|
||||
Revises: 804a3c76314c
|
||||
Create Date: 2017-04-03 20:56:00.169599
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2b42d90729da'
|
||||
down_revision = '804a3c76314c'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.common import constants
|
||||
from neutron.db import migration
|
||||
|
||||
|
||||
policies_table_name = "qos_policies"
|
||||
bw_limit_table_name = "qos_bandwidth_limit_rules"
|
||||
direction_enum = sa.Enum(
|
||||
constants.EGRESS_DIRECTION, constants.INGRESS_DIRECTION,
|
||||
name="directions"
|
||||
)
|
||||
|
||||
|
||||
def upgrade():
|
||||
if op.get_context().bind.dialect.name == 'postgresql':
|
||||
direction_enum.create(op.get_bind(), checkfirst=True)
|
||||
|
||||
with migration.remove_fks_from_table(bw_limit_table_name,
|
||||
remove_unique_constraints=True):
|
||||
op.add_column(bw_limit_table_name,
|
||||
sa.Column("direction", direction_enum,
|
||||
server_default=constants.EGRESS_DIRECTION,
|
||||
nullable=False))
|
||||
|
||||
op.create_unique_constraint(
|
||||
op.f('qos_bandwidth_rules0qos_policy_id0direction'),
|
||||
bw_limit_table_name,
|
||||
['qos_policy_id', 'direction'])
|
||||
|
||||
|
||||
def expand_drop_exceptions():
|
||||
"""
|
||||
Drop the existing QoS policy foreign key uniq constraint and then replace
|
||||
it with new unique constraint for pair (policy_id, direction).
|
||||
|
||||
As names of constraints are different in MySQL and PGSQL there is need to
|
||||
add both variants to drop exceptions.
|
||||
"""
|
||||
|
||||
# TODO(slaweq): replace hardcoded constaints names with names get directly
|
||||
# from database model after bug
|
||||
# https://bugs.launchpad.net/neutron/+bug/1685352 will be closed
|
||||
return {
|
||||
sa.ForeignKeyConstraint: [
|
||||
"qos_bandwidth_limit_rules_ibfk_1", # MySQL name
|
||||
"qos_bandwidth_limit_rules_qos_policy_id_fkey" # PGSQL name
|
||||
],
|
||||
sa.UniqueConstraint: [
|
||||
"qos_policy_id", # MySQL name
|
||||
"qos_bandwidth_limit_rules_qos_policy_id_key" # PGSQL name
|
||||
]
|
||||
}
|
@ -78,12 +78,23 @@ class QosBandwidthLimitRule(model_base.HasId, model_base.BASEV2):
|
||||
qos_policy_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('qos_policies.id',
|
||||
ondelete='CASCADE'),
|
||||
nullable=False,
|
||||
unique=True)
|
||||
nullable=False)
|
||||
max_kbps = sa.Column(sa.Integer)
|
||||
max_burst_kbps = 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="directions"),
|
||||
default=constants.EGRESS_DIRECTION,
|
||||
server_default=constants.EGRESS_DIRECTION,
|
||||
nullable=False)
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint(
|
||||
qos_policy_id, direction,
|
||||
name="qos_bandwidth_rules0qos_policy_id0direction"),
|
||||
model_base.BASEV2.__table_args__
|
||||
)
|
||||
|
||||
|
||||
class QosDscpMarkingRule(model_base.HasId, model_base.BASEV2):
|
||||
|
@ -32,6 +32,7 @@ from neutron.objects.qos import rule as rule_object
|
||||
from neutron.plugins.common import constants
|
||||
from neutron.services.qos import qos_consts
|
||||
|
||||
ALIAS = "qos"
|
||||
QOS_PREFIX = "/qos"
|
||||
|
||||
# Attribute Map
|
||||
@ -74,8 +75,10 @@ RESOURCE_ATTRIBUTE_MAP = {
|
||||
}
|
||||
}
|
||||
|
||||
BANDWIDTH_LIMIT_RULES = "bandwidth_limit_rules"
|
||||
|
||||
SUB_RESOURCE_ATTRIBUTE_MAP = {
|
||||
'bandwidth_limit_rules': {
|
||||
BANDWIDTH_LIMIT_RULES: {
|
||||
'parent': {'collection_name': 'policies',
|
||||
'member_name': 'policy'},
|
||||
'parameters': dict(QOS_RULE_COMMON_FIELDS,
|
||||
@ -88,7 +91,7 @@ SUB_RESOURCE_ATTRIBUTE_MAP = {
|
||||
'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': 0,
|
||||
'validate': {'type:range': [0,
|
||||
common_constants.DB_INTEGER_MAX_VALUE]}}})
|
||||
common_constants.DB_INTEGER_MAX_VALUE]}}}),
|
||||
},
|
||||
'dscp_marking_rules': {
|
||||
'parent': {'collection_name': 'policies',
|
||||
@ -196,12 +199,15 @@ class Qos(api_extensions.ExtensionDescriptor):
|
||||
|
||||
def update_attributes_map(self, attributes, extension_attrs_map=None):
|
||||
super(Qos, self).update_attributes_map(
|
||||
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
|
||||
attributes,
|
||||
extension_attrs_map=dict(list(RESOURCE_ATTRIBUTE_MAP.items()) +
|
||||
list(SUB_RESOURCE_ATTRIBUTE_MAP.items())))
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return dict(list(EXTENDED_ATTRIBUTES_2_0.items()) +
|
||||
list(RESOURCE_ATTRIBUTE_MAP.items()))
|
||||
list(RESOURCE_ATTRIBUTE_MAP.items()) +
|
||||
list(SUB_RESOURCE_ATTRIBUTE_MAP.items()))
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
84
neutron/extensions/qos_bw_limit_direction.py
Normal file
84
neutron/extensions/qos_bw_limit_direction.py
Normal file
@ -0,0 +1,84 @@
|
||||
# Copyright (c) 2017 OVH SAS
|
||||
# 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 import extensions as api_extensions
|
||||
|
||||
from neutron.common import constants as common_constants
|
||||
from neutron.extensions import qos
|
||||
|
||||
|
||||
# The name of the extension.
|
||||
NAME = "Direction for QoS bandwidth limit rule"
|
||||
|
||||
# The alias of the extension.
|
||||
ALIAS = "qos-bw-limit-direction"
|
||||
|
||||
# The description of the extension.
|
||||
DESCRIPTION = ("Allow to configure QoS bandwidth limit rule with specific "
|
||||
"direction: ingress or egress")
|
||||
|
||||
# The list of required extensions.
|
||||
REQUIRED_EXTENSIONS = [qos.ALIAS]
|
||||
|
||||
# The list of optional extensions.
|
||||
OPTIONAL_EXTENSIONS = None
|
||||
|
||||
# The resource attribute map for the extension.
|
||||
SUB_RESOURCE_ATTRIBUTE_MAP = {
|
||||
qos.BANDWIDTH_LIMIT_RULES: {
|
||||
'parameters': dict(
|
||||
qos.SUB_RESOURCE_ATTRIBUTE_MAP[
|
||||
qos.BANDWIDTH_LIMIT_RULES]['parameters'],
|
||||
**{'direction': {
|
||||
'allow_post': True,
|
||||
'allow_put': True,
|
||||
'is_visible': True,
|
||||
'default': common_constants.EGRESS_DIRECTION,
|
||||
'validate': {
|
||||
'type:values': common_constants.VALID_DIRECTIONS}}}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Qos_bw_limit_direction(api_extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return NAME
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return ALIAS
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return DESCRIPTION
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2017-04-10T10:00:00-00:00"
|
||||
|
||||
def get_required_extensions(self):
|
||||
return REQUIRED_EXTENSIONS or []
|
||||
|
||||
def get_optional_extensions(self):
|
||||
return OPTIONAL_EXTENSIONS or []
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return SUB_RESOURCE_ATTRIBUTE_MAP
|
||||
else:
|
||||
return {}
|
@ -21,6 +21,7 @@ from oslo_versionedobjects import exception
|
||||
from oslo_versionedobjects import fields as obj_fields
|
||||
|
||||
from neutron._i18n import _
|
||||
from neutron.common import constants as n_const
|
||||
from neutron.common import exceptions
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db import models_v2
|
||||
@ -40,7 +41,8 @@ class QosPolicy(rbac_db.NeutronRbacObject):
|
||||
# Version 1.2: Added QosMinimumBandwidthRule
|
||||
# Version 1.3: Added standard attributes (created_at, revision, etc)
|
||||
# Version 1.4: Changed tenant_id to project_id
|
||||
VERSION = '1.4'
|
||||
# Version 1.5: Direction for bandwidth limit rule added
|
||||
VERSION = '1.5'
|
||||
|
||||
# required by RbacNeutronMetaclass
|
||||
rbac_db_model = QosPolicyRBAC
|
||||
@ -222,6 +224,19 @@ class QosPolicy(rbac_db.NeutronRbacObject):
|
||||
return [rule for rule in rules if
|
||||
rule['versioned_object.name'] in obj_names]
|
||||
|
||||
def filter_ingress_bandwidth_limit_rules(rules):
|
||||
bwlimit_obj_name = rule_obj_impl.QosBandwidthLimitRule.obj_name()
|
||||
filtered_rules = []
|
||||
for rule in rules:
|
||||
if rule['versioned_object.name'] == bwlimit_obj_name:
|
||||
direction = rule['versioned_object.data'].get("direction")
|
||||
if direction == n_const.EGRESS_DIRECTION:
|
||||
rule['versioned_object.data'].pop('direction')
|
||||
filtered_rules.append(rule)
|
||||
else:
|
||||
filtered_rules.append(rule)
|
||||
return filtered_rules
|
||||
|
||||
_target_version = versionutils.convert_version_to_tuple(target_version)
|
||||
names = []
|
||||
if _target_version >= (1, 0):
|
||||
@ -244,3 +259,8 @@ class QosPolicy(rbac_db.NeutronRbacObject):
|
||||
|
||||
if _target_version < (1, 4):
|
||||
primitive['tenant_id'] = primitive.pop('project_id')
|
||||
|
||||
if _target_version < (1, 5):
|
||||
if 'rules' in primitive:
|
||||
primitive['rules'] = filter_ingress_bandwidth_limit_rules(
|
||||
primitive['rules'])
|
||||
|
@ -24,6 +24,7 @@ from oslo_versionedobjects import exception
|
||||
from oslo_versionedobjects import fields as obj_fields
|
||||
import six
|
||||
|
||||
from neutron.common import constants as n_const
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db.qos import models as qos_db_model
|
||||
from neutron.objects import base
|
||||
@ -50,11 +51,12 @@ class QosRule(base.NeutronDbObject):
|
||||
# Version 1.0: Initial version, only BandwidthLimitRule
|
||||
# 1.1: Added DscpMarkingRule
|
||||
# 1.2: Added QosMinimumBandwidthRule
|
||||
# 1.3: Added direction for BandwidthLimitRule
|
||||
#
|
||||
#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.2'
|
||||
VERSION = '1.3'
|
||||
|
||||
fields = {
|
||||
'id': common_types.UUIDField(),
|
||||
@ -106,11 +108,22 @@ class QosBandwidthLimitRule(QosRule):
|
||||
|
||||
fields = {
|
||||
'max_kbps': obj_fields.IntegerField(nullable=True),
|
||||
'max_burst_kbps': obj_fields.IntegerField(nullable=True)
|
||||
'max_burst_kbps': obj_fields.IntegerField(nullable=True),
|
||||
'direction': common_types.FlowDirectionEnumField(
|
||||
default=n_const.EGRESS_DIRECTION)
|
||||
}
|
||||
|
||||
rule_type = qos_consts.RULE_TYPE_BANDWIDTH_LIMIT
|
||||
|
||||
def obj_make_compatible(self, primitive, target_version):
|
||||
_target_version = versionutils.convert_version_to_tuple(target_version)
|
||||
if _target_version < (1, 3) and 'direction' in primitive:
|
||||
direction = primitive.pop('direction')
|
||||
if direction == n_const.INGRESS_DIRECTION:
|
||||
raise exception.IncompatibleObjectVersion(
|
||||
objver=target_version,
|
||||
objtype="QosBandwidthLimitRule")
|
||||
|
||||
|
||||
@obj_base.VersionedObjectRegistry.register
|
||||
class QosDscpMarkingRule(QosRule):
|
||||
|
@ -29,7 +29,9 @@ SUPPORTED_RULES = {
|
||||
qos_consts.MAX_KBPS: {
|
||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
||||
qos_consts.MAX_BURST: {
|
||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]}
|
||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
||||
qos_consts.DIRECTION: {
|
||||
'type:values': [constants.EGRESS_DIRECTION]}
|
||||
},
|
||||
qos_consts.RULE_TYPE_DSCP_MARKING: {
|
||||
qos_consts.DSCP_MARK: {'type:values': constants.VALID_DSCP_MARKS}
|
||||
|
@ -29,7 +29,9 @@ SUPPORTED_RULES = {
|
||||
qos_consts.MAX_KBPS: {
|
||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
||||
qos_consts.MAX_BURST: {
|
||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]}
|
||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
||||
qos_consts.DIRECTION: {
|
||||
'type:values': [constants.EGRESS_DIRECTION]}
|
||||
},
|
||||
qos_consts.RULE_TYPE_DSCP_MARKING: {
|
||||
qos_consts.DSCP_MARK: {'type:values': constants.VALID_DSCP_MARKS}
|
||||
|
@ -29,7 +29,9 @@ SUPPORTED_RULES = {
|
||||
qos_consts.MAX_KBPS: {
|
||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
||||
qos_consts.MAX_BURST: {
|
||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]}
|
||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
||||
qos_consts.DIRECTION: {
|
||||
'type:values': [constants.EGRESS_DIRECTION]}
|
||||
},
|
||||
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: {
|
||||
qos_consts.MIN_KBPS: {
|
||||
|
@ -36,7 +36,7 @@ class QoSPlugin(qos.QoSPluginBase):
|
||||
service parameters over ports and networks.
|
||||
|
||||
"""
|
||||
supported_extension_aliases = ['qos']
|
||||
supported_extension_aliases = ['qos', 'qos-bw-limit-direction']
|
||||
|
||||
__native_pagination_support = True
|
||||
__native_sorting_support = True
|
||||
|
@ -374,11 +374,12 @@ class BaseNetworkTest(test.BaseTestCase):
|
||||
return qos_policy
|
||||
|
||||
@classmethod
|
||||
def create_qos_bandwidth_limit_rule(cls, policy_id,
|
||||
max_kbps, max_burst_kbps):
|
||||
def create_qos_bandwidth_limit_rule(cls, policy_id, max_kbps,
|
||||
max_burst_kbps,
|
||||
direction=constants.EGRESS_DIRECTION):
|
||||
"""Wrapper utility that returns a test QoS bandwidth limit rule."""
|
||||
body = cls.admin_client.create_bandwidth_limit_rule(
|
||||
policy_id, max_kbps, max_burst_kbps)
|
||||
policy_id, max_kbps, max_burst_kbps, direction)
|
||||
qos_rule = body['bandwidth_limit_rule']
|
||||
cls.qos_rules.append(qos_rule)
|
||||
return qos_rule
|
||||
|
@ -17,12 +17,16 @@ from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
from tempest import test
|
||||
|
||||
import testscenarios
|
||||
import testtools
|
||||
|
||||
from neutron.services.qos import qos_consts
|
||||
from neutron.tests.tempest.api import base
|
||||
|
||||
|
||||
load_tests = testscenarios.load_tests_apply_scenarios
|
||||
|
||||
|
||||
class QosTestJSON(base.BaseAdminNetworkTest):
|
||||
@classmethod
|
||||
@test.requires_ext(extension="qos", service="network")
|
||||
@ -360,20 +364,34 @@ class QosTestJSON(base.BaseAdminNetworkTest):
|
||||
|
||||
|
||||
class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
|
||||
direction = None
|
||||
|
||||
@classmethod
|
||||
@test.requires_ext(extension="qos", service="network")
|
||||
@base.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
|
||||
def resource_setup(cls):
|
||||
super(QosBandwidthLimitRuleTestJSON, cls).resource_setup()
|
||||
|
||||
@property
|
||||
def opposite_direction(self):
|
||||
if self.direction == "ingress":
|
||||
return "egress"
|
||||
elif self.direction == "egress":
|
||||
return "ingress"
|
||||
else:
|
||||
return None
|
||||
|
||||
@decorators.idempotent_id('8a59b00b-3e9c-4787-92f8-93a5cdf5e378')
|
||||
def test_rule_create(self):
|
||||
policy = self.create_qos_policy(name='test-policy',
|
||||
description='test policy',
|
||||
shared=False)
|
||||
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
||||
max_kbps=200,
|
||||
max_burst_kbps=1337)
|
||||
rule = self.create_qos_bandwidth_limit_rule(
|
||||
policy_id=policy['id'],
|
||||
max_kbps=200,
|
||||
max_burst_kbps=1337,
|
||||
direction=self.direction)
|
||||
|
||||
# Test 'show rule'
|
||||
retrieved_rule = self.admin_client.show_bandwidth_limit_rule(
|
||||
@ -382,6 +400,8 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
self.assertEqual(rule['id'], retrieved_rule['id'])
|
||||
self.assertEqual(200, retrieved_rule['max_kbps'])
|
||||
self.assertEqual(1337, retrieved_rule['max_burst_kbps'])
|
||||
if self.direction:
|
||||
self.assertEqual(self.direction, retrieved_rule['direction'])
|
||||
|
||||
# Test 'list rules'
|
||||
rules = self.admin_client.list_bandwidth_limit_rules(policy['id'])
|
||||
@ -404,12 +424,14 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
shared=False)
|
||||
self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
||||
max_kbps=200,
|
||||
max_burst_kbps=1337)
|
||||
max_burst_kbps=1337,
|
||||
direction=self.direction)
|
||||
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.create_qos_bandwidth_limit_rule,
|
||||
policy_id=policy['id'],
|
||||
max_kbps=201, max_burst_kbps=1338)
|
||||
max_kbps=201, max_burst_kbps=1338,
|
||||
direction=self.direction)
|
||||
|
||||
@decorators.idempotent_id('149a6988-2568-47d2-931e-2dbc858943b3')
|
||||
def test_rule_update(self):
|
||||
@ -418,18 +440,24 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
shared=False)
|
||||
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
||||
max_kbps=1,
|
||||
max_burst_kbps=1)
|
||||
max_burst_kbps=1,
|
||||
direction=self.direction)
|
||||
|
||||
self.admin_client.update_bandwidth_limit_rule(policy['id'],
|
||||
rule['id'],
|
||||
max_kbps=200,
|
||||
max_burst_kbps=1337)
|
||||
self.admin_client.update_bandwidth_limit_rule(
|
||||
policy['id'],
|
||||
rule['id'],
|
||||
max_kbps=200,
|
||||
max_burst_kbps=1337,
|
||||
direction=self.opposite_direction)
|
||||
|
||||
retrieved_policy = self.admin_client.show_bandwidth_limit_rule(
|
||||
policy['id'], rule['id'])
|
||||
retrieved_policy = retrieved_policy['bandwidth_limit_rule']
|
||||
self.assertEqual(200, retrieved_policy['max_kbps'])
|
||||
self.assertEqual(1337, retrieved_policy['max_burst_kbps'])
|
||||
if self.opposite_direction:
|
||||
self.assertEqual(self.opposite_direction,
|
||||
retrieved_policy['direction'])
|
||||
|
||||
@decorators.idempotent_id('67ee6efd-7b33-4a68-927d-275b4f8ba958')
|
||||
def test_rule_delete(self):
|
||||
@ -437,7 +465,7 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
description='test policy',
|
||||
shared=False)
|
||||
rule = self.admin_client.create_bandwidth_limit_rule(
|
||||
policy['id'], 200, 1337)['bandwidth_limit_rule']
|
||||
policy['id'], 200, 1337, self.direction)['bandwidth_limit_rule']
|
||||
|
||||
retrieved_policy = self.admin_client.show_bandwidth_limit_rule(
|
||||
policy['id'], rule['id'])
|
||||
@ -454,14 +482,14 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.create_qos_bandwidth_limit_rule,
|
||||
'policy', 200, 1337)
|
||||
'policy', 200, 1337, self.direction)
|
||||
|
||||
@decorators.idempotent_id('a4a2e7ad-786f-4927-a85a-e545a93bd274')
|
||||
def test_rule_create_forbidden_for_regular_tenants(self):
|
||||
self.assertRaises(
|
||||
exceptions.Forbidden,
|
||||
self.client.create_bandwidth_limit_rule,
|
||||
'policy', 1, 2)
|
||||
'policy', 1, 2, self.direction)
|
||||
|
||||
@decorators.idempotent_id('1bfc55d9-6fd8-4293-ab3a-b1d69bf7cd2e')
|
||||
def test_rule_update_forbidden_for_regular_tenants_own_policy(self):
|
||||
@ -471,7 +499,8 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
tenant_id=self.client.tenant_id)
|
||||
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
||||
max_kbps=1,
|
||||
max_burst_kbps=1)
|
||||
max_burst_kbps=1,
|
||||
direction=self.direction)
|
||||
self.assertRaises(
|
||||
exceptions.Forbidden,
|
||||
self.client.update_bandwidth_limit_rule,
|
||||
@ -485,7 +514,8 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
tenant_id=self.admin_client.tenant_id)
|
||||
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
||||
max_kbps=1,
|
||||
max_burst_kbps=1)
|
||||
max_burst_kbps=1,
|
||||
direction=self.direction)
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.client.update_bandwidth_limit_rule,
|
||||
@ -498,14 +528,16 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
shared=False)
|
||||
rule1 = self.create_qos_bandwidth_limit_rule(policy_id=policy1['id'],
|
||||
max_kbps=200,
|
||||
max_burst_kbps=1337)
|
||||
max_burst_kbps=1337,
|
||||
direction=self.direction)
|
||||
|
||||
policy2 = self.create_qos_policy(name='test-policy2',
|
||||
description='test policy2',
|
||||
shared=False)
|
||||
rule2 = self.create_qos_bandwidth_limit_rule(policy_id=policy2['id'],
|
||||
max_kbps=5000,
|
||||
max_burst_kbps=2523)
|
||||
max_burst_kbps=2523,
|
||||
direction=self.direction)
|
||||
|
||||
# Test 'list rules'
|
||||
rules = self.admin_client.list_bandwidth_limit_rules(policy1['id'])
|
||||
@ -515,6 +547,20 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
self.assertNotIn(rule2['id'], rules_ids)
|
||||
|
||||
|
||||
class QosBandwidthLimitRuleWithDirectionTestJSON(
|
||||
QosBandwidthLimitRuleTestJSON):
|
||||
|
||||
scenarios = [
|
||||
('ingress', {'direction': 'ingress'}),
|
||||
('egress', {'direction': 'egress'}),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@test.requires_ext(extension="qos-bw-limit-direction", service="network")
|
||||
def resource_setup(cls):
|
||||
super(QosBandwidthLimitRuleWithDirectionTestJSON, cls).resource_setup()
|
||||
|
||||
|
||||
class RbacSharedQosPoliciesTest(base.BaseAdminNetworkTest):
|
||||
|
||||
force_tenant_isolation = True
|
||||
|
@ -577,16 +577,19 @@ class NetworkClientJSON(service_client.RestClient):
|
||||
self.expected_success(200, resp.status)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def create_bandwidth_limit_rule(self, policy_id, max_kbps, max_burst_kbps):
|
||||
def create_bandwidth_limit_rule(self, policy_id, max_kbps,
|
||||
max_burst_kbps, direction=None):
|
||||
uri = '%s/qos/policies/%s/bandwidth_limit_rules' % (
|
||||
self.uri_prefix, policy_id)
|
||||
post_data = self.serialize({
|
||||
post_data = {
|
||||
'bandwidth_limit_rule': {
|
||||
'max_kbps': max_kbps,
|
||||
'max_burst_kbps': max_burst_kbps
|
||||
}
|
||||
})
|
||||
resp, body = self.post(uri, post_data)
|
||||
}
|
||||
if direction:
|
||||
post_data['bandwidth_limit_rule']['direction'] = direction
|
||||
resp, body = self.post(uri, self.serialize(post_data))
|
||||
self.expected_success(201, resp.status)
|
||||
body = jsonutils.loads(body)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
@ -610,6 +613,8 @@ class NetworkClientJSON(service_client.RestClient):
|
||||
def update_bandwidth_limit_rule(self, policy_id, rule_id, **kwargs):
|
||||
uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % (
|
||||
self.uri_prefix, policy_id, rule_id)
|
||||
if "direction" in kwargs and kwargs['direction'] is None:
|
||||
kwargs.pop('direction')
|
||||
post_data = {'bandwidth_limit_rule': kwargs}
|
||||
resp, body = self.put(uri, jsonutils.dumps(post_data))
|
||||
body = self.deserialize_single(body)
|
||||
|
@ -14,6 +14,7 @@ import mock
|
||||
from oslo_versionedobjects import exception
|
||||
import testtools
|
||||
|
||||
from neutron.common import constants as n_const
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.db import models_v2
|
||||
from neutron.objects.db import api as db_api
|
||||
@ -132,13 +133,17 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
self.objs[0].create()
|
||||
return self.objs[0]
|
||||
|
||||
def _create_test_policy_with_rules(self, rule_type, reload_rules=False):
|
||||
def _create_test_policy_with_rules(self, rule_type, reload_rules=False,
|
||||
bwlimit_direction=None):
|
||||
policy_obj = self._create_test_policy()
|
||||
rules = []
|
||||
for obj_cls in (RULE_OBJ_CLS.get(rule_type)
|
||||
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
|
||||
bwlimit_direction is not None):
|
||||
rule_fields['direction'] = bwlimit_direction
|
||||
rule_obj = obj_cls(self.context, **rule_fields)
|
||||
rule_obj.create()
|
||||
rules.append(rule_obj)
|
||||
@ -380,11 +385,11 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
policy_obj, rule_objs = self._create_test_policy_with_rules(
|
||||
RULE_OBJ_CLS.keys(), reload_rules=True)
|
||||
|
||||
policy_obj_v1_2 = self._policy_through_version(
|
||||
policy_obj_v1_5 = self._policy_through_version(
|
||||
policy_obj, policy.QosPolicy.VERSION)
|
||||
|
||||
for rule_obj in rule_objs:
|
||||
self.assertIn(rule_obj, policy_obj_v1_2.rules)
|
||||
self.assertIn(rule_obj, policy_obj_v1_5.rules)
|
||||
|
||||
def test_object_version_degradation_1_3_to_1_2_null_description(self):
|
||||
policy_obj = self._create_test_policy()
|
||||
@ -398,7 +403,8 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
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], reload_rules=True)
|
||||
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH],
|
||||
reload_rules=True, bwlimit_direction=n_const.EGRESS_DIRECTION)
|
||||
|
||||
policy_obj_v1_0 = self._policy_through_version(policy_obj, '1.0')
|
||||
|
||||
@ -412,7 +418,8 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
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], reload_rules=True)
|
||||
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH],
|
||||
reload_rules=True, bwlimit_direction=n_const.EGRESS_DIRECTION)
|
||||
|
||||
policy_obj_v1_1 = self._policy_through_version(policy_obj, '1.1')
|
||||
|
||||
@ -426,12 +433,14 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
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], reload_rules=True)
|
||||
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH],
|
||||
reload_rules=True, bwlimit_direction=n_const.EGRESS_DIRECTION)
|
||||
|
||||
policy_obj_v1_2 = self._policy_through_version(policy_obj, '1.2')
|
||||
|
||||
for rule_obj in rule_objs:
|
||||
self.assertIn(rule_obj, policy_obj_v1_2.rules)
|
||||
self.assertIn(rule_objs[0], policy_obj_v1_2.rules)
|
||||
self.assertIn(rule_objs[1], policy_obj_v1_2.rules)
|
||||
self.assertIn(rule_objs[2], policy_obj_v1_2.rules)
|
||||
|
||||
def test_v1_4_to_v1_3_drops_project_id(self):
|
||||
policy_new = self._create_test_policy()
|
||||
@ -440,6 +449,32 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
self.assertNotIn('project_id', policy_v1_3['versioned_object.data'])
|
||||
self.assertIn('tenant_id', policy_v1_3['versioned_object.data'])
|
||||
|
||||
def test_object_version_degradation_1_5_to_1_4_ingress_direction(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],
|
||||
reload_rules=True, bwlimit_direction=n_const.INGRESS_DIRECTION)
|
||||
|
||||
policy_obj_v1_4 = self._policy_through_version(policy_obj, '1.4')
|
||||
|
||||
self.assertNotIn(rule_objs[0], policy_obj_v1_4.rules)
|
||||
self.assertIn(rule_objs[1], policy_obj_v1_4.rules)
|
||||
self.assertIn(rule_objs[2], policy_obj_v1_4.rules)
|
||||
|
||||
def test_object_version_degradation_1_5_to_1_4_egress_direction(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],
|
||||
reload_rules=True, bwlimit_direction=n_const.EGRESS_DIRECTION)
|
||||
|
||||
policy_obj_v1_4 = self._policy_through_version(policy_obj, '1.4')
|
||||
|
||||
self.assertIn(rule_objs[0], policy_obj_v1_4.rules)
|
||||
self.assertIn(rule_objs[1], policy_obj_v1_4.rules)
|
||||
self.assertIn(rule_objs[2], policy_obj_v1_4.rules)
|
||||
|
||||
def test_filter_by_shared(self):
|
||||
policy_obj = policy.QosPolicy(
|
||||
self.context, name='shared-policy', shared=True)
|
||||
|
@ -14,6 +14,7 @@ from neutron_lib import constants
|
||||
|
||||
from oslo_versionedobjects import exception
|
||||
|
||||
from neutron.common import constants as n_const
|
||||
from neutron.objects.qos import policy
|
||||
from neutron.objects.qos import rule
|
||||
from neutron.services.qos import qos_consts
|
||||
@ -117,6 +118,25 @@ class QosBandwidthLimitRuleObjectTestCase(test_base.BaseObjectIfaceTestCase):
|
||||
dict_ = obj.to_dict()
|
||||
self.assertEqual(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, dict_['type'])
|
||||
|
||||
def test_bandwidth_limit_object_version_degradation(self):
|
||||
self.db_objs[0]['direction'] = n_const.EGRESS_DIRECTION
|
||||
rule_obj = rule.QosBandwidthLimitRule(self.context, **self.db_objs[0])
|
||||
primitive_rule = rule_obj.obj_to_primitive('1.2')
|
||||
self.assertNotIn(
|
||||
"direction", primitive_rule['versioned_object.data'].keys())
|
||||
self.assertEqual(
|
||||
self.db_objs[0]['max_kbps'],
|
||||
primitive_rule['versioned_object.data']['max_kbps'])
|
||||
self.assertEqual(
|
||||
self.db_objs[0]['max_burst_kbps'],
|
||||
primitive_rule['versioned_object.data']['max_burst_kbps'])
|
||||
|
||||
self.db_objs[0]['direction'] = n_const.INGRESS_DIRECTION
|
||||
rule_obj = rule.QosBandwidthLimitRule(self.context, **self.db_objs[0])
|
||||
self.assertRaises(
|
||||
exception.IncompatibleObjectVersion,
|
||||
rule_obj.obj_to_primitive, '1.2')
|
||||
|
||||
|
||||
class QosBandwidthLimitRuleDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
testlib_api.SqlTestCase):
|
||||
|
@ -60,11 +60,11 @@ object_data = {
|
||||
'PortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',
|
||||
'ProviderResourceAssociation': '1.0-05ab2d5a3017e5ce9dd381328f285f34',
|
||||
'ProvisioningBlock': '1.0-c19d6d05bfa8143533471c1296066125',
|
||||
'QosBandwidthLimitRule': '1.2-4e44a8f5c2895ab1278399f87b40a13d',
|
||||
'QosDscpMarkingRule': '1.2-0313c6554b34fd10c753cb63d638256c',
|
||||
'QosMinimumBandwidthRule': '1.2-314c3419f4799067cc31cc319080adff',
|
||||
'QosBandwidthLimitRule': '1.3-51b662b12a8d1dfa89288d826c6d26d3',
|
||||
'QosDscpMarkingRule': '1.3-0313c6554b34fd10c753cb63d638256c',
|
||||
'QosMinimumBandwidthRule': '1.3-314c3419f4799067cc31cc319080adff',
|
||||
'QosRuleType': '1.2-e6fd08fcca152c339cbd5e9b94b1b8e7',
|
||||
'QosPolicy': '1.4-50460f619c34428ec5651916e938e5a0',
|
||||
'QosPolicy': '1.5-50460f619c34428ec5651916e938e5a0',
|
||||
'Quota': '1.0-6bb6a0f1bd5d66a2134ffa1a61873097',
|
||||
'QuotaUsage': '1.0-6fbf820368681aac7c5d664662605cf9',
|
||||
'Reservation': '1.0-49929fef8e82051660342eed51b48f2a',
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- The QoS service plugin now supports new attribute in
|
||||
``qos_bandwidth_limit_rule``. This new parameter is called
|
||||
``direction`` and allows to specify direction of traffic
|
||||
for which the limit should be applied.
|
Loading…
x
Reference in New Issue
Block a user