Rework network config (#1001)

* Update network configuration

* Rework network configuration

* Update documentation

* Fix flake8

* Update

Co-authored-by: Daniel Girtler <girtler.daniel@gmail.com>
Co-authored-by: Anton Hvornum <anton.feeds@gmail.com>
This commit is contained in:
Daniel 2022-03-01 01:57:57 +11:00 committed by GitHub
parent fa87d85708
commit 537b9cab03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 177 additions and 91 deletions

View File

@ -6,5 +6,4 @@ max-complexity = 40
max-line-length = 236
show-source = True
statistics = True
per-file-ignores = __init__.py:F401,F403,F405 simple_menu.py:C901,W503 guided.py:C901
builtins = _
per-file-ignores = __init__.py:F401,F403,F405 simple_menu.py:C901,W503 guided.py:C901 network_configuration.py:F821

View File

@ -123,7 +123,7 @@ def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, erro
print(f" We ignore the entry {element} as it isn't related to any argument")
return config
def get_arguments():
def get_arguments() -> Dict[str, Any]:
""" The handling of parameters from the command line
Is done on following steps:
0) we create a dict to store the arguments and their values
@ -203,13 +203,8 @@ def load_config():
storage['gfx_driver_packages'] = AVAILABLE_GFX_DRIVERS.get(arguments.get('gfx_driver', None), None)
if arguments.get('servers', None) is not None:
storage['_selected_servers'] = arguments.get('servers', None)
if nic_config := arguments.get('nic', {}):
if isinstance(nic_config,str) or nic_config.get('nic', '') == 'Copy ISO network configuration to installation':
arguments['nic'] = {'type': 'iso_config'}
elif 'NetworkManager' in nic_config:
arguments['nic'] = {'type': 'network_manager', 'NetworkManager': True}
else:
arguments['nic'] = {k if k != 'nic' else 'type': v for k, v in nic_config.items()}
if arguments.get('nic', None) is not None:
arguments['nic'] = NetworkConfiguration.parse_arguments(arguments.get('nic'))
def post_process_arguments(arguments):
storage['arguments'] = arguments

View File

@ -35,6 +35,7 @@ __packages__ = ["base", "base-devel", "linux-firmware", "linux", "linux-lts", "l
__accessibility_packages__ = ["brltty", "espeakup", "alsa-utils"]
from .pacman import run_pacman
from .models.network_configuration import NetworkConfiguration
class InstallationFile:
@ -479,37 +480,35 @@ class Installer:
def drop_to_shell(self) -> None:
subprocess.check_call(f"/usr/bin/arch-chroot {self.target}", shell=True)
def configure_nic(self,
nic :str,
dhcp :bool = True,
ip :Optional[str] = None,
gateway :Optional[str] = None,
dns :Optional[str] = None,
*args :str,
**kwargs :str
) -> None:
def configure_nic(self, network_config: NetworkConfiguration) -> None:
from .systemd import Networkd
if dhcp:
conf = Networkd(Match={"Name": nic}, Network={"DHCP": "yes"})
if network_config.dhcp:
conf = Networkd(Match={"Name": network_config.iface}, Network={"DHCP": "yes"})
else:
assert ip
network = {"Address": network_config.ip}
if network_config.gateway:
network["Gateway"] = network_config.gateway
if network_config.dns:
dns = network_config.dns
network["DNS"] = dns if isinstance(dns, list) else [dns]
network = {"Address": ip}
if gateway:
network["Gateway"] = gateway
if dns:
assert type(dns) == list
network["DNS"] = dns
conf = Networkd(Match={"Name": nic}, Network=network)
conf = Networkd(Match={"Name": network_config.iface}, Network=network)
for plugin in plugins.values():
if hasattr(plugin, 'on_configure_nic'):
if (new_conf := plugin.on_configure_nic(nic, dhcp, ip, gateway, dns)):
new_conf = plugin.on_configure_nic(
network_config.iface,
network_config.dhcp,
network_config.ip,
network_config.gateway,
network_config.dns
)
if new_conf:
conf = new_conf
with open(f"{self.target}/etc/systemd/network/10-{nic}.network", "a") as netconf:
with open(f"{self.target}/etc/systemd/network/10-{network_config.iface}.network", "a") as netconf:
netconf.write(str(conf))
def copy_iso_network_config(self, enable_services :bool = False) -> bool:

View File

@ -0,0 +1,97 @@
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import List, Optional, Dict
from ... import log
class NicType(str, Enum):
ISO = "iso"
NM = "nm"
MANUAL = "manual"
@dataclass
class NetworkConfiguration:
type: NicType
iface: str = None
ip: str = None
dhcp: bool = True
gateway: str = None
dns: List[str] = None
def __str__(self):
if self.is_iso():
return "Copy ISO configuration"
elif self.is_network_manager():
return "Use NetworkManager"
elif self.is_manual():
if self.dhcp:
return f'iface={self.iface}, dhcp=auto'
else:
return f'iface={self.iface}, ip={self.ip}, dhcp=staticIp, gateway={self.gateway}, dns={self.dns}'
else:
return 'Unknown type'
# for json serialization when calling json.dumps(...) on this class
def json(self):
return self.__dict__
@classmethod
def parse_arguments(cls, config: Dict[str, str]) -> Optional["NetworkConfiguration"]:
nic_type = config.get('type', None)
if not nic_type:
return None
try:
type = NicType(nic_type)
except ValueError:
options = [e.value for e in NicType]
log(_('Unknown nic type: {}. Possible values are {}').format(nic_type, options), fg='red')
exit(1)
if type == NicType.MANUAL:
if config.get('dhcp', False) or not any([config.get(v) for v in ['ip', 'gateway', 'dns']]):
return NetworkConfiguration(type, iface=config.get('iface', ''))
ip = config.get('ip', '')
if not ip:
log('Manual nic configuration with no auto DHCP requires an IP address', fg='red')
exit(1)
return NetworkConfiguration(
type,
iface=config.get('iface', ''),
ip=ip,
gateway=config.get('gateway', ''),
dns=config.get('dns', []),
dhcp=False
)
else:
return NetworkConfiguration(type)
def is_iso(self) -> bool:
return self.type == NicType.ISO
def is_network_manager(self) -> bool:
return self.type == NicType.NM
def is_manual(self) -> bool:
return self.type == NicType.MANUAL
def config_installer(self, installation: 'Installer'):
# If user selected to copy the current ISO network configuration
# Perform a copy of the config
if self.is_iso():
installation.copy_iso_network_config(enable_services=True) # Sources the ISO network configuration to the install medium.
elif self.is_network_manager():
installation.add_additional_packages("networkmanager")
installation.enable_service('NetworkManager.service')
# Otherwise, if a interface was selected, configure that interface
elif self.is_manual():
installation.configure_nic(self)
installation.enable_service('systemd-networkd')
installation.enable_service('systemd-resolved')

View File

@ -14,6 +14,7 @@ from typing import List, Any, Optional, Dict, Union, TYPE_CHECKING
# https://stackoverflow.com/a/39757388/929999
from .menu.text_input import TextInput
from .models.network_configuration import NetworkConfiguration, NicType
if TYPE_CHECKING:
from .disk.partition import Partition
@ -450,11 +451,12 @@ def ask_additional_packages_to_install(pre_set_packages :List[str] = []) -> List
return packages
def ask_to_configure_network(preset :Dict[str, Any] = {}) -> Dict[str, Any]:
# Optionally configure one network interface.
# while 1:
# {MAC: Ifname}
def ask_to_configure_network(preset :Dict[str, Any] = {}) -> Optional[NetworkConfiguration]:
"""
Configure the network on the newly installed system
"""
interfaces = {
'none': str(_('No network configuration')),
'iso_config': str(_('Copy ISO network configuration to installation')),
'network_manager': str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)')),
**list_interfaces()
@ -473,17 +475,17 @@ def ask_to_configure_network(preset :Dict[str, Any] = {}) -> Dict[str, Any]:
except ValueError:
pass
nic = Menu(_('Select one network interface to configure'), interfaces.values(),cursor_index=cursor_idx).run()
nic = Menu(_('Select one network interface to configure'), interfaces.values(), cursor_index=cursor_idx, sort=False).run()
if not nic:
return {}
return None
# nic = network_manager_nt
if nic == interfaces['iso_config']:
return {'type': 'iso_config'}
if nic == interfaces['none']:
return None
elif nic == interfaces['iso_config']:
return NetworkConfiguration(NicType.ISO)
elif nic == interfaces['network_manager']:
return {'type': 'network_manager', 'NetworkManager': True}
return NetworkConfiguration(NicType.NM)
else:
# Current workaround:
# For selecting modes without entering text within brackets,
@ -543,10 +545,17 @@ def ask_to_configure_network(preset :Dict[str, Any] = {}) -> Dict[str, Any]:
if len(dns_input):
dns = dns_input.split(' ')
return {'type': nic, 'dhcp': False, 'ip': ip, 'gateway': gateway, 'dns': dns}
return NetworkConfiguration(
NicType.MANUAL,
iface=nic,
ip=ip,
gateway=gateway,
dns=dns,
dhcp=False
)
else:
# this will contain network iface names
return {'type': nic}
return NetworkConfiguration(NicType.MANUAL, iface=nic)
def partition_overlap(partitions :list, start :str, end :str) -> bool:

View File

@ -73,8 +73,7 @@ There are three different configuration files, all of which are optional.
"keyboard-language": "us",
"mirror-region": "Worldwide",
"nic": {
"NetworkManager": true,
"nic": "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
"type": "NM"
},
"ntp": true,
"packages": ["docker", "git", "wget", "zsh"],
@ -124,9 +123,13 @@ Options for ``--config``
| | | "Worldwide" or "Sweden" | | Either takes a dictionary structure of region and a given set of mirrors. | |
| | | | Or just a region and archinstall will source any mirrors for that region automatically | |
+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
| nic | | { NetworkManager: <boolean> } | | Takes three different kind of options. Copy, NetworkManager or a nic name. | No |
| | | { "eth0": {"address": "<ip>", "subnet": "255.0.0.0"}}| | Copy will copy the network configuration used in the live ISO. NetworkManager will | |
| | | "Copy ISO network configuration to installation" | | install and configure `NetworkManager <https://wiki.archlinux.org/title/NetworkManager>`_ | |
| nic | | { type: <ISO|NM|MANUAL> } | | Type must be one of ISO, NM, MANUAL. ISO will copy the configuration on the image, | No |
| | | | | NM configures NetworkManager and MANUAL allows to specify custom configuration | |
| | | { "iface": "eth0"} | | Only MANUAL: name of the interface | |
| | | { "dhcp": <boolean>} | | Only MANUAL: If set to true DHCP auto will be setup and all further configs ignored | |
| | | { "ip": <ip>} | | Only MANUAL: Ip address to set, is MANDATORY | |
| | | { "gateway": <ip>} | | Only MANUAL: Optional gateway | |
| | | { "dns": [<ip>]} | | Only MANUAL: Optional DNS servers | |
+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
| ntp | <boolean> | Set to true to set-up ntp post install | No |
+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+

View File

@ -15,8 +15,7 @@
}
},
"nic": {
"NetworkManager": true,
"nic": "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)"
"type": "NM"
},
"ntp": true,
"packages": [],
@ -26,4 +25,4 @@
"sys-encoding": "utf-8",
"sys-language": "en_US",
"timezone": "UTC"
}
}

View File

@ -13,7 +13,7 @@
"keyboard-layout": "us",
"mirror-region": "Worldwide",
"nic": {
"NetworkManager": true
"type": "NM"
},
"ntp": true,
"packages": ["docker", "git", "wget", "zsh"],

View File

@ -145,11 +145,11 @@ def perform_installation(mountpoint):
# Set mirrors used by pacstrap (outside of installation)
if archinstall.arguments.get('mirror-region', None):
archinstall.use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium
# Retrieve list of additional repositories and set boolean values appropriately
enable_testing = 'testing' in archinstall.arguments.get('additional-repositories', None)
enable_multilib = 'multilib' in archinstall.arguments.get('additional-repositories', None)
if installation.minimal_installation(testing=enable_testing, multilib=enable_multilib):
installation.set_locale(archinstall.arguments['sys-language'], archinstall.arguments['sys-encoding'].upper())
installation.set_hostname(archinstall.arguments['hostname'])
@ -163,16 +163,10 @@ def perform_installation(mountpoint):
# If user selected to copy the current ISO network configuration
# Perform a copy of the config
if archinstall.arguments.get('nic', {}).get('type', '') == 'iso_config':
installation.copy_iso_network_config(enable_services=True) # Sources the ISO network configuration to the install medium.
elif archinstall.arguments.get('nic', {}).get('NetworkManager', False):
installation.add_additional_packages("networkmanager")
installation.enable_service('NetworkManager.service')
# Otherwise, if a interface was selected, configure that interface
elif archinstall.arguments.get('nic', {}):
installation.configure_nic(**archinstall.arguments.get('nic', {}))
installation.enable_service('systemd-networkd')
installation.enable_service('systemd-resolved')
network_config = archinstall.arguments.get('nic', None)
if network_config:
network_config.config_installer(installation)
if archinstall.arguments.get('audio', None) is not None:
installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}", level=logging.INFO)

View File

@ -393,19 +393,10 @@ def os_setup(installation):
if archinstall.arguments['swap']:
installation.setup_swap('zram')
# If user selected to copy the current ISO network configuration
# Perform a copy of the config
if archinstall.arguments.get('nic', {}).get('type', '') == 'iso_config':
installation.copy_iso_network_config(
enable_services=True) # Sources the ISO network configuration to the install medium.
elif archinstall.arguments.get('nic', {}).get('NetworkManager', False):
installation.add_additional_packages("networkmanager")
installation.enable_service('NetworkManager.service')
# Otherwise, if a interface was selected, configure that interface
elif archinstall.arguments.get('nic', {}):
installation.configure_nic(**archinstall.arguments.get('nic', {}))
installation.enable_service('systemd-networkd')
installation.enable_service('systemd-resolved')
network_config = archinstall.arguments.get('nic', None)
if network_config:
network_config.config_installer(installation)
if archinstall.arguments.get('audio', None) is not None:
installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}",level=logging.INFO)

View File

@ -84,18 +84,18 @@
"description": "Choose between NetworkManager, manual configuration, use systemd-networkd from the ISO or no configuration",
"type": "object",
"properties": {
"NetworkManager": {
"description": "<boolean>",
"type": "boolean"
},
"interface-name": {
"address": "ip address",
"subnet": "255.255.255.0",
"gateway": "ip address",
"dns": "ip address"
},
"nic": "Copy ISO network configuration to installation",
"nic": {}
"type": "string",
"iface": "string",
"dhcp": "boolean",
"ip": "string",
"gateway": "string",
"dns": {
"description": "List of DNS servers",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"ntp": {