]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Introduce mechanism to determine supported qos rule types for a plugin
authorIhar Hrachyshka <ihrachys@redhat.com>
Thu, 11 Jun 2015 06:11:08 +0000 (08:11 +0200)
committerJohn Schwarz <jschwarz@redhat.com>
Mon, 27 Jul 2015 13:48:48 +0000 (16:48 +0300)
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 <irenab.dev@gmail.com>
Co-Authored-By: John Schwarz <jschwarz@redhat.com>
Change-Id: I0d18ae256877a129e203110003fcadd1d63590b4

19 files changed:
doc/source/devref/quality_of_service.rst
neutron/extensions/qos.py
neutron/objects/base.py
neutron/objects/qos/policy.py
neutron/objects/qos/rule.py
neutron/objects/qos/rule_type.py [new file with mode: 0644]
neutron/plugins/ml2/drivers/linuxbridge/mech_driver/mech_linuxbridge.py
neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py
neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py
neutron/plugins/ml2/managers.py
neutron/plugins/ml2/plugin.py
neutron/services/qos/qos_consts.py [new file with mode: 0644]
neutron/services/qos/qos_plugin.py
neutron/tests/api/test_qos.py
neutron/tests/tempest/services/network/json/network_client.py
neutron/tests/unit/objects/qos/test_rule_type.py [new file with mode: 0644]
neutron/tests/unit/objects/test_base.py
neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py
neutron/tests/unit/plugins/ml2/test_plugin.py

index 53b9942d3c7e087912303cbea4e729f282e621ab..1c5570205c3c2349d79bc55f4276f9390d70be85 100644 (file)
@@ -31,6 +31,21 @@ Service side design
   notifications to any interested agent, using `RPC callbacks <rpc_callbacks.html>`_.
 
 
+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).
-
index 76b9f6f8ac77a8e3944824aa02adf85ee17b8b48..034b8bdc43467df53e2770720fd23dc0846d3e72 100644 (file)
@@ -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,
index 4fe8431d60282bcd9ac30a649ad9a14f15640407..5e1f59263710907ad3c2863594ffa8503b733fc5 100644 (file)
@@ -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)
index a5938d948736a0b1d3f08786b7f1b63dad99d998..53c34a9934b55182e91c0a48790848fac369c96f 100644 (file)
@@ -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
 
index b2bdd93f4b3e89eab5d7ae72b7ace0bb3e3334b0..d62ad9419571e00b42cf651c836c2f332e11b3e5 100644 (file)
@@ -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 (file)
index 0000000..1a009b5
--- /dev/null
@@ -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]
index f69b5da4160061255c12fa90588fb21524407720..0269c67d42d70baa95768d05ea04b6ea566908b6 100644 (file)
@@ -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__(
index de7da77e88add044792e93ad1f05fadbd45af26f..2902218beeab2c41d5a6730e5324d7e6d3fc2d71 100644 (file)
@@ -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)
index 13128a246ba5b743b71c9319547a15d2b70aa349..2ad29dd00b38c61c6d0f4744ca5ec9d11b5364ce 100644 (file)
@@ -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,
index 0de60e5d624b6c420d286b67beaad33480b8cb8d..d4b49088110c13f5a788f0bb14cff7924843997a 100644 (file)
@@ -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)
index 7d359425086fab551e16d808f25a9704e50b5ba0..33b3f63345090bc70e90c1542c848ef17c58e4b7 100644 (file)
@@ -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 (file)
index 0000000..0a7407f
--- /dev/null
@@ -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]
index 7c1864559dd6cefbf76112f883f6787b05d49c0a..fb84aa9de1580dc644ef7b50f53a4da92e359671 100644 (file)
@@ -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()]
index ac262941debc469a5fbf436a9da66f00de18ed06..a1247039795616307c28070a99f292558f6facbe 100644 (file)
@@ -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)
index a95443294331447f86e2419512a6a85b2c063cef..b17fa4864453724f7789dc0288e9d0e9652530c0 100644 (file)
@@ -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 (file)
index 0000000..b9a3159
--- /dev/null
@@ -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')
index 0b1c4b2390a14901b5ab44fc6b7d45e9c1643b1a..932e22ab0eb36f62ca458dd860cc9e3caa77dda1 100644 (file)
@@ -29,7 +29,7 @@ class FakeModel(object):
 
 
 @obj_base.VersionedObjectRegistry.register
-class FakeNeutronObject(base.NeutronObject):
+class FakeNeutronObject(base.NeutronDbObject):
 
     db_model = FakeModel
 
index 0d7300b6fbdaa33b4b4841e9eb8f1dd5ab5599c2..3a55fce8d481501ce72fded82880054fabecc0f7 100644 (file)
@@ -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'}
 
index aa9cc520d46c7be4c13d390fcabfa6570d4e2170..948a27b6485b9b5269ef5d08860898256c149250 100644 (file)
@@ -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