Fix network settings loading from config file (#1921)

* Fix network config error and simplify code

* Update schema and exmaple

---------

Co-authored-by: Daniel Girtler <girtler.daniel@gmail.com>
This commit is contained in:
Daniel Girtler 2023-07-17 17:27:21 +10:00 committed by GitHub
parent c67bb0b549
commit 2f273868d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 187 additions and 234 deletions

View File

@ -225,10 +225,9 @@ def load_config():
if arguments.get('servers', None) is not None: if arguments.get('servers', None) is not None:
storage['_selected_servers'] = arguments.get('servers', None) storage['_selected_servers'] = arguments.get('servers', None)
if arguments.get('nic', None) is not None: if arguments.get('network_config', None) is not None:
handler = models.NetworkConfigurationHandler() config = NetworkConfiguration.parse_arg(arguments.get('network_config'))
handler.parse_arguments(arguments.get('nic')) arguments['network_config'] = config
arguments['nic'] = handler.configuration
if arguments.get('!users', None) is not None or arguments.get('!superusers', None) is not None: if arguments.get('!users', None) is not None or arguments.get('!superusers', None) is not None:
users = arguments.get('!users', None) users = arguments.get('!users', None)

View File

@ -7,7 +7,7 @@ from .general import secret
from .locale.locale_menu import LocaleConfiguration, LocaleMenu from .locale.locale_menu import LocaleConfiguration, LocaleMenu
from .menu import Selector, AbstractMenu from .menu import Selector, AbstractMenu
from .mirrors import MirrorConfiguration, MirrorMenu from .mirrors import MirrorConfiguration, MirrorMenu
from .models import NetworkConfiguration from .models import NetworkConfiguration, NicType
from .models.bootloader import Bootloader from .models.bootloader import Bootloader
from .models.users import User from .models.users import User
from .output import FormattedOutput from .output import FormattedOutput
@ -142,7 +142,7 @@ class GlobalMenu(AbstractMenu):
lambda preset: select_additional_repositories(preset), lambda preset: select_additional_repositories(preset),
display_func=lambda x: ', '.join(x) if x else None, display_func=lambda x: ', '.join(x) if x else None,
default=[]) default=[])
self._menu_options['nic'] = \ self._menu_options['network_config'] = \
Selector( Selector(
_('Network configuration'), _('Network configuration'),
lambda preset: ask_to_configure_network(preset), lambda preset: ask_to_configure_network(preset),
@ -221,14 +221,11 @@ class GlobalMenu(AbstractMenu):
return _('Install ({} config(s) missing)').format(missing) return _('Install ({} config(s) missing)').format(missing)
return _('Install') return _('Install')
def _display_network_conf(self, cur_value: Union[NetworkConfiguration, List[NetworkConfiguration]]) -> str: def _display_network_conf(self, config: Optional[NetworkConfiguration]) -> str:
if not cur_value: if not config:
return _('Not configured, unavailable unless setup manually') return str(_('Not configured, unavailable unless setup manually'))
else:
if isinstance(cur_value, list): return config.type.display_msg()
return str(_('Configured {} interfaces')).format(len(cur_value))
else:
return str(cur_value)
def _disk_encryption(self, preset: Optional[disk.DiskEncryption]) -> Optional[disk.DiskEncryption]: def _disk_encryption(self, preset: Optional[disk.DiskEncryption]) -> Optional[disk.DiskEncryption]:
mods: Optional[List[disk.DeviceModification]] = self._menu_options['disk_config'].current_selection mods: Optional[List[disk.DeviceModification]] = self._menu_options['disk_config'].current_selection
@ -257,11 +254,11 @@ class GlobalMenu(AbstractMenu):
return None return None
def _prev_network_config(self) -> Optional[str]: def _prev_network_config(self) -> Optional[str]:
selector = self._menu_options['nic'] selector: Optional[NetworkConfiguration] = self._menu_options['network_config'].current_selection
if selector.has_selection(): if selector:
ifaces = selector.current_selection if selector.type == NicType.MANUAL:
if isinstance(ifaces, list): output = FormattedOutput.as_table(selector.nics)
return FormattedOutput.as_table(ifaces) return output
return None return None
def _prev_additional_pkgs(self): def _prev_additional_pkgs(self):

View File

@ -19,7 +19,7 @@ from .locale import verify_keyboard_layout, verify_x11_keyboard_layout
from .luks import Luks2 from .luks import Luks2
from .mirrors import use_mirrors, MirrorConfiguration, add_custom_mirrors from .mirrors import use_mirrors, MirrorConfiguration, add_custom_mirrors
from .models.bootloader import Bootloader from .models.bootloader import Bootloader
from .models.network_configuration import NetworkConfiguration from .models.network_configuration import Nic
from .models.users import User from .models.users import User
from .output import log, error, info, warn, debug from .output import log, error, info, warn, debug
from . import pacman from . import pacman
@ -458,20 +458,20 @@ class Installer:
def drop_to_shell(self) -> None: def drop_to_shell(self) -> None:
subprocess.check_call(f"/usr/bin/arch-chroot {self.target}", shell=True) subprocess.check_call(f"/usr/bin/arch-chroot {self.target}", shell=True)
def configure_nic(self, network_config: NetworkConfiguration) -> None: def configure_nic(self, nic: Nic):
conf = network_config.as_systemd_config() conf = nic.as_systemd_config()
for plugin in plugins.values(): for plugin in plugins.values():
if hasattr(plugin, 'on_configure_nic'): if hasattr(plugin, 'on_configure_nic'):
conf = plugin.on_configure_nic( conf = plugin.on_configure_nic(
network_config.iface, nic.iface,
network_config.dhcp, nic.dhcp,
network_config.ip, nic.ip,
network_config.gateway, nic.gateway,
network_config.dns nic.dns
) or conf ) or conf
with open(f"{self.target}/etc/systemd/network/10-{network_config.iface}.network", "a") as netconf: with open(f"{self.target}/etc/systemd/network/10-{nic.iface}.network", "a") as netconf:
netconf.write(str(conf)) netconf.write(str(conf))
def copy_iso_network_config(self, enable_services :bool = False) -> bool: def copy_iso_network_config(self, enable_services :bool = False) -> bool:

View File

@ -1,5 +1,5 @@
from .manage_users_conf import UserList, ask_for_additional_users from .manage_users_conf import UserList, ask_for_additional_users
from .network_conf import ManualNetworkConfig, ask_to_configure_network from .network_menu import ManualNetworkConfig, ask_to_configure_network
from .utils import get_password from .utils import get_password
from .disk_conf import ( from .disk_conf import (

View File

@ -1,10 +1,10 @@
from __future__ import annotations from __future__ import annotations
import ipaddress import ipaddress
from typing import Any, Optional, TYPE_CHECKING, List, Union, Dict from typing import Any, Optional, TYPE_CHECKING, List, Dict
from ..menu import MenuSelectionType, TextInput from ..menu import MenuSelectionType, TextInput
from ..models.network_configuration import NetworkConfiguration, NicType from ..models.network_configuration import NetworkConfiguration, NicType, Nic
from ..networking import list_interfaces from ..networking import list_interfaces
from ..output import FormattedOutput, warn from ..output import FormattedOutput, warn
@ -19,23 +19,22 @@ class ManualNetworkConfig(ListManager):
subclass of ListManager for the managing of network configurations subclass of ListManager for the managing of network configurations
""" """
def __init__(self, prompt: str, ifaces: List[NetworkConfiguration]): def __init__(self, prompt: str, preset: List[Nic]):
self._actions = [ self._actions = [
str(_('Add interface')), str(_('Add interface')),
str(_('Edit interface')), str(_('Edit interface')),
str(_('Delete interface')) str(_('Delete interface'))
] ]
super().__init__(prompt, preset, [self._actions[0]], self._actions[1:])
super().__init__(prompt, ifaces, [self._actions[0]], self._actions[1:]) def reformat(self, data: List[Nic]) -> Dict[str, Optional[Nic]]:
def reformat(self, data: List[NetworkConfiguration]) -> Dict[str, Optional[NetworkConfiguration]]:
table = FormattedOutput.as_table(data) table = FormattedOutput.as_table(data)
rows = table.split('\n') rows = table.split('\n')
# these are the header rows of the table and do not map to any User obviously # these are the header rows of the table and do not map to any User obviously
# we're adding 2 spaces as prefix because the menu selector '> ' will be put before # we're adding 2 spaces as prefix because the menu selector '> ' will be put before
# the selectable rows so the header has to be aligned # the selectable rows so the header has to be aligned
display_data: Dict[str, Optional[NetworkConfiguration]] = {f' {rows[0]}': None, f' {rows[1]}': None} display_data: Dict[str, Optional[Nic]] = {f' {rows[0]}': None, f' {rows[1]}': None}
for row, iface in zip(rows[2:], data): for row, iface in zip(rows[2:], data):
row = row.replace('|', '\\|') row = row.replace('|', '\\|')
@ -43,16 +42,16 @@ class ManualNetworkConfig(ListManager):
return display_data return display_data
def selected_action_display(self, iface: NetworkConfiguration) -> str: def selected_action_display(self, nic: Nic) -> str:
return iface.iface if iface.iface else '' return nic.iface if nic.iface else ''
def handle_action(self, action: str, entry: Optional[NetworkConfiguration], data: List[NetworkConfiguration]): def handle_action(self, action: str, entry: Optional[Nic], data: List[Nic]):
if action == self._actions[0]: # add if action == self._actions[0]: # add
iface_name = self._select_iface(data) iface = self._select_iface(data)
if iface_name: if iface:
iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name) nic = Nic(iface=iface)
iface = self._edit_iface(iface) nic = self._edit_iface(nic)
data += [iface] data += [nic]
elif entry: elif entry:
if action == self._actions[1]: # edit interface if action == self._actions[1]: # edit interface
data = [d for d in data if d.iface != entry.iface] data = [d for d in data if d.iface != entry.iface]
@ -62,7 +61,7 @@ class ManualNetworkConfig(ListManager):
return data return data
def _select_iface(self, data: List[NetworkConfiguration]) -> Optional[Any]: def _select_iface(self, data: List[Nic]) -> Optional[str]:
all_ifaces = list_interfaces().values() all_ifaces = list_interfaces().values()
existing_ifaces = [d.iface for d in data] existing_ifaces = [d.iface for d in data]
available = set(all_ifaces) - set(existing_ifaces) available = set(all_ifaces) - set(existing_ifaces)
@ -71,10 +70,10 @@ class ManualNetworkConfig(ListManager):
if choice.type_ == MenuSelectionType.Skip: if choice.type_ == MenuSelectionType.Skip:
return None return None
return choice.value return choice.single_value
def _edit_iface(self, edit_iface: NetworkConfiguration): def _edit_iface(self, edit_nic: Nic) -> Nic:
iface_name = edit_iface.iface iface_name = edit_nic.iface
modes = ['DHCP (auto detect)', 'IP (static)'] modes = ['DHCP (auto detect)', 'IP (static)']
default_mode = 'DHCP (auto detect)' default_mode = 'DHCP (auto detect)'
@ -84,7 +83,7 @@ class ManualNetworkConfig(ListManager):
if mode.value == 'IP (static)': if mode.value == 'IP (static)':
while 1: while 1:
prompt = _('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(iface_name) prompt = _('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(iface_name)
ip = TextInput(prompt, edit_iface.ip).run().strip() ip = TextInput(prompt, edit_nic.ip).run().strip()
# Implemented new check for correct IP/subnet input # Implemented new check for correct IP/subnet input
try: try:
ipaddress.ip_interface(ip) ipaddress.ip_interface(ip)
@ -98,7 +97,7 @@ class ManualNetworkConfig(ListManager):
while 1: while 1:
gateway = TextInput( gateway = TextInput(
_('Enter your gateway (router) IP address or leave blank for none: '), _('Enter your gateway (router) IP address or leave blank for none: '),
edit_iface.gateway edit_nic.gateway
).run().strip() ).run().strip()
try: try:
if len(gateway) > 0: if len(gateway) > 0:
@ -107,8 +106,8 @@ class ManualNetworkConfig(ListManager):
except ValueError: except ValueError:
warn("You need to enter a valid gateway (router) IP address") warn("You need to enter a valid gateway (router) IP address")
if edit_iface.dns: if edit_nic.dns:
display_dns = ' '.join(edit_iface.dns) display_dns = ' '.join(edit_nic.dns)
else: else:
display_dns = None display_dns = None
dns_input = TextInput(_('Enter your DNS servers (space separated, blank for none): '), display_dns).run().strip() dns_input = TextInput(_('Enter your DNS servers (space separated, blank for none): '), display_dns).run().strip()
@ -117,39 +116,24 @@ class ManualNetworkConfig(ListManager):
if len(dns_input): if len(dns_input):
dns = dns_input.split(' ') dns = dns_input.split(' ')
return NetworkConfiguration(NicType.MANUAL, iface=iface_name, ip=ip, gateway=gateway, dns=dns, dhcp=False) return Nic(iface=iface_name, ip=ip, gateway=gateway, dns=dns, dhcp=False)
else: else:
# this will contain network iface names # this will contain network iface names
return NetworkConfiguration(NicType.MANUAL, iface=iface_name) return Nic(iface=iface_name)
def ask_to_configure_network( def ask_to_configure_network(preset: Optional[NetworkConfiguration]) -> Optional[NetworkConfiguration]:
preset: Union[NetworkConfiguration, List[NetworkConfiguration]]
) -> Optional[NetworkConfiguration | List[NetworkConfiguration]]:
""" """
Configure the network on the newly installed system Configure the network on the newly installed system
""" """
network_options = { options = {n.display_msg(): n for n in NicType}
'none': str(_('No network configuration')), preset_val = preset.type.display_msg() if preset else None
'iso_config': str(_('Copy ISO network configuration to installation')),
'network_manager': str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)')),
'manual': str(_('Manual configuration'))
}
# for this routine it's easier to set the cursor position rather than a preset value
cursor_idx = None
if preset and not isinstance(preset, list):
if preset.type == 'iso_config':
cursor_idx = 0
elif preset.type == 'network_manager':
cursor_idx = 1
warning = str(_('Are you sure you want to reset this setting?')) warning = str(_('Are you sure you want to reset this setting?'))
choice = Menu( choice = Menu(
_('Select one network interface to configure'), _('Select one network interface to configure'),
list(network_options.values()), list(options.keys()),
cursor_index=cursor_idx, preset_values=preset_val,
sort=False, sort=False,
allow_reset=True, allow_reset=True,
allow_reset_warning_msg=warning allow_reset_warning_msg=warning
@ -158,15 +142,18 @@ def ask_to_configure_network(
match choice.type_: match choice.type_:
case MenuSelectionType.Skip: return preset case MenuSelectionType.Skip: return preset
case MenuSelectionType.Reset: return None case MenuSelectionType.Reset: return None
case MenuSelectionType.Selection:
nic_type = options[choice.single_value]
if choice.value == network_options['none']: match nic_type:
return None case NicType.ISO:
elif choice.value == network_options['iso_config']: return NetworkConfiguration(NicType.ISO)
return NetworkConfiguration(NicType.ISO) case NicType.NM:
elif choice.value == network_options['network_manager']: return NetworkConfiguration(NicType.NM)
return NetworkConfiguration(NicType.NM) case NicType.MANUAL:
elif choice.value == network_options['manual']: preset_nics = preset.nics if preset else []
preset_ifaces = preset if isinstance(preset, list) else [] nics = ManualNetworkConfig('Configure interfaces', preset_nics).run()
return ManualNetworkConfig('Configure interfaces', preset_ifaces).run() if nics:
return NetworkConfiguration(NicType.MANUAL, nics)
return preset return preset

View File

@ -1,4 +1,8 @@
from .network_configuration import NetworkConfiguration, NicType, NetworkConfigurationHandler from .network_configuration import (
NetworkConfiguration,
NicType,
Nic
)
from .bootloader import Bootloader from .bootloader import Bootloader
from .gen import VersionDef, PackageSearchResult, PackageSearch, LocalPackage from .gen import VersionDef, PackageSearchResult, PackageSearch, LocalPackage
from .users import PasswordStrength, User from .users import PasswordStrength, User

View File

@ -2,56 +2,64 @@ from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from typing import List, Optional, Dict, Union, Any, TYPE_CHECKING, Tuple from typing import List, Optional, Dict, Any, TYPE_CHECKING, Tuple
from ..output import debug
from ..profile import ProfileConfiguration from ..profile import ProfileConfiguration
if TYPE_CHECKING: if TYPE_CHECKING:
_: Any _: Any
class NicType(str, Enum): class NicType(Enum):
ISO = "iso" ISO = "iso"
NM = "nm" NM = "nm"
MANUAL = "manual" MANUAL = "manual"
def display_msg(self) -> str:
match self:
case NicType.ISO:
return str(_('Copy ISO network configuration to installation'))
case NicType.NM:
return str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)'))
case NicType.MANUAL:
return str(_('Manual configuration'))
@dataclass @dataclass
class NetworkConfiguration: class Nic:
type: NicType
iface: Optional[str] = None iface: Optional[str] = None
ip: Optional[str] = None ip: Optional[str] = None
dhcp: bool = True dhcp: bool = True
gateway: Optional[str] = None gateway: Optional[str] = None
dns: List[str] = field(default_factory=list) dns: List[str] = field(default_factory=list)
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'
def table_data(self) -> Dict[str, Any]: def table_data(self) -> Dict[str, Any]:
exclude_fields = ['type'] return {
data = {} 'iface': self.iface if self.iface else '',
for k, v in self.__dict__.items(): 'ip': self.ip if self.ip else '',
if k not in exclude_fields: 'dhcp': self.dhcp,
if isinstance(v, list) and len(v) == 0: 'gateway': self.gateway if self.gateway else '',
v = '' 'dns': self.dns
elif v is None: }
v = ''
data[k] = v def __dump__(self) -> Dict[str, Any]:
return {
'iface': self.iface,
'ip': self.ip,
'dhcp': self.dhcp,
'gateway': self.gateway,
'dns': self.dns
}
return data @staticmethod
def parse_arg(arg: Dict[str, Any]) -> Nic:
return Nic(
iface=arg.get('iface', None),
ip=arg.get('ip', None),
dhcp=arg.get('dhcp', True),
gateway=arg.get('gateway', None),
dns=arg.get('dns', []),
)
def as_systemd_config(self) -> str: def as_systemd_config(self) -> str:
match: List[Tuple[str, str]] = [] match: List[Tuple[str, str]] = []
@ -80,107 +88,57 @@ class NetworkConfiguration:
return config_str return config_str
def json(self) -> Dict:
# for json serialization when calling json.dumps(...) on this class
return self.__dict__
def is_iso(self) -> bool: @dataclass
return self.type == NicType.ISO class NetworkConfiguration:
type: NicType
nics: List[Nic] = field(default_factory=list)
def is_network_manager(self) -> bool: def __dump__(self) -> Dict[str, Any]:
return self.type == NicType.NM config: Dict[str, Any] = {'type': self.type.value}
if self.nics:
config['nics'] = [n.__dump__() for n in self.nics]
def is_manual(self) -> bool: return config
return self.type == NicType.MANUAL
@staticmethod
def parse_arg(config: Dict[str, Any]) -> Optional[NetworkConfiguration]:
nic_type = config.get('type', None)
if not nic_type:
return None
class NetworkConfigurationHandler: match NicType(nic_type):
def __init__(self, config: Union[None, NetworkConfiguration, List[NetworkConfiguration]] = None): case NicType.ISO:
self._configuration = config return NetworkConfiguration(NicType.ISO)
case NicType.NM:
return NetworkConfiguration(NicType.NM)
case NicType.MANUAL:
nics_arg = config.get('nics', [])
if nics_arg:
nics = [Nic.parse_arg(n) for n in nics_arg]
return NetworkConfiguration(NicType.MANUAL, nics)
@property return None
def configuration(self):
return self._configuration
def config_installer( def install_network_config(
self, self,
installation: Any, installation: Any,
profile_config: Optional[ProfileConfiguration] = None profile_config: Optional[ProfileConfiguration] = None
): ):
if self._configuration is None: match self.type:
return case NicType.ISO:
if isinstance(self._configuration, list):
for config in self._configuration:
installation.configure_nic(config)
installation.enable_service('systemd-networkd')
installation.enable_service('systemd-resolved')
else:
# If user selected to copy the current ISO network configuration
# Perform a copy of the config
if self._configuration.is_iso():
installation.copy_iso_network_config( installation.copy_iso_network_config(
enable_services=True # Sources the ISO network configuration to the install medium. enable_services=True # Sources the ISO network configuration to the install medium.
) )
elif self._configuration.is_network_manager(): case NicType.NM:
installation.add_additional_packages(["networkmanager"]) installation.add_additional_packages(["networkmanager"])
if profile_config and profile_config.profile: if profile_config and profile_config.profile:
if profile_config.profile.is_desktop_type_profile(): if profile_config.profile.is_desktop_type_profile():
installation.add_additional_packages(["network-manager-applet"]) installation.add_additional_packages(["network-manager-applet"])
installation.enable_service('NetworkManager.service') installation.enable_service('NetworkManager.service')
case NicType.MANUAL:
for nic in self.nics:
installation.configure_nic(nic)
def _parse_manual_config(self, configs: List[Dict[str, Any]]) -> Optional[List[NetworkConfiguration]]: installation.enable_service('systemd-networkd')
configurations = [] installation.enable_service('systemd-resolved')
for manual_config in configs:
iface = manual_config.get('iface', None)
if iface is None:
raise ValueError('No iface specified for manual configuration')
if manual_config.get('dhcp', False) or not any([manual_config.get(v, '') for v in ['ip', 'gateway', 'dns']]):
configurations.append(
NetworkConfiguration(NicType.MANUAL, iface=iface)
)
else:
ip = manual_config.get('ip', '')
if not ip:
raise ValueError('Manual nic configuration with no auto DHCP requires an IP address')
dns = manual_config.get('dns', [])
if not isinstance(dns, list):
dns = [dns]
configurations.append(
NetworkConfiguration(
NicType.MANUAL,
iface=iface,
ip=ip,
gateway=manual_config.get('gateway', ''),
dns=dns,
dhcp=False
)
)
return configurations
def _parse_nic_type(self, nic_type: str) -> NicType:
try:
return NicType(nic_type)
except ValueError:
options = [e.value for e in NicType]
raise ValueError(f'Unknown nic type: {nic_type}. Possible values are {options}')
def parse_arguments(self, config: Any):
if isinstance(config, list): # new data format
self._configuration = self._parse_manual_config(config)
elif nic_type := config.get('type', None): # new data format
type_ = self._parse_nic_type(nic_type)
if type_ != NicType.MANUAL:
self._configuration = NetworkConfiguration(type_)
else: # manual configuration settings
self._configuration = self._parse_manual_config([config])
else:
debug(f'Unable to parse network configuration: {config}')

View File

@ -1,6 +1,6 @@
import os import os
from pathlib import Path from pathlib import Path
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING, Optional
import archinstall import archinstall
from archinstall import info, debug from archinstall import info, debug
@ -14,7 +14,7 @@ from archinstall.lib.installer import Installer
from archinstall.lib.menu import Menu from archinstall.lib.menu import Menu
from archinstall.lib.mirrors import use_mirrors, add_custom_mirrors from archinstall.lib.mirrors import use_mirrors, add_custom_mirrors
from archinstall.lib.models.bootloader import Bootloader from archinstall.lib.models.bootloader import Bootloader
from archinstall.lib.models.network_configuration import NetworkConfigurationHandler from archinstall.lib.models.network_configuration import NetworkConfiguration
from archinstall.lib.networking import check_mirror_reachable from archinstall.lib.networking import check_mirror_reachable
from archinstall.lib.profile.profiles_handler import profile_handler from archinstall.lib.profile.profiles_handler import profile_handler
@ -82,7 +82,7 @@ def ask_user_questions():
global_menu.enable('parallel downloads') global_menu.enable('parallel downloads')
# Ask or Call the helper function that asks the user to optionally configure a network. # Ask or Call the helper function that asks the user to optionally configure a network.
global_menu.enable('nic') global_menu.enable('network_config')
global_menu.enable('timezone') global_menu.enable('timezone')
@ -158,11 +158,10 @@ def perform_installation(mountpoint: Path):
# If user selected to copy the current ISO network configuration # If user selected to copy the current ISO network configuration
# Perform a copy of the config # Perform a copy of the config
network_config = archinstall.arguments.get('nic', None) network_config: Optional[NetworkConfiguration] = archinstall.arguments.get('network_config', None)
if network_config: if network_config:
handler = NetworkConfigurationHandler(network_config) network_config.install_network_config(
handler.config_installer(
installation, installation,
archinstall.arguments.get('profile_config', None) archinstall.arguments.get('profile_config', None)
) )

View File

@ -95,7 +95,7 @@ class SwissMainMenu(GlobalMenu):
options_list = [ options_list = [
'mirror_config', 'disk_config', 'mirror_config', 'disk_config',
'disk_encryption', 'swap', 'bootloader', 'hostname', '!root-password', 'disk_encryption', 'swap', 'bootloader', 'hostname', '!root-password',
'!users', 'profile_config', 'audio', 'kernels', 'packages', 'additional-repositories', 'nic', '!users', 'profile_config', 'audio', 'kernels', 'packages', 'additional-repositories', 'network_config',
'timezone', 'ntp' 'timezone', 'ntp'
] ]
@ -110,7 +110,7 @@ class SwissMainMenu(GlobalMenu):
options_list = [ options_list = [
'mirror_config','bootloader', 'hostname', 'mirror_config','bootloader', 'hostname',
'!root-password', '!users', 'profile_config', 'audio', 'kernels', '!root-password', '!users', 'profile_config', 'audio', 'kernels',
'packages', 'additional-repositories', 'nic', 'timezone', 'ntp' 'packages', 'additional-repositories', 'network_config', 'timezone', 'ntp'
] ]
mandatory_list = ['hostname'] mandatory_list = ['hostname']
@ -222,11 +222,10 @@ def perform_installation(mountpoint: Path, exec_mode: ExecutionMode):
# If user selected to copy the current ISO network configuration # If user selected to copy the current ISO network configuration
# Perform a copy of the config # Perform a copy of the config
network_config = archinstall.arguments.get('nic', None) network_config = archinstall.arguments.get('network_config', None)
if network_config: if network_config:
handler = models.NetworkConfigurationHandler(network_config) network_config.install_network_config(
handler.config_installer(
installation, installation,
archinstall.arguments.get('profile_config', None) archinstall.arguments.get('profile_config', None)
) )

View File

@ -29,7 +29,7 @@ To start the installer, run the following in the latest Arch Linux ISO:
.. code-block:: sh .. code-block:: sh
archinstall --script guided archinstall --script guided
| The ``--script guided`` argument is optional as it's the default behavior. | The ``--script guided`` argument is optional as it's the default behavior.
| But this will use our most guided installation and if you skip all the option steps it will install a minimal Arch Linux experience. | But this will use our most guided installation and if you skip all the option steps it will install a minimal Arch Linux experience.
@ -49,7 +49,7 @@ There are three different configuration files, all of which are optional.
.. note:: .. note::
You can always get the latest options with ``archinstall --dry-run``, but edit the following json according to your needs. You can always get the latest options with ``archinstall --dry-run``, but edit the following json according to your needs.
Save the configuration as a ``.json`` file. Archinstall can source it via a local or remote path (URL) Save the configuration as a ``.json`` file. Archinstall can source it via a local or remote path (URL)
.. code-block:: json .. code-block:: json
{ {
@ -72,8 +72,8 @@ There are three different configuration files, all of which are optional.
], ],
"keyboard-language": "us", "keyboard-language": "us",
"mirror-region": "Worldwide", "mirror-region": "Worldwide",
"nic": { "network_config": {
"type": "NM" "type": "nm"
}, },
"ntp": true, "ntp": true,
"packages": ["docker", "git", "wget", "zsh"], "packages": ["docker", "git", "wget", "zsh"],

View File

@ -99,13 +99,19 @@
"http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch": true, "http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch": true,
} }
}, },
"nic": { "network_config": {
"dhcp": true, "nics": [
"dns": null, {
"gateway": null, "dhcp": false,
"iface": null, "dns": [
"ip": null, "3.3.3.3"
"type": "nm" ],
"gateway": "2.2.2.2",
"iface": "enp0s31f6",
"ip": "1.1.1.1"
}
],
"type": "manual"
}, },
"no_pkg_lookups": false, "no_pkg_lookups": false,
"ntp": true, "ntp": true,

View File

@ -12,8 +12,8 @@
], ],
"keyboard-layout": "us", "keyboard-layout": "us",
"mirror-region": "Worldwide", "mirror-region": "Worldwide",
"nic": { "network_config": {
"type": "NM" "type": "nm"
}, },
"ntp": true, "ntp": true,
"packages": ["docker", "git", "wget", "zsh"], "packages": ["docker", "git", "wget", "zsh"],

View File

@ -61,7 +61,7 @@ def ask_user_questions():
global_menu.enable('parallel downloads') global_menu.enable('parallel downloads')
# Ask or Call the helper function that asks the user to optionally configure a network. # Ask or Call the helper function that asks the user to optionally configure a network.
global_menu.enable('nic') global_menu.enable('network_config')
global_menu.enable('timezone') global_menu.enable('timezone')
@ -137,11 +137,10 @@ def perform_installation(mountpoint: Path):
# If user selected to copy the current ISO network configuration # If user selected to copy the current ISO network configuration
# Perform a copy of the config # Perform a copy of the config
network_config = archinstall.arguments.get('nic', None) network_config = archinstall.arguments.get('network_config', None)
if network_config: if network_config:
handler = models.NetworkConfigurationHandler(network_config) network_config.install_network_config(
handler.config_installer(
installation, installation,
archinstall.arguments.get('profile_config', None) archinstall.arguments.get('profile_config', None)
) )

View File

@ -69,21 +69,26 @@
"description": "By default, it will autodetect the best region. Enter a region or a dictionary of regions and mirrors to use specific ones", "description": "By default, it will autodetect the best region. Enter a region or a dictionary of regions and mirrors to use specific ones",
"type": "object" "type": "object"
}, },
"nic": { "network_config": {
"description": "Choose between NetworkManager, manual configuration, use systemd-networkd from the ISO or no configuration", "description": "Choose between NetworkManager, manual configuration, use systemd-networkd from the ISO or no configuration",
"type": "object", "type": "object",
"properties": { "properties": {
"type": "string", "type": "string",
"iface": "string", "nics": [
"dhcp": "boolean",
"ip": "string",
"gateway": "string",
"dns": {
"description": "List of DNS servers",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "iface": "string",
} "dhcp": "boolean",
"ip": "string",
"gateway": "string",
"dns": {
"description": "List of DNS servers",
"type": "array",
"items": {
"type": "string"
}
}
]
} }
} }
}, },