Merge "Add QoS bandwidth limit for instance ingress traffic"
This commit is contained in:
commit
90b01bb6ca
neutron
db
migration/alembic_migrations/versions
qos
extensions
objects/qos
services/qos
tests
tempest
unit/objects
releasenotes/notes
@ -1 +1 @@
|
||||
804a3c76314c
|
||||
2b42d90729da
|
||||
|
81
neutron/db/migration/alembic_migrations/versions/pike/expand/2b42d90729da_qos_add_direction_to_bw_limit_rule_table.py
Normal file
81
neutron/db/migration/alembic_migrations/versions/pike/expand/2b42d90729da_qos_add_direction_to_bw_limit_rule_table.py
Normal file
@ -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: {
|
||||
|
@ -37,7 +37,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):
|
||||
|
@ -62,11 +62,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