index : archinstall32 | |
Archlinux32 installer | gitolite user |
summaryrefslogtreecommitdiff |
-rw-r--r-- | archinstall/lib/configuration.py | 191 |
diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index c036783f..95e237d7 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -1,28 +1,17 @@ import os import json import stat -import logging -import pathlib -from typing import Optional, Dict +import readline +from pathlib import Path +from typing import Optional, Dict, Any, TYPE_CHECKING -from .hsm.fido import Fido2 -from .models.disk_encryption import DiskEncryption +from .menu import Menu, MenuSelectionType from .storage import storage from .general import JSON, UNSAFE_JSON -from .output import log -from .exceptions import RequirementError - - -def configuration_sanity_check(): - disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption') - if disk_encryption is not None and disk_encryption.hsm_device: - if not Fido2.get_fido2_devices(): - raise RequirementError( - f"In order to use HSM to pair with the disk encryption," - + f" one needs to be accessible through /dev/hidraw* and support" - + f" the FIDO2 protocol. You can check this by running" - + f" 'systemd-cryptenroll --fido2-device=list'." - ) +from .output import debug, info, warn + +if TYPE_CHECKING: + _: Any class ConfigurationOutput: @@ -35,15 +24,13 @@ class ConfigurationOutput: :type config: Dict """ self._config = config - self._user_credentials = {} - self._disk_layout = None - self._user_config = {} - self._default_save_path = pathlib.Path(storage.get('LOG_PATH', '.')) + self._user_credentials: Dict[str, Any] = {} + self._user_config: Dict[str, Any] = {} + self._default_save_path = storage.get('LOG_PATH', Path('.')) self._user_config_file = 'user_configuration.json' self._user_creds_file = "user_credentials.json" - self._disk_layout_file = "user_disk_layout.json" - self._sensitive = ['!users'] + self._sensitive = ['!users', '!root-password'] self._ignore = ['abort', 'install', 'config', 'creds', 'dry_run'] self._process_config() @@ -56,23 +43,18 @@ class ConfigurationOutput: def user_configuration_file(self): return self._user_config_file - @property - def disk_layout_file(self): - return self._disk_layout_file - def _process_config(self): - for key in self._config: + for key, value in self._config.items(): if key in self._sensitive: - self._user_credentials[key] = self._config[key] - elif key == 'disk_layouts': - self._disk_layout = self._config[key] + self._user_credentials[key] = value elif key in self._ignore: pass else: - self._user_config[key] = self._config[key] + self._user_config[key] = value - if key == 'disk_encryption' and self._config[key]: # special handling for encryption password - self._user_credentials['encryption_password'] = self._config[key].encryption_password + # special handling for encryption password + if key == 'disk_encryption' and value: + self._user_credentials['encryption_password'] = value.encryption_password def user_config_to_json(self) -> str: return json.dumps({ @@ -81,11 +63,6 @@ class ConfigurationOutput: 'version': storage['__version__'] }, indent=4, sort_keys=True, cls=JSON) - def disk_layout_to_json(self) -> Optional[str]: - if self._disk_layout: - return json.dumps(self._disk_layout, indent=4, sort_keys=True, cls=JSON) - return None - def user_credentials_to_json(self) -> Optional[str]: if self._user_credentials: return json.dumps(self._user_credentials, indent=4, sort_keys=True, cls=UNSAFE_JSON) @@ -93,60 +70,112 @@ class ConfigurationOutput: def show(self): print(_('\nThis is your chosen configuration:')) - log(" -- Chosen configuration --", level=logging.DEBUG) - - user_conig = self.user_config_to_json() - disk_layout = self.disk_layout_to_json() - log(user_conig, level=logging.INFO) - - if disk_layout: - log(disk_layout, level=logging.INFO) + debug(" -- Chosen configuration --") + info(self.user_config_to_json()) print() - def _is_valid_path(self, dest_path :pathlib.Path) -> bool: - if (not dest_path.exists()) or not (dest_path.is_dir()): - log( - 'Destination directory {} does not exist or is not a directory,\n Configuration files can not be saved'.format(dest_path.resolve()), - fg="yellow" + def _is_valid_path(self, dest_path: Path) -> bool: + dest_path_ok = dest_path.exists() and dest_path.is_dir() + if not dest_path_ok: + warn( + f'Destination directory {dest_path.resolve()} does not exist or is not a directory\n.', + 'Configuration files can not be saved' ) - return False - return True + return dest_path_ok - def save_user_config(self, dest_path :pathlib.Path = None): + def save_user_config(self, dest_path: Path): if self._is_valid_path(dest_path): target = dest_path / self._user_config_file + target.write_text(self.user_config_to_json()) + os.chmod(target, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) - with open(target, 'w') as config_file: - config_file.write(self.user_config_to_json()) - - os.chmod(str(dest_path / self._user_config_file), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) - - def save_user_creds(self, dest_path :pathlib.Path = None): + def save_user_creds(self, dest_path: Path): if self._is_valid_path(dest_path): if user_creds := self.user_credentials_to_json(): target = dest_path / self._user_creds_file + target.write_text(user_creds) + os.chmod(target, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) - with open(target, 'w') as config_file: - config_file.write(user_creds) - - os.chmod(str(target), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) + def save(self, dest_path: Optional[Path] = None): + dest_path = dest_path or self._default_save_path - def save_disk_layout(self, dest_path :pathlib.Path = None): if self._is_valid_path(dest_path): - if disk_layout := self.disk_layout_to_json(): - target = dest_path / self._disk_layout_file - - with target.open('w') as config_file: - config_file.write(disk_layout) + self.save_user_config(dest_path) + self.save_user_creds(dest_path) - os.chmod(str(target), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) - def save(self, dest_path :pathlib.Path = None): - if not dest_path: - dest_path = self._default_save_path +def save_config(config: Dict): + def preview(selection: str): + match options[selection]: + case "user_config": + serialized = config_output.user_config_to_json() + return f"{config_output.user_configuration_file}\n{serialized}" + case "user_creds": + if maybe_serial := config_output.user_credentials_to_json(): + return f"{config_output.user_credentials_file}\n{maybe_serial}" + return str(_("No configuration")) + case "all": + output = [config_output.user_configuration_file] + if config_output.user_credentials_to_json(): + output.append(config_output.user_credentials_file) + return '\n'.join(output) + return None - if self._is_valid_path(dest_path): - self.save_user_config(dest_path) - self.save_user_creds(dest_path) - self.save_disk_layout(dest_path) + try: + config_output = ConfigurationOutput(config) + + options = { + str(_("Save user configuration (including disk layout)")): "user_config", + str(_("Save user credentials")): "user_creds", + str(_("Save all")): "all", + } + + save_choice = Menu( + _("Choose which configuration to save"), + list(options), + sort=False, + skip=True, + preview_size=0.75, + preview_command=preview, + ).run() + + if save_choice.type_ == MenuSelectionType.Skip: + return + + readline.set_completer_delims("\t\n=") + readline.parse_and_bind("tab: complete") + while True: + path = input( + _( + "Enter a directory for the configuration(s) to be saved (tab completion enabled)\nSave directory: " + ) + ).strip(" ") + dest_path = Path(path) + if dest_path.exists() and dest_path.is_dir(): + break + info(_("Not a valid directory: {}").format(dest_path), fg="red") + + if not path: + return + + prompt = _( + "Do you want to save {} configuration file(s) in the following location?\n\n{}" + ).format(options[str(save_choice.value)], dest_path.absolute()) + + save_confirmation = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() + if save_confirmation == Menu.no(): + return + + debug("Saving {} configuration files to {}".format(options[str(save_choice.value)], dest_path.absolute())) + + match options[str(save_choice.value)]: + case "user_config": + config_output.save_user_config(dest_path) + case "user_creds": + config_output.save_user_creds(dest_path) + case "all": + config_output.save(dest_path) + + except (KeyboardInterrupt, EOFError): + return |