NSX|V add qos support for networks
Add support for the qos service in NSX|V, including: - Attach/Detach qos policy to a new or updated network - Allow qos configuration on a backend network only, and only if use_dvs_features is True - Update the bw limitations on the edge through the dvs - Update the networks bw limitations when a policy or rule changes through the QoS notification driver Change-Id: Icee25b59e8e0f3c1c093077b631250a908e127c1
This commit is contained in:
parent
42cc581c01
commit
24a84004ff
@ -34,7 +34,7 @@ neutron.core_plugins =
|
|||||||
vmware = vmware_nsx.plugin:NsxMhPlugin
|
vmware = vmware_nsx.plugin:NsxMhPlugin
|
||||||
neutron.service_plugins =
|
neutron.service_plugins =
|
||||||
vmware_nsx_l2gw = vmware_nsx.services.l2gateway.common.plugin:NsxL2GatewayPlugin
|
vmware_nsx_l2gw = vmware_nsx.services.l2gateway.common.plugin:NsxL2GatewayPlugin
|
||||||
vmware_nsx_qos = vmware_nsx.services.qos.plugin:NsxQosPlugin
|
vmware_nsxv_qos = vmware_nsx.services.qos.nsx_v.plugin:NsxVQosPlugin
|
||||||
vmware_nsx.neutron.nsxv.router_type_drivers =
|
vmware_nsx.neutron.nsxv.router_type_drivers =
|
||||||
shared = vmware_nsx.plugins.nsx_v.drivers.shared_router_driver:RouterSharedDriver
|
shared = vmware_nsx.plugins.nsx_v.drivers.shared_router_driver:RouterSharedDriver
|
||||||
distributed = vmware_nsx.plugins.nsx_v.drivers.distributed_router_driver:RouterDistributedDriver
|
distributed = vmware_nsx.plugins.nsx_v.drivers.distributed_router_driver:RouterDistributedDriver
|
||||||
|
@ -22,6 +22,7 @@ from vmware_nsx.common import exceptions as nsx_exc
|
|||||||
from vmware_nsx.dvs import dvs_utils
|
from vmware_nsx.dvs import dvs_utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
PORTGROUP_PREFIX = 'dvportgroup'
|
||||||
|
|
||||||
|
|
||||||
class DvsManager(object):
|
class DvsManager(object):
|
||||||
@ -118,6 +119,128 @@ class DvsManager(object):
|
|||||||
return val
|
return val
|
||||||
raise exceptions.NetworkNotFound(net_id=net_id)
|
raise exceptions.NetworkNotFound(net_id=net_id)
|
||||||
|
|
||||||
|
def _is_vlan_network_by_moref(self, moref):
|
||||||
|
"""
|
||||||
|
This can either be a VXLAN or a VLAN network. The type is determined
|
||||||
|
by the prefix of the moref.
|
||||||
|
"""
|
||||||
|
return moref.startswith(PORTGROUP_PREFIX)
|
||||||
|
|
||||||
|
def _copy_port_group_spec(self, orig_spec):
|
||||||
|
client_factory = self._session.vim.client.factory
|
||||||
|
pg_spec = client_factory.create('ns0:DVPortgroupConfigSpec')
|
||||||
|
pg_spec.autoExpand = orig_spec['autoExpand']
|
||||||
|
pg_spec.configVersion = orig_spec['configVersion']
|
||||||
|
pg_spec.defaultPortConfig = orig_spec['defaultPortConfig']
|
||||||
|
pg_spec.name = orig_spec['name']
|
||||||
|
pg_spec.numPorts = orig_spec['numPorts']
|
||||||
|
pg_spec.policy = orig_spec['policy']
|
||||||
|
pg_spec.type = orig_spec['type']
|
||||||
|
return pg_spec
|
||||||
|
|
||||||
|
def update_port_group_spec_qos(self, pg_spec, qos_data):
|
||||||
|
outPol = pg_spec.defaultPortConfig.outShapingPolicy
|
||||||
|
if qos_data.enabled:
|
||||||
|
outPol.inherited = False
|
||||||
|
outPol.enabled.inherited = False
|
||||||
|
outPol.enabled.value = True
|
||||||
|
outPol.averageBandwidth.inherited = False
|
||||||
|
outPol.averageBandwidth.value = qos_data.averageBandwidth
|
||||||
|
outPol.peakBandwidth.inherited = False
|
||||||
|
outPol.peakBandwidth.value = qos_data.peakBandwidth
|
||||||
|
outPol.burstSize.inherited = False
|
||||||
|
outPol.burstSize.value = qos_data.burstSize
|
||||||
|
else:
|
||||||
|
outPol.inherited = True
|
||||||
|
|
||||||
|
def _reconfigure_port_group(self, pg_moref, spec_update_calback,
|
||||||
|
spec_update_data):
|
||||||
|
# Get the current configuration of the port group
|
||||||
|
pg_spec = self._session.invoke_api(vim_util,
|
||||||
|
'get_object_properties',
|
||||||
|
self._session.vim,
|
||||||
|
pg_moref, ['config'])
|
||||||
|
if len(pg_spec) == 0 or len(pg_spec[0].propSet[0]) == 0:
|
||||||
|
LOG.error(_LE('Failed to get object properties of %s'), pg_moref)
|
||||||
|
raise nsx_exc.DvsNotFound(dvs=pg_moref)
|
||||||
|
|
||||||
|
# Convert the extracted config to DVPortgroupConfigSpec
|
||||||
|
new_spec = self._copy_port_group_spec(pg_spec[0].propSet[0].val)
|
||||||
|
|
||||||
|
# Update the configuration using the callback & data
|
||||||
|
spec_update_calback(new_spec, spec_update_data)
|
||||||
|
|
||||||
|
# Update the port group configuration
|
||||||
|
task = self._session.invoke_api(self._session.vim,
|
||||||
|
'ReconfigureDVPortgroup_Task',
|
||||||
|
pg_moref, spec=new_spec)
|
||||||
|
try:
|
||||||
|
self._session.wait_for_task(task)
|
||||||
|
except Exception:
|
||||||
|
LOG.error(_LE('Failed to reconfigure DVPortGroup %s'), pg_moref)
|
||||||
|
raise nsx_exc.DvsNotFound(dvs=pg_moref)
|
||||||
|
|
||||||
|
# Update the dvs port groups config for a vxlan/vlan network
|
||||||
|
# update the spec using a callback and user data
|
||||||
|
def update_port_groups_config(self, net_id, net_moref,
|
||||||
|
spec_update_calback, spec_update_data):
|
||||||
|
is_vlan = self._is_vlan_network_by_moref(net_moref)
|
||||||
|
if is_vlan:
|
||||||
|
return self._update_net_port_groups_config(net_moref,
|
||||||
|
spec_update_calback,
|
||||||
|
spec_update_data)
|
||||||
|
else:
|
||||||
|
return self._update_vxlan_port_groups_config(net_id,
|
||||||
|
net_moref,
|
||||||
|
spec_update_calback,
|
||||||
|
spec_update_data)
|
||||||
|
|
||||||
|
# Update the dvs port groups config for a vxlan network
|
||||||
|
# Searching the port groups for a partial match to the network id & moref
|
||||||
|
# update the spec using a callback and user data
|
||||||
|
def _update_vxlan_port_groups_config(self,
|
||||||
|
net_id,
|
||||||
|
net_moref,
|
||||||
|
spec_update_calback,
|
||||||
|
spec_update_data):
|
||||||
|
port_groups = self._session.invoke_api(vim_util,
|
||||||
|
'get_object_properties',
|
||||||
|
self._session.vim,
|
||||||
|
self._dvs_moref,
|
||||||
|
['portgroup'])
|
||||||
|
found = False
|
||||||
|
if len(port_groups) and hasattr(port_groups[0], 'propSet'):
|
||||||
|
for prop in port_groups[0].propSet:
|
||||||
|
for pg_moref in prop.val[0]:
|
||||||
|
props = self._session.invoke_api(vim_util,
|
||||||
|
'get_object_properties',
|
||||||
|
self._session.vim,
|
||||||
|
pg_moref, ['name'])
|
||||||
|
if len(props) and hasattr(props[0], 'propSet'):
|
||||||
|
for prop in props[0].propSet:
|
||||||
|
if net_id in prop.val and net_moref in prop.val:
|
||||||
|
found = True
|
||||||
|
self._reconfigure_port_group(
|
||||||
|
pg_moref,
|
||||||
|
spec_update_calback,
|
||||||
|
spec_update_data)
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
raise exceptions.NetworkNotFound(net_id=net_id)
|
||||||
|
|
||||||
|
# Update the dvs port groups config for a vlan network
|
||||||
|
# Finding the port group using the exact moref of the network
|
||||||
|
# update the spec using a callback and user data
|
||||||
|
def _update_net_port_groups_config(self,
|
||||||
|
net_moref,
|
||||||
|
spec_update_calback,
|
||||||
|
spec_update_data):
|
||||||
|
pg_moref = vim_util.get_moref(net_moref,
|
||||||
|
"DistributedVirtualPortgroup")
|
||||||
|
self._reconfigure_port_group(pg_moref,
|
||||||
|
spec_update_calback,
|
||||||
|
spec_update_data)
|
||||||
|
|
||||||
def delete_port_group(self, net_id):
|
def delete_port_group(self, net_id):
|
||||||
"""Delete a specific port group."""
|
"""Delete a specific port group."""
|
||||||
moref = self._net_id_to_moref(net_id)
|
moref = self._net_id_to_moref(net_id)
|
||||||
|
@ -27,10 +27,14 @@ from oslo_utils import uuidutils
|
|||||||
from sqlalchemy.orm import exc as sa_exc
|
from sqlalchemy.orm import exc as sa_exc
|
||||||
|
|
||||||
from neutron.api import extensions as neutron_extensions
|
from neutron.api import extensions as neutron_extensions
|
||||||
|
from neutron.api.rpc.callbacks.consumer import registry as callbacks_registry
|
||||||
|
from neutron.api.rpc.callbacks import resources as callbacks_resources
|
||||||
|
from neutron.api.rpc.handlers import resources_rpc
|
||||||
from neutron.api.v2 import attributes as attr
|
from neutron.api.v2 import attributes as attr
|
||||||
from neutron.callbacks import events
|
from neutron.callbacks import events
|
||||||
from neutron.callbacks import registry
|
from neutron.callbacks import registry
|
||||||
from neutron.callbacks import resources
|
from neutron.callbacks import resources
|
||||||
|
from neutron.common import rpc as n_rpc
|
||||||
from neutron import context as n_context
|
from neutron import context as n_context
|
||||||
from neutron.db import agents_db
|
from neutron.db import agents_db
|
||||||
from neutron.db import allowedaddresspairs_db as addr_pair_db
|
from neutron.db import allowedaddresspairs_db as addr_pair_db
|
||||||
@ -56,7 +60,9 @@ from neutron.extensions import securitygroup as ext_sg
|
|||||||
from neutron.plugins.common import constants as plugin_const
|
from neutron.plugins.common import constants as plugin_const
|
||||||
from neutron.plugins.common import utils
|
from neutron.plugins.common import utils
|
||||||
from neutron.quota import resource_registry
|
from neutron.quota import resource_registry
|
||||||
|
from neutron.services.qos import qos_consts
|
||||||
from vmware_nsx.dvs import dvs
|
from vmware_nsx.dvs import dvs
|
||||||
|
from vmware_nsx.services.qos.nsx_v import utils as qos_utils
|
||||||
|
|
||||||
import vmware_nsx
|
import vmware_nsx
|
||||||
from vmware_nsx._i18n import _, _LE, _LI, _LW
|
from vmware_nsx._i18n import _, _LE, _LI, _LW
|
||||||
@ -183,6 +189,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
else:
|
else:
|
||||||
self._dvs = None
|
self._dvs = None
|
||||||
|
|
||||||
|
# Bind QoS notifications
|
||||||
|
callbacks_registry.subscribe(self._handle_qos_notification,
|
||||||
|
callbacks_resources.QOS_POLICY)
|
||||||
|
self._start_rpc_listeners()
|
||||||
|
|
||||||
has_metadata_cfg = (
|
has_metadata_cfg = (
|
||||||
cfg.CONF.nsxv.nova_metadata_ips
|
cfg.CONF.nsxv.nova_metadata_ips
|
||||||
and cfg.CONF.nsxv.mgt_net_moid
|
and cfg.CONF.nsxv.mgt_net_moid
|
||||||
@ -192,6 +203,16 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
self.metadata_proxy_handler = (
|
self.metadata_proxy_handler = (
|
||||||
nsx_v_md_proxy.NsxVMetadataProxyHandler(self))
|
nsx_v_md_proxy.NsxVMetadataProxyHandler(self))
|
||||||
|
|
||||||
|
def _start_rpc_listeners(self):
|
||||||
|
self.conn = n_rpc.create_connection()
|
||||||
|
qos_topic = resources_rpc.resource_type_versioned_topic(
|
||||||
|
callbacks_resources.QOS_POLICY)
|
||||||
|
self.conn.create_consumer(
|
||||||
|
qos_topic, [resources_rpc.ResourcesPushRpcCallback()],
|
||||||
|
fanout=False)
|
||||||
|
|
||||||
|
return self.conn.consume_in_threads()
|
||||||
|
|
||||||
def _create_security_group_container(self):
|
def _create_security_group_container(self):
|
||||||
name = "OpenStack Security Group container"
|
name = "OpenStack Security Group container"
|
||||||
with locking.LockManager.get_lock('security-group-container-init'):
|
with locking.LockManager.get_lock('security-group-container-init'):
|
||||||
@ -342,6 +363,18 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
context, neutron_port_db['id'], network_id,
|
context, neutron_port_db['id'], network_id,
|
||||||
neutron_port_db['mac_address'])
|
neutron_port_db['mac_address'])
|
||||||
|
|
||||||
|
def _validate_network_qos(self, network, backend_network):
|
||||||
|
err_msg = None
|
||||||
|
if attr.is_attr_set(network.get(qos_consts.QOS_POLICY_ID)):
|
||||||
|
if not backend_network:
|
||||||
|
err_msg = (_("Cannot configure QOS on external networks"))
|
||||||
|
if not cfg.CONF.nsxv.use_dvs_features:
|
||||||
|
err_msg = (_("Cannot configure QOS "
|
||||||
|
"without enabling use_dvs_features"))
|
||||||
|
|
||||||
|
if err_msg:
|
||||||
|
raise n_exc.InvalidInput(error_message=err_msg)
|
||||||
|
|
||||||
def _validate_provider_create(self, context, network):
|
def _validate_provider_create(self, context, network):
|
||||||
if not attr.is_attr_set(network.get(mpnet.SEGMENTS)):
|
if not attr.is_attr_set(network.get(mpnet.SEGMENTS)):
|
||||||
return
|
return
|
||||||
@ -678,6 +711,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
external = net_data.get(ext_net_extn.EXTERNAL)
|
external = net_data.get(ext_net_extn.EXTERNAL)
|
||||||
backend_network = (not attr.is_attr_set(external) or
|
backend_network = (not attr.is_attr_set(external) or
|
||||||
attr.is_attr_set(external) and not external)
|
attr.is_attr_set(external) and not external)
|
||||||
|
self._validate_network_qos(net_data, backend_network)
|
||||||
|
|
||||||
if backend_network:
|
if backend_network:
|
||||||
network_type = None
|
network_type = None
|
||||||
#NOTE(abhiraut): Consider refactoring code below to have more
|
#NOTE(abhiraut): Consider refactoring code below to have more
|
||||||
@ -704,9 +739,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
h, c = self.nsx_v.vcns.create_virtual_wire(vdn_scope_id,
|
h, c = self.nsx_v.vcns.create_virtual_wire(vdn_scope_id,
|
||||||
config_spec)
|
config_spec)
|
||||||
net_morefs = [c]
|
net_morefs = [c]
|
||||||
|
dvs_net_ids = [net_data['id']]
|
||||||
elif network_type == c_utils.NsxVNetworkTypes.PORTGROUP:
|
elif network_type == c_utils.NsxVNetworkTypes.PORTGROUP:
|
||||||
segment = net_data[mpnet.SEGMENTS][0]
|
segment = net_data[mpnet.SEGMENTS][0]
|
||||||
net_morefs = [segment.get(pnet.PHYSICAL_NETWORK)]
|
net_morefs = [segment.get(pnet.PHYSICAL_NETWORK)]
|
||||||
|
dvs_net_ids = [net_data['name']]
|
||||||
else:
|
else:
|
||||||
segment = net_data[mpnet.SEGMENTS][0]
|
segment = net_data[mpnet.SEGMENTS][0]
|
||||||
physical_network = segment.get(pnet.PHYSICAL_NETWORK)
|
physical_network = segment.get(pnet.PHYSICAL_NETWORK)
|
||||||
@ -714,6 +751,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
# If physical_network attr is not set, retrieve a list
|
# If physical_network attr is not set, retrieve a list
|
||||||
# consisting of a single dvs-id pre-configured in nsx.ini
|
# consisting of a single dvs-id pre-configured in nsx.ini
|
||||||
dvs_ids = self._get_dvs_ids(physical_network)
|
dvs_ids = self._get_dvs_ids(physical_network)
|
||||||
|
dvs_net_ids = []
|
||||||
# Save the list of netmorefs from the backend
|
# Save the list of netmorefs from the backend
|
||||||
net_morefs = []
|
net_morefs = []
|
||||||
dvs_pg_mappings = {}
|
dvs_pg_mappings = {}
|
||||||
@ -731,6 +769,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
self._delete_backend_network(net_moref)
|
self._delete_backend_network(net_moref)
|
||||||
dvs_pg_mappings[dvs_id] = net_moref
|
dvs_pg_mappings[dvs_id] = net_moref
|
||||||
net_morefs.append(net_moref)
|
net_morefs.append(net_moref)
|
||||||
|
dvs_net_ids.append(self._get_vlan_network_name(
|
||||||
|
net_data, dvs_id))
|
||||||
try:
|
try:
|
||||||
# Create SpoofGuard policy for network anti-spoofing
|
# Create SpoofGuard policy for network anti-spoofing
|
||||||
if cfg.CONF.nsxv.spoofguard_enabled and backend_network:
|
if cfg.CONF.nsxv.spoofguard_enabled and backend_network:
|
||||||
@ -801,12 +841,36 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
self._delete_backend_network(net_moref)
|
self._delete_backend_network(net_moref)
|
||||||
LOG.exception(_LE('Failed to create network'))
|
LOG.exception(_LE('Failed to create network'))
|
||||||
|
|
||||||
|
if backend_network:
|
||||||
|
# Update the QOS restrictions of the backend network
|
||||||
|
self._update_network_qos(context, net_data, dvs_net_ids, net_moref)
|
||||||
|
|
||||||
# this extra lookup is necessary to get the
|
# this extra lookup is necessary to get the
|
||||||
# latest db model for the extension functions
|
# latest db model for the extension functions
|
||||||
net_model = self._get_network(context, new_net['id'])
|
net_model = self._get_network(context, new_net['id'])
|
||||||
self._apply_dict_extend_functions('networks', new_net, net_model)
|
self._apply_dict_extend_functions('networks', new_net, net_model)
|
||||||
return new_net
|
return new_net
|
||||||
|
|
||||||
|
def _update_network_qos(self, context, net_data, dvs_net_ids, net_moref):
|
||||||
|
if attr.is_attr_set(net_data.get(qos_consts.QOS_POLICY_ID)):
|
||||||
|
# Translate the QoS rule data into Nsx values
|
||||||
|
qos_data = qos_utils.NsxVQosRule(
|
||||||
|
context=context,
|
||||||
|
qos_policy_id=net_data[qos_consts.QOS_POLICY_ID])
|
||||||
|
|
||||||
|
# update the qos data on the dvs
|
||||||
|
for dvs_net_id in dvs_net_ids:
|
||||||
|
self._dvs.update_port_groups_config(
|
||||||
|
dvs_net_id,
|
||||||
|
net_moref,
|
||||||
|
self._dvs.update_port_group_spec_qos, qos_data)
|
||||||
|
|
||||||
|
# attach the policy to the network in the neutron DB
|
||||||
|
qos_utils.update_network_policy_binding(
|
||||||
|
context,
|
||||||
|
net_data['id'],
|
||||||
|
net_data[qos_consts.QOS_POLICY_ID])
|
||||||
|
|
||||||
def _cleanup_dhcp_edge_before_deletion(self, context, net_id):
|
def _cleanup_dhcp_edge_before_deletion(self, context, net_id):
|
||||||
if self.metadata_proxy_handler:
|
if self.metadata_proxy_handler:
|
||||||
# Find if this is the last network which is bound
|
# Find if this is the last network which is bound
|
||||||
@ -914,6 +978,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
if net_attrs.get("admin_state_up") is False:
|
if net_attrs.get("admin_state_up") is False:
|
||||||
raise NotImplementedError(_("admin_state_up=False networks "
|
raise NotImplementedError(_("admin_state_up=False networks "
|
||||||
"are not supported."))
|
"are not supported."))
|
||||||
|
net_morefs = nsx_db.get_nsx_switch_ids(context.session, id)
|
||||||
|
backend_network = True if len(net_morefs) > 0 else False
|
||||||
|
self._validate_network_qos(net_attrs, backend_network)
|
||||||
|
|
||||||
# PortSecurity validation checks
|
# PortSecurity validation checks
|
||||||
# TODO(roeyc): enacapsulate validation in a method
|
# TODO(roeyc): enacapsulate validation in a method
|
||||||
@ -938,7 +1005,6 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
# old state
|
# old state
|
||||||
if cfg.CONF.nsxv.spoofguard_enabled and psec_update:
|
if cfg.CONF.nsxv.spoofguard_enabled and psec_update:
|
||||||
policy_id = nsxv_db.get_spoofguard_policy_id(context.session, id)
|
policy_id = nsxv_db.get_spoofguard_policy_id(context.session, id)
|
||||||
net_morefs = nsx_db.get_nsx_switch_ids(context.session, id)
|
|
||||||
try:
|
try:
|
||||||
self.nsx_v.vcns.update_spoofguard_policy(
|
self.nsx_v.vcns.update_spoofguard_policy(
|
||||||
policy_id, net_morefs, id,
|
policy_id, net_morefs, id,
|
||||||
@ -951,6 +1017,25 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
context, revert_update, net_res)
|
context, revert_update, net_res)
|
||||||
super(NsxVPluginV2, self).update_network(
|
super(NsxVPluginV2, self).update_network(
|
||||||
context, id, {'network': revert_update})
|
context, id, {'network': revert_update})
|
||||||
|
|
||||||
|
# Handle QOS updates (Value can be None, meaning to delete the
|
||||||
|
# current policy)
|
||||||
|
if qos_consts.QOS_POLICY_ID in net_attrs:
|
||||||
|
# update the qos data
|
||||||
|
qos_data = qos_utils.NsxVQosRule(
|
||||||
|
context=context,
|
||||||
|
qos_policy_id=net_attrs[qos_consts.QOS_POLICY_ID])
|
||||||
|
|
||||||
|
# get the network moref/s from the db
|
||||||
|
for moref in net_morefs:
|
||||||
|
# update the qos restrictions of the network
|
||||||
|
self._dvs.update_port_groups_config(
|
||||||
|
id, moref, self._dvs.update_port_group_spec_qos, qos_data)
|
||||||
|
|
||||||
|
# attach the policy to the network in neutron DB
|
||||||
|
qos_utils.update_network_policy_binding(
|
||||||
|
context, id, net_attrs[qos_consts.QOS_POLICY_ID])
|
||||||
|
|
||||||
return net_res
|
return net_res
|
||||||
|
|
||||||
def _validate_address_pairs(self, attrs, db_port):
|
def _validate_address_pairs(self, attrs, db_port):
|
||||||
@ -2495,6 +2580,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
error = _("Configured %s not found") % field
|
error = _("Configured %s not found") % field
|
||||||
raise nsx_exc.NsxPluginException(err_msg=error)
|
raise nsx_exc.NsxPluginException(err_msg=error)
|
||||||
|
|
||||||
|
def _handle_qos_notification(self, qos_policy, event_type):
|
||||||
|
qos_utils.handle_qos_notification(qos_policy, event_type, self._dvs)
|
||||||
|
|
||||||
|
|
||||||
# Register the callback
|
# Register the callback
|
||||||
def _validate_network_has_subnet(resource, event, trigger, **kwargs):
|
def _validate_network_has_subnet(resource, event, trigger, **kwargs):
|
||||||
|
0
vmware_nsx/services/qos/nsx_v/__init__.py
Normal file
0
vmware_nsx/services/qos/nsx_v/__init__.py
Normal file
41
vmware_nsx/services/qos/nsx_v/plugin.py
Normal file
41
vmware_nsx/services/qos/nsx_v/plugin.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from neutron.services.qos import qos_plugin
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from vmware_nsx._i18n import _, _LI
|
||||||
|
from vmware_nsx.common import exceptions as nsx_exc
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class NsxVQosPlugin(qos_plugin.QoSPlugin):
|
||||||
|
|
||||||
|
"""Service plugin for VMware NSX-v to implement Neutron's Qos API."""
|
||||||
|
|
||||||
|
supported_extension_aliases = ["qos"]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
LOG.info(_LI("Loading VMware NSX-V Qos Service Plugin"))
|
||||||
|
super(NsxVQosPlugin, self).__init__()
|
||||||
|
|
||||||
|
if not cfg.CONF.nsxv.use_dvs_features:
|
||||||
|
error = _("Cannot use the NSX-V QoS plugin without "
|
||||||
|
"enabling the dvs features")
|
||||||
|
raise nsx_exc.NsxPluginException(err_msg=error)
|
113
vmware_nsx/services/qos/nsx_v/utils.py
Normal file
113
vmware_nsx/services/qos/nsx_v/utils.py
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from neutron.api.rpc.callbacks import events as callbacks_events
|
||||||
|
from neutron import context as n_context
|
||||||
|
from neutron import manager
|
||||||
|
from neutron.objects.qos import policy as qos_policy
|
||||||
|
from neutron.plugins.common import constants
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from vmware_nsx.db import db as nsx_db
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class NsxVQosRule(object):
|
||||||
|
|
||||||
|
def __init__(self, context=None, qos_policy_id=None):
|
||||||
|
super(NsxVQosRule, self).__init__()
|
||||||
|
|
||||||
|
# Data structure to hold the NSX-V representation
|
||||||
|
# of the neutron qos rule.
|
||||||
|
self._qos_plugin = None
|
||||||
|
self.enabled = False
|
||||||
|
self.averageBandwidth = 0
|
||||||
|
self.peakBandwidth = 0
|
||||||
|
self.burstSize = 0
|
||||||
|
|
||||||
|
if qos_policy_id is not None:
|
||||||
|
self._init_from_policy_id(context, qos_policy_id)
|
||||||
|
|
||||||
|
def _get_qos_plugin(self):
|
||||||
|
if not self._qos_plugin:
|
||||||
|
loaded_plugins = manager.NeutronManager.get_service_plugins()
|
||||||
|
self._qos_plugin = loaded_plugins[constants.QOS]
|
||||||
|
return self._qos_plugin
|
||||||
|
|
||||||
|
# init the nsx_v qos data (outShapingPolicy) from a neutron qos policy
|
||||||
|
def _init_from_policy_id(self, context, qos_policy_id):
|
||||||
|
self.enabled = False
|
||||||
|
# read the neutron policy restrictions
|
||||||
|
if qos_policy_id is not None:
|
||||||
|
# read the QOS rule from DB
|
||||||
|
plugin = self._get_qos_plugin()
|
||||||
|
rules_obj = plugin.get_policy_bandwidth_limit_rules(
|
||||||
|
context, qos_policy_id)
|
||||||
|
if rules_obj is not None and len(rules_obj) > 0:
|
||||||
|
rule_obj = rules_obj[0]
|
||||||
|
self.enabled = True
|
||||||
|
# averageBandwidth: kbps (neutron) -> bps (nsxv)
|
||||||
|
self.averageBandwidth = rule_obj['max_kbps'] * 1024
|
||||||
|
# peakBandwidth: the same as the average value because the
|
||||||
|
# neutron qos configuration supports only 1 value
|
||||||
|
self.peakBandwidth = self.averageBandwidth
|
||||||
|
# burstSize: kbps (neutron) -> Bytes (nsxv)
|
||||||
|
self.burstSize = rule_obj['max_burst_kbps'] * 128
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
def update_network_policy_binding(context, net_id, new_policy_id):
|
||||||
|
# detach the old policy (if exists) from the network
|
||||||
|
old_policy = qos_policy.QosPolicy.get_network_policy(
|
||||||
|
context, net_id)
|
||||||
|
if old_policy:
|
||||||
|
old_policy.detach_network(net_id)
|
||||||
|
|
||||||
|
# attach the new policy (if exists) to the network
|
||||||
|
if new_policy_id is not None:
|
||||||
|
new_policy = qos_policy.QosPolicy.get_object(
|
||||||
|
context, id=new_policy_id)
|
||||||
|
if new_policy:
|
||||||
|
new_policy.attach_network(net_id)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_qos_notification(policy_obj, event_type, dvs):
|
||||||
|
# Check if QoS policy rule was created/deleted/updated
|
||||||
|
# Only if the policy rule was updated, we need to update the dvs
|
||||||
|
if (event_type == callbacks_events.UPDATED and
|
||||||
|
hasattr(policy_obj, "rules")):
|
||||||
|
|
||||||
|
# Reload the policy as admin so we will have a context
|
||||||
|
context = n_context.get_admin_context()
|
||||||
|
admin_policy = qos_policy.QosPolicy.get_object(
|
||||||
|
context, id=policy_obj.id)
|
||||||
|
# get all the bound networks of this policy
|
||||||
|
networks = admin_policy.get_bound_networks()
|
||||||
|
qos_rule = NsxVQosRule(context=context,
|
||||||
|
qos_policy_id=policy_obj.id)
|
||||||
|
|
||||||
|
for net_id in networks:
|
||||||
|
# update the new bw limitations for this network
|
||||||
|
net_morefs = nsx_db.get_nsx_switch_ids(context.session, net_id)
|
||||||
|
for moref in net_morefs:
|
||||||
|
# update the qos restrictions of the network
|
||||||
|
dvs.update_port_groups_config(
|
||||||
|
net_id,
|
||||||
|
moref,
|
||||||
|
dvs.update_port_group_spec_qos,
|
||||||
|
qos_rule)
|
0
vmware_nsx/services/qos/nsx_v3/__init__.py
Normal file
0
vmware_nsx/services/qos/nsx_v3/__init__.py
Normal file
@ -28,14 +28,14 @@ from vmware_nsx.nsxlib import v3 as nsxlib
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NsxQosPlugin(qos_plugin.QoSPlugin):
|
class NsxV3QosPlugin(qos_plugin.QoSPlugin):
|
||||||
|
|
||||||
"""Service plugin for VMware NSX to implement Neutron's Qos API."""
|
"""Service plugin for VMware NSX-v3 to implement Neutron's Qos API."""
|
||||||
|
|
||||||
supported_extension_aliases = ["qos"]
|
supported_extension_aliases = ["qos"]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(NsxQosPlugin, self).__init__()
|
super(NsxV3QosPlugin, self).__init__()
|
||||||
LOG.info(_LI("Loading VMware Qos Service Plugin"))
|
LOG.info(_LI("Loading VMware Qos Service Plugin"))
|
||||||
|
|
||||||
@db_base_plugin_common.convert_result_to_dict
|
@db_base_plugin_common.convert_result_to_dict
|
@ -101,6 +101,40 @@ class DvsTestCase(base.BaseTestCase):
|
|||||||
'fake-uuid')
|
'fake-uuid')
|
||||||
fake_get_moref.assert_called_once_with('fake-uuid')
|
fake_get_moref.assert_called_once_with('fake-uuid')
|
||||||
|
|
||||||
|
@mock.patch.object(dvs.DvsManager, '_update_vxlan_port_groups_config')
|
||||||
|
@mock.patch.object(dvs.DvsManager, '_get_port_group_spec',
|
||||||
|
return_value='fake-spec')
|
||||||
|
@mock.patch.object(dvs.DvsManager, '_net_id_to_moref',
|
||||||
|
return_value='fake-moref')
|
||||||
|
def test_update_vxlan_net_group_conf(self, fake_get_moref,
|
||||||
|
fake_get_spec, fake_update_vxlan):
|
||||||
|
net_id = 'vxlan-uuid'
|
||||||
|
vlan = 7
|
||||||
|
self._dvs.add_port_group(net_id, vlan)
|
||||||
|
moref = self._dvs._net_id_to_moref(net_id)
|
||||||
|
fake_get_moref.assert_called_once_with(net_id)
|
||||||
|
fake_get_spec.assert_called_once_with(net_id, vlan)
|
||||||
|
|
||||||
|
self._dvs.update_port_groups_config(net_id, moref, None, None)
|
||||||
|
fake_update_vxlan.assert_called_once_with(net_id, moref, None, None)
|
||||||
|
|
||||||
|
@mock.patch.object(dvs.DvsManager, '_update_net_port_groups_config')
|
||||||
|
@mock.patch.object(dvs.DvsManager, '_get_port_group_spec',
|
||||||
|
return_value='fake-spec')
|
||||||
|
@mock.patch.object(dvs.DvsManager, '_net_id_to_moref',
|
||||||
|
return_value='dvportgroup-fake-moref')
|
||||||
|
def test_update_flat_net_conf(self, fake_get_moref,
|
||||||
|
fake_get_spec, fake_update_net):
|
||||||
|
net_id = 'flat-uuid'
|
||||||
|
vlan = 7
|
||||||
|
self._dvs.add_port_group(net_id, vlan)
|
||||||
|
moref = self._dvs._net_id_to_moref(net_id)
|
||||||
|
fake_get_moref.assert_called_once_with(net_id)
|
||||||
|
fake_get_spec.assert_called_once_with(net_id, vlan)
|
||||||
|
|
||||||
|
self._dvs.update_port_groups_config(net_id, moref, None, None)
|
||||||
|
fake_update_net.assert_called_once_with(moref, None, None)
|
||||||
|
|
||||||
|
|
||||||
class NeutronSimpleDvsTest(test_plugin.NeutronDbPluginV2TestCase):
|
class NeutronSimpleDvsTest(test_plugin.NeutronDbPluginV2TestCase):
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import contextlib
|
|||||||
from eventlet import greenthread
|
from eventlet import greenthread
|
||||||
import mock
|
import mock
|
||||||
import netaddr
|
import netaddr
|
||||||
|
from neutron.api.rpc.callbacks import events as callbacks_events
|
||||||
from neutron.api.v2 import attributes
|
from neutron.api.v2 import attributes
|
||||||
from neutron import context
|
from neutron import context
|
||||||
from neutron.extensions import dvr as dist_router
|
from neutron.extensions import dvr as dist_router
|
||||||
@ -28,6 +29,7 @@ from neutron.extensions import portsecurity as psec
|
|||||||
from neutron.extensions import providernet as pnet
|
from neutron.extensions import providernet as pnet
|
||||||
from neutron.extensions import securitygroup as secgrp
|
from neutron.extensions import securitygroup as secgrp
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
|
from neutron.objects.qos import policy as qos_pol
|
||||||
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
||||||
import neutron.tests.unit.db.test_allowedaddresspairs_db as test_addr_pair
|
import neutron.tests.unit.db.test_allowedaddresspairs_db as test_addr_pair
|
||||||
import neutron.tests.unit.db.test_db_base_plugin_v2 as test_plugin
|
import neutron.tests.unit.db.test_db_base_plugin_v2 as test_plugin
|
||||||
@ -48,6 +50,8 @@ from vmware_nsx._i18n import _
|
|||||||
from vmware_nsx.common import exceptions as nsxv_exc
|
from vmware_nsx.common import exceptions as nsxv_exc
|
||||||
from vmware_nsx.common import nsx_constants
|
from vmware_nsx.common import nsx_constants
|
||||||
from vmware_nsx.db import nsxv_db
|
from vmware_nsx.db import nsxv_db
|
||||||
|
from vmware_nsx.dvs import dvs
|
||||||
|
from vmware_nsx.dvs import dvs_utils
|
||||||
from vmware_nsx.extensions import routersize as router_size
|
from vmware_nsx.extensions import routersize as router_size
|
||||||
from vmware_nsx.extensions import routertype as router_type
|
from vmware_nsx.extensions import routertype as router_type
|
||||||
from vmware_nsx.extensions import securitygrouplogging
|
from vmware_nsx.extensions import securitygrouplogging
|
||||||
@ -56,6 +60,7 @@ from vmware_nsx.plugins.nsx_v.drivers import (
|
|||||||
shared_router_driver as router_driver)
|
shared_router_driver as router_driver)
|
||||||
from vmware_nsx.plugins.nsx_v.vshield.common import constants as vcns_const
|
from vmware_nsx.plugins.nsx_v.vshield.common import constants as vcns_const
|
||||||
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
|
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
|
||||||
|
from vmware_nsx.services.qos.nsx_v import utils as qos_utils
|
||||||
from vmware_nsx.tests import unit as vmware
|
from vmware_nsx.tests import unit as vmware
|
||||||
from vmware_nsx.tests.unit.extensions import test_vnic_index
|
from vmware_nsx.tests.unit.extensions import test_vnic_index
|
||||||
from vmware_nsx.tests.unit.nsx_v.vshield import fake_vcns
|
from vmware_nsx.tests.unit.nsx_v.vshield import fake_vcns
|
||||||
@ -459,6 +464,132 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase):
|
|||||||
context.get_admin_context(),
|
context.get_admin_context(),
|
||||||
data)
|
data)
|
||||||
|
|
||||||
|
def test_create_network_with_qos_no_dvs_fail(self):
|
||||||
|
# network creation should fail if the qos policy parameter exists,
|
||||||
|
# and no use_dvs_features configured
|
||||||
|
data = {'network': {
|
||||||
|
'name': 'test-qos',
|
||||||
|
'tenant_id': self._tenant_id,
|
||||||
|
'qos_policy_id': _uuid()}}
|
||||||
|
plugin = manager.NeutronManager.get_plugin()
|
||||||
|
self.assertRaises(n_exc.InvalidInput,
|
||||||
|
plugin.create_network,
|
||||||
|
context.get_admin_context(),
|
||||||
|
data)
|
||||||
|
|
||||||
|
def test_update_network_with_qos_no_dvs_fail(self):
|
||||||
|
# network update should fail if the qos policy parameter exists,
|
||||||
|
# and no use_dvs_features configured
|
||||||
|
data = {'network': {'qos_policy_id': _uuid()}}
|
||||||
|
with self.network() as net:
|
||||||
|
plugin = manager.NeutronManager.get_plugin()
|
||||||
|
self.assertRaises(n_exc.InvalidInput,
|
||||||
|
plugin.update_network,
|
||||||
|
context.get_admin_context(),
|
||||||
|
net['network']['id'], data)
|
||||||
|
|
||||||
|
def _get_core_plugin_with_dvs(self):
|
||||||
|
# enable dvs features to allow policy with QOS
|
||||||
|
cfg.CONF.set_default('use_dvs_features', True, 'nsxv')
|
||||||
|
plugin = manager.NeutronManager.get_plugin()
|
||||||
|
with mock.patch.object(dvs_utils, 'dvs_create_session'):
|
||||||
|
with mock.patch.object(dvs.DvsManager, '_get_dvs_moref'):
|
||||||
|
plugin._dvs = dvs.DvsManager()
|
||||||
|
return plugin
|
||||||
|
|
||||||
|
@mock.patch.object(dvs.DvsManager, 'update_port_groups_config')
|
||||||
|
@mock.patch.object(qos_utils.NsxVQosRule, '_init_from_policy_id')
|
||||||
|
def test_create_network_with_qos_policy(self,
|
||||||
|
fake_init_from_policy,
|
||||||
|
fake_dvs_update):
|
||||||
|
# enable dvs features to allow policy with QOS
|
||||||
|
plugin = self._get_core_plugin_with_dvs()
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
|
||||||
|
# fake policy id
|
||||||
|
policy_id = _uuid()
|
||||||
|
data = {'network': {
|
||||||
|
'name': 'test-qos',
|
||||||
|
'tenant_id': self._tenant_id,
|
||||||
|
'qos_policy_id': policy_id,
|
||||||
|
'port_security_enabled': False,
|
||||||
|
'admin_state_up': False,
|
||||||
|
'shared': False
|
||||||
|
}}
|
||||||
|
# create the network - should succeed and translate the policy id
|
||||||
|
plugin.create_network(ctx, data)
|
||||||
|
fake_init_from_policy.assert_called_once_with(ctx, policy_id)
|
||||||
|
self.assertTrue(fake_dvs_update.called)
|
||||||
|
|
||||||
|
@mock.patch.object(dvs.DvsManager, 'update_port_groups_config')
|
||||||
|
@mock.patch.object(qos_utils.NsxVQosRule, '_init_from_policy_id')
|
||||||
|
def test_update_network_with_qos_policy(self,
|
||||||
|
fake_init_from_policy,
|
||||||
|
fake_dvs_update):
|
||||||
|
# enable dvs features to allow policy with QOS
|
||||||
|
plugin = self._get_core_plugin_with_dvs()
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
|
||||||
|
# create the network without qos policy
|
||||||
|
data = {'network': {
|
||||||
|
'name': 'test-qos',
|
||||||
|
'tenant_id': self._tenant_id,
|
||||||
|
'port_security_enabled': False,
|
||||||
|
'admin_state_up': True,
|
||||||
|
'shared': False
|
||||||
|
}}
|
||||||
|
net = plugin.create_network(ctx, data)
|
||||||
|
|
||||||
|
# fake policy id
|
||||||
|
policy_id = _uuid()
|
||||||
|
data['network']['qos_policy_id'] = policy_id
|
||||||
|
# update the network - should succeed and translate the policy id
|
||||||
|
plugin.update_network(ctx, net['id'], data)
|
||||||
|
fake_init_from_policy.assert_called_once_with(ctx, policy_id)
|
||||||
|
self.assertTrue(fake_dvs_update.called)
|
||||||
|
|
||||||
|
@mock.patch.object(dvs.DvsManager, 'update_port_groups_config')
|
||||||
|
@mock.patch.object(qos_utils.NsxVQosRule, '_init_from_policy_id')
|
||||||
|
def test_network_with_updated_qos_policy(self,
|
||||||
|
fake_init_from_policy,
|
||||||
|
fake_dvs_update):
|
||||||
|
# enable dvs features to allow policy with QOS
|
||||||
|
plugin = self._get_core_plugin_with_dvs()
|
||||||
|
|
||||||
|
ctx = context.get_admin_context()
|
||||||
|
|
||||||
|
# create the network with qos policy
|
||||||
|
policy_id = _uuid()
|
||||||
|
data = {'network': {
|
||||||
|
'name': 'test-qos',
|
||||||
|
'tenant_id': self._tenant_id,
|
||||||
|
'qos_policy_id': policy_id,
|
||||||
|
'port_security_enabled': False,
|
||||||
|
'admin_state_up': True,
|
||||||
|
'shared': False
|
||||||
|
}}
|
||||||
|
net = plugin.create_network(ctx, data)
|
||||||
|
|
||||||
|
# reset fake methods called flag
|
||||||
|
fake_init_from_policy.called = False
|
||||||
|
fake_dvs_update.called = False
|
||||||
|
|
||||||
|
# fake QoS policy obj:
|
||||||
|
fake_policy = qos_pol.QosPolicy()
|
||||||
|
fake_policy.id = policy_id
|
||||||
|
fake_policy.rules = []
|
||||||
|
|
||||||
|
# call the plugin notification callback as if the network was updated
|
||||||
|
with mock.patch.object(qos_pol.QosPolicy, "get_object",
|
||||||
|
return_value=fake_policy):
|
||||||
|
with mock.patch.object(qos_pol.QosPolicy, "get_bound_networks",
|
||||||
|
return_value=[net["id"]]):
|
||||||
|
plugin._handle_qos_notification(fake_policy,
|
||||||
|
callbacks_events.UPDATED)
|
||||||
|
# make sure the policy data was read, and the dvs was updated
|
||||||
|
self.assertTrue(fake_init_from_policy.called)
|
||||||
|
self.assertTrue(fake_dvs_update.called)
|
||||||
|
|
||||||
|
|
||||||
class TestVnicIndex(NsxVPluginV2TestCase,
|
class TestVnicIndex(NsxVPluginV2TestCase,
|
||||||
test_vnic_index.VnicIndexDbTestCase):
|
test_vnic_index.VnicIndexDbTestCase):
|
||||||
@ -2058,7 +2189,6 @@ class L3NatTestCaseBase(test_l3_plugin.L3NatTestCaseMixin):
|
|||||||
|
|
||||||
def test_router_add_interface_ipv6_port_existing_network_returns_400(self):
|
def test_router_add_interface_ipv6_port_existing_network_returns_400(self):
|
||||||
"""Ensure unique IPv6 router ports per network id.
|
"""Ensure unique IPv6 router ports per network id.
|
||||||
|
|
||||||
Adding a router port containing one or more IPv6 subnets with the same
|
Adding a router port containing one or more IPv6 subnets with the same
|
||||||
network id as an existing router port should fail. This is so
|
network id as an existing router port should fail. This is so
|
||||||
there is no ambiguity regarding on which port to add an IPv6 subnet
|
there is no ambiguity regarding on which port to add an IPv6 subnet
|
||||||
|
0
vmware_nsx/tests/unit/services/qos/__init__.py
Normal file
0
vmware_nsx/tests/unit/services/qos/__init__.py
Normal file
39
vmware_nsx/tests/unit/services/qos/fake_nsxv_notifier.py
Normal file
39
vmware_nsx/tests/unit/services/qos/fake_nsxv_notifier.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# 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.api.rpc.callbacks import events
|
||||||
|
from neutron.services.qos.notification_drivers import message_queue
|
||||||
|
|
||||||
|
from vmware_nsx.services.qos.nsx_v import utils as qos_utils
|
||||||
|
|
||||||
|
|
||||||
|
class DummyNsxVNotificationDriver(
|
||||||
|
message_queue.RpcQosServiceNotificationDriver):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(DummyNsxVNotificationDriver, self).__init__()
|
||||||
|
self._dvs = mock.Mock()
|
||||||
|
|
||||||
|
def create_policy(self, context, policy):
|
||||||
|
# there is no notification for newly created policy
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update_policy(self, context, policy):
|
||||||
|
qos_utils.handle_qos_notification(policy, events.UPDATED, self._dvs)
|
||||||
|
|
||||||
|
def delete_policy(self, context, policy):
|
||||||
|
qos_utils.handle_qos_notification(policy, events.DELETED, self._dvs)
|
190
vmware_nsx/tests/unit/services/qos/test_nsxv_notification.py
Normal file
190
vmware_nsx/tests/unit/services/qos/test_nsxv_notification.py
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
# 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 oslo_config import cfg
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
|
from neutron import context
|
||||||
|
from neutron import manager
|
||||||
|
from neutron.objects.qos import policy as policy_object
|
||||||
|
from neutron.objects.qos import rule as rule_object
|
||||||
|
from neutron.services.qos import qos_plugin
|
||||||
|
from neutron.tests.unit.services.qos import base
|
||||||
|
|
||||||
|
from vmware_nsx.dvs import dvs
|
||||||
|
from vmware_nsx.dvs import dvs_utils
|
||||||
|
from vmware_nsx.services.qos.nsx_v import utils as qos_utils
|
||||||
|
from vmware_nsx.tests.unit.nsx_v import test_plugin
|
||||||
|
|
||||||
|
CORE_PLUGIN = "vmware_nsx.plugins.nsx_v.plugin.NsxVPluginV2"
|
||||||
|
|
||||||
|
|
||||||
|
class TestQosNsxVNotification(test_plugin.NsxVPluginV2TestCase,
|
||||||
|
base.BaseQosTestCase):
|
||||||
|
|
||||||
|
@mock.patch.object(dvs_utils, 'dvs_create_session')
|
||||||
|
@mock.patch.object(dvs.DvsManager, '_get_dvs_moref')
|
||||||
|
def setUp(self, *mocks):
|
||||||
|
# init the nsx-v plugin for testing with DVS
|
||||||
|
self._init_dvs_config()
|
||||||
|
super(TestQosNsxVNotification, self).setUp(plugin=CORE_PLUGIN,
|
||||||
|
ext_mgr=None)
|
||||||
|
plugin_instance = manager.NeutronManager.get_plugin()
|
||||||
|
self._core_plugin = plugin_instance
|
||||||
|
|
||||||
|
# Setup the QoS plugin:
|
||||||
|
# Add a dummy notification driver that calls our handler directly
|
||||||
|
# (to skip the message queue)
|
||||||
|
cfg.CONF.set_override(
|
||||||
|
"notification_drivers",
|
||||||
|
['vmware_nsx.tests.unit.services.qos.fake_nsxv_notifier.'
|
||||||
|
'DummyNsxVNotificationDriver'],
|
||||||
|
"qos")
|
||||||
|
self.qos_plugin = qos_plugin.QoSPlugin()
|
||||||
|
mock.patch.object(qos_utils.NsxVQosRule,
|
||||||
|
'_get_qos_plugin',
|
||||||
|
return_value=self.qos_plugin).start()
|
||||||
|
|
||||||
|
# Pre defined QoS data for the tests
|
||||||
|
self.ctxt = context.Context('fake_user', 'fake_tenant')
|
||||||
|
self.policy_data = {
|
||||||
|
'policy': {'id': uuidutils.generate_uuid(),
|
||||||
|
'tenant_id': uuidutils.generate_uuid(),
|
||||||
|
'name': 'test-policy',
|
||||||
|
'description': 'Test policy description',
|
||||||
|
'shared': True}}
|
||||||
|
|
||||||
|
self.rule_data = {
|
||||||
|
'bandwidth_limit_rule': {'id': uuidutils.generate_uuid(),
|
||||||
|
'max_kbps': 100,
|
||||||
|
'max_burst_kbps': 150}}
|
||||||
|
|
||||||
|
self.policy = policy_object.QosPolicy(
|
||||||
|
self.ctxt, **self.policy_data['policy'])
|
||||||
|
|
||||||
|
self.rule = rule_object.QosBandwidthLimitRule(
|
||||||
|
self.ctxt, **self.rule_data['bandwidth_limit_rule'])
|
||||||
|
|
||||||
|
self._net_data = {'network': {
|
||||||
|
'name': 'test-qos',
|
||||||
|
'tenant_id': 'fake_tenant',
|
||||||
|
'qos_policy_id': self.policy.id,
|
||||||
|
'port_security_enabled': False,
|
||||||
|
'admin_state_up': False,
|
||||||
|
'shared': False
|
||||||
|
}}
|
||||||
|
self._rules = [self.rule_data['bandwidth_limit_rule']]
|
||||||
|
|
||||||
|
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()
|
||||||
|
mock.patch('neutron.objects.db.api.get_object').start()
|
||||||
|
mock.patch(
|
||||||
|
'neutron.objects.qos.policy.QosPolicy.obj_load_attr').start()
|
||||||
|
|
||||||
|
def _init_dvs_config(self):
|
||||||
|
# Ensure that DVS is enabled
|
||||||
|
# and enable the DVS features for nsxv qos support
|
||||||
|
cfg.CONF.set_override('host_ip', 'fake_ip', group='dvs')
|
||||||
|
cfg.CONF.set_override('host_username', 'fake_user', group='dvs')
|
||||||
|
cfg.CONF.set_override('host_password', 'fake_password', group='dvs')
|
||||||
|
cfg.CONF.set_override('dvs_name', 'fake_dvs', group='dvs')
|
||||||
|
cfg.CONF.set_default('use_dvs_features', True, 'nsxv')
|
||||||
|
|
||||||
|
def _create_net(self):
|
||||||
|
return self._core_plugin.create_network(self.ctxt, self._net_data)
|
||||||
|
|
||||||
|
@mock.patch.object(qos_utils, 'update_network_policy_binding')
|
||||||
|
@mock.patch.object(dvs.DvsManager, 'update_port_groups_config')
|
||||||
|
def test_create_network_with_policy_rule(self,
|
||||||
|
dvs_update_mock,
|
||||||
|
update_bindings_mock):
|
||||||
|
"""Test the DVS update when a QoS rule is attached to a network"""
|
||||||
|
# Create a policy with a rule
|
||||||
|
_policy = policy_object.QosPolicy(
|
||||||
|
self.ctxt, **self.policy_data['policy'])
|
||||||
|
setattr(_policy, "rules", [self.rule])
|
||||||
|
|
||||||
|
with mock.patch('neutron.services.qos.qos_plugin.QoSPlugin.'
|
||||||
|
'get_policy_bandwidth_limit_rules',
|
||||||
|
return_value=self._rules) as get_rules_mock:
|
||||||
|
# create the network to use this policy
|
||||||
|
net = self._create_net()
|
||||||
|
|
||||||
|
# make sure the network-policy binding was updated
|
||||||
|
update_bindings_mock.assert_called_once_with(
|
||||||
|
self.ctxt, net['id'], self.policy.id)
|
||||||
|
# make sure the qos rule was found
|
||||||
|
get_rules_mock.assert_called_with(self.ctxt, self.policy.id)
|
||||||
|
# make sure the dvs was updated
|
||||||
|
self.assertTrue(dvs_update_mock.called)
|
||||||
|
|
||||||
|
def _test_rule_action_notification(self, action):
|
||||||
|
with mock.patch.object(qos_utils, 'update_network_policy_binding'):
|
||||||
|
with mock.patch.object(dvs.DvsManager,
|
||||||
|
'update_port_groups_config') as dvs_mock:
|
||||||
|
|
||||||
|
# Create a policy with a rule
|
||||||
|
_policy = policy_object.QosPolicy(
|
||||||
|
self.ctxt, **self.policy_data['policy'])
|
||||||
|
|
||||||
|
# set the rule in the policy data
|
||||||
|
if action != 'create':
|
||||||
|
setattr(_policy, "rules", [self.rule])
|
||||||
|
|
||||||
|
with mock.patch('neutron.services.qos.qos_plugin.QoSPlugin.'
|
||||||
|
'get_policy_bandwidth_limit_rules',
|
||||||
|
return_value=self._rules) as get_rules_mock:
|
||||||
|
with mock.patch('neutron.objects.qos.policy.'
|
||||||
|
'QosPolicy.get_object',
|
||||||
|
return_value=_policy):
|
||||||
|
# create the network to use this policy
|
||||||
|
self._create_net()
|
||||||
|
|
||||||
|
# create/update/delete the rule
|
||||||
|
if action == 'create':
|
||||||
|
self.qos_plugin.create_policy_bandwidth_limit_rule(
|
||||||
|
self.ctxt, self.policy.id, self.rule_data)
|
||||||
|
elif action == 'update':
|
||||||
|
self.qos_plugin.update_policy_bandwidth_limit_rule(
|
||||||
|
self.ctxt, self.rule.id,
|
||||||
|
self.policy.id, self.rule_data)
|
||||||
|
else:
|
||||||
|
self.qos_plugin.delete_policy_bandwidth_limit_rule(
|
||||||
|
self.ctxt, self.rule.id, self.policy.id)
|
||||||
|
|
||||||
|
# make sure the qos rule was found
|
||||||
|
self.assertTrue(get_rules_mock.called)
|
||||||
|
# make sure the dvs was updated
|
||||||
|
self.assertTrue(dvs_mock.called)
|
||||||
|
|
||||||
|
def test_create_rule_notification(self):
|
||||||
|
"""Test the DVS update when a QoS rule, attached to a network,
|
||||||
|
is created
|
||||||
|
"""
|
||||||
|
self._test_rule_action_notification('create')
|
||||||
|
|
||||||
|
def test_update_rule_notification(self):
|
||||||
|
"""Test the DVS update when a QoS rule, attached to a network,
|
||||||
|
is modified
|
||||||
|
"""
|
||||||
|
self._test_rule_action_notification('update')
|
||||||
|
|
||||||
|
def test_delete_rule_notification(self):
|
||||||
|
"""Test the DVS update when a QoS rule, attached to a network,
|
||||||
|
is deleted
|
||||||
|
"""
|
||||||
|
self._test_rule_action_notification('delete')
|
Loading…
Reference in New Issue
Block a user