From 12f7abd3982d3580abdb9055c650bdad50900cf4 Mon Sep 17 00:00:00 2001 From: Ihar Hrachyshka Date: Thu, 11 Jun 2015 08:11:08 +0200 Subject: [PATCH] Introduce mechanism to determine supported qos rule types for a plugin Every plugin that supports some of QoS rules will define a property called supported_qos_rule_types of list type. For ml2, determine supported qos rule types as a subset of rule types supported by all drivers. (In the future, we may expand the list to include all types supported by at least one of enabled drivers. This would require synchronized work with nova scheduler though.) For ml2, tests are limited, and should be expanded to check that common subset of qos rules is calculated properly when intersection != the list of each plugins. For now, it's enough since we don't have more than one rule type planned for Liberty. Added API test for the resource. Partially-Implements: blueprint ml2-qos Co-Authored-By: Irena Berezovsky Co-Authored-By: John Schwarz Change-Id: I0d18ae256877a129e203110003fcadd1d63590b4 --- doc/source/devref/quality_of_service.rst | 16 ++++++- neutron/extensions/qos.py | 3 -- neutron/objects/base.py | 29 ++++++++++-- neutron/objects/qos/policy.py | 6 +-- neutron/objects/qos/rule.py | 6 +-- neutron/objects/qos/rule_type.py | 41 +++++++++++++++++ .../mech_driver/mech_linuxbridge.py | 7 +++ .../agent/extension_drivers/qos_driver.py | 8 ++-- .../mech_driver/mech_openvswitch.py | 3 ++ neutron/plugins/ml2/managers.py | 37 ++++++++++++++- neutron/plugins/ml2/plugin.py | 4 ++ neutron/services/qos/qos_consts.py | 17 +++++++ neutron/services/qos/qos_plugin.py | 4 +- neutron/tests/api/test_qos.py | 20 ++++++++ .../services/network/json/network_client.py | 8 ++++ .../tests/unit/objects/qos/test_rule_type.py | 46 +++++++++++++++++++ neutron/tests/unit/objects/test_base.py | 2 +- .../extension_drivers/test_qos_driver.py | 4 +- neutron/tests/unit/plugins/ml2/test_plugin.py | 32 +++++++++++++ 19 files changed, 270 insertions(+), 23 deletions(-) create mode 100644 neutron/objects/qos/rule_type.py create mode 100644 neutron/services/qos/qos_consts.py create mode 100644 neutron/tests/unit/objects/qos/test_rule_type.py diff --git a/doc/source/devref/quality_of_service.rst b/doc/source/devref/quality_of_service.rst index 53b9942d3..1c5570205 100644 --- a/doc/source/devref/quality_of_service.rst +++ b/doc/source/devref/quality_of_service.rst @@ -31,6 +31,21 @@ Service side design notifications to any interested agent, using `RPC callbacks `_. +Supported QoS rule types +------------------------ + +Any plugin or Ml2 mechanism driver can claim support for some QoS rule types by +providing a plugin/driver class property called 'supported_qos_rule_types' that +should return a list of strings that correspond to QoS rule types (for the list +of all rule types, see: neutron.extensions.qos.VALID_RULE_TYPES). + +In the most simple case, the property can be represented by a simple Python +list defined on the class. + +For Ml2 plugin, the list of supported QoS rule types is defined as a common +subset of rules supported by all active mechanism drivers. + + QoS resources ------------- @@ -253,4 +268,3 @@ in terms of how those objects are implemented. Specific test classes can obviously extend the set of test cases as they see needed (f.e. you need to define new test cases for those additional methods that you may add to your object implementations on top of base semantics common to all neutron objects). - diff --git a/neutron/extensions/qos.py b/neutron/extensions/qos.py index 76b9f6f8a..034b8bdc4 100644 --- a/neutron/extensions/qos.py +++ b/neutron/extensions/qos.py @@ -28,9 +28,6 @@ from neutron.services import service_base QOS_PREFIX = "/qos" -RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth_limit' -VALID_RULE_TYPES = [RULE_TYPE_BANDWIDTH_LIMIT] - # Attribute Map QOS_RULE_COMMON_FIELDS = { 'id': {'allow_post': False, 'allow_put': False, diff --git a/neutron/objects/base.py b/neutron/objects/base.py index 4fe8431d6..5e1f59263 100644 --- a/neutron/objects/base.py +++ b/neutron/objects/base.py @@ -26,6 +26,31 @@ class NeutronObject(obj_base.VersionedObject, obj_base.VersionedObjectDictCompat, obj_base.ComparableVersionedObject): + # TODO(QoS): this should be revisited on how we plan to work with dicts + def to_dict(self): + return dict(self.items()) + + @classmethod + def get_by_id(cls, context, id): + raise NotImplementedError() + + @classmethod + @abc.abstractmethod + def get_objects(cls, context, **kwargs): + raise NotImplementedError() + + def create(self): + raise NotImplementedError() + + def update(self): + raise NotImplementedError() + + def delete(self): + raise NotImplementedError() + + +class NeutronDbObject(NeutronObject): + # should be overridden for all persistent objects db_model = None @@ -42,10 +67,6 @@ class NeutronObject(obj_base.VersionedObject, break self.obj_reset_changes() - # TODO(QoS): this should be revisited on how we plan to work with dicts - def to_dict(self): - return dict(self.items()) - @classmethod def get_by_id(cls, context, id): db_obj = db_api.get_object(context, cls.db_model, id=id) diff --git a/neutron/objects/qos/policy.py b/neutron/objects/qos/policy.py index a5938d948..53c34a993 100644 --- a/neutron/objects/qos/policy.py +++ b/neutron/objects/qos/policy.py @@ -24,9 +24,9 @@ from neutron.common import utils from neutron.db import api as db_api from neutron.db.qos import api as qos_db_api from neutron.db.qos import models as qos_db_model -from neutron.extensions import qos as qos_extension from neutron.objects import base from neutron.objects.qos import rule as rule_obj_impl +from neutron.services.qos import qos_consts class QosRulesExtenderMeta(abc.ABCMeta): @@ -35,7 +35,7 @@ class QosRulesExtenderMeta(abc.ABCMeta): cls = super(QosRulesExtenderMeta, mcs).__new__(mcs, name, bases, dct) cls.rule_fields = {} - for rule in qos_extension.VALID_RULE_TYPES: + for rule in qos_consts.VALID_RULE_TYPES: rule_cls_name = 'Qos%sRule' % utils.camelize(rule) field = '%s_rules' % rule cls.fields[field] = obj_fields.ListOfObjectsField(rule_cls_name) @@ -48,7 +48,7 @@ class QosRulesExtenderMeta(abc.ABCMeta): @obj_base.VersionedObjectRegistry.register @six.add_metaclass(QosRulesExtenderMeta) -class QosPolicy(base.NeutronObject): +class QosPolicy(base.NeutronDbObject): db_model = qos_db_model.QosPolicy diff --git a/neutron/objects/qos/rule.py b/neutron/objects/qos/rule.py index b2bdd93f4..d62ad9419 100644 --- a/neutron/objects/qos/rule.py +++ b/neutron/objects/qos/rule.py @@ -21,12 +21,12 @@ import six from neutron.db import api as db_api from neutron.db.qos import models as qos_db_model -from neutron.extensions import qos as qos_extension from neutron.objects import base +from neutron.services.qos import qos_consts @six.add_metaclass(abc.ABCMeta) -class QosRule(base.NeutronObject): +class QosRule(base.NeutronDbObject): base_db_model = qos_db_model.QosRule @@ -155,7 +155,7 @@ class QosBandwidthLimitRule(QosRule): db_model = qos_db_model.QosBandwidthLimitRule - rule_type = qos_extension.RULE_TYPE_BANDWIDTH_LIMIT + rule_type = qos_consts.RULE_TYPE_BANDWIDTH_LIMIT fields = { 'max_kbps': obj_fields.IntegerField(nullable=True), diff --git a/neutron/objects/qos/rule_type.py b/neutron/objects/qos/rule_type.py new file mode 100644 index 000000000..1a009b559 --- /dev/null +++ b/neutron/objects/qos/rule_type.py @@ -0,0 +1,41 @@ +# 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 oslo_versionedobjects import base as obj_base +from oslo_versionedobjects import fields as obj_fields + +from neutron import manager +from neutron.objects import base +from neutron.services.qos import qos_consts + + +class RuleTypeField(obj_fields.BaseEnumField): + + def __init__(self, **kwargs): + self.AUTO_TYPE = obj_fields.Enum( + valid_values=qos_consts.VALID_RULE_TYPES) + super(RuleTypeField, self).__init__(**kwargs) + + +@obj_base.VersionedObjectRegistry.register +class QosRuleType(base.NeutronObject): + + fields = { + 'type': RuleTypeField(), + } + + # we don't receive context because we don't need db access at all + @classmethod + def get_objects(cls, **kwargs): + core_plugin = manager.NeutronManager.get_plugin() + return [cls(type=type_) + for type_ in core_plugin.supported_qos_rule_types] diff --git a/neutron/plugins/ml2/drivers/linuxbridge/mech_driver/mech_linuxbridge.py b/neutron/plugins/ml2/drivers/linuxbridge/mech_driver/mech_linuxbridge.py index f69b5da41..0269c67d4 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/mech_driver/mech_linuxbridge.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/mech_driver/mech_linuxbridge.py @@ -20,6 +20,7 @@ from neutron.common import constants from neutron.extensions import portbindings from neutron.plugins.common import constants as p_constants from neutron.plugins.ml2.drivers import mech_agent +from neutron.services.qos import qos_consts LOG = log.getLogger(__name__) @@ -34,6 +35,12 @@ class LinuxbridgeMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): network. """ + # TODO(QoS): really, there is no support for QoS in the driver. Leaving it + # here since API tests are executed against both ovs and lb drivers, and it + # effectively makes ml2 plugin return an empty list for supported rule + # types + supported_qos_rule_types = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT] + def __init__(self): sg_enabled = securitygroups_rpc.is_firewall_enabled() super(LinuxbridgeMechanismDriver, self).__init__( diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py b/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py index de7da77e8..2902218be 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py @@ -17,7 +17,7 @@ from oslo_log import log as logging from neutron.agent.common import ovs_lib from neutron.agent.l2.extensions import qos_agent -from neutron.extensions import qos +from neutron.services.qos import qos_consts LOG = logging.getLogger(__name__) @@ -33,11 +33,11 @@ class QosOVSAgentDriver(qos_agent.QosAgentDriver): self.handlers = {} def initialize(self): - self.handlers[('update', qos.RULE_TYPE_BANDWIDTH_LIMIT)] = ( + self.handlers[('update', qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)] = ( self._update_bw_limit_rule) - self.handlers[('create', qos.RULE_TYPE_BANDWIDTH_LIMIT)] = ( + self.handlers[('create', qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)] = ( self._update_bw_limit_rule) - self.handlers[('delete', qos.RULE_TYPE_BANDWIDTH_LIMIT)] = ( + self.handlers[('delete', qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)] = ( self._delete_bw_limit_rule) self.br_int = ovs_lib.OVSBridge(self.br_int_name) diff --git a/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py b/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py index 13128a246..2ad29dd00 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py +++ b/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py @@ -20,6 +20,7 @@ from neutron.common import constants from neutron.extensions import portbindings from neutron.plugins.common import constants as p_constants from neutron.plugins.ml2.drivers import mech_agent +from neutron.services.qos import qos_consts LOG = log.getLogger(__name__) @@ -34,6 +35,8 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): network. """ + supported_qos_rule_types = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT] + def __init__(self): sg_enabled = securitygroups_rpc.is_firewall_enabled() vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled, diff --git a/neutron/plugins/ml2/managers.py b/neutron/plugins/ml2/managers.py index 0de60e5d6..d4b490881 100644 --- a/neutron/plugins/ml2/managers.py +++ b/neutron/plugins/ml2/managers.py @@ -25,11 +25,12 @@ from neutron.extensions import multiprovidernet as mpnet from neutron.extensions import portbindings from neutron.extensions import providernet as provider from neutron.extensions import vlantransparent -from neutron.i18n import _LE, _LI +from neutron.i18n import _LE, _LI, _LW from neutron.plugins.ml2.common import exceptions as ml2_exc from neutron.plugins.ml2 import db from neutron.plugins.ml2 import driver_api as api from neutron.plugins.ml2 import models +from neutron.services.qos import qos_consts LOG = log.getLogger(__name__) @@ -312,6 +313,40 @@ class MechanismManager(stevedore.named.NamedExtensionManager): LOG.info(_LI("Registered mechanism drivers: %s"), [driver.name for driver in self.ordered_mech_drivers]) + @property + def supported_qos_rule_types(self): + if not self.ordered_mech_drivers: + return [] + + rule_types = set(qos_consts.VALID_RULE_TYPES) + + # Recalculate on every call to allow drivers determine supported rule + # types dynamically + for driver in self.ordered_mech_drivers: + if hasattr(driver.obj, 'supported_qos_rule_types'): + new_rule_types = \ + rule_types & set(driver.obj.supported_qos_rule_types) + dropped_rule_types = new_rule_types - rule_types + if dropped_rule_types: + LOG.info( + _LI("%(rule_types)s rule types disabled for ml2 " + "because %(driver)s does not support them"), + {'rule_types': ', '.join(dropped_rule_types), + 'driver': driver.name}) + rule_types = new_rule_types + else: + # at least one of drivers does not support QoS, meaning there + # are no rule types supported by all of them + LOG.warn( + _LW("%s does not support QoS; no rule types available"), + driver.name) + return [] + + rule_types = list(rule_types) + LOG.debug("Supported QoS rule types " + "(common subset for all mech drivers): %s", rule_types) + return rule_types + def initialize(self): for driver in self.ordered_mech_drivers: LOG.info(_LI("Initializing mechanism driver '%s'"), driver.name) diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 7d3594250..33b3f6334 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -165,6 +165,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, ) self.start_periodic_dhcp_agent_status_check() + @property + def supported_qos_rule_types(self): + return self.mechanism_manager.supported_qos_rule_types + @log_helpers.log_method_call def start_rpc_listeners(self): """Start the RPC loop to let the plugin communicate with agents.""" diff --git a/neutron/services/qos/qos_consts.py b/neutron/services/qos/qos_consts.py new file mode 100644 index 000000000..0a7407f96 --- /dev/null +++ b/neutron/services/qos/qos_consts.py @@ -0,0 +1,17 @@ +# Copyright (c) 2015 Red Hat 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. + +RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth_limit' +VALID_RULE_TYPES = [RULE_TYPE_BANDWIDTH_LIMIT] diff --git a/neutron/services/qos/qos_plugin.py b/neutron/services/qos/qos_plugin.py index 7c1864559..fb84aa9de 100644 --- a/neutron/services/qos/qos_plugin.py +++ b/neutron/services/qos/qos_plugin.py @@ -22,6 +22,7 @@ from neutron.extensions import qos from neutron.i18n import _LW from neutron.objects.qos import policy as policy_object from neutron.objects.qos import rule as rule_object +from neutron.objects.qos import rule_type as rule_type_object from neutron.plugins.common import constants from oslo_log import log as logging @@ -140,4 +141,5 @@ class QoSPlugin(qos.QoSPluginBase): def get_rule_types(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): - pass + return [rule_type_obj.to_dict() for rule_type_obj in + rule_type_object.QosRuleType.get_objects()] diff --git a/neutron/tests/api/test_qos.py b/neutron/tests/api/test_qos.py index ac262941d..a12470397 100644 --- a/neutron/tests/api/test_qos.py +++ b/neutron/tests/api/test_qos.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron.services.qos import qos_consts from neutron.tests.api import base from neutron.tests.tempest import config from neutron.tests.tempest import test @@ -70,6 +71,25 @@ class QosTestJSON(base.BaseAdminNetworkTest): rules_ids = [r['id'] for r in rules] self.assertIn(rule['id'], rules_ids) + @test.attr(type='smoke') + @test.idempotent_id('cf776f77-8d3d-49f2-8572-12d6a1557224') + def test_list_rule_types(self): + # List supported rule types + expected_rule_types = qos_consts.VALID_RULE_TYPES + expected_rule_details = ['type'] + + rule_types = self.admin_client.list_qos_rule_types() + actual_list_rule_types = rule_types['rule_types'] + actual_rule_types = [rule['type'] for rule in actual_list_rule_types] + + # Verify that only required fields present in rule details + for rule in actual_list_rule_types: + self.assertEqual(tuple(rule.keys()), tuple(expected_rule_details)) + + # Verify if expected rules are present in the actual rules list + for rule in expected_rule_types: + self.assertIn(rule, actual_rule_types) + #TODO(QoS): policy update (name) #TODO(QoS): create several bandwidth-limit rules (not sure it makes sense, # but to test more than one rule) diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py index a95443294..b17fa4864 100644 --- a/neutron/tests/tempest/services/network/json/network_client.py +++ b/neutron/tests/tempest/services/network/json/network_client.py @@ -68,6 +68,7 @@ class NetworkClientJSON(service_client.ServiceClient): 'firewalls': 'fw', 'policies': 'qos', 'bandwidth_limit_rules': 'qos', + 'rule_types': 'qos', } service_prefix = service_resource_prefix_map.get( plural_name) @@ -692,3 +693,10 @@ class NetworkClientJSON(service_client.ServiceClient): resp, body = self.put(uri, json.dumps(post_data)) self.expected_success(200, 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) + self.expected_success(200, resp.status) + body = json.loads(body) + return service_client.ResponseBody(resp, body) diff --git a/neutron/tests/unit/objects/qos/test_rule_type.py b/neutron/tests/unit/objects/qos/test_rule_type.py new file mode 100644 index 000000000..b9a315903 --- /dev/null +++ b/neutron/tests/unit/objects/qos/test_rule_type.py @@ -0,0 +1,46 @@ +# 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. + +# rule types are so different from other objects that we don't base the test +# class on the common base class for all objects + +import mock + +from neutron import manager +from neutron.objects.qos import rule_type +from neutron.services.qos import qos_consts +from neutron.tests import base as test_base + + +DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' + + +class QosRuleTypeObjectTestCase(test_base.BaseTestCase): + + def setUp(self): + self.config_parse() + self.setup_coreplugin(DB_PLUGIN_KLASS) + super(QosRuleTypeObjectTestCase, self).setUp() + + def test_get_objects(self): + core_plugin = manager.NeutronManager.get_plugin() + rule_types_mock = mock.PropertyMock( + return_value=qos_consts.VALID_RULE_TYPES) + with mock.patch.object(core_plugin, 'supported_qos_rule_types', + new_callable=rule_types_mock, + create=True): + types = rule_type.QosRuleType.get_objects() + self.assertEqual(sorted(qos_consts.VALID_RULE_TYPES), + sorted(type_['type'] for type_ in types)) + + def test_wrong_type(self): + self.assertRaises(ValueError, rule_type.QosRuleType, type='bad_type') diff --git a/neutron/tests/unit/objects/test_base.py b/neutron/tests/unit/objects/test_base.py index 0b1c4b239..932e22ab0 100644 --- a/neutron/tests/unit/objects/test_base.py +++ b/neutron/tests/unit/objects/test_base.py @@ -29,7 +29,7 @@ class FakeModel(object): @obj_base.VersionedObjectRegistry.register -class FakeNeutronObject(base.NeutronObject): +class FakeNeutronObject(base.NeutronDbObject): db_model = FakeModel diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py index 0d7300b6f..3a55fce8d 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py @@ -12,9 +12,9 @@ import mock -from neutron.extensions import qos from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers import ( qos_driver) +from neutron.services.qos import qos_consts from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent import ( ovs_test_base) @@ -37,7 +37,7 @@ class OVSQoSAgentDriverBwLimitRule(ovs_test_base.OVSAgentConfigTestBase): self.port = self._create_fake_port() def _create_bw_limit_rule(self): - return {'type': qos.RULE_TYPE_BANDWIDTH_LIMIT, + return {'type': qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, 'max_kbps': '200', 'max_burst_kbps': '2'} diff --git a/neutron/tests/unit/plugins/ml2/test_plugin.py b/neutron/tests/unit/plugins/ml2/test_plugin.py index aa9cc520d..948a27b64 100644 --- a/neutron/tests/unit/plugins/ml2/test_plugin.py +++ b/neutron/tests/unit/plugins/ml2/test_plugin.py @@ -49,6 +49,7 @@ from neutron.plugins.ml2 import driver_context from neutron.plugins.ml2.drivers import type_vlan from neutron.plugins.ml2 import models from neutron.plugins.ml2 import plugin as ml2_plugin +from neutron.services.qos import qos_consts from neutron.tests import base from neutron.tests.unit import _test_extension_portbindings as test_bindings from neutron.tests.unit.agent import test_securitygroups_rpc as test_sg_rpc @@ -139,6 +140,37 @@ class TestMl2BulkToggleWithoutBulkless(Ml2PluginV2TestCase): self.assertFalse(self._skip_native_bulk) +class TestMl2SupportedQosRuleTypes(Ml2PluginV2TestCase): + + def test_empty_driver_list(self, *mocks): + mech_drivers_mock = mock.PropertyMock(return_value=[]) + with mock.patch.object(self.driver.mechanism_manager, + 'ordered_mech_drivers', + new_callable=mech_drivers_mock): + self.assertEqual( + [], self.driver.mechanism_manager.supported_qos_rule_types) + + def test_no_rule_types_in_common(self): + self.assertEqual( + [], self.driver.mechanism_manager.supported_qos_rule_types) + + @mock.patch.object(mech_logger.LoggerMechanismDriver, + 'supported_qos_rule_types', + new_callable=mock.PropertyMock, + create=True) + @mock.patch.object(mech_test.TestMechanismDriver, + 'supported_qos_rule_types', + new_callable=mock.PropertyMock, + create=True) + def test_rule_type_in_common(self, *mocks): + # make sure both plugins have the same supported qos rule types + for mock_ in mocks: + mock_.return_value = qos_consts.VALID_RULE_TYPES + self.assertEqual( + qos_consts.VALID_RULE_TYPES, + self.driver.mechanism_manager.supported_qos_rule_types) + + class TestMl2BasicGet(test_plugin.TestBasicGet, Ml2PluginV2TestCase): pass -- 2.45.2