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
-------------
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).
-
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,
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
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)
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):
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)
@obj_base.VersionedObjectRegistry.register
@six.add_metaclass(QosRulesExtenderMeta)
-class QosPolicy(base.NeutronObject):
+class QosPolicy(base.NeutronDbObject):
db_model = qos_db_model.QosPolicy
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
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),
--- /dev/null
+# 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]
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__)
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__(
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__)
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)
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__)
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,
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__)
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)
)
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."""
--- /dev/null
+# 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]
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
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()]
# 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
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)
'firewalls': 'fw',
'policies': 'qos',
'bandwidth_limit_rules': 'qos',
+ 'rule_types': 'qos',
}
service_prefix = service_resource_prefix_map.get(
plural_name)
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)
--- /dev/null
+# 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')
@obj_base.VersionedObjectRegistry.register
-class FakeNeutronObject(base.NeutronObject):
+class FakeNeutronObject(base.NeutronDbObject):
db_model = FakeModel
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)
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'}
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
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