From 4f7e1f6b9fa92a99d0a5df7e7440d62df3f51748 Mon Sep 17 00:00:00 2001 From: LIU Yulong Date: Sat, 29 Sep 2018 18:04:10 +0800 Subject: [PATCH] Trigger router update only when gateway port IP changed During the ha router state change event, the gateway port only changed the L2 binding host. So l3 agent has the entire gateway port information. It is not necessary to send a router_update message to l3 agent again. Depends-On: https://review.opendev.org/708825/ Closes-Bug: #1795127 Change-Id: Ia332421aff995f42e7a6e6e96b74be1338d54fe1 (cherry picked from commit 452b2824125641128b5297fcf84de090e0763740) --- neutron/common/utils.py | 22 ++++++++ neutron/db/l3_dvrscheduler_db.py | 13 +++-- .../unit/scheduler/test_l3_agent_scheduler.py | 51 +++++++++++++++++++ 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/neutron/common/utils.py b/neutron/common/utils.py index ed2e2debcd7..663aa3b1158 100644 --- a/neutron/common/utils.py +++ b/neutron/common/utils.py @@ -799,3 +799,25 @@ def disable_extension_by_service_plugin(core_plugin, service_plugin): core_plugin.supported_extension_aliases.remove('filter-validation') LOG.info('Disable filter validation extension by service plugin ' '%s.', service_plugin.__class__.__name__) + + +def get_port_fixed_ips_set(port): + return set([ip["ip_address"] for ip in port.get("fixed_ips", [])]) + + +def port_ip_changed(new_port, original_port): + if not new_port or not original_port: + return False + + # Quantity is not same, so it is changed. + if (len(new_port.get("fixed_ips", [])) != + len(original_port.get("fixed_ips", []))): + return True + + # IPs can be placed in any order, so use python set to verify the + # fixed IP addresses. + if (get_port_fixed_ips_set(new_port) != + get_port_fixed_ips_set(original_port)): + return True + + return False diff --git a/neutron/db/l3_dvrscheduler_db.py b/neutron/db/l3_dvrscheduler_db.py index 44c1f90f83a..52b8d8162aa 100644 --- a/neutron/db/l3_dvrscheduler_db.py +++ b/neutron/db/l3_dvrscheduler_db.py @@ -527,6 +527,15 @@ def _notify_l3_agent_port_update(resource, event, trigger, **kwargs): new_port = kwargs.get('port') original_port = kwargs.get('original_port') + is_fixed_ips_changed = n_utils.port_ip_changed(new_port, original_port) + + if (original_port['device_owner'] in + [n_const.DEVICE_OWNER_HA_REPLICATED_INT, + n_const.DEVICE_OWNER_ROUTER_SNAT, + n_const.DEVICE_OWNER_ROUTER_GW] and + not is_fixed_ips_changed): + return + if new_port and original_port: l3plugin = directory.get_plugin(plugin_constants.L3) context = kwargs['context'] @@ -616,10 +625,6 @@ def _notify_l3_agent_port_update(resource, event, trigger, **kwargs): l3plugin, context, original_port, address_pair) return - is_fixed_ips_changed = ( - 'fixed_ips' in new_port and - 'fixed_ips' in original_port and - new_port['fixed_ips'] != original_port['fixed_ips']) if kwargs.get('mac_address_updated') or is_fixed_ips_changed: l3plugin.update_arp_entry_for_dvr_service_port( context, new_port) diff --git a/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py b/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py index 78b574f7ea1..4fd75b64d01 100644 --- a/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py +++ b/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py @@ -1029,10 +1029,12 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, 'context': self.adminContext, 'original_port': { portbindings.HOST_ID: 'vm-host', + 'device_owner': DEVICE_OWNER_COMPUTE, 'mac_address': '02:04:05:17:18:19' }, 'port': { portbindings.HOST_ID: 'vm-host', + 'device_owner': DEVICE_OWNER_COMPUTE, 'mac_address': '02:04:05:17:18:29' }, 'mac_address_updated': True @@ -1047,6 +1049,55 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, self.adminContext, kwargs.get('port')) self.assertFalse(l3plugin.dvr_handle_new_service_port.called) + def test__notify_l3_agent_update_port_with_ip_update(self): + kwargs = { + 'context': self.adminContext, + 'original_port': { + portbindings.HOST_ID: 'vm-host', + 'device_owner': constants.DEVICE_OWNER_ROUTER_GW, + 'fixed_ips': [{'ip_address': '1.1.1.1'}], + 'mac_address': '02:04:05:17:18:19' + }, + 'port': { + portbindings.HOST_ID: 'vm-host', + 'device_owner': constants.DEVICE_OWNER_ROUTER_GW, + 'fixed_ips': [{'ip_address': '2.2.2.2'}], + 'mac_address': '02:04:05:17:18:19' + }, + 'mac_address_updated': False + } + l3plugin = mock.Mock() + directory.add_plugin(plugin_constants.L3, l3plugin) + l3_dvrscheduler_db._notify_l3_agent_port_update( + 'port', 'after_update', mock.ANY, **kwargs) + + l3plugin.update_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, kwargs.get('port')) + self.assertFalse(l3plugin.dvr_handle_new_service_port.called) + + def test__notify_l3_agent_update_port_without_ip_change(self): + kwargs = { + 'context': self.adminContext, + 'original_port': { + portbindings.HOST_ID: 'vm-host', + 'device_owner': constants.DEVICE_OWNER_ROUTER_GW, + 'fixed_ips': [{'ip_address': '1.1.1.1'}], + }, + 'port': { + portbindings.HOST_ID: 'vm-host', + 'device_owner': constants.DEVICE_OWNER_ROUTER_GW, + 'fixed_ips': [{'ip_address': '1.1.1.1'}], + }, + } + l3plugin = mock.Mock() + directory.add_plugin(plugin_constants.L3, l3plugin) + l3_dvrscheduler_db._notify_l3_agent_port_update( + 'port', 'after_update', mock.ANY, **kwargs) + + self.assertFalse(l3plugin.update_arp_entry_for_dvr_service_port.called) + self.assertFalse(l3plugin.dvr_handle_new_service_port.called) + def test__notify_l3_agent_port_binding_change(self): self._test__notify_l3_agent_port_binding_change()