From df0609f923765b08a50b7a3fc688a16762f90d6e 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. Conflicts: neutron/db/l3_dvr_db.py neutron/db/l3_dvrscheduler_db.py Closes-Bug: #1512199 Change-Id: Ic7a4bbfca8b477c41b233235d2e2a2864f7af411 (cherry picked from commit 5535a71e753d7c6ef679437ee93faffc6bc31f62) --- 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 75ca9634e8b..b299a27164d 100644 --- a/neutron/db/l3_dvr_db.py +++ b/neutron/db/l3_dvr_db.py @@ -621,6 +621,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 dvr_vmarp_table_update(self, context, port_dict, action): """Notify L3 agents of VM ARP table changes. @@ -633,24 +651,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 24afcb4aa0c..33744df0aef 100644 --- a/neutron/db/l3_dvrscheduler_db.py +++ b/neutron/db/l3_dvrscheduler_db.py @@ -515,6 +515,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] != @@ -523,7 +527,7 @@ def _notify_l3_agent_port_update(resource, event, trigger, **kwargs): n_utils.is_dvr_serviced(new_device_owner)): l3plugin.dvr_update_router_addvm(context, new_port) l3plugin.dvr_vmarp_table_update(context, new_port, "add") - elif kwargs.get('mac_address_updated'): + elif kwargs.get('mac_address_updated') or is_fixed_ips_changed: l3plugin.dvr_vmarp_table_update(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 44411036962..b69f117eb48 100644 --- a/neutron/tests/unit/db/test_l3_dvr_db.py +++ b/neutron/tests/unit/db/test_l3_dvr_db.py @@ -567,10 +567,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 } @@ -585,9 +588,9 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): dvr_router.extra_attributes.distributed = True self.mixin.dvr_vmarp_table_update(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_dvr_vmarp_table_update_with_service_port_added(self): action = 'add'