Each QoS policy contains zero or more QoS rules. A policy is then applied to a
network or a port, making all rules of the policy applied to the corresponding
-Neutron resource (for a network, applying a policy means that the policy will
-be applied to all ports that belong to it).
+Neutron resource.
+
+When applied through a network association, policy rules could apply or not
+to neutron internal ports (like router, dhcp, load balancer, etc..). The QosRule
+base object provides a default should_apply_to_port method which could be
+overridden. In the future we may want to have a flag in QoSNetworkPolicyBinding
+or QosRule to enforce such type of application (for example when limiting all
+the ingress of routers devices on an external network automatically).
From database point of view, following objects are defined in schema:
def _handle_update_create_rules(self, action, port, qos_policy):
for rule in self._iterate_rules(qos_policy.rules):
- handler_name = "".join((action, "_", rule.rule_type))
- handler = getattr(self, handler_name)
- handler(port, rule)
+ if rule.should_apply_to_port(port):
+ handler_name = "".join((action, "_", rule.rule_type))
+ handler = getattr(self, handler_name)
+ handler(port, rule)
+ else:
+ LOG.debug("Port %(port)s excluded from QoS rule %(rule)s",
+ {'port': port, 'rule': rule.id})
class PortPolicyMap(object):
Update events are handled in _handle_notification.
"""
port_id = port['port_id']
- qos_policy_id = port.get('qos_policy_id')
+ port_qos_policy_id = port.get('qos_policy_id')
+ network_qos_policy_id = port.get('network_qos_policy_id')
+ qos_policy_id = port_qos_policy_id or network_qos_policy_id
if qos_policy_id is None:
self._process_reset_port(port)
return
DEVICE_OWNER_LOADBALANCER = "neutron:LOADBALANCER"
DEVICE_OWNER_LOADBALANCERV2 = "neutron:LOADBALANCERV2"
+DEVICE_OWNER_PREFIXES = ["network:", "neutron:"]
+
# Collection used to identify devices owned by router interfaces.
# DEVICE_OWNER_ROUTER_HA_INTF is a special case and so is not included.
ROUTER_INTERFACE_OWNERS = (DEVICE_OWNER_ROUTER_INTF,
from oslo_versionedobjects import fields as obj_fields
import six
+from neutron.common import constants
from neutron.common import utils
from neutron.db import api as db_api
from neutron.db.qos import models as qos_db_model
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_network_rule = self.qos_policy_id != port[qos_consts.QOS_POLICY_ID]
+ is_network_device_port = any(port['device_owner'].startswith(prefix)
+ for prefix
+ in constants.DEVICE_OWNER_PREFIXES)
+
+ return not (is_network_rule and is_network_device_port)
+
@obj_base.VersionedObjectRegistry.register
class QosBandwidthLimitRule(QosRule):
host,
port_context.network.current)
- qos_policy_id = (port.get(qos_consts.QOS_POLICY_ID) or
- port_context.network._network.get(
- qos_consts.QOS_POLICY_ID))
+ network_qos_policy_id = port_context.network._network.get(
+ qos_consts.QOS_POLICY_ID)
entry = {'device': device,
'network_id': port['network_id'],
'port_id': port['id'],
'device_owner': port['device_owner'],
'allowed_address_pairs': port['allowed_address_pairs'],
'port_security_enabled': port.get(psec.PORTSECURITY, True),
- 'qos_policy_id': qos_policy_id,
+ 'qos_policy_id': port.get(qos_consts.QOS_POLICY_ID),
+ 'network_qos_policy_id': network_qos_policy_id,
'profile': port[portbindings.PROFILE]}
if 'security_groups' in port:
entry['security_groups'] = port['security_groups']
TEST_POLICY_ID2 = "46ebaec0-0570-43ac-82f6-60d2b03168c5"
TEST_BW_LIMIT_RULE_1 = rule.QosBandwidthLimitRule(
context=None,
+ qos_policy_id=TEST_POLICY_ID1,
id="5f126d84-551a-4dcf-bb01-0e9c0df0c793",
max_kbps=1000,
max_burst_kbps=10)
TEST_BW_LIMIT_RULE_2 = rule.QosBandwidthLimitRule(
context=None,
+ qos_policy_id=TEST_POLICY_ID2,
id="fa9128d9-44af-49b2-99bb-96548378ad42",
max_kbps=900,
max_burst_kbps=9)
port_dict = super(OVSAgentQoSExtensionTestFramework,
self)._create_test_port_dict()
port_dict['qos_policy_id'] = policy_id
+ port_dict['network_qos_policy_id'] = None
return port_dict
def _get_device_details(self, port, network):
self.policy = TEST_POLICY
self.rule = (
rule.QosBandwidthLimitRule(context=None, id='fake_rule_id',
+ qos_policy_id=self.policy.id,
max_kbps=100, max_burst_kbps=200))
self.policy.rules = [self.rule]
- self.port = object()
- self.fake_rule = QosFakeRule(context=None, id='really_fake_rule_id')
+ self.port = {'qos_policy_id': None, 'network_qos_policy_id': None,
+ 'device_owner': 'random-device-owner'}
+
+ self.fake_rule = QosFakeRule(context=None, id='really_fake_rule_id',
+ qos_policy_id=self.policy.id)
def test_create(self):
self.driver.create(self.port, self.policy)
self.assertEqual(1, len(rules))
self.assertIsInstance(rules[0], rule.QosBandwidthLimitRule)
+ def test__handle_update_create_rules_checks_should_apply_to_port(self):
+ self.rule.should_apply_to_port = mock.Mock(return_value=False)
+ self.driver.create(self.port, self.policy)
+ self.assertFalse(self.driver.create_bandwidth_limit.called)
+
+ self.rule.should_apply_to_port = mock.Mock(return_value=True)
+ self.driver.create(self.port, self.policy)
+ self.assertTrue(self.driver.create_bandwidth_limit.called)
+
class QosExtensionBaseTestCase(base.BaseTestCase):
# License for the specific language governing permissions and limitations
# under the License.
+from neutron.common import constants
from neutron.objects.qos import policy
from neutron.objects.qos import rule
from neutron.services.qos import qos_consts
+from neutron.tests import base as neutron_test_base
from neutron.tests.unit.objects import test_base
from neutron.tests.unit import testlib_api
+POLICY_ID_A = 'policy-id-a'
+POLICY_ID_B = 'policy-id-b'
+DEVICE_OWNER_COMPUTE = 'compute:None'
+
+
+class QosRuleObjectTestCase(neutron_test_base.BaseTestCase):
+
+ def _test_should_apply_to_port(self, rule_policy_id, port_policy_id,
+ device_owner, expected_result):
+ test_rule = rule.QosRule(qos_policy_id=rule_policy_id)
+ port = {qos_consts.QOS_POLICY_ID: port_policy_id,
+ 'device_owner': device_owner}
+ self.assertEqual(expected_result, test_rule.should_apply_to_port(port))
+
+ def test_should_apply_to_port_with_network_port_and_net_policy(self):
+ self._test_should_apply_to_port(
+ rule_policy_id=POLICY_ID_B,
+ port_policy_id=POLICY_ID_A,
+ device_owner=constants.DEVICE_OWNER_ROUTER_INTF,
+ expected_result=False)
+
+ def test_should_apply_to_port_with_network_port_and_port_policy(self):
+ self._test_should_apply_to_port(
+ rule_policy_id=POLICY_ID_A,
+ port_policy_id=POLICY_ID_A,
+ device_owner=constants.DEVICE_OWNER_ROUTER_INTF,
+ expected_result=True)
+
+ def test_should_apply_to_port_with_compute_port_and_net_policy(self):
+ self._test_should_apply_to_port(
+ rule_policy_id=POLICY_ID_B,
+ port_policy_id=POLICY_ID_A,
+ device_owner=DEVICE_OWNER_COMPUTE,
+ expected_result=True)
+
+ def test_should_apply_to_port_with_compute_port_and_port_policy(self):
+ self._test_should_apply_to_port(
+ rule_policy_id=POLICY_ID_A,
+ port_policy_id=POLICY_ID_A,
+ device_owner=DEVICE_OWNER_COMPUTE,
+ expected_result=True)
+
class QosBandwidthLimitRuleObjectTestCase(test_base.BaseObjectIfaceTestCase):
from neutron.plugins.ml2.drivers.mech_sriov.agent.common import exceptions
from neutron.plugins.ml2.drivers.mech_sriov.agent.extension_drivers import (
qos_driver)
+from neutron.services.qos import qos_consts
from neutron.tests import base
self.clear_max_rate_mock = self.qos_driver.eswitch_mgr.clear_max_rate
self.rule = self._create_bw_limit_rule_obj()
self.qos_policy = self._create_qos_policy_obj([self.rule])
- self.port = self._create_fake_port()
+ self.port = self._create_fake_port(self.qos_policy.id)
def _create_bw_limit_rule_obj(self):
rule_obj = rule.QosBandwidthLimitRule()
'rules': rules}
policy_obj = policy.QosPolicy(self.context, **policy_dict)
policy_obj.obj_reset_changes()
+ for policy_rule in policy_obj.rules:
+ policy_rule.qos_policy_id = policy_obj.id
+ policy_rule.obj_reset_changes()
+
return policy_obj
- def _create_fake_port(self):
+ def _create_fake_port(self, qos_policy_id):
return {'port_id': uuidutils.generate_uuid(),
'profile': {'pci_slot': self.PCI_SLOT},
- 'device': self.ASSIGNED_MAC}
+ 'device': self.ASSIGNED_MAC,
+ qos_consts.QOS_POLICY_ID: qos_policy_id,
+ 'device_owner': uuidutils.generate_uuid()}
def test_create_rule(self):
self.qos_driver.create(self.port, self.qos_policy)
self.create = self.qos_driver.br_int.create_egress_bw_limit_for_port
self.rule = self._create_bw_limit_rule_obj()
self.qos_policy = self._create_qos_policy_obj([self.rule])
- self.port = self._create_fake_port()
+ self.port = self._create_fake_port(self.qos_policy.id)
def _create_bw_limit_rule_obj(self):
rule_obj = rule.QosBandwidthLimitRule()
'rules': rules}
policy_obj = policy.QosPolicy(self.context, **policy_dict)
policy_obj.obj_reset_changes()
+ for policy_rule in policy_obj.rules:
+ policy_rule.qos_policy_id = policy_obj.id
+ policy_rule.obj_reset_changes()
return policy_obj
- def _create_fake_port(self):
+ def _create_fake_port(self, policy_id):
self.port_name = 'fakeport'
class FakeVifPort(object):
port_name = self.port_name
- return {'vif_port': FakeVifPort()}
+ return {'vif_port': FakeVifPort(),
+ 'qos_policy_id': policy_id,
+ 'network_qos_policy_id': None,
+ 'device_owner': uuidutils.generate_uuid()}
def test_create_new_rule(self):
self.qos_driver.br_int.get_egress_bw_limit_for_port = mock.Mock(
res = self.callbacks.get_device_details(mock.Mock(), host='fake')
self.assertIsNone(res['qos_policy_id'])
- def test_get_device_details_qos_policy_id_inherited_from_network(self):
+ def test_get_device_details_network_qos_policy_id(self):
port = collections.defaultdict(lambda: 'fake_port')
self.plugin.get_bound_port_context().current = port
self.plugin.get_bound_port_context().network._network = (
{"id": "fake_network",
qos_consts.QOS_POLICY_ID: 'test-policy-id'})
res = self.callbacks.get_device_details(mock.Mock(), host='fake')
- self.assertEqual('test-policy-id', res['qos_policy_id'])
+ self.assertEqual('test-policy-id', res['network_qos_policy_id'])
- def test_get_device_details_qos_policy_id_taken_from_port(self):
+ def test_get_device_details_qos_policy_id_from_port(self):
port = collections.defaultdict(
lambda: 'fake_port',
{qos_consts.QOS_POLICY_ID: 'test-port-policy-id'})