NSX|P: QoS support
Change-Id: I719c1adfa94676b5e8b3a7b60f8d9d034d54eeb3
This commit is contained in:
parent
17759dba87
commit
74f3831027
@ -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))
|
||||
|
@ -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
|
||||
-------
|
||||
|
||||
|
@ -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:
|
||||
raise nsx_exc.QoSOnExternalNet()
|
||||
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)):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
160
vmware_nsx/services/qos/nsx_v3/pol_utils.py
Normal file
160
vmware_nsx/services/qos/nsx_v3/pol_utils.py
Normal 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)
|
@ -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):
|
||||
|
@ -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')
|
||||
|
||||
|
365
vmware_nsx/tests/unit/services/qos/test_nsxp_notification.py
Normal file
365
vmware_nsx/tests/unit/services/qos/test_nsxp_notification.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user