Merge "Support filtering for QoS rule type list"

This commit is contained in:
Zuul 2022-03-15 15:42:10 +00:00 committed by Gerrit Code Review
commit fd4db01242
10 changed files with 101 additions and 84 deletions

View File

@ -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:

View File

@ -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,

View 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

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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))

View File

@ -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]}

View File

@ -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))

View File

@ -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.