Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib
diff options
context:
space:
mode:
authorDaniel Girtler <blackrabbit256@gmail.com>2023-07-17 17:27:21 +1000
committerGitHub <noreply@github.com>2023-07-17 09:27:21 +0200
commit2f273868d416c3309191db8c616aae683d78370a (patch)
tree9b38c0b631774d50b037bda3cef764e8cdf740d8 /archinstall/lib
parentc67bb0b549b35ce335941c9c1cbe22f99c28f7fe (diff)
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>
Diffstat (limited to 'archinstall/lib')
-rw-r--r--archinstall/lib/global_menu.py27
-rw-r--r--archinstall/lib/installer.py18
-rw-r--r--archinstall/lib/interactions/__init__.py2
-rw-r--r--archinstall/lib/interactions/network_menu.py (renamed from archinstall/lib/interactions/network_conf.py)97
-rw-r--r--archinstall/lib/models/__init__.py6
-rw-r--r--archinstall/lib/models/network_configuration.py200
6 files changed, 148 insertions, 202 deletions
diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py
index 02b1b0b6..5503d9ce 100644
--- a/archinstall/lib/global_menu.py
+++ b/archinstall/lib/global_menu.py
@@ -7,7 +7,7 @@ from .general import secret
from .locale.locale_menu import LocaleConfiguration, LocaleMenu
from .menu import Selector, AbstractMenu
from .mirrors import MirrorConfiguration, MirrorMenu
-from .models import NetworkConfiguration
+from .models import NetworkConfiguration, NicType
from .models.bootloader import Bootloader
from .models.users import User
from .output import FormattedOutput
@@ -142,7 +142,7 @@ class GlobalMenu(AbstractMenu):
lambda preset: select_additional_repositories(preset),
display_func=lambda x: ', '.join(x) if x else None,
default=[])
- self._menu_options['nic'] = \
+ self._menu_options['network_config'] = \
Selector(
_('Network configuration'),
lambda preset: ask_to_configure_network(preset),
@@ -221,14 +221,11 @@ class GlobalMenu(AbstractMenu):
return _('Install ({} config(s) missing)').format(missing)
return _('Install')
- def _display_network_conf(self, cur_value: Union[NetworkConfiguration, List[NetworkConfiguration]]) -> str:
- if not cur_value:
- return _('Not configured, unavailable unless setup manually')
- else:
- if isinstance(cur_value, list):
- return str(_('Configured {} interfaces')).format(len(cur_value))
- else:
- return str(cur_value)
+ def _display_network_conf(self, config: Optional[NetworkConfiguration]) -> str:
+ if not config:
+ return str(_('Not configured, unavailable unless setup manually'))
+
+ return config.type.display_msg()
def _disk_encryption(self, preset: Optional[disk.DiskEncryption]) -> Optional[disk.DiskEncryption]:
mods: Optional[List[disk.DeviceModification]] = self._menu_options['disk_config'].current_selection
@@ -257,11 +254,11 @@ class GlobalMenu(AbstractMenu):
return None
def _prev_network_config(self) -> Optional[str]:
- selector = self._menu_options['nic']
- if selector.has_selection():
- ifaces = selector.current_selection
- if isinstance(ifaces, list):
- return FormattedOutput.as_table(ifaces)
+ selector: Optional[NetworkConfiguration] = self._menu_options['network_config'].current_selection
+ if selector:
+ if selector.type == NicType.MANUAL:
+ output = FormattedOutput.as_table(selector.nics)
+ return output
return None
def _prev_additional_pkgs(self):
diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py
index 02d48768..8c5e7648 100644
--- a/archinstall/lib/installer.py
+++ b/archinstall/lib/installer.py
@@ -19,7 +19,7 @@ from .locale import verify_keyboard_layout, verify_x11_keyboard_layout
from .luks import Luks2
from .mirrors import use_mirrors, MirrorConfiguration, add_custom_mirrors
from .models.bootloader import Bootloader
-from .models.network_configuration import NetworkConfiguration
+from .models.network_configuration import Nic
from .models.users import User
from .output import log, error, info, warn, debug
from . import pacman
@@ -458,20 +458,20 @@ class Installer:
def drop_to_shell(self) -> None:
subprocess.check_call(f"/usr/bin/arch-chroot {self.target}", shell=True)
- def configure_nic(self, network_config: NetworkConfiguration) -> None:
- conf = network_config.as_systemd_config()
+ def configure_nic(self, nic: Nic):
+ conf = nic.as_systemd_config()
for plugin in plugins.values():
if hasattr(plugin, 'on_configure_nic'):
conf = plugin.on_configure_nic(
- network_config.iface,
- network_config.dhcp,
- network_config.ip,
- network_config.gateway,
- network_config.dns
+ nic.iface,
+ nic.dhcp,
+ nic.ip,
+ nic.gateway,
+ nic.dns
) 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))
def copy_iso_network_config(self, enable_services :bool = False) -> bool:
diff --git a/archinstall/lib/interactions/__init__.py b/archinstall/lib/interactions/__init__.py
index 466cfa0b..53be8e7a 100644
--- a/archinstall/lib/interactions/__init__.py
+++ b/archinstall/lib/interactions/__init__.py
@@ -1,5 +1,5 @@
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 .disk_conf import (
diff --git a/archinstall/lib/interactions/network_conf.py b/archinstall/lib/interactions/network_menu.py
index 18a834a1..14fc5785 100644
--- a/archinstall/lib/interactions/network_conf.py
+++ b/archinstall/lib/interactions/network_menu.py
@@ -1,10 +1,10 @@
from __future__ import annotations
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 ..models.network_configuration import NetworkConfiguration, NicType
+from ..models.network_configuration import NetworkConfiguration, NicType, Nic
from ..networking import list_interfaces
from ..output import FormattedOutput, warn
@@ -19,23 +19,22 @@ class ManualNetworkConfig(ListManager):
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 = [
str(_('Add interface')),
str(_('Edit 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[NetworkConfiguration]) -> Dict[str, Optional[NetworkConfiguration]]:
+ def reformat(self, data: List[Nic]) -> Dict[str, Optional[Nic]]:
table = FormattedOutput.as_table(data)
rows = table.split('\n')
# 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
# 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):
row = row.replace('|', '\\|')
@@ -43,16 +42,16 @@ class ManualNetworkConfig(ListManager):
return display_data
- def selected_action_display(self, iface: NetworkConfiguration) -> str:
- return iface.iface if iface.iface else ''
+ def selected_action_display(self, nic: Nic) -> str:
+ 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
- iface_name = self._select_iface(data)
- if iface_name:
- iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name)
- iface = self._edit_iface(iface)
- data += [iface]
+ iface = self._select_iface(data)
+ if iface:
+ nic = Nic(iface=iface)
+ nic = self._edit_iface(nic)
+ data += [nic]
elif entry:
if action == self._actions[1]: # edit interface
data = [d for d in data if d.iface != entry.iface]
@@ -62,7 +61,7 @@ class ManualNetworkConfig(ListManager):
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()
existing_ifaces = [d.iface for d in data]
available = set(all_ifaces) - set(existing_ifaces)
@@ -71,10 +70,10 @@ class ManualNetworkConfig(ListManager):
if choice.type_ == MenuSelectionType.Skip:
return None
- return choice.value
+ return choice.single_value
- def _edit_iface(self, edit_iface: NetworkConfiguration):
- iface_name = edit_iface.iface
+ def _edit_iface(self, edit_nic: Nic) -> Nic:
+ iface_name = edit_nic.iface
modes = ['DHCP (auto detect)', 'IP (static)']
default_mode = 'DHCP (auto detect)'
@@ -84,7 +83,7 @@ class ManualNetworkConfig(ListManager):
if mode.value == 'IP (static)':
while 1:
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
try:
ipaddress.ip_interface(ip)
@@ -98,7 +97,7 @@ class ManualNetworkConfig(ListManager):
while 1:
gateway = TextInput(
_('Enter your gateway (router) IP address or leave blank for none: '),
- edit_iface.gateway
+ edit_nic.gateway
).run().strip()
try:
if len(gateway) > 0:
@@ -107,8 +106,8 @@ class ManualNetworkConfig(ListManager):
except ValueError:
warn("You need to enter a valid gateway (router) IP address")
- if edit_iface.dns:
- display_dns = ' '.join(edit_iface.dns)
+ if edit_nic.dns:
+ display_dns = ' '.join(edit_nic.dns)
else:
display_dns = None
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):
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:
# this will contain network iface names
- return NetworkConfiguration(NicType.MANUAL, iface=iface_name)
+ return Nic(iface=iface_name)
-def ask_to_configure_network(
- preset: Union[NetworkConfiguration, List[NetworkConfiguration]]
-) -> Optional[NetworkConfiguration | List[NetworkConfiguration]]:
+def ask_to_configure_network(preset: Optional[NetworkConfiguration]) -> Optional[NetworkConfiguration]:
"""
Configure the network on the newly installed system
"""
- network_options = {
- '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)')),
- '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
-
+ options = {n.display_msg(): n for n in NicType}
+ preset_val = preset.type.display_msg() if preset else None
warning = str(_('Are you sure you want to reset this setting?'))
choice = Menu(
_('Select one network interface to configure'),
- list(network_options.values()),
- cursor_index=cursor_idx,
+ list(options.keys()),
+ preset_values=preset_val,
sort=False,
allow_reset=True,
allow_reset_warning_msg=warning
@@ -158,15 +142,18 @@ def ask_to_configure_network(
match choice.type_:
case MenuSelectionType.Skip: return preset
case MenuSelectionType.Reset: return None
-
- if choice.value == network_options['none']:
- return None
- elif choice.value == network_options['iso_config']:
- return NetworkConfiguration(NicType.ISO)
- elif choice.value == network_options['network_manager']:
- return NetworkConfiguration(NicType.NM)
- elif choice.value == network_options['manual']:
- preset_ifaces = preset if isinstance(preset, list) else []
- return ManualNetworkConfig('Configure interfaces', preset_ifaces).run()
+ case MenuSelectionType.Selection:
+ nic_type = options[choice.single_value]
+
+ match nic_type:
+ case NicType.ISO:
+ return NetworkConfiguration(NicType.ISO)
+ case NicType.NM:
+ return NetworkConfiguration(NicType.NM)
+ case NicType.MANUAL:
+ preset_nics = preset.nics if preset else []
+ nics = ManualNetworkConfig('Configure interfaces', preset_nics).run()
+ if nics:
+ return NetworkConfiguration(NicType.MANUAL, nics)
return preset
diff --git a/archinstall/lib/models/__init__.py b/archinstall/lib/models/__init__.py
index 8cc49ea0..7415f63f 100644
--- a/archinstall/lib/models/__init__.py
+++ b/archinstall/lib/models/__init__.py
@@ -1,4 +1,8 @@
-from .network_configuration import NetworkConfiguration, NicType, NetworkConfigurationHandler
+from .network_configuration import (
+ NetworkConfiguration,
+ NicType,
+ Nic
+)
from .bootloader import Bootloader
from .gen import VersionDef, PackageSearchResult, PackageSearch, LocalPackage
from .users import PasswordStrength, User
diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py
index e564b97b..fac7bbef 100644
--- a/archinstall/lib/models/network_configuration.py
+++ b/archinstall/lib/models/network_configuration.py
@@ -2,56 +2,64 @@ from __future__ import annotations
from dataclasses import dataclass, field
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
if TYPE_CHECKING:
_: Any
-class NicType(str, Enum):
+class NicType(Enum):
ISO = "iso"
NM = "nm"
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
-class NetworkConfiguration:
- type: NicType
+class Nic:
iface: Optional[str] = None
ip: Optional[str] = None
dhcp: bool = True
gateway: Optional[str] = None
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]:
- exclude_fields = ['type']
- data = {}
- for k, v in self.__dict__.items():
- if k not in exclude_fields:
- if isinstance(v, list) and len(v) == 0:
- v = ''
- elif v is None:
- v = ''
-
- data[k] = v
-
- return data
+ return {
+ 'iface': self.iface if self.iface else '',
+ 'ip': self.ip if self.ip else '',
+ 'dhcp': self.dhcp,
+ 'gateway': self.gateway if self.gateway else '',
+ 'dns': self.dns
+ }
+
+ def __dump__(self) -> Dict[str, Any]:
+ return {
+ 'iface': self.iface,
+ 'ip': self.ip,
+ 'dhcp': self.dhcp,
+ 'gateway': self.gateway,
+ 'dns': self.dns
+ }
+
+ @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:
match: List[Tuple[str, str]] = []
@@ -80,107 +88,57 @@ class NetworkConfiguration:
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:
- 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
-
-
-class NetworkConfigurationHandler:
- def __init__(self, config: Union[None, NetworkConfiguration, List[NetworkConfiguration]] = None):
- self._configuration = config
-
- @property
- def configuration(self):
- return self._configuration
- def config_installer(
+@dataclass
+class NetworkConfiguration:
+ type: NicType
+ nics: List[Nic] = field(default_factory=list)
+
+ def __dump__(self) -> Dict[str, Any]:
+ config: Dict[str, Any] = {'type': self.type.value}
+ if self.nics:
+ config['nics'] = [n.__dump__() for n in self.nics]
+
+ return config
+
+ @staticmethod
+ def parse_arg(config: Dict[str, Any]) -> Optional[NetworkConfiguration]:
+ nic_type = config.get('type', None)
+ if not nic_type:
+ return None
+
+ match NicType(nic_type):
+ case NicType.ISO:
+ 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)
+
+ return None
+
+ def install_network_config(
self,
installation: Any,
profile_config: Optional[ProfileConfiguration] = None
):
- if self._configuration is None:
- return
-
- 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():
+ match self.type:
+ case NicType.ISO:
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"])
if profile_config and profile_config.profile:
if profile_config.profile.is_desktop_type_profile():
installation.add_additional_packages(["network-manager-applet"])
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]]:
- configurations = []
-
- 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}')
+ installation.enable_service('systemd-networkd')
+ installation.enable_service('systemd-resolved')