Merge "Update placement allocation of bound ports when network QoS policy is changed"

This commit is contained in:
Zuul 2021-12-16 20:13:26 +00:00 committed by Gerrit Code Review
commit b737f4a86e
2 changed files with 214 additions and 2 deletions

View File

@ -165,6 +165,10 @@ class QoSPlugin(qos.QoSPluginBase):
self._validate_create_network_callback,
callbacks_resources.NETWORK,
callbacks_events.PRECOMMIT_CREATE)
callbacks_registry.subscribe(
self._check_network_for_placement_allocation_change,
callbacks_resources.NETWORK,
callbacks_events.AFTER_UPDATE)
@staticmethod
@resource_extend.extends([port_def.COLLECTION_NAME])
@ -650,6 +654,39 @@ class QoSPlugin(qos.QoSPluginBase):
context.elevated(), id=policy_id)
self.validate_policy_for_network(context, policy, network_id)
def _check_network_for_placement_allocation_change(self, resource, event,
trigger, payload=None):
context = payload.context
original_network, updated_network = payload.states
original_policy_id = original_network.get(qos_consts.QOS_POLICY_ID)
policy_id = updated_network.get(qos_consts.QOS_POLICY_ID)
if policy_id == original_policy_id:
return
original_policy = policy_object.QosPolicy.get_object(
context.elevated(), id=original_policy_id)
policy = policy_object.QosPolicy.get_object(
context.elevated(), id=policy_id)
ports = ports_object.Port.get_objects(
context, network_id=updated_network['id'])
# Filter compute bound ports without overwritten QoS policy
ports = [port for port in ports if (port.qos_policy_id is None and
nl_constants.DEVICE_OWNER_COMPUTE_PREFIX in port['device_owner'])]
for port in ports:
# Use _make_port_dict() to load extension data
port_dict = trigger._make_port_dict(port)
updated_port_attrs = {}
self._change_placement_allocation(
original_policy, policy, port_dict, updated_port_attrs)
for port_binding in port.bindings:
port_binding.profile = updated_port_attrs.get(
'binding:profile', {})
port_binding.update()
def _validate_update_network_callback(self, resource, event, trigger,
payload=None):
context = payload.context

View File

@ -2039,12 +2039,14 @@ class TestQosPluginDB(base.BaseQosTestCase):
qos_rule.create()
return qos_rule
def _make_port(self, network_id, qos_policy_id=None, port_id=None):
def _make_port(self, network_id, qos_policy_id=None, port_id=None,
device_owner=None):
port_id = port_id if port_id else uuidutils.generate_uuid()
base_mac = ['aa', 'bb', 'cc', 'dd', 'ee', 'ff']
mac = netaddr.EUI(next(net_utils.random_mac_generator(base_mac)))
device_owner = device_owner if device_owner else '3'
port = ports_object.Port(
self.context, network_id=network_id, device_owner='3',
self.context, network_id=network_id, device_owner=device_owner,
project_id=self.project_id, admin_state_up=True, status='DOWN',
device_id='2', qos_policy_id=qos_policy_id, mac_address=mac,
id=port_id)
@ -2218,6 +2220,179 @@ class TestQosPluginDB(base.BaseQosTestCase):
context, states=(original_port, port)))
mock_alloc_change.assert_not_called()
def test_check_network_for_placement_allocation_change_no_qos_change(self):
qos1 = self._make_qos_policy()
original_network = self._make_network(qos1.id)
network = original_network
ml2plugin_mock = mock.MagicMock()
with mock.patch.object(
self.qos_plugin,
'_change_placement_allocation') as mock_alloc_change:
self.qos_plugin._check_network_for_placement_allocation_change(
'network', 'after_update', ml2plugin_mock,
payload=events.DBEventPayload(
self.context, states=(original_network, network)))
mock_alloc_change.assert_not_called()
ml2plugin_mock._make_port_dict.assert_not_called()
def test_check_network_for_placement_allocation_change_no_ports_to_update(
self):
original_qos = self._make_qos_policy()
qos = self._make_qos_policy()
port_qos = self._make_qos_policy()
original_network = self._make_network(original_qos.id)
network = self._make_network(qos.id)
# Port which is not compute bound
self._make_port(network_id=network.id, qos_policy_id=None,
device_owner='uu:id')
# Port with overwritten QoS policy
self._make_port(network_id=network.id, qos_policy_id=port_qos.id,
device_owner='compute:uu:id')
ml2plugin_mock = mock.MagicMock()
with mock.patch.object(
self.qos_plugin,
'_change_placement_allocation') as mock_alloc_change:
self.qos_plugin._check_network_for_placement_allocation_change(
'network', 'after_update', ml2plugin_mock,
payload=events.DBEventPayload(
self.context, states=(original_network, network)))
mock_alloc_change.assert_not_called()
ml2plugin_mock._make_port_dict.assert_not_called()
def test_check_network_for_placement_allocation_change_remove_qos(self):
original_qos = self._make_qos_policy()
original_network = self._make_network(original_qos.id)
network = self._make_network()
ml2plugin_mock = mock.MagicMock()
def fake_make_port_dict(port):
return {
'id': port.id,
'device_owner': port.device_owner,
'qos_policy_id': port.qos_policy_id,
'qos_network_policy_id': port.qos_network_policy_id,
}
ml2plugin_mock._make_port_dict.side_effect = fake_make_port_dict
port1 = self._make_port(
network_id=network.id, qos_policy_id=None,
device_owner='compute:uu:id')
port1_binding = ports_object.PortBinding(
self.context, port_id=port1.id, host='fake_host1',
vnic_type='fake_vnic_type', vif_type='fake_vif_type',
profile={'allocation': 'fake_allocation'})
port1_binding.create()
port1.bindings = [port1_binding]
port1.update()
with mock.patch.object(
self.qos_plugin,
'_change_placement_allocation') as mock_alloc_change:
def fake_change_placement_allocation(orig_policy, policy,
orig_port, port):
port['binding:profile'] = {}
mock_alloc_change.side_effect = fake_change_placement_allocation
self.qos_plugin._check_network_for_placement_allocation_change(
'network', 'after_update', ml2plugin_mock,
payload=events.DBEventPayload(
self.context, states=(original_network, network)))
self.assertEqual(ml2plugin_mock._make_port_dict.call_count, 1)
mock_alloc_change_calls = [
mock.call(
original_qos,
None,
{'id': port1.id,
'device_owner': 'compute:uu:id',
'qos_policy_id': None,
'qos_network_policy_id': None},
mock.ANY),
]
mock_alloc_change.assert_has_calls(mock_alloc_change_calls,
any_order=True)
port1.update()
self.assertDictEqual(port1.bindings[0].profile, {})
def test_check_network_for_placement_allocation_change(self):
original_qos = self._make_qos_policy()
qos = self._make_qos_policy()
original_network = self._make_network(original_qos.id)
network = self._make_network(qos.id)
ml2plugin_mock = mock.MagicMock()
def fake_make_port_dict(port):
return {
'id': port.id,
'device_owner': port.device_owner,
'qos_policy_id': port.qos_policy_id,
'qos_network_policy_id': port.qos_network_policy_id,
}
ml2plugin_mock._make_port_dict.side_effect = fake_make_port_dict
port1 = self._make_port(
network_id=network.id, qos_policy_id=None,
device_owner='compute:uu:id')
port1_binding = ports_object.PortBinding(
self.context, port_id=port1.id, host='fake_host1',
vnic_type='fake_vnic_type', vif_type='fake_vif_type', profile={})
port1_binding.create()
port1.bindings = [port1_binding]
port1.update()
port2 = self._make_port(
network_id=network.id, qos_policy_id=None,
device_owner='compute:uu:id')
port2_binding = ports_object.PortBinding(
self.context, port_id=port2.id, host='fake_host2',
vnic_type='fake_vnic_type', vif_type='fake_vif_type', profile={})
port2_binding.create()
port2.bindings = [port2_binding]
port2.update()
with mock.patch.object(
self.qos_plugin,
'_change_placement_allocation') as mock_alloc_change:
def fake_change_placement_allocation(orig_policy, policy,
orig_port, port):
port['binding:profile'] = {'allocation': 'fake_allocation'}
mock_alloc_change.side_effect = fake_change_placement_allocation
self.qos_plugin._check_network_for_placement_allocation_change(
'network', 'after_update', ml2plugin_mock,
payload=events.DBEventPayload(
self.context, states=(original_network, network)))
self.assertEqual(ml2plugin_mock._make_port_dict.call_count, 2)
mock_alloc_change_calls = [
mock.call(
original_qos,
qos,
{'id': port1.id,
'device_owner': 'compute:uu:id',
'qos_policy_id': None,
'qos_network_policy_id': qos.id},
mock.ANY),
mock.call(
original_qos,
qos,
{'id': port2.id,
'device_owner': 'compute:uu:id',
'qos_policy_id': None,
'qos_network_policy_id': qos.id},
mock.ANY)]
mock_alloc_change.assert_has_calls(mock_alloc_change_calls,
any_order=True)
port1.update()
port2.update()
self.assertDictEqual(
port1.bindings[0].profile, {'allocation': 'fake_allocation'})
self.assertDictEqual(
port2.bindings[0].profile, {'allocation': 'fake_allocation'})
def _prepare_port_for_placement_allocation(self, original_qos,
desired_qos=None,
original_min_kbps=None,