Support for multiple network interfaces (#1052)
* Support for multiple network interfaces * Fix mypy * Fix flake8 Co-authored-by: Daniel Girtler <girtler.daniel@gmail.com>
This commit is contained in:
parent
2529d6a5f5
commit
477b5b120e
|
|
@ -13,5 +13,6 @@ jobs:
|
||||||
- run: python --version
|
- run: python --version
|
||||||
- run: mypy --version
|
- run: mypy --version
|
||||||
# one day this will be enabled
|
# one day this will be enabled
|
||||||
|
# run: mypy --strict --module archinstall || exit 0
|
||||||
- name: run mypy
|
- name: run mypy
|
||||||
run: mypy --strict --module archinstall || exit 0
|
run: mypy --follow-imports=skip archinstall/lib/menu/selection_menu.py archinstall/lib/models/network_configuration.py archinstall/lib/menu/list_manager.py archinstall/lib/user_interaction/network_conf.py
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ from .lib.installer import __packages__, Installer, accessibility_tools_in_use
|
||||||
from .lib.locale_helpers import *
|
from .lib.locale_helpers import *
|
||||||
from .lib.luks import *
|
from .lib.luks import *
|
||||||
from .lib.mirrors import *
|
from .lib.mirrors import *
|
||||||
|
from .lib.models.network_configuration import NetworkConfigurationHandler
|
||||||
from .lib.networking import *
|
from .lib.networking import *
|
||||||
from .lib.output import *
|
from .lib.output import *
|
||||||
from .lib.models.dataclasses import (
|
from .lib.models.dataclasses import (
|
||||||
|
|
@ -207,7 +208,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('nic', None) is not None:
|
||||||
arguments['nic'] = NetworkConfiguration.parse_arguments(arguments.get('nic'))
|
handler = NetworkConfigurationHandler()
|
||||||
|
handler.parse_arguments(arguments.get('nic'))
|
||||||
|
arguments['nic'] = handler.configuration
|
||||||
|
|
||||||
def post_process_arguments(arguments):
|
def post_process_arguments(arguments):
|
||||||
storage['arguments'] = arguments
|
storage['arguments'] = arguments
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, List, Optional
|
from typing import Any, List, Optional, Union
|
||||||
|
|
||||||
from ..menu import Menu
|
from ..menu import Menu
|
||||||
from ..menu.selection_menu import Selector, GeneralMenu
|
from ..menu.selection_menu import Selector, GeneralMenu
|
||||||
from ..general import SysCommand, secret
|
from ..general import SysCommand, secret
|
||||||
from ..hardware import has_uefi
|
from ..hardware import has_uefi
|
||||||
|
from ..models import NetworkConfiguration
|
||||||
from ..storage import storage
|
from ..storage import storage
|
||||||
from ..output import log
|
from ..output import log
|
||||||
from ..profiles import is_desktop_profile
|
from ..profiles import is_desktop_profile
|
||||||
|
|
@ -139,7 +140,7 @@ class GlobalMenu(GeneralMenu):
|
||||||
Selector(
|
Selector(
|
||||||
_('Configure network'),
|
_('Configure network'),
|
||||||
ask_to_configure_network,
|
ask_to_configure_network,
|
||||||
display_func=lambda x: x if x else _('Not configured, unavailable unless setup manually'),
|
display_func=lambda x: self._prev_network_configuration(x),
|
||||||
default={})
|
default={})
|
||||||
self._menu_options['timezone'] = \
|
self._menu_options['timezone'] = \
|
||||||
Selector(
|
Selector(
|
||||||
|
|
@ -192,6 +193,16 @@ class GlobalMenu(GeneralMenu):
|
||||||
return _('Install ({} config(s) missing)').format(missing)
|
return _('Install ({} config(s) missing)').format(missing)
|
||||||
return 'Install'
|
return 'Install'
|
||||||
|
|
||||||
|
def _prev_network_configuration(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):
|
||||||
|
ifaces = [x.iface for x in cur_value]
|
||||||
|
return f'Configured ifaces: {ifaces}'
|
||||||
|
else:
|
||||||
|
return str(cur_value)
|
||||||
|
|
||||||
def _prev_install_missing_config(self) -> Optional[str]:
|
def _prev_install_missing_config(self) -> Optional[str]:
|
||||||
if missing := self._missing_configs():
|
if missing := self._missing_configs():
|
||||||
text = str(_('Missing configurations:\n'))
|
text = str(_('Missing configurations:\n'))
|
||||||
|
|
|
||||||
|
|
@ -89,10 +89,21 @@ from .text_input import TextInput
|
||||||
from .menu import Menu
|
from .menu import Menu
|
||||||
from os import system
|
from os import system
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from typing import Union
|
from typing import Union, Any, List, TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
_: Any
|
||||||
|
|
||||||
|
|
||||||
class ListManager:
|
class ListManager:
|
||||||
def __init__(self,prompt :str, base_list :Union[list,dict] ,base_actions :list = None,null_action :str = None, default_action :Union[str,list] = None, header :Union[str,list] = None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
prompt :str,
|
||||||
|
base_list :Union[list,dict] ,
|
||||||
|
base_actions :list = None,
|
||||||
|
null_action :str = None,
|
||||||
|
default_action :Union[str,list] = None,
|
||||||
|
header :Union[str,list] = None):
|
||||||
"""
|
"""
|
||||||
param :prompt Text which will appear at the header
|
param :prompt Text which will appear at the header
|
||||||
type param: string | DeferredTranslation
|
type param: string | DeferredTranslation
|
||||||
|
|
@ -115,16 +126,16 @@ class ListManager:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
explainer = str(_('\n Choose an object from the list, and select one of the available actions for it to execute'))
|
explainer = str(_('\n Choose an object from the list, and select one of the available actions for it to execute'))
|
||||||
self.prompt = prompt + explainer if prompt else explainer
|
self._prompt = prompt + explainer if prompt else explainer
|
||||||
|
|
||||||
self.null_action = str(null_action) if null_action else None
|
self._null_action = str(null_action) if null_action else None
|
||||||
|
|
||||||
if not default_action:
|
if not default_action:
|
||||||
self.default_action = [self.null_action,]
|
self._default_action = [self._null_action]
|
||||||
elif isinstance(default_action,(list,tuple)):
|
elif isinstance(default_action,(list,tuple)):
|
||||||
self.default_action = default_action
|
self._default_action = default_action
|
||||||
else:
|
else:
|
||||||
self.default_action = [str(default_action),]
|
self._default_action = [str(default_action),]
|
||||||
|
|
||||||
self.header = header if header else None
|
self.header = header if header else None
|
||||||
self.cancel_action = str(_('Cancel'))
|
self.cancel_action = str(_('Cancel'))
|
||||||
|
|
@ -133,24 +144,23 @@ class ListManager:
|
||||||
self.bottom_list = [self.confirm_action,self.cancel_action]
|
self.bottom_list = [self.confirm_action,self.cancel_action]
|
||||||
self.bottom_item = [self.cancel_action]
|
self.bottom_item = [self.cancel_action]
|
||||||
self.base_actions = base_actions if base_actions else [str(_('Add')),str(_('Copy')),str(_('Edit')),str(_('Delete'))]
|
self.base_actions = base_actions if base_actions else [str(_('Add')),str(_('Copy')),str(_('Edit')),str(_('Delete'))]
|
||||||
|
|
||||||
self.base_data = base_list
|
self.base_data = base_list
|
||||||
self.data = copy(base_list) # as refs, changes are immediate
|
self._data = copy(base_list) # as refs, changes are immediate
|
||||||
# default values for the null case
|
# default values for the null case
|
||||||
self.target = None
|
self.target = None
|
||||||
self.action = self.null_action
|
self.action = self._null_action
|
||||||
if len(self.data) == 0 and self.null_action:
|
if len(self._data) == 0 and self._null_action:
|
||||||
self.exec_action()
|
self.exec_action(self._data)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while True:
|
while True:
|
||||||
self.data_formatted = self.reformat()
|
self._data_formatted = self.reformat(self._data)
|
||||||
options = self.data_formatted + [self.separator]
|
options = self._data_formatted + [self.separator]
|
||||||
if self.default_action:
|
if self._default_action:
|
||||||
options += self.default_action
|
options += self._default_action
|
||||||
options += self.bottom_list
|
options += self.bottom_list
|
||||||
system('clear')
|
system('clear')
|
||||||
target = Menu(self.prompt,
|
target = Menu(self._prompt,
|
||||||
options,
|
options,
|
||||||
sort=False,
|
sort=False,
|
||||||
clear_screen=False,
|
clear_screen=False,
|
||||||
|
|
@ -162,53 +172,53 @@ class ListManager:
|
||||||
break
|
break
|
||||||
if target and target == self.separator:
|
if target and target == self.separator:
|
||||||
continue
|
continue
|
||||||
if target and target in self.default_action:
|
if target and target in self._default_action:
|
||||||
self.action = target
|
self.action = target
|
||||||
target = None
|
target = None
|
||||||
self.target = None
|
self.target = None
|
||||||
self.exec_action()
|
self.exec_action(self._data)
|
||||||
continue
|
continue
|
||||||
if isinstance(self.data,dict):
|
if isinstance(self._data,dict):
|
||||||
key = list(self.data.keys())[self.data_formatted.index(target)]
|
key = list(self._data.keys())[self._data_formatted.index(target)]
|
||||||
self.target = {key: self.data[key]}
|
self.target = {key: self._data[key]}
|
||||||
else:
|
else:
|
||||||
self.target = self.data[self.data_formatted.index(target)]
|
self.target = self._data[self._data_formatted.index(target)]
|
||||||
# Possible enhacement. If run_actions returns false a message line indicating the failure
|
# Possible enhacement. If run_actions returns false a message line indicating the failure
|
||||||
self.run_actions(target)
|
self.run_actions(target)
|
||||||
|
|
||||||
if not target or target == self.cancel_action: # TODO dubious
|
if not target or target == self.cancel_action: # TODO dubious
|
||||||
return self.base_data # return the original list
|
return self.base_data # return the original list
|
||||||
else:
|
else:
|
||||||
return self.data
|
return self._data
|
||||||
|
|
||||||
def run_actions(self,prompt_data=None):
|
def run_actions(self,prompt_data=None):
|
||||||
options = self.action_list() + self.bottom_item
|
options = self.action_list() + self.bottom_item
|
||||||
prompt = _("Select an action for < {} >").format(prompt_data if prompt_data else self.target)
|
prompt = _("Select an action for < {} >").format(prompt_data if prompt_data else self.target)
|
||||||
self.action = Menu(prompt,
|
self.action = Menu(
|
||||||
options,
|
prompt,
|
||||||
sort=False,
|
options,
|
||||||
skip=False,
|
sort=False,
|
||||||
clear_screen=False,
|
clear_screen=False,
|
||||||
clear_menu_on_exit=False,
|
clear_menu_on_exit=False,
|
||||||
preset_values=self.bottom_item,
|
preset_values=self.bottom_item,
|
||||||
show_search_hint=False).run()
|
show_search_hint=False).run()
|
||||||
if self.action == self.cancel_action:
|
if not self.action or self.action == self.cancel_action:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return self.exec_action()
|
return self.exec_action(self._data)
|
||||||
"""
|
"""
|
||||||
The following methods are expected to be overwritten by the user if the needs of the list are beyond the simple case
|
The following methods are expected to be overwritten by the user if the needs of the list are beyond the simple case
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def reformat(self):
|
def reformat(self, data: Any) -> List[Any]:
|
||||||
"""
|
"""
|
||||||
method to get the data in a format suitable to be shown
|
method to get the data in a format suitable to be shown
|
||||||
It is executed once for run loop and processes the whole self.data structure
|
It is executed once for run loop and processes the whole self._data structure
|
||||||
"""
|
"""
|
||||||
if isinstance(self.data,dict):
|
if isinstance(data,dict):
|
||||||
return list(map(lambda x:f"{x} : {self.data[x]}",self.data))
|
return list(map(lambda x:f"{x} : {data[x]}",data))
|
||||||
else:
|
else:
|
||||||
return list(map(lambda x:str(x),self.data))
|
return list(map(lambda x:str(x),data))
|
||||||
|
|
||||||
def action_list(self):
|
def action_list(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -217,32 +227,32 @@ class ListManager:
|
||||||
"""
|
"""
|
||||||
return self.base_actions
|
return self.base_actions
|
||||||
|
|
||||||
def exec_action(self):
|
def exec_action(self, data: Any):
|
||||||
"""
|
"""
|
||||||
what's executed one an item (self.target) and one action (self.action) is selected.
|
what's executed one an item (self.target) and one action (self.action) is selected.
|
||||||
Should be overwritten by the user
|
Should be overwritten by the user
|
||||||
The result is expected to update self.data in this routine, else it is ignored
|
The result is expected to update self._data in this routine, else it is ignored
|
||||||
The basic code is useful for simple lists and dictionaries (key:value pairs, both strings)
|
The basic code is useful for simple lists and dictionaries (key:value pairs, both strings)
|
||||||
"""
|
"""
|
||||||
# TODO guarantee unicity
|
# TODO guarantee unicity
|
||||||
if isinstance(self.data,list):
|
if isinstance(self._data,list):
|
||||||
if self.action == str(_('Add')):
|
if self.action == str(_('Add')):
|
||||||
self.target = TextInput(_('Add :'),None).run()
|
self.target = TextInput(_('Add :'),None).run()
|
||||||
self.data.append(self.target)
|
self._data.append(self.target)
|
||||||
if self.action == str(_('Copy')):
|
if self.action == str(_('Copy')):
|
||||||
while True:
|
while True:
|
||||||
target = TextInput(_('Copy to :'),self.target).run()
|
target = TextInput(_('Copy to :'),self.target).run()
|
||||||
if target != self.target:
|
if target != self.target:
|
||||||
self.data.append(self.target)
|
self._data.append(self.target)
|
||||||
break
|
break
|
||||||
elif self.action == str(_('Edit')):
|
elif self.action == str(_('Edit')):
|
||||||
tgt = self.target
|
tgt = self.target
|
||||||
idx = self.data.index(self.target)
|
idx = self._data.index(self.target)
|
||||||
result = TextInput(_('Edite :'),tgt).run()
|
result = TextInput(_('Edite :'),tgt).run()
|
||||||
self.data[idx] = result
|
self._data[idx] = result
|
||||||
elif self.action == str(_('Delete')):
|
elif self.action == str(_('Delete')):
|
||||||
del self.data[self.data.index(self.target)]
|
del self._data[self._data.index(self.target)]
|
||||||
elif isinstance(self.data,dict):
|
elif isinstance(self._data,dict):
|
||||||
# allows overwrites
|
# allows overwrites
|
||||||
if self.target:
|
if self.target:
|
||||||
origkey,origval = list(self.target.items())[0]
|
origkey,origval = list(self.target.items())[0]
|
||||||
|
|
@ -252,27 +262,15 @@ class ListManager:
|
||||||
if self.action == str(_('Add')):
|
if self.action == str(_('Add')):
|
||||||
key = TextInput(_('Key :'),None).run()
|
key = TextInput(_('Key :'),None).run()
|
||||||
value = TextInput(_('Value :'),None).run()
|
value = TextInput(_('Value :'),None).run()
|
||||||
self.data[key] = value
|
self._data[key] = value
|
||||||
if self.action == str(_('Copy')):
|
if self.action == str(_('Copy')):
|
||||||
while True:
|
while True:
|
||||||
key = TextInput(_('Copy to new key:'),origkey).run()
|
key = TextInput(_('Copy to new key:'),origkey).run()
|
||||||
if key != origkey:
|
if key != origkey:
|
||||||
self.data[key] = origval
|
self._data[key] = origval
|
||||||
break
|
break
|
||||||
elif self.action == str(_('Edit')):
|
elif self.action == str(_('Edit')):
|
||||||
value = TextInput(_(f'Edit {origkey} :'),origval).run()
|
value = TextInput(_(f'Edit {origkey} :'),origval).run()
|
||||||
self.data[origkey] = value
|
self._data[origkey] = value
|
||||||
elif self.action == str(_('Delete')):
|
elif self.action == str(_('Delete')):
|
||||||
del self.data[origkey]
|
del self._data[origkey]
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# opciones = ['uno','dos','tres','cuatro']
|
|
||||||
# opciones = archinstall.list_mirrors()
|
|
||||||
opciones = {'uno':1,'dos':2,'tres':3,'cuatro':4}
|
|
||||||
acciones = ['editar','borrar','añadir']
|
|
||||||
cabecera = ["En Jaen Donde Resido","Vive don Lope de Sosa"]
|
|
||||||
opciones = ListManager('Vamos alla',opciones,None,_('Add'),default_action=acciones,header=cabecera).run()
|
|
||||||
print(opciones)
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import sys
|
|
||||||
import logging
|
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
from typing import Callable, Any, List, Iterator, Tuple, Optional, Dict, TYPE_CHECKING
|
from typing import Callable, Any, List, Iterator, Tuple, Optional, Dict, TYPE_CHECKING
|
||||||
|
|
||||||
from .menu import Menu
|
from .menu import Menu
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,12 @@ from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List, Optional, Dict
|
from typing import List, Optional, Dict, Union, Any, TYPE_CHECKING
|
||||||
|
|
||||||
|
from ..output import log
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
_: Any
|
||||||
|
|
||||||
|
|
||||||
class NicType(str, Enum):
|
class NicType(str, Enum):
|
||||||
|
|
@ -14,11 +19,11 @@ class NicType(str, Enum):
|
||||||
@dataclass
|
@dataclass
|
||||||
class NetworkConfiguration:
|
class NetworkConfiguration:
|
||||||
type: NicType
|
type: NicType
|
||||||
iface: str = None
|
iface: Optional[str] = None
|
||||||
ip: str = None
|
ip: Optional[str] = None
|
||||||
dhcp: bool = True
|
dhcp: bool = True
|
||||||
gateway: str = None
|
gateway: Optional[str] = None
|
||||||
dns: List[str] = None
|
dns: Union[None, List[str]] = None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.is_iso():
|
if self.is_iso():
|
||||||
|
|
@ -37,63 +42,6 @@ class NetworkConfiguration:
|
||||||
def json(self):
|
def json(self):
|
||||||
return self.__dict__
|
return self.__dict__
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse_arguments(cls, config: Union[str,Dict[str, str]]) -> Optional["NetworkConfiguration"]:
|
|
||||||
from ... import log
|
|
||||||
|
|
||||||
nic_type = config.get('type', None)
|
|
||||||
|
|
||||||
if not nic_type:
|
|
||||||
# old style definitions
|
|
||||||
if isinstance(config,str): # is a ISO network
|
|
||||||
return NetworkConfiguration(NicType.ISO)
|
|
||||||
elif config.get('NetworkManager'): # is a network manager configuration
|
|
||||||
return NetworkConfiguration(NicType.NM)
|
|
||||||
elif 'ip' in config:
|
|
||||||
return NetworkConfiguration(
|
|
||||||
NicType.MANUAL,
|
|
||||||
iface=config.get('nic', ''),
|
|
||||||
ip=config.get('ip'),
|
|
||||||
gateway=config.get('gateway', ''),
|
|
||||||
dns=config.get('dns', []),
|
|
||||||
dhcp=False
|
|
||||||
)
|
|
||||||
elif 'nic' in config:
|
|
||||||
return NetworkConfiguration(
|
|
||||||
NicType.MANUAL,
|
|
||||||
iface=config.get('nic', ''),
|
|
||||||
dhcp=True
|
|
||||||
)
|
|
||||||
else: # not recognized
|
|
||||||
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:
|
def is_iso(self) -> bool:
|
||||||
return self.type == NicType.ISO
|
return self.type == NicType.ISO
|
||||||
|
|
||||||
|
|
@ -103,37 +51,123 @@ class NetworkConfiguration:
|
||||||
def is_manual(self) -> bool:
|
def is_manual(self) -> bool:
|
||||||
return self.type == NicType.MANUAL
|
return self.type == NicType.MANUAL
|
||||||
|
|
||||||
def config_installer(self, installation: 'Installer'):
|
|
||||||
# If user selected to copy the current ISO network configuration
|
class NetworkConfigurationHandler:
|
||||||
# Perform a copy of the config
|
def __init__(self, config: Union[None, NetworkConfiguration, List[NetworkConfiguration]] = None):
|
||||||
if self.is_iso():
|
self._configuration = config
|
||||||
installation.copy_iso_network_config(enable_services=True) # Sources the ISO network configuration to the install medium.
|
|
||||||
elif self.is_network_manager():
|
@property
|
||||||
installation.add_additional_packages("networkmanager")
|
def configuration(self):
|
||||||
installation.enable_service('NetworkManager.service')
|
return self._configuration
|
||||||
# Otherwise, if a interface was selected, configure that interface
|
|
||||||
elif self.is_manual():
|
def config_installer(self, installation: Any):
|
||||||
installation.configure_nic(self)
|
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-networkd')
|
||||||
installation.enable_service('systemd-resolved')
|
installation.enable_service('systemd-resolved')
|
||||||
|
|
||||||
def get(self, key :str, default_value :Any = None) -> Any:
|
|
||||||
result = self.__getitem__(key)
|
|
||||||
if result is None:
|
|
||||||
return default_value
|
|
||||||
else:
|
else:
|
||||||
return result
|
# 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(
|
||||||
|
enable_services=True) # Sources the ISO network configuration to the install medium.
|
||||||
|
elif self._configuration.is_network_manager():
|
||||||
|
installation.add_additional_packages("networkmanager")
|
||||||
|
installation.enable_service('NetworkManager.service')
|
||||||
|
|
||||||
def __getitem__(self, key :str) -> Any:
|
def _backwards_compability_config(self, config: Union[str,Dict[str, str]]) -> Union[List[NetworkConfiguration], NetworkConfiguration, None]:
|
||||||
if key == 'type':
|
def get(config: Dict[str, str], key: str) -> List[str]:
|
||||||
return self.type
|
if (value := config.get(key, None)) is not None:
|
||||||
elif key == 'iface':
|
return [value]
|
||||||
return self.iface
|
return []
|
||||||
elif key == 'gateway':
|
|
||||||
return self.gateway
|
if isinstance(config, str): # is a ISO network
|
||||||
elif key == 'dns':
|
return NetworkConfiguration(NicType.ISO)
|
||||||
return self.dns
|
elif config.get('NetworkManager'): # is a network manager configuration
|
||||||
elif key == 'dhcp':
|
return NetworkConfiguration(NicType.NM)
|
||||||
return self.dhcp
|
elif 'ip' in config:
|
||||||
else:
|
return [NetworkConfiguration(
|
||||||
raise KeyError(f"key {key} not available at NetworkConfiguration")
|
NicType.MANUAL,
|
||||||
|
iface=config.get('nic', ''),
|
||||||
|
ip=config.get('ip'),
|
||||||
|
gateway=config.get('gateway', ''),
|
||||||
|
dns=get(config, 'dns'),
|
||||||
|
dhcp=False
|
||||||
|
)]
|
||||||
|
elif 'nic' in config:
|
||||||
|
return [NetworkConfiguration(
|
||||||
|
NicType.MANUAL,
|
||||||
|
iface=config.get('nic', ''),
|
||||||
|
dhcp=True
|
||||||
|
)]
|
||||||
|
else: # not recognized
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _parse_manual_config(self, config: Dict[str, Any]) -> Union[None, List[NetworkConfiguration]]:
|
||||||
|
manual_configs: List = config.get('config', [])
|
||||||
|
|
||||||
|
if not manual_configs:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not isinstance(manual_configs, list):
|
||||||
|
log(_('Manual configuration setting must be a list'))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
configurations = []
|
||||||
|
|
||||||
|
for manual_config in manual_configs:
|
||||||
|
iface = manual_config.get('iface', None)
|
||||||
|
|
||||||
|
if iface is None:
|
||||||
|
log(_('No iface specified for manual configuration'))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
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 = config.get('ip', '')
|
||||||
|
if not ip:
|
||||||
|
log(_('Manual nic configuration with no auto DHCP requires an IP address'), fg='red')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
configurations.append(
|
||||||
|
NetworkConfiguration(
|
||||||
|
NicType.MANUAL,
|
||||||
|
iface=iface,
|
||||||
|
ip=ip,
|
||||||
|
gateway=config.get('gateway', ''),
|
||||||
|
dns=config.get('dns', []),
|
||||||
|
dhcp=False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return configurations
|
||||||
|
|
||||||
|
def parse_arguments(self, config: Any):
|
||||||
|
nic_type = config.get('type', None)
|
||||||
|
|
||||||
|
if not nic_type:
|
||||||
|
# old style definitions
|
||||||
|
network_config = self._backwards_compability_config(config)
|
||||||
|
if network_config:
|
||||||
|
return network_config
|
||||||
|
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:
|
||||||
|
self._configuration = NetworkConfiguration(type_)
|
||||||
|
else: # manual configuration settings
|
||||||
|
self._configuration = self._parse_manual_config(config)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from typing import Any, Dict, TYPE_CHECKING
|
from typing import Any, Dict, TYPE_CHECKING, List
|
||||||
|
|
||||||
from ..menu import Menu
|
from ..menu import Menu
|
||||||
from ..menu.list_manager import ListManager
|
from ..menu.list_manager import ListManager
|
||||||
|
|
@ -34,25 +34,22 @@ class UserList(ListManager):
|
||||||
str(_('Promote/Demote user')),
|
str(_('Promote/Demote user')),
|
||||||
str(_('Delete User'))
|
str(_('Delete User'))
|
||||||
]
|
]
|
||||||
self.default_action = self.actions[0]
|
super().__init__(prompt, lusers, self.actions, self.actions[0])
|
||||||
super().__init__(prompt, lusers, self.actions, self.default_action)
|
|
||||||
|
|
||||||
def reformat(self):
|
def reformat(self, data: Any) -> List[Any]:
|
||||||
|
def format_element(elem :str):
|
||||||
def format_element(elem):
|
|
||||||
# secret gives away the length of the password
|
# secret gives away the length of the password
|
||||||
if self.data[elem].get('!password'):
|
if data[elem].get('!password'):
|
||||||
pwd = '*' * 16
|
pwd = '*' * 16
|
||||||
# pwd = archinstall.secret(self.data[elem]['!password'])
|
|
||||||
else:
|
else:
|
||||||
pwd = ''
|
pwd = ''
|
||||||
if self.data[elem].get('sudoer'):
|
if data[elem].get('sudoer'):
|
||||||
super = 'Superuser'
|
super_user = 'Superuser'
|
||||||
else:
|
else:
|
||||||
super = ' '
|
super_user = ' '
|
||||||
return f"{elem:16}: password {pwd:16} {super}"
|
return f"{elem:16}: password {pwd:16} {super_user}"
|
||||||
|
|
||||||
return list(map(lambda x: format_element(x), self.data))
|
return list(map(lambda x: format_element(x), data))
|
||||||
|
|
||||||
def action_list(self):
|
def action_list(self):
|
||||||
if self.target:
|
if self.target:
|
||||||
|
|
@ -71,7 +68,7 @@ class UserList(ListManager):
|
||||||
else:
|
else:
|
||||||
return self.actions
|
return self.actions
|
||||||
|
|
||||||
def exec_action(self):
|
def exec_action(self, data: Any):
|
||||||
if self.target:
|
if self.target:
|
||||||
active_user = list(self.target.keys())[0]
|
active_user = list(self.target.keys())[0]
|
||||||
else:
|
else:
|
||||||
|
|
@ -80,14 +77,14 @@ class UserList(ListManager):
|
||||||
if self.action == self.actions[0]: # add
|
if self.action == self.actions[0]: # add
|
||||||
new_user = self.add_user()
|
new_user = self.add_user()
|
||||||
# no unicity check, if exists will be replaced
|
# no unicity check, if exists will be replaced
|
||||||
self.data.update(new_user)
|
data.update(new_user)
|
||||||
elif self.action == self.actions[1]: # change password
|
elif self.action == self.actions[1]: # change password
|
||||||
self.data[active_user]['!password'] = get_password(
|
data[active_user]['!password'] = get_password(
|
||||||
prompt=str(_('Password for user "{}": ').format(active_user)))
|
prompt=str(_('Password for user "{}": ').format(active_user)))
|
||||||
elif self.action == self.actions[2]: # promote/demote
|
elif self.action == self.actions[2]: # promote/demote
|
||||||
self.data[active_user]['sudoer'] = not self.data[active_user]['sudoer']
|
data[active_user]['sudoer'] = not data[active_user]['sudoer']
|
||||||
elif self.action == self.actions[3]: # delete
|
elif self.action == self.actions[3]: # delete
|
||||||
del self.data[active_user]
|
del data[active_user]
|
||||||
|
|
||||||
def _check_for_correct_username(self, username: str) -> bool:
|
def _check_for_correct_username(self, username: str) -> bool:
|
||||||
if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32:
|
if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32:
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import logging
|
import logging
|
||||||
from copy import copy
|
from typing import Any, Optional, TYPE_CHECKING, List, Union
|
||||||
from typing import Any, Optional, Dict, TYPE_CHECKING
|
|
||||||
|
|
||||||
from ..menu.text_input import TextInput
|
from ..menu.text_input import TextInput
|
||||||
from ..models.network_configuration import NetworkConfiguration, NicType
|
from ..models.network_configuration import NetworkConfiguration, NicType
|
||||||
|
|
@ -11,69 +10,77 @@ from ..models.network_configuration import NetworkConfiguration, NicType
|
||||||
from ..networking import list_interfaces
|
from ..networking import list_interfaces
|
||||||
from ..menu import Menu
|
from ..menu import Menu
|
||||||
from ..output import log
|
from ..output import log
|
||||||
|
from ..menu.list_manager import ListManager
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
_: Any
|
_: Any
|
||||||
|
|
||||||
|
|
||||||
def ask_to_configure_network(preset: Dict[str, Any] = {}) -> Optional[NetworkConfiguration]:
|
class ManualNetworkConfig(ListManager):
|
||||||
"""
|
"""
|
||||||
Configure the network on the newly installed system
|
subclass of ListManager for the managing of network configuration accounts
|
||||||
"""
|
"""
|
||||||
interfaces = {
|
|
||||||
'none': str(_('No network configuration')),
|
def __init__(self, prompt: str, ifaces: Union[None, NetworkConfiguration, List[NetworkConfiguration]]):
|
||||||
'iso_config': str(_('Copy ISO network configuration to installation')),
|
"""
|
||||||
'network_manager': str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)')),
|
param: prompt
|
||||||
**list_interfaces()
|
type: str
|
||||||
}
|
param: ifaces already defined previously
|
||||||
# for this routine it's easier to set the cursor position rather than a preset value
|
type: Dict
|
||||||
cursor_idx = None
|
"""
|
||||||
if preset:
|
|
||||||
if preset['type'] == 'iso_config':
|
if ifaces is not None and isinstance(ifaces, list):
|
||||||
cursor_idx = 0
|
display_values = {iface.iface: iface for iface in ifaces}
|
||||||
elif preset['type'] == 'network_manager':
|
|
||||||
cursor_idx = 1
|
|
||||||
else:
|
else:
|
||||||
try:
|
display_values = {}
|
||||||
# let's hope order in dictionaries stay
|
|
||||||
cursor_idx = list(interfaces.values()).index(preset.get('type'))
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
nic = Menu(_('Select one network interface to configure'), interfaces.values(), cursor_index=cursor_idx,
|
self._action_add = str(_('Add interface'))
|
||||||
sort=False).run()
|
self._action_edit = str(_('Edit interface'))
|
||||||
|
self._action_delete = str(_('Delete interface'))
|
||||||
|
|
||||||
if not nic:
|
self._iface_actions = [self._action_edit, self._action_delete]
|
||||||
return None
|
|
||||||
|
|
||||||
if nic == interfaces['none']:
|
super().__init__(prompt, display_values, self._iface_actions, self._action_add)
|
||||||
return None
|
|
||||||
elif nic == interfaces['iso_config']:
|
|
||||||
return NetworkConfiguration(NicType.ISO)
|
|
||||||
elif nic == interfaces['network_manager']:
|
|
||||||
return NetworkConfiguration(NicType.NM)
|
|
||||||
else:
|
|
||||||
# Current workaround:
|
|
||||||
# For selecting modes without entering text within brackets,
|
|
||||||
# printing out this part separate from options, passed in
|
|
||||||
# `generic_select`
|
|
||||||
# we only keep data if it is the same nic as before
|
|
||||||
if preset.get('type') != nic:
|
|
||||||
preset_d = {'type': nic, 'dhcp': True, 'ip': None, 'gateway': None, 'dns': []}
|
|
||||||
else:
|
|
||||||
preset_d = copy(preset)
|
|
||||||
|
|
||||||
|
def run_manual(self) -> List[NetworkConfiguration]:
|
||||||
|
ifaces = super().run()
|
||||||
|
if ifaces is not None:
|
||||||
|
return list(ifaces.values())
|
||||||
|
return []
|
||||||
|
|
||||||
|
def exec_action(self, data: Any):
|
||||||
|
if self.action == self._action_add:
|
||||||
|
iface_name = self._select_iface(data.keys())
|
||||||
|
if iface_name:
|
||||||
|
iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name)
|
||||||
|
data[iface_name] = self._edit_iface(iface)
|
||||||
|
elif self.target:
|
||||||
|
iface_name = list(self.target.keys())[0]
|
||||||
|
iface = data[iface_name]
|
||||||
|
|
||||||
|
if self.action == self._action_edit:
|
||||||
|
data[iface_name] = self._edit_iface(iface)
|
||||||
|
elif self.action == self._action_delete:
|
||||||
|
del data[iface_name]
|
||||||
|
|
||||||
|
def _select_iface(self, existing_ifaces: List[str]) -> Optional[str]:
|
||||||
|
all_ifaces = list_interfaces().values()
|
||||||
|
available = set(all_ifaces) - set(existing_ifaces)
|
||||||
|
iface = Menu(str(_('Select interface to add')), list(available), skip=True).run()
|
||||||
|
return iface
|
||||||
|
|
||||||
|
def _edit_iface(self, edit_iface :NetworkConfiguration):
|
||||||
|
iface_name = edit_iface.iface
|
||||||
modes = ['DHCP (auto detect)', 'IP (static)']
|
modes = ['DHCP (auto detect)', 'IP (static)']
|
||||||
default_mode = 'DHCP (auto detect)'
|
default_mode = 'DHCP (auto detect)'
|
||||||
cursor_idx = 0 if preset_d.get('dhcp', True) else 1
|
|
||||||
|
|
||||||
prompt = _('Select which mode to configure for "{}" or skip to use default mode "{}"').format(nic, default_mode)
|
prompt = _('Select which mode to configure for "{}" or skip to use default mode "{}"').format(iface_name, default_mode)
|
||||||
mode = Menu(prompt, modes, default_option=default_mode, cursor_index=cursor_idx).run()
|
mode = Menu(prompt, modes, default_option=default_mode).run()
|
||||||
# TODO preset values for ip and gateway
|
|
||||||
if mode == 'IP (static)':
|
if mode == 'IP (static)':
|
||||||
while 1:
|
while 1:
|
||||||
prompt = _('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(nic)
|
prompt = _('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(iface_name)
|
||||||
ip = TextInput(prompt, preset_d.get('ip')).run().strip()
|
ip = TextInput(prompt, edit_iface.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)
|
||||||
|
|
@ -84,7 +91,7 @@ def ask_to_configure_network(preset: Dict[str, Any] = {}) -> Optional[NetworkCon
|
||||||
# Implemented new check for correct gateway IP address
|
# Implemented new check for correct gateway IP address
|
||||||
while 1:
|
while 1:
|
||||||
gateway = TextInput(_('Enter your gateway (router) IP address or leave blank for none: '),
|
gateway = TextInput(_('Enter your gateway (router) IP address or leave blank for none: '),
|
||||||
preset_d.get('gateway')).run().strip()
|
edit_iface.gateway).run().strip()
|
||||||
try:
|
try:
|
||||||
if len(gateway) == 0:
|
if len(gateway) == 0:
|
||||||
gateway = None
|
gateway = None
|
||||||
|
|
@ -94,18 +101,58 @@ def ask_to_configure_network(preset: Dict[str, Any] = {}) -> Optional[NetworkCon
|
||||||
except ValueError:
|
except ValueError:
|
||||||
log("You need to enter a valid gateway (router) IP address.", level=logging.WARNING, fg='red')
|
log("You need to enter a valid gateway (router) IP address.", level=logging.WARNING, fg='red')
|
||||||
|
|
||||||
dns = None
|
if edit_iface.dns:
|
||||||
if preset_d.get('dns'):
|
display_dns = ' '.join(edit_iface.dns)
|
||||||
preset_d['dns'] = ' '.join(preset_d['dns'])
|
|
||||||
else:
|
else:
|
||||||
preset_d['dns'] = None
|
display_dns = None
|
||||||
dns_input = TextInput(_('Enter your DNS servers (space separated, blank for none): '),
|
dns_input = TextInput(_('Enter your DNS servers (space separated, blank for none): '), display_dns).run().strip()
|
||||||
preset_d['dns']).run().strip()
|
|
||||||
|
|
||||||
if len(dns_input):
|
if len(dns_input):
|
||||||
dns = dns_input.split(' ')
|
dns = dns_input.split(' ')
|
||||||
|
|
||||||
return NetworkConfiguration(NicType.MANUAL, iface=nic, ip=ip, gateway=gateway, dns=dns, dhcp=False)
|
return NetworkConfiguration(NicType.MANUAL, 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=nic)
|
return NetworkConfiguration(NicType.MANUAL, iface=iface_name)
|
||||||
|
|
||||||
|
|
||||||
|
def ask_to_configure_network(preset: Union[None, NetworkConfiguration, List[NetworkConfiguration]]) -> Optional[Union[List[NetworkConfiguration], 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
|
||||||
|
|
||||||
|
nic = Menu(_(
|
||||||
|
'Select one network interface to configure'),
|
||||||
|
list(network_options.values()),
|
||||||
|
cursor_index=cursor_idx,
|
||||||
|
sort=False
|
||||||
|
).run()
|
||||||
|
|
||||||
|
if not nic:
|
||||||
|
return preset
|
||||||
|
|
||||||
|
if nic == network_options['none']:
|
||||||
|
return None
|
||||||
|
elif nic == network_options['iso_config']:
|
||||||
|
return NetworkConfiguration(NicType.ISO)
|
||||||
|
elif nic == network_options['network_manager']:
|
||||||
|
return NetworkConfiguration(NicType.NM)
|
||||||
|
elif nic == network_options['manual']:
|
||||||
|
manual = ManualNetworkConfig('Configure interfaces', preset)
|
||||||
|
return manual.run_manual()
|
||||||
|
|
||||||
|
return preset
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
from typing import List, Any, Dict
|
||||||
|
|
||||||
from ..menu.list_manager import ListManager
|
from ..menu.list_manager import ListManager
|
||||||
from ..menu.selection_menu import Selector, GeneralMenu
|
from ..menu.selection_menu import Selector, GeneralMenu
|
||||||
from ..menu.text_input import TextInput
|
from ..menu.text_input import TextInput
|
||||||
|
|
@ -12,8 +14,8 @@ class SubvolumeList(ListManager):
|
||||||
self.ObjectDefaultAction = str(_('Add'))
|
self.ObjectDefaultAction = str(_('Add'))
|
||||||
super().__init__(prompt,list,None,self.ObjectNullAction,self.ObjectDefaultAction)
|
super().__init__(prompt,list,None,self.ObjectNullAction,self.ObjectDefaultAction)
|
||||||
|
|
||||||
def reformat(self):
|
def reformat(self, data: Any) -> List[Any]:
|
||||||
def presentation(key,value):
|
def presentation(key :str, value :Dict):
|
||||||
text = _(" Subvolume :{:16}").format(key)
|
text = _(" Subvolume :{:16}").format(key)
|
||||||
if isinstance(value,str):
|
if isinstance(value,str):
|
||||||
text += _(" mounted at {:16}").format(value)
|
text += _(" mounted at {:16}").format(value)
|
||||||
|
|
@ -26,32 +28,31 @@ class SubvolumeList(ListManager):
|
||||||
text += _(" with option {}").format(', '.join(value['options']))
|
text += _(" with option {}").format(', '.join(value['options']))
|
||||||
return text
|
return text
|
||||||
|
|
||||||
return sorted(list(map(lambda x:presentation(x,self.data[x]),self.data)))
|
return sorted(list(map(lambda x:presentation(x,data[x]),data)))
|
||||||
|
|
||||||
def action_list(self):
|
def action_list(self):
|
||||||
return super().action_list()
|
return super().action_list()
|
||||||
|
|
||||||
def exec_action(self):
|
def exec_action(self, data: Any):
|
||||||
if self.target:
|
if self.target:
|
||||||
origkey,origval = list(self.target.items())[0]
|
origkey,origval = list(self.target.items())[0]
|
||||||
else:
|
else:
|
||||||
origkey = None
|
origkey = None
|
||||||
|
|
||||||
if self.action == str(_('Delete')):
|
if self.action == str(_('Delete')):
|
||||||
del self.data[origkey]
|
del data[origkey]
|
||||||
return True
|
|
||||||
|
|
||||||
if self.action == str(_('Add')):
|
|
||||||
self.target = {}
|
|
||||||
print(_('\n Fill the desired values for a new subvolume \n'))
|
|
||||||
with SubvolumeMenu(self.target,self.action) as add_menu:
|
|
||||||
for data in ['name','mountpoint','options']:
|
|
||||||
add_menu.exec_option(data)
|
|
||||||
else:
|
else:
|
||||||
SubvolumeMenu(self.target,self.action).run()
|
if self.action == str(_('Add')):
|
||||||
self.data.update(self.target)
|
self.target = {}
|
||||||
|
print(_('\n Fill the desired values for a new subvolume \n'))
|
||||||
|
with SubvolumeMenu(self.target,self.action) as add_menu:
|
||||||
|
for data in ['name','mountpoint','options']:
|
||||||
|
add_menu.exec_option(data)
|
||||||
|
else:
|
||||||
|
SubvolumeMenu(self.target,self.action).run()
|
||||||
|
|
||||||
|
data.update(self.target)
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
class SubvolumeMenu(GeneralMenu):
|
class SubvolumeMenu(GeneralMenu):
|
||||||
def __init__(self,parameters,action=None):
|
def __init__(self,parameters,action=None):
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import time
|
||||||
|
|
||||||
import archinstall
|
import archinstall
|
||||||
from archinstall import ConfigurationOutput
|
from archinstall import ConfigurationOutput
|
||||||
|
from archinstall.lib.models.network_configuration import NetworkConfigurationHandler
|
||||||
|
|
||||||
if archinstall.arguments.get('help'):
|
if archinstall.arguments.get('help'):
|
||||||
print("See `man archinstall` for help.")
|
print("See `man archinstall` for help.")
|
||||||
|
|
@ -167,7 +168,8 @@ def perform_installation(mountpoint):
|
||||||
network_config = archinstall.arguments.get('nic', None)
|
network_config = archinstall.arguments.get('nic', None)
|
||||||
|
|
||||||
if network_config:
|
if network_config:
|
||||||
network_config.config_installer(installation)
|
handler = NetworkConfigurationHandler(network_config)
|
||||||
|
handler.config_installer(installation)
|
||||||
|
|
||||||
if archinstall.arguments.get('audio', None) is not None:
|
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)
|
installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}", level=logging.INFO)
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,13 @@ import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import pathlib
|
import pathlib
|
||||||
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
import archinstall
|
import archinstall
|
||||||
from archinstall import ConfigurationOutput
|
from archinstall import ConfigurationOutput, NetworkConfigurationHandler
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
_: Any
|
||||||
|
|
||||||
if archinstall.arguments.get('help'):
|
if archinstall.arguments.get('help'):
|
||||||
print("See `man archinstall` for help.")
|
print("See `man archinstall` for help.")
|
||||||
|
|
@ -397,7 +401,8 @@ def os_setup(installation):
|
||||||
network_config = archinstall.arguments.get('nic', None)
|
network_config = archinstall.arguments.get('nic', None)
|
||||||
|
|
||||||
if network_config:
|
if network_config:
|
||||||
network_config.config_installer(installation)
|
handler = NetworkConfigurationHandler(network_config)
|
||||||
|
handler.config_installer(installation)
|
||||||
|
|
||||||
if archinstall.arguments.get('audio', None) is not None:
|
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)
|
installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}",level=logging.INFO)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue