cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #00566
[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