← Back to team overview

cloud-init-dev team mailing list archive

[Merge] lp:~shraddha-pandhe/cloud-init/cloud-init-ipv6-support into lp:cloud-init

 

Shraddha Pandhe has proposed merging lp:~shraddha-pandhe/cloud-init/cloud-init-ipv6-support into lp:cloud-init.

Requested reviews:
  cloud init development team (cloud-init-dev)
Related bugs:
  Bug #1391695 in cloud-init: "IPv6 network info translation support for rhel"
  https://bugs.launchpad.net/cloud-init/+bug/1391695

For more details, see:
https://code.launchpad.net/~shraddha-pandhe/cloud-init/cloud-init-ipv6-support/+merge/242547

IPv6 support for rhel
-- 
Your team cloud init development team is requested to review the proposed merge of lp:~shraddha-pandhe/cloud-init/cloud-init-ipv6-support into lp:cloud-init.
=== modified file 'cloudinit/distros/net_util.py'
--- cloudinit/distros/net_util.py	2014-01-24 21:20:54 +0000
+++ cloudinit/distros/net_util.py	2014-11-21 22:24:21 +0000
@@ -113,6 +113,10 @@
     for info in ifaces:
         if 'iface' not in info:
             continue
+        use_ipv6 = False
+        # Check if current device has an ipv6 IP
+        if 'inet6' in info['iface']:
+            use_ipv6 = True
         iface_details = info['iface'].split(None)
         dev_name = None
         if len(iface_details) >= 1:
@@ -122,6 +126,7 @@
         if not dev_name:
             continue
         iface_info = {}
+        iface_info['ipv6'] = {}
         if len(iface_details) >= 3:
             proto_type = iface_details[2].strip().lower()
             # Seems like this can be 'loopback' which we don't
@@ -129,26 +134,39 @@
             if proto_type in ['dhcp', 'static']:
                 iface_info['bootproto'] = proto_type
         # These can just be copied over
-        for k in ['netmask', 'address', 'gateway', 'broadcast']:
-            if k in info:
-                val = info[k].strip().lower()
-                if val:
-                    iface_info[k] = val
-        # Name server info provided??
-        if 'dns-nameservers' in info:
-            iface_info['dns-nameservers'] = info['dns-nameservers'].split()
-        # Name server search info provided??
-        if 'dns-search' in info:
-            iface_info['dns-search'] = info['dns-search'].split()
-        # Is any mac address spoofing going on??
-        if 'hwaddress' in info:
-            hw_info = info['hwaddress'].lower().strip()
-            hw_split = hw_info.split(None, 1)
-            if len(hw_split) == 2 and hw_split[0].startswith('ether'):
-                hw_addr = hw_split[1]
-                if hw_addr:
-                    iface_info['hwaddress'] = hw_addr
-        real_ifaces[dev_name] = iface_info
+        if use_ipv6:
+            for k in ['address', 'gateway']:
+                if k in info:
+                    val = info[k].strip().lower()
+                    if val:
+                        iface_info['ipv6'][k] = val
+        else:
+            for k in ['netmask', 'address', 'gateway', 'broadcast']:
+                if k in info:
+                    val = info[k].strip().lower()
+                    if val:
+                        iface_info[k] = val
+            # Name server info provided??
+            if 'dns-nameservers' in info:
+                iface_info['dns-nameservers'] = info['dns-nameservers'].split()
+            # Name server search info provided??
+            if 'dns-search' in info:
+                iface_info['dns-search'] = info['dns-search'].split()
+            # Is any mac address spoofing going on??
+            if 'hwaddress' in info:
+                hw_info = info['hwaddress'].lower().strip()
+                hw_split = hw_info.split(None, 1)
+                if len(hw_split) == 2 and hw_split[0].startswith('ether'):
+                    hw_addr = hw_split[1]
+                    if hw_addr:
+                        iface_info['hwaddress'] = hw_addr
+
+        # If ipv6 is enabled, device will have multiple IPs.
+        # Update the dictionary instead of overwriting it
+        if dev_name in real_ifaces:
+            real_ifaces[dev_name].update(iface_info)
+        else:
+            real_ifaces[dev_name] = iface_info
     # Check for those that should be started on boot via 'auto'
     for (cmd, args) in entries:
         if cmd == 'auto':
@@ -160,4 +178,6 @@
             dev_name = args[0].strip().lower()
             if dev_name in real_ifaces:
                 real_ifaces[dev_name]['auto'] = True
+        if cmd == 'iface' and 'inet6' in args:
+                real_ifaces[dev_name]['inet6'] = True
     return real_ifaces

=== modified file 'cloudinit/distros/rhel.py'
--- cloudinit/distros/rhel.py	2014-10-17 19:32:41 +0000
+++ cloudinit/distros/rhel.py	2014-11-21 22:24:21 +0000
@@ -71,6 +71,7 @@
         nameservers = []
         searchservers = []
         dev_names = entries.keys()
+        use_ipv6 = False
         for (dev, info) in entries.iteritems():
             net_fn = self.network_script_tpl % (dev)
             net_cfg = {
@@ -83,6 +84,13 @@
                 'MACADDR': info.get('hwaddress'),
                 'ONBOOT': _make_sysconfig_bool(info.get('auto')),
             }
+            if info.get('inet6'):
+                use_ipv6 = True
+                net_cfg.update({
+                    'IPV6INIT': _make_sysconfig_bool(True),
+                    'IPV6ADDR': info.get('ipv6').get('address'),
+                    'IPV6_DEFAULTGW': info.get('ipv6').get('gateway'),
+            })
             rhel_util.update_sysconfig_file(net_fn, net_cfg)
             if 'dns-nameservers' in info:
                 nameservers.extend(info['dns-nameservers'])
@@ -95,6 +103,10 @@
             net_cfg = {
                 'NETWORKING': _make_sysconfig_bool(True),
             }
+            # If IPv6 interface present, enable ipv6 networking
+            if use_ipv6:
+                net_cfg['NETWORKING_IPV6'] = _make_sysconfig_bool(True)
+                net_cfg['IPV6_AUTOCONF'] = _make_sysconfig_bool(False)
             rhel_util.update_sysconfig_file(self.network_conf_fn, net_cfg)
         return dev_names
 

=== modified file 'cloudinit/netinfo.py'
--- cloudinit/netinfo.py	2014-09-12 21:22:29 +0000
+++ cloudinit/netinfo.py	2014-11-21 22:24:21 +0000
@@ -72,6 +72,7 @@
                 "bcast:": "bcast", "broadcast": "bcast",
                 "mask:": "mask", "netmask": "mask",
                 "hwaddr": "hwaddr", "ether": "hwaddr",
+                "scope": "scope",
             }
             for origfield, field in ifconfigfields.items():
                 target = "%s%s" % (field, fieldpost)
@@ -96,7 +97,12 @@
 
 def route_info():
     (route_out, _err) = util.subp(["netstat", "-rn"])
-    routes = []
+    (route_out6, _err6) = util.subp(["netstat", "-A inet6", "-n"])
+
+    routes = {}
+    routes['ipv4'] = []
+    routes['ipv6'] = []
+
     entries = route_out.splitlines()[1:]
     for line in entries:
         if not line:
@@ -132,7 +138,26 @@
             'iface': toks[7],
         }
 
-        routes.append(entry)
+        routes['ipv4'].append(entry)
+
+    entries6 = route_out6.splitlines()[1:]
+    for line in entries6:
+        if not line:
+            continue
+        toks = line.split()
+
+        if (len(toks) < 6 or toks[0] == "Kernel" or
+                toks[0] == "Proto" or toks[0] == "Active"):
+            continue
+        entry = {
+            'proto': toks[0],
+            'recv-q': toks[1],
+            'send-q': toks[2],
+            'local address': toks[3],
+            'foreign address': toks[4],
+            'state': toks[5],
+        }
+        routes['ipv6'].append(entry)
     return routes
 
 
@@ -156,10 +181,12 @@
         lines.append(util.center("Net device info failed", '!', 80))
         netdev = None
     if netdev is not None:
-        fields = ['Device', 'Up', 'Address', 'Mask', 'Hw-Address']
+        fields = ['Device', 'Up', 'Address', 'Mask', 'Scope', 'Hw-Address']
         tbl = PrettyTable(fields)
         for (dev, d) in netdev.iteritems():
-            tbl.add_row([dev, d["up"], d["addr"], d["mask"], d["hwaddr"]])
+            tbl.add_row([dev, d["up"], d["addr"], d["mask"], ".", d["hwaddr"]])
+            if d["addr6"]:
+                tbl.add_row([dev, d["up"], d["addr6"], ".", d["scope6"], d["hwaddr"]])
         netdev_s = tbl.get_string()
         max_len = len(max(netdev_s.splitlines(), key=len))
         header = util.center("Net device info", "+", max_len)
@@ -176,15 +203,30 @@
         util.logexc(LOG, "Route info failed: %s" % e)
         routes = None
     if routes is not None:
-        fields = ['Route', 'Destination', 'Gateway',
+        fields_v4 = ['Route', 'Destination', 'Gateway',
                   'Genmask', 'Interface', 'Flags']
-        tbl = PrettyTable(fields)
-        for (n, r) in enumerate(routes):
+
+        if routes.get('ipv6') is not None:
+            fields_v6 = ['Route', 'Proto', 'Recv-Q', 'Send-Q', 'Local Address',
+                           'Foreign Address', 'State']
+
+        tbl_v4 = PrettyTable(fields_v4)
+        for (n, r) in enumerate(routes.get('ipv4')):
             route_id = str(n)
-            tbl.add_row([route_id, r['destination'],
+            tbl_v4.add_row([route_id, r['destination'],
                         r['gateway'], r['genmask'],
                         r['iface'], r['flags']])
-        route_s = tbl.get_string()
+        route_s = tbl_v4.get_string()
+        if fields_v6:
+            tbl_v6 = PrettyTable(fields_v6)
+            for (n, r) in enumerate(routes.get('ipv6')):
+                route_id = str(n)
+                tbl_v6.add_row([route_id, r['proto'],
+                            r['recv-q'], r['send-q'],
+                            r['local address'], r['foreign address'],
+                            r['state']])
+            route_s = route_s + tbl_v6.get_string()
+
         max_len = len(max(route_s.splitlines(), key=len))
         header = util.center("Route info", "+", max_len)
         lines.extend([header, route_s])

=== modified file 'tests/unittests/test_distros/test_netconfig.py'
--- tests/unittests/test_distros/test_netconfig.py	2014-10-11 01:54:28 +0000
+++ tests/unittests/test_distros/test_netconfig.py	2014-11-21 22:24:21 +0000
@@ -30,6 +30,24 @@
 iface eth1 inet dhcp
 '''
 
+BASE_NET_CFG_IPV6 = '''
+auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet static
+    address 192.168.1.5
+    netmask 255.255.255.0
+    network 192.168.0.0
+    broadcast 192.168.1.0
+    gateway 192.168.1.254
+
+iface eth0 inet6 static
+    address 2607:f0d0:1002:0011::2
+    netmask 64
+    gateway 2607:f0d0:1002:0011::1
+'''
+
 
 class WriteBuffer(object):
     def __init__(self):
@@ -174,6 +192,81 @@
         self.assertCfgEquals(expected_buf, str(write_buf))
         self.assertEquals(write_buf.mode, 0644)
 
+    def test_write_ipv6_rhel(self):
+        rh_distro = self._get_distro('rhel')
+        write_mock = self.mocker.replace(util.write_file,
+                                         spec=False, passthrough=False)
+        load_mock = self.mocker.replace(util.load_file,
+                                        spec=False, passthrough=False)
+        exists_mock = self.mocker.replace(os.path.isfile,
+                                          spec=False, passthrough=False)
+
+        write_bufs = {}
+
+        def replace_write(filename, content, mode=0644, omode="wb"):
+            buf = WriteBuffer()
+            buf.mode = mode
+            buf.omode = omode
+            buf.write(content)
+            write_bufs[filename] = buf
+
+        exists_mock(mocker.ARGS)
+        self.mocker.count(0, None)
+        self.mocker.result(False)
+
+        load_mock(mocker.ARGS)
+        self.mocker.count(0, None)
+        self.mocker.result('')
+
+        for _i in range(0, 2):
+            write_mock(mocker.ARGS)
+            self.mocker.call(replace_write)
+
+        write_mock(mocker.ARGS)
+        self.mocker.call(replace_write)
+
+        self.mocker.replay()
+        rh_distro.apply_network(BASE_NET_CFG_IPV6, False)
+
+        self.assertEquals(len(write_bufs), 3)
+        self.assertIn('/etc/sysconfig/network-scripts/ifcfg-lo', write_bufs)
+        write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-lo']
+        expected_buf = '''
+DEVICE="lo"
+ONBOOT=yes
+'''
+        self.assertCfgEquals(expected_buf, str(write_buf))
+        self.assertEquals(write_buf.mode, 0644)
+
+        self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth0', write_bufs)
+        write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth0']
+        expected_buf = '''
+DEVICE="eth0"
+BOOTPROTO="static"
+NETMASK="255.255.255.0"
+IPADDR="192.168.1.5"
+ONBOOT=yes
+GATEWAY="192.168.1.254"
+BROADCAST="192.168.1.0"
+IPV6INIT=yes
+IPV6ADDR="2607:f0d0:1002:0011::2"
+IPV6_DEFAULTGW="2607:f0d0:1002:0011::1"
+'''
+        self.assertCfgEquals(expected_buf, str(write_buf))
+        self.assertEquals(write_buf.mode, 0644)
+
+        self.assertIn('/etc/sysconfig/network', write_bufs)
+        write_buf = write_bufs['/etc/sysconfig/network']
+        expected_buf = '''
+# Created by cloud-init v. 0.7
+NETWORKING=yes
+NETWORKING_IPV6=yes
+IPV6_AUTOCONF=no
+'''
+        self.assertCfgEquals(expected_buf, str(write_buf))
+        self.assertEquals(write_buf.mode, 0644)
+
+
     def test_simple_write_freebsd(self):
         fbsd_distro = self._get_distro('freebsd')
         util_mock = self.mocker.replace(util.write_file,


Follow ups