Skip to content

Commit 9b0f0bd

Browse files
committed
Allow to install an host with IPv6 management interface
Adds a screen to choose between IPv4, IPv6 and dual stack (with IPv4 as primary address type for management). Write network conf file for IPv6 so that netinstall work in IPv6's modes Configure the host with an IPv6 management interface Signed-off-by: BenjiReis <benjamin.reis@vates.fr>
1 parent cc21554 commit 9b0f0bd

6 files changed

Lines changed: 347 additions & 180 deletions

File tree

backend.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,15 +1503,15 @@ def configureNetworking(mounts, admin_iface, admin_bridge, admin_config, hn_conf
15031503
print >>mc, "NETMASK='%s'" % admin_config.netmask
15041504
if admin_config.gateway:
15051505
print >>mc, "GATEWAY='%s'" % admin_config.gateway
1506-
if manual_nameservers:
1507-
print >>mc, "DNS='%s'" % (','.join(nameservers),)
1508-
if domain:
1509-
print >>mc, "DOMAIN='%s'" % domain
15101506
print >>mc, "MODEV6='%s'" % netinterface.NetInterface.getModeStr(admin_config.modev6)
15111507
if admin_config.modev6 == netinterface.NetInterface.Static:
15121508
print >>mc, "IPv6='%s'" % admin_config.ipv6addr
15131509
if admin_config.ipv6_gateway:
15141510
print >>mc, "IPv6_GATEWAY='%s'" % admin_config.ipv6_gateway
1511+
if manual_nameservers:
1512+
print >>mc, "DNS='%s'" % (','.join(nameservers),)
1513+
if domain:
1514+
print >>mc, "DOMAIN='%s'" % domain
15151515
if admin_config.vlan:
15161516
print >>mc, "VLAN='%d'" % admin_config.vlan
15171517
mc.close()
@@ -1553,12 +1553,17 @@ def configureNetworking(mounts, admin_iface, admin_bridge, admin_config, hn_conf
15531553
# now we need to write /etc/sysconfig/network
15541554
nfd = open("%s/etc/sysconfig/network" % mounts["root"], "w")
15551555
nfd.write("NETWORKING=yes\n")
1556-
if admin_config.modev6:
1557-
nfd.write("NETWORKING_IPV6=yes\n")
1558-
util.runCmd2(['chroot', mounts['root'], 'systemctl', 'enable', 'ip6tables'])
1559-
else:
1560-
nfd.write("NETWORKING_IPV6=no\n")
1561-
netutil.disable_ipv6_module(mounts["root"])
1556+
with open("%s/etc/sysctl.d/91-net-ipv6.conf" % mounts["root"], "w") as ipv6_conf:
1557+
if admin_config.modev6:
1558+
nfd.write("NETWORKING_IPV6=yes\n")
1559+
util.runCmd2(['chroot', mounts['root'], 'systemctl', 'enable', 'ip6tables'])
1560+
for i in ['all', 'default']:
1561+
ipv6_conf.write('net.ipv6.conf.%s.disable_ipv6=0\n' % i)
1562+
else:
1563+
nfd.write("NETWORKING_IPV6=no\n")
1564+
for i in ['all', 'default']:
1565+
ipv6_conf.write('net.ipv6.conf.%s.disable_ipv6=1\n' % i)
1566+
netutil.disable_ipv6_module(mounts["root"])
15621567
nfd.write("IPV6_AUTOCONF=no\n")
15631568
nfd.write('NTPSERVERARGS="iburst prefer"\n')
15641569
nfd.close()

netinterface.py

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def getTextOrNone(nodelist):
1616
rc = rc + node.data
1717
return rc == "" and None or rc.strip().encode()
1818

19-
class NetInterface:
19+
class NetInterface(object):
2020
""" Represents the configuration of a network interface. """
2121

2222
Static = 1
@@ -25,7 +25,7 @@ class NetInterface:
2525

2626
def __init__(self, mode, hwaddr, ipaddr=None, netmask=None, gateway=None,
2727
dns=None, domain=None, vlan=None):
28-
assert mode is None or mode == self.Static or mode == self.DHCP
28+
assert mode in [None, self.Static, self.DHCP, self.Autoconf]
2929
if ipaddr == '':
3030
ipaddr = None
3131
if netmask == '':
@@ -36,31 +36,25 @@ def __init__(self, mode, hwaddr, ipaddr=None, netmask=None, gateway=None,
3636
dns = None
3737
elif isinstance(dns, str):
3838
dns = [ dns ]
39-
if mode == self.Static:
40-
assert ipaddr
41-
assert netmask
39+
is_static = mode == self.Static
40+
if is_static:
41+
assert ipaddr and netmask
4242

43-
self.mode = mode
4443
self.hwaddr = hwaddr
45-
if mode == self.Static:
46-
self.ipaddr = ipaddr
47-
self.netmask = netmask
48-
self.gateway = gateway
49-
self.dns = dns
50-
self.domain = domain
51-
else:
52-
self.ipaddr = None
53-
self.netmask = None
54-
self.gateway = None
55-
self.dns = None
56-
self.domain = None
57-
self.vlan = vlan
5844

59-
# Initialise IPv6 to None.
6045
self.modev6 = None
6146
self.ipv6addr = None
6247
self.ipv6_gateway = None
6348

49+
self.mode = mode
50+
self.ipaddr = ipaddr if is_static else None
51+
self.netmask = netmask if is_static else None
52+
self.gateway = gateway if is_static else None
53+
54+
self.dns = dns if is_static else None
55+
self.domain = domain if is_static else None
56+
self.vlan = vlan
57+
6458
def __repr__(self):
6559
hw = "hwaddr = '%s' " % self.hwaddr
6660

@@ -124,7 +118,10 @@ def valid(self):
124118

125119
def isStatic(self):
126120
""" Returns true if a static interface configuration is represented. """
127-
return self.mode == self.Static
121+
return self.mode == self.Static or (self.mode == None and self.modev6 == self.Static)
122+
123+
def isDHCP(self):
124+
return self.mode == self.DHCP or (self.mode == None and self.modev6 == self.DHCP)
128125

129126
def isVlan(self):
130127
return self.vlan is not None
@@ -143,13 +140,12 @@ def writeDebStyleInterface(self, iface, f):
143140

144141
# Debian style interfaces are only used for the installer; dom0 only uses CentOS style
145142
# IPv6 is only enabled through answerfiles and so is not supported here.
146-
assert self.modev6 is None
147-
assert self.mode
143+
assert self.modev6 or self.mode
148144
iface_vlan = self.getInterfaceName(iface)
149145

150146
if self.mode == self.DHCP:
151147
f.write("iface %s inet dhcp\n" % iface_vlan)
152-
else:
148+
elif self.mode == self.Static:
153149
# CA-11825: broadcast needs to be determined for non-standard networks
154150
bcast = self.getBroadcast()
155151
f.write("iface %s inet static\n" % iface_vlan)
@@ -160,12 +156,21 @@ def writeDebStyleInterface(self, iface, f):
160156
if self.gateway:
161157
f.write(" gateway %s\n" % self.gateway)
162158

159+
if self.modev6 == self.DHCP:
160+
f.write("iface %s inet6 dhcp\n" % iface_vlan)
161+
if self.modev6 == self.Autoconf:
162+
f.write("iface %s inet6 auto\n" % iface_vlan)
163+
elif self.modev6 == self.Static:
164+
f.write("iface %s inet6 static\n" % iface_vlan)
165+
f.write(" address %s\n" % self.ipv6addr)
166+
if self.ipv6_gateway:
167+
f.write(" gateway %s\n" % self.ipv6_gateway)
168+
163169
def writeRHStyleInterface(self, iface):
164170
""" Write a RedHat-style configuration entry for this interface to
165171
file object f using interface name iface. """
166172

167-
assert self.modev6 is None
168-
assert self.mode
173+
assert self.modev6 or self.mode
169174
iface_vlan = self.getInterfaceName(iface)
170175

171176
f = open('/etc/sysconfig/network-scripts/ifcfg-%s' % iface_vlan, 'w')
@@ -175,17 +180,38 @@ def writeRHStyleInterface(self, iface):
175180
f.write("BOOTPROTO=dhcp\n")
176181
f.write("PERSISTENT_DHCLIENT=1\n")
177182
else:
183+
f.write("BOOTPROTO=none\n")
184+
185+
if self.mode == self.Static:
178186
# CA-11825: broadcast needs to be determined for non-standard networks
179187
bcast = self.getBroadcast()
180-
f.write("BOOTPROTO=none\n")
181188
f.write("IPADDR=%s\n" % self.ipaddr)
182189
if bcast is not None:
183190
f.write("BROADCAST=%s\n" % bcast)
184191
f.write("NETMASK=%s\n" % self.netmask)
185192
if self.gateway:
186193
f.write("GATEWAY=%s\n" % self.gateway)
194+
195+
if self.modev6:
196+
with open('/etc/sysconfig/network', 'w') as net_conf:
197+
net_conf.write("NETWORKING_IPV6=yes\n")
198+
f.write("IPV6INIT=yes\n")
199+
f.write("IPV6_DEFROUTE=yes\n")
200+
f.write("IPV6_DEFAULTDEV=%s\n" % iface_vlan)
201+
f.write("IPV6_AUTOCONF=yes\n" if self.modev6 == self.Autoconf else "IPV6_AUTOCONF=no\n")
202+
203+
if self.modev6 == self.DHCP:
204+
f.write("DHCPV6C=yes\n")
205+
f.write("PERSISTENT_DHCLIENT_IPV6=yes\n")
206+
f.write("IPV6_FORCE_ACCEPT_RA=yes\n")
207+
elif self.modev6 == self.Static:
208+
f.write("IPV6ADDR=%s\n" % self.ipv6addr)
209+
if self.ipv6_gateway:
210+
f.write("IPV6_DEFAULTGW=%s\n" % (self.ipv6_gateway))
211+
187212
if self.vlan:
188213
f.write("VLAN=yes\n")
214+
189215
f.close()
190216

191217

@@ -348,3 +374,21 @@ def loadFromNetDb(jdata, hwaddr):
348374

349375
nic.addIPv6(modev6, ipv6addr, gatewayv6)
350376
return nic
377+
378+
class NetInterfaceV6(NetInterface):
379+
def __init__(self, mode, hwaddr, ipaddr=None, netmask=None, gateway=None, dns=None, domain=None, vlan=None):
380+
super(NetInterfaceV6, self).__init__(None, hwaddr, None, None, None, None, None, vlan)
381+
382+
is_static = mode == self.Static
383+
ipv6addr = None
384+
if is_static:
385+
assert ipaddr and netmask
386+
ipv6addr = ipaddr + "/" + netmask
387+
if dns == '':
388+
dns = None
389+
elif isinstance(dns, str):
390+
dns = [ dns ]
391+
self.dns = dns
392+
self.domain = domain
393+
394+
self.addIPv6(mode, ipv6addr=ipv6addr, ipv6gw=gateway)

netutil.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
import diskutil
55
import util
66
import re
7+
import socket
78
import subprocess
89
import time
910
import errno
1011
from xcp import logger
1112
from xcp.net.biosdevname import all_devices_all_names
12-
from socket import inet_ntoa
1313
from struct import pack
1414

1515
class NIC:
@@ -92,7 +92,7 @@ def writeResolverFile(configuration, filename):
9292

9393
for iface in configuration:
9494
settings = configuration[iface]
95-
if settings.isStatic() and settings.dns:
95+
if not settings.isDHCP() and settings.dns:
9696
if settings.dns:
9797
for server in settings.dns:
9898
outfile.write("nameserver %s\n" % server)
@@ -137,7 +137,11 @@ def interfaceUp(interface):
137137
if rc != 0:
138138
return False
139139
inets = filter(lambda x: x.startswith(" inet "), out.split("\n"))
140-
return len(inets) == 1
140+
if len(inets) == 1:
141+
return True
142+
143+
inet6s = filter(lambda x: x.startswith(" inet6 "), out.split("\n"))
144+
return len(inet6s) > 1 # Not just the fe80:: address
141145

142146
# work out if a link is up:
143147
def linkUp(interface):
@@ -225,16 +229,21 @@ def valid_vlan(vlan):
225229
return False
226230
return True
227231

228-
def valid_ip_addr(addr):
229-
if not re.match('^\d+\.\d+\.\d+\.\d+$', addr):
230-
return False
231-
els = addr.split('.')
232-
if len(els) != 4:
232+
def valid_ip_address_family(addr, family):
233+
try:
234+
socket.inet_pton(family, addr)
235+
return True
236+
except socket.error:
233237
return False
234-
for el in els:
235-
if int(el) > 255:
236-
return False
237-
return True
238+
239+
def valid_ipv4_addr(addr):
240+
return valid_ip_address_family(addr, socket.AF_INET)
241+
242+
def valid_ipv6_addr(addr):
243+
return valid_ip_address_family(addr, socket.AF_INET6)
244+
245+
def valid_ip_addr(addr):
246+
return valid_ipv4_addr(addr) or valid_ipv6_addr(addr)
238247

239248
def network(ipaddr, netmask):
240249
ip = map(int,ipaddr.split('.',3))
@@ -246,7 +255,7 @@ def prefix2netmask(mask):
246255
bits = 0
247256
for i in xrange(32-mask, 32):
248257
bits |= (1 << i)
249-
return inet_ntoa(pack('>I', bits))
258+
return socket.inet_ntoa(pack('>I', bits))
250259

251260
class NetDevices:
252261
def __init__(self):

tui/installer/screens.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,8 @@ def ns_callback((enabled, )):
805805
for entry in [ns1_entry, ns2_entry, ns3_entry]:
806806
entry.setFlags(FLAG_DISABLED, enabled)
807807

808-
hide_rb = answers['net-admin-configuration'].isStatic()
808+
admin_config = answers['net-admin-configuration']
809+
hide_rb = admin_config.valid() and not admin_config.isDHCP()
809810

810811
# HOSTNAME:
811812
hn_title = Textbox(len("Hostname Configuration"), 1, "Hostname Configuration")
@@ -935,8 +936,9 @@ def nsvalue(answers, id):
935936
answers['manual-nameservers'][1].append(ns2_entry.value())
936937
if ns3_entry.value() != '':
937938
answers['manual-nameservers'][1].append(ns3_entry.value())
938-
if 'net-admin-configuration' in answers and answers['net-admin-configuration'].isStatic():
939-
answers['net-admin-configuration'].dns = answers['manual-nameservers'][1]
939+
admin_config = answers.get('net-admin-configuration')
940+
if admin_config is not None and admin_config.valid() and not admin_config.isDHCP():
941+
admin_config.dns = answers['manual-nameservers'][1]
940942
else:
941943
answers['manual-nameservers'] = (False, None)
942944

@@ -1036,7 +1038,8 @@ def dhcp_change():
10361038
for x in [ ntp1_field, ntp2_field, ntp3_field ]:
10371039
x.setFlags(FLAG_DISABLED, not dhcp_cb.value())
10381040

1039-
hide_cb = answers['net-admin-configuration'].isStatic()
1041+
admin_config = answers['net-admin-configuration']
1042+
hide_cb = admin_config.valid() and not admin_config.isDHCP()
10401043

10411044
gf = GridFormHelp(tui.screen, 'NTP Configuration', 'ntpconf', 1, 4)
10421045
text = TextboxReflowed(60, "Please specify details of the NTP servers you wish to use (e.g. pool.ntp.org)?")

0 commit comments

Comments
 (0)