All router ports (internal and external) used to be excluded from QoS policies applied on network. This patch excludes only internal router ports from network QoS policies. This allows cloud administrators to set an egress QoS policy to a public/external network and have the QoS policy applied on all external router ports (DVR or not). To the tenant this is also egress traffic so no confusion compared to QoS policies applied to VM ports. DocImpact Update networking-guide/config-qos, User workflow section: - Replace "Network owned ports" with "Internal network owned ports" Change-Id: I2428c2466f41a022196576f4b14526752543da7a Closes-Bug: #1659265 Related-Bug: #1486039
152 lines
5.5 KiB
Python
152 lines
5.5 KiB
Python
# Copyright 2015 Huawei Technologies India Pvt Ltd, Inc.
|
|
# 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.
|
|
|
|
import abc
|
|
import sys
|
|
|
|
from neutron_lib import constants
|
|
from neutron_lib.utils import helpers
|
|
from oslo_utils import versionutils
|
|
from oslo_versionedobjects import base as obj_base
|
|
from oslo_versionedobjects import exception
|
|
from oslo_versionedobjects import fields as obj_fields
|
|
import six
|
|
|
|
from neutron.db import api as db_api
|
|
from neutron.db.qos import models as qos_db_model
|
|
from neutron.objects import base
|
|
from neutron.objects import common_types
|
|
from neutron.services.qos import qos_consts
|
|
|
|
DSCP_MARK = 'dscp_mark'
|
|
|
|
|
|
def get_rules(context, qos_policy_id):
|
|
all_rules = []
|
|
with db_api.autonested_transaction(context.session):
|
|
for rule_type in qos_consts.VALID_RULE_TYPES:
|
|
rule_cls_name = 'Qos%sRule' % helpers.camelize(rule_type)
|
|
rule_cls = getattr(sys.modules[__name__], rule_cls_name)
|
|
|
|
rules = rule_cls.get_objects(context, qos_policy_id=qos_policy_id)
|
|
all_rules.extend(rules)
|
|
return all_rules
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
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.2'
|
|
|
|
fields = {
|
|
'id': common_types.UUIDField(),
|
|
'qos_policy_id': common_types.UUIDField()
|
|
}
|
|
|
|
fields_no_update = ['id', 'qos_policy_id']
|
|
|
|
# should be redefined in subclasses
|
|
rule_type = None
|
|
|
|
def to_dict(self):
|
|
dict_ = super(QosRule, self).to_dict()
|
|
dict_['type'] = self.rule_type
|
|
return dict_
|
|
|
|
def should_apply_to_port(self, port):
|
|
"""Check whether a rule can be applied to a specific port.
|
|
|
|
This function has the logic to decide whether a rule should
|
|
be applied to a port or not, depending on the source of the
|
|
policy (network, or port). Eventually rules could override
|
|
this method, or we could make it abstract to allow different
|
|
rule behaviour.
|
|
"""
|
|
is_port_policy = self.qos_policy_id == port[qos_consts.QOS_POLICY_ID]
|
|
is_network_policy_only = port[qos_consts.QOS_POLICY_ID] is None
|
|
is_network_device_port = any(port['device_owner'].startswith(prefix)
|
|
for prefix
|
|
in constants.DEVICE_OWNER_PREFIXES)
|
|
# NOTE(miouge): Network QoS policies should apply to ext routers ports:
|
|
# - DEVICE_OWNER_AGENT_GW for DVR routers
|
|
# - DEVICE_OWNER_ROUTER_GW for normal neutron routers
|
|
is_router_gw = any(port['device_owner'].startswith(prefix)
|
|
for prefix in [constants.DEVICE_OWNER_AGENT_GW,
|
|
constants.DEVICE_OWNER_ROUTER_GW])
|
|
# NOTE(ralonsoh): return True if:
|
|
# - Is a port QoS policy (not a network QoS policy)
|
|
# - Is not an internal network device (e.g. router) and is a network
|
|
# QoS policy and there is no port QoS policy
|
|
return (is_port_policy or ((is_router_gw or not is_network_device_port)
|
|
and is_network_policy_only))
|
|
|
|
|
|
@obj_base.VersionedObjectRegistry.register
|
|
class QosBandwidthLimitRule(QosRule):
|
|
|
|
db_model = qos_db_model.QosBandwidthLimitRule
|
|
|
|
fields = {
|
|
'max_kbps': obj_fields.IntegerField(nullable=True),
|
|
'max_burst_kbps': obj_fields.IntegerField(nullable=True)
|
|
}
|
|
|
|
rule_type = qos_consts.RULE_TYPE_BANDWIDTH_LIMIT
|
|
|
|
|
|
@obj_base.VersionedObjectRegistry.register
|
|
class QosDscpMarkingRule(QosRule):
|
|
|
|
db_model = qos_db_model.QosDscpMarkingRule
|
|
|
|
fields = {
|
|
DSCP_MARK: common_types.DscpMarkField(),
|
|
}
|
|
|
|
rule_type = qos_consts.RULE_TYPE_DSCP_MARKING
|
|
|
|
def obj_make_compatible(self, primitive, target_version):
|
|
_target_version = versionutils.convert_version_to_tuple(target_version)
|
|
if _target_version < (1, 1):
|
|
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")
|