From 2cc547241c99b01e36fdc69a08c59f975b32c508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awek=20Kap=C5=82o=C5=84ski?= Date: Mon, 19 Jun 2017 06:35:25 +0000 Subject: [PATCH] New API call to get details of supported QoS rule type This commit adds new API call that allows to discover details about supported QoS rule type and its parameters by each of loaded backend drivers. DocImpact: New call to get details about supported rule_type for each loaded backend driver ApiImpact Change-Id: I2008e9d3e400dd717434fbdd2e693c9c5e34c3a4 Closes-Bug: #1686035 --- neutron/common/constants.py | 4 + neutron/extensions/qos.py | 8 +- neutron/extensions/qos_rule_type_details.py | 74 +++++++++++++++++ neutron/objects/qos/rule_type.py | 47 ++++++++++- neutron/services/qos/drivers/manager.py | 39 +++++++++ neutron/services/qos/qos_plugin.py | 14 +++- neutron/tests/tempest/api/test_qos.py | 31 +++++++ .../services/network/json/network_client.py | 8 ++ .../tests/unit/objects/qos/test_rule_type.py | 48 +++++++++++ neutron/tests/unit/objects/test_objects.py | 3 +- .../unit/services/qos/drivers/test_manager.py | 83 +++++++++++++++++++ .../unit/services/qos/test_qos_plugin.py | 34 +++++++- ...ype-details-api-call-27d792980235aec4.yaml | 8 ++ 13 files changed, 395 insertions(+), 6 deletions(-) create mode 100644 neutron/extensions/qos_rule_type_details.py create mode 100644 releasenotes/notes/qos-rule-type-details-api-call-27d792980235aec4.yaml diff --git a/neutron/common/constants.py b/neutron/common/constants.py index a18630251f0..b1105894f81 100644 --- a/neutron/common/constants.py +++ b/neutron/common/constants.py @@ -165,3 +165,7 @@ PORT_BINDING_STATUS_ACTIVE = 'ACTIVE' PORT_BINDING_STATUS_INACTIVE = 'INACTIVE' PORT_BINDING_STATUSES = (PORT_BINDING_STATUS_ACTIVE, PORT_BINDING_STATUS_INACTIVE) + +# Possible types of values (e.g. in QoS rule types) +VALUES_TYPE_CHOICES = "choices" +VALUES_TYPE_RANGE = "range" diff --git a/neutron/extensions/qos.py b/neutron/extensions/qos.py index eab3d15d37f..2959e7bb0f2 100644 --- a/neutron/extensions/qos.py +++ b/neutron/extensions/qos.py @@ -48,6 +48,8 @@ QOS_RULE_COMMON_FIELDS = { 'is_visible': True}, } +RULE_TYPES = "rule_types" + RESOURCE_ATTRIBUTE_MAP = { COLLECTION_NAME: { 'id': {'allow_post': False, 'allow_put': False, @@ -66,7 +68,7 @@ RESOURCE_ATTRIBUTE_MAP = { 'is_visible': True}, 'rules': {'allow_post': False, 'allow_put': False, 'is_visible': True}, }, - 'rule_types': { + RULE_TYPES: { 'type': {'allow_post': False, 'allow_put': False, 'is_visible': True} } @@ -309,6 +311,10 @@ class QoSPluginBase(service_base.ServicePluginBase): def get_plugin_type(cls): return constants.QOS + @abc.abstractmethod + def get_rule_type(self, context, rule_type_name, fields=None): + pass + @abc.abstractmethod def get_rule_types(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): diff --git a/neutron/extensions/qos_rule_type_details.py b/neutron/extensions/qos_rule_type_details.py new file mode 100644 index 00000000000..2bf995d2d8c --- /dev/null +++ b/neutron/extensions/qos_rule_type_details.py @@ -0,0 +1,74 @@ +# Copyright (c) 2017 OVH SAS +# 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. + +from neutron_lib.api import extensions as api_extensions + +from neutron.extensions import qos + + +# The name of the extension. +NAME = "Details of QoS rule types" + +# The alias of the extension. +ALIAS = "qos-rule-type-details" + +# The description of the extension. +DESCRIPTION = ("Expose details about QoS rule types supported by loaded " + "backend drivers") + +# The list of required extensions. +REQUIRED_EXTENSIONS = [qos.ALIAS] + +# The list of optional extensions. +OPTIONAL_EXTENSIONS = None + +# The resource attribute map for the extension. +RESOURCE_ATTRIBUTE_MAP = { + qos.RULE_TYPES: { + 'drivers': {'allow_post': False, 'allow_put': False, + 'is_visible': True} + } +} + + +class Qos_rule_type_details(api_extensions.ExtensionDescriptor): + + @classmethod + def get_name(cls): + return NAME + + @classmethod + def get_alias(cls): + return ALIAS + + @classmethod + def get_description(cls): + return DESCRIPTION + + @classmethod + def get_updated(cls): + return "2017-06-22T10:00:00-00:00" + + def get_required_extensions(self): + return REQUIRED_EXTENSIONS or [] + + def get_optional_extensions(self): + return OPTIONAL_EXTENSIONS or [] + + def get_extended_resources(self, version): + if version == "2.0": + return RESOURCE_ATTRIBUTE_MAP + else: + return {} diff --git a/neutron/objects/qos/rule_type.py b/neutron/objects/qos/rule_type.py index 558a0888881..df70ea32f16 100644 --- a/neutron/objects/qos/rule_type.py +++ b/neutron/objects/qos/rule_type.py @@ -13,10 +13,12 @@ from neutron.plugins.common import constants from neutron_lib.plugins import directory from oslo_log import log as logging +from oslo_utils import versionutils from oslo_versionedobjects import base as obj_base from oslo_versionedobjects import fields as obj_fields from neutron.objects import base +from neutron.objects import common_types from neutron.services.qos import qos_consts LOG = logging.getLogger(__name__) @@ -35,12 +37,29 @@ class QosRuleType(base.NeutronObject): # Version 1.0: Initial version # Version 1.1: Added QosDscpMarkingRule # Version 1.2: Added QosMinimumBandwidthRule - VERSION = '1.2' + # Version 1.3: Added drivers field + VERSION = '1.3' fields = { 'type': RuleTypeField(), + 'drivers': obj_fields.ListOfObjectsField( + 'QosRuleTypeDriver', nullable=True) } + synthetic_fields = ['drivers'] + + # we don't receive context because we don't need db access at all + @classmethod + def get_object(cls, rule_type_name, **kwargs): + plugin = directory.get_plugin(alias=constants.QOS) + drivers = plugin.supported_rule_type_details(rule_type_name) + drivers_obj = [QosRuleTypeDriver( + name=driver['name'], + supported_parameters=driver['supported_parameters']) + for driver in drivers] + + return cls(type=rule_type_name, drivers=drivers_obj) + # we don't receive context because we don't need db access at all @classmethod def get_objects(cls, validate_filters=True, **kwargs): @@ -52,3 +71,29 @@ class QosRuleType(base.NeutronObject): # TODO(ihrachys): apply filters to returned result return [cls(type=type_) for type_ in rule_types] + + def obj_make_compatible(self, primitive, target_version): + _target_version = versionutils.convert_version_to_tuple(target_version) + + if _target_version < (1, 3): + primitive.pop('drivers', None) + + +@obj_base.VersionedObjectRegistry.register +class QosRuleTypeDriver(base.NeutronObject): + # Version 1.0: Initial version + VERSION = '1.0' + + fields = { + 'name': obj_fields.StringField(), + 'supported_parameters': common_types.ListOfDictOfMiscValuesField() + } + + def to_dict(self): + return { + 'name': self.name, + 'supported_parameters': self.supported_parameters} + + @classmethod + def get_objects(cls, context, **kwargs): + raise NotImplementedError() diff --git a/neutron/services/qos/drivers/manager.py b/neutron/services/qos/drivers/manager.py index ede5e83211c..9474f4ea572 100644 --- a/neutron/services/qos/drivers/manager.py +++ b/neutron/services/qos/drivers/manager.py @@ -20,6 +20,7 @@ from neutron.api.rpc.callbacks import events as rpc_events from neutron.api.rpc.callbacks.producer import registry as rpc_registry from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc +from neutron.common import constants from neutron.common import exceptions from neutron.objects.qos import policy as policy_object from neutron.services.qos import qos_consts @@ -83,6 +84,20 @@ class QosServiceDriverManager(object): 'driver': driver.name}) return False + @staticmethod + def _parse_parameter_values(parameter_values): + validator, possible_values = list(parameter_values.items())[0] + if validator == 'type:range': + parameter_values = { + "start": possible_values[0], + "end": possible_values[1] + } + parameter_type = constants.VALUES_TYPE_RANGE + elif validator == 'type:values': + parameter_values = possible_values + parameter_type = constants.VALUES_TYPE_CHOICES + return parameter_values, parameter_type + def call(self, method_name, *args, **kwargs): """Helper method for calling a method across all extension drivers.""" exc_list = [] @@ -158,3 +173,27 @@ class QosServiceDriverManager(object): LOG.debug("Supported QoS rule types " "(common subset for all loaded QoS drivers): %s", rule_types) return rule_types + + def supported_rule_type_details(self, rule_type_name): + if not self._drivers: + return [] + + rule_type_drivers = [] + for driver in self._drivers: + if rule_type_name in driver.supported_rules: + supported_parameters = [] + rule_parameters = driver.supported_rules.get(rule_type_name) + for name, values in rule_parameters.items(): + parameter_values, parameter_type = ( + self._parse_parameter_values(values)) + supported_parameters.append({ + "parameter_name": name, + "parameter_values": parameter_values, + "parameter_type": parameter_type + }) + rule_type_drivers.append({ + "name": driver.name, + "supported_parameters": supported_parameters + }) + + return rule_type_drivers diff --git a/neutron/services/qos/qos_plugin.py b/neutron/services/qos/qos_plugin.py index 8b61fbceab3..1acfcf8bf6d 100644 --- a/neutron/services/qos/qos_plugin.py +++ b/neutron/services/qos/qos_plugin.py @@ -16,6 +16,7 @@ from neutron_lib.callbacks import events as callbacks_events from neutron_lib.callbacks import registry as callbacks_registry from neutron_lib.callbacks import resources as callbacks_resources +from neutron_lib import exceptions as lib_exc from neutron.common import exceptions as n_exc from neutron.db import api as db_api @@ -40,7 +41,8 @@ class QoSPlugin(qos.QoSPluginBase): """ supported_extension_aliases = ['qos', 'qos-bw-limit-direction', - 'qos-default'] + 'qos-default', + 'qos-rule-type-details'] __native_pagination_support = True __native_sorting_support = True @@ -264,6 +266,13 @@ class QoSPlugin(qos.QoSPluginBase): return policy_object.QosPolicy.get_objects(context, _pager=pager, **filters) + @db_base_plugin_common.filter_fields + @db_base_plugin_common.convert_result_to_dict + def get_rule_type(self, context, rule_type_name, fields=None): + if not context.is_admin: + raise lib_exc.NotAuthorized() + return rule_type_object.QosRuleType.get_object(rule_type_name) + @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_rule_types(self, context, filters=None, fields=None, @@ -273,6 +282,9 @@ class QoSPlugin(qos.QoSPluginBase): filters = {} return rule_type_object.QosRuleType.get_objects(**filters) + def supported_rule_type_details(self, rule_type_name): + return self.driver_manager.supported_rule_type_details(rule_type_name) + @property def supported_rule_types(self): return self.driver_manager.supported_rule_types diff --git a/neutron/tests/tempest/api/test_qos.py b/neutron/tests/tempest/api/test_qos.py index f35abd8e084..7735710484b 100644 --- a/neutron/tests/tempest/api/test_qos.py +++ b/neutron/tests/tempest/api/test_qos.py @@ -31,6 +31,12 @@ class QosTestJSON(base.BaseAdminNetworkTest): required_extensions = ['qos'] + @staticmethod + def _get_driver_details(rule_type_details, driver_name): + for driver in rule_type_details['drivers']: + if driver['name'] == driver_name: + return driver + @decorators.idempotent_id('108fbdf7-3463-4e47-9871-d07f3dcf5bbb') def test_create_policy(self): policy = self.create_qos_policy(name='test-policy', @@ -172,6 +178,31 @@ class QosTestJSON(base.BaseAdminNetworkTest): for rule in actual_list_rule_types: self.assertEqual(tuple(expected_rule_keys), tuple(rule.keys())) + @decorators.idempotent_id('8ececa21-ef97-4904-a152-9f04c90f484d') + def test_show_rule_type_details_as_user(self): + self.assertRaises( + exceptions.Forbidden, + self.client.show_qos_rule_type, + qos_consts.RULE_TYPE_BANDWIDTH_LIMIT) + + @decorators.idempotent_id('d0a2460b-7325-481f-a531-050bd96ab25e') + def test_show_rule_type_details_as_admin(self): + # Since returned rule types depend on loaded backend drivers this test + # is checking only if returned keys are same as expected keys + + # In theory, we could make the test conditional on which ml2 drivers + # are enabled in gate, but that option doesn't seem to be + # available through tempest.lib framework + expected_rule_type_details_keys = ['type', 'drivers'] + + rule_type_details = self.admin_client.show_qos_rule_type( + qos_consts.RULE_TYPE_BANDWIDTH_LIMIT).get("rule_type") + + # Verify that only required fields present in rule details + self.assertEqual( + sorted(tuple(expected_rule_type_details_keys)), + sorted(tuple(rule_type_details.keys()))) + def _disassociate_network(self, client, network_id): updated_network = client.update_network(network_id, qos_policy_id=None) diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py index f5621ea785d..7c311f46dbc 100644 --- a/neutron/tests/tempest/services/network/json/network_client.py +++ b/neutron/tests/tempest/services/network/json/network_client.py @@ -727,6 +727,14 @@ class NetworkClientJSON(service_client.RestClient): body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) + def show_qos_rule_type(self, rule_type_name): + uri = '%s/qos/rule-types/%s' % ( + self.uri_prefix, rule_type_name) + resp, body = self.get(uri) + self.expected_success(200, resp.status) + body = jsonutils.loads(body) + return service_client.ResponseBody(resp, body) + def create_trunk(self, parent_port_id, subports, tenant_id=None, name=None, admin_state_up=None, description=None): diff --git a/neutron/tests/unit/objects/qos/test_rule_type.py b/neutron/tests/unit/objects/qos/test_rule_type.py index 97ba7da92a8..bc609812e0f 100644 --- a/neutron/tests/unit/objects/qos/test_rule_type.py +++ b/neutron/tests/unit/objects/qos/test_rule_type.py @@ -16,6 +16,7 @@ import mock from oslo_config import cfg +from neutron.common import constants from neutron import manager from neutron.objects.qos import rule_type from neutron.services.qos import qos_consts @@ -25,6 +26,22 @@ from neutron.tests import base as test_base DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' +DRIVER_SUPPORTED_PARAMETERS = [ + { + 'parameter_name': qos_consts.MAX_KBPS, + 'parameter_type': constants.VALUES_TYPE_RANGE, + 'parameter_values': {"start": 0, "end": constants.DB_INTEGER_MAX_VALUE} + }, { + 'parameter_name': qos_consts.MAX_BURST, + 'parameter_type': constants.VALUES_TYPE_RANGE, + 'parameter_values': {"start": 0, "end": constants.DB_INTEGER_MAX_VALUE} + }, { + 'parameter_name': qos_consts.DIRECTION, + 'parameter_type': constants.VALUES_TYPE_CHOICES, + 'parameter_values': constants.VALID_DIRECTIONS + } +] + class QosRuleTypeObjectTestCase(test_base.BaseTestCase): @@ -37,6 +54,26 @@ class QosRuleTypeObjectTestCase(test_base.BaseTestCase): cfg.CONF.set_override("service_plugins", ["qos"]) manager.init() + def test_get_object(self): + driver_details = { + 'name': "backend_driver", + 'supported_parameters': DRIVER_SUPPORTED_PARAMETERS + } + with mock.patch.object( + qos_plugin.QoSPlugin, 'supported_rule_type_details', + return_value=[driver_details] + ): + rule_type_details = rule_type.QosRuleType.get_object( + qos_consts.RULE_TYPE_BANDWIDTH_LIMIT) + self.assertEqual( + driver_details['name'], rule_type_details.drivers[0].name) + self.assertEqual( + driver_details['supported_parameters'], + rule_type_details.drivers[0].supported_parameters) + self.assertEqual(1, len(rule_type_details.drivers)) + self.assertEqual( + qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, rule_type_details.type) + def test_get_objects(self): rule_types_mock = mock.PropertyMock( return_value=set(qos_consts.VALID_RULE_TYPES)) @@ -64,3 +101,14 @@ class QosRuleTypeObjectTestCase(test_base.BaseTestCase): self.assertIn(qos_consts.RULE_TYPE_DSCP_MARKING, tuple(rule_type_v1_1.fields['type'].AUTO_TYPE. _valid_values)) + + def test_object_version_degradation_1_3_to_1_2(self): + drivers_obj = rule_type.QosRuleTypeDriver( + name="backend_driver", supported_parameters=[{}] + ) + qos_rule_type = rule_type.QosRuleType( + type=qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, drivers=[drivers_obj]) + + rule_type_v1_2 = self._policy_through_version(qos_rule_type, '1.2') + self.assertNotIn("drivers", rule_type_v1_2) + self.assertIn("type", rule_type_v1_2) diff --git a/neutron/tests/unit/objects/test_objects.py b/neutron/tests/unit/objects/test_objects.py index 8711dcfea83..bc68a56893e 100644 --- a/neutron/tests/unit/objects/test_objects.py +++ b/neutron/tests/unit/objects/test_objects.py @@ -66,7 +66,8 @@ object_data = { 'QosBandwidthLimitRule': '1.3-51b662b12a8d1dfa89288d826c6d26d3', 'QosDscpMarkingRule': '1.3-0313c6554b34fd10c753cb63d638256c', 'QosMinimumBandwidthRule': '1.3-314c3419f4799067cc31cc319080adff', - 'QosRuleType': '1.2-e6fd08fcca152c339cbd5e9b94b1b8e7', + 'QosRuleType': '1.3-7286188edeb3a0386f9cf7979b9700fc', + 'QosRuleTypeDriver': '1.0-7d8cb9f0ef661ac03700eae97118e3db', 'QosPolicy': '1.6-4adb0cde3102c10d8970ec9487fd7fe7', 'QosPolicyDefault': '1.0-59e5060eedb1f06dd0935a244d27d11c', 'Quota': '1.0-6bb6a0f1bd5d66a2134ffa1a61873097', diff --git a/neutron/tests/unit/services/qos/drivers/test_manager.py b/neutron/tests/unit/services/qos/drivers/test_manager.py index 8c57323e3f7..dcdbb2dd680 100644 --- a/neutron/tests/unit/services/qos/drivers/test_manager.py +++ b/neutron/tests/unit/services/qos/drivers/test_manager.py @@ -206,6 +206,89 @@ class TestQosDriversManagerRules(TestQosDriversManagerBase): }) self.assertEqual(driver_manager.supported_rule_types, set([])) + def test__parse_parameter_values(self): + range_parameter = {'type:range': [0, 10]} + values_parameter = {'type:values': [1, 10, 100, 1000]} + expected_parsed_range_parameter = {'start': 0, 'end': 10} + expected_parsed_values_parameter = [1, 10, 100, 1000] + + parameter_values, parameter_type = ( + driver_mgr.QosServiceDriverManager._parse_parameter_values( + range_parameter)) + self.assertEqual( + expected_parsed_range_parameter, parameter_values) + self.assertEqual( + constants.VALUES_TYPE_RANGE, parameter_type) + + parameter_values, parameter_type = ( + driver_mgr.QosServiceDriverManager._parse_parameter_values( + values_parameter)) + self.assertEqual( + expected_parsed_values_parameter, parameter_values) + self.assertEqual( + constants.VALUES_TYPE_CHOICES, parameter_type) + + def test_supported_rule_type_details(self): + driver_manager = self._create_manager_with_drivers({ + 'driver-A': { + 'is_loaded': True, + 'rules': { + qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: { + "max_kbps": {'type:range': [0, 1000]}, + "max_burst_kbps": {'type:range': [0, 1000]} + } + } + }, + 'driver-B': { + 'is_loaded': True, + 'rules': { + qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: { + "min_kbps": {'type:range': [0, 1000]}, + 'direction': { + 'type:values': constants.VALID_DIRECTIONS} + }, + qos_consts.RULE_TYPE_DSCP_MARKING: { + "dscp_mark": { + 'type:values': constants.VALID_DSCP_MARKS} + } + } + } + }) + expected_rule_type_details = [{ + 'name': 'driver-A', + 'supported_parameters': [{ + 'parameter_name': 'max_kbps', + 'parameter_type': constants.VALUES_TYPE_RANGE, + 'parameter_values': {'start': 0, 'end': 1000} + }, { + 'parameter_name': 'max_burst_kbps', + 'parameter_type': constants.VALUES_TYPE_RANGE, + 'parameter_values': {'start': 0, 'end': 1000} + }] + }] + bandwidth_limit_details = driver_manager.supported_rule_type_details( + qos_consts.RULE_TYPE_BANDWIDTH_LIMIT) + self.assertEqual( + len(expected_rule_type_details), len(bandwidth_limit_details)) + self.assertEqual( + expected_rule_type_details[0]['name'], + bandwidth_limit_details[0]['name']) + self.assertEqual( + len(expected_rule_type_details[0]['supported_parameters']), + len(bandwidth_limit_details[0]['supported_parameters']) + ) + for parameter in expected_rule_type_details[0]['supported_parameters']: + self.assertIn( + parameter, + bandwidth_limit_details[0]['supported_parameters']) + + def test_supported_rule_type_details_no_drivers_loaded(self): + driver_manager = self._create_manager_with_drivers({}) + self.assertEqual( + [], + driver_manager.supported_rule_type_details( + qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)) + class TestQosDriversCalls(TestQosDriversManagerBase): """Test QoS driver calls""" diff --git a/neutron/tests/unit/services/qos/test_qos_plugin.py b/neutron/tests/unit/services/qos/test_qos_plugin.py index 5b0529383f6..60c4cf6c09f 100644 --- a/neutron/tests/unit/services/qos/test_qos_plugin.py +++ b/neutron/tests/unit/services/qos/test_qos_plugin.py @@ -12,16 +12,18 @@ import mock from neutron_lib import context +from neutron_lib import exceptions as lib_exc from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import uuidutils +from neutron.common import constants from neutron.common import exceptions as n_exc from neutron import manager from neutron.objects import base as base_object from neutron.objects.qos import policy as policy_object from neutron.objects.qos import rule as rule_object -from neutron.plugins.common import constants +from neutron.plugins.common import constants as plugins_constants from neutron.services.qos import qos_consts from neutron.services.qos import qos_plugin from neutron.tests.unit.services.qos import base @@ -55,7 +57,7 @@ class TestQosPlugin(base.BaseQosTestCase): cfg.CONF.set_override("service_plugins", ["qos"]) manager.init() - self.qos_plugin = directory.get_plugin(constants.QOS) + self.qos_plugin = directory.get_plugin(plugins_constants.QOS) self.qos_plugin.driver_manager = mock.Mock() @@ -811,6 +813,34 @@ class TestQosPlugin(base.BaseQosTestCase): self.assertRaises(AttributeError, getattr, self.qos_plugin, 'create_policy_bandwidth_limit_rules') + def test_get_rule_type(self): + admin_ctxt = context.get_admin_context() + drivers_details = [{ + 'name': 'fake-driver', + 'supported_parameters': [{ + 'parameter_name': 'max_kbps', + 'parameter_type': constants.VALUES_TYPE_RANGE, + 'parameter_range': {'start': 0, 'end': 100} + }] + }] + with mock.patch.object( + qos_plugin.QoSPlugin, "supported_rule_type_details", + return_value=drivers_details + ): + rule_type_details = self.qos_plugin.get_rule_type( + admin_ctxt, qos_consts.RULE_TYPE_BANDWIDTH_LIMIT) + self.assertEqual( + qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, + rule_type_details['type']) + self.assertEqual( + drivers_details, rule_type_details['drivers']) + + def test_get_rule_type_as_user(self): + self.assertRaises( + lib_exc.NotAuthorized, + self.qos_plugin.get_rule_type, + self.ctxt, qos_consts.RULE_TYPE_BANDWIDTH_LIMIT) + def test_get_rule_types(self): rule_types_mock = mock.PropertyMock( return_value=qos_consts.VALID_RULE_TYPES) diff --git a/releasenotes/notes/qos-rule-type-details-api-call-27d792980235aec4.yaml b/releasenotes/notes/qos-rule-type-details-api-call-27d792980235aec4.yaml new file mode 100644 index 00000000000..945e1616a12 --- /dev/null +++ b/releasenotes/notes/qos-rule-type-details-api-call-27d792980235aec4.yaml @@ -0,0 +1,8 @@ +--- +prelude: > + New API to get details of supported rule types. +features: + - | + The QoS service plugin can now expose details about supported QoS rule + types in Neutron deployment. + New API call is allowed only for users with admin priviliges.