Merge "Support filtering for QoS rule type list"
This commit is contained in:
commit
fd4db01242
@ -98,7 +98,7 @@ NotImplemented.
|
|||||||
Supported QoS rule types
|
Supported QoS rule types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Each QoS driver has a property called supported_rule_types, where the driver
|
Each QoS driver has a member called ``supported_rules``, where the driver
|
||||||
exposes the rules it's able to handle.
|
exposes the rules it's able to handle.
|
||||||
|
|
||||||
For a list of all rule types, see:
|
For a list of all rule types, see:
|
||||||
|
@ -52,6 +52,7 @@ from neutron_lib.api.definitions import qos
|
|||||||
from neutron_lib.api.definitions import qos_bw_limit_direction
|
from neutron_lib.api.definitions import qos_bw_limit_direction
|
||||||
from neutron_lib.api.definitions import qos_default
|
from neutron_lib.api.definitions import qos_default
|
||||||
from neutron_lib.api.definitions import qos_rule_type_details
|
from neutron_lib.api.definitions import qos_rule_type_details
|
||||||
|
from neutron_lib.api.definitions import qos_rule_type_filter
|
||||||
from neutron_lib.api.definitions import qos_rules_alias
|
from neutron_lib.api.definitions import qos_rules_alias
|
||||||
from neutron_lib.api.definitions import quota_check_limit
|
from neutron_lib.api.definitions import quota_check_limit
|
||||||
from neutron_lib.api.definitions import rbac_address_scope
|
from neutron_lib.api.definitions import rbac_address_scope
|
||||||
@ -124,6 +125,7 @@ ML2_SUPPORTED_API_EXTENSIONS = [
|
|||||||
qos_bw_limit_direction.ALIAS,
|
qos_bw_limit_direction.ALIAS,
|
||||||
qos_default.ALIAS,
|
qos_default.ALIAS,
|
||||||
qos_rule_type_details.ALIAS,
|
qos_rule_type_details.ALIAS,
|
||||||
|
qos_rule_type_filter.ALIAS,
|
||||||
qos_rules_alias.ALIAS,
|
qos_rules_alias.ALIAS,
|
||||||
'quotas',
|
'quotas',
|
||||||
quota_check_limit.ALIAS,
|
quota_check_limit.ALIAS,
|
||||||
|
20
neutron/extensions/qos_rule_type_filter.py
Normal file
20
neutron/extensions/qos_rule_type_filter.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Copyright (c) 2022 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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.definitions import qos_rule_type_filter as apidef
|
||||||
|
from neutron_lib.api import extensions
|
||||||
|
|
||||||
|
|
||||||
|
class Qos_rule_type_filter(extensions.APIExtensionDescriptor):
|
||||||
|
api_definition = apidef
|
@ -60,13 +60,14 @@ class QosRuleType(base.NeutronObject):
|
|||||||
# 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):
|
||||||
|
all_supported = kwargs.pop('all_supported', None)
|
||||||
|
all_rules = kwargs.pop('all_rules', None)
|
||||||
if validate_filters:
|
if validate_filters:
|
||||||
cls.validate_filters(**kwargs)
|
cls.validate_filters(**kwargs)
|
||||||
|
|
||||||
rule_types = (
|
rule_types = directory.get_plugin(
|
||||||
directory.get_plugin(alias=constants.QOS).supported_rule_types)
|
alias=constants.QOS).supported_rule_types(
|
||||||
|
all_supported=all_supported, all_rules=all_rules)
|
||||||
# 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]
|
||||||
|
|
||||||
# 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
|
||||||
|
@ -163,15 +163,24 @@ class QosServiceDriverManager(object):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
def supported_rule_types(self, all_supported=None, all_rules=None):
|
||||||
def supported_rule_types(self):
|
rule_types = set(qos_consts.VALID_RULE_TYPES)
|
||||||
|
if all_rules:
|
||||||
|
LOG.debug('All supported QoS rule types: %s', rule_types)
|
||||||
|
return rule_types
|
||||||
|
|
||||||
if not self._drivers:
|
if not self._drivers:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
rule_types = set(qos_consts.VALID_RULE_TYPES)
|
|
||||||
|
|
||||||
# Recalculate on every call to allow drivers determine supported rule
|
# Recalculate on every call to allow drivers determine supported rule
|
||||||
# types dynamically
|
# types dynamically
|
||||||
|
if all_supported:
|
||||||
|
rule_types = set.union(*[set(driver.supported_rules) for driver in
|
||||||
|
self._drivers])
|
||||||
|
LOG.debug('All QoS rule types supported by at least one loaded '
|
||||||
|
'driver: %s', rule_types)
|
||||||
|
return rule_types
|
||||||
|
|
||||||
for driver in self._drivers:
|
for driver in self._drivers:
|
||||||
new_rule_types = rule_types & set(driver.supported_rules)
|
new_rule_types = rule_types & set(driver.supported_rules)
|
||||||
dropped_rule_types = rule_types - new_rule_types
|
dropped_rule_types = rule_types - new_rule_types
|
||||||
|
@ -31,6 +31,7 @@ from neutron_lib.api.definitions import qos_pps_minimum_rule
|
|||||||
from neutron_lib.api.definitions import qos_pps_minimum_rule_alias
|
from neutron_lib.api.definitions import qos_pps_minimum_rule_alias
|
||||||
from neutron_lib.api.definitions import qos_pps_rule
|
from neutron_lib.api.definitions import qos_pps_rule
|
||||||
from neutron_lib.api.definitions import qos_rule_type_details
|
from neutron_lib.api.definitions import qos_rule_type_details
|
||||||
|
from neutron_lib.api.definitions import qos_rule_type_filter
|
||||||
from neutron_lib.api.definitions import qos_rules_alias
|
from neutron_lib.api.definitions import qos_rules_alias
|
||||||
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
|
||||||
@ -126,6 +127,7 @@ class QoSPlugin(qos.QoSPluginBase):
|
|||||||
qos_bw_limit_direction.ALIAS,
|
qos_bw_limit_direction.ALIAS,
|
||||||
qos_default.ALIAS,
|
qos_default.ALIAS,
|
||||||
qos_rule_type_details.ALIAS,
|
qos_rule_type_details.ALIAS,
|
||||||
|
qos_rule_type_filter.ALIAS,
|
||||||
port_resource_request.ALIAS,
|
port_resource_request.ALIAS,
|
||||||
port_resource_request_groups.ALIAS,
|
port_resource_request_groups.ALIAS,
|
||||||
qos_bw_minimum_ingress.ALIAS,
|
qos_bw_minimum_ingress.ALIAS,
|
||||||
@ -882,9 +884,9 @@ class QoSPlugin(qos.QoSPluginBase):
|
|||||||
def supported_rule_type_details(self, rule_type_name):
|
def supported_rule_type_details(self, rule_type_name):
|
||||||
return self.driver_manager.supported_rule_type_details(rule_type_name)
|
return self.driver_manager.supported_rule_type_details(rule_type_name)
|
||||||
|
|
||||||
@property
|
def supported_rule_types(self, all_supported=None, all_rules=None):
|
||||||
def supported_rule_types(self):
|
return self.driver_manager.supported_rule_types(
|
||||||
return self.driver_manager.supported_rule_types
|
all_supported=all_supported, all_rules=all_rules)
|
||||||
|
|
||||||
@db_base_plugin_common.convert_result_to_dict
|
@db_base_plugin_common.convert_result_to_dict
|
||||||
def create_policy_rule(self, context, rule_cls, policy_id, rule_data):
|
def create_policy_rule(self, context, rule_cls, policy_id, rule_data):
|
||||||
|
@ -77,10 +77,8 @@ class QosRuleTypeObjectTestCase(test_base.BaseTestCase):
|
|||||||
qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, rule_type_details.type)
|
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(
|
|
||||||
return_value=set(qos_consts.VALID_RULE_TYPES))
|
|
||||||
with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types',
|
with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types',
|
||||||
new_callable=rule_types_mock):
|
return_value=set(qos_consts.VALID_RULE_TYPES)):
|
||||||
types = rule_type.QosRuleType.get_objects()
|
types = rule_type.QosRuleType.get_objects()
|
||||||
self.assertEqual(sorted(qos_consts.VALID_RULE_TYPES),
|
self.assertEqual(sorted(qos_consts.VALID_RULE_TYPES),
|
||||||
sorted(type_['type'] for type_ in types))
|
sorted(type_['type'] for type_ in types))
|
||||||
|
@ -10,9 +10,11 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import itertools
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from neutron_lib.api.definitions import portbindings
|
from neutron_lib.api.definitions import portbindings
|
||||||
|
from neutron_lib.callbacks import registry
|
||||||
from neutron_lib import constants as lib_consts
|
from neutron_lib import constants as lib_consts
|
||||||
from neutron_lib import context
|
from neutron_lib import context
|
||||||
from neutron_lib import exceptions
|
from neutron_lib import exceptions
|
||||||
@ -20,9 +22,14 @@ from neutron_lib.services.qos import base as qos_driver_base
|
|||||||
from neutron_lib.services.qos import constants as qos_consts
|
from neutron_lib.services.qos import constants as qos_consts
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
|
from neutron.api.rpc.callbacks.producer import registry as rpc_registry
|
||||||
from neutron.objects import ports as ports_object
|
from neutron.objects import ports as ports_object
|
||||||
from neutron.objects.qos import rule as rule_object
|
from neutron.objects.qos import rule as rule_object
|
||||||
|
from neutron.services.qos.drivers.linuxbridge import driver as lb_driver
|
||||||
from neutron.services.qos.drivers import manager as driver_mgr
|
from neutron.services.qos.drivers import manager as driver_mgr
|
||||||
|
from neutron.services.qos.drivers.openvswitch import driver as ovs_driver
|
||||||
|
from neutron.services.qos.drivers.ovn import driver as ovn_driver
|
||||||
|
from neutron.services.qos.drivers.sriov import driver as sriov_driver
|
||||||
from neutron.tests.unit.services.qos import base
|
from neutron.tests.unit.services.qos import base
|
||||||
|
|
||||||
|
|
||||||
@ -32,9 +39,14 @@ class TestQosDriversManagerBase(base.BaseQosTestCase):
|
|||||||
super(TestQosDriversManagerBase, self).setUp()
|
super(TestQosDriversManagerBase, self).setUp()
|
||||||
self.config_parse()
|
self.config_parse()
|
||||||
self.setup_coreplugin(load_plugins=False)
|
self.setup_coreplugin(load_plugins=False)
|
||||||
|
self._loaded_qos_drivers = []
|
||||||
|
|
||||||
@staticmethod
|
def _delete_loaded_qos_drivers(self):
|
||||||
def _create_manager_with_drivers(drivers_details):
|
registry._get_callback_manager().clear()
|
||||||
|
for idx, _ in enumerate(self._loaded_qos_drivers):
|
||||||
|
del self._loaded_qos_drivers[idx]
|
||||||
|
|
||||||
|
def _create_manager_with_drivers(self, drivers_details):
|
||||||
for name, driver_details in drivers_details.items():
|
for name, driver_details in drivers_details.items():
|
||||||
|
|
||||||
class QoSDriver(qos_driver_base.DriverBase):
|
class QoSDriver(qos_driver_base.DriverBase):
|
||||||
@ -43,10 +55,11 @@ class TestQosDriversManagerBase(base.BaseQosTestCase):
|
|||||||
return driver_details['is_loaded']
|
return driver_details['is_loaded']
|
||||||
|
|
||||||
# the new ad-hoc driver will register on the QOS_PLUGIN registry
|
# the new ad-hoc driver will register on the QOS_PLUGIN registry
|
||||||
QoSDriver(name,
|
self._loaded_qos_drivers.append(
|
||||||
driver_details.get('vif_types', []),
|
QoSDriver(name,
|
||||||
driver_details.get('vnic_types', []),
|
driver_details.get('vif_types', []),
|
||||||
driver_details.get('rules', []))
|
driver_details.get('vnic_types', []),
|
||||||
|
driver_details.get('rules', [])))
|
||||||
|
|
||||||
return driver_mgr.QosServiceDriverManager()
|
return driver_mgr.QosServiceDriverManager()
|
||||||
|
|
||||||
@ -169,67 +182,30 @@ class TestQoSDriversRulesValidations(TestQosDriversManagerBase):
|
|||||||
|
|
||||||
class TestQosDriversManagerRules(TestQosDriversManagerBase):
|
class TestQosDriversManagerRules(TestQosDriversManagerBase):
|
||||||
"""Test supported rules"""
|
"""Test supported rules"""
|
||||||
def test_available_rules_one_in_common(self):
|
|
||||||
driver_manager = self._create_manager_with_drivers({
|
|
||||||
'driver-A': {
|
|
||||||
'is_loaded': True,
|
|
||||||
'rules': {
|
|
||||||
qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: {
|
|
||||||
"max_kbps": {'type:values': None},
|
|
||||||
"max_burst_kbps": {'type:values': None}
|
|
||||||
},
|
|
||||||
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: {
|
|
||||||
"min_kbps": {'type:values': None},
|
|
||||||
'direction': {
|
|
||||||
'type:values': lib_consts.VALID_DIRECTIONS}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'driver-B': {
|
|
||||||
'is_loaded': True,
|
|
||||||
'rules': {
|
|
||||||
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: {
|
|
||||||
"min_kbps": {'type:values': None},
|
|
||||||
'direction': {
|
|
||||||
'type:values': lib_consts.VALID_DIRECTIONS}
|
|
||||||
},
|
|
||||||
qos_consts.RULE_TYPE_DSCP_MARKING: {
|
|
||||||
"dscp_mark": {
|
|
||||||
'type:values': lib_consts.VALID_DSCP_MARKS}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
self.assertEqual(driver_manager.supported_rule_types,
|
|
||||||
set([qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH]))
|
|
||||||
|
|
||||||
def test_available_rules_no_rule_in_common(self):
|
@mock.patch.object(rpc_registry, 'provide')
|
||||||
driver_manager = self._create_manager_with_drivers({
|
def test_available_rules(self, *args):
|
||||||
'driver-A': {
|
available_drivers = {'linuxbridge': lb_driver.SUPPORTED_RULES,
|
||||||
'is_loaded': True,
|
'ovs': ovs_driver.SUPPORTED_RULES,
|
||||||
'rules': {
|
'ovn': ovn_driver.SUPPORTED_RULES,
|
||||||
qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: {
|
'sriov': sriov_driver.SUPPORTED_RULES}
|
||||||
"max_kbps": {'type:values': None},
|
for drivers in itertools.combinations(available_drivers, 2):
|
||||||
"max_burst_kbps": {'type:values': None}
|
rules_0 = set(available_drivers[drivers[0]])
|
||||||
}
|
rules_1 = set(available_drivers[drivers[1]])
|
||||||
}
|
driver_manager = self._create_manager_with_drivers(
|
||||||
},
|
{drivers[0]: {'is_loaded': True, 'rules': rules_0},
|
||||||
'driver-B': {
|
drivers[1]: {'is_loaded': True, 'rules': rules_1}})
|
||||||
'is_loaded': True,
|
self.assertEqual(
|
||||||
'rules': {
|
driver_manager.supported_rule_types(),
|
||||||
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: {
|
rules_0 & rules_1)
|
||||||
"min_kbps": {'type:values': None},
|
self.assertEqual(
|
||||||
'direction': {
|
driver_manager.supported_rule_types(all_supported=True),
|
||||||
'type:values': lib_consts.VALID_DIRECTIONS}
|
rules_0 | rules_1)
|
||||||
},
|
self.assertEqual(
|
||||||
qos_consts.RULE_TYPE_DSCP_MARKING: {
|
driver_manager.supported_rule_types(all_rules=True),
|
||||||
"dscp_mark": {
|
set(qos_consts.VALID_RULE_TYPES))
|
||||||
'type:values': lib_consts.VALID_DSCP_MARKS}
|
|
||||||
}
|
self._delete_loaded_qos_drivers()
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
self.assertEqual(driver_manager.supported_rule_types, set([]))
|
|
||||||
|
|
||||||
def test__parse_parameter_values(self):
|
def test__parse_parameter_values(self):
|
||||||
range_parameter = {'type:range': [0, 10]}
|
range_parameter = {'type:range': [0, 10]}
|
||||||
|
@ -1310,11 +1310,9 @@ class TestQosPlugin(base.BaseQosTestCase):
|
|||||||
self.ctxt, qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
|
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(
|
|
||||||
return_value=qos_consts.VALID_RULE_TYPES)
|
|
||||||
filters = {'type': 'type_id'}
|
filters = {'type': 'type_id'}
|
||||||
with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types',
|
with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types',
|
||||||
new_callable=rule_types_mock):
|
return_value=qos_consts.VALID_RULE_TYPES):
|
||||||
types = self.qos_plugin.get_rule_types(self.ctxt, filters=filters)
|
types = self.qos_plugin.get_rule_types(self.ctxt, filters=filters)
|
||||||
self.assertEqual(sorted(qos_consts.VALID_RULE_TYPES),
|
self.assertEqual(sorted(qos_consts.VALID_RULE_TYPES),
|
||||||
sorted(type_['type'] for type_ in types))
|
sorted(type_['type'] for type_ in types))
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
QoS rule type list accepts two filter flags:
|
||||||
|
|
||||||
|
* ``all_supported``: if True, the listing call will print all QoS
|
||||||
|
rule types supported by at least one loaded mechanism driver.
|
||||||
|
* ``all_rules``: if True, the listing call will print all QoS rule
|
||||||
|
types supported by the Neutron server.
|
||||||
|
|
||||||
|
Both filter flags are exclusive and not required.
|
Loading…
Reference in New Issue
Block a user