From 5535a71e753d7c6ef679437ee93faffc6bc31f62 Mon Sep 17 00:00:00 2001 From: John Schwarz Date: Tue, 5 Jan 2016 17:21:30 +0200 Subject: [PATCH] DVR: when updating port's fixed_ips, update arp Currently, when updating a port's fixed_ips, the l3 agents fail to update the arp tables of this change, which can lead to east-west connectivity issues when a router is connected to more than one tenant network. Closes-Bug: #1512199 Change-Id: Ic7a4bbfca8b477c41b233235d2e2a2864f7af411 --- neutron/db/l3_dvr_db.py | 47 +++++++++++++++---------- neutron/db/l3_dvrscheduler_db.py | 6 +++- neutron/tests/unit/db/test_l3_dvr_db.py | 15 ++++---- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/neutron/db/l3_dvr_db.py b/neutron/db/l3_dvr_db.py index 4c883d87ae7..5d34bb8b0d1 100644 --- a/neutron/db/l3_dvr_db.py +++ b/neutron/db/l3_dvr_db.py @@ -684,6 +684,24 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin, self._populate_subnets_for_ports(context, port_list) return port_list + def _generate_arp_table_and_notify_agent( + self, context, fixed_ip, mac_address, notifier): + """Generates the arp table entry and notifies the l3 agent.""" + ip_address = fixed_ip['ip_address'] + subnet = fixed_ip['subnet_id'] + filters = {'fixed_ips': {'subnet_id': [subnet]}} + ports = self._core_plugin.get_ports(context, filters=filters) + for port in ports: + if port['device_owner'] == l3_const.DEVICE_OWNER_DVR_INTERFACE: + router_id = port['device_id'] + router_dict = self._get_router(context, router_id) + if router_dict.extra_attributes.distributed: + arp_table = {'ip_address': ip_address, + 'mac_address': mac_address, + 'subnet_id': subnet} + notifier(context, router_id, arp_table) + return + def update_arp_entry_for_dvr_service_port( self, context, port_dict, action): """Notify L3 agents of ARP table entry for dvr service port. @@ -697,24 +715,17 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin, if not (n_utils.is_dvr_serviced(port_dict['device_owner']) and port_dict['fixed_ips']): return - ip_address = port_dict['fixed_ips'][0]['ip_address'] - subnet = port_dict['fixed_ips'][0]['subnet_id'] - filters = {'fixed_ips': {'subnet_id': [subnet]}} - ports = self._core_plugin.get_ports(context, filters=filters) - for port in ports: - if port['device_owner'] == l3_const.DEVICE_OWNER_DVR_INTERFACE: - router_id = port['device_id'] - router_dict = self._get_router(context, router_id) - if router_dict.extra_attributes.distributed: - arp_table = {'ip_address': ip_address, - 'mac_address': port_dict['mac_address'], - 'subnet_id': subnet} - if action == "add": - notify_action = self.l3_rpc_notifier.add_arp_entry - elif action == "del": - notify_action = self.l3_rpc_notifier.del_arp_entry - notify_action(context, router_id, arp_table) - return + changed_fixed_ips = port_dict['fixed_ips'] + for fixed_ip in changed_fixed_ips: + if action == "add": + notifier = self.l3_rpc_notifier.add_arp_entry + elif action == "del": + notifier = self.l3_rpc_notifier.del_arp_entry + else: + return + + self._generate_arp_table_and_notify_agent( + context, fixed_ip, port_dict['mac_address'], notifier) def delete_csnat_router_interface_ports(self, context, router, subnet_id=None): diff --git a/neutron/db/l3_dvrscheduler_db.py b/neutron/db/l3_dvrscheduler_db.py index 497546725a8..738a9081014 100644 --- a/neutron/db/l3_dvrscheduler_db.py +++ b/neutron/db/l3_dvrscheduler_db.py @@ -496,6 +496,10 @@ def _notify_l3_agent_port_update(resource, event, trigger, **kwargs): event, resource, trigger, **removed_router_args) if not n_utils.is_dvr_serviced(new_device_owner): 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']) is_new_port_binding_changed = ( new_port[portbindings.HOST_ID] and (original_port[portbindings.HOST_ID] != @@ -505,7 +509,7 @@ def _notify_l3_agent_port_update(resource, event, trigger, **kwargs): l3plugin.dvr_handle_new_service_port(context, new_port) l3plugin.update_arp_entry_for_dvr_service_port( context, new_port, "add") - elif kwargs.get('mac_address_updated'): + elif kwargs.get('mac_address_updated') or is_fixed_ips_changed: l3plugin.update_arp_entry_for_dvr_service_port( context, new_port, "add") diff --git a/neutron/tests/unit/db/test_l3_dvr_db.py b/neutron/tests/unit/db/test_l3_dvr_db.py index 7c495b898b2..84b772f8358 100644 --- a/neutron/tests/unit/db/test_l3_dvr_db.py +++ b/neutron/tests/unit/db/test_l3_dvr_db.py @@ -593,10 +593,13 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): gp.return_value = plugin port = { 'id': 'my_port_id', - 'fixed_ips': [{ - 'ip_address': 'my_ip', - 'subnet_id': 'my_subnet_id', - }], + 'fixed_ips': [ + {'subnet_id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323', + 'ip_address': '10.0.0.11'}, + {'subnet_id': '2b7c8a07-6f8e-4937-8701-f1d5da1a807c', + 'ip_address': '10.0.0.21'}, + {'subnet_id': '48534187-f077-4e81-93ff-81ec4cc0ad3b', + 'ip_address': 'fd45:1515:7e0:0:f816:3eff:fe1a:1111'}], 'mac_address': 'my_mac', 'device_owner': device_owner } @@ -612,9 +615,9 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): self.mixin.update_arp_entry_for_dvr_service_port( self.ctx, port, action) if action == 'add': - self.assertTrue(l3_notify.add_arp_entry.called) + self.assertEqual(3, l3_notify.add_arp_entry.call_count) elif action == 'del': - self.assertTrue(l3_notify.del_arp_entry.called) + self.assertTrue(3, l3_notify.del_arp_entry.call_count) def test_update_arp_entry_for_dvr_service_port_added(self): action = 'add'