From 44ad1741fbe5b2060a1ee78434250be36d39d51f Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 10:06:07 +0200 Subject: [PATCH 01/11] Write `manual_nameservers` and `domain` for IPv6 too Both fields are used for the 2 IP families and were writen in an IPv4 specific block. Signed-off-by: BenjiReis --- backend.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend.py b/backend.py index 3c499329..bae81467 100644 --- a/backend.py +++ b/backend.py @@ -1535,15 +1535,15 @@ 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 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() From c27fe4294e7c94b8a4951fc424ff74461bd5cbe7 Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 10:36:16 +0200 Subject: [PATCH 02/11] Add `NetInterfaceV6` to init an IPv6 interface Inherits from `NetInterface` to mutualize the code Signed-off-by: BenjiReis --- netinterface.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/netinterface.py b/netinterface.py index 0cdcad47..b0ea60ed 100644 --- a/netinterface.py +++ b/netinterface.py @@ -16,7 +16,7 @@ def getTextOrNone(nodelist): rc = rc + node.data return rc == "" and None or rc.strip().encode() -class NetInterface: +class NetInterface(Object): """ Represents the configuration of a network interface. """ Static = 1 @@ -348,3 +348,22 @@ def loadFromNetDb(jdata, hwaddr): nic.addIPv6(modev6, ipv6addr, gatewayv6) return nic + +class NetInterfaceV6(NetInterface): + def __init__(self, mode, hwaddr, ipaddr=None, netmask=None, gateway=None, dns=None, domain=None, vlan=None): + super(NetInterfaceV6, self).__init__(None, hwaddr, None, None, None, None, None, vlan) + + ipv6addr = None + if mode == self.Static: + assert ipaddr + assert netmask + + ipv6addr = ipaddr + "/" + netmask + if dns == '': + dns = None + elif isinstance(dns, str): + dns = [ dns ] + self.dns = dns + self.domain = domain + + self.addIPv6(mode, ipv6addr=ipv6addr, ipv6gw=gateway) From c0af979eb0d2de0a36b4b09b2bb290ee46def96d Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 10:40:33 +0200 Subject: [PATCH 03/11] IP mode helpers take IPv6 into account when no IPv4 configured IPv4 has priority on IPv6 as when both are configured, the host's management interface's primary address type will be IPv4 Signed-off-by: BenjiReis --- netinterface.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/netinterface.py b/netinterface.py index b0ea60ed..7a50561c 100644 --- a/netinterface.py +++ b/netinterface.py @@ -124,7 +124,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 From d01f0c9fc41cd5b2afe9b665883cdda44aa2b23a Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 10:46:12 +0200 Subject: [PATCH 04/11] Use `socket.inet_pton` to validate IP Add family specific validator as well Signed-off-by: BenjiReis --- netutil.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/netutil.py b/netutil.py index 898fe0ea..234cbe4f 100644 --- a/netutil.py +++ b/netutil.py @@ -4,12 +4,12 @@ import diskutil import util import re +import socket import subprocess import time import errno from xcp import logger from xcp.net.biosdevname import all_devices_all_names -from socket import inet_ntoa from struct import pack class NIC: @@ -225,16 +225,21 @@ def valid_vlan(vlan): return False 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: +def valid_ip_address_family(addr, family): + try: + socket.inet_pton(family, addr) + return True + except socket.error: return False - for el in els: - if int(el) > 255: - return False - return True + +def valid_ipv4_addr(addr): + return valid_ip_address_family(addr, socket.AF_INET) + +def valid_ipv6_addr(addr): + return valid_ip_address_family(addr, socket.AF_INET6) + +def valid_ip_addr(addr): + return valid_ipv4_addr(addr) or valid_ipv6_addr(addr) def network(ipaddr, netmask): ip = map(int,ipaddr.split('.',3)) @@ -246,7 +251,7 @@ def prefix2netmask(mask): bits = 0 for i in xrange(32-mask, 32): bits |= (1 << i) - return inet_ntoa(pack('>I', bits)) + return socket.inet_ntoa(pack('>I', bits)) class NetDevices: def __init__(self): From 0a69216c62d7e9849a2559b06711cb4063d0d825 Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 10:48:11 +0200 Subject: [PATCH 05/11] Take IPv6 addresses into account to determine an interface is up Signed-off-by: BenjiReis --- netutil.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/netutil.py b/netutil.py index 234cbe4f..4d4cb6f1 100644 --- a/netutil.py +++ b/netutil.py @@ -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): From d7694a080baa0b7256d66b50c449c5e461cf63ca Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 10:56:47 +0200 Subject: [PATCH 06/11] Enable IPv6 in the host when it is configured Signed-off-by: BenjiReis --- backend.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/backend.py b/backend.py index bae81467..0bf7160a 100644 --- a/backend.py +++ b/backend.py @@ -1585,12 +1585,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") - if admin_config.modev6: + ipv6 = admin_config.modev6 is not None + if ipv6: nfd.write("NETWORKING_IPV6=yes\n") util.runCmd2(['chroot', mounts['root'], 'systemctl', 'enable', 'ip6tables']) else: nfd.write("NETWORKING_IPV6=no\n") netutil.disable_ipv6_module(mounts["root"]) + + with open("%s/etc/sysctl.d/91-net-ipv6.conf" % mounts["root"], "w") as ipv6_conf: + for i in ['all', 'default']: + ipv6_conf.write('net.ipv6.conf.%s.disable_ipv6=%d\n' % (i, int(not ipv6))) + nfd.write("IPV6_AUTOCONF=no\n") nfd.write('NTPSERVERARGS="iburst prefer"\n') nfd.close() From 8e758ff02240249fb037dd044a7d569acf777788 Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 11:16:57 +0200 Subject: [PATCH 07/11] Write IPv6 conf files for the installer Signed-off-by: BenjiReis --- netinterface.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/netinterface.py b/netinterface.py index 7a50561c..105e72e8 100644 --- a/netinterface.py +++ b/netinterface.py @@ -167,8 +167,7 @@ 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') @@ -177,7 +176,7 @@ def writeRHStyleInterface(self, iface): if self.mode == self.DHCP: f.write("BOOTPROTO=dhcp\n") f.write("PERSISTENT_DHCLIENT=1\n") - else: + elif self.mode == self.Static: # CA-11825: broadcast needs to be determined for non-standard networks bcast = self.getBroadcast() f.write("BOOTPROTO=none\n") @@ -187,6 +186,27 @@ def writeRHStyleInterface(self, iface): f.write("NETMASK=%s\n" % self.netmask) if self.gateway: f.write("GATEWAY=%s\n" % self.gateway) + + if self.modev6: + with open('/etc/sysconfig/network', 'w') as net_conf: + net_conf.write("NETWORKING_IPV6=yes\n") + f.write("IPV6INIT=yes\n") + f.write("IPV6_DEFROUTE=yes\n") + f.write("IPV6_DEFAULTDEV=%s\n" % iface_vlan) + + if self.modev6 == self.DHCP: + f.write("DHCPV6C=yes\n") + f.write("PERSISTENT_DHCLIENT_IPV6=yes\n") + f.write("IPV6_FORCE_ACCEPT_RA=yes\n") + f.write("IPV6_AUTOCONF=no\n") + elif self.modev6 == self.Static: + f.write("IPV6ADDR=%s\n" % self.ipv6addr) + if self.ipv6_gateway: + f.write("IPV6_DEFAULTGW=%s\n" % (self.ipv6_gateway)) + f.write("IPV6_AUTOCONF=no\n") + elif self.modev6 == self.Autoconf: + f.write("IPV6_AUTOCONF=yes\n") + if self.vlan: f.write("VLAN=yes\n") f.close() From dd56022d4b511622069abae1b3309fe572895c5e Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 11:38:10 +0200 Subject: [PATCH 08/11] Configure IPv6 in TUI Add a screen to choose to configure IPv4, IPv6 or both Display the interface screen for IPv4 and/or IPv6 Signed-off-by: BenjiReis --- tui/network.py | 353 +++++++++++++++++++++++++++++++------------------ 1 file changed, 227 insertions(+), 126 deletions(-) diff --git a/tui/network.py b/tui/network.py index faf87dc9..aff016f9 100644 --- a/tui/network.py +++ b/tui/network.py @@ -9,128 +9,215 @@ from netinterface import * import version import os +import time +import socket from snack import * def get_iface_configuration(nic, txt=None, defaults=None, include_dns=False): - - def use_vlan_cb_change(): - vlan_field.setFlags(FLAG_DISABLED, vlan_cb.value()) - - def dhcp_change(): - for x in [ ip_field, gateway_field, subnet_field, dns_field ]: - x.setFlags(FLAG_DISABLED, not dhcp_rb.selected()) - - gf = GridFormHelp(tui.screen, 'Networking', 'ifconfig', 1, 8) - if txt is None: - txt = "Configuration for %s (%s)" % (nic.name, nic.hwaddr) - text = TextboxReflowed(45, txt) - b = [("Ok", "ok"), ("Back", "back")] - buttons = ButtonBar(tui.screen, b) - - ip_field = Entry(16) - subnet_field = Entry(16) - gateway_field = Entry(16) - dns_field = Entry(16) - vlan_field = Entry(16) - - if defaults and defaults.isStatic(): - # static configuration defined previously - dhcp_rb = SingleRadioButton("Automatic configuration (DHCP)", None, 0) - dhcp_rb.setCallback(dhcp_change, ()) - static_rb = SingleRadioButton("Static configuration:", dhcp_rb, 1) - static_rb.setCallback(dhcp_change, ()) - if defaults.ipaddr: - ip_field.set(defaults.ipaddr) - if defaults.netmask: - subnet_field.set(defaults.netmask) - if defaults.gateway: - gateway_field.set(defaults.gateway) - if defaults.dns: - dns_field.set(defaults.dns[0]) - else: - dhcp_rb = SingleRadioButton("Automatic configuration (DHCP)", None, 1) + def choose_primary_address_type(nic): + gf = GridFormHelp(tui.screen, 'Networking', 'Address type', 1, 8) + txt = "Choose an address type for %s (%s)" % (nic.name, nic.hwaddr) + text = TextboxReflowed(45, txt) + + b = [("Ok", "ok"), ("Back", "back")] + buttons = ButtonBar(tui.screen, b) + + # IPv4 by default + ipv4_rb = SingleRadioButton("IPv4", None, 1) + ipv6_rb = SingleRadioButton("IPv6", ipv4_rb, 0) + dual_rb = SingleRadioButton("Dual stack (IPv4 primary)", ipv6_rb, 0) + + gf.add(text, 0, 0, padding=(0, 0, 0, 1)) + gf.add(ipv4_rb, 0, 2, anchorLeft=True) + gf.add(ipv6_rb, 0, 3, anchorLeft=True) + gf.add(dual_rb, 0, 4, anchorLeft=True) + gf.add(buttons, 0, 5, growx=1) + + loop = True + direction = LEFT_BACKWARDS + address_type = None + while loop: + result = gf.run() + if buttons.buttonPressed(result) == 'back': + loop = False + elif buttons.buttonPressed(result) == 'ok': + value = None + if ipv4_rb.selected(): + value = "ipv4" + elif ipv6_rb.selected(): + value = "ipv6" + elif dual_rb.selected(): + value = "dual" + loop = False + direction = RIGHT_FORWARDS + address_type = value + + tui.screen.popWindow() + return direction, address_type + + def get_ip_configuration(nic, txt, defaults, include_dns, iface_class): + def use_vlan_cb_change(): + vlan_field.setFlags(FLAG_DISABLED, vlan_cb.value()) + + def dhcp_change(): + for x in [ ip_field, gateway_field, subnet_field, dns_field ]: + x.setFlags(FLAG_DISABLED, static_rb.selected()) + + ipv6 = iface_class == NetInterfaceV6 + + gf = GridFormHelp(tui.screen, 'Networking', 'ifconfig', 1, 10) + if txt is None: + txt = "Configuration for %s (%s)" % (nic.name, nic.hwaddr) + text = TextboxReflowed(45, txt) + b = [("Ok", "ok"), ("Back", "back")] + buttons = ButtonBar(tui.screen, b) + + #TODO? Change size for IPv6? If so which size? + ip_field = Entry(16) + subnet_field = Entry(16) + gateway_field = Entry(16) + dns_field = Entry(16) + vlan_field = Entry(16) + + static = bool(defaults and (defaults.modev6 if ipv6 else defaults.mode) == NetInterface.Static) + dhcp_rb = SingleRadioButton("Automatic configuration (DHCP)", None, not static) dhcp_rb.setCallback(dhcp_change, ()) - static_rb = SingleRadioButton("Static configuration:", dhcp_rb, 0) + static_rb = SingleRadioButton("Static configuration:", dhcp_rb, static) static_rb.setCallback(dhcp_change, ()) - ip_field.setFlags(FLAG_DISABLED, False) - subnet_field.setFlags(FLAG_DISABLED, False) - gateway_field.setFlags(FLAG_DISABLED, False) - dns_field.setFlags(FLAG_DISABLED, False) - - vlan_cb = Checkbox("Use VLAN:", defaults.isVlan() if defaults else False) - vlan_cb.setCallback(use_vlan_cb_change, ()) - if defaults and defaults.isVlan(): - vlan_field.set(str(defaults.vlan)) - else: - vlan_field.setFlags(FLAG_DISABLED, False) - - ip_text = Textbox(15, 1, "IP Address:") - subnet_text = Textbox(15, 1, "Subnet mask:") - gateway_text = Textbox(15, 1, "Gateway:") - dns_text = Textbox(15, 1, "Nameserver:") - vlan_text = Textbox(15, 1, "VLAN (1-4094):") - - entry_grid = Grid(2, include_dns and 4 or 3) - entry_grid.setField(ip_text, 0, 0) - entry_grid.setField(ip_field, 1, 0) - entry_grid.setField(subnet_text, 0, 1) - entry_grid.setField(subnet_field, 1, 1) - entry_grid.setField(gateway_text, 0, 2) - entry_grid.setField(gateway_field, 1, 2) - if include_dns: - entry_grid.setField(dns_text, 0, 3) - entry_grid.setField(dns_field, 1, 3) - - vlan_grid = Grid(2, 1) - vlan_grid.setField(vlan_text, 0, 0) - vlan_grid.setField(vlan_field, 1, 0) - - gf.add(text, 0, 0, padding=(0, 0, 0, 1)) - gf.add(dhcp_rb, 0, 2, anchorLeft=True) - gf.add(static_rb, 0, 3, anchorLeft=True) - gf.add(entry_grid, 0, 4, padding=(0, 0, 0, 1)) - gf.add(vlan_cb, 0, 5, anchorLeft=True) - gf.add(vlan_grid, 0, 6, padding=(0, 0, 0, 1)) - gf.add(buttons, 0, 7, growx=1) - - loop = True - while loop: - result = gf.run() - - if buttons.buttonPressed(result) in ['ok', None]: - # validate input - msg = '' - if static_rb.selected(): - if not netutil.valid_ip_addr(ip_field.value()): - msg = 'IP Address' - elif not netutil.valid_ip_addr(subnet_field.value()): - msg = 'Subnet mask' - elif gateway_field.value() != '' and not netutil.valid_ip_addr(gateway_field.value()): - msg = 'Gateway' - elif dns_field.value() != '' and not netutil.valid_ip_addr(dns_field.value()): - msg = 'Nameserver' - if vlan_cb.selected(): - if not netutil.valid_vlan(vlan_field.value()): - msg = 'VLAN' - if msg != '': - tui.progress.OKDialog("Networking", "Invalid %s, please check the field and try again." % msg) + if ipv6: + autoconf_rb = SingleRadioButton("Automatic configuration (Autoconf)", static_rb, 0) + autoconf_rb.setCallback(dhcp_change, ()) + dhcp_change() + + if defaults: + if ipv6: + if defaults.ipv6addr: + ip6addr, netmask = defaults.ipv6addr.split("/") + ip_field.set(ip6addr) + subnet_field.set(netmask) + if defaults.ipv6_gateway: + gateway_field.set(defaults.ipv6_gateway) + else: + if defaults.ipaddr: + ip_field.set(defaults.ipaddr) + if defaults.netmask: + subnet_field.set(defaults.netmask) + if defaults.gateway: + gateway_field.set(defaults.gateway) + + if defaults.dns: + dns_field.set(defaults.dns[0]) + + vlan_cb = Checkbox("Use VLAN:", defaults.isVlan() if defaults else False) + vlan_cb.setCallback(use_vlan_cb_change, ()) + if defaults and defaults.isVlan(): + vlan_field.set(str(defaults.vlan)) + else: + vlan_field.setFlags(FLAG_DISABLED, False) + + ip_msg = "IPv6 Address" if ipv6 else "IP Address" + mask_msg = "CIDR (4-128)" if ipv6 else "Subnet mask" + ip_text = Textbox(15, 1, "%s:" % ip_msg) + subnet_text = Textbox(15, 1, "%s:" % mask_msg) + gateway_text = Textbox(15, 1, "Gateway:") + dns_text = Textbox(15, 1, "Nameserver:") + vlan_text = Textbox(15, 1, "VLAN (1-4094):") + + entry_grid = Grid(2, include_dns and 4 or 3) + entry_grid.setField(ip_text, 0, 0) + entry_grid.setField(ip_field, 1, 0) + entry_grid.setField(subnet_text, 0, 1) + entry_grid.setField(subnet_field, 1, 1) + entry_grid.setField(gateway_text, 0, 2) + entry_grid.setField(gateway_field, 1, 2) + if include_dns: + entry_grid.setField(dns_text, 0, 3) + entry_grid.setField(dns_field, 1, 3) + + vlan_grid = Grid(2, 1) + vlan_grid.setField(vlan_text, 0, 0) + vlan_grid.setField(vlan_field, 1, 0) + + gf.add(text, 0, 0, padding=(0, 0, 0, 1)) + gf.add(dhcp_rb, 0, 2, anchorLeft=True) + gf.add(static_rb, 0, 3, anchorLeft=True) + gf.add(entry_grid, 0, 4, padding=(0, 0, 0, 1)) + if ipv6: + gf.add(autoconf_rb, 0, 5, anchorLeft=True) + # One more line for IPv6 autoconf + gf.add(vlan_cb, 0, 5 + ipv6, anchorLeft=True) + gf.add(vlan_grid, 0, 6 + ipv6, padding=(0, 0, 0, 1)) + gf.add(buttons, 0, 7 + ipv6, growx=1) + + loop = True + ip_family = socket.AF_INET6 if ipv6 else socket.AF_INET + while loop: + result = gf.run() + + if buttons.buttonPressed(result) in ['ok', None]: + # validate input + msg = '' + if static_rb.selected(): + invalid_subnet = int(subnet_field.value()) > 128 or int(subnet_field.value()) < 4 if ipv6 else not netutil.valid_ipv4_addr(subnet_field.value()) + if not netutil.valid_ip_address_family(ip_field.value(), ip_family): + msg = ip_msg + elif invalid_subnet: + msg = mask_msg + elif gateway_field.value() != '' and not netutil.valid_ip_address_family(gateway_field.value(), ip_family): + msg = 'Gateway' + elif dns_field.value() != '' and not netutil.valid_ip_address_family(dns_field.value(), ip_family): + msg = 'Nameserver' + if vlan_cb.selected(): + if not netutil.valid_vlan(vlan_field.value()): + msg = 'VLAN' + if msg != '': + tui.progress.OKDialog("Networking", "Invalid %s, please check the field and try again." % msg) + else: + loop = False else: loop = False + + tui.screen.popWindow() + + if buttons.buttonPressed(result) == 'back': return LEFT_BACKWARDS, None + + vlan_value = int(vlan_field.value()) if vlan_cb.selected() else None + if bool(dhcp_rb.selected()): + answers = iface_class(NetInterface.DHCP, nic.hwaddr, vlan=vlan_value) + elif ipv6 and bool(autoconf_rb.selected()): + answers = iface_class(NetInterface.Autoconf, nic.hwaddr, vlan=vlan_value) else: - loop = False + answers = iface_class(NetInterface.Static, nic.hwaddr, ip_field.value(), + subnet_field.value(), gateway_field.value(), + dns_field.value(), vlan=vlan_value) - tui.screen.popWindow() + return RIGHT_FORWARDS, answers - if buttons.buttonPressed(result) == 'back': return LEFT_BACKWARDS, None + direction, address_type = choose_primary_address_type(nic) + if direction == LEFT_BACKWARDS: + return LEFT_BACKWARDS, None + + answers = None + if address_type in ["ipv4", "dual"]: + direction, answers = get_ip_configuration(nic, txt, defaults, include_dns, NetInterface) + if direction == LEFT_BACKWARDS: + return LEFT_BACKWARDS, None + + if address_type in ["ipv6", "dual"]: + direction, answers_ipv6 = get_ip_configuration(nic, txt, defaults, include_dns, NetInterfaceV6) + if direction == LEFT_BACKWARDS: + return LEFT_BACKWARDS, None + + if answers == None: + answers = answers_ipv6 + else: + answers.modev6 = answers_ipv6.modev6 + answers.ipv6addr = answers_ipv6.ipv6addr + answers.ipv6_gateway = answers_ipv6.ipv6_gateway + if answers_ipv6.dns != None: + answers.dns = answers_ipv6.dns if answers.dns == None else answers.dns + answers_ipv6.dns - vlan_value = int(vlan_field.value()) if vlan_cb.selected() else None - if bool(dhcp_rb.selected()): - answers = NetInterface(NetInterface.DHCP, nic.hwaddr, vlan=vlan_value) - else: - answers = NetInterface(NetInterface.Static, nic.hwaddr, ip_field.value(), - subnet_field.value(), gateway_field.value(), - dns_field.value(), vlan=vlan_value) return RIGHT_FORWARDS, answers def select_netif(text, conf, offer_existing=False, default=None): @@ -286,23 +373,37 @@ def specify_configuration(answers, txt, defaults): ifaceName = conf_dict['config'].getInterfaceName(conf_dict['interface']) netutil.ifdown(ifaceName) - # check that we have *some* network: - if netutil.ifup(ifaceName) != 0 or not netutil.interfaceUp(ifaceName): + def display_error(): tui.progress.clearModelessDialog() tui.progress.OKDialog("Networking", "The network still does not appear to be active. Please check your settings, and try again.") - direction = REPEAT_STEP - else: - if answers and type(answers) == dict: - # write out results - answers[interface_key] = conf_dict['interface'] - answers[config_key] = conf_dict['config'] - # update cache of manual configurations - manual_config = {} - all_dhcp = False - if 'runtime-iface-configuration' in answers: - manual_config = answers['runtime-iface-configuration'][1] - manual_config[conf_dict['interface']] = conf_dict['config'] - answers['runtime-iface-configuration'] = (all_dhcp, manual_config) - tui.progress.clearModelessDialog() + return REPEAT_STEP + + if netutil.ifup(ifaceName) != 0: + return display_error() + + # For Autoconf wait a bit for network setup + try_nb = 10 if conf_dict['config'].modev6 == NetInterface.Autoconf else 0 + while True: + if try_nb == 0 or netutil.interfaceUp(ifaceName): + break + try_nb -= 1 + time.sleep(0.1) + + # check that we have *some* network: + if not netutil.interfaceUp(ifaceName): + return display_error() + + if answers and type(answers) == dict: + # write out results + answers[interface_key] = conf_dict['interface'] + answers[config_key] = conf_dict['config'] + # update cache of manual configurations + manual_config = {} + all_dhcp = False + if 'runtime-iface-configuration' in answers: + manual_config = answers['runtime-iface-configuration'][1] + manual_config[conf_dict['interface']] = conf_dict['config'] + answers['runtime-iface-configuration'] = (all_dhcp, manual_config) + tui.progress.clearModelessDialog() return direction From 2aaf43bad4fbcde7d148656c5b7b0476d9ed70ad Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 11:37:02 +0200 Subject: [PATCH 09/11] Enable manual set of DNS, nameservers and NTP server in autoconf Signed-off-by: BenjiReis --- tui/installer/screens.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tui/installer/screens.py b/tui/installer/screens.py index d6168baa..4270b5c0 100644 --- a/tui/installer/screens.py +++ b/tui/installer/screens.py @@ -888,7 +888,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") @@ -1018,8 +1019,9 @@ 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(): - answers['net-admin-configuration'].dns = answers['manual-nameservers'][1] + admin_config = answers.get('net-admin-configuration') + if admin_config is not None and admin_config.valid() and not admin_config.isDHCP(): + admin_config.dns = answers['manual-nameservers'][1] else: answers['manual-nameservers'] = (False, None) @@ -1119,7 +1121,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)?") From ae01174f486ffb466eebee2128f5d2a766efa3cf Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 11:41:15 +0200 Subject: [PATCH 10/11] Re-enable IPv6 on upgrade when relevant Signed-off-by: BenjiReis --- upgrade.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/upgrade.py b/upgrade.py index ec8d9104..f2bacc6a 100644 --- a/upgrade.py +++ b/upgrade.py @@ -474,6 +474,11 @@ def completeUpgrade(self, mounts, prev_install, target_disk, backup_partnum, log nfd.write("NETWORKING_IPV6=no\n") nfd.close() netutil.disable_ipv6_module(mounts["root"]) + else: + # Enable IPV6 + with open("%s/etc/sysctl.d/91-net-ipv6.conf" % mounts["root"], "w") as ipv6_conf: + for i in ['all', 'default']: + ipv6_conf.write('net.ipv6.conf.%s.disable_ipv6=0\n' % i) # handle the conversion of devices from aacraid to smartpqi primary_disk = self.source.getInventoryValue("PRIMARY_DISK") From 6518d6e105234236d0cc886427b1a7208ce57e27 Mon Sep 17 00:00:00 2001 From: BenjiReis Date: Tue, 30 May 2023 11:43:05 +0200 Subject: [PATCH 11/11] IPv6 in DebStyle net files Signed-off-by: BenjiReis --- netinterface.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/netinterface.py b/netinterface.py index 105e72e8..1cb9d64b 100644 --- a/netinterface.py +++ b/netinterface.py @@ -146,13 +146,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) @@ -163,6 +162,16 @@ 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) + elif 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. """