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:
parent
c67bb0b549
commit
2f273868d4
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}')
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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"],
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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"],
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
23
schema.json
23
schema.json
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue