Merge "Transition qos notification driver into qos driver"

This commit is contained in:
Jenkins 2017-01-27 02:01:52 +00:00 committed by Gerrit Code Review
commit 2dba0775e9
34 changed files with 629 additions and 302 deletions

View File

@ -45,18 +45,15 @@ Service side design
QoSPlugin, service plugin that implements 'qos' extension, receiving and QoSPlugin, service plugin that implements 'qos' extension, receiving and
handling API calls to create/modify policies and rules. handling API calls to create/modify policies and rules.
* neutron.services.qos.notification_drivers.manager: * neutron.services.qos.drivers.manager:
the manager that passes object notifications down to every enabled the manager that passes object actions down to every enabled QoS driver and
notification driver. issues RPC calls when any of the drivers require RPC push notifications.
* neutron.services.qos.notification_drivers.qos_base: * neutron.services.qos.drivers.base:
the interface class for pluggable notification drivers that are used to the interface class for pluggable QoS drivers that are used to update
update backends about new {create, update, delete} events on any rule or backends about new {create, update, delete} events on any rule or policy
policy change. change. The drivers also declare which QoS rules, VIF drivers and VNIC
types are supported.
* neutron.services.qos.notification_drivers.message_queue:
MQ-based reference notification driver which updates agents via messaging
bus, using `RPC callbacks <rpc_callbacks.html>`_.
* neutron.core_extensions.base: * neutron.core_extensions.base:
Contains an interface class to implement core resource (port/network) Contains an interface class to implement core resource (port/network)
@ -96,21 +93,19 @@ NotImplemented.
Supported QoS rule types Supported QoS rule types
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
Any plugin or Ml2 mechanism driver can claim support for some QoS rule types by Each QoS driver has a property called supported_rule_types, where the driver
providing a plugin/driver class property called 'supported_qos_rule_types' that exposes the rules it's able to handle.
should return a list of strings that correspond to QoS rule types (for the list
of all rule types, see: neutron.services.qos.qos_consts.VALID_RULE_TYPES).
In the most simple case, the property can be represented by a simple Python For a list of all rule types, see:
list defined on the class. neutron.services.qos.qos_consts.VALID_RULE_TYPES.
For Ml2 plugin, the list of supported QoS rule types is defined as a common The list of supported QoS rule types exposed by neutron is calculated as
subset of rules supported by all active mechanism drivers. the common subset of rules supported by all active QoS drivers.
Note: the list of supported rule types reported by core plugin is not enforced Note: the list of supported rule types reported by core plugin is not enforced
when accessing QoS rule resources. This is mostly because then we would not be when accessing QoS rule resources. This is mostly because then we would not be
able to create any rules while at least one ml2 driver in gate lacks support able to create rules while at least one of the QoS driver in gate lacks
for QoS (at the moment of writing, linuxbridge is such a driver). support for the rules we're trying to test.
Database models Database models
@ -401,15 +396,15 @@ Traffic in the tap port is redirected (mirrored) to the IFB using a Traffic
Control filter Control filter
(`Filter Actions <http://linux-ip.net/gl/tc-filters/tc-filters-node2.html>`_). (`Filter Actions <http://linux-ip.net/gl/tc-filters/tc-filters-node2.html>`_).
Notification driver design QoS driver design
-------------------------- -----------------
QoS framework is flexible enough to support any third-party vendor. To integrate a QoS framework is flexible enough to support any third-party vendor. To integrate a
third party driver (that just wants to be aware of the QoS create/update/delete API third party driver (that just wants to be aware of the QoS create/update/delete API
calls), one needs to implement 'neutron.services.qos.notification_drivers.qos_base', calls), one needs to implement 'neutron.services.qos.drivers.base', and register
register its specific driver information to the 'notification_drivers' stevedore the driver during the core plugin or mechanism driver load, see
namespace in the setup.cfg and finally set the 'notification_drivers' parameter in
the [qos] section of the neutron config file. neutron.services.qos.drivers.openvswitch.driver register method for an example.
.. note:: .. note::
All the functionality MUST be implemented by the vendor, neutron's QoS framework All the functionality MUST be implemented by the vendor, neutron's QoS framework
@ -424,7 +419,6 @@ To enable the service, the following steps should be followed:
On server side: On server side:
* enable qos service in service_plugins; * enable qos service in service_plugins;
* set the needed notification_drivers in [qos] section (message_queue is the default);
* for ml2, add 'qos' to extension_drivers in [ml2] section. * for ml2, add 'qos' to extension_drivers in [ml2] section.
On agent side (OVS): On agent side (OVS):

View File

@ -16,7 +16,9 @@ from neutron._i18n import _
QOS_PLUGIN_OPTS = [ QOS_PLUGIN_OPTS = [
cfg.ListOpt('notification_drivers', cfg.ListOpt('notification_drivers',
default=['message_queue'], default=['message_queue'],
help=_('Drivers list to use to send the update notification')), help=_("Drivers list to use to send the update notification. "
"This option will be unused in Pike."),
deprecated_for_removal=True),
] ]

View File

@ -10,13 +10,18 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
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_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._i18n import _LW
from neutron.objects import base from neutron.objects import base
from neutron.services.qos import qos_consts from neutron.services.qos import qos_consts
LOG = logging.getLogger(__name__)
class RuleTypeField(obj_fields.BaseEnumField): class RuleTypeField(obj_fields.BaseEnumField):
@ -42,7 +47,22 @@ class QosRuleType(base.NeutronObject):
def get_objects(cls, validate_filters=True, **kwargs): def get_objects(cls, validate_filters=True, **kwargs):
if validate_filters: if validate_filters:
cls.validate_filters(**kwargs) cls.validate_filters(**kwargs)
core_plugin = directory.get_plugin()
#TODO(mangelajo): remove in backwards compatible available rule
# inspection in Pike
core_plugin_supported_rules = getattr(
directory.get_plugin(), 'supported_qos_rule_types', None)
rule_types = (
core_plugin_supported_rules or
directory.get_plugin(alias=constants.QOS).supported_rule_types)
if core_plugin_supported_rules:
LOG.warning(_LW(
"Your core plugin defines supported_qos_rule_types which is "
"deprecated and shall be implemented through a QoS driver."
))
# TODO(ihrachys): apply filters to returned result # TODO(ihrachys): apply filters to returned result
return [cls(type=type_) return [cls(type=type_) for type_ in rule_types]
for type_ in core_plugin.supported_qos_rule_types]

View File

@ -22,8 +22,7 @@ from neutron.agent.l2.extensions import qos_linux as qos
from neutron.agent.linux import iptables_manager from neutron.agent.linux import iptables_manager
from neutron.agent.linux import tc_lib from neutron.agent.linux import tc_lib
import neutron.common.constants as const import neutron.common.constants as const
from neutron.plugins.ml2.drivers.linuxbridge.mech_driver import ( from neutron.services.qos.drivers.linuxbridge import driver
mech_linuxbridge)
from neutron.services.qos import qos_consts from neutron.services.qos import qos_consts
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@ -36,9 +35,7 @@ class QosLinuxbridgeAgentDriver(qos.QosLinuxAgentDriver):
# the delete function, to have the 'direction' parameter. This QoS # the delete function, to have the 'direction' parameter. This QoS
# extension modification is going to be implemented in # extension modification is going to be implemented in
# https://review.openstack.org/#/c/341186/ # https://review.openstack.org/#/c/341186/
SUPPORTED_RULES = ( SUPPORTED_RULES = driver.SUPPORTED_RULES
mech_linuxbridge.LinuxbridgeMechanismDriver.supported_qos_rule_types
)
IPTABLES_DIRECTION = {const.INGRESS_DIRECTION: 'physdev-out', IPTABLES_DIRECTION = {const.INGRESS_DIRECTION: 'physdev-out',
const.EGRESS_DIRECTION: 'physdev-in'} const.EGRESS_DIRECTION: 'physdev-in'}

View File

@ -19,7 +19,7 @@ from neutron.agent import securitygroups_rpc
from neutron.extensions import portbindings from neutron.extensions import portbindings
from neutron.plugins.common import constants as p_constants from neutron.plugins.common import constants as p_constants
from neutron.plugins.ml2.drivers import mech_agent from neutron.plugins.ml2.drivers import mech_agent
from neutron.services.qos import qos_consts from neutron.services.qos.drivers.linuxbridge import driver as lb_qos_driver
class LinuxbridgeMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): class LinuxbridgeMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
@ -32,16 +32,13 @@ class LinuxbridgeMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
network. network.
""" """
supported_qos_rule_types = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
qos_consts.RULE_TYPE_DSCP_MARKING,
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH]
def __init__(self): def __init__(self):
sg_enabled = securitygroups_rpc.is_firewall_enabled() sg_enabled = securitygroups_rpc.is_firewall_enabled()
super(LinuxbridgeMechanismDriver, self).__init__( super(LinuxbridgeMechanismDriver, self).__init__(
constants.AGENT_TYPE_LINUXBRIDGE, constants.AGENT_TYPE_LINUXBRIDGE,
portbindings.VIF_TYPE_BRIDGE, portbindings.VIF_TYPE_BRIDGE,
{portbindings.CAP_PORT_FILTER: sg_enabled}) {portbindings.CAP_PORT_FILTER: sg_enabled})
lb_qos_driver.register()
def get_allowed_network_types(self, agent): def get_allowed_network_types(self, agent):
return (agent['configurations'].get('tunnel_types', []) + return (agent['configurations'].get('tunnel_types', []) +

View File

@ -19,16 +19,14 @@ from neutron.agent.l2.extensions import qos_linux as qos
from neutron.plugins.ml2.drivers.mech_sriov.agent.common import ( from neutron.plugins.ml2.drivers.mech_sriov.agent.common import (
exceptions as exc) exceptions as exc)
from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm
from neutron.plugins.ml2.drivers.mech_sriov.mech_driver import ( from neutron.services.qos.drivers.sriov import driver
mech_driver)
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class QosSRIOVAgentDriver(qos.QosLinuxAgentDriver): class QosSRIOVAgentDriver(qos.QosLinuxAgentDriver):
SUPPORTED_RULES = ( SUPPORTED_RULES = driver.SUPPORTED_RULES
mech_driver.SriovNicSwitchMechanismDriver.supported_qos_rule_types)
def __init__(self): def __init__(self):
super(QosSRIOVAgentDriver, self).__init__() super(QosSRIOVAgentDriver, self).__init__()

View File

@ -23,7 +23,7 @@ from neutron.plugins.ml2 import driver_api as api
from neutron.plugins.ml2.drivers import mech_agent from neutron.plugins.ml2.drivers import mech_agent
from neutron.plugins.ml2.drivers.mech_sriov.mech_driver \ from neutron.plugins.ml2.drivers.mech_sriov.mech_driver \
import exceptions as exc import exceptions as exc
from neutron.services.qos import qos_consts from neutron.services.qos.drivers.sriov import driver as sriov_qos_driver
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@ -43,11 +43,6 @@ class SriovNicSwitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
L2 Agent presents in order to manage port update events. L2 Agent presents in order to manage port update events.
""" """
supported_qos_rule_types = [
qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH,
]
def __init__(self, def __init__(self,
agent_type=constants.AGENT_TYPE_NIC_SWITCH, agent_type=constants.AGENT_TYPE_NIC_SWITCH,
vif_details={portbindings.CAP_PORT_FILTER: False}, vif_details={portbindings.CAP_PORT_FILTER: False},
@ -69,6 +64,7 @@ class SriovNicSwitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
else portbindings.VIF_TYPE_HW_VEB else portbindings.VIF_TYPE_HW_VEB
for vtype in self.supported_vnic_types}) for vtype in self.supported_vnic_types})
self.vif_details = vif_details self.vif_details = vif_details
sriov_qos_driver.register()
def get_allowed_network_types(self, agent): def get_allowed_network_types(self, agent):
return (p_const.TYPE_FLAT, p_const.TYPE_VLAN) return (p_const.TYPE_FLAT, p_const.TYPE_VLAN)

View File

@ -18,8 +18,7 @@ from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from neutron.agent.l2.extensions import qos_linux as qos from neutron.agent.l2.extensions import qos_linux as qos
from neutron.plugins.ml2.drivers.openvswitch.mech_driver import ( from neutron.services.qos.drivers.openvswitch import driver
mech_openvswitch)
from neutron.services.qos import qos_consts from neutron.services.qos import qos_consts
@ -28,8 +27,7 @@ LOG = logging.getLogger(__name__)
class QosOVSAgentDriver(qos.QosLinuxAgentDriver): class QosOVSAgentDriver(qos.QosLinuxAgentDriver):
SUPPORTED_RULES = ( SUPPORTED_RULES = driver.SUPPORTED_RULES
mech_openvswitch.OpenvswitchMechanismDriver.supported_qos_rule_types)
def __init__(self): def __init__(self):
super(QosOVSAgentDriver, self).__init__() super(QosOVSAgentDriver, self).__init__()

View File

@ -27,7 +27,8 @@ from neutron.plugins.ml2 import driver_api as api
from neutron.plugins.ml2.drivers import mech_agent from neutron.plugins.ml2.drivers import mech_agent
from neutron.plugins.ml2.drivers.openvswitch.agent.common \ from neutron.plugins.ml2.drivers.openvswitch.agent.common \
import constants as a_const import constants as a_const
from neutron.services.qos import qos_consts from neutron.services.qos.drivers.openvswitch import driver as ovs_qos_driver
IPTABLES_FW_DRIVER_FULL = ("neutron.agent.linux.iptables_firewall." IPTABLES_FW_DRIVER_FULL = ("neutron.agent.linux.iptables_firewall."
"OVSHybridIptablesFirewallDriver") "OVSHybridIptablesFirewallDriver")
@ -43,9 +44,6 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
network. network.
""" """
supported_qos_rule_types = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
qos_consts.RULE_TYPE_DSCP_MARKING]
def __init__(self): def __init__(self):
sg_enabled = securitygroups_rpc.is_firewall_enabled() sg_enabled = securitygroups_rpc.is_firewall_enabled()
hybrid_plug_required = (not cfg.CONF.SECURITYGROUP.firewall_driver or hybrid_plug_required = (not cfg.CONF.SECURITYGROUP.firewall_driver or
@ -57,6 +55,7 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
constants.AGENT_TYPE_OVS, constants.AGENT_TYPE_OVS,
portbindings.VIF_TYPE_OVS, portbindings.VIF_TYPE_OVS,
vif_details) vif_details)
ovs_qos_driver.register()
def get_allowed_network_types(self, agent): def get_allowed_network_types(self, agent):
return (agent['configurations'].get('tunnel_types', []) + return (agent['configurations'].get('tunnel_types', []) +

View File

@ -33,7 +33,6 @@ from neutron.extensions import vlantransparent
from neutron.plugins.ml2.common import exceptions as ml2_exc from neutron.plugins.ml2.common import exceptions as ml2_exc
from neutron.plugins.ml2 import driver_api as api from neutron.plugins.ml2 import driver_api as api
from neutron.plugins.ml2 import models from neutron.plugins.ml2 import models
from neutron.services.qos import qos_consts
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@ -369,48 +368,6 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
LOG.info(_LI("Registered mechanism drivers: %s"), LOG.info(_LI("Registered mechanism drivers: %s"),
[driver.name for driver in self.ordered_mech_drivers]) [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)
binding_driver_found = False
# Recalculate on every call to allow drivers determine supported rule
# types dynamically
for driver in self.ordered_mech_drivers:
driver_obj = driver.obj
if driver_obj._supports_port_binding:
binding_driver_found = True
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.warning(
_LW("%s does not support QoS; "
"no rule types available"),
driver.name)
return []
if binding_driver_found:
rule_types = list(rule_types)
else:
rule_types = []
LOG.debug("Supported QoS rule types "
"(common subset for all mech drivers): %s", rule_types)
return rule_types
def initialize(self): def initialize(self):
for driver in self.ordered_mech_drivers: for driver in self.ordered_mech_drivers:
LOG.info(_LI("Initializing mechanism driver '%s'"), driver.name) LOG.info(_LI("Initializing mechanism driver '%s'"), driver.name)

View File

@ -236,10 +236,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
return return
self.update_port_status(context, port_id, const.PORT_STATUS_ACTIVE) self.update_port_status(context, port_id, const.PORT_STATUS_ACTIVE)
@property
def supported_qos_rule_types(self):
return self.mechanism_manager.supported_qos_rule_types
@log_helpers.log_method_call @log_helpers.log_method_call
def _start_rpc_notifiers(self): def _start_rpc_notifiers(self):
"""Initialize RPC notifiers for agents.""" """Initialize RPC notifiers for agents."""

View File

View File

@ -0,0 +1,100 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company, LP
# Copyright 2016 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.
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.services.qos import qos_consts
class DriverBase(object):
def __init__(self, name, vif_types, vnic_types,
supported_rules,
requires_rpc_notifications=False):
"""Instantiate a qos driver.
:param name: driver name.
:param vif_types: list of interfaces (VIFs) supported.
:param vnic_types: list of vnic types supported.
:param supported_rules: list of supported rules.
:param requires_rpc_notifications: indicates if this driver
expects rpc push notifications to be sent from the driver.
"""
self.name = name
self.vif_types = vif_types
self.vnic_types = vnic_types
self.supported_rules = supported_rules
self.requires_rpc_notifications = requires_rpc_notifications
registry.subscribe(self._register,
qos_consts.QOS_PLUGIN,
events.AFTER_INIT)
def _register(self, resource, event, trigger, **kwargs):
if self.is_loaded:
# trigger is the QosServiceDriverManager
trigger.register_driver(self)
def is_loaded(self):
"""True if the driver is active for the Neutron Server.
Implement this property to determine if your driver is actively
configured for this Neutron Server deployment.
"""
return True
def is_vif_type_compatible(self, vif_type):
"""True if the driver is compatible with the VIF type."""
return vif_type in self.vif_types
def is_vnic_compatible(self, vnic_type):
"""True if the driver is compatible with the specific VNIC type."""
return vnic_type in self.vnic_types
def is_rule_supported(self, rule):
return rule.rule_type in self.supported_rules
def create_policy(self, context, policy):
"""Create policy invocation.
This method can be implemented by the specific driver subclass
to update the backend where necessary with the specific policy
information.
:param context: current running context information
:param policy: a QoSPolicy object being created, which will have no
rules.
"""
def update_policy(self, context, policy):
"""Update policy invocation.
This method can be implemented by the specific driver subclass
to update the backend where necessary.
:param context: current running context information
:param policy: a QoSPolicy object being updated.
"""
def delete_policy(self, context, policy):
"""Delete policy invocation.
This method can be implemented by the specific driver subclass
to delete the backend policy where necessary.
:param context: current running context information
:param policy: a QoSPolicy object being deleted
"""

View File

@ -0,0 +1,48 @@
# Copyright (c) 2016 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.
from oslo_log import log as logging
from neutron.extensions import portbindings
from neutron.services.qos.drivers import base
from neutron.services.qos import qos_consts
LOG = logging.getLogger(__name__)
DRIVER = None
SUPPORTED_RULES = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
qos_consts.RULE_TYPE_DSCP_MARKING,
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH]
class LinuxBridgeDriver(base.DriverBase):
@staticmethod
def create():
return LinuxBridgeDriver(
name='linuxbridge',
vif_types=[portbindings.VIF_TYPE_BRIDGE],
vnic_types=[portbindings.VNIC_NORMAL],
supported_rules=SUPPORTED_RULES,
requires_rpc_notifications=True)
def register():
"""Register the driver."""
global DRIVER
if not DRIVER:
DRIVER = LinuxBridgeDriver.create()
LOG.debug('Linuxbridge QoS driver registered')

View File

@ -0,0 +1,117 @@
# 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_log import log as logging
from oslo_utils import excutils
from neutron._i18n import _LE, _LW
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.callbacks import events
from neutron.callbacks import registry
from neutron.conf.services import qos_driver_manager as qos_mgr
from neutron.objects.qos import policy as policy_object
from neutron.services.qos import qos_consts
qos_mgr.register_qos_plugin_opts()
LOG = logging.getLogger(__name__)
class QosServiceDriverManager(object):
def __init__(self, enable_rpc=False):
self._drivers = []
self.notification_api = resources_rpc.ResourcesPushRpcApi()
#TODO(mangelajo): remove the enable_rpc parameter in Pike since
# we only use it when a message_queue derived driver
# is found in the notification_drivers
self.rpc_notifications_required = enable_rpc
rpc_registry.provide(self._get_qos_policy_cb, resources.QOS_POLICY)
# notify any registered QoS driver that we're ready, those will
# call the driver manager back with register_driver if they
# are enabled
registry.notify(qos_consts.QOS_PLUGIN, events.AFTER_INIT, self)
if self.rpc_notifications_required:
self.push_api = resources_rpc.ResourcesPushRpcApi()
@staticmethod
def _get_qos_policy_cb(resource, policy_id, **kwargs):
context = kwargs.get('context')
if context is None:
LOG.warning(_LW(
'Received %(resource)s %(policy_id)s without context'),
{'resource': resource, 'policy_id': policy_id}
)
return
policy = policy_object.QosPolicy.get_object(context, id=policy_id)
return policy
def call(self, method_name, *args, **kwargs):
"""Helper method for calling a method across all extension drivers."""
for driver in self._drivers:
try:
getattr(driver, method_name)(*args, **kwargs)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("Extension driver '%(name)s' failed in "
"%(method)s"),
{'name': driver.name, 'method': method_name})
if self.rpc_notifications_required:
context = kwargs.get('context') or args[0]
policy_obj = kwargs.get('policy_obj') or args[1]
# we don't push create_policy events since policies are empty
# on creation, they only become of any use when rules get
# attached to them.
if method_name == 'update_policy':
self.push_api.push(context, [policy_obj], rpc_events.UPDATED)
elif method_name == 'delete_policy':
self.push_api.push(context, [policy_obj], rpc_events.DELETED)
def register_driver(self, driver):
"""Register driver with qos plugin.
This method is called from drivers on INIT event.
"""
self._drivers.append(driver)
self.rpc_notifications_required |= driver.requires_rpc_notifications
@property
def supported_rule_types(self):
if not self._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._drivers:
new_rule_types = rule_types & set(driver.supported_rules)
dropped_rule_types = rule_types - new_rule_types
if dropped_rule_types:
LOG.debug("%(rule_types)s rule types disabled "
"because enabled %(driver)s does not support them",
{'rule_types': ', '.join(dropped_rule_types),
'driver': driver.name})
rule_types = new_rule_types
LOG.debug("Supported QoS rule types "
"(common subset for all loaded QoS drivers): %s", rule_types)
return rule_types

View File

@ -0,0 +1,48 @@
# Copyright (c) 2016 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.
from oslo_log import log as logging
from neutron.extensions import portbindings
from neutron.services.qos.drivers import base
from neutron.services.qos import qos_consts
LOG = logging.getLogger(__name__)
DRIVER = None
SUPPORTED_RULES = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
qos_consts.RULE_TYPE_DSCP_MARKING]
class OVSDriver(base.DriverBase):
@staticmethod
def create():
return OVSDriver(
name='openvswitch',
vif_types=[portbindings.VIF_TYPE_OVS,
portbindings.VIF_TYPE_VHOST_USER],
vnic_types=[portbindings.VNIC_NORMAL],
supported_rules=SUPPORTED_RULES,
requires_rpc_notifications=True)
def register():
"""Register the driver."""
global DRIVER
if not DRIVER:
DRIVER = OVSDriver.create()
LOG.debug('Open vSwitch QoS driver registered')

View File

@ -0,0 +1,48 @@
# Copyright (c) 2016 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.
from oslo_log import log as logging
from neutron.extensions import portbindings
from neutron.services.qos.drivers import base
from neutron.services.qos import qos_consts
LOG = logging.getLogger(__name__)
DRIVER = None
SUPPORTED_RULES = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH]
class SRIOVNICSwitchDriver(base.DriverBase):
@staticmethod
def create():
return SRIOVNICSwitchDriver(
name='sriovnicswitch',
vif_types=[portbindings.VIF_TYPE_HW_VEB],
vnic_types=[portbindings.VNIC_DIRECT,
portbindings.VNIC_MACVTAP],
supported_rules=SUPPORTED_RULES,
requires_rpc_notifications=True)
def register():
"""Register the driver."""
global DRIVER
if not DRIVER:
DRIVER = SRIOVNICSwitchDriver.create()
LOG.debug('SR-IOV NIC Switch QoS driver registered')

View File

@ -12,9 +12,10 @@
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from neutron._i18n import _LI
from neutron.conf.services import qos_driver_manager as qos_mgr from neutron.conf.services import qos_driver_manager as qos_mgr
from neutron._i18n import _, _LI
from neutron import manager from neutron import manager
from neutron.services.qos.notification_drivers import message_queue
QOS_DRIVER_NAMESPACE = 'neutron.qos.notification_drivers' QOS_DRIVER_NAMESPACE = 'neutron.qos.notification_drivers'
qos_mgr.register_qos_plugin_opts() qos_mgr.register_qos_plugin_opts()
@ -40,14 +41,23 @@ class QosServiceNotificationDriverManager(object):
for driver in self.notification_drivers: for driver in self.notification_drivers:
driver.create_policy(context, qos_policy) driver.create_policy(context, qos_policy)
@property
def has_message_queue_driver(self):
"""Determine if we have any message_queue derived driver in the
notifications drivers, so the QoS plugin will forcefully enable
the rpc notifications for Pike, since our message_queue driver
is a dummy which doesn't send any messages.
"""
#TODO(mangelajo): remove this in Pike
return any(
isinstance(driver, message_queue.RpcQosServiceNotificationDriver)
for driver in self.notification_drivers)
def _load_drivers(self, notification_drivers): def _load_drivers(self, notification_drivers):
"""Load all the instances of the configured QoS notification drivers """Load all the instances of the configured QoS notification drivers
:param notification_drivers: comma separated string :param notification_drivers: comma separated string
""" """
if not notification_drivers:
raise SystemExit(_('A QoS driver must be specified'))
LOG.debug("Loading QoS notification drivers: %s", notification_drivers)
for notification_driver in notification_drivers: for notification_driver in notification_drivers:
driver_ins = self._load_driver_instance(notification_driver) driver_ins = self._load_driver_instance(notification_driver)
self.notification_drivers.append(driver_ins) self.notification_drivers.append(driver_ins)

View File

@ -13,47 +13,29 @@
from oslo_log import log as logging from oslo_log import log as logging
from neutron._i18n import _LW from neutron._i18n import _LW
from neutron.api.rpc.callbacks import events
from neutron.api.rpc.callbacks.producer import registry
from neutron.api.rpc.callbacks import resources
from neutron.api.rpc.handlers import resources_rpc
from neutron.objects.qos import policy as policy_object
from neutron.services.qos.notification_drivers import qos_base from neutron.services.qos.notification_drivers import qos_base
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def _get_qos_policy_cb(resource, policy_id, **kwargs):
context = kwargs.get('context')
if context is None:
LOG.warning(_LW(
'Received %(resource)s %(policy_id)s without context'),
{'resource': resource, 'policy_id': policy_id}
)
return
policy = policy_object.QosPolicy.get_object(context, id=policy_id)
return policy
class RpcQosServiceNotificationDriver( class RpcQosServiceNotificationDriver(
qos_base.QosServiceNotificationDriverBase): qos_base.QosServiceNotificationDriverBase):
"""RPC message queue service notification driver for QoS.""" """RPC message queue service notification driver for QoS."""
def __init__(self): def __init__(self):
self.notification_api = resources_rpc.ResourcesPushRpcApi() LOG.warning(_LW("The QoS message_queue notification driver "
registry.provide(_get_qos_policy_cb, resources.QOS_POLICY) "has been ignored, since rpc push is implemented "
"for any QoS driver that requests it."))
def get_description(self): def get_description(self):
return "Message queue updates" return "Message queue updates"
def create_policy(self, context, policy): def create_policy(self, context, policy):
#No need to update agents on create
pass pass
def update_policy(self, context, policy): def update_policy(self, context, policy):
self.notification_api.push(context, [policy], events.UPDATED) pass
def delete_policy(self, context, policy): def delete_policy(self, context, policy):
self.notification_api.push(context, [policy], events.DELETED) pass

View File

@ -23,6 +23,8 @@ VALID_RULE_TYPES = [RULE_TYPE_BANDWIDTH_LIMIT,
QOS_POLICY_ID = 'qos_policy_id' QOS_POLICY_ID = 'qos_policy_id'
QOS_PLUGIN = 'qos_plugin'
# NOTE(slaweq): Value used to calculate burst value for egress bandwidth limit # NOTE(slaweq): Value used to calculate burst value for egress bandwidth limit
# if burst is not given by user. In such case burst value will be calculated # if burst is not given by user. In such case burst value will be calculated
# as 80% of bw_limit to ensure that at least limits for TCP traffic will work # as 80% of bw_limit to ensure that at least limits for TCP traffic will work

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_log import log
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
from neutron.db import db_base_plugin_common from neutron.db import db_base_plugin_common
@ -20,9 +22,12 @@ from neutron.extensions import qos
from neutron.objects import base as base_obj from neutron.objects import base as base_obj
from neutron.objects.qos import policy as policy_object from neutron.objects.qos import policy as policy_object
from neutron.objects.qos import rule_type as rule_type_object from neutron.objects.qos import rule_type as rule_type_object
from neutron.services.qos.drivers import manager
from neutron.services.qos.notification_drivers import manager as driver_mgr from neutron.services.qos.notification_drivers import manager as driver_mgr
from neutron.services.qos import qos_consts from neutron.services.qos import qos_consts
LOG = log.getLogger(__name__)
class QoSPlugin(qos.QoSPluginBase): class QoSPlugin(qos.QoSPluginBase):
"""Implementation of the Neutron QoS Service Plugin. """Implementation of the Neutron QoS Service Plugin.
@ -38,9 +43,14 @@ class QoSPlugin(qos.QoSPluginBase):
def __init__(self): def __init__(self):
super(QoSPlugin, self).__init__() super(QoSPlugin, self).__init__()
# TODO(mangelajo): remove notification_driver_manager in Pike
self.notification_driver_manager = ( self.notification_driver_manager = (
driver_mgr.QosServiceNotificationDriverManager()) driver_mgr.QosServiceNotificationDriverManager())
self.driver_manager = manager.QosServiceDriverManager(enable_rpc=(
self.notification_driver_manager.has_message_queue_driver))
@db_base_plugin_common.convert_result_to_dict @db_base_plugin_common.convert_result_to_dict
def create_policy(self, context, policy): def create_policy(self, context, policy):
"""Create a QoS policy. """Create a QoS policy.
@ -61,7 +71,12 @@ class QoSPlugin(qos.QoSPluginBase):
policy_obj = policy_object.QosPolicy(context, **policy['policy']) policy_obj = policy_object.QosPolicy(context, **policy['policy'])
policy_obj.create() policy_obj.create()
self.driver_manager.call('create_policy', context, policy_obj)
#TODO(majopela): remove notification_driver_manager call in Pike
self.notification_driver_manager.create_policy(context, policy_obj) self.notification_driver_manager.create_policy(context, policy_obj)
return policy_obj return policy_obj
@db_base_plugin_common.convert_result_to_dict @db_base_plugin_common.convert_result_to_dict
@ -81,7 +96,12 @@ class QoSPlugin(qos.QoSPluginBase):
policy_obj = policy_object.QosPolicy(context, id=policy_id) policy_obj = policy_object.QosPolicy(context, id=policy_id)
policy_obj.update_fields(policy_data, reset_changes=True) policy_obj.update_fields(policy_data, reset_changes=True)
policy_obj.update() policy_obj.update()
self.driver_manager.call('update_policy', context, policy_obj)
#TODO(majopela): remove notification_driver_manager call in Pike
self.notification_driver_manager.update_policy(context, policy_obj) self.notification_driver_manager.update_policy(context, policy_obj)
return policy_obj return policy_obj
def delete_policy(self, context, policy_id): def delete_policy(self, context, policy_id):
@ -97,6 +117,10 @@ class QoSPlugin(qos.QoSPluginBase):
policy = policy_object.QosPolicy(context) policy = policy_object.QosPolicy(context)
policy.id = policy_id policy.id = policy_id
policy.delete() policy.delete()
self.driver_manager.call('delete_policy', context, policy)
#TODO(majopela): remove notification_driver_manager call in Pike
self.notification_driver_manager.delete_policy(context, policy) self.notification_driver_manager.delete_policy(context, policy)
def _get_policy_obj(self, context, policy_id): def _get_policy_obj(self, context, policy_id):
@ -155,6 +179,10 @@ class QoSPlugin(qos.QoSPluginBase):
filters = {} filters = {}
return rule_type_object.QosRuleType.get_objects(**filters) return rule_type_object.QosRuleType.get_objects(**filters)
@property
def supported_rule_types(self):
return self.driver_manager.supported_rule_types
@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):
"""Create a QoS policy rule. """Create a QoS policy rule.
@ -179,7 +207,12 @@ class QoSPlugin(qos.QoSPluginBase):
rule = rule_cls(context, qos_policy_id=policy_id, **rule_data) rule = rule_cls(context, qos_policy_id=policy_id, **rule_data)
rule.create() rule.create()
policy.reload_rules() policy.reload_rules()
self.driver_manager.call('update_policy', context, policy)
#TODO(majopela): remove notification_driver_manager call in Pike
self.notification_driver_manager.update_policy(context, policy) self.notification_driver_manager.update_policy(context, policy)
return rule return rule
@db_base_plugin_common.convert_result_to_dict @db_base_plugin_common.convert_result_to_dict
@ -212,7 +245,12 @@ class QoSPlugin(qos.QoSPluginBase):
rule.update_fields(rule_data, reset_changes=True) rule.update_fields(rule_data, reset_changes=True)
rule.update() rule.update()
policy.reload_rules() policy.reload_rules()
self.driver_manager.call('update_policy', context, policy)
#TODO(majopela): remove notification_driver_manager call in Pike
self.notification_driver_manager.update_policy(context, policy) self.notification_driver_manager.update_policy(context, policy)
return rule return rule
def delete_policy_rule(self, context, rule_cls, rule_id, policy_id): def delete_policy_rule(self, context, rule_cls, rule_id, policy_id):
@ -235,6 +273,10 @@ class QoSPlugin(qos.QoSPluginBase):
rule = policy.get_rule_by_id(rule_id) rule = policy.get_rule_by_id(rule_id)
rule.delete() rule.delete()
policy.reload_rules() policy.reload_rules()
self.driver_manager.call('update_policy', context, policy)
#TODO(majopela): remove notification_driver_manager call in Pike
self.notification_driver_manager.update_policy(context, policy) self.notification_driver_manager.update_policy(context, policy)
@db_base_plugin_common.filter_fields @db_base_plugin_common.filter_fields

View File

@ -42,6 +42,7 @@ import testtools
from neutron._i18n import _ from neutron._i18n import _
from neutron.agent.linux import external_process from neutron.agent.linux import external_process
from neutron.api.rpc.callbacks.consumer import registry as rpc_consumer_reg from neutron.api.rpc.callbacks.consumer import registry as rpc_consumer_reg
from neutron.api.rpc.callbacks.producer import registry as rpc_producer_reg
from neutron.callbacks import manager as registry_manager from neutron.callbacks import manager as registry_manager
from neutron.callbacks import registry from neutron.callbacks import registry
from neutron.common import config from neutron.common import config
@ -298,6 +299,7 @@ class BaseTestCase(DietTestCase):
self.addCleanup(resource_registry.unregister_all_resources) self.addCleanup(resource_registry.unregister_all_resources)
self.addCleanup(db_api.sqla_remove_all) self.addCleanup(db_api.sqla_remove_all)
self.addCleanup(rpc_consumer_reg.clear) self.addCleanup(rpc_consumer_reg.clear)
self.addCleanup(rpc_producer_reg.clear)
def get_new_temp_dir(self): def get_new_temp_dir(self):
"""Create a new temporary directory. """Create a new temporary directory.

View File

@ -30,8 +30,7 @@ from neutron.tests.unit import testlib_api
from neutron.plugins.ml2.drivers.linuxbridge.agent import \ from neutron.plugins.ml2.drivers.linuxbridge.agent import \
linuxbridge_neutron_agent as linuxbridge_agent linuxbridge_neutron_agent as linuxbridge_agent
from neutron.plugins.ml2.drivers.openvswitch.mech_driver import \ from neutron.services.qos.drivers.openvswitch import driver as ovs_drv
mech_openvswitch as mech_ovs
load_tests = testlib_api.module_load_tests load_tests = testlib_api.module_load_tests
@ -351,6 +350,5 @@ class TestQoSWithL2Population(base.BaseFullStackTestCase):
def test_supported_qos_rule_types(self): def test_supported_qos_rule_types(self):
res = self.client.list_qos_rule_types() res = self.client.list_qos_rule_types()
rule_types = {t['type'] for t in res['rule_types']} rule_types = {t['type'] for t in res['rule_types']}
expected_rules = ( expected_rules = set(ovs_drv.SUPPORTED_RULES)
set(mech_ovs.OpenvswitchMechanismDriver.supported_qos_rule_types))
self.assertEqual(expected_rules, rule_types) self.assertEqual(expected_rules, rule_types)

View File

@ -58,7 +58,6 @@ from neutron.plugins.ml2 import managers
from neutron.plugins.ml2 import models from neutron.plugins.ml2 import models
from neutron.plugins.ml2 import plugin as ml2_plugin from neutron.plugins.ml2 import plugin as ml2_plugin
from neutron.services.l3_router import l3_router_plugin from neutron.services.l3_router import l3_router_plugin
from neutron.services.qos import qos_consts
from neutron.services.revisions import revision_plugin from neutron.services.revisions import revision_plugin
from neutron.services.segments import db as segments_plugin_db from neutron.services.segments import db as segments_plugin_db
from neutron.services.segments import plugin as segments_plugin from neutron.services.segments import plugin as segments_plugin
@ -160,54 +159,6 @@ class TestMl2BulkToggleWithoutBulkless(Ml2PluginV2TestCase):
self.assertFalse(self._skip_native_bulk) 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
for rule in qos_consts.VALID_RULE_TYPES:
self.assertIn(
rule,
self.driver.mechanism_manager.supported_qos_rule_types)
@mock.patch.object(mech_test.TestMechanismDriver,
'supported_qos_rule_types',
new_callable=mock.PropertyMock,
return_value=qos_consts.VALID_RULE_TYPES,
create=True)
@mock.patch.object(mech_logger.LoggerMechanismDriver,
'_supports_port_binding',
new_callable=mock.PropertyMock,
return_value=False)
def test_rule_types_with_driver_that_does_not_implement_binding(self,
*mocks):
for rule in qos_consts.VALID_RULE_TYPES:
self.assertIn(
rule,
self.driver.mechanism_manager.supported_qos_rule_types)
class TestMl2BasicGet(test_plugin.TestBasicGet, class TestMl2BasicGet(test_plugin.TestBasicGet,
Ml2PluginV2TestCase): Ml2PluginV2TestCase):
pass pass

View File

@ -0,0 +1,94 @@
# 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_config import cfg
from neutron.conf.services import qos_driver_manager as notif_driver_mgr_config
from neutron.services.qos.drivers import base as qos_driver_base
from neutron.services.qos.drivers import manager as driver_mgr
from neutron.services.qos import qos_consts
from neutron.tests.unit.services.qos import base
class TestQosDriversManagerBase(base.BaseQosTestCase):
def setUp(self):
super(TestQosDriversManagerBase, self).setUp()
self.config_parse()
self.setup_coreplugin(load_plugins=False)
config = cfg.ConfigOpts()
notif_driver_mgr_config.register_qos_plugin_opts(config)
@staticmethod
def _create_manager_with_drivers(drivers_details):
for name, driver_details in drivers_details.items():
class QoSDriver(qos_driver_base.DriverBase):
@property
def is_loaded(self):
return driver_details['is_loaded']
# the new ad-hoc driver will register on the QOS_PLUGIN registry
QoSDriver(name,
driver_details.get('vif_types', []),
driver_details.get('vnic_types', []),
driver_details.get('rules', []))
return driver_mgr.QosServiceDriverManager()
class TestQosDriversManagerMulti(TestQosDriversManagerBase):
"""Test calls happen to all drivers"""
def test_driver_manager_empty_with_no_drivers(self):
driver_manager = self._create_manager_with_drivers({})
self.assertEqual(len(driver_manager._drivers), 0)
def test_driver_manager_empty_with_no_loaded_drivers(self):
driver_manager = self._create_manager_with_drivers(
{'driver-A': {'is_loaded': False}})
self.assertEqual(len(driver_manager._drivers), 0)
def test_driver_manager_with_one_loaded_driver(self):
driver_manager = self._create_manager_with_drivers(
{'driver-A': {'is_loaded': True}})
self.assertEqual(len(driver_manager._drivers), 1)
def test_driver_manager_with_two_loaded_drivers(self):
driver_manager = self._create_manager_with_drivers(
{'driver-A': {'is_loaded': True},
'driver-B': {'is_loaded': True}})
self.assertEqual(len(driver_manager._drivers), 2)
class TestQosDriversManagerRules(TestQosDriversManagerBase):
"""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,
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH]},
'driver-B': {'is_loaded': True,
'rules': [qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH,
qos_consts.RULE_TYPE_DSCP_MARKING]}
})
self.assertEqual(driver_manager.supported_rule_types,
set([qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH]))
def test_available_rules_no_rule_in_common(self):
driver_manager = self._create_manager_with_drivers(
{'driver-A': {'is_loaded': True,
'rules': [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT]},
'driver-B': {'is_loaded': True,
'rules': [qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH,
qos_consts.RULE_TYPE_DSCP_MARKING]}
})
self.assertEqual(driver_manager.supported_rule_types, set([]))

View File

@ -14,7 +14,6 @@ import mock
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import uuidutils from oslo_utils import uuidutils
from neutron.api.rpc.callbacks import events
from neutron.conf.services import qos_driver_manager as driver_mgr_config from neutron.conf.services import qos_driver_manager as driver_mgr_config
from neutron import context from neutron import context
from neutron.objects.qos import policy as policy_object from neutron.objects.qos import policy as policy_object
@ -55,37 +54,6 @@ class TestQosDriversManagerBase(base.BaseQosTestCase):
self.kwargs = {'context': ctxt} self.kwargs = {'context': ctxt}
class TestQosDriversManager(TestQosDriversManagerBase):
def setUp(self):
super(TestQosDriversManager, self).setUp()
#TODO(Qos): Fix this unittest to test manager and not message_queue
# notification driver
rpc_api_cls = mock.patch('neutron.api.rpc.handlers.resources_rpc'
'.ResourcesPushRpcApi').start()
self.rpc_api = rpc_api_cls.return_value
self.driver_manager = driver_mgr.QosServiceNotificationDriverManager()
def _validate_registry_params(self, event_type, policy):
self.rpc_api.push.assert_called_with(self.context, [policy],
event_type)
def test_create_policy_default_configuration(self):
#RPC driver should be loaded by default
self.driver_manager.create_policy(self.context, self.policy)
self.assertFalse(self.rpc_api.push.called)
def test_update_policy_default_configuration(self):
#RPC driver should be loaded by default
self.driver_manager.update_policy(self.context, self.policy)
self._validate_registry_params(events.UPDATED, self.policy)
def test_delete_policy_default_configuration(self):
#RPC driver should be loaded by default
self.driver_manager.delete_policy(self.context, self.policy)
self._validate_registry_params(events.DELETED, self.policy)
class TestQosDriversManagerMulti(TestQosDriversManagerBase): class TestQosDriversManagerMulti(TestQosDriversManagerBase):
def _test_multi_drivers_configuration_op(self, op): def _test_multi_drivers_configuration_op(self, op):

View File

@ -1,69 +0,0 @@
# 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.
import mock
from oslo_utils import uuidutils
from neutron.api.rpc.callbacks import events
from neutron import context
from neutron.objects.qos import policy as policy_object
from neutron.objects.qos import rule as rule_object
from neutron.services.qos.notification_drivers import message_queue
from neutron.tests.unit.services.qos import base
DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
class TestQosRpcNotificationDriver(base.BaseQosTestCase):
def setUp(self):
super(TestQosRpcNotificationDriver, self).setUp()
rpc_api_cls = mock.patch('neutron.api.rpc.handlers.resources_rpc'
'.ResourcesPushRpcApi').start()
self.rpc_api = rpc_api_cls.return_value
self.driver = message_queue.RpcQosServiceNotificationDriver()
policy_id = uuidutils.generate_uuid()
self.policy_data = {'policy': {
'id': policy_id,
'project_id': uuidutils.generate_uuid(),
'name': 'testi-policy',
'description': 'test policyi description',
'shared': True}}
self.rule_data = {'bandwidth_limit_rule': {
'id': policy_id,
'max_kbps': 100,
'max_burst_kbps': 150}}
self.context = context.get_admin_context()
self.policy = policy_object.QosPolicy(self.context,
**self.policy_data['policy'])
self.rule = rule_object.QosBandwidthLimitRule(
self.context,
**self.rule_data['bandwidth_limit_rule'])
def _validate_push_params(self, event_type, policy):
self.rpc_api.push.assert_called_once_with(self.context, [policy],
event_type)
def test_create_policy(self):
self.driver.create_policy(self.context, self.policy)
self.assertFalse(self.rpc_api.push.called)
def test_update_policy(self):
self.driver.update_policy(self.context, self.policy)
self._validate_push_params(events.UPDATED, self.policy)
def test_delete_policy(self):
self.driver.delete_policy(self.context, self.policy)
self._validate_push_params(events.DELETED, self.policy)

View File

@ -51,7 +51,13 @@ class TestQosPlugin(base.BaseQosTestCase):
manager.init() manager.init()
self.qos_plugin = directory.get_plugin(constants.QOS) self.qos_plugin = directory.get_plugin(constants.QOS)
#TODO(mangelajo): Remove notification_driver_manager mock in Pike
self.qos_plugin.notification_driver_manager = mock.Mock() self.qos_plugin.notification_driver_manager = mock.Mock()
self.qos_plugin.driver_manager = mock.Mock()
self.rpc_push = mock.patch('neutron.api.rpc.handlers.resources_rpc'
'.ResourcesPushRpcApi.push').start()
self.ctxt = context.Context('fake_user', 'fake_tenant') self.ctxt = context.Context('fake_user', 'fake_tenant')
mock.patch.object(self.ctxt.session, 'refresh').start() mock.patch.object(self.ctxt.session, 'refresh').start()
@ -80,19 +86,27 @@ class TestQosPlugin(base.BaseQosTestCase):
self.dscp_rule = rule_object.QosDscpMarkingRule( self.dscp_rule = rule_object.QosDscpMarkingRule(
self.ctxt, **self.rule_data['dscp_marking_rule']) self.ctxt, **self.rule_data['dscp_marking_rule'])
def _validate_notif_driver_params(self, method_name): def _validate_driver_params(self, method_name):
method = getattr(self.qos_plugin.notification_driver_manager, method = getattr(self.qos_plugin.notification_driver_manager,
method_name) method_name)
self.assertTrue(method.called) self.assertTrue(method.called)
self.assertIsInstance( self.assertIsInstance(
method.call_args[0][1], policy_object.QosPolicy) method.call_args[0][1], policy_object.QosPolicy)
self.assertTrue(self.qos_plugin.driver_manager.call.called)
self.assertEqual(self.qos_plugin.driver_manager.call.call_args[0][0],
method_name)
self.assertIsInstance(
self.qos_plugin.driver_manager.call.call_args[0][2],
policy_object.QosPolicy
)
@mock.patch( @mock.patch(
'neutron.objects.rbac_db.RbacNeutronDbObjectMixin' 'neutron.objects.rbac_db.RbacNeutronDbObjectMixin'
'.create_rbac_policy') '.create_rbac_policy')
def test_add_policy(self, *mocks): def test_add_policy(self, *mocks):
self.qos_plugin.create_policy(self.ctxt, self.policy_data) self.qos_plugin.create_policy(self.ctxt, self.policy_data)
self._validate_notif_driver_params('create_policy') self._validate_driver_params('create_policy')
def test_add_policy_with_extra_tenant_keyword(self, *mocks): def test_add_policy_with_extra_tenant_keyword(self, *mocks):
policy_id = uuidutils.generate_uuid() policy_id = uuidutils.generate_uuid()
@ -124,19 +138,19 @@ class TestQosPlugin(base.BaseQosTestCase):
policy_object.QosPolicy, self.policy_data['policy']) policy_object.QosPolicy, self.policy_data['policy'])
self.qos_plugin.update_policy( self.qos_plugin.update_policy(
self.ctxt, self.policy.id, {'policy': fields}) self.ctxt, self.policy.id, {'policy': fields})
self._validate_notif_driver_params('update_policy') self._validate_driver_params('update_policy')
@mock.patch('neutron.objects.db.api.get_object', return_value=None) @mock.patch('neutron.objects.db.api.get_object', return_value=None)
def test_delete_policy(self, *mocks): def test_delete_policy(self, *mocks):
self.qos_plugin.delete_policy(self.ctxt, self.policy.id) self.qos_plugin.delete_policy(self.ctxt, self.policy.id)
self._validate_notif_driver_params('delete_policy') self._validate_driver_params('delete_policy')
def test_create_policy_rule(self): def test_create_policy_rule(self):
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
return_value=self.policy): return_value=self.policy):
self.qos_plugin.create_policy_bandwidth_limit_rule( self.qos_plugin.create_policy_bandwidth_limit_rule(
self.ctxt, self.policy.id, self.rule_data) self.ctxt, self.policy.id, self.rule_data)
self._validate_notif_driver_params('update_policy') self._validate_driver_params('update_policy')
def test_update_policy_rule(self): def test_update_policy_rule(self):
_policy = policy_object.QosPolicy( _policy = policy_object.QosPolicy(
@ -146,7 +160,7 @@ class TestQosPlugin(base.BaseQosTestCase):
setattr(_policy, "rules", [self.rule]) setattr(_policy, "rules", [self.rule])
self.qos_plugin.update_policy_bandwidth_limit_rule( self.qos_plugin.update_policy_bandwidth_limit_rule(
self.ctxt, self.rule.id, self.policy.id, self.rule_data) self.ctxt, self.rule.id, self.policy.id, self.rule_data)
self._validate_notif_driver_params('update_policy') self._validate_driver_params('update_policy')
def test_update_policy_rule_bad_policy(self): def test_update_policy_rule_bad_policy(self):
_policy = policy_object.QosPolicy( _policy = policy_object.QosPolicy(
@ -168,7 +182,7 @@ class TestQosPlugin(base.BaseQosTestCase):
setattr(_policy, "rules", [self.rule]) setattr(_policy, "rules", [self.rule])
self.qos_plugin.delete_policy_bandwidth_limit_rule( self.qos_plugin.delete_policy_bandwidth_limit_rule(
self.ctxt, self.rule.id, _policy.id) self.ctxt, self.rule.id, _policy.id)
self._validate_notif_driver_params('update_policy') self._validate_driver_params('update_policy')
def test_delete_policy_rule_bad_policy(self): def test_delete_policy_rule_bad_policy(self):
_policy = policy_object.QosPolicy( _policy = policy_object.QosPolicy(
@ -250,7 +264,7 @@ class TestQosPlugin(base.BaseQosTestCase):
setattr(_policy, "rules", [self.dscp_rule]) setattr(_policy, "rules", [self.dscp_rule])
self.qos_plugin.create_policy_dscp_marking_rule( self.qos_plugin.create_policy_dscp_marking_rule(
self.ctxt, self.policy.id, self.rule_data) self.ctxt, self.policy.id, self.rule_data)
self._validate_notif_driver_params('update_policy') self._validate_driver_params('update_policy')
def test_update_policy_dscp_marking_rule(self): def test_update_policy_dscp_marking_rule(self):
_policy = policy_object.QosPolicy( _policy = policy_object.QosPolicy(
@ -260,7 +274,7 @@ class TestQosPlugin(base.BaseQosTestCase):
setattr(_policy, "rules", [self.dscp_rule]) setattr(_policy, "rules", [self.dscp_rule])
self.qos_plugin.update_policy_dscp_marking_rule( self.qos_plugin.update_policy_dscp_marking_rule(
self.ctxt, self.dscp_rule.id, self.policy.id, self.rule_data) self.ctxt, self.dscp_rule.id, self.policy.id, self.rule_data)
self._validate_notif_driver_params('update_policy') self._validate_driver_params('update_policy')
def test_delete_policy_dscp_marking_rule(self): def test_delete_policy_dscp_marking_rule(self):
_policy = policy_object.QosPolicy( _policy = policy_object.QosPolicy(
@ -270,7 +284,7 @@ class TestQosPlugin(base.BaseQosTestCase):
setattr(_policy, "rules", [self.dscp_rule]) setattr(_policy, "rules", [self.dscp_rule])
self.qos_plugin.delete_policy_dscp_marking_rule( self.qos_plugin.delete_policy_dscp_marking_rule(
self.ctxt, self.dscp_rule.id, self.policy.id) self.ctxt, self.dscp_rule.id, self.policy.id)
self._validate_notif_driver_params('update_policy') self._validate_driver_params('update_policy')
def test_get_policy_dscp_marking_rules(self): def test_get_policy_dscp_marking_rules(self):
with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
@ -436,7 +450,7 @@ class TestQosPlugin(base.BaseQosTestCase):
mock_manager.mock_calls.index(notify_mock_call)) mock_manager.mock_calls.index(notify_mock_call))
@mock.patch('neutron.objects.qos.policy.QosPolicy') @mock.patch('neutron.objects.qos.policy.QosPolicy')
def test_rule_notification_ordering(self, qos_policy_mock): def test_rule_notification_and_driver_ordering(self, qos_policy_mock):
rule_cls_mock = mock.Mock() rule_cls_mock = mock.Mock()
rule_cls_mock.rule_type = 'fake' rule_cls_mock.rule_type = 'fake'
@ -448,6 +462,8 @@ class TestQosPlugin(base.BaseQosTestCase):
'delete': [self.ctxt, rule_cls_mock, 'delete': [self.ctxt, rule_cls_mock,
self.rule.id, self.policy.id]} self.rule.id, self.policy.id]}
# TODO(mangelajo): Remove notification_driver_manager checks in Pike
# and rename this test
self.qos_plugin.notification_driver_manager = mock.Mock() self.qos_plugin.notification_driver_manager = mock.Mock()
mock_manager = mock.Mock() mock_manager = mock.Mock()
@ -455,6 +471,7 @@ class TestQosPlugin(base.BaseQosTestCase):
mock_manager.attach_mock(rule_cls_mock, 'RuleCls') mock_manager.attach_mock(rule_cls_mock, 'RuleCls')
mock_manager.attach_mock(self.qos_plugin.notification_driver_manager, mock_manager.attach_mock(self.qos_plugin.notification_driver_manager,
'notification_driver') 'notification_driver')
mock_manager.attach_mock(self.qos_plugin.driver_manager, 'driver')
for action, arguments in rule_actions.items(): for action, arguments in rule_actions.items():
mock_manager.reset_mock() mock_manager.reset_mock()
@ -470,6 +487,9 @@ class TestQosPlugin(base.BaseQosTestCase):
notify_mock_call = mock.call.notification_driver.update_policy( notify_mock_call = mock.call.notification_driver.update_policy(
self.ctxt, mock.ANY) self.ctxt, mock.ANY)
driver_mock_call = mock.call.driver.call('update_policy',
self.ctxt, mock.ANY)
if rule_mock_call in mock_manager.mock_calls: if rule_mock_call in mock_manager.mock_calls:
action_index = mock_manager.mock_calls.index(rule_mock_call) action_index = mock_manager.mock_calls.index(rule_mock_call)
else: else:
@ -478,3 +498,6 @@ class TestQosPlugin(base.BaseQosTestCase):
self.assertTrue( self.assertTrue(
action_index < mock_manager.mock_calls.index(notify_mock_call)) action_index < mock_manager.mock_calls.index(notify_mock_call))
self.assertTrue(
action_index < mock_manager.mock_calls.index(driver_mock_call))

View File

@ -13,7 +13,6 @@
# under the License. # under the License.
# #
import mock
import netaddr import netaddr
from neutron_lib import constants from neutron_lib import constants
from neutron_lib.plugins import directory from neutron_lib.plugins import directory
@ -43,8 +42,6 @@ class TestRevisionPlugin(test_plugin.Ml2PluginV2TestCase):
config.cfg.CONF.set_override('extension_drivers', config.cfg.CONF.set_override('extension_drivers',
self._extension_drivers, self._extension_drivers,
group='ml2') group='ml2')
mock.patch('neutron.services.qos.notification_drivers.message_queue'
'.RpcQosServiceNotificationDriver').start()
super(TestRevisionPlugin, self).setUp() super(TestRevisionPlugin, self).setUp()
self.cp = directory.get_plugin() self.cp = directory.get_plugin()
self.l3p = directory.get_plugin(constants.L3) self.l3p = directory.get_plugin(constants.L3)

View File

@ -0,0 +1,12 @@
---
features:
- The QoS driver architecture has been refactored to overcome several
previous limitations, the main one was the coupling of QoS details
into the mechanism drivers, and the next one was the need of
configuration knobs to enable each specific notification driver,
that will be handled automatically from now on.
deprecations:
- |
notification_drivers from [qos] section has been deprecated. It will be
removed in a future release.