NSX|P: QoS support

Change-Id: I719c1adfa94676b5e8b3a7b60f8d9d034d54eeb3
This commit is contained in:
Adit Sarfaty 2018-12-20 15:28:47 +02:00
parent 17759dba87
commit 74f3831027
11 changed files with 877 additions and 134 deletions

View File

@ -211,6 +211,8 @@ class NSXClient(object):
segment_id, p['id'])
self.nsxpolicy.segment_port_discovery_profiles.delete(
segment_id, p['id'])
self.nsxpolicy.segment_port_qos_profiles.delete(
segment_id, p['id'])
self.nsxpolicy.segment_port.delete(segment_id, p['id'])
except exceptions.ManagerError as e:
print("Failed to delete segment port %s: %s" % (p['id'], e))

View File

@ -283,6 +283,25 @@ Add octavia repo as an external repository and configure following flags in ``lo
[oslo_messaging]
topic=vmwarensxv_edge_lb
NSX-P
-----
QoS Driver
~~~~~~~~~~
Enable the qos in ``local.conf``::
[[local|localrc]]
ENABLED_SERVICES+=,q-qos
Q_SERVICE_PLUGIN_CLASSES+=,neutron.services.qos.qos_plugin.QoSPlugin
Optional: Update the nsx qos_peak_bw_multiplier in nsx.ini (default value is 2.0)::
[NSX]
qos_peak_bw_multiplier = <i.e 10.0>
NSX-TVD
-------

View File

@ -351,19 +351,23 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
if is_external_net:
raise nsx_exc.QoSOnExternalNet()
def _validate_update_network(self, context, id, original_net, net_data):
def _validate_update_network(self, context, net_id, original_net,
net_data):
"""Validate the updated parameters of a network
This method includes general validations that does not depend on
provider attributes, or plugin specific configurations
"""
extern_net = self._network_is_external(context, id)
extern_net = self._network_is_external(context, net_id)
with_qos = validators.is_attr_set(
net_data.get(qos_consts.QOS_POLICY_ID))
# Do not allow QoS on external networks
if with_qos and extern_net:
if with_qos:
if extern_net:
raise nsx_exc.QoSOnExternalNet()
self._validate_qos_policy_id(
context, net_data.get(qos_consts.QOS_POLICY_ID))
# Do not support changing external/non-external networks
if (extnet_apidef.EXTERNAL in net_data and
@ -371,6 +375,10 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
err_msg = _("Cannot change the router:external flag of a network")
raise n_exc.InvalidInput(error_message=err_msg)
is_ens_net = self._is_ens_tz_net(context, net_id)
if is_ens_net:
self._assert_on_ens_with_qos(net_data)
def _assert_on_illegal_port_with_qos(self, device_owner):
# Prevent creating/update port with QoS policy
# on router-interface/network-dhcp ports.
@ -392,6 +400,23 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
def _validate_ens_create_port(self, context, port_data):
qos_selected = validators.is_attr_set(port_data.get(
qos_consts.QOS_POLICY_ID))
if qos_selected:
err_msg = _("Cannot configure QOS on ENS networks")
raise n_exc.InvalidInput(error_message=err_msg)
def _assert_on_port_admin_state(self, port_data, device_owner):
"""Do not allow changing the admin state of some ports"""
if (device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF or
device_owner == l3_db.DEVICE_OWNER_ROUTER_GW):
if port_data.get("admin_state_up") is False:
err_msg = _("admin_state_up=False router ports are not "
"supported")
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
def _validate_create_port(self, context, port_data):
self._validate_max_ips_per_port(port_data.get('fixed_ips', []),
port_data.get('device_owner'))
@ -416,6 +441,10 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._assert_on_port_admin_state(port_data, device_owner)
is_ens_tz_port = self._is_ens_tz_port(context, port_data)
if is_ens_tz_port:
self._validate_ens_create_port(context, port_data)
def _assert_on_vpn_port_change(self, port_data):
if port_data['device_owner'] == ipsec_utils.VPN_PORT_OWNER:
msg = _('Can not update/delete VPNaaS port %s') % port_data['id']
@ -478,16 +507,6 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
def _assert_on_port_admin_state(self, port_data, device_owner):
"""Do not allow changing the admin state of some ports"""
if (device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF or
device_owner == l3_db.DEVICE_OWNER_ROUTER_GW):
if port_data.get("admin_state_up") is False:
err_msg = _("admin_state_up=False router ports are not "
"supported")
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
def _validate_update_port(self, context, id, original_port, port_data):
qos_selected = validators.is_attr_set(port_data.get
(qos_consts.QOS_POLICY_ID))
@ -496,6 +515,7 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
device_owner = (port_data['device_owner']
if 'device_owner' in port_data
else original_port.get('device_owner'))
is_ens_tz_port = self._is_ens_tz_port(context, original_port)
# QoS validations
if qos_selected:
@ -504,6 +524,9 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
if is_external_net:
raise nsx_exc.QoSOnExternalNet()
self._assert_on_illegal_port_with_qos(device_owner)
if is_ens_tz_port:
err_msg = _("Cannot configure QOS on ENS networks")
raise n_exc.InvalidInput(error_message=err_msg)
# External networks validations:
if is_external_net:
@ -620,6 +643,44 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
err_msg = _("Cannot configure QOS on ENS networks")
raise n_exc.InvalidInput(error_message=err_msg)
def _get_port_qos_policy_id(self, context, original_port,
updated_port):
"""Return the QoS policy Id of a port that is being created/updated
Return the QoS policy assigned directly to the port (after update or
originally), or the policy of the network, if it is a compute port that
should inherit it.
original_port: the neutron port data before this update
(or None in a case of a new port creation)
updated_ports: the modified fields of this port
(or all th attributes of the new port)
"""
orig_compute = False
if original_port:
orig_compute = original_port.get('device_owner', '').startswith(
constants.DEVICE_OWNER_COMPUTE_PREFIX)
updated_compute = updated_port.get('device_owner', '').startswith(
constants.DEVICE_OWNER_COMPUTE_PREFIX)
is_new_compute = updated_compute and not orig_compute
qos_policy_id = None
if validators.is_attr_set(updated_port.get(qos_consts.QOS_POLICY_ID)):
qos_policy_id = updated_port[qos_consts.QOS_POLICY_ID]
elif original_port:
# Look for the original QoS policy of this port
qos_policy_id = qos_com_utils.get_port_policy_id(
context, original_port['id'])
# If the port is now a 'compute' port (attached to a vm) and
# Qos policy was not configured on the port directly,
# try to take it from the ports network
if qos_policy_id is None and is_new_compute:
# check if the network of this port has a policy
net_id = (original_port.get('network_id') if original_port
else updated_port.get('network_id'))
qos_policy_id = qos_com_utils.get_network_policy_id(
context, net_id)
return qos_policy_id
def _ens_psec_supported(self):
"""Should be implemented by each plugin"""
pass
@ -646,13 +707,13 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
"""
pass
def _is_ens_tz_net(self, context, net_id):
def _is_ens_tz_net(self, context, network_id):
"""Should be implemented by each plugin"""
pass
def _is_ens_tz_port(self, context, port_data):
"""Should be implemented by each plugin"""
pass
# Check the host-switch-mode of the TZ connected to the ports network
return self._is_ens_tz_net(context, port_data['network_id'])
def _is_overlay_network(self, network_id):
"""Should be implemented by each plugin"""
@ -691,7 +752,7 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
- net_type: provider network type or None
- physical_net: the uuid of the relevant transport zone or None
- vlan_id: vlan tag, 0 or None
- switch_mode: standard ot ENS
- switch_mode: standard or ENS
"""
is_provider_net = any(
validators.is_attr_set(network_data.get(f))
@ -870,6 +931,12 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
port_data[pbin.VIF_DETAILS]['segmentation-id'] = (
self._get_network_segmentation_id(context, net_id))
def _extend_qos_port_dict_binding(self, context, port):
# add the qos policy id from the DB
if 'id' in port:
port[qos_consts.QOS_POLICY_ID] = qos_com_utils.get_port_policy_id(
context, port['id'])
def fix_direct_vnic_port_sec(self, direct_vnic_type, port_data):
if direct_vnic_type:
if validators.is_attr_set(port_data.get(psec.PORTSECURITY)):

View File

@ -43,6 +43,9 @@ from neutron_lib.db import api as db_api
from neutron_lib.db import resource_extend
from neutron_lib.db import utils as db_utils
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import constants as plugin_const
from neutron_lib.plugins import directory
from neutron_lib.services.qos import constants as qos_consts
from vmware_nsx._i18n import _
from vmware_nsx.common import config # noqa
@ -60,6 +63,9 @@ from vmware_nsx.extensions import securitygrouplogging as sg_logging
from vmware_nsx.plugins.common_v3 import plugin as nsx_plugin_common
from vmware_nsx.plugins.nsx_p import availability_zones as nsxp_az
from vmware_nsx.plugins.nsx_v3 import utils as v3_utils
from vmware_nsx.services.qos.common import utils as qos_com_utils
from vmware_nsx.services.qos.nsx_v3 import driver as qos_driver
from vmware_nsx.services.qos.nsx_v3 import pol_utils as qos_utils
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts
@ -172,6 +178,9 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
self._init_native_dhcp()
# Init QoS
qos_driver.register(qos_utils.PolicyQosNotificationsHandler())
# subscribe the init complete method last, so it will be called only
# if init was successful
registry.subscribe(self.init_complete,
@ -288,9 +297,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# update the network name to indicate the neutron id too.
net_name = utils.get_name_and_uuid(net_data['name'] or 'network',
net_data['id'])
tags = self.nsxpolicy.build_v3_tags_payload(
net_data, resource_type='os-neutron-net-id',
project_name=context.tenant_name)
tags = self.nsxpolicy.build_v3_api_version_project_tag(
context.tenant_name)
# TODO(annak): admin state config is missing on policy
# should we not create networks that are down?
@ -418,6 +426,16 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# latest db model for the extension functions
net_model = self._get_network(context, created_net['id'])
resource_extend.apply_funcs('networks', created_net, net_model)
# Update the QoS policy (will affect only future compute ports)
qos_com_utils.set_qos_policy_on_new_net(
context, net_data, created_net)
if net_data.get(qos_consts.QOS_POLICY_ID):
LOG.info("QoS Policy %(qos)s will be applied to future compute "
"ports of network %(net)s",
{'qos': net_data[qos_consts.QOS_POLICY_ID],
'net': created_net['id']})
return created_net
def delete_network(self, context, network_id):
@ -438,6 +456,10 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
context, network_id)
net_data = network['network']
# Validate the updated parameters
self._validate_update_network(context, network_id, original_net,
net_data)
# Neutron does not support changing provider network values
providernet._raise_if_updates_provider_attributes(net_data)
extern_net = self._network_is_external(context, network_id)
@ -457,6 +479,19 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
self._process_l3_update(context, updated_net, network['network'])
self._extend_network_dict_provider(context, updated_net)
if qos_consts.QOS_POLICY_ID in net_data:
# attach the policy to the network in neutron DB
#(will affect only future compute ports)
qos_com_utils.update_network_policy_binding(
context, network_id, net_data[qos_consts.QOS_POLICY_ID])
updated_net[qos_consts.QOS_POLICY_ID] = net_data[
qos_consts.QOS_POLICY_ID]
if net_data[qos_consts.QOS_POLICY_ID]:
LOG.info("QoS Policy %(qos)s will be applied to future "
"compute ports of network %(net)s",
{'qos': net_data[qos_consts.QOS_POLICY_ID],
'net': network_id})
# Update the backend segment
if (not extern_net and not is_nsx_net and
('name' in net_data or 'description' in net_data)):
@ -562,7 +597,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
return tags
def _create_port_on_backend(self, context, port_data, is_psec_on):
def _create_port_on_backend(self, context, port_data, is_psec_on,
qos_policy_id):
# TODO(annak): admin_state not supported by policy
name = self._build_port_name(context, port_data)
address_bindings = self._build_port_address_bindings(
@ -620,6 +656,12 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
name, segment_id, port_data['id'],
mac_discovery_profile_id=mac_discovery_profile)
# Add QoS segment profile (only if QoS is enabled)
if directory.get_plugin(plugin_const.QOS):
self.nsxpolicy.segment_port_qos_profiles.create_or_overwrite(
name, segment_id, port_data['id'],
qos_profile_id=qos_policy_id)
def base_create_port(self, context, port):
neutron_db = super(NsxPolicyPlugin, self).create_port(context, port)
self._extension_manager.process_create_port(
@ -628,8 +670,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
def create_port(self, context, port, l2gw_port_check=False):
port_data = port['port']
self._validate_max_ips_per_port(port_data.get('fixed_ips', []),
port_data.get('device_owner'))
# validate the new port parameters
self._validate_create_port(context, port_data)
# Validate the vnic type (the same types as for the NSX-T plugin)
direct_vnic_type = self._validate_port_vnic_type(
@ -673,9 +715,13 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# ATTR_NOT_SPECIFIED
port_data.pop(mac_ext.MAC_LEARNING)
qos_policy_id = self._get_port_qos_policy_id(
context, None, port_data)
if not is_external_net:
try:
self._create_port_on_backend(context, port_data, is_psec_on)
self._create_port_on_backend(context, port_data, is_psec_on,
qos_policy_id)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error('Failed to create port %(id)s on NSX '
@ -684,6 +730,11 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
super(NsxPolicyPlugin, self).delete_port(
context, neutron_db['id'])
# Attach the policy to the port in the neutron DB
if qos_policy_id:
qos_com_utils.update_port_policy_binding(context,
neutron_db['id'],
qos_policy_id)
# this extra lookup is necessary to get the
# latest db model for the extension functions
port_model = self._get_port(context, port_data['id'])
@ -716,6 +767,9 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
segment_id, port_id)
self.nsxpolicy.segment_port_discovery_profiles.delete(
segment_id, port_id)
if directory.get_plugin(plugin_const.QOS):
self.nsxpolicy.segment_port_qos_profiles.delete(
segment_id, port_id)
self.nsxpolicy.segment_port.delete(segment_id, port_id)
except Exception as ex:
LOG.error("Failed to delete port %(id)s on NSX backend "
@ -724,10 +778,11 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
def _update_port_on_backend(self, context, lport_id,
original_port, updated_port,
is_psec_on):
is_psec_on, qos_policy_id):
# For now port create and update are the same
# Update might evolve with more features
return self._create_port_on_backend(context, updated_port, is_psec_on)
return self._create_port_on_backend(context, updated_port, is_psec_on,
qos_policy_id)
def update_port(self, context, port_id, port):
with db_api.CONTEXT_WRITER.using(context):
@ -792,13 +847,19 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
self._update_mac_learning_state(context, port_id,
mac_learning_state)
# Update the QoS policy
qos_policy_id = self._get_port_qos_policy_id(
context, original_port, updated_port)
qos_com_utils.update_port_policy_binding(context, port_id,
qos_policy_id)
# update the port in the backend, only if it exists in the DB
# (i.e not external net)
if not is_external_net:
try:
self._update_port_on_backend(context, port_id,
original_port, updated_port,
port_security)
port_security, qos_policy_id)
except Exception as e:
LOG.error('Failed to update port %(id)s on NSX '
'backend. Exception: %(e)s',
@ -833,6 +894,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
port_model = self._get_port(context, port['id'])
resource_extend.apply_funcs('ports', port, port_model)
self._extend_nsx_port_dict_binding(context, port)
self._extend_qos_port_dict_binding(context, port)
self._remove_provider_security_groups_from_list(port)
return db_utils.resource_fields(port, fields)
@ -859,6 +921,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
ports.remove(port)
continue
self._extend_nsx_port_dict_binding(context, port)
self._extend_qos_port_dict_binding(context, port)
self._remove_provider_security_groups_from_list(port)
return (ports if not fields else
[db_utils.resource_fields(port, fields) for port in ports])
@ -1845,10 +1908,6 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
#TODO(annak): handle ENS case
return False
def _is_ens_tz_port(self, context, port_data):
#TODO(annak): handle ENS case
return False
def _has_native_dhcp_metadata(self):
return True

View File

@ -100,6 +100,7 @@ from vmware_nsx.services.lbaas.octavia import constants as oct_const
from vmware_nsx.services.lbaas.octavia import octavia_listener
from vmware_nsx.services.qos.common import utils as qos_com_utils
from vmware_nsx.services.qos.nsx_v3 import driver as qos_driver
from vmware_nsx.services.qos.nsx_v3 import utils as qos_utils
from vmware_nsx.services.trunk.nsx_v3 import driver as trunk_driver
from vmware_nsxlib.v3 import core_resources as nsx_resources
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
@ -231,7 +232,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
) % NSX_V3_EXCLUDED_PORT_NSGROUP_NAME
raise nsx_exc.NsxPluginException(err_msg=msg)
qos_driver.register()
qos_driver.register(qos_utils.QosNotificationsHandler())
self.start_rpc_listeners_called = False
@ -1196,17 +1197,12 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
net_data = network['network']
# Neutron does not support changing provider network values
providernet._raise_if_updates_provider_attributes(net_data)
self._validate_qos_policy_id(
context, net_data.get(qos_consts.QOS_POLICY_ID))
extern_net = self._network_is_external(context, id)
is_nsx_net = self._network_is_nsx_net(context, id)
is_ens_net = self._is_ens_tz_net(context, id)
# Validate the updated parameters
self._validate_update_network(context, id, original_net, net_data)
# add some plugin specific validations
if is_ens_net:
self._assert_on_ens_with_qos(net_data)
updated_net = super(NsxV3Plugin, self).update_network(context, id,
network)
@ -1766,13 +1762,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
profiles.append(self._dhcp_profile)
# Add QoS switching profile, if exists
qos_policy_id = None
if validators.is_attr_set(port_data.get(qos_consts.QOS_POLICY_ID)):
qos_policy_id = port_data[qos_consts.QOS_POLICY_ID]
elif device_owner.startswith(const.DEVICE_OWNER_COMPUTE_PREFIX):
# check if the network of this port has a policy
qos_policy_id = qos_com_utils.get_network_policy_id(
context, port_data['network_id'])
qos_policy_id = self._get_port_qos_policy_id(
context, None, port_data)
if qos_policy_id:
qos_profile_id = self._get_qos_profile_id(context, qos_policy_id)
profiles.append(qos_profile_id)
@ -1845,10 +1836,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
mode = self.nsxlib.transport_zone.get_host_switch_mode(tz_id)
return mode == self.nsxlib.transport_zone.HOST_SWITCH_MODE_ENS
def _is_ens_tz_port(self, context, port_data):
# Check the host-switch-mode of the TZ connected to the ports network
return self._is_ens_tz_net(context, port_data['network_id'])
def _has_native_dhcp_metadata(self):
return cfg.CONF.nsx_v3.native_dhcp_metadata
@ -2222,19 +2209,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
self.nsxlib.ns_group.update_lport_nsgroups(
lport_id, nsx_origial, nsx_updated)
def base_create_port(self, context, port):
neutron_db = super(NsxV3Plugin, self).create_port(context, port)
self._extension_manager.process_create_port(
context, port['port'], neutron_db)
return neutron_db
def _validate_ens_create_port(self, context, port_data):
qos_selected = validators.is_attr_set(port_data.get(
qos_consts.QOS_POLICY_ID))
if qos_selected:
err_msg = _("Cannot configure QOS on ENS networks")
raise n_exc.InvalidInput(error_message=err_msg)
def _disable_ens_portsec(self, port_data):
if (cfg.CONF.nsx_v3.disable_port_security_for_ens and
not self._ens_psec_supported()):
LOG.warning("Disabling port security for network %s",
@ -2242,6 +2217,12 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
port_data[psec.PORTSECURITY] = False
port_data['security_groups'] = []
def base_create_port(self, context, port):
neutron_db = super(NsxV3Plugin, self).create_port(context, port)
self._extension_manager.process_create_port(
context, port['port'], neutron_db)
return neutron_db
def create_port(self, context, port, l2gw_port_check=False):
port_data = port['port']
@ -2253,7 +2234,14 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
self._assert_on_dhcp_relay_without_router(context, port_data)
is_ens_tz_port = self._is_ens_tz_port(context, port_data)
if is_ens_tz_port:
self._validate_ens_create_port(context, port_data)
self._disable_ens_portsec(port_data)
if (cfg.CONF.nsx_v3.disable_port_security_for_ens and
not self._ens_psec_supported()):
LOG.warning("Disabling port security for network %s",
port_data['network_id'])
port_data[psec.PORTSECURITY] = False
port_data['security_groups'] = []
is_external_net = self._network_is_external(
context, port_data['network_id'])
@ -2567,14 +2555,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
switch_profile_ids.append(self._dhcp_profile)
# Update QoS switch profile
orig_compute = original_device_owner.startswith(
const.DEVICE_OWNER_COMPUTE_PREFIX)
updated_compute = updated_device_owner.startswith(
const.DEVICE_OWNER_COMPUTE_PREFIX)
is_new_compute = updated_compute and not orig_compute
qos_policy_id, qos_profile_id = self._get_port_qos_ids(context,
updated_port,
is_new_compute)
qos_policy_id, qos_profile_id = self._get_port_qos_ids(
context, original_port, updated_port)
if qos_profile_id is not None:
switch_profile_ids.append(qos_profile_id)
@ -2619,28 +2601,13 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
updated_port['id'],
qos_policy_id)
def _get_port_qos_ids(self, context, updated_port, is_new_compute):
# when a port is updated, get the current QoS policy/profile ids
policy_id = None
def _get_port_qos_ids(self, context, original_port, updated_port):
qos_policy_id = self._get_port_qos_policy_id(
context, original_port, updated_port)
profile_id = None
if (qos_consts.QOS_POLICY_ID in updated_port):
policy_id = updated_port[qos_consts.QOS_POLICY_ID]
else:
# Look for the previous QoS policy
policy_id = qos_com_utils.get_port_policy_id(
context, updated_port['id'])
# If the port is now a 'compute' port (attached to a vm) and
# Qos policy was not configured on the port directly,
# try to take it from the ports network
if policy_id is None and is_new_compute:
# check if the network of this port has a policy
policy_id = qos_com_utils.get_network_policy_id(
context, updated_port.get('network_id'))
if policy_id is not None:
profile_id = self._get_qos_profile_id(context, policy_id)
return policy_id, profile_id
if qos_policy_id is not None:
profile_id = self._get_qos_profile_id(context, qos_policy_id)
return qos_policy_id, profile_id
def update_port(self, context, id, port):
with db_api.CONTEXT_WRITER.using(context):
@ -2660,11 +2627,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
self._assert_on_dhcp_relay_without_router(context, port_data,
original_port)
is_ens_tz_port = self._is_ens_tz_port(context, original_port)
qos_selected = validators.is_attr_set(port_data.get
(qos_consts.QOS_POLICY_ID))
if is_ens_tz_port and qos_selected:
err_msg = _("Cannot configure QOS on ENS networks")
raise n_exc.InvalidInput(error_message=err_msg)
dhcp_opts = port_data.get(ext_edo.EXTRADHCPOPTS)
self._validate_extra_dhcp_options(dhcp_opts)
@ -2780,11 +2742,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
def _extend_get_port_dict_qos_and_binding(self, context, port):
# Not using the register api for this because we need the context
self._extend_nsx_port_dict_binding(context, port)
# add the qos policy id from the DB
if 'id' in port:
port[qos_consts.QOS_POLICY_ID] = qos_com_utils.get_port_policy_id(
context, port['id'])
self._extend_qos_port_dict_binding(context, port)
def get_port(self, context, id, fields=None):
port = super(NsxV3Plugin, self).get_port(context, id, fields=None)

View File

@ -93,7 +93,7 @@ def set_qos_policy_on_new_net(context, net_data, created_net):
# attach the policy to the network in the neutron DB
update_network_policy_binding(
context,
net_data['id'],
created_net['id'],
qos_policy_id)
created_net[qos_consts.QOS_POLICY_ID] = qos_policy_id
return qos_policy_id

View File

@ -20,8 +20,6 @@ from neutron_lib.services.qos import base
from neutron_lib.services.qos import constants as qos_consts
from oslo_log import log as logging
from vmware_nsx.services.qos.nsx_v3 import utils as qos_utils
LOG = logging.getLogger(__name__)
DRIVER = None
@ -45,16 +43,17 @@ SUPPORTED_RULES = {
class NSXv3QosDriver(base.DriverBase):
@staticmethod
def create():
def create(handler):
return NSXv3QosDriver(
name='NSXv3QosDriver',
vif_types=None,
vnic_types=None,
supported_rules=SUPPORTED_RULES,
requires_rpc_notifications=False)
requires_rpc_notifications=False,
handler=handler)
def __init__(self, **kwargs):
self.handler = qos_utils.QosNotificationsHandler()
def __init__(self, handler=None, **kwargs):
self.handler = handler
super(NSXv3QosDriver, self).__init__(**kwargs)
def is_vif_type_compatible(self, vif_type):
@ -67,11 +66,12 @@ class NSXv3QosDriver(base.DriverBase):
self.handler.create_policy(context, policy)
def update_policy(self, context, policy):
# Update the rules
if (hasattr(policy, "rules")):
self.handler.update_policy_rules(
context, policy.id, policy["rules"])
# May also need to update name / description
# Update the entire policy
self.handler.update_policy(context, policy.id, policy)
def delete_policy(self, context, policy):
@ -84,9 +84,9 @@ class NSXv3QosDriver(base.DriverBase):
self.handler.validate_policy_rule(context, policy.id, rule)
def register():
def register(handler):
"""Register the NSX-V3 QoS driver."""
global DRIVER
if not DRIVER:
DRIVER = NSXv3QosDriver.create()
DRIVER = NSXv3QosDriver.create(handler)
LOG.debug('NSXv3QosDriver QoS driver registered')

View File

@ -0,0 +1,160 @@
# Copyright 2018 VMware, 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_config import cfg
from oslo_log import log as logging
from neutron_lib import constants as n_consts
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import directory
from neutron_lib.services.qos import constants as qos_consts
from vmware_nsx._i18n import _
from vmware_nsx.common import utils
LOG = logging.getLogger(__name__)
MAX_KBPS_MIN_VALUE = 1024
# The max limit is calculated so that the value sent to the backed will
# be smaller than 2**31
MAX_BURST_MAX_VALUE = int((2 ** 31 - 1) / 128)
class PolicyQosNotificationsHandler(object):
def __init__(self):
super(PolicyQosNotificationsHandler, self).__init__()
self._core_plugin = None
@property
def core_plugin(self):
if not self._core_plugin:
self._core_plugin = directory.get_plugin()
return self._core_plugin
@property
def _nsxpolicy(self):
return self.core_plugin.nsxpolicy
def create_or_update_policy(self, context, policy):
policy_id = policy.id
tags = self._nsxpolicy.build_v3_api_version_project_tag(
context.tenant_name, project_id=policy.tenant_id)
pol_name = utils.get_name_and_uuid(policy.name or 'policy',
policy.id)
shapers = []
dscp = None
if (hasattr(policy, "rules")):
for rule in policy["rules"]:
if rule.rule_type == qos_consts.RULE_TYPE_BANDWIDTH_LIMIT:
# the NSX direction is opposite to the neutron one
is_ingress = rule.direction == n_consts.EGRESS_DIRECTION
shapers.append(self._get_shaper_from_rule(
rule, is_ingress=is_ingress))
elif rule.rule_type == qos_consts.RULE_TYPE_DSCP_MARKING:
dscp = self._get_dscp_from_rule(rule)
else:
LOG.warning("The NSX-Policy plugin does not support QoS "
"rule of type %s", rule.rule_type)
self._nsxpolicy.qos_profile.create_or_overwrite(
pol_name, profile_id=policy_id,
description=policy.get('description'),
dscp=dscp, shaper_configurations=shapers,
tags=tags)
def create_policy(self, context, policy):
return self.create_or_update_policy(context, policy)
def delete_policy(self, context, policy_id):
self._nsxpolicy.qos_profile.delete(policy_id)
def update_policy(self, context, policy_id, policy):
return self.create_or_update_policy(context, policy)
def _validate_bw_values(self, bw_rule):
"""Validate that the values are allowed by the NSX backend"""
# Validate the max bandwidth value minimum value
# (max value is above what neutron allows so no need to check it)
if (bw_rule.max_kbps < MAX_KBPS_MIN_VALUE):
msg = (_("Invalid input for max_kbps. "
"The minimal legal value is %s") % MAX_KBPS_MIN_VALUE)
LOG.error(msg)
raise n_exc.InvalidInput(error_message=msg)
# validate the burst size value max value
# (max value is 0, and neutron already validates this)
if (bw_rule.max_burst_kbps > MAX_BURST_MAX_VALUE):
msg = (_("Invalid input for burst_size. "
"The maximal legal value is %s") % MAX_BURST_MAX_VALUE)
LOG.error(msg)
raise n_exc.InvalidInput(error_message=msg)
def _get_shaper_from_rule(self, bw_rule, is_ingress=True):
"""Translate the neutron bandwidth_limit_rule values into the
NSX-lib Policy QoS shaper
"""
kwargs = {}
if is_ingress:
shaper = self._nsxpolicy.qos_profile.build_ingress_rate_limiter
else:
shaper = self._nsxpolicy.qos_profile.build_egress_rate_limiter
if bw_rule:
kwargs['enabled'] = True
# translate kbps -> bytes
kwargs['burst_size'] = int(bw_rule.max_burst_kbps) * 128
# value in kbps -> Mb/s
kwargs['average_bandwidth'] = int(
round(float(bw_rule.max_kbps) / 1024))
# peakBandwidth: a Multiplying on the average BW because the
# neutron qos configuration supports only 1 value
kwargs['peak_bandwidth'] = int(
round(kwargs['average_bandwidth'] *
cfg.CONF.NSX.qos_peak_bw_multiplier))
else:
kwargs['enabled'] = False
return shaper(**kwargs)
def _get_dscp_from_rule(self, dscp_rule):
"""Translate the neutron DSCP marking rule values into NSX-lib
Policy QoS Dscp object
"""
trusted = False if dscp_rule else True
priority = dscp_rule.dscp_mark if dscp_rule else 0
return self._nsxpolicy.qos_profile.build_dscp(
trusted=trusted, priority=priority)
def update_policy_rules(self, context, policy_id, rules):
"""This handler will do all the updates through the create_or_update"""
pass
def validate_policy_rule(self, context, policy_id, rule):
"""Raise an exception if the rule values are not supported"""
if rule.rule_type == qos_consts.RULE_TYPE_BANDWIDTH_LIMIT:
self._validate_bw_values(rule)
elif rule.rule_type == qos_consts.RULE_TYPE_DSCP_MARKING:
pass
else:
msg = (_("The NSX-Policy plugin does not support QoS rule of type "
"%s") % rule.rule_type)
LOG.error(msg)
raise n_exc.InvalidInput(error_message=msg)

View File

@ -97,12 +97,7 @@ class QosNotificationsHandler(object):
description=policy.description)
def _validate_bw_values(self, bw_rule):
"""Validate that the configured values are allowed by the NSX backend.
Since failing the action from the notification callback
is not possible, just log the warning and use the minimal/maximal
values.
"""
"""Validate that the values are allowed by the NSX backend"""
# Validate the max bandwidth value minimum value
# (max value is above what neutron allows so no need to check it)
if (bw_rule.max_kbps < MAX_KBPS_MIN_VALUE):

View File

@ -43,6 +43,7 @@ from neutron_lib.callbacks import resources
from neutron_lib import constants
from neutron_lib import context
from neutron_lib import exceptions as n_exc
from neutron_lib.objects import registry as obj_reg
from neutron_lib.plugins import directory
from vmware_nsx.common import utils
@ -86,6 +87,7 @@ class NsxPPluginTestCaseMixin(
self.setup_conf_overrides()
super(NsxPPluginTestCaseMixin, self).setUp(plugin=plugin,
ext_mgr=ext_mgr)
self.ctx = context.get_admin_context()
def _mock_nsx_policy_backend_calls(self):
resource_list_result = {'results': [{'id': 'test',
@ -181,6 +183,17 @@ class NsxPPluginTestCaseMixin(
'', tenant_id)
return network_req.get_response(self.api)
def _create_l3_ext_network(self, physical_network='abc'):
name = 'l3_ext_net'
net_type = utils.NetworkTypes.L3_EXT
providernet_args = {pnet.NETWORK_TYPE: net_type,
pnet.PHYSICAL_NETWORK: physical_network}
return self.network(name=name,
router__external=True,
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE,
pnet.PHYSICAL_NETWORK))
class NsxPTestNetworks(test_db_base_plugin_v2.TestNetworksV2,
NsxPPluginTestCaseMixin):
@ -419,6 +432,47 @@ class NsxPTestNetworks(test_db_base_plugin_v2.TestNetworksV2,
az_hints = net['network']['availability_zone_hints']
self.assertListEqual(az_hints, zone)
def test_create_net_with_qos(self):
policy_id = uuidutils.generate_uuid()
data = {'network': {
'tenant_id': self._tenant_id,
'qos_policy_id': policy_id,
'name': 'qos_net',
'admin_state_up': True,
'shared': False}
}
dummy = mock.Mock()
dummy.id = policy_id
with mock.patch.object(self.plugin, '_validate_qos_policy_id'),\
mock.patch.object(obj_reg.load_class('QosPolicy'),
'get_network_policy',
return_value=dummy):
net = self.plugin.create_network(self.ctx, data)
self.assertEqual(policy_id, net['qos_policy_id'])
net = self.plugin.get_network(self.ctx, net['id'])
self.assertEqual(policy_id, net['qos_policy_id'])
def test_update_net_with_qos(self):
data = {'network': {
'tenant_id': self._tenant_id,
'name': 'qos_net',
'admin_state_up': True,
'shared': False}
}
net = self.plugin.create_network(self.ctx, data)
policy_id = uuidutils.generate_uuid()
data['network']['qos_policy_id'] = policy_id
dummy = mock.Mock()
dummy.id = policy_id
with mock.patch.object(self.plugin, '_validate_qos_policy_id'),\
mock.patch.object(obj_reg.load_class('QosPolicy'),
'get_network_policy',
return_value=dummy):
res = self.plugin.update_network(self.ctx, net['id'], data)
self.assertEqual(policy_id, res['qos_policy_id'])
res = self.plugin.get_network(self.ctx, net['id'])
self.assertEqual(policy_id, res['qos_policy_id'])
class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
NsxPPluginTestCaseMixin):
@ -557,6 +611,81 @@ class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
self.assertEqual(res['port']['fixed_ips'],
data['port']['fixed_ips'])
def test_create_port_with_qos(self):
with self.network() as network:
policy_id = uuidutils.generate_uuid()
data = {'port': {
'network_id': network['network']['id'],
'tenant_id': self._tenant_id,
'qos_policy_id': policy_id,
'name': 'qos_port',
'admin_state_up': True,
'device_id': 'fake_device',
'device_owner': 'fake_owner',
'fixed_ips': [],
'mac_address': '00:00:00:00:00:01'}
}
with mock.patch.object(self.plugin, '_validate_qos_policy_id'):
port = self.plugin.create_port(self.ctx, data)
self.assertEqual(policy_id, port['qos_policy_id'])
# Get port should also return the qos policy id
with mock.patch('vmware_nsx.services.qos.common.utils.'
'get_port_policy_id',
return_value=policy_id):
port = self.plugin.get_port(self.ctx, port['id'])
self.assertEqual(policy_id, port['qos_policy_id'])
def test_update_port_with_qos(self):
with self.network() as network:
data = {'port': {
'network_id': network['network']['id'],
'tenant_id': self._tenant_id,
'name': 'qos_port',
'admin_state_up': True,
'device_id': 'fake_device',
'device_owner': 'fake_owner',
'fixed_ips': [],
'mac_address': '00:00:00:00:00:01'}
}
port = self.plugin.create_port(self.ctx, data)
policy_id = uuidutils.generate_uuid()
data['port']['qos_policy_id'] = policy_id
with mock.patch.object(self.plugin, '_validate_qos_policy_id'):
res = self.plugin.update_port(self.ctx, port['id'], data)
self.assertEqual(policy_id, res['qos_policy_id'])
# Get port should also return the qos policy id
with mock.patch('vmware_nsx.services.qos.common.utils.'
'get_port_policy_id',
return_value=policy_id):
res = self.plugin.get_port(self.ctx, port['id'])
self.assertEqual(policy_id, res['qos_policy_id'])
def test_create_ext_port_with_qos_fail(self):
with self._create_l3_ext_network() as network:
with self.subnet(network=network, cidr='10.0.0.0/24',
enable_dhcp=False),\
mock.patch.object(self.plugin, '_validate_qos_policy_id'):
policy_id = uuidutils.generate_uuid()
data = {'port': {'network_id': network['network']['id'],
'tenant_id': self._tenant_id,
'qos_policy_id': policy_id}}
# Cannot add qos policy to a router port
self.assertRaises(n_exc.InvalidInput,
self.plugin.create_port, self.ctx, data)
def _test_create_illegal_port_with_qos_fail(self, device_owner):
with self.network() as network:
with self.subnet(network=network, cidr='10.0.0.0/24'),\
mock.patch.object(self.plugin, '_validate_qos_policy_id'):
policy_id = uuidutils.generate_uuid()
data = {'port': {'network_id': network['network']['id'],
'tenant_id': self._tenant_id,
'device_owner': device_owner,
'qos_policy_id': policy_id}}
# Cannot add qos policy to this type of port
self.assertRaises(n_exc.InvalidInput,
self.plugin.create_port, self.ctx, data)
def test_create_port_with_mac_learning_true(self):
plugin = directory.get_plugin()
ctx = context.get_admin_context()
@ -564,7 +693,7 @@ class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
data = {'port': {
'network_id': network['network']['id'],
'tenant_id': self._tenant_id,
'name': 'qos_port',
'name': 'port',
'admin_state_up': True,
'device_id': 'fake_device',
'device_owner': 'fake_owner',
@ -583,7 +712,7 @@ class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
data = {'port': {
'network_id': network['network']['id'],
'tenant_id': self._tenant_id,
'name': 'qos_port',
'name': 'port',
'admin_state_up': True,
'device_id': 'fake_device',
'device_owner': 'fake_owner',
@ -602,7 +731,7 @@ class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
data = {'port': {
'network_id': network['network']['id'],
'tenant_id': self._tenant_id,
'name': 'qos_port',
'name': 'port',
'admin_state_up': True,
'device_id': 'fake_device',
'device_owner': 'fake_owner',
@ -622,7 +751,7 @@ class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
data = {'port': {
'network_id': network['network']['id'],
'tenant_id': self._tenant_id,
'name': 'qos_port',
'name': 'port',
'admin_state_up': True,
'device_id': 'fake_device',
'device_owner': 'fake_owner',
@ -642,7 +771,7 @@ class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
data = {'port': {
'network_id': network['network']['id'],
'tenant_id': self._tenant_id,
'name': 'qos_port',
'name': 'port',
'admin_state_up': True,
'device_id': 'fake_device',
'device_owner': constants.DEVICE_OWNER_FLOATINGIP,
@ -1032,17 +1161,6 @@ class NsxPTestL3NatTest(common_v3.FixExternalNetBaseTest,
arg_list=(pnet.NETWORK_TYPE,
pnet.PHYSICAL_NETWORK))
def _create_l3_ext_network(self, physical_network='abc'):
name = 'l3_ext_net'
net_type = utils.NetworkTypes.L3_EXT
providernet_args = {pnet.NETWORK_TYPE: net_type,
pnet.PHYSICAL_NETWORK: physical_network}
return self.network(name=name,
router__external=True,
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE,
pnet.PHYSICAL_NETWORK))
def test_floatingip_create_different_fixed_ip_same_port(self):
self.skipTest('Multiple fixed ips on a port are not supported')

View File

@ -0,0 +1,365 @@
# Copyright 2016 VMware, 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.
import mock
from neutron_lib import context
from neutron_lib import exceptions
from neutron_lib.objects import registry as obj_reg
from oslo_config import cfg
from oslo_utils import uuidutils
from neutron.services.qos import qos_plugin
from neutron.tests.unit.services.qos import base
from vmware_nsx.common import utils
from vmware_nsx.plugins.nsx_v3 import utils as v3_utils
from vmware_nsx.services.qos.nsx_v3 import driver as qos_driver
from vmware_nsx.services.qos.nsx_v3 import pol_utils as qos_utils
from vmware_nsx.tests.unit.nsx_p import test_plugin
from vmware_nsxlib.v3.policy import core_defs as policy_defs
PLUGIN_NAME = 'vmware_nsx.plugins.nsx_p.plugin.NsxPolicyPlugin'
QoSPolicy = obj_reg.load_class('QosPolicy')
QosBandwidthLimitRule = obj_reg.load_class('QosBandwidthLimitRule')
QosDscpMarkingRule = obj_reg.load_class('QosDscpMarkingRule')
QosMinimumBandwidthRule = obj_reg.load_class('QosMinimumBandwidthRule')
class TestQosNsxPNotification(base.BaseQosTestCase,
test_plugin.NsxPPluginTestCaseMixin):
def setUp(self):
# Reset the drive to re-create it
qos_driver.DRIVER = None
super(TestQosNsxPNotification, self).setUp()
self.setup_coreplugin(PLUGIN_NAME)
self.qos_plugin = qos_plugin.QoSPlugin()
self.ctxt = context.Context('fake_user', 'fake_tenant')
mock.patch.object(self.ctxt.session, 'refresh').start()
mock.patch.object(self.ctxt.session, 'expunge').start()
policy_id = uuidutils.generate_uuid()
self.project_id = uuidutils.generate_uuid()
self.policy_data = {
'policy': {'id': policy_id,
'project_id': self.project_id,
'name': 'test-policy',
'description': 'Test policy description',
'shared': True}}
self.rule_data = {
'bandwidth_limit_rule': {'id': uuidutils.generate_uuid(),
'max_kbps': 2000,
'max_burst_kbps': 150}}
self.ingress_rule_data = {
'bandwidth_limit_rule': {'id': uuidutils.generate_uuid(),
'max_kbps': 3000,
'max_burst_kbps': 350,
'direction': 'ingress'}}
self.dscp_rule_data = {
'dscp_marking_rule': {'id': uuidutils.generate_uuid(),
'dscp_mark': 22}}
self.policy = QoSPolicy(
self.ctxt, **self.policy_data['policy'])
# egress BW limit rule
self.rule = QosBandwidthLimitRule(
self.ctxt, **self.rule_data['bandwidth_limit_rule'])
# ingress bw limit rule
self.ingress_rule = QosBandwidthLimitRule(
self.ctxt, **self.ingress_rule_data['bandwidth_limit_rule'])
self.dscp_rule = QosDscpMarkingRule(
self.ctxt, **self.dscp_rule_data['dscp_marking_rule'])
self.fake_profile = {'id': policy_id}
mock.patch('neutron.objects.db.api.create_object').start()
mock.patch('neutron.objects.db.api.update_object').start()
mock.patch('neutron.objects.db.api.delete_object').start()
self.peak_bw_multiplier = cfg.CONF.NSX.qos_peak_bw_multiplier
self.nsxlib = v3_utils.get_nsxlib_wrapper()
@mock.patch.object(QoSPolicy, 'create_rbac_policy')
def test_policy_create_profile(self, *mocks):
# test the profile creation when a QoS policy is created
with mock.patch('vmware_nsxlib.v3.policy.core_resources.'
'NsxQosProfileApi.create_or_overwrite',
return_value=self.fake_profile) as create_profile,\
mock.patch.object(QoSPolicy, 'get_object',
return_value=self.policy),\
mock.patch.object(QoSPolicy, 'create'):
self.qos_plugin.create_policy(self.ctxt, self.policy_data)
expected_tags = self.nsxlib.build_v3_api_version_project_tag(
project_name=self.ctxt.tenant_name,
project_id=self.project_id)
exp_name = utils.get_name_and_uuid(self.policy.name,
self.policy.id)
create_profile.assert_called_once_with(
exp_name,
profile_id=self.policy.id,
description=self.policy_data["policy"]["description"],
dscp=None,
shaper_configurations=[],
tags=expected_tags)
@mock.patch.object(QoSPolicy, '_reload_rules')
def test_bw_rule_create_profile(self, *mocks):
# test the profile update when an egress QoS BW rule is created
_policy = QoSPolicy(
self.ctxt, **self.policy_data['policy'])
# add a rule to the policy
setattr(_policy, "rules", [self.rule])
with mock.patch.object(QoSPolicy, 'get_object', return_value=_policy),\
mock.patch('vmware_nsxlib.v3.policy.core_resources.'
'NsxQosProfileApi.'
'create_or_overwrite') as create_profile,\
mock.patch('neutron.objects.db.api.update_object',
return_value=self.rule_data):
self.qos_plugin.update_policy_bandwidth_limit_rule(
self.ctxt, self.rule.id, _policy.id, self.rule_data)
# validate the data on the profile
rule_dict = self.rule_data['bandwidth_limit_rule']
expected_bw = int(round(float(
rule_dict['max_kbps']) / 1024))
expected_burst = rule_dict['max_burst_kbps'] * 128
expected_peak = int(expected_bw * self.peak_bw_multiplier)
expected_tags = self.nsxlib.build_v3_api_version_project_tag(
project_name=self.ctxt.tenant_name,
project_id=self.project_id)
exp_name = utils.get_name_and_uuid(self.policy.name,
self.policy.id)
# egress neutron rule -> ingress nsx args
shaper_type = policy_defs.QoSRateLimiter.INGRESS_RATE_LIMITER_TYPE
expected_shaper = policy_defs.QoSRateLimiter(
resource_type=shaper_type,
enabled=True,
burst_size=expected_burst,
peak_bandwidth=expected_peak,
average_bandwidth=expected_bw)
create_profile.assert_called_once_with(
exp_name,
profile_id=self.policy.id,
description=self.policy_data["policy"]["description"],
dscp=None,
shaper_configurations=[mock.ANY],
tags=expected_tags)
# Compare the shaper
actual_shaper = create_profile.call_args[1][
'shaper_configurations'][0]
self.assertEqual(expected_shaper.get_obj_dict(),
actual_shaper.get_obj_dict())
@mock.patch.object(QoSPolicy, '_reload_rules')
def test_ingress_bw_rule_create_profile(self, *mocks):
# test the profile update when a ingress QoS BW rule is created
_policy = QoSPolicy(
self.ctxt, **self.policy_data['policy'])
# add a rule to the policy
setattr(_policy, "rules", [self.ingress_rule])
with mock.patch.object(QoSPolicy, 'get_object', return_value=_policy),\
mock.patch('vmware_nsxlib.v3.policy.core_resources.'
'NsxQosProfileApi.'
'create_or_overwrite') as create_profile,\
mock.patch('neutron.objects.db.api.update_object',
return_value=self.ingress_rule_data):
self.qos_plugin.update_policy_bandwidth_limit_rule(
self.ctxt, self.ingress_rule.id, _policy.id,
self.ingress_rule_data)
# validate the data on the profile
rule_dict = self.ingress_rule_data['bandwidth_limit_rule']
expected_bw = int(round(float(
rule_dict['max_kbps']) / 1024))
expected_burst = rule_dict['max_burst_kbps'] * 128
expected_peak = int(expected_bw * self.peak_bw_multiplier)
exp_name = utils.get_name_and_uuid(self.policy.name,
self.policy.id)
expected_tags = self.nsxlib.build_v3_api_version_project_tag(
project_name=self.ctxt.tenant_name,
project_id=self.project_id)
# ingress neutron rule -> egress nsx args
shaper_type = policy_defs.QoSRateLimiter.EGRESS_RATE_LIMITER_TYPE
expected_shaper = policy_defs.QoSRateLimiter(
resource_type=shaper_type,
enabled=True,
burst_size=expected_burst,
peak_bandwidth=expected_peak,
average_bandwidth=expected_bw)
create_profile.assert_called_once_with(
exp_name,
profile_id=self.policy.id,
description=self.policy_data["policy"]["description"],
dscp=None,
shaper_configurations=[mock.ANY],
tags=expected_tags)
# Compare the shaper
actual_shaper = create_profile.call_args[1][
'shaper_configurations'][0]
self.assertEqual(expected_shaper.get_obj_dict(),
actual_shaper.get_obj_dict())
@mock.patch.object(QoSPolicy, '_reload_rules')
def test_bw_rule_create_profile_minimal_val(self, *mocks):
# test driver precommit with an invalid limit value
bad_limit = qos_utils.MAX_KBPS_MIN_VALUE - 1
rule_data = {
'bandwidth_limit_rule': {'id': uuidutils.generate_uuid(),
'max_kbps': bad_limit,
'max_burst_kbps': 150}}
rule = QosBandwidthLimitRule(
self.ctxt, **rule_data['bandwidth_limit_rule'])
_policy = QoSPolicy(
self.ctxt, **self.policy_data['policy'])
# add a rule to the policy
setattr(_policy, "rules", [rule])
with mock.patch.object(QoSPolicy, 'get_object',
return_value=_policy),\
mock.patch('neutron.objects.db.api.update_object',
return_value=rule_data):
self.assertRaises(
exceptions.DriverCallError,
self.qos_plugin.update_policy_bandwidth_limit_rule,
self.ctxt, rule.id, _policy.id, rule_data)
@mock.patch.object(QoSPolicy, '_reload_rules')
def test_bw_rule_create_profile_maximal_val(self, *mocks):
# test driver precommit with an invalid burst value
bad_burst = qos_utils.MAX_BURST_MAX_VALUE + 1
rule_data = {
'bandwidth_limit_rule': {'id': uuidutils.generate_uuid(),
'max_kbps': 1025,
'max_burst_kbps': bad_burst}}
rule = QosBandwidthLimitRule(
self.ctxt, **rule_data['bandwidth_limit_rule'])
_policy = QoSPolicy(
self.ctxt, **self.policy_data['policy'])
# add a rule to the policy
setattr(_policy, "rules", [rule])
with mock.patch.object(QoSPolicy, 'get_object',
return_value=_policy),\
mock.patch('neutron.objects.db.api.update_object',
return_value=rule_data):
self.assertRaises(
exceptions.DriverCallError,
self.qos_plugin.update_policy_bandwidth_limit_rule,
self.ctxt, rule.id, _policy.id, rule_data)
@mock.patch.object(QoSPolicy, '_reload_rules')
def test_dscp_rule_create_profile(self, *mocks):
# test the profile update when a QoS DSCP rule is created
_policy = QoSPolicy(
self.ctxt, **self.policy_data['policy'])
# add a rule to the policy
setattr(_policy, "rules", [self.dscp_rule])
with mock.patch.object(QoSPolicy, 'get_object', return_value=_policy),\
mock.patch('vmware_nsxlib.v3.policy.core_resources.'
'NsxQosProfileApi.'
'create_or_overwrite') as create_profile,\
mock.patch('neutron.objects.db.api.update_object',
return_value=self.dscp_rule_data):
self.qos_plugin.update_policy_dscp_marking_rule(
self.ctxt, self.dscp_rule.id,
_policy.id, self.dscp_rule_data)
# validate the data on the profile
rule_dict = self.dscp_rule_data['dscp_marking_rule']
dscp_mark = rule_dict['dscp_mark']
exp_name = utils.get_name_and_uuid(self.policy.name,
self.policy.id)
expected_tags = self.nsxlib.build_v3_api_version_project_tag(
project_name=self.ctxt.tenant_name,
project_id=self.project_id)
expected_dscp = policy_defs.QoSDscp(
mode=policy_defs.QoSDscp.QOS_DSCP_UNTRUSTED,
priority=dscp_mark)
create_profile.assert_called_once_with(
exp_name,
profile_id=self.policy.id,
description=self.policy_data["policy"]["description"],
dscp=mock.ANY,
shaper_configurations=[],
tags=expected_tags)
# Compare the dscp obj
actual_dscp = create_profile.call_args[1]['dscp']
self.assertEqual(expected_dscp.get_obj_dict(),
actual_dscp.get_obj_dict())
@mock.patch.object(QoSPolicy, '_reload_rules')
def test_minimum_bw_rule_create_profile(self, *mocks):
# Minimum BW rules are not supported
policy = QoSPolicy(
self.ctxt, **self.policy_data['policy'])
min_bw_rule_data = {
'minimum_bandwidth_rule': {'id': uuidutils.generate_uuid(),
'min_kbps': 10,
'direction': 'egress'}}
min_bw_rule = QosMinimumBandwidthRule(
self.ctxt, **min_bw_rule_data['minimum_bandwidth_rule'])
# add a rule to the policy
setattr(policy, "rules", [min_bw_rule])
with mock.patch.object(
QoSPolicy, 'get_object', return_value=policy),\
mock.patch('neutron.objects.db.api.'
'update_object', return_value=self.dscp_rule_data):
self.assertRaises(
exceptions.DriverCallError,
self.qos_plugin.update_policy_minimum_bandwidth_rule,
self.ctxt, min_bw_rule.id,
policy.id, min_bw_rule_data)
def test_rule_delete_profile(self):
# test the profile update when a QoS rule is deleted
_policy = QoSPolicy(
self.ctxt, **self.policy_data['policy'])
# The mock will return the policy without the rule,
# as if it was deleted
with mock.patch.object(QoSPolicy, 'get_object', return_value=_policy),\
mock.patch('vmware_nsxlib.v3.policy.core_resources.'
'NsxQosProfileApi.'
'create_or_overwrite') as set_profile:
setattr(_policy, "rules", [self.rule])
self.qos_plugin.delete_policy_bandwidth_limit_rule(
self.ctxt, self.rule.id, self.policy.id)
# validate the data on the profile
expected_tags = self.nsxlib.build_v3_api_version_project_tag(
project_name=self.ctxt.tenant_name,
project_id=self.project_id)
exp_name = utils.get_name_and_uuid(self.policy.name,
self.policy.id)
set_profile.assert_called_once_with(
exp_name,
profile_id=self.policy.id,
description=self.policy_data["policy"]["description"],
dscp=None,
shaper_configurations=[],
tags=expected_tags)
@mock.patch('neutron.objects.db.api.get_object', return_value=None)
def test_policy_delete_profile(self, *mocks):
# test the profile deletion when a QoS policy is deleted
with mock.patch('vmware_nsxlib.v3.policy.core_resources.'
'NsxQosProfileApi.delete') as delete_profile:
self.qos_plugin.delete_policy(self.ctxt, self.policy.id)
delete_profile.assert_called_once_with(self.policy.id)