Skip to content

Commit

Permalink
Allow to install an host with IPv6 management interface
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
benjamreis committed Dec 20, 2022
1 parent 65259e0 commit 156f470
Show file tree
Hide file tree
Showing 6 changed files with 399 additions and 155 deletions.
15 changes: 11 additions & 4 deletions backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1495,15 +1495,16 @@ def configureNetworking(mounts, admin_iface, admin_bridge, admin_config, hn_conf
print >>mc, "NETMASK='%s'" % admin_config.netmask
if admin_config.gateway:
print >>mc, "GATEWAY='%s'" % admin_config.gateway
if manual_nameservers:
print >>mc, "DNS='%s'" % (','.join(nameservers),)
if domain:
print >>mc, "DOMAIN='%s'" % domain
print >>mc, "MODEV6='%s'" % netinterface.NetInterface.getModeStr(admin_config.modev6)
if admin_config.modev6 == netinterface.NetInterface.Static:
print >>mc, "IPv6='%s'" % admin_config.ipv6addr
if admin_config.ipv6_gateway:
print >>mc, "IPv6_GATEWAY='%s'" % admin_config.ipv6_gateway
if admin_config.valid() and not admin_config.isDHCP():
if manual_nameservers:
print >>mc, "DNS='%s'" % (','.join(nameservers),)
if domain:
print >>mc, "DOMAIN='%s'" % domain
if admin_config.vlan:
print >>mc, "VLAN='%d'" % admin_config.vlan
mc.close()
Expand Down Expand Up @@ -1545,12 +1546,18 @@ def configureNetworking(mounts, admin_iface, admin_bridge, admin_config, hn_conf
# now we need to write /etc/sysconfig/network
nfd = open("%s/etc/sysconfig/network" % mounts["root"], "w")
nfd.write("NETWORKING=yes\n")
ipv6_conf = open("%s/etc/sysctl.d/91-net-ipv6.conf" % mounts["root"], "w")
if admin_config.modev6:
nfd.write("NETWORKING_IPV6=yes\n")
util.runCmd2(['chroot', mounts['root'], 'systemctl', 'enable', 'ip6tables'])
for i in ['all', 'default']:
ipv6_conf.write('net.ipv6.conf.%s.disable_ipv6=0\n' % i)
else:
nfd.write("NETWORKING_IPV6=no\n")
for i in ['all', 'default']:
ipv6_conf.write('net.ipv6.conf.%s.disable_ipv6=1\n' % i)
netutil.disable_ipv6_module(mounts["root"])
ipv6_conf.close()
nfd.write("IPV6_AUTOCONF=no\n")
nfd.write('NTPSERVERARGS="iburst prefer"\n')
nfd.close()
Expand Down
80 changes: 55 additions & 25 deletions netinterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class NetInterface:
Autoconf = 3

def __init__(self, mode, hwaddr, ipaddr=None, netmask=None, gateway=None,
dns=None, domain=None, vlan=None):
assert mode is None or mode == self.Static or mode == self.DHCP
dns=None, domain=None, vlan=None, ipv6=False):
assert mode in [None, self.Static, self.DHCP, self.Autoconf]
if ipaddr == '':
ipaddr = None
if netmask == '':
Expand All @@ -40,26 +40,29 @@ def __init__(self, mode, hwaddr, ipaddr=None, netmask=None, gateway=None,
assert ipaddr
assert netmask

self.mode = mode
self.hwaddr = hwaddr
if mode == self.Static:
self.ipaddr = ipaddr
self.netmask = netmask
self.gateway = gateway
self.dns = dns
self.domain = domain
else:
if ipv6:
self.mode = None
self.ipaddr = None
self.netmask = None
self.gateway = None
self.dns = None
self.domain = None
self.vlan = vlan

# Initialise IPv6 to None.
self.modev6 = None
self.ipv6addr = None
self.ipv6_gateway = None
self.modev6 = mode
self.ipv6addr = ipaddr + "/" + netmask if mode == self.Static else None
self.ipv6_gateway = gateway if mode == self.Static else None
else:
self.modev6 = None
self.ipv6addr = None
self.ipv6_gateway = None

self.mode = mode
self.ipaddr = ipaddr if mode == self.Static else None
self.netmask = netmask if mode == self.Static else None
self.gateway = gateway if mode == self.Static else None

self.dns = dns if mode == self.Static else None
self.domain = domain if mode == self.Static else None
self.vlan = vlan

def __repr__(self):
hw = "hwaddr = '%s' " % self.hwaddr
Expand Down Expand Up @@ -124,7 +127,10 @@ def valid(self):

def isStatic(self):
""" Returns true if a static interface configuration is represented. """
return self.mode == self.Static
return self.mode == self.Static or (self.mode == None and self.modev6 == self.Static)

def isDHCP(self):
return self.mode == self.DHCP or (self.mode == None and self.modev6 == self.DHCP)

def isVlan(self):
return self.vlan is not None
Expand All @@ -143,13 +149,12 @@ def writeDebStyleInterface(self, iface, f):

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

if self.mode == self.DHCP:
f.write("iface %s inet dhcp\n" % iface_vlan)
else:
elif self.mode == self.Static:
# CA-11825: broadcast needs to be determined for non-standard networks
bcast = self.getBroadcast()
f.write("iface %s inet static\n" % iface_vlan)
Expand All @@ -160,32 +165,57 @@ def writeDebStyleInterface(self, iface, f):
if self.gateway:
f.write(" gateway %s\n" % self.gateway)

if self.modev6 == self.DHCP:
f.write("iface %s inet6 dhcp\n" % iface_vlan)
if self.modev6 == self.Autoconf:
f.write("iface %s inet6 auto\n" % iface_vlan)
elif self.modev6 == self.Static:
f.write("iface %s inet6 static\n" % iface_vlan)
f.write(" address %s\n" % self.ipv6addr)
if self.ipv6_gateway:
f.write(" gateway %s\n" % self.ipv6_gateway)

def writeRHStyleInterface(self, iface):
""" Write a RedHat-style configuration entry for this interface to
file object f using interface name iface. """

assert self.modev6 is None
assert self.mode
assert self.modev6 or self.mode
iface_vlan = self.getInterfaceName(iface)

f = open('/etc/sysconfig/network-scripts/ifcfg-%s' % iface_vlan, 'w')
f.write("DEVICE=%s\n" % iface_vlan)
f.write("ONBOOT=yes\n")
if self.mode == self.DHCP:
if self.mode == self.DHCP or self.modev6 == self.DHCP:
f.write("BOOTPROTO=dhcp\n")
f.write("PERSISTENT_DHCLIENT=1\n")
else:
f.write("BOOTPROTO=none\n")

if self.mode == self.Static:
# CA-11825: broadcast needs to be determined for non-standard networks
bcast = self.getBroadcast()
f.write("BOOTPROTO=none\n")
f.write("IPADDR=%s\n" % self.ipaddr)
if bcast is not None:
f.write("BROADCAST=%s\n" % bcast)
f.write("NETMASK=%s\n" % self.netmask)
if self.gateway:
f.write("GATEWAY=%s\n" % self.gateway)

if self.modev6:
f.write("NETWORKING_IPV6=yes\n")
f.write("IPV6INIT=yes\n")
f.write("IPV6_AUTOCONF=yes\n" if self.modev6 == self.Autoconf else "IPV6_AUTOCONF=no\n")
if self.modev6 == self.DHCP:
f.write("DHCPV6C=yes\n")
elif self.modev6 == self.Static:
f.write("IPV6ADDR=%s\n" % self.ipv6addr)
if self.ipv6_gateway:
prefix = self.ipv6addr.split("/")[1]
f.write("IPV6_DEFAULTGW=%s/%s\n" % (self.ipv6_gateway, prefix))

if self.vlan:
f.write("VLAN=yes\n")

f.close()


Expand Down
20 changes: 9 additions & 11 deletions netutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def writeResolverFile(configuration, filename):

for iface in configuration:
settings = configuration[iface]
if settings.isStatic() and settings.dns:
if not settings.isDHCP() and settings.dns:
if settings.dns:
for server in settings.dns:
outfile.write("nameserver %s\n" % server)
Expand Down Expand Up @@ -137,7 +137,11 @@ def interfaceUp(interface):
if rc != 0:
return False
inets = filter(lambda x: x.startswith(" inet "), out.split("\n"))
return len(inets) == 1
if len(inets) == 1:
return True

inet6s = filter(lambda x: x.startswith(" inet6 "), out.split("\n"))
return len(inet6s) > 1 # Not just the fe80:: address

# work out if a link is up:
def linkUp(interface):
Expand Down Expand Up @@ -226,15 +230,9 @@ def valid_vlan(vlan):
return True

def valid_ip_addr(addr):
if not re.match('^\d+\.\d+\.\d+\.\d+$', addr):
return False
els = addr.split('.')
if len(els) != 4:
return False
for el in els:
if int(el) > 255:
return False
return True
ipv4_re = '^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}'
ipv6_re = '^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))'
return re.match(ipv4_re, addr) or re.match(ipv6_re, addr)

def network(ipaddr, netmask):
ip = map(int,ipaddr.split('.',3))
Expand Down
8 changes: 5 additions & 3 deletions tui/installer/screens.py
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,8 @@ def ns_callback((enabled, )):
for entry in [ns1_entry, ns2_entry, ns3_entry]:
entry.setFlags(FLAG_DISABLED, enabled)

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

# HOSTNAME:
hn_title = Textbox(len("Hostname Configuration"), 1, "Hostname Configuration")
Expand Down Expand Up @@ -935,7 +936,7 @@ def nsvalue(answers, id):
answers['manual-nameservers'][1].append(ns2_entry.value())
if ns3_entry.value() != '':
answers['manual-nameservers'][1].append(ns3_entry.value())
if 'net-admin-configuration' in answers and answers['net-admin-configuration'].isStatic():
if 'net-admin-configuration' in answers and answers['net-admin-configuration'].valid() and not answers['net-admin-configuration'].isDHCP():
answers['net-admin-configuration'].dns = answers['manual-nameservers'][1]
else:
answers['manual-nameservers'] = (False, None)
Expand Down Expand Up @@ -1036,7 +1037,8 @@ def dhcp_change():
for x in [ ntp1_field, ntp2_field, ntp3_field ]:
x.setFlags(FLAG_DISABLED, not dhcp_cb.value())

hide_cb = answers['net-admin-configuration'].isStatic()
admin_config = answers['net-admin-configuration']
hide_cb = admin_config.valid() and not admin_config.isDHCP()

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

0 comments on commit 156f470

Please sign in to comment.