Merge "Add QoS bandwidth limit for instance ingress traffic"
This commit is contained in:
commit
90b01bb6ca
|
@ -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),
|
qos_policy_id = sa.Column(sa.String(36),
|
||||||
sa.ForeignKey('qos_policies.id',
|
sa.ForeignKey('qos_policies.id',
|
||||||
ondelete='CASCADE'),
|
ondelete='CASCADE'),
|
||||||
nullable=False,
|
nullable=False)
|
||||||
unique=True)
|
|
||||||
max_kbps = sa.Column(sa.Integer)
|
max_kbps = sa.Column(sa.Integer)
|
||||||
max_burst_kbps = sa.Column(sa.Integer)
|
max_burst_kbps = sa.Column(sa.Integer)
|
||||||
revises_on_change = ('qos_policy', )
|
revises_on_change = ('qos_policy', )
|
||||||
qos_policy = sa.orm.relationship(QosPolicy, load_on_pending=True)
|
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):
|
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.plugins.common import constants
|
||||||
from neutron.services.qos import qos_consts
|
from neutron.services.qos import qos_consts
|
||||||
|
|
||||||
|
ALIAS = "qos"
|
||||||
QOS_PREFIX = "/qos"
|
QOS_PREFIX = "/qos"
|
||||||
|
|
||||||
# Attribute Map
|
# Attribute Map
|
||||||
|
@ -74,8 +75,10 @@ RESOURCE_ATTRIBUTE_MAP = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BANDWIDTH_LIMIT_RULES = "bandwidth_limit_rules"
|
||||||
|
|
||||||
SUB_RESOURCE_ATTRIBUTE_MAP = {
|
SUB_RESOURCE_ATTRIBUTE_MAP = {
|
||||||
'bandwidth_limit_rules': {
|
BANDWIDTH_LIMIT_RULES: {
|
||||||
'parent': {'collection_name': 'policies',
|
'parent': {'collection_name': 'policies',
|
||||||
'member_name': 'policy'},
|
'member_name': 'policy'},
|
||||||
'parameters': dict(QOS_RULE_COMMON_FIELDS,
|
'parameters': dict(QOS_RULE_COMMON_FIELDS,
|
||||||
|
@ -88,7 +91,7 @@ SUB_RESOURCE_ATTRIBUTE_MAP = {
|
||||||
'allow_post': True, 'allow_put': True,
|
'allow_post': True, 'allow_put': True,
|
||||||
'is_visible': True, 'default': 0,
|
'is_visible': True, 'default': 0,
|
||||||
'validate': {'type:range': [0,
|
'validate': {'type:range': [0,
|
||||||
common_constants.DB_INTEGER_MAX_VALUE]}}})
|
common_constants.DB_INTEGER_MAX_VALUE]}}}),
|
||||||
},
|
},
|
||||||
'dscp_marking_rules': {
|
'dscp_marking_rules': {
|
||||||
'parent': {'collection_name': 'policies',
|
'parent': {'collection_name': 'policies',
|
||||||
|
@ -196,12 +199,15 @@ class Qos(api_extensions.ExtensionDescriptor):
|
||||||
|
|
||||||
def update_attributes_map(self, attributes, extension_attrs_map=None):
|
def update_attributes_map(self, attributes, extension_attrs_map=None):
|
||||||
super(Qos, self).update_attributes_map(
|
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):
|
def get_extended_resources(self, version):
|
||||||
if version == "2.0":
|
if version == "2.0":
|
||||||
return dict(list(EXTENDED_ATTRIBUTES_2_0.items()) +
|
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:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
|
@ -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 oslo_versionedobjects import fields as obj_fields
|
||||||
|
|
||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
|
from neutron.common import constants as n_const
|
||||||
from neutron.common import exceptions
|
from neutron.common import exceptions
|
||||||
from neutron.db import api as db_api
|
from neutron.db import api as db_api
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
|
@ -40,7 +41,8 @@ class QosPolicy(rbac_db.NeutronRbacObject):
|
||||||
# Version 1.2: Added QosMinimumBandwidthRule
|
# Version 1.2: Added QosMinimumBandwidthRule
|
||||||
# Version 1.3: Added standard attributes (created_at, revision, etc)
|
# Version 1.3: Added standard attributes (created_at, revision, etc)
|
||||||
# Version 1.4: Changed tenant_id to project_id
|
# 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
|
# required by RbacNeutronMetaclass
|
||||||
rbac_db_model = QosPolicyRBAC
|
rbac_db_model = QosPolicyRBAC
|
||||||
|
@ -222,6 +224,19 @@ class QosPolicy(rbac_db.NeutronRbacObject):
|
||||||
return [rule for rule in rules if
|
return [rule for rule in rules if
|
||||||
rule['versioned_object.name'] in obj_names]
|
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)
|
_target_version = versionutils.convert_version_to_tuple(target_version)
|
||||||
names = []
|
names = []
|
||||||
if _target_version >= (1, 0):
|
if _target_version >= (1, 0):
|
||||||
|
@ -244,3 +259,8 @@ class QosPolicy(rbac_db.NeutronRbacObject):
|
||||||
|
|
||||||
if _target_version < (1, 4):
|
if _target_version < (1, 4):
|
||||||
primitive['tenant_id'] = primitive.pop('project_id')
|
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
|
from oslo_versionedobjects import fields as obj_fields
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from neutron.common import constants as n_const
|
||||||
from neutron.db import api as db_api
|
from neutron.db import api as db_api
|
||||||
from neutron.db.qos import models as qos_db_model
|
from neutron.db.qos import models as qos_db_model
|
||||||
from neutron.objects import base
|
from neutron.objects import base
|
||||||
|
@ -50,11 +51,12 @@ class QosRule(base.NeutronDbObject):
|
||||||
# Version 1.0: Initial version, only BandwidthLimitRule
|
# Version 1.0: Initial version, only BandwidthLimitRule
|
||||||
# 1.1: Added DscpMarkingRule
|
# 1.1: Added DscpMarkingRule
|
||||||
# 1.2: Added QosMinimumBandwidthRule
|
# 1.2: Added QosMinimumBandwidthRule
|
||||||
|
# 1.3: Added direction for BandwidthLimitRule
|
||||||
#
|
#
|
||||||
#NOTE(mangelajo): versions need to be handled from the top QosRule object
|
#NOTE(mangelajo): versions need to be handled from the top QosRule object
|
||||||
# because it's the only reference QosPolicy can make
|
# because it's the only reference QosPolicy can make
|
||||||
# to them via obj_relationships version map
|
# to them via obj_relationships version map
|
||||||
VERSION = '1.2'
|
VERSION = '1.3'
|
||||||
|
|
||||||
fields = {
|
fields = {
|
||||||
'id': common_types.UUIDField(),
|
'id': common_types.UUIDField(),
|
||||||
|
@ -106,11 +108,22 @@ class QosBandwidthLimitRule(QosRule):
|
||||||
|
|
||||||
fields = {
|
fields = {
|
||||||
'max_kbps': obj_fields.IntegerField(nullable=True),
|
'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
|
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
|
@obj_base.VersionedObjectRegistry.register
|
||||||
class QosDscpMarkingRule(QosRule):
|
class QosDscpMarkingRule(QosRule):
|
||||||
|
|
|
@ -29,7 +29,9 @@ SUPPORTED_RULES = {
|
||||||
qos_consts.MAX_KBPS: {
|
qos_consts.MAX_KBPS: {
|
||||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
||||||
qos_consts.MAX_BURST: {
|
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.RULE_TYPE_DSCP_MARKING: {
|
||||||
qos_consts.DSCP_MARK: {'type:values': constants.VALID_DSCP_MARKS}
|
qos_consts.DSCP_MARK: {'type:values': constants.VALID_DSCP_MARKS}
|
||||||
|
|
|
@ -29,7 +29,9 @@ SUPPORTED_RULES = {
|
||||||
qos_consts.MAX_KBPS: {
|
qos_consts.MAX_KBPS: {
|
||||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
||||||
qos_consts.MAX_BURST: {
|
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.RULE_TYPE_DSCP_MARKING: {
|
||||||
qos_consts.DSCP_MARK: {'type:values': constants.VALID_DSCP_MARKS}
|
qos_consts.DSCP_MARK: {'type:values': constants.VALID_DSCP_MARKS}
|
||||||
|
|
|
@ -29,7 +29,9 @@ SUPPORTED_RULES = {
|
||||||
qos_consts.MAX_KBPS: {
|
qos_consts.MAX_KBPS: {
|
||||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
||||||
qos_consts.MAX_BURST: {
|
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.RULE_TYPE_MINIMUM_BANDWIDTH: {
|
||||||
qos_consts.MIN_KBPS: {
|
qos_consts.MIN_KBPS: {
|
||||||
|
|
|
@ -37,7 +37,7 @@ class QoSPlugin(qos.QoSPluginBase):
|
||||||
service parameters over ports and networks.
|
service parameters over ports and networks.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
supported_extension_aliases = ['qos']
|
supported_extension_aliases = ['qos', 'qos-bw-limit-direction']
|
||||||
|
|
||||||
__native_pagination_support = True
|
__native_pagination_support = True
|
||||||
__native_sorting_support = True
|
__native_sorting_support = True
|
||||||
|
|
|
@ -374,11 +374,12 @@ class BaseNetworkTest(test.BaseTestCase):
|
||||||
return qos_policy
|
return qos_policy
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_qos_bandwidth_limit_rule(cls, policy_id,
|
def create_qos_bandwidth_limit_rule(cls, policy_id, max_kbps,
|
||||||
max_kbps, max_burst_kbps):
|
max_burst_kbps,
|
||||||
|
direction=constants.EGRESS_DIRECTION):
|
||||||
"""Wrapper utility that returns a test QoS bandwidth limit rule."""
|
"""Wrapper utility that returns a test QoS bandwidth limit rule."""
|
||||||
body = cls.admin_client.create_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']
|
qos_rule = body['bandwidth_limit_rule']
|
||||||
cls.qos_rules.append(qos_rule)
|
cls.qos_rules.append(qos_rule)
|
||||||
return qos_rule
|
return qos_rule
|
||||||
|
|
|
@ -17,12 +17,16 @@ from tempest.lib import decorators
|
||||||
from tempest.lib import exceptions
|
from tempest.lib import exceptions
|
||||||
from tempest import test
|
from tempest import test
|
||||||
|
|
||||||
|
import testscenarios
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from neutron.services.qos import qos_consts
|
from neutron.services.qos import qos_consts
|
||||||
from neutron.tests.tempest.api import base
|
from neutron.tests.tempest.api import base
|
||||||
|
|
||||||
|
|
||||||
|
load_tests = testscenarios.load_tests_apply_scenarios
|
||||||
|
|
||||||
|
|
||||||
class QosTestJSON(base.BaseAdminNetworkTest):
|
class QosTestJSON(base.BaseAdminNetworkTest):
|
||||||
@classmethod
|
@classmethod
|
||||||
@test.requires_ext(extension="qos", service="network")
|
@test.requires_ext(extension="qos", service="network")
|
||||||
|
@ -360,20 +364,34 @@ class QosTestJSON(base.BaseAdminNetworkTest):
|
||||||
|
|
||||||
|
|
||||||
class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||||
|
|
||||||
|
direction = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@test.requires_ext(extension="qos", service="network")
|
@test.requires_ext(extension="qos", service="network")
|
||||||
@base.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
|
@base.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
|
||||||
def resource_setup(cls):
|
def resource_setup(cls):
|
||||||
super(QosBandwidthLimitRuleTestJSON, cls).resource_setup()
|
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')
|
@decorators.idempotent_id('8a59b00b-3e9c-4787-92f8-93a5cdf5e378')
|
||||||
def test_rule_create(self):
|
def test_rule_create(self):
|
||||||
policy = self.create_qos_policy(name='test-policy',
|
policy = self.create_qos_policy(name='test-policy',
|
||||||
description='test policy',
|
description='test policy',
|
||||||
shared=False)
|
shared=False)
|
||||||
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
rule = self.create_qos_bandwidth_limit_rule(
|
||||||
max_kbps=200,
|
policy_id=policy['id'],
|
||||||
max_burst_kbps=1337)
|
max_kbps=200,
|
||||||
|
max_burst_kbps=1337,
|
||||||
|
direction=self.direction)
|
||||||
|
|
||||||
# Test 'show rule'
|
# Test 'show rule'
|
||||||
retrieved_rule = self.admin_client.show_bandwidth_limit_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(rule['id'], retrieved_rule['id'])
|
||||||
self.assertEqual(200, retrieved_rule['max_kbps'])
|
self.assertEqual(200, retrieved_rule['max_kbps'])
|
||||||
self.assertEqual(1337, retrieved_rule['max_burst_kbps'])
|
self.assertEqual(1337, retrieved_rule['max_burst_kbps'])
|
||||||
|
if self.direction:
|
||||||
|
self.assertEqual(self.direction, retrieved_rule['direction'])
|
||||||
|
|
||||||
# Test 'list rules'
|
# Test 'list rules'
|
||||||
rules = self.admin_client.list_bandwidth_limit_rules(policy['id'])
|
rules = self.admin_client.list_bandwidth_limit_rules(policy['id'])
|
||||||
|
@ -404,12 +424,14 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||||
shared=False)
|
shared=False)
|
||||||
self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
||||||
max_kbps=200,
|
max_kbps=200,
|
||||||
max_burst_kbps=1337)
|
max_burst_kbps=1337,
|
||||||
|
direction=self.direction)
|
||||||
|
|
||||||
self.assertRaises(exceptions.Conflict,
|
self.assertRaises(exceptions.Conflict,
|
||||||
self.create_qos_bandwidth_limit_rule,
|
self.create_qos_bandwidth_limit_rule,
|
||||||
policy_id=policy['id'],
|
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')
|
@decorators.idempotent_id('149a6988-2568-47d2-931e-2dbc858943b3')
|
||||||
def test_rule_update(self):
|
def test_rule_update(self):
|
||||||
|
@ -418,18 +440,24 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||||
shared=False)
|
shared=False)
|
||||||
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
||||||
max_kbps=1,
|
max_kbps=1,
|
||||||
max_burst_kbps=1)
|
max_burst_kbps=1,
|
||||||
|
direction=self.direction)
|
||||||
|
|
||||||
self.admin_client.update_bandwidth_limit_rule(policy['id'],
|
self.admin_client.update_bandwidth_limit_rule(
|
||||||
rule['id'],
|
policy['id'],
|
||||||
max_kbps=200,
|
rule['id'],
|
||||||
max_burst_kbps=1337)
|
max_kbps=200,
|
||||||
|
max_burst_kbps=1337,
|
||||||
|
direction=self.opposite_direction)
|
||||||
|
|
||||||
retrieved_policy = self.admin_client.show_bandwidth_limit_rule(
|
retrieved_policy = self.admin_client.show_bandwidth_limit_rule(
|
||||||
policy['id'], rule['id'])
|
policy['id'], rule['id'])
|
||||||
retrieved_policy = retrieved_policy['bandwidth_limit_rule']
|
retrieved_policy = retrieved_policy['bandwidth_limit_rule']
|
||||||
self.assertEqual(200, retrieved_policy['max_kbps'])
|
self.assertEqual(200, retrieved_policy['max_kbps'])
|
||||||
self.assertEqual(1337, retrieved_policy['max_burst_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')
|
@decorators.idempotent_id('67ee6efd-7b33-4a68-927d-275b4f8ba958')
|
||||||
def test_rule_delete(self):
|
def test_rule_delete(self):
|
||||||
|
@ -437,7 +465,7 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||||
description='test policy',
|
description='test policy',
|
||||||
shared=False)
|
shared=False)
|
||||||
rule = self.admin_client.create_bandwidth_limit_rule(
|
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(
|
retrieved_policy = self.admin_client.show_bandwidth_limit_rule(
|
||||||
policy['id'], rule['id'])
|
policy['id'], rule['id'])
|
||||||
|
@ -454,14 +482,14 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exceptions.NotFound,
|
exceptions.NotFound,
|
||||||
self.create_qos_bandwidth_limit_rule,
|
self.create_qos_bandwidth_limit_rule,
|
||||||
'policy', 200, 1337)
|
'policy', 200, 1337, self.direction)
|
||||||
|
|
||||||
@decorators.idempotent_id('a4a2e7ad-786f-4927-a85a-e545a93bd274')
|
@decorators.idempotent_id('a4a2e7ad-786f-4927-a85a-e545a93bd274')
|
||||||
def test_rule_create_forbidden_for_regular_tenants(self):
|
def test_rule_create_forbidden_for_regular_tenants(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exceptions.Forbidden,
|
exceptions.Forbidden,
|
||||||
self.client.create_bandwidth_limit_rule,
|
self.client.create_bandwidth_limit_rule,
|
||||||
'policy', 1, 2)
|
'policy', 1, 2, self.direction)
|
||||||
|
|
||||||
@decorators.idempotent_id('1bfc55d9-6fd8-4293-ab3a-b1d69bf7cd2e')
|
@decorators.idempotent_id('1bfc55d9-6fd8-4293-ab3a-b1d69bf7cd2e')
|
||||||
def test_rule_update_forbidden_for_regular_tenants_own_policy(self):
|
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)
|
tenant_id=self.client.tenant_id)
|
||||||
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
||||||
max_kbps=1,
|
max_kbps=1,
|
||||||
max_burst_kbps=1)
|
max_burst_kbps=1,
|
||||||
|
direction=self.direction)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exceptions.Forbidden,
|
exceptions.Forbidden,
|
||||||
self.client.update_bandwidth_limit_rule,
|
self.client.update_bandwidth_limit_rule,
|
||||||
|
@ -485,7 +514,8 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||||
tenant_id=self.admin_client.tenant_id)
|
tenant_id=self.admin_client.tenant_id)
|
||||||
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
|
||||||
max_kbps=1,
|
max_kbps=1,
|
||||||
max_burst_kbps=1)
|
max_burst_kbps=1,
|
||||||
|
direction=self.direction)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exceptions.NotFound,
|
exceptions.NotFound,
|
||||||
self.client.update_bandwidth_limit_rule,
|
self.client.update_bandwidth_limit_rule,
|
||||||
|
@ -498,14 +528,16 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||||
shared=False)
|
shared=False)
|
||||||
rule1 = self.create_qos_bandwidth_limit_rule(policy_id=policy1['id'],
|
rule1 = self.create_qos_bandwidth_limit_rule(policy_id=policy1['id'],
|
||||||
max_kbps=200,
|
max_kbps=200,
|
||||||
max_burst_kbps=1337)
|
max_burst_kbps=1337,
|
||||||
|
direction=self.direction)
|
||||||
|
|
||||||
policy2 = self.create_qos_policy(name='test-policy2',
|
policy2 = self.create_qos_policy(name='test-policy2',
|
||||||
description='test policy2',
|
description='test policy2',
|
||||||
shared=False)
|
shared=False)
|
||||||
rule2 = self.create_qos_bandwidth_limit_rule(policy_id=policy2['id'],
|
rule2 = self.create_qos_bandwidth_limit_rule(policy_id=policy2['id'],
|
||||||
max_kbps=5000,
|
max_kbps=5000,
|
||||||
max_burst_kbps=2523)
|
max_burst_kbps=2523,
|
||||||
|
direction=self.direction)
|
||||||
|
|
||||||
# Test 'list rules'
|
# Test 'list rules'
|
||||||
rules = self.admin_client.list_bandwidth_limit_rules(policy1['id'])
|
rules = self.admin_client.list_bandwidth_limit_rules(policy1['id'])
|
||||||
|
@ -515,6 +547,20 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||||
self.assertNotIn(rule2['id'], rules_ids)
|
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):
|
class RbacSharedQosPoliciesTest(base.BaseAdminNetworkTest):
|
||||||
|
|
||||||
force_tenant_isolation = True
|
force_tenant_isolation = True
|
||||||
|
|
|
@ -577,16 +577,19 @@ class NetworkClientJSON(service_client.RestClient):
|
||||||
self.expected_success(200, resp.status)
|
self.expected_success(200, resp.status)
|
||||||
return service_client.ResponseBody(resp, body)
|
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' % (
|
uri = '%s/qos/policies/%s/bandwidth_limit_rules' % (
|
||||||
self.uri_prefix, policy_id)
|
self.uri_prefix, policy_id)
|
||||||
post_data = self.serialize({
|
post_data = {
|
||||||
'bandwidth_limit_rule': {
|
'bandwidth_limit_rule': {
|
||||||
'max_kbps': max_kbps,
|
'max_kbps': max_kbps,
|
||||||
'max_burst_kbps': max_burst_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)
|
self.expected_success(201, resp.status)
|
||||||
body = jsonutils.loads(body)
|
body = jsonutils.loads(body)
|
||||||
return service_client.ResponseBody(resp, 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):
|
def update_bandwidth_limit_rule(self, policy_id, rule_id, **kwargs):
|
||||||
uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % (
|
uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % (
|
||||||
self.uri_prefix, policy_id, rule_id)
|
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}
|
post_data = {'bandwidth_limit_rule': kwargs}
|
||||||
resp, body = self.put(uri, jsonutils.dumps(post_data))
|
resp, body = self.put(uri, jsonutils.dumps(post_data))
|
||||||
body = self.deserialize_single(body)
|
body = self.deserialize_single(body)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import mock
|
||||||
from oslo_versionedobjects import exception
|
from oslo_versionedobjects import exception
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
|
from neutron.common import constants as n_const
|
||||||
from neutron.common import exceptions as n_exc
|
from neutron.common import exceptions as n_exc
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
from neutron.objects.db import api as db_api
|
from neutron.objects.db import api as db_api
|
||||||
|
@ -132,13 +133,17 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||||
self.objs[0].create()
|
self.objs[0].create()
|
||||||
return self.objs[0]
|
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()
|
policy_obj = self._create_test_policy()
|
||||||
rules = []
|
rules = []
|
||||||
for obj_cls in (RULE_OBJ_CLS.get(rule_type)
|
for obj_cls in (RULE_OBJ_CLS.get(rule_type)
|
||||||
for rule_type in rule_type):
|
for rule_type in rule_type):
|
||||||
rule_fields = self.get_random_object_fields(obj_cls=obj_cls)
|
rule_fields = self.get_random_object_fields(obj_cls=obj_cls)
|
||||||
rule_fields['qos_policy_id'] = policy_obj.id
|
rule_fields['qos_policy_id'] = policy_obj.id
|
||||||
|
if (obj_cls.rule_type == qos_consts.RULE_TYPE_BANDWIDTH_LIMIT and
|
||||||
|
bwlimit_direction is not None):
|
||||||
|
rule_fields['direction'] = bwlimit_direction
|
||||||
rule_obj = obj_cls(self.context, **rule_fields)
|
rule_obj = obj_cls(self.context, **rule_fields)
|
||||||
rule_obj.create()
|
rule_obj.create()
|
||||||
rules.append(rule_obj)
|
rules.append(rule_obj)
|
||||||
|
@ -380,11 +385,11 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||||
policy_obj, rule_objs = self._create_test_policy_with_rules(
|
policy_obj, rule_objs = self._create_test_policy_with_rules(
|
||||||
RULE_OBJ_CLS.keys(), reload_rules=True)
|
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)
|
policy_obj, policy.QosPolicy.VERSION)
|
||||||
|
|
||||||
for rule_obj in rule_objs:
|
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):
|
def test_object_version_degradation_1_3_to_1_2_null_description(self):
|
||||||
policy_obj = self._create_test_policy()
|
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(
|
policy_obj, rule_objs = self._create_test_policy_with_rules(
|
||||||
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
|
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
|
||||||
qos_consts.RULE_TYPE_DSCP_MARKING,
|
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')
|
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(
|
policy_obj, rule_objs = self._create_test_policy_with_rules(
|
||||||
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
|
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
|
||||||
qos_consts.RULE_TYPE_DSCP_MARKING,
|
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')
|
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(
|
policy_obj, rule_objs = self._create_test_policy_with_rules(
|
||||||
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
|
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
|
||||||
qos_consts.RULE_TYPE_DSCP_MARKING,
|
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')
|
policy_obj_v1_2 = self._policy_through_version(policy_obj, '1.2')
|
||||||
|
|
||||||
for rule_obj in rule_objs:
|
self.assertIn(rule_objs[0], policy_obj_v1_2.rules)
|
||||||
self.assertIn(rule_obj, 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):
|
def test_v1_4_to_v1_3_drops_project_id(self):
|
||||||
policy_new = self._create_test_policy()
|
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.assertNotIn('project_id', policy_v1_3['versioned_object.data'])
|
||||||
self.assertIn('tenant_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):
|
def test_filter_by_shared(self):
|
||||||
policy_obj = policy.QosPolicy(
|
policy_obj = policy.QosPolicy(
|
||||||
self.context, name='shared-policy', shared=True)
|
self.context, name='shared-policy', shared=True)
|
||||||
|
|
|
@ -14,6 +14,7 @@ from neutron_lib import constants
|
||||||
|
|
||||||
from oslo_versionedobjects import exception
|
from oslo_versionedobjects import exception
|
||||||
|
|
||||||
|
from neutron.common import constants as n_const
|
||||||
from neutron.objects.qos import policy
|
from neutron.objects.qos import policy
|
||||||
from neutron.objects.qos import rule
|
from neutron.objects.qos import rule
|
||||||
from neutron.services.qos import qos_consts
|
from neutron.services.qos import qos_consts
|
||||||
|
@ -117,6 +118,25 @@ class QosBandwidthLimitRuleObjectTestCase(test_base.BaseObjectIfaceTestCase):
|
||||||
dict_ = obj.to_dict()
|
dict_ = obj.to_dict()
|
||||||
self.assertEqual(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, dict_['type'])
|
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,
|
class QosBandwidthLimitRuleDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||||
testlib_api.SqlTestCase):
|
testlib_api.SqlTestCase):
|
||||||
|
|
|
@ -62,11 +62,11 @@ object_data = {
|
||||||
'PortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',
|
'PortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',
|
||||||
'ProviderResourceAssociation': '1.0-05ab2d5a3017e5ce9dd381328f285f34',
|
'ProviderResourceAssociation': '1.0-05ab2d5a3017e5ce9dd381328f285f34',
|
||||||
'ProvisioningBlock': '1.0-c19d6d05bfa8143533471c1296066125',
|
'ProvisioningBlock': '1.0-c19d6d05bfa8143533471c1296066125',
|
||||||
'QosBandwidthLimitRule': '1.2-4e44a8f5c2895ab1278399f87b40a13d',
|
'QosBandwidthLimitRule': '1.3-51b662b12a8d1dfa89288d826c6d26d3',
|
||||||
'QosDscpMarkingRule': '1.2-0313c6554b34fd10c753cb63d638256c',
|
'QosDscpMarkingRule': '1.3-0313c6554b34fd10c753cb63d638256c',
|
||||||
'QosMinimumBandwidthRule': '1.2-314c3419f4799067cc31cc319080adff',
|
'QosMinimumBandwidthRule': '1.3-314c3419f4799067cc31cc319080adff',
|
||||||
'QosRuleType': '1.2-e6fd08fcca152c339cbd5e9b94b1b8e7',
|
'QosRuleType': '1.2-e6fd08fcca152c339cbd5e9b94b1b8e7',
|
||||||
'QosPolicy': '1.4-50460f619c34428ec5651916e938e5a0',
|
'QosPolicy': '1.5-50460f619c34428ec5651916e938e5a0',
|
||||||
'Quota': '1.0-6bb6a0f1bd5d66a2134ffa1a61873097',
|
'Quota': '1.0-6bb6a0f1bd5d66a2134ffa1a61873097',
|
||||||
'QuotaUsage': '1.0-6fbf820368681aac7c5d664662605cf9',
|
'QuotaUsage': '1.0-6fbf820368681aac7c5d664662605cf9',
|
||||||
'Reservation': '1.0-49929fef8e82051660342eed51b48f2a',
|
'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…
Reference in New Issue