DVR: when updating port's fixed_ips, update arp
authorJohn Schwarz <jschwarz@redhat.com>
Tue, 5 Jan 2016 15:21:30 +0000 (17:21 +0200)
committerBrian Haley <brian.haley@hpe.com>
Thu, 14 Jan 2016 23:24:22 +0000 (18:24 -0500)
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
neutron/db/l3_dvrscheduler_db.py
neutron/tests/unit/db/test_l3_dvr_db.py

index 4c883d87ae7a711c86c4e23d03aee1423ec4e13b..5d34bb8b0d1fd374f943334f233029c9b5e48adb 100644 (file)
@@ -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):
index 497546725a81c318ac92cef39c2073ccae49438c..738a9081014f4f895ba26965ce954269ae81b019 100644 (file)
@@ -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")
 
index 7c495b898b2c37c80619ea308b2f21bc7e069f5e..84b772f83585510dec4609d3c711c58fb7f22e02 100644 (file)
@@ -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'