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:
parent
58eeff88d2
commit
eebeeec051
@ -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
|
||||
|
@ -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)
|
||||
|
0
vmware_nsx/services/qos/common/__init__.py
Normal file
0
vmware_nsx/services/qos/common/__init__.py
Normal file
51
vmware_nsx/services/qos/common/utils.py
Normal file
51
vmware_nsx/services/qos/common/utils.py
Normal 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)
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user