From 1bf6e20bdea743a4f6fedc077d69ae58b59901e1 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Mon, 28 Mar 2022 16:41:03 +0200 Subject: moved GlobalMenu into lib/menu/global_menu.py instead --- archinstall/lib/menu/__init__.py | 2 +- archinstall/lib/menu/global_menu.py | 293 ++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 archinstall/lib/menu/global_menu.py (limited to 'archinstall/lib/menu') diff --git a/archinstall/lib/menu/__init__.py b/archinstall/lib/menu/__init__.py index fd83ee01..9b0adb8b 100644 --- a/archinstall/lib/menu/__init__.py +++ b/archinstall/lib/menu/__init__.py @@ -1,2 +1,2 @@ from .menu import Menu as Menu -from .selection_menu import GlobalMenu as GlobalMenu \ No newline at end of file +from .global_menu import GlobalMenu as GlobalMenu \ No newline at end of file diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py new file mode 100644 index 00000000..3f57941a --- /dev/null +++ b/archinstall/lib/menu/global_menu.py @@ -0,0 +1,293 @@ +from __future__ import annotations + +from typing import Any, List, Optional + +from ..menu import Menu +from ..menu.selection_menu import Selector, GeneralMenu +from ..general import SysCommand, secret +from ..hardware import has_uefi +from ..storage import storage +from ..output import log +from ..profiles import is_desktop_profile +from ..disk import encrypted_partitions + +from ..user_interaction import get_password, ask_for_a_timezone, save_config +from ..user_interaction import ask_ntp +from ..user_interaction import ask_for_swap +from ..user_interaction import ask_for_bootloader +from ..user_interaction import ask_hostname +from ..user_interaction import ask_for_audio_selection +from ..user_interaction import ask_additional_packages_to_install +from ..user_interaction import ask_to_configure_network +from ..user_interaction import ask_for_superuser_account +from ..user_interaction import ask_for_additional_users +from ..user_interaction import select_language +from ..user_interaction import select_mirror_regions +from ..user_interaction import select_locale_lang +from ..user_interaction import select_locale_enc +from ..user_interaction import select_disk_layout +from ..user_interaction import select_kernel +from ..user_interaction import select_encrypted_partitions +from ..user_interaction import select_harddrives +from ..user_interaction import select_profile +from ..user_interaction import select_additional_repositories + +class GlobalMenu(GeneralMenu): + def __init__(self,data_store): + super().__init__(data_store=data_store, auto_cursor=True) + + def _setup_selection_menu_options(self): + # archinstall.Language will not use preset values + self._menu_options['archinstall-language'] = \ + Selector( + _('Select Archinstall language'), + lambda x: self._select_archinstall_language('English'), + default='English', + enabled=True) + self._menu_options['keyboard-layout'] = \ + Selector(_('Select keyboard layout'), lambda preset: select_language('us',preset), default='us') + self._menu_options['mirror-region'] = \ + Selector( + _('Select mirror region'), + select_mirror_regions, + display_func=lambda x: list(x.keys()) if x else '[]', + default={}) + self._menu_options['sys-language'] = \ + Selector(_('Select locale language'), lambda preset: select_locale_lang('en_US',preset), default='en_US') + self._menu_options['sys-encoding'] = \ + Selector(_('Select locale encoding'), lambda preset: select_locale_enc('utf-8',preset), default='utf-8') + self._menu_options['harddrives'] = \ + Selector( + _('Select harddrives'), + self._select_harddrives) + self._menu_options['disk_layouts'] = \ + Selector( + _('Select disk layout'), + lambda x: select_disk_layout( + storage['arguments'].get('harddrives', []), + storage['arguments'].get('advanced', False) + ), + dependencies=['harddrives']) + self._menu_options['!encryption-password'] = \ + Selector( + _('Set encryption password'), + lambda x: self._select_encrypted_password(), + display_func=lambda x: secret(x) if x else 'None', + dependencies=['harddrives']) + self._menu_options['swap'] = \ + Selector( + _('Use swap'), + lambda preset: ask_for_swap(preset), + default=True) + self._menu_options['bootloader'] = \ + Selector( + _('Select bootloader'), + lambda preset: ask_for_bootloader(storage['arguments'].get('advanced', False),preset), + default="systemd-bootctl" if has_uefi() else "grub-install") + self._menu_options['hostname'] = \ + Selector( + _('Specify hostname'), + ask_hostname, + default='archlinux') + # root password won't have preset value + self._menu_options['!root-password'] = \ + Selector( + _('Set root password'), + lambda preset:self._set_root_password(), + display_func=lambda x: secret(x) if x else 'None') + self._menu_options['!superusers'] = \ + Selector( + _('Specify superuser account'), + lambda preset: self._create_superuser_account(), + exec_func=lambda n,v:self._users_resynch(), + dependencies_not=['!root-password'], + display_func=lambda x: self._display_superusers()) + self._menu_options['!users'] = \ + Selector( + _('Specify user account'), + lambda x: self._create_user_account(), + default={}, + exec_func=lambda n,v:self._users_resynch(), + display_func=lambda x: list(x.keys()) if x else '[]') + self._menu_options['profile'] = \ + Selector( + _('Specify profile'), + lambda x: self._select_profile(), + display_func=lambda x: x if x else 'None') + self._menu_options['audio'] = \ + Selector( + _('Select audio'), + lambda preset: ask_for_audio_selection(is_desktop_profile(storage['arguments'].get('profile', None)),preset)) + self._menu_options['kernels'] = \ + Selector( + _('Select kernels'), + lambda preset: select_kernel(preset), + default=['linux']) + self._menu_options['packages'] = \ + Selector( + _('Additional packages to install'), + # lambda x: ask_additional_packages_to_install(storage['arguments'].get('packages', None)), + ask_additional_packages_to_install, + default=[]) + self._menu_options['additional-repositories'] = \ + Selector( + _('Additional repositories to enable'), + select_additional_repositories, + default=[]) + self._menu_options['nic'] = \ + Selector( + _('Configure network'), + ask_to_configure_network, + display_func=lambda x: x if x else _('Not configured, unavailable unless setup manually'), + default={}) + self._menu_options['timezone'] = \ + Selector( + _('Select timezone'), + lambda preset: ask_for_a_timezone(preset), + default='UTC') + self._menu_options['ntp'] = \ + Selector( + _('Set automatic time sync (NTP)'), + lambda preset: self._select_ntp(preset), + default=True) + self._menu_options['save_config'] = \ + Selector( + _('Save configuration'), + lambda preset: save_config(self._data_store), + enabled=True, + no_store=True) + self._menu_options['install'] = \ + Selector( + self._install_text(), + exec_func=lambda n,v: True if len(self._missing_configs()) == 0 else False, + preview_func=self._prev_install_missing_config, + enabled=True, + no_store=True) + + self._menu_options['abort'] = Selector(_('Abort'), exec_func=lambda n,v:exit(1), enabled=True) + + def _update_install_text(self, name :str = None, result :Any = None): + text = self._install_text() + self._menu_options.get('install').update_description(text) + + def post_callback(self,name :str = None ,result :Any = None): + self._update_install_text(name, result) + + def exit_callback(self): + if self._data_store.get('harddrives', None) and self._data_store.get('!encryption-password', None): + # If no partitions was marked as encrypted, but a password was supplied and we have some disks to format.. + # Then we need to identify which partitions to encrypt. This will default to / (root). + if len(list(encrypted_partitions(storage['arguments'].get('disk_layouts', [])))) == 0: + storage['arguments']['disk_layouts'] = select_encrypted_partitions( + storage['arguments']['disk_layouts'], storage['arguments']['!encryption-password']) + + def _install_text(self): + missing = len(self._missing_configs()) + if missing > 0: + return _('Install ({} config(s) missing)').format(missing) + return 'Install' + + def _prev_install_missing_config(self) -> Optional[str]: + if missing := self._missing_configs(): + text = str(_('Missing configurations:\n')) + for m in missing: + text += f'- {m}\n' + return text[:-1] # remove last new line + return None + + def _missing_configs(self) -> List[str]: + def check(s): + return self._menu_options.get(s).has_selection() + + missing = [] + if not check('bootloader'): + missing += ['Bootloader'] + if not check('hostname'): + missing += ['Hostname'] + if not check('audio'): + missing += ['Audio'] + if not check('!root-password') and not check('!superusers'): + missing += [str(_('Either root-password or at least 1 superuser must be specified'))] + if not check('harddrives'): + missing += ['Hard drives'] + if check('harddrives'): + if not self._menu_options.get('harddrives').is_empty() and not check('disk_layouts'): + missing += ['Disk layout'] + + return missing + + def _set_root_password(self): + prompt = str(_('Enter root password (leave blank to disable root): ')) + password = get_password(prompt=prompt) + return password + + def _select_encrypted_password(self): + if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))): + return passwd + else: + return None + + def _select_ntp(self, preset :bool = True) -> bool: + ntp = ask_ntp(preset) + + value = str(ntp).lower() + SysCommand(f'timedatectl set-ntp {value}') + + return ntp + + def _select_harddrives(self, old_harddrives : list) -> list: + # old_haddrives = storage['arguments'].get('harddrives', []) + harddrives = select_harddrives(old_harddrives) + + # in case the harddrives got changed we have to reset the disk layout as well + if old_harddrives != harddrives: + self._menu_options.get('disk_layouts').set_current_selection(None) + storage['arguments']['disk_layouts'] = {} + + if not harddrives: + prompt = _( + "You decided to skip harddrive selection\nand will use whatever drive-setup is mounted at {} (experimental)\n" + "WARNING: Archinstall won't check the suitability of this setup\n" + "Do you wish to continue?" + ).format(storage['MOUNT_POINT']) + + choice = Menu(prompt, ['yes', 'no'], default_option='yes').run() + + if choice == 'no': + return self._select_harddrives(old_harddrives) + + return harddrives + + def _select_profile(self): + profile = select_profile() + + # Check the potentially selected profiles preparations to get early checks if some additional questions are needed. + if profile and profile.has_prep_function(): + namespace = f'{profile.namespace}.py' + with profile.load_instructions(namespace=namespace) as imported: + if not imported._prep_function(): + log(' * Profile\'s preparation requirements was not fulfilled.', fg='red') + exit(1) + + return profile + + def _create_superuser_account(self): + superusers = ask_for_superuser_account(str(_('Manage superuser accounts: '))) + return superusers if superusers else None + + def _create_user_account(self): + users = ask_for_additional_users(str(_('Manage ordinary user accounts: '))) + return users + + def _display_superusers(self): + superusers = self._data_store.get('!superusers', {}) + + if self._menu_options.get('!root-password').has_selection(): + return list(superusers.keys()) if superusers else '[]' + else: + return list(superusers.keys()) if superusers else '' + + def _users_resynch(self): + self.synch('!superusers') + self.synch('!users') + return False -- cgit v1.2.3-70-g09d2