Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel <blackrabbit256@gmail.com>2022-03-01 01:57:57 +1100
committerGitHub <noreply@github.com>2022-02-28 15:57:57 +0100
commit537b9cab037aecfd18edef156dd3ea55072918e9 (patch)
treefe56cd4af527e816d831b220c6672c1dcfc2958f
parentfa87d85708331ad45f28906217f94937bae474fe (diff)
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>
-rw-r--r--.flake83
-rw-r--r--archinstall/__init__.py11
-rw-r--r--archinstall/lib/installer.py43
-rw-r--r--archinstall/lib/models/network_configuration.py97
-rw-r--r--archinstall/lib/user_interaction.py35
-rw-r--r--docs/installing/guided.rst13
-rw-r--r--examples/config-sample.json5
-rw-r--r--examples/custom-command-sample.json2
-rw-r--r--examples/guided.py18
-rw-r--r--examples/swiss.py17
-rw-r--r--schema.json24
11 files changed, 177 insertions, 91 deletions
diff --git a/.flake8 b/.flake8
index d9fb938e..bf169692 100644
--- a/.flake8
+++ b/.flake8
@@ -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 = _ \ No newline at end of file
+per-file-ignores = __init__.py:F401,F403,F405 simple_menu.py:C901,W503 guided.py:C901 network_configuration.py:F821
diff --git a/archinstall/__init__.py b/archinstall/__init__.py
index cb95028b..8c64a1f3 100644
--- a/archinstall/__init__.py
+++ b/archinstall/__init__.py
@@ -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
diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py
index cf643b27..894bcc2a 100644
--- a/archinstall/lib/installer.py
+++ b/archinstall/lib/installer.py
@@ -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:
diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py
new file mode 100644
index 00000000..f1ee4c3f
--- /dev/null
+++ b/archinstall/lib/models/network_configuration.py
@@ -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')
diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py
index 7524dd8b..1f62b7fd 100644
--- a/archinstall/lib/user_interaction.py
+++ b/archinstall/lib/user_interaction.py
@@ -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 {}
-
- # nic = network_manager_nt
+ return None
- 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:
diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst
index 5c360256..ac6254f2 100644
--- a/docs/installing/guided.rst
+++ b/docs/installing/guided.rst
@@ -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 |
+----------------------+--------------------------------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------+
diff --git a/examples/config-sample.json b/examples/config-sample.json
index 18644860..dc8693a7 100644
--- a/examples/config-sample.json
+++ b/examples/config-sample.json
@@ -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"
-} \ No newline at end of file
+}
diff --git a/examples/custom-command-sample.json b/examples/custom-command-sample.json
index 9c39e15f..8d8d611d 100644
--- a/examples/custom-command-sample.json
+++ b/examples/custom-command-sample.json
@@ -13,7 +13,7 @@
"keyboard-layout": "us",
"mirror-region": "Worldwide",
"nic": {
- "NetworkManager": true
+ "type": "NM"
},
"ntp": true,
"packages": ["docker", "git", "wget", "zsh"],
diff --git a/examples/guided.py b/examples/guided.py
index 23fd45a6..d2447341 100644
--- a/examples/guided.py
+++ b/examples/guided.py
@@ -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)
diff --git a/examples/swiss.py b/examples/swiss.py
index f60ff60f..4eb51a05 100644
--- a/examples/swiss.py
+++ b/examples/swiss.py
@@ -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)
diff --git a/schema.json b/schema.json
index aacf3f07..e4b19b64 100644
--- a/schema.json
+++ b/schema.json
@@ -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": {