NSX|V3 add QoS support for networks

The user will be able to attach/detach QoS policies to internal
networks. This will affect newly created 'compute' ports, or ports
with a newly updated 'compute' device owner which will get the same
policy as their network, unless a specific policy was set to this port.
Updating the policy of a network will only affect new ports

Change-Id: I043c3002db0ee018f6556bf4db32a505f92240dd
This commit is contained in:
Adit Sarfaty 2016-04-10 12:24:41 +03:00
parent 58eeff88d2
commit eebeeec051
8 changed files with 161 additions and 56 deletions

View File

@ -62,6 +62,7 @@ from neutron.plugins.common import utils
from neutron.quota import resource_registry
from neutron.services.qos import qos_consts
from vmware_nsx.dvs import dvs
from vmware_nsx.services.qos.common import utils as qos_com_utils
from vmware_nsx.services.qos.nsx_v import utils as qos_utils
import vmware_nsx
@ -868,7 +869,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
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(
qos_com_utils.update_network_policy_binding(
context,
net_data['id'],
net_data[qos_consts.QOS_POLICY_ID])
@ -1035,7 +1036,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
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(
qos_com_utils.update_network_policy_binding(
context, id, net_attrs[qos_consts.QOS_POLICY_ID])
return net_res

View File

@ -83,6 +83,7 @@ from vmware_nsx.nsxlib.v3 import dfw_api as firewall
from vmware_nsx.nsxlib.v3 import resources as nsx_resources
from vmware_nsx.nsxlib.v3 import router
from vmware_nsx.nsxlib.v3 import security
from vmware_nsx.services.qos.common import utils as qos_com_utils
from vmware_nsx.services.qos.nsx_v3 import utils as qos_utils
@ -507,21 +508,18 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
network[pnet.PHYSICAL_NETWORK] = bindings[0].phy_uuid
network[pnet.SEGMENTATION_ID] = bindings[0].vlan_id
# NSX-V3 networks cannot be associated with QoS policies
def _validate_no_qos(self, net_data):
err_msg = None
def _assert_on_external_net_with_qos(self, net_data):
# Prevent creating/update external network with QoS policy
if attributes.is_attr_set(net_data.get(qos_consts.QOS_POLICY_ID)):
err_msg = _("Cannot configure QOS on networks")
if err_msg:
raise n_exc.InvalidInput(error_message=err_msg)
def create_network(self, context, network):
net_data = network['network']
self._validate_no_qos(net_data)
external = net_data.get(ext_net_extn.EXTERNAL)
is_backend_network = False
if attributes.is_attr_set(external) and external:
self._assert_on_external_net_with_qos(net_data)
is_provider_net, net_type, physical_net, vlan_id = (
self._validate_external_net_create(net_data))
else:
@ -581,6 +579,15 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# latest db model for the extension functions
net_model = self._get_network(context, created_net['id'])
self._apply_dict_extend_functions('networks', created_net, net_model)
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,
created_net['id'],
net_data[qos_consts.QOS_POLICY_ID])
return created_net
def _retry_delete_network(self, context, network_id):
@ -655,9 +662,11 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
def update_network(self, context, id, network):
original_net = super(NsxV3Plugin, self).get_network(context, id)
net_data = network['network']
self._validate_no_qos(net_data)
# Neutron does not support changing provider network values
pnet._raise_if_updates_provider_attributes(net_data)
extern_net = self._network_is_external(context, id)
if extern_net:
self._assert_on_external_net_with_qos(net_data)
updated_net = super(NsxV3Plugin, self).update_network(context, id,
network)
@ -667,7 +676,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._process_l3_update(context, updated_net, network['network'])
self._extend_network_dict_provider(context, updated_net)
if (not self._network_is_external(context, id) and
if (not extern_net and
'name' in net_data or 'admin_state_up' in net_data):
try:
# get the nsx switch id from the DB mapping
@ -687,6 +696,12 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
super(NsxV3Plugin, self).update_network(
context, id, {'network': original_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, id, net_data[qos_consts.QOS_POLICY_ID])
return updated_net
def create_subnet(self, context, subnet):
@ -866,6 +881,11 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
qos_policy_id = None
if attributes.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_utils.get_network_policy_id(
context, port_data['network_id'])
if qos_policy_id:
qos_profile_id = self._get_qos_profile_id(context, qos_policy_id)
profiles.append(qos_profile_id)
@ -892,9 +912,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# Attach the policy to the port in the neutron DB
if qos_policy_id:
qos_utils.update_port_policy_binding(context,
port_data['id'],
qos_policy_id)
qos_com_utils.update_port_policy_binding(context,
port_data['id'],
qos_policy_id)
return result
def _validate_address_pairs(self, address_pairs):
@ -949,7 +969,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
if lport_id:
self._port_client.delete(lport_id)
def _assert_on_external_net_with_qos(self, port_data):
def _assert_on_external_net_port_with_qos(self, port_data):
# Prevent creating/update port with QoS policy
# on external networks.
if attributes.is_attr_set(port_data.get(qos_consts.QOS_POLICY_ID)):
@ -968,7 +988,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
context, port_data['network_id'])
if is_external_net:
self._assert_on_external_net_with_compute(port_data)
self._assert_on_external_net_with_qos(port_data)
self._assert_on_external_net_port_with_qos(port_data)
neutron_db = super(NsxV3Plugin, self).create_port(context, port)
port["port"].update(neutron_db)
@ -1184,8 +1204,14 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
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)
updated_port,
is_new_compute)
if qos_profile_id is not None:
switch_profile_ids.append(qos_profile_id)
@ -1208,11 +1234,11 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
raise nsx_exc.NsxPluginException(err_msg=msg)
# Attach/Detach the QoS policies to the port in the neutron DB
qos_utils.update_port_policy_binding(context,
updated_port['id'],
qos_policy_id)
qos_com_utils.update_port_policy_binding(context,
updated_port['id'],
qos_policy_id)
def _get_port_qos_ids(self, context, updated_port):
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
profile_id = None
@ -1222,6 +1248,14 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# Look for the previous QoS policy
policy_id = qos_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_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)
@ -1238,7 +1272,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
context, original_port['network_id'])
if is_external_net:
self._assert_on_external_net_with_compute(port['port'])
self._assert_on_external_net_with_qos(port['port'])
self._assert_on_external_net_port_with_qos(port['port'])
updated_port = super(NsxV3Plugin, self).update_port(context,
id, port)

View File

@ -0,0 +1,51 @@
# 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.objects.qos import policy as qos_policy
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:
if old_policy.id == new_policy_id:
return
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 update_port_policy_binding(context, port_id, new_policy_id):
# detach the old policy (if exists) from the port
old_policy = qos_policy.QosPolicy.get_port_policy(
context, port_id)
if old_policy:
if old_policy.id == new_policy_id:
return
old_policy.detach_port(port_id)
# attach the new policy (if exists) to the port
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_port(port_id)

View File

@ -71,21 +71,6 @@ class NsxVQosRule(object):
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

View File

@ -30,29 +30,18 @@ LOG = logging.getLogger(__name__)
MAX_KBPS_MIN_VALUE = 1024
def update_port_policy_binding(context, port_id, new_policy_id):
# detach the old policy (if exists) from the port
old_policy = qos_policy.QosPolicy.get_port_policy(
context, port_id)
if old_policy:
if old_policy.id == new_policy_id:
return
old_policy.detach_port(port_id)
# attach the new policy (if exists) to the port
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_port(port_id)
def get_port_policy_id(context, port_id):
policy = qos_policy.QosPolicy.get_port_policy(
context, port_id)
if policy:
return policy.id
return
def get_network_policy_id(context, net_id):
policy = qos_policy.QosPolicy.get_network_policy(
context, net_id)
if policy:
return policy.id
def handle_qos_notification(policy_obj, event_type):

View File

@ -310,6 +310,50 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
self.assertRaises(n_exc.InvalidInput,
self.plugin.create_port, self.ctx, data)
def test_create_port_with_qos_on_net(self):
with self.network() as network:
policy_id = uuidutils.generate_uuid()
device_owner = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'X'
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': device_owner,
'fixed_ips': [],
'mac_address': '00:00:00:00:00:01'}
}
with mock.patch.object(self.plugin,
'_get_qos_profile_id') as get_profile:
with mock.patch('vmware_nsx.services.qos.nsx_v3.utils.'
'get_network_policy_id', return_value=policy_id):
self.plugin.create_port(self.ctx, data)
get_profile.assert_called_once_with(self.ctx, policy_id)
def test_update_port_with_qos_on_net(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()
device_owner = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'X'
data['port']['device_owner'] = device_owner
with mock.patch.object(self.plugin,
'_get_qos_profile_id') as get_profile:
with mock.patch('vmware_nsx.services.qos.nsx_v3.utils.'
'get_network_policy_id', return_value=policy_id):
self.plugin.update_port(self.ctx, port['id'], data)
get_profile.assert_called_once_with(self.ctx, policy_id)
class DHCPOptsTestCase(test_dhcpopts.TestExtraDhcpOpt,
NsxV3PluginTestCaseMixin):

View File

@ -26,6 +26,7 @@ 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.common import utils as qos_com_utils
from vmware_nsx.services.qos.nsx_v import utils as qos_utils
from vmware_nsx.tests.unit.nsx_v import test_plugin
@ -107,7 +108,7 @@ class TestQosNsxVNotification(test_plugin.NsxVPluginV2TestCase,
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(qos_com_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,
@ -133,7 +134,7 @@ class TestQosNsxVNotification(test_plugin.NsxVPluginV2TestCase,
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(qos_com_utils, 'update_network_policy_binding'):
with mock.patch.object(dvs.DvsManager,
'update_port_groups_config') as dvs_mock: