Merge "conman:  Maintain /tmp/hosts."
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 2a3f574..7ff526a 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -10,6 +10,7 @@
 import os
 import random
 import re
+import socket
 import subprocess
 import time
 
@@ -25,6 +26,9 @@
 import status
 
 
+HOSTNAME = socket.gethostname()
+TMP_HOSTS = '/tmp/hosts'
+
 experiment.register('WifiNo2GClient')
 
 
@@ -557,6 +561,37 @@
     self.acs()
     self.internet()
 
+    # Update /etc/hosts (depends on routing table)
+    self._update_tmp_hosts()
+
+  def _update_tmp_hosts(self):
+    """Update the contents of /tmp/hosts."""
+    lowest_metric_interface = None
+    for ifc in [self.bridge] + self.wifi:
+      route = ifc.current_route()
+      if route:
+        metric = route.get('metric', 0)
+        # Skip temporary connection_check routes.
+        if metric == '99':
+          continue
+        candidate = (metric, ifc)
+        if (lowest_metric_interface is None or
+            candidate < lowest_metric_interface):
+          lowest_metric_interface = candidate
+
+    ip_line = ''
+    if lowest_metric_interface:
+      ip = lowest_metric_interface[1].get_ip_address()
+      ip_line = '%s %s\n' % (ip, HOSTNAME) if ip else ''
+
+    new_tmp_hosts = '%s127.0.0.1 localhost' % ip_line
+
+    if not os.path.exists(TMP_HOSTS) or open(TMP_HOSTS).read() != new_tmp_hosts:
+      tmp_hosts_tmp_filename = TMP_HOSTS + '.tmp'
+      tmp_hosts_tmp = open(tmp_hosts_tmp_filename, 'w')
+      tmp_hosts_tmp.write(new_tmp_hosts)
+      os.rename(tmp_hosts_tmp_filename, TMP_HOSTS)
+
   def handle_event(self, path, filename, deleted):
     if deleted:
       self._handle_deleted_file(path, filename)
diff --git a/conman/connection_manager_test.py b/conman/connection_manager_test.py
index db8855d..26b3694 100755
--- a/conman/connection_manager_test.py
+++ b/conman/connection_manager_test.py
@@ -506,6 +506,10 @@
     os.unlink(ap_filename)
 
 
+def check_tmp_hosts(expected_contents):
+  wvtest.WVPASSEQ(open(connection_manager.TMP_HOSTS).read(), expected_contents)
+
+
 def connection_manager_test(radio_config, wlan_configs=None,
                             quantenna_interfaces=None, **cm_kwargs):
   """Returns a decorator that does ConnectionManager test boilerplate."""
@@ -531,6 +535,7 @@
 
       try:
         # No initial state.
+        connection_manager.TMP_HOSTS = tempfile.mktemp()
         tmp_dir = tempfile.mkdtemp()
         config_dir = tempfile.mkdtemp()
         os.mkdir(os.path.join(tmp_dir, 'interfaces'))
@@ -561,6 +566,8 @@
 
         f(c)
       finally:
+        if os.path.exists(connection_manager.TMP_HOSTS):
+          os.unlink(connection_manager.TMP_HOSTS)
         shutil.rmtree(tmp_dir)
         shutil.rmtree(config_dir)
         shutil.rmtree(moca_tmp_dir)
@@ -598,6 +605,7 @@
   wvtest.WVPASS(c.internet())
   wvtest.WVPASS(c.has_status_files([status.P.CAN_REACH_ACS,
                                     status.P.CAN_REACH_INTERNET]))
+  hostname = connection_manager.HOSTNAME
 
   c.run_once()
   wvtest.WVPASS(c.acs())
@@ -661,12 +669,14 @@
   wvtest.WVFAIL(c.acs())
   wvtest.WVFAIL(c.internet())
   wvtest.WVFAIL(c.bridge.current_route())
+  check_tmp_hosts('127.0.0.1 localhost')
 
   # Now there are some scan results.
   c.interface_with_scan_results = c.wifi_for_band(band).name
   # Wait for a scan, plus 3 cycles, so that s2 will have been tried.
   c.run_until_scan(band)
   wvtest.WVPASSEQ(c.log_upload_count, 0)
+  c.wifi_for_band(band).ip_testonly = '192.168.1.100'
   for _ in range(3):
     c.run_once()
     wvtest.WVPASS(c.has_status_files([status.P.CONNECTED_TO_OPEN]))
@@ -684,6 +694,8 @@
   wvtest.WVPASSEQ(c.log_upload_count, 1)
   # Disable scan results again.
   c.interface_with_scan_results = None
+  c.run_until_interface_update()
+  check_tmp_hosts('192.168.1.100 %s\n127.0.0.1 localhost' % hostname)
 
   # Now, create a WLAN configuration which should be connected to.
   ssid = 'wlan'
@@ -743,16 +755,20 @@
   wvtest.WVPASS(c.client_up(band))
   wvtest.WVPASS(c.wifi_for_band(band).current_route())
   wvtest.WVFAIL(c.bridge.current_route())
+  c.run_until_interface_update()
+  check_tmp_hosts('192.168.1.100 %s\n127.0.0.1 localhost' % hostname)
 
   # Now bring up the bridge.  We should remove the wifi connection and start
   # an AP.
   c.set_ethernet(True)
   c.bridge.set_connection_check_result('succeed')
+  c.bridge.ip_testonly = '192.168.1.101'
   c.run_until_interface_update()
   wvtest.WVPASS(c.access_point_up(band))
   wvtest.WVFAIL(c.client_up(band))
   wvtest.WVFAIL(c.wifi_for_band(band).current_route())
   wvtest.WVPASS(c.bridge.current_route())
+  check_tmp_hosts('192.168.1.101 %s\n127.0.0.1 localhost' % hostname)
 
   # Now move (rather than delete) the configuration file.  The AP should go
   # away, and we should not be able to join the WLAN.  Routes should not be
@@ -783,6 +799,7 @@
   c.run_until_interface_update()
   wvtest.WVFAIL(c.acs())
   wvtest.WVFAIL(c.internet())
+  check_tmp_hosts('127.0.0.1 localhost')
   # s3 is not what the cycler would suggest trying next.
   wvtest.WVPASSNE('s3', c.wifi_for_band(band).cycler.peek())
   # Run only once, so that only one BSS can be tried.  It should be the s3 one,
diff --git a/conman/interface.py b/conman/interface.py
index 12fa7ce..87a080f 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -28,6 +28,7 @@
 
   CONNECTION_CHECK = 'connection_check'
   IP_ROUTE = ['ip', 'route']
+  IP_ADDR_SHOW = ['ip', 'addr', 'show', 'dev']
 
   def __init__(self, name, metric):
     self.name = name
@@ -190,6 +191,18 @@
                     e.message)
       return ''
 
+  def _ip_addr_show(self):
+    try:
+      return subprocess.check_output(self.IP_ADDR_SHOW + [self.name])
+    except subprocess.CalledProcessError as e:
+      logging.error('Could not get IP address for %s: %s', self.name, e.message)
+      return None
+
+  def get_ip_address(self):
+    match = re.search(r'^\s*inet (?P<IP>\d+\.\d+\.\d+\.\d+)',
+                      self._ip_addr_show(), re.MULTILINE)
+    return match and match.group('IP') or None
+
   def set_gateway_ip(self, gateway_ip):
     logging.info('New gateway IP %s for %s', gateway_ip, self.name)
     self._gateway_ip = gateway_ip
diff --git a/conman/interface_test.py b/conman/interface_test.py
index 5b9d431..0d8ead7 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -22,6 +22,13 @@
 from wvtest import wvtest
 
 
+# pylint: disable=line-too-long
+_IP_ADDR_SHOW_TPL = """4: {name}: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
+    inet {ip}/21 brd 100.100.55.255 scope global {name}
+       valid_lft forever preferred_lft forever
+"""
+
+
 class FakeInterfaceMixin(object):
   """Replace Interface methods which interact with the system."""
 
@@ -29,6 +36,7 @@
     super(FakeInterfaceMixin, self).__init__(*args, **kwargs)
     self.set_connection_check_result('succeed')
     self.routing_table = {}
+    self.ip_testonly = None
 
   def set_connection_check_result(self, result):
     if result in ['succeed', 'fail', 'restricted']:
@@ -63,6 +71,12 @@
             del self.routing_table[k]
             break
 
+  def _ip_addr_show(self):
+    if self.ip_testonly:
+      return _IP_ADDR_SHOW_TPL.format(name=self.name, ip=self.ip_testonly)
+
+    return ''
+
 
 class Bridge(FakeInterfaceMixin, interface.Bridge):
   pass
@@ -361,6 +375,10 @@
     wvtest.WVPASS(b.current_route())
     wvtest.WVPASS(os.path.exists(autoprov_filepath))
 
+    wvtest.WVFAIL(b.get_ip_address())
+    b.ip_testonly = '192.168.1.100'
+    wvtest.WVPASSEQ(b.get_ip_address(), '192.168.1.100')
+
   finally:
     shutil.rmtree(tmp_dir)