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()