Add default IPv4 and IPv6 addrs to metadata

This patch collects the default IPv4 and IPv6 addresses for the
persisted metadata if either can be discovered.
diff --git a/DataSourceVMwareGuestInfo.py b/DataSourceVMwareGuestInfo.py
index 3a3b884..10e2513 100644
--- a/DataSourceVMwareGuestInfo.py
+++ b/DataSourceVMwareGuestInfo.py
@@ -18,17 +18,21 @@
 A cloud init datasource for VMware GuestInfo.
 '''
 
-import collections
 import base64
-import zlib
-import json
+import collections
 from distutils.spawn import find_executable
+import json
+import socket
+import zlib
 
 from cloudinit import log as logging
 from cloudinit import sources
 from cloudinit import util
 from cloudinit import safeyaml
 
+from deepmerge import always_merger
+import netifaces
+
 LOG = logging.getLogger(__name__)
 NOVAL = "No value found"
 VMTOOLSD = find_executable("vmtoolsd")
@@ -123,20 +127,14 @@
         brought up the OS at this point.
         """
 
-        # Set the hostname.
-        hostname = self.metadata.get('local-hostname')
-        if hostname:
-            self.distro.set_hostname(hostname)
-            LOG.info("set hostname %s", hostname)
-
-        # Update the metadata with the actual host name and actual network
-        # interface information.
+        # Get information about the host.
         host_info = get_host_info()
         LOG.info("got host-info: %s", host_info)
-        hostname = host_info.get('local-hostname', hostname)
-        self.metadata['local-hostname'] = hostname
-        interfaces = host_info['network']['interfaces']
-        self.metadata['network']['interfaces'] = interfaces
+
+        # Ensure the metadata gets updated with information about the
+        # host, including the network interfaces, default IP addresses,
+        # etc.
+        self.metadata = always_merger.merge(self.metadata, host_info)
 
         # Persist the instance data for versions of cloud-init that support
         # doing so. This occurs here rather than in the get_data call in
@@ -298,30 +296,94 @@
     return [DataSourceVMwareGuestInfo]
 
 
+def get_default_ip_addrs():
+    '''
+    Returns the default IPv4 and IPv6 addresses based on the device(s) used for
+    the default route. Please note that None may be returned for either address
+    family if that family has no default route or if there are multiple
+    addresses associated with the device used by the default route for a given
+    address.
+    '''
+    gateways = netifaces.gateways()
+    if 'default' not in gateways:
+        return None, None
+
+    default_gw = gateways['default']
+    if netifaces.AF_INET not in default_gw and netifaces.AF_INET6 not in default_gw:
+        return None, None
+
+    ipv4 = None
+    ipv6 = None
+
+    gw4 = default_gw.get(netifaces.AF_INET)
+    if gw4:
+        _, dev4 = gw4
+        addr_fams = netifaces.ifaddresses(dev4)
+        if addr_fams:
+            af_inet = addr_fams.get(netifaces.AF_INET)
+            if af_inet:
+                if len(af_inet) > 1:
+                    LOG.warn(
+                        "device %s has more than one ipv4 address: %s", dev4, af_inet)
+                elif 'addr' in af_inet[0]:
+                    ipv4 = af_inet[0]['addr']
+
+    # Try to get the default IPv6 address by first seeing if there is a default
+    # IPv6 route. If there isn't then see if there is a single IPv6 address
+    # associated with the same device associated with the default IPv4 address.
+    gw6 = default_gw.get(netifaces.AF_INET6)
+    if gw6:
+        _, dev6 = gw6
+        addr6_fams = netifaces.ifaddresses(dev6)
+        if addr6_fams:
+            af_inet6 = addr6_fams.get(netifaces.AF_INET6)
+            if af_inet6:
+                if len(af_inet6) > 1:
+                    LOG.warn(
+                        "device %s has more than one ipv6 address: %s", dev6, af_inet6)
+                elif 'addr' in af_inet6[0]:
+                    ipv6 = af_inet6[0]['addr']
+    elif addr_fams:
+        af_inet6 = addr_fams.get(netifaces.AF_INET6)
+        if af_inet6:
+            if len(af_inet6) > 1:
+                LOG.warn(
+                    "device %s has more than one ipv6 address: %s", dev4, af_inet6)
+            elif 'addr' in af_inet6[0]:
+                ipv6 = af_inet6[0]['addr']
+
+    return ipv4, ipv6
+
+
 def get_host_info():
     '''
     Returns host information such as the host name and network interfaces.
     '''
-    import netifaces
-    import socket
 
     host_info = {
         'network': {
             'interfaces': {
                 'by-mac': collections.OrderedDict(),
-                'by-ip4': collections.OrderedDict(),
-                'by-ip6': collections.OrderedDict(),
+                'by-ipv4': collections.OrderedDict(),
+                'by-ipv6': collections.OrderedDict(),
             },
         },
     }
 
     hostname = socket.getfqdn()
     if hostname:
+        host_info['hostname'] = hostname
         host_info['local-hostname'] = hostname
 
+    default_ipv4, default_ipv6 = get_default_ip_addrs()
+    if default_ipv4:
+        host_info['local-ipv4'] = default_ipv4
+    if default_ipv6:
+        host_info['local-ipv6'] = default_ipv6
+
     by_mac = host_info['network']['interfaces']['by-mac']
-    by_ip4 = host_info['network']['interfaces']['by-ip4']
-    by_ip6 = host_info['network']['interfaces']['by-ip6']
+    by_ipv4 = host_info['network']['interfaces']['by-ipv4']
+    by_ipv6 = host_info['network']['interfaces']['by-ipv6']
 
     ifaces = netifaces.interfaces()
     for dev_name in ifaces:
@@ -342,33 +404,47 @@
             key = mac
             val = {}
             if af_inet:
-                val["ip4"] = af_inet
+                val["ipv4"] = af_inet
             if af_inet6:
-                val["ip6"] = af_inet6
+                val["ipv6"] = af_inet6
             by_mac[key] = val
 
         if af_inet:
             for ip_info in af_inet:
                 key = ip_info['addr']
+                if key == '127.0.0.1':
+                    continue
                 val = ip_info.copy()
                 del val['addr']
                 if mac:
                     val['mac'] = mac
-                by_ip4[key] = val
+                by_ipv4[key] = val
 
         if af_inet6:
             for ip_info in af_inet6:
                 key = ip_info['addr']
+                if key == '::1':
+                    continue
                 val = ip_info.copy()
                 del val['addr']
                 if mac:
                     val['mac'] = mac
-                by_ip6[key] = val
+                by_ipv6[key] = val
 
     return host_info
 
 
+def main():
+    '''
+    Executed when this file is used as a program.
+    '''
+    metadata = {'network': {'config': {'dhcp': True}}}
+    host_info = get_host_info()
+    metadata = always_merger.merge(metadata, host_info)
+    print(util.json_dumps(metadata))
+
+
 if __name__ == "__main__":
-    print util.json_dumps(get_host_info())
+    main()
 
 # vi: ts=4 expandtab