Add QoS minimum bandwidth rule for instance egress traffic
This patch introduces the front end implementation for QoS minimum bandwidth rule. APIImpact: New type of parameter for QoS rule in neutron API DocImpact Change-Id: I6b619a96a2bfde164646c71409b671352bc6ce7d Partial-Bug: #1560963
This commit is contained in:
parent
79927038bc
commit
60325f4ae9
@ -184,6 +184,10 @@ For QoS, new neutron objects were implemented:
|
||||
of the IP header, and only certain configurations are valid. As a result, the list
|
||||
of valid DSCP rule types is: 0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
|
||||
34, 36, 38, 40, 46, 48, and 56.
|
||||
* QosMinimumBandwidthRule: defines the minimum assured bandwidth rule type,
|
||||
characterized by a min_kbps parameter. This rule has also a direction
|
||||
parameter to set the traffic direction, from the instance point of view. The
|
||||
only direction now implemented is egress.
|
||||
|
||||
Those are defined in:
|
||||
|
||||
|
@ -208,6 +208,10 @@
|
||||
"delete_policy_dscp_marking_rule": "rule:admin_only",
|
||||
"update_policy_dscp_marking_rule": "rule:admin_only",
|
||||
"get_rule_type": "rule:regular_user",
|
||||
"get_policy_minimum_bandwidth_rule": "rule:regular_user",
|
||||
"create_policy_minimum_bandwidth_rule": "rule:admin_only",
|
||||
"delete_policy_minimum_bandwidth_rule": "rule:admin_only",
|
||||
"update_policy_minimum_bandwidth_rule": "rule:admin_only",
|
||||
|
||||
"restrict_wildcard": "(not field:rbac_policy:target_tenant=*) or rule:admin_only",
|
||||
"create_rbac_policy": "",
|
||||
|
@ -1 +1 @@
|
||||
a5648cfeeadf
|
||||
0f5bef0f87d4
|
||||
|
@ -0,0 +1,49 @@
|
||||
# Copyright 2016 Intel Corporation.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""add_qos_minimum_bandwidth_rules
|
||||
|
||||
Revision ID: 0f5bef0f87d4
|
||||
Revises: a5648cfeeadf
|
||||
Create Date: 2016-07-29 14:33:37.243487
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0f5bef0f87d4'
|
||||
down_revision = 'a5648cfeeadf'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.common import constants
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'qos_minimum_bandwidth_rules',
|
||||
sa.Column('id', sa.String(length=36), primary_key=True),
|
||||
sa.Column('qos_policy_id',
|
||||
sa.String(length=36),
|
||||
sa.ForeignKey('qos_policies.id', ondelete='CASCADE'),
|
||||
nullable=False, index=True),
|
||||
sa.Column('min_kbps', sa.Integer()),
|
||||
sa.Column('direction', sa.Enum(constants.EGRESS_DIRECTION,
|
||||
constants.INGRESS_DIRECTION,
|
||||
name='directions'),
|
||||
nullable=False, server_default=constants.EGRESS_DIRECTION),
|
||||
sa.UniqueConstraint('qos_policy_id', 'direction',
|
||||
name='qos_minimum_bandwidth_rules0qos_policy_id0direction')
|
||||
)
|
@ -16,6 +16,7 @@
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.api.v2 import attributes as attrs
|
||||
from neutron.common import constants
|
||||
from neutron.db import model_base
|
||||
from neutron.db import models_v2
|
||||
from neutron.db import rbac_db_models
|
||||
@ -87,3 +88,24 @@ class QosDscpMarkingRule(models_v2.HasId, model_base.BASEV2):
|
||||
nullable=False,
|
||||
unique=True)
|
||||
dscp_mark = sa.Column(sa.Integer)
|
||||
|
||||
|
||||
class QosMinimumBandwidthRule(models_v2.HasId, model_base.BASEV2):
|
||||
__tablename__ = 'qos_minimum_bandwidth_rules'
|
||||
qos_policy_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('qos_policies.id',
|
||||
ondelete='CASCADE'),
|
||||
nullable=False,
|
||||
index=True)
|
||||
min_kbps = sa.Column(sa.Integer)
|
||||
direction = sa.Column(sa.Enum(constants.EGRESS_DIRECTION,
|
||||
constants.INGRESS_DIRECTION,
|
||||
name='directions'),
|
||||
nullable=False,
|
||||
server_default=constants.EGRESS_DIRECTION)
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint(
|
||||
qos_policy_id, direction,
|
||||
name='qos_minimum_bandwidth_rules0qos_policy_id0direction'),
|
||||
model_base.BASEV2.__table_args__
|
||||
)
|
||||
|
@ -95,6 +95,21 @@ SUB_RESOURCE_ATTRIBUTE_MAP = {
|
||||
'is_visible': True, 'default': None,
|
||||
'validate': {'type:values': common_constants.
|
||||
VALID_DSCP_MARKS}}})
|
||||
},
|
||||
'minimum_bandwidth_rules': {
|
||||
'parent': {'collection_name': 'policies',
|
||||
'member_name': 'policy'},
|
||||
'parameters': dict(QOS_RULE_COMMON_FIELDS,
|
||||
**{'min_kbps': {
|
||||
'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': None,
|
||||
'validate': {'type:range': [0,
|
||||
common_constants.DB_INTEGER_MAX_VALUE]}},
|
||||
'direction': {
|
||||
'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': 'egress',
|
||||
'validate': {'type:values':
|
||||
[common_constants.EGRESS_DIRECTION]}}})
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,7 +209,8 @@ class QoSPluginBase(service_base.ServicePluginBase):
|
||||
|
||||
# The rule object type to use for each incoming rule-related request.
|
||||
rule_objects = {'bandwidth_limit': rule_object.QosBandwidthLimitRule,
|
||||
'dscp_marking': rule_object.QosDscpMarkingRule}
|
||||
'dscp_marking': rule_object.QosDscpMarkingRule,
|
||||
'minimum_bandwidth': rule_object.QosMinimumBandwidthRule}
|
||||
|
||||
# Patterns used to call method proxies for all policy-rule-specific
|
||||
# method calls (see __getattr__ docstring, below).
|
||||
|
@ -38,7 +38,8 @@ from neutron.objects import rbac_db
|
||||
class QosPolicy(base.NeutronDbObject):
|
||||
# Version 1.0: Initial version
|
||||
# Version 1.1: QosDscpMarkingRule introduced
|
||||
VERSION = '1.1'
|
||||
# Version 1.2: Added QosMinimumBandwidthRule
|
||||
VERSION = '1.2'
|
||||
|
||||
# required by RbacNeutronMetaclass
|
||||
rbac_db_model = QosPolicyRBAC
|
||||
@ -212,11 +213,15 @@ class QosPolicy(base.NeutronDbObject):
|
||||
return set(bound_tenants)
|
||||
|
||||
def obj_make_compatible(self, primitive, target_version):
|
||||
def filter_rules(obj_names, rules):
|
||||
return filter(lambda rule:
|
||||
(rule['versioned_object.name'] in obj_names), rules)
|
||||
|
||||
_target_version = versionutils.convert_version_to_tuple(target_version)
|
||||
if _target_version < (1, 1):
|
||||
if 'rules' in primitive:
|
||||
bw_obj_name = rule_obj_impl.QosBandwidthLimitRule.obj_name()
|
||||
primitive['rules'] = filter(
|
||||
lambda rule: (rule['versioned_object.name'] ==
|
||||
bw_obj_name),
|
||||
primitive['rules'])
|
||||
names = []
|
||||
if _target_version >= (1, 0):
|
||||
names.append(rule_obj_impl.QosBandwidthLimitRule.obj_name())
|
||||
if _target_version >= (1, 1):
|
||||
names.append(rule_obj_impl.QosDscpMarkingRule.obj_name())
|
||||
if 'rules' in primitive and names:
|
||||
primitive['rules'] = filter_rules(names, primitive['rules'])
|
||||
|
@ -49,11 +49,12 @@ def get_rules(context, qos_policy_id):
|
||||
class QosRule(base.NeutronDbObject):
|
||||
# Version 1.0: Initial version, only BandwidthLimitRule
|
||||
# 1.1: Added DscpMarkingRule
|
||||
# 1.2: Added QosMinimumBandwidthRule
|
||||
#
|
||||
#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.1'
|
||||
VERSION = '1.2'
|
||||
|
||||
fields = {
|
||||
'id': obj_fields.UUIDField(),
|
||||
@ -117,3 +118,23 @@ class QosDscpMarkingRule(QosRule):
|
||||
raise exception.IncompatibleObjectVersion(
|
||||
objver=target_version,
|
||||
objname="QosDscpMarkingRule")
|
||||
|
||||
|
||||
@obj_base.VersionedObjectRegistry.register
|
||||
class QosMinimumBandwidthRule(QosRule):
|
||||
|
||||
db_model = qos_db_model.QosMinimumBandwidthRule
|
||||
|
||||
fields = {
|
||||
'min_kbps': obj_fields.IntegerField(nullable=True),
|
||||
'direction': common_types.FlowDirectionEnumField(),
|
||||
}
|
||||
|
||||
rule_type = qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH
|
||||
|
||||
def obj_make_compatible(self, primitive, target_version):
|
||||
_target_version = versionutils.convert_version_to_tuple(target_version)
|
||||
if _target_version < (1, 2):
|
||||
raise exception.IncompatibleObjectVersion(
|
||||
objver=target_version,
|
||||
objname="QosMinimumBandwidthRule")
|
||||
|
@ -29,8 +29,9 @@ class RuleTypeField(obj_fields.BaseEnumField):
|
||||
@obj_base.VersionedObjectRegistry.register
|
||||
class QosRuleType(base.NeutronObject):
|
||||
# Version 1.0: Initial version
|
||||
# Version 1.1: Added DscpMarkingRule
|
||||
VERSION = '1.1'
|
||||
# Version 1.1: Added QosDscpMarkingRule
|
||||
# Version 1.2: Added QosMinimumBandwidthRule
|
||||
VERSION = '1.2'
|
||||
|
||||
fields = {
|
||||
'type': RuleTypeField(),
|
||||
|
@ -15,7 +15,11 @@
|
||||
|
||||
RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth_limit'
|
||||
RULE_TYPE_DSCP_MARKING = 'dscp_marking'
|
||||
VALID_RULE_TYPES = [RULE_TYPE_BANDWIDTH_LIMIT, RULE_TYPE_DSCP_MARKING]
|
||||
RULE_TYPE_MINIMUM_BANDWIDTH = 'minimum_bandwidth'
|
||||
VALID_RULE_TYPES = [RULE_TYPE_BANDWIDTH_LIMIT,
|
||||
RULE_TYPE_DSCP_MARKING,
|
||||
RULE_TYPE_MINIMUM_BANDWIDTH,
|
||||
]
|
||||
|
||||
QOS_POLICY_ID = 'qos_policy_id'
|
||||
|
||||
|
@ -208,6 +208,10 @@
|
||||
"delete_policy_dscp_marking_rule": "rule:admin_only",
|
||||
"update_policy_dscp_marking_rule": "rule:admin_only",
|
||||
"get_rule_type": "rule:regular_user",
|
||||
"get_policy_minimum_bandwidth_rule": "rule:regular_user",
|
||||
"create_policy_minimum_bandwidth_rule": "rule:admin_only",
|
||||
"delete_policy_minimum_bandwidth_rule": "rule:admin_only",
|
||||
"update_policy_minimum_bandwidth_rule": "rule:admin_only",
|
||||
|
||||
"restrict_wildcard": "(not field:rbac_policy:target_tenant=*) or rule:admin_only",
|
||||
"create_rbac_policy": "",
|
||||
|
@ -337,6 +337,13 @@ class QosTestJSON(base.BaseAdminNetworkTest):
|
||||
obtained_policy = self.client.show_qos_policy(policy['id'])['policy']
|
||||
self.assertEqual(obtained_policy, policy)
|
||||
|
||||
@test.idempotent_id('aed8e2a6-22da-421b-89b9-935a2c1a1b50')
|
||||
def test_policy_create_forbidden_for_regular_tenants(self):
|
||||
self.assertRaises(
|
||||
exceptions.Forbidden,
|
||||
self.client.create_qos_policy,
|
||||
'test-policy', 'test policy', False)
|
||||
|
||||
|
||||
class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
@classmethod
|
||||
@ -434,13 +441,6 @@ class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
self.create_qos_bandwidth_limit_rule,
|
||||
'policy', 200, 1337)
|
||||
|
||||
@test.idempotent_id('eed8e2a6-22da-421b-89b9-935a2c1a1b50')
|
||||
def test_policy_create_forbidden_for_regular_tenants(self):
|
||||
self.assertRaises(
|
||||
exceptions.Forbidden,
|
||||
self.client.create_qos_policy,
|
||||
'test-policy', 'test policy', False)
|
||||
|
||||
@test.idempotent_id('a4a2e7ad-786f-4927-a85a-e545a93bd274')
|
||||
def test_rule_create_forbidden_for_regular_tenants(self):
|
||||
self.assertRaises(
|
||||
@ -883,6 +883,147 @@ class QosDscpMarkingRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
self.assertNotIn(rule2['id'], rules_ids)
|
||||
|
||||
|
||||
class QosMinimumBandwidthRuleTestJSON(base.BaseAdminNetworkTest):
|
||||
DIRECTION_EGRESS = "egress"
|
||||
DIRECTION_INGRESS = "ingress"
|
||||
RULE_NAME = qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH + "_rule"
|
||||
RULES_NAME = RULE_NAME + "s"
|
||||
|
||||
@classmethod
|
||||
@test.requires_ext(extension="qos", service="network")
|
||||
def resource_setup(cls):
|
||||
super(QosMinimumBandwidthRuleTestJSON, cls).resource_setup()
|
||||
|
||||
@test.idempotent_id('aa59b00b-3e9c-4787-92f8-93a5cdf5e378')
|
||||
def test_rule_create(self):
|
||||
policy = self.create_qos_policy(name='test-policy',
|
||||
description='test policy',
|
||||
shared=False)
|
||||
rule = self.admin_client.create_minimum_bandwidth_rule(
|
||||
policy_id=policy['id'], min_kbps=1138,
|
||||
direction=self.DIRECTION_EGRESS)[self.RULE_NAME]
|
||||
|
||||
# Test 'show rule'
|
||||
retrieved_rule = self.admin_client.show_minimum_bandwidth_rule(
|
||||
policy['id'], rule['id'])
|
||||
retrieved_rule = retrieved_rule[self.RULE_NAME]
|
||||
self.assertEqual(rule['id'], retrieved_rule['id'])
|
||||
self.assertEqual(1138, retrieved_rule['min_kbps'])
|
||||
self.assertEqual(self.DIRECTION_EGRESS, retrieved_rule['direction'])
|
||||
|
||||
# Test 'list rules'
|
||||
rules = self.admin_client.list_minimum_bandwidth_rules(policy['id'])
|
||||
rules = rules[self.RULES_NAME]
|
||||
rules_ids = [r['id'] for r in rules]
|
||||
self.assertIn(rule['id'], rules_ids)
|
||||
|
||||
# Test 'show policy'
|
||||
retrieved_policy = self.admin_client.show_qos_policy(policy['id'])
|
||||
policy_rules = retrieved_policy['policy']['rules']
|
||||
self.assertEqual(1, len(policy_rules))
|
||||
self.assertEqual(rule['id'], policy_rules[0]['id'])
|
||||
self.assertEqual(qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH,
|
||||
policy_rules[0]['type'])
|
||||
|
||||
@test.idempotent_id('aa59b00b-ab01-4787-92f8-93a5cdf5e378')
|
||||
def test_rule_create_fail_for_the_same_type(self):
|
||||
policy = self.create_qos_policy(name='test-policy',
|
||||
description='test policy',
|
||||
shared=False)
|
||||
self.admin_client.create_minimum_bandwidth_rule(
|
||||
policy_id=policy['id'], min_kbps=200,
|
||||
direction=self.DIRECTION_EGRESS)
|
||||
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.admin_client.create_minimum_bandwidth_rule,
|
||||
policy_id=policy['id'],
|
||||
min_kbps=201, direction=self.DIRECTION_EGRESS)
|
||||
|
||||
@test.idempotent_id('d6fce764-e511-4fa6-9f86-f4b41cf142cf')
|
||||
def test_rule_create_fail_for_direction_ingress(self):
|
||||
policy = self.create_qos_policy(name='test-policy',
|
||||
description='test policy',
|
||||
shared=False)
|
||||
self.assertRaises(exceptions.BadRequest,
|
||||
self.admin_client.create_minimum_bandwidth_rule,
|
||||
policy_id=policy['id'],
|
||||
min_kbps=201, direction=self.DIRECTION_INGRESS)
|
||||
|
||||
@test.idempotent_id('a49a6988-2568-47d2-931e-2dbc858943b3')
|
||||
def test_rule_update(self):
|
||||
policy = self.create_qos_policy(name='test-policy',
|
||||
description='test policy',
|
||||
shared=False)
|
||||
rule = self.admin_client.create_minimum_bandwidth_rule(
|
||||
policy_id=policy['id'], min_kbps=300,
|
||||
direction=self.DIRECTION_EGRESS)[self.RULE_NAME]
|
||||
|
||||
self.admin_client.update_minimum_bandwidth_rule(policy['id'],
|
||||
rule['id'], min_kbps=350, direction=self.DIRECTION_EGRESS)
|
||||
|
||||
retrieved_policy = self.admin_client.show_minimum_bandwidth_rule(
|
||||
policy['id'], rule['id'])
|
||||
retrieved_policy = retrieved_policy[self.RULE_NAME]
|
||||
self.assertEqual(350, retrieved_policy['min_kbps'])
|
||||
self.assertEqual(self.DIRECTION_EGRESS, retrieved_policy['direction'])
|
||||
|
||||
@test.idempotent_id('a7ee6efd-7b33-4a68-927d-275b4f8ba958')
|
||||
def test_rule_delete(self):
|
||||
policy = self.create_qos_policy(name='test-policy',
|
||||
description='test policy',
|
||||
shared=False)
|
||||
rule = self.admin_client.create_minimum_bandwidth_rule(
|
||||
policy['id'], 200, self.DIRECTION_EGRESS)[self.RULE_NAME]
|
||||
|
||||
retrieved_policy = self.admin_client.show_minimum_bandwidth_rule(
|
||||
policy['id'], rule['id'])
|
||||
retrieved_policy = retrieved_policy[self.RULE_NAME]
|
||||
self.assertEqual(rule['id'], retrieved_policy['id'])
|
||||
|
||||
self.admin_client.delete_minimum_bandwidth_rule(policy['id'],
|
||||
rule['id'])
|
||||
self.assertRaises(exceptions.NotFound,
|
||||
self.admin_client.show_minimum_bandwidth_rule,
|
||||
policy['id'], rule['id'])
|
||||
|
||||
@test.idempotent_id('a211222c-5808-46cb-a961-983bbab6b852')
|
||||
def test_rule_create_rule_nonexistent_policy(self):
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.admin_client.create_minimum_bandwidth_rule,
|
||||
'policy', 200, self.DIRECTION_EGRESS)
|
||||
|
||||
@test.idempotent_id('b4a2e7ad-786f-4927-a85a-e545a93bd274')
|
||||
def test_rule_create_forbidden_for_regular_tenants(self):
|
||||
self.assertRaises(
|
||||
exceptions.Forbidden,
|
||||
self.client.create_minimum_bandwidth_rule,
|
||||
'policy', 300, self.DIRECTION_EGRESS)
|
||||
|
||||
@test.idempotent_id('de0bd0c2-54d9-4e29-85f1-cfb36ac3ebe2')
|
||||
def test_get_rules_by_policy(self):
|
||||
policy1 = self.create_qos_policy(name='test-policy1',
|
||||
description='test policy1',
|
||||
shared=False)
|
||||
rule1 = self.admin_client.create_minimum_bandwidth_rule(
|
||||
policy_id=policy1['id'], min_kbps=200,
|
||||
direction=self.DIRECTION_EGRESS)[self.RULE_NAME]
|
||||
|
||||
policy2 = self.create_qos_policy(name='test-policy2',
|
||||
description='test policy2',
|
||||
shared=False)
|
||||
rule2 = self.admin_client.create_minimum_bandwidth_rule(
|
||||
policy_id=policy2['id'], min_kbps=5000,
|
||||
direction=self.DIRECTION_EGRESS)[self.RULE_NAME]
|
||||
|
||||
# Test 'list rules'
|
||||
rules = self.admin_client.list_minimum_bandwidth_rules(policy1['id'])
|
||||
rules = rules[self.RULES_NAME]
|
||||
rules_ids = [r['id'] for r in rules]
|
||||
self.assertIn(rule1['id'], rules_ids)
|
||||
self.assertNotIn(rule2['id'], rules_ids)
|
||||
|
||||
|
||||
class QosSearchCriteriaTest(base.BaseSearchCriteriaTest,
|
||||
base.BaseAdminNetworkTest):
|
||||
|
||||
|
@ -55,6 +55,7 @@ class NetworkClientJSON(service_client.RestClient):
|
||||
'metering_label_rules': 'metering',
|
||||
'policies': 'qos',
|
||||
'bandwidth_limit_rules': 'qos',
|
||||
'minimum_bandwidth_rules': 'qos',
|
||||
'rule_types': 'qos',
|
||||
'rbac-policies': '',
|
||||
}
|
||||
@ -652,6 +653,52 @@ class NetworkClientJSON(service_client.RestClient):
|
||||
self.expected_success(204, resp.status)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def create_minimum_bandwidth_rule(self, policy_id, min_kbps, direction):
|
||||
uri = '%s/qos/policies/%s/minimum_bandwidth_rules' % (
|
||||
self.uri_prefix, policy_id)
|
||||
post_data = self.serialize({
|
||||
'minimum_bandwidth_rule': {
|
||||
'min_kbps': min_kbps,
|
||||
'direction': direction
|
||||
}
|
||||
})
|
||||
resp, body = self.post(uri, post_data)
|
||||
self.expected_success(201, resp.status)
|
||||
body = jsonutils.loads(body)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def list_minimum_bandwidth_rules(self, policy_id):
|
||||
uri = '%s/qos/policies/%s/minimum_bandwidth_rules' % (
|
||||
self.uri_prefix, policy_id)
|
||||
resp, body = self.get(uri)
|
||||
body = self.deserialize_single(body)
|
||||
self.expected_success(200, resp.status)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def show_minimum_bandwidth_rule(self, policy_id, rule_id):
|
||||
uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % (
|
||||
self.uri_prefix, policy_id, rule_id)
|
||||
resp, body = self.get(uri)
|
||||
body = self.deserialize_single(body)
|
||||
self.expected_success(200, resp.status)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def update_minimum_bandwidth_rule(self, policy_id, rule_id, **kwargs):
|
||||
uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % (
|
||||
self.uri_prefix, policy_id, rule_id)
|
||||
post_data = {'minimum_bandwidth_rule': kwargs}
|
||||
resp, body = self.put(uri, jsonutils.dumps(post_data))
|
||||
body = self.deserialize_single(body)
|
||||
self.expected_success(200, resp.status)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def delete_minimum_bandwidth_rule(self, policy_id, rule_id):
|
||||
uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % (
|
||||
self.uri_prefix, policy_id, rule_id)
|
||||
resp, body = self.delete(uri)
|
||||
self.expected_success(204, resp.status)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def list_qos_rule_types(self):
|
||||
uri = '%s/qos/rule-types' % self.uri_prefix
|
||||
resp, body = self.get(uri)
|
||||
|
@ -17,10 +17,18 @@ from neutron.db import models_v2
|
||||
from neutron.objects.db import api as db_api
|
||||
from neutron.objects.qos import policy
|
||||
from neutron.objects.qos import rule
|
||||
from neutron.services.qos import qos_consts
|
||||
from neutron.tests.unit.objects import test_base
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
|
||||
RULE_OBJ_CLS = {
|
||||
qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: rule.QosBandwidthLimitRule,
|
||||
qos_consts.RULE_TYPE_DSCP_MARKING: rule.QosDscpMarkingRule,
|
||||
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: rule.QosMinimumBandwidthRule,
|
||||
}
|
||||
|
||||
|
||||
class QosPolicyObjectTestCase(test_base.BaseObjectIfaceTestCase):
|
||||
|
||||
_test_class = policy.QosPolicy
|
||||
@ -36,13 +44,19 @@ class QosPolicyObjectTestCase(test_base.BaseObjectIfaceTestCase):
|
||||
self.get_random_fields(rule.QosDscpMarkingRule)
|
||||
for _ in range(3)]
|
||||
|
||||
self.db_qos_minimum_bandwidth_rules = [
|
||||
self.get_random_fields(rule.QosMinimumBandwidthRule)
|
||||
for _ in range(3)]
|
||||
|
||||
self.model_map = {
|
||||
self._test_class.db_model: self.db_objs,
|
||||
self._test_class.rbac_db_model: [],
|
||||
self._test_class.port_binding_model: [],
|
||||
self._test_class.network_binding_model: [],
|
||||
rule.QosBandwidthLimitRule.db_model: self.db_qos_bandwidth_rules,
|
||||
rule.QosDscpMarkingRule.db_model: self.db_qos_dscp_rules}
|
||||
rule.QosDscpMarkingRule.db_model: self.db_qos_dscp_rules,
|
||||
rule.QosMinimumBandwidthRule.db_model:
|
||||
self.db_qos_minimum_bandwidth_rules}
|
||||
|
||||
self.get_object = mock.patch.object(
|
||||
db_api, 'get_object', side_effect=self.fake_get_object).start()
|
||||
@ -127,17 +141,20 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
policy_obj.create()
|
||||
return policy_obj
|
||||
|
||||
def _create_test_policy_with_bwrule(self):
|
||||
def _create_test_policy_with_rules(self, rule_type, reload_rules=False):
|
||||
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_fields(obj_cls=obj_cls)
|
||||
rule_fields['qos_policy_id'] = policy_obj.id
|
||||
rule_obj = obj_cls(self.context, **rule_fields)
|
||||
rule_obj.create()
|
||||
rules.append(rule_obj)
|
||||
|
||||
rule_fields = self.get_random_fields(
|
||||
obj_cls=rule.QosBandwidthLimitRule)
|
||||
rule_fields['qos_policy_id'] = policy_obj.id
|
||||
|
||||
rule_obj = rule.QosBandwidthLimitRule(self.context, **rule_fields)
|
||||
rule_obj.create()
|
||||
|
||||
return policy_obj, rule_obj
|
||||
if reload_rules:
|
||||
policy_obj.reload_rules()
|
||||
return policy_obj, rules
|
||||
|
||||
def test_attach_network_get_network_policy(self):
|
||||
|
||||
@ -291,27 +308,30 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
policy_obj.detach_network, self._network['id'])
|
||||
|
||||
def test_synthetic_rule_fields(self):
|
||||
policy_obj, rule_obj = self._create_test_policy_with_bwrule()
|
||||
policy_obj, rule_obj = self._create_test_policy_with_rules(
|
||||
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT])
|
||||
policy_obj = policy.QosPolicy.get_object(self.context,
|
||||
id=policy_obj.id)
|
||||
self.assertEqual([rule_obj], policy_obj.rules)
|
||||
self.assertEqual(rule_obj, policy_obj.rules)
|
||||
|
||||
def test_get_object_fetches_rules_non_lazily(self):
|
||||
policy_obj, rule_obj = self._create_test_policy_with_bwrule()
|
||||
policy_obj, rule_obj = self._create_test_policy_with_rules(
|
||||
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT])
|
||||
policy_obj = policy.QosPolicy.get_object(self.context,
|
||||
id=policy_obj.id)
|
||||
self.assertEqual([rule_obj], policy_obj.rules)
|
||||
self.assertEqual(rule_obj, policy_obj.rules)
|
||||
|
||||
primitive = policy_obj.obj_to_primitive()
|
||||
self.assertNotEqual([], (primitive['versioned_object.data']['rules']))
|
||||
|
||||
def test_to_dict_returns_rules_as_dicts(self):
|
||||
policy_obj, rule_obj = self._create_test_policy_with_bwrule()
|
||||
policy_obj, rule_obj = self._create_test_policy_with_rules(
|
||||
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT])
|
||||
policy_obj = policy.QosPolicy.get_object(self.context,
|
||||
id=policy_obj.id)
|
||||
|
||||
obj_dict = policy_obj.to_dict()
|
||||
rule_dict = rule_obj.to_dict()
|
||||
rule_dict = rule_obj[0].to_dict()
|
||||
|
||||
# first make sure that to_dict() is still sane and does not return
|
||||
# objects
|
||||
@ -343,11 +363,12 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
obj.delete()
|
||||
|
||||
def test_reload_rules_reloads_rules(self):
|
||||
policy_obj, rule_obj = self._create_test_policy_with_bwrule()
|
||||
policy_obj, rule_obj = self._create_test_policy_with_rules(
|
||||
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT])
|
||||
self.assertEqual([], policy_obj.rules)
|
||||
|
||||
policy_obj.reload_rules()
|
||||
self.assertEqual([rule_obj], policy_obj.rules)
|
||||
self.assertEqual(rule_obj, policy_obj.rules)
|
||||
|
||||
def test_get_bound_tenant_ids_returns_set_of_tenant_ids(self):
|
||||
obj = self._create_test_policy()
|
||||
@ -364,37 +385,40 @@ class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
primitive = obj.obj_to_primitive(target_version=version)
|
||||
return policy.QosPolicy.clean_obj_from_primitive(primitive)
|
||||
|
||||
def _create_test_policy_with_bw_and_dscp(self):
|
||||
policy_obj, rule_obj_band = self._create_test_policy_with_bwrule()
|
||||
|
||||
rule_fields = self.get_random_fields(obj_cls=rule.QosDscpMarkingRule)
|
||||
rule_fields['qos_policy_id'] = policy_obj.id
|
||||
|
||||
rule_obj_dscp = rule.QosDscpMarkingRule(self.context, **rule_fields)
|
||||
rule_obj_dscp.create()
|
||||
|
||||
policy_obj.reload_rules()
|
||||
return policy_obj, rule_obj_band, rule_obj_dscp
|
||||
|
||||
def test_object_version(self):
|
||||
policy_obj, rule_obj_band, rule_obj_dscp = (
|
||||
self._create_test_policy_with_bw_and_dscp())
|
||||
policy_obj, rule_objs = self._create_test_policy_with_rules(
|
||||
RULE_OBJ_CLS.keys(), reload_rules=True)
|
||||
|
||||
policy_obj_v1_1 = self._policy_through_version(policy_obj, '1.1')
|
||||
policy_obj_v1_2 = self._policy_through_version(policy_obj, '1.2')
|
||||
|
||||
self.assertIn(rule_obj_band, policy_obj_v1_1.rules)
|
||||
self.assertIn(rule_obj_dscp, policy_obj_v1_1.rules)
|
||||
for rule_obj in rule_objs:
|
||||
self.assertIn(rule_obj, policy_obj_v1_2.rules)
|
||||
|
||||
def test_object_version_degradation_1_1_to_1_0(self):
|
||||
#NOTE(mangelajo): we should not check .VERSION, since that's the
|
||||
# local version on the class definition
|
||||
policy_obj, rule_obj_band, rule_obj_dscp = (
|
||||
self._create_test_policy_with_bw_and_dscp())
|
||||
policy_obj, rule_objs = self._create_test_policy_with_rules(
|
||||
[qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
|
||||
qos_consts.RULE_TYPE_DSCP_MARKING], reload_rules=True)
|
||||
|
||||
policy_obj_v1_0 = self._policy_through_version(policy_obj, '1.0')
|
||||
|
||||
self.assertIn(rule_obj_band, policy_obj_v1_0.rules)
|
||||
self.assertNotIn(rule_obj_dscp, policy_obj_v1_0.rules)
|
||||
self.assertIn(rule_objs[0], policy_obj_v1_0.rules)
|
||||
self.assertNotIn(rule_objs[1], policy_obj_v1_0.rules)
|
||||
|
||||
def test_object_version_degradation_1_2_to_1_1(self):
|
||||
#NOTE(mangelajo): we should not check .VERSION, since that's the
|
||||
# local version on the class definition
|
||||
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)
|
||||
|
||||
policy_obj_v1_1 = self._policy_through_version(policy_obj, '1.1')
|
||||
|
||||
self.assertIn(rule_objs[0], policy_obj_v1_1.rules)
|
||||
self.assertIn(rule_objs[1], policy_obj_v1_1.rules)
|
||||
self.assertNotIn(rule_objs[2], policy_obj_v1_1.rules)
|
||||
|
||||
def test_filter_by_shared(self):
|
||||
policy_obj = policy.QosPolicy(
|
||||
|
@ -121,3 +121,37 @@ class QosDscpMarkingRuleDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
policy_obj = policy.QosPolicy(self.context,
|
||||
id=generated_qos_policy_id)
|
||||
policy_obj.create()
|
||||
|
||||
|
||||
class QosMinimumBandwidthRuleObjectTestCase(test_base.BaseObjectIfaceTestCase):
|
||||
|
||||
_test_class = rule.QosMinimumBandwidthRule
|
||||
|
||||
def test_min_bw_object_version_degradation(self):
|
||||
min_bw_rule = rule.QosMinimumBandwidthRule()
|
||||
|
||||
for version in ['1.0', '1.1']:
|
||||
self.assertRaises(exception.IncompatibleObjectVersion,
|
||||
min_bw_rule.obj_to_primitive, version)
|
||||
|
||||
def test_min_bw_object_version(self):
|
||||
min_bw_rule = rule.QosMinimumBandwidthRule()
|
||||
|
||||
prim = min_bw_rule.obj_to_primitive('1.2')
|
||||
|
||||
self.assertTrue(prim)
|
||||
|
||||
|
||||
class QosMinimumBandwidthRuleDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
testlib_api.SqlTestCase):
|
||||
|
||||
_test_class = rule.QosMinimumBandwidthRule
|
||||
|
||||
def setUp(self):
|
||||
super(QosMinimumBandwidthRuleDbObjectTestCase, self).setUp()
|
||||
# Prepare policy to be able to insert a rule
|
||||
for obj in self.db_objs:
|
||||
generated_qos_policy_id = obj['qos_policy_id']
|
||||
policy_obj = policy.QosPolicy(self.context,
|
||||
id=generated_qos_policy_id)
|
||||
policy_obj.create()
|
||||
|
@ -343,6 +343,10 @@ def get_random_dscp_mark():
|
||||
return random.choice(constants.VALID_DSCP_MARKS)
|
||||
|
||||
|
||||
def get_random_direction():
|
||||
return random.choice(constants.VALID_DIRECTIONS)
|
||||
|
||||
|
||||
def get_list_of_random_networks(num=10):
|
||||
for i in range(5):
|
||||
res = [tools.get_random_ip_network() for i in range(num)]
|
||||
@ -360,6 +364,7 @@ FIELD_TYPE_VALUE_GENERATOR_MAP = {
|
||||
obj_fields.ObjectField: lambda: None,
|
||||
obj_fields.ListOfObjectsField: lambda: [],
|
||||
common_types.DscpMarkField: get_random_dscp_mark,
|
||||
common_types.FlowDirectionEnumField: get_random_direction,
|
||||
obj_fields.IPNetworkField: tools.get_random_ip_network,
|
||||
common_types.IPNetworkField: tools.get_random_ip_network,
|
||||
common_types.IPNetworkPrefixLenField: tools.get_random_prefixlen,
|
||||
|
@ -35,10 +35,11 @@ object_data = {
|
||||
'NetworkSegment': '1.0-865567a6f70eb85cf33fb7a5575a4eab',
|
||||
'PortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',
|
||||
'AllowedAddressPair': '1.0-9f9186b6f952fbf31d257b0458b852c0',
|
||||
'QosBandwidthLimitRule': '1.1-4e44a8f5c2895ab1278399f87b40a13d',
|
||||
'QosDscpMarkingRule': '1.1-0313c6554b34fd10c753cb63d638256c',
|
||||
'QosRuleType': '1.1-8a53fef4c6a43839d477a85b787d22ce',
|
||||
'QosPolicy': '1.1-7c5659e1c1f64395223592d3d3293e22',
|
||||
'QosBandwidthLimitRule': '1.2-4e44a8f5c2895ab1278399f87b40a13d',
|
||||
'QosDscpMarkingRule': '1.2-0313c6554b34fd10c753cb63d638256c',
|
||||
'QosMinimumBandwidthRule': '1.2-314c3419f4799067cc31cc319080adff',
|
||||
'QosRuleType': '1.2-e6fd08fcca152c339cbd5e9b94b1b8e7',
|
||||
'QosPolicy': '1.2-7c5659e1c1f64395223592d3d3293e22',
|
||||
'Route': '1.0-a9883a63b416126f9e345523ec09483b',
|
||||
'SecurityGroup': '1.0-e26b90c409b31fd2e3c6fcec402ac0b9',
|
||||
'SecurityGroupRule': '1.0-e9b8dace9d48b936c62ad40fe1f339d5',
|
||||
|
@ -290,6 +290,59 @@ class TestQosPlugin(base.BaseQosTestCase):
|
||||
self.qos_plugin.get_policy_dscp_marking_rules,
|
||||
self.ctxt, self.policy.id)
|
||||
|
||||
def test_get_policy_minimum_bandwidth_rule(self):
|
||||
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
|
||||
return_value=self.policy):
|
||||
with mock.patch('neutron.objects.qos.rule.'
|
||||
'QosMinimumBandwidthRule.'
|
||||
'get_object') as get_object_mock:
|
||||
self.qos_plugin.get_policy_minimum_bandwidth_rule(
|
||||
self.ctxt, self.rule.id, self.policy.id)
|
||||
get_object_mock.assert_called_once_with(self.ctxt,
|
||||
id=self.rule.id)
|
||||
|
||||
def test_get_policy_minimum_bandwidth_rules_for_policy(self):
|
||||
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
|
||||
return_value=self.policy):
|
||||
with mock.patch('neutron.objects.qos.rule.'
|
||||
'QosMinimumBandwidthRule.'
|
||||
'get_objects') as get_objects_mock:
|
||||
self.qos_plugin.get_policy_minimum_bandwidth_rules(
|
||||
self.ctxt, self.policy.id)
|
||||
get_objects_mock.assert_called_once_with(
|
||||
self.ctxt, _pager=mock.ANY, qos_policy_id=self.policy.id)
|
||||
|
||||
def test_get_policy_minimum_bandwidth_rules_for_policy_with_filters(self):
|
||||
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
|
||||
return_value=self.policy):
|
||||
with mock.patch('neutron.objects.qos.rule.'
|
||||
'QosMinimumBandwidthRule.'
|
||||
'get_objects') as get_objects_mock:
|
||||
|
||||
filters = {'filter': 'filter_id'}
|
||||
self.qos_plugin.get_policy_minimum_bandwidth_rules(
|
||||
self.ctxt, self.policy.id, filters=filters)
|
||||
get_objects_mock.assert_called_once_with(
|
||||
self.ctxt, _pager=mock.ANY,
|
||||
qos_policy_id=self.policy.id,
|
||||
filter='filter_id')
|
||||
|
||||
def test_get_policy_minimum_bandwidth_rule_for_nonexistent_policy(self):
|
||||
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
|
||||
return_value=None):
|
||||
self.assertRaises(
|
||||
n_exc.QosPolicyNotFound,
|
||||
self.qos_plugin.get_policy_minimum_bandwidth_rule,
|
||||
self.ctxt, self.rule.id, self.policy.id)
|
||||
|
||||
def test_get_policy_minimum_bandwidth_rules_for_nonexistent_policy(self):
|
||||
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
|
||||
return_value=None):
|
||||
self.assertRaises(
|
||||
n_exc.QosPolicyNotFound,
|
||||
self.qos_plugin.get_policy_minimum_bandwidth_rules,
|
||||
self.ctxt, self.policy.id)
|
||||
|
||||
def test_create_policy_rule_for_nonexistent_policy(self):
|
||||
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
|
||||
return_value=None):
|
||||
|
@ -0,0 +1,10 @@
|
||||
---
|
||||
features:
|
||||
- Users can now apply a QoS rule to a port or network to
|
||||
setup the minimum egress bandwidth per queue and port.
|
||||
The minimum egress bandwidth rule is applied to each port
|
||||
individually.
|
||||
other:
|
||||
- At the time of writing, Neutron bandwidth booking is not
|
||||
integrated with Compute scheduler, which means that minimal
|
||||
bandwidth is not guaranteed but provided as best effort.
|
Loading…
Reference in New Issue
Block a user