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
This commit is contained in:
parent
9d20de62bf
commit
2cc547241c
@ -165,3 +165,7 @@ PORT_BINDING_STATUS_ACTIVE = 'ACTIVE'
|
|||||||
PORT_BINDING_STATUS_INACTIVE = 'INACTIVE'
|
PORT_BINDING_STATUS_INACTIVE = 'INACTIVE'
|
||||||
PORT_BINDING_STATUSES = (PORT_BINDING_STATUS_ACTIVE,
|
PORT_BINDING_STATUSES = (PORT_BINDING_STATUS_ACTIVE,
|
||||||
PORT_BINDING_STATUS_INACTIVE)
|
PORT_BINDING_STATUS_INACTIVE)
|
||||||
|
|
||||||
|
# Possible types of values (e.g. in QoS rule types)
|
||||||
|
VALUES_TYPE_CHOICES = "choices"
|
||||||
|
VALUES_TYPE_RANGE = "range"
|
||||||
|
@ -48,6 +48,8 @@ QOS_RULE_COMMON_FIELDS = {
|
|||||||
'is_visible': True},
|
'is_visible': True},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RULE_TYPES = "rule_types"
|
||||||
|
|
||||||
RESOURCE_ATTRIBUTE_MAP = {
|
RESOURCE_ATTRIBUTE_MAP = {
|
||||||
COLLECTION_NAME: {
|
COLLECTION_NAME: {
|
||||||
'id': {'allow_post': False, 'allow_put': False,
|
'id': {'allow_post': False, 'allow_put': False,
|
||||||
@ -66,7 +68,7 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||||||
'is_visible': True},
|
'is_visible': True},
|
||||||
'rules': {'allow_post': False, 'allow_put': False, 'is_visible': True},
|
'rules': {'allow_post': False, 'allow_put': False, 'is_visible': True},
|
||||||
},
|
},
|
||||||
'rule_types': {
|
RULE_TYPES: {
|
||||||
'type': {'allow_post': False, 'allow_put': False,
|
'type': {'allow_post': False, 'allow_put': False,
|
||||||
'is_visible': True}
|
'is_visible': True}
|
||||||
}
|
}
|
||||||
@ -309,6 +311,10 @@ class QoSPluginBase(service_base.ServicePluginBase):
|
|||||||
def get_plugin_type(cls):
|
def get_plugin_type(cls):
|
||||||
return constants.QOS
|
return constants.QOS
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_rule_type(self, context, rule_type_name, fields=None):
|
||||||
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_rule_types(self, context, filters=None, fields=None, sorts=None,
|
def get_rule_types(self, context, filters=None, fields=None, sorts=None,
|
||||||
limit=None, marker=None, page_reverse=False):
|
limit=None, marker=None, page_reverse=False):
|
||||||
|
74
neutron/extensions/qos_rule_type_details.py
Normal file
74
neutron/extensions/qos_rule_type_details.py
Normal file
@ -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 {}
|
@ -13,10 +13,12 @@
|
|||||||
from neutron.plugins.common import constants
|
from neutron.plugins.common import constants
|
||||||
from neutron_lib.plugins import directory
|
from neutron_lib.plugins import directory
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import versionutils
|
||||||
from oslo_versionedobjects import base as obj_base
|
from oslo_versionedobjects import base as obj_base
|
||||||
from oslo_versionedobjects import fields as obj_fields
|
from oslo_versionedobjects import fields as obj_fields
|
||||||
|
|
||||||
from neutron.objects import base
|
from neutron.objects import base
|
||||||
|
from neutron.objects import common_types
|
||||||
from neutron.services.qos import qos_consts
|
from neutron.services.qos import qos_consts
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -35,12 +37,29 @@ class QosRuleType(base.NeutronObject):
|
|||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
# Version 1.1: Added QosDscpMarkingRule
|
# Version 1.1: Added QosDscpMarkingRule
|
||||||
# Version 1.2: Added QosMinimumBandwidthRule
|
# Version 1.2: Added QosMinimumBandwidthRule
|
||||||
VERSION = '1.2'
|
# Version 1.3: Added drivers field
|
||||||
|
VERSION = '1.3'
|
||||||
|
|
||||||
fields = {
|
fields = {
|
||||||
'type': RuleTypeField(),
|
'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
|
# we don't receive context because we don't need db access at all
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_objects(cls, validate_filters=True, **kwargs):
|
def get_objects(cls, validate_filters=True, **kwargs):
|
||||||
@ -52,3 +71,29 @@ class QosRuleType(base.NeutronObject):
|
|||||||
|
|
||||||
# TODO(ihrachys): apply filters to returned result
|
# TODO(ihrachys): apply filters to returned result
|
||||||
return [cls(type=type_) for type_ in rule_types]
|
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()
|
||||||
|
@ -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.producer import registry as rpc_registry
|
||||||
from neutron.api.rpc.callbacks import resources
|
from neutron.api.rpc.callbacks import resources
|
||||||
from neutron.api.rpc.handlers import resources_rpc
|
from neutron.api.rpc.handlers import resources_rpc
|
||||||
|
from neutron.common import constants
|
||||||
from neutron.common import exceptions
|
from neutron.common import exceptions
|
||||||
from neutron.objects.qos import policy as policy_object
|
from neutron.objects.qos import policy as policy_object
|
||||||
from neutron.services.qos import qos_consts
|
from neutron.services.qos import qos_consts
|
||||||
@ -83,6 +84,20 @@ class QosServiceDriverManager(object):
|
|||||||
'driver': driver.name})
|
'driver': driver.name})
|
||||||
return False
|
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):
|
def call(self, method_name, *args, **kwargs):
|
||||||
"""Helper method for calling a method across all extension drivers."""
|
"""Helper method for calling a method across all extension drivers."""
|
||||||
exc_list = []
|
exc_list = []
|
||||||
@ -158,3 +173,27 @@ class QosServiceDriverManager(object):
|
|||||||
LOG.debug("Supported QoS rule types "
|
LOG.debug("Supported QoS rule types "
|
||||||
"(common subset for all loaded QoS drivers): %s", rule_types)
|
"(common subset for all loaded QoS drivers): %s", rule_types)
|
||||||
return 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
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
from neutron_lib.callbacks import events as callbacks_events
|
from neutron_lib.callbacks import events as callbacks_events
|
||||||
from neutron_lib.callbacks import registry as callbacks_registry
|
from neutron_lib.callbacks import registry as callbacks_registry
|
||||||
from neutron_lib.callbacks import resources as callbacks_resources
|
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.common import exceptions as n_exc
|
||||||
from neutron.db import api as db_api
|
from neutron.db import api as db_api
|
||||||
@ -40,7 +41,8 @@ class QoSPlugin(qos.QoSPluginBase):
|
|||||||
"""
|
"""
|
||||||
supported_extension_aliases = ['qos',
|
supported_extension_aliases = ['qos',
|
||||||
'qos-bw-limit-direction',
|
'qos-bw-limit-direction',
|
||||||
'qos-default']
|
'qos-default',
|
||||||
|
'qos-rule-type-details']
|
||||||
|
|
||||||
__native_pagination_support = True
|
__native_pagination_support = True
|
||||||
__native_sorting_support = True
|
__native_sorting_support = True
|
||||||
@ -264,6 +266,13 @@ class QoSPlugin(qos.QoSPluginBase):
|
|||||||
return policy_object.QosPolicy.get_objects(context, _pager=pager,
|
return policy_object.QosPolicy.get_objects(context, _pager=pager,
|
||||||
**filters)
|
**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.filter_fields
|
||||||
@db_base_plugin_common.convert_result_to_dict
|
@db_base_plugin_common.convert_result_to_dict
|
||||||
def get_rule_types(self, context, filters=None, fields=None,
|
def get_rule_types(self, context, filters=None, fields=None,
|
||||||
@ -273,6 +282,9 @@ class QoSPlugin(qos.QoSPluginBase):
|
|||||||
filters = {}
|
filters = {}
|
||||||
return rule_type_object.QosRuleType.get_objects(**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
|
@property
|
||||||
def supported_rule_types(self):
|
def supported_rule_types(self):
|
||||||
return self.driver_manager.supported_rule_types
|
return self.driver_manager.supported_rule_types
|
||||||
|
@ -31,6 +31,12 @@ class QosTestJSON(base.BaseAdminNetworkTest):
|
|||||||
|
|
||||||
required_extensions = ['qos']
|
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')
|
@decorators.idempotent_id('108fbdf7-3463-4e47-9871-d07f3dcf5bbb')
|
||||||
def test_create_policy(self):
|
def test_create_policy(self):
|
||||||
policy = self.create_qos_policy(name='test-policy',
|
policy = self.create_qos_policy(name='test-policy',
|
||||||
@ -172,6 +178,31 @@ class QosTestJSON(base.BaseAdminNetworkTest):
|
|||||||
for rule in actual_list_rule_types:
|
for rule in actual_list_rule_types:
|
||||||
self.assertEqual(tuple(expected_rule_keys), tuple(rule.keys()))
|
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):
|
def _disassociate_network(self, client, network_id):
|
||||||
updated_network = client.update_network(network_id,
|
updated_network = client.update_network(network_id,
|
||||||
qos_policy_id=None)
|
qos_policy_id=None)
|
||||||
|
@ -727,6 +727,14 @@ class NetworkClientJSON(service_client.RestClient):
|
|||||||
body = jsonutils.loads(body)
|
body = jsonutils.loads(body)
|
||||||
return service_client.ResponseBody(resp, 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,
|
def create_trunk(self, parent_port_id, subports,
|
||||||
tenant_id=None, name=None, admin_state_up=None,
|
tenant_id=None, name=None, admin_state_up=None,
|
||||||
description=None):
|
description=None):
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import mock
|
import mock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from neutron.common import constants
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
from neutron.objects.qos import rule_type
|
from neutron.objects.qos import rule_type
|
||||||
from neutron.services.qos import qos_consts
|
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'
|
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):
|
class QosRuleTypeObjectTestCase(test_base.BaseTestCase):
|
||||||
|
|
||||||
@ -37,6 +54,26 @@ class QosRuleTypeObjectTestCase(test_base.BaseTestCase):
|
|||||||
cfg.CONF.set_override("service_plugins", ["qos"])
|
cfg.CONF.set_override("service_plugins", ["qos"])
|
||||||
manager.init()
|
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):
|
def test_get_objects(self):
|
||||||
rule_types_mock = mock.PropertyMock(
|
rule_types_mock = mock.PropertyMock(
|
||||||
return_value=set(qos_consts.VALID_RULE_TYPES))
|
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,
|
self.assertIn(qos_consts.RULE_TYPE_DSCP_MARKING,
|
||||||
tuple(rule_type_v1_1.fields['type'].AUTO_TYPE.
|
tuple(rule_type_v1_1.fields['type'].AUTO_TYPE.
|
||||||
_valid_values))
|
_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)
|
||||||
|
@ -66,7 +66,8 @@ object_data = {
|
|||||||
'QosBandwidthLimitRule': '1.3-51b662b12a8d1dfa89288d826c6d26d3',
|
'QosBandwidthLimitRule': '1.3-51b662b12a8d1dfa89288d826c6d26d3',
|
||||||
'QosDscpMarkingRule': '1.3-0313c6554b34fd10c753cb63d638256c',
|
'QosDscpMarkingRule': '1.3-0313c6554b34fd10c753cb63d638256c',
|
||||||
'QosMinimumBandwidthRule': '1.3-314c3419f4799067cc31cc319080adff',
|
'QosMinimumBandwidthRule': '1.3-314c3419f4799067cc31cc319080adff',
|
||||||
'QosRuleType': '1.2-e6fd08fcca152c339cbd5e9b94b1b8e7',
|
'QosRuleType': '1.3-7286188edeb3a0386f9cf7979b9700fc',
|
||||||
|
'QosRuleTypeDriver': '1.0-7d8cb9f0ef661ac03700eae97118e3db',
|
||||||
'QosPolicy': '1.6-4adb0cde3102c10d8970ec9487fd7fe7',
|
'QosPolicy': '1.6-4adb0cde3102c10d8970ec9487fd7fe7',
|
||||||
'QosPolicyDefault': '1.0-59e5060eedb1f06dd0935a244d27d11c',
|
'QosPolicyDefault': '1.0-59e5060eedb1f06dd0935a244d27d11c',
|
||||||
'Quota': '1.0-6bb6a0f1bd5d66a2134ffa1a61873097',
|
'Quota': '1.0-6bb6a0f1bd5d66a2134ffa1a61873097',
|
||||||
|
@ -206,6 +206,89 @@ class TestQosDriversManagerRules(TestQosDriversManagerBase):
|
|||||||
})
|
})
|
||||||
self.assertEqual(driver_manager.supported_rule_types, set([]))
|
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):
|
class TestQosDriversCalls(TestQosDriversManagerBase):
|
||||||
"""Test QoS driver calls"""
|
"""Test QoS driver calls"""
|
||||||
|
@ -12,16 +12,18 @@
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
from neutron_lib import context
|
from neutron_lib import context
|
||||||
|
from neutron_lib import exceptions as lib_exc
|
||||||
from neutron_lib.plugins import directory
|
from neutron_lib.plugins import directory
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
|
from neutron.common import constants
|
||||||
from neutron.common import exceptions as n_exc
|
from neutron.common import exceptions as n_exc
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
from neutron.objects import base as base_object
|
from neutron.objects import base as base_object
|
||||||
from neutron.objects.qos import policy as policy_object
|
from neutron.objects.qos import policy as policy_object
|
||||||
from neutron.objects.qos import rule as rule_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_consts
|
||||||
from neutron.services.qos import qos_plugin
|
from neutron.services.qos import qos_plugin
|
||||||
from neutron.tests.unit.services.qos import base
|
from neutron.tests.unit.services.qos import base
|
||||||
@ -55,7 +57,7 @@ class TestQosPlugin(base.BaseQosTestCase):
|
|||||||
cfg.CONF.set_override("service_plugins", ["qos"])
|
cfg.CONF.set_override("service_plugins", ["qos"])
|
||||||
|
|
||||||
manager.init()
|
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()
|
self.qos_plugin.driver_manager = mock.Mock()
|
||||||
|
|
||||||
@ -811,6 +813,34 @@ class TestQosPlugin(base.BaseQosTestCase):
|
|||||||
self.assertRaises(AttributeError, getattr, self.qos_plugin,
|
self.assertRaises(AttributeError, getattr, self.qos_plugin,
|
||||||
'create_policy_bandwidth_limit_rules')
|
'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):
|
def test_get_rule_types(self):
|
||||||
rule_types_mock = mock.PropertyMock(
|
rule_types_mock = mock.PropertyMock(
|
||||||
return_value=qos_consts.VALID_RULE_TYPES)
|
return_value=qos_consts.VALID_RULE_TYPES)
|
||||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user