]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
support 'send_arp_for_ha' option in l3_agent
authorjustin ljj <iamljj@gmail.com>
Mon, 27 Aug 2012 08:27:23 +0000 (16:27 +0800)
committerJiajun Liu <iamljj@gmail.com>
Mon, 29 Oct 2012 02:47:07 +0000 (10:47 +0800)
Fixes bug 1042046

Sending gratuitous ARPs when a external router or a floating
IP was added in order to facilitate HA.

I am using iputils-arping package to send ARP packets. You will need
to install iputils-arping on Ubuntu and iputils on Fedora/RHEL/CentOS.
Arping locates in /usr/bin/arping on Ubuntu and locates in /sbin/arping
on Fedora/RHEL/CentOS.

Change-Id: I125dbc57b90027dc5e99ff0a5d6877843a0b02a5

etc/l3_agent.ini
etc/quantum/rootwrap.d/l3.filters
quantum/agent/l3_agent.py
quantum/tests/unit/test_l3_agent.py

index 586c6670447b3516e3516cc64b5be7b0bc49a0ca..c76b1af26b3d91014fa277d67796423bc9bbb1b1 100644 (file)
@@ -54,3 +54,7 @@ root_helper = sudo
 
 # The time in seconds between state poll requests
 # polling_interval = 3
+
+# Send this many gratuitous ARPs for HA setup. Set it below or equal to 0
+# to disable this feature.
+# send_arp_for_ha = 3
index 82b9ed575320e077eccf6a60f35ab71f9e7c103f..a84800067cc36115d860d75f8ee0a7bcadadfe14 100644 (file)
@@ -8,6 +8,10 @@
 
 [Filters]
 
+# arping
+arping: CommandFilter, /usr/bin/arping, root
+arping_sbin: CommandFilter, /sbin/arping, root
+
 # l3_agent
 sysctl: CommandFilter, /sbin/sysctl, root
 route: CommandFilter, /sbin/route, root
index 26a56fc71f3eb49cb238eb9737f467b67b470768..b60f5627d3c744ebb3fde48622f1262893681abe 100644 (file)
@@ -83,10 +83,10 @@ class L3NATAgent(object):
         cfg.IntOpt('metadata_port',
                    default=8775,
                    help="TCP Port used by Nova metadata server."),
-        #FIXME(danwent): not currently used
-        cfg.BoolOpt('send_arp_for_ha',
-                    default=True,
-                    help="Send gratuitious ARP when router IP is configured"),
+        cfg.IntOpt('send_arp_for_ha',
+                   default=3,
+                   help="Send this many gratuitous ARPs for HA setup, "
+                        "set it below or equal to 0 to disable this feature."),
         cfg.BoolOpt('use_namespaces', default=True,
                     help="Allow overlapping IP."),
         cfg.StrOpt('router_id', default='',
@@ -359,6 +359,23 @@ class L3NATAgent(object):
             LOG.error("Ignoring multiple gateway ports for router %s"
                       % ri.router_id)
 
+    def _send_gratuitous_arp_packet(self, ri, interface_name, ip_address):
+        if self.conf.send_arp_for_ha > 0:
+            arping_cmd = ['arping', '-A', '-U',
+                          '-I', interface_name,
+                          '-c', self.conf.send_arp_for_ha,
+                          ip_address]
+            try:
+                if self.conf.use_namespaces:
+                    ip_wrapper = ip_lib.IPWrapper(self.conf.root_helper,
+                                                  namespace=ri.ns_name())
+                    ip_wrapper.netns.execute(arping_cmd, check_exit_code=True)
+                else:
+                    utils.execute(arping_cmd, check_exit_code=True,
+                                  root_helper=self.conf.root_helper)
+            except Exception as e:
+                LOG.error(_("Failed sending gratuitous ARP: %s") % str(e))
+
     def get_internal_device_name(self, port_id):
         return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
 
@@ -380,6 +397,8 @@ class L3NATAgent(object):
                              prefix=EXTERNAL_DEV_PREFIX)
         self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']],
                             namespace=ri.ns_name())
+        ip_address = ex_gw_port['ip_cidr'].split('/')[0]
+        self._send_gratuitous_arp_packet(ri, interface_name, ip_address)
 
         gw_ip = ex_gw_port['subnet']['gateway_ip']
         if ex_gw_port['subnet']['gateway_ip']:
@@ -454,6 +473,8 @@ class L3NATAgent(object):
 
         self.driver.init_l3(interface_name, [internal_cidr],
                             namespace=ri.ns_name())
+        ip_address = internal_cidr.split('/')[0]
+        self._send_gratuitous_arp_packet(ri, interface_name, ip_address)
 
         if ex_gw_port:
             ex_gw_ip = ex_gw_port['fixed_ips'][0]['ip_address']
@@ -494,6 +515,7 @@ class L3NATAgent(object):
         if not ip_cidr in [addr['cidr'] for addr in device.addr.list()]:
             net = netaddr.IPNetwork(ip_cidr)
             device.addr.add(net.version, ip_cidr, str(net.broadcast))
+            self._send_gratuitous_arp_packet(ri, interface_name, floating_ip)
 
         for chain, rule in self.floating_forward_rules(floating_ip, fixed_ip):
             ri.iptables_manager.ipv4['nat'].add_rule(chain, rule)
index f7383c3453be7f3997fb6fb768a377282ee3f642..5b45e0ed5cfb56687447453ec656fef7e1ad1279 100644 (file)
@@ -125,13 +125,23 @@ class TestBasicRouterOperations(unittest.TestCase):
                       'network_id': _uuid(),
                       'mac_address': 'ca:fe:de:ad:be:ef',
                       'ip_cidr': '20.0.0.30/24'}
+        interface_name = agent.get_external_device_name(ex_gw_port['id'])
 
         if action == 'add':
             self.device_exists.return_value = False
             agent.external_gateway_added(ri, ex_gw_port, internal_cidrs)
             self.assertEquals(self.mock_driver.plug.call_count, 1)
             self.assertEquals(self.mock_driver.init_l3.call_count, 1)
-            self.assertEquals(self.mock_ip.netns.execute.call_count, 1)
+            arping_cmd = ['arping', '-A', '-U',
+                          '-I', interface_name,
+                          '-c', self.conf.send_arp_for_ha,
+                          '20.0.0.30']
+            if self.conf.use_namespaces:
+                self.mock_ip.netns.execute.assert_any_call(
+                    arping_cmd, check_exit_code=True)
+            else:
+                self.utils_exec.assert_any_call(
+                    check_exit_code=True, root_helper=self.conf.root_helper)
 
         elif action == 'remove':
             self.device_exists.return_value = True
@@ -159,10 +169,21 @@ class TestBasicRouterOperations(unittest.TestCase):
                       'id': _uuid(),
                       'mac_address': 'ca:fe:de:ad:be:ef',
                       'ip_cidr': '20.0.0.30/24'}
+        interface_name = agent.get_external_device_name(ex_gw_port['id'])
 
         if action == 'add':
             self.device_exists.return_value = False
             agent.floating_ip_added(ri, ex_gw_port, floating_ip, fixed_ip)
+            arping_cmd = ['arping', '-A', '-U',
+                          '-I', interface_name,
+                          '-c', self.conf.send_arp_for_ha,
+                          floating_ip]
+            if self.conf.use_namespaces:
+                self.mock_ip.netns.execute.assert_any_call(
+                    arping_cmd, check_exit_code=True)
+            else:
+                self.utils_exec.assert_any_call(
+                    check_exit_code=True, root_helper=self.conf.root_helper)
 
         elif action == 'remove':
             self.device_exists.return_value = True