IPv6 support for rhel distro

When the ubuntu networking info file has ipv6 addresses inside
it we need to make sure that we parse that information out and
place it (at least) in the rhel network configuration writing.

In later patches the other distros that use this parsed network
configuration will likely also need to be updated (ubuntu and
debian already should function as expected with regard to ipv6
support).
This commit is contained in:
Joshua Harlow 2014-11-24 16:30:00 -08:00
commit a189df34f3
4 changed files with 228 additions and 33 deletions

View File

@ -114,6 +114,10 @@ def translate_network(settings):
if 'iface' not in info: if 'iface' not in info:
continue continue
iface_details = info['iface'].split(None) iface_details = info['iface'].split(None)
# Check if current device *may* have an ipv6 IP
use_ipv6 = False
if 'inet6' in iface_details:
use_ipv6 = True
dev_name = None dev_name = None
if len(iface_details) >= 1: if len(iface_details) >= 1:
dev = iface_details[0].strip().lower() dev = iface_details[0].strip().lower()
@ -122,6 +126,7 @@ def translate_network(settings):
if not dev_name: if not dev_name:
continue continue
iface_info = {} iface_info = {}
iface_info['ipv6'] = {}
if len(iface_details) >= 3: if len(iface_details) >= 3:
proto_type = iface_details[2].strip().lower() proto_type = iface_details[2].strip().lower()
# Seems like this can be 'loopback' which we don't # Seems like this can be 'loopback' which we don't
@ -129,35 +134,50 @@ def translate_network(settings):
if proto_type in ['dhcp', 'static']: if proto_type in ['dhcp', 'static']:
iface_info['bootproto'] = proto_type iface_info['bootproto'] = proto_type
# These can just be copied over # These can just be copied over
for k in ['netmask', 'address', 'gateway', 'broadcast']: if use_ipv6:
if k in info: for k in ['address', 'gateway']:
val = info[k].strip().lower() if k in info:
if val: val = info[k].strip().lower()
iface_info[k] = val if val:
# Name server info provided?? iface_info['ipv6'][k] = val
if 'dns-nameservers' in info: else:
iface_info['dns-nameservers'] = info['dns-nameservers'].split() for k in ['netmask', 'address', 'gateway', 'broadcast']:
# Name server search info provided?? if k in info:
if 'dns-search' in info: val = info[k].strip().lower()
iface_info['dns-search'] = info['dns-search'].split() if val:
# Is any mac address spoofing going on?? iface_info[k] = val
if 'hwaddress' in info: # Name server info provided??
hw_info = info['hwaddress'].lower().strip() if 'dns-nameservers' in info:
hw_split = hw_info.split(None, 1) iface_info['dns-nameservers'] = info['dns-nameservers'].split()
if len(hw_split) == 2 and hw_split[0].startswith('ether'): # Name server search info provided??
hw_addr = hw_split[1] if 'dns-search' in info:
if hw_addr: iface_info['dns-search'] = info['dns-search'].split()
iface_info['hwaddress'] = hw_addr # Is any mac address spoofing going on??
real_ifaces[dev_name] = iface_info 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, so we need to
# 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' # Check for those that should be started on boot via 'auto'
for (cmd, args) in entries: for (cmd, args) in entries:
args = args.split(None)
if not args:
continue
dev_name = args[0].strip().lower()
if cmd == 'auto': if cmd == 'auto':
# Seems like auto can be like 'auto eth0 eth0:1' so just get the # Seems like auto can be like 'auto eth0 eth0:1' so just get the
# first part out as the device name # first part out as the device name
args = args.split(None)
if not args:
continue
dev_name = args[0].strip().lower()
if dev_name in real_ifaces: if dev_name in real_ifaces:
real_ifaces[dev_name]['auto'] = True real_ifaces[dev_name]['auto'] = True
if cmd == 'iface' and 'inet6' in args:
real_ifaces[dev_name]['inet6'] = True
return real_ifaces return real_ifaces

View File

@ -71,6 +71,7 @@ class Distro(distros.Distro):
nameservers = [] nameservers = []
searchservers = [] searchservers = []
dev_names = entries.keys() dev_names = entries.keys()
use_ipv6 = False
for (dev, info) in entries.iteritems(): for (dev, info) in entries.iteritems():
net_fn = self.network_script_tpl % (dev) net_fn = self.network_script_tpl % (dev)
net_cfg = { net_cfg = {
@ -83,6 +84,13 @@ class Distro(distros.Distro):
'MACADDR': info.get('hwaddress'), 'MACADDR': info.get('hwaddress'),
'ONBOOT': _make_sysconfig_bool(info.get('auto')), '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) rhel_util.update_sysconfig_file(net_fn, net_cfg)
if 'dns-nameservers' in info: if 'dns-nameservers' in info:
nameservers.extend(info['dns-nameservers']) nameservers.extend(info['dns-nameservers'])
@ -95,6 +103,10 @@ class Distro(distros.Distro):
net_cfg = { net_cfg = {
'NETWORKING': _make_sysconfig_bool(True), '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) rhel_util.update_sysconfig_file(self.network_conf_fn, net_cfg)
return dev_names return dev_names

View File

@ -72,6 +72,7 @@ def netdev_info(empty=""):
"bcast:": "bcast", "broadcast": "bcast", "bcast:": "bcast", "broadcast": "bcast",
"mask:": "mask", "netmask": "mask", "mask:": "mask", "netmask": "mask",
"hwaddr": "hwaddr", "ether": "hwaddr", "hwaddr": "hwaddr", "ether": "hwaddr",
"scope": "scope",
} }
for origfield, field in ifconfigfields.items(): for origfield, field in ifconfigfields.items():
target = "%s%s" % (field, fieldpost) target = "%s%s" % (field, fieldpost)
@ -96,7 +97,12 @@ def netdev_info(empty=""):
def route_info(): def route_info():
(route_out, _err) = util.subp(["netstat", "-rn"]) (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:] entries = route_out.splitlines()[1:]
for line in entries: for line in entries:
if not line: if not line:
@ -132,7 +138,26 @@ def route_info():
'iface': toks[7], '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 return routes
@ -156,10 +181,12 @@ def netdev_pformat():
lines.append(util.center("Net device info failed", '!', 80)) lines.append(util.center("Net device info failed", '!', 80))
netdev = None netdev = None
if netdev is not None: if netdev is not None:
fields = ['Device', 'Up', 'Address', 'Mask', 'Hw-Address'] fields = ['Device', 'Up', 'Address', 'Mask', 'Scope', 'Hw-Address']
tbl = PrettyTable(fields) tbl = PrettyTable(fields)
for (dev, d) in netdev.iteritems(): 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() netdev_s = tbl.get_string()
max_len = len(max(netdev_s.splitlines(), key=len)) max_len = len(max(netdev_s.splitlines(), key=len))
header = util.center("Net device info", "+", max_len) header = util.center("Net device info", "+", max_len)
@ -176,15 +203,30 @@ def route_pformat():
util.logexc(LOG, "Route info failed: %s" % e) util.logexc(LOG, "Route info failed: %s" % e)
routes = None routes = None
if routes is not None: if routes is not None:
fields = ['Route', 'Destination', 'Gateway', fields_v4 = ['Route', 'Destination', 'Gateway',
'Genmask', 'Interface', 'Flags'] '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) route_id = str(n)
tbl.add_row([route_id, r['destination'], tbl_v4.add_row([route_id, r['destination'],
r['gateway'], r['genmask'], r['gateway'], r['genmask'],
r['iface'], r['flags']]) 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)) max_len = len(max(route_s.splitlines(), key=len))
header = util.center("Route info", "+", max_len) header = util.center("Route info", "+", max_len)
lines.extend([header, route_s]) lines.extend([header, route_s])

View File

@ -30,6 +30,36 @@ auto eth1
iface eth1 inet dhcp 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
iface eth1 inet static
address 192.168.1.6
netmask 255.255.255.0
network 192.168.0.0
broadcast 192.168.1.0
gateway 192.168.1.254
iface eth1 inet6 static
address 2607:f0d0:1002:0011::3
netmask 64
gateway 2607:f0d0:1002:0011::1
'''
class WriteBuffer(object): class WriteBuffer(object):
def __init__(self): def __init__(self):
@ -174,6 +204,97 @@ NETWORKING=yes
self.assertCfgEquals(expected_buf, str(write_buf)) self.assertCfgEquals(expected_buf, str(write_buf))
self.assertEquals(write_buf.mode, 0644) 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, 3):
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), 4)
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-scripts/ifcfg-eth1', write_bufs)
write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth1']
expected_buf = '''
DEVICE="eth1"
BOOTPROTO="static"
NETMASK="255.255.255.0"
IPADDR="192.168.1.6"
ONBOOT=no
GATEWAY="192.168.1.254"
BROADCAST="192.168.1.0"
IPV6INIT=yes
IPV6ADDR="2607:f0d0:1002:0011::3"
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): def test_simple_write_freebsd(self):
fbsd_distro = self._get_distro('freebsd') fbsd_distro = self._get_distro('freebsd')
util_mock = self.mocker.replace(util.write_file, util_mock = self.mocker.replace(util.write_file,