-
-
Notifications
You must be signed in to change notification settings - Fork 742
Teardown after installation #4491 #4536
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,18 @@ | ||
| import os | ||
| from contextlib import suppress | ||
| from pathlib import Path | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| from pydantic import BaseModel | ||
|
|
||
| from archinstall.lib.command import SysCommand | ||
| from archinstall.lib.exceptions import DiskError, SysCallError | ||
| from archinstall.lib.models.device import LsblkInfo | ||
| from archinstall.lib.models.device import EncryptionType, FilesystemType, LsblkInfo | ||
| from archinstall.lib.output import debug, info, warn | ||
|
|
||
| if TYPE_CHECKING: | ||
| from archinstall.lib.installer import Installer | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this causeing circular dependencies? If not can we move this to the top |
||
|
|
||
|
|
||
| class LsblkOutput(BaseModel): | ||
| blockdevices: list[LsblkInfo] | ||
|
|
@@ -196,3 +202,119 @@ def swapon(path: Path) -> None: | |
| SysCommand(['swapon', str(path)]) | ||
| except SysCallError as err: | ||
| raise DiskError(f'Could not enable swap {path}:\n{err.message}') | ||
|
|
||
|
|
||
| def teardown(installer: Installer) -> None: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be passing the configuration here not the installer |
||
| if not installer._layout_teardown_required: | ||
| debug('No mounted layout registered for teardown') | ||
| return | ||
|
|
||
| info('Tearing down installation target') | ||
|
|
||
| with suppress(Exception): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't supress things, either handle it or log it |
||
| os.sync() | ||
|
|
||
| _swapoff_layout(installer) | ||
|
|
||
| with suppress(SysCallError, DiskError): | ||
| umount(installer.target, recursive=True) | ||
|
|
||
| match installer._disk_encryption.encryption_type: | ||
| case EncryptionType.LUKS: | ||
| _teardown_luks_partitions(installer) | ||
|
|
||
| case EncryptionType.LUKS_ON_LVM: | ||
| _teardown_luks_lvm(installer) | ||
| _teardown_lvm(installer) | ||
|
|
||
| case EncryptionType.LVM_ON_LUKS: | ||
| _teardown_lvm(installer) | ||
| _teardown_luks_partitions(installer) | ||
|
|
||
| case EncryptionType.NO_ENCRYPTION: | ||
| _teardown_lvm(installer) | ||
|
|
||
| with suppress(SysCallError): | ||
| udev_sync() | ||
|
|
||
| installer._layout_teardown_required = False | ||
|
|
||
|
|
||
| def _swapoff_path(path: Path | None) -> None: | ||
| if path is None: | ||
| return | ||
|
|
||
| with suppress(SysCallError, DiskError): | ||
| SysCommand(['swapoff', str(path)]) | ||
|
|
||
|
|
||
| def _swapoff_layout(installer: Installer) -> None: | ||
| for mod in installer._disk_config.device_modifications: | ||
| for part in mod.partitions: | ||
| if part.is_swap(): | ||
| _swapoff_path(part.dev_path) | ||
|
|
||
| if part.mapper_name: | ||
| _swapoff_path(Path('/dev/mapper') / part.mapper_name) | ||
|
|
||
| if not installer._disk_config.lvm_config: | ||
| return | ||
|
|
||
| for vol in installer._disk_config.lvm_config.get_all_volumes(): | ||
| if vol.fs_type == FilesystemType.LINUX_SWAP: | ||
| _swapoff_path(vol.dev_path) | ||
|
|
||
| if vol.mapper_name: | ||
| _swapoff_path(Path('/dev/mapper') / vol.mapper_name) | ||
|
|
||
|
|
||
| def _teardown_lvm(installer: Installer) -> None: | ||
| from archinstall.lib.disk.lvm import lvm_vg_change, lvm_vol_change | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move to the top |
||
|
|
||
| lvm_config = installer._disk_config.lvm_config | ||
|
|
||
| if not lvm_config: | ||
| return | ||
|
|
||
| for vg in reversed(lvm_config.vol_groups): | ||
| for vol in reversed(vg.volumes): | ||
| if not vol.dev_path: | ||
| continue | ||
|
|
||
| with suppress(SysCallError, DiskError): | ||
| lvm_vol_change(vol, False) | ||
|
|
||
| with suppress(SysCallError, DiskError): | ||
| lvm_vg_change(vg, False) | ||
|
|
||
|
|
||
| def _teardown_luks_partitions(installer: Installer) -> None: | ||
| from archinstall.lib.disk.luks import Luks2 | ||
|
|
||
| for part_mod in reversed(installer._disk_encryption.partitions): | ||
| if not part_mod.dev_path or not part_mod.mapper_name: | ||
| continue | ||
|
|
||
| luks_handler = Luks2( | ||
| part_mod.dev_path, | ||
| mapper_name=part_mod.mapper_name, | ||
| ) | ||
|
|
||
| with suppress(SysCallError, DiskError): | ||
| luks_handler.lock() | ||
|
|
||
|
|
||
| def _teardown_luks_lvm(installer: Installer) -> None: | ||
| from archinstall.lib.disk.luks import Luks2 | ||
|
|
||
| for vol in reversed(installer._disk_encryption.lvm_volumes): | ||
| if not vol.dev_path or not vol.mapper_name: | ||
| continue | ||
|
|
||
| luks_handler = Luks2( | ||
| vol.dev_path, | ||
| mapper_name=vol.mapper_name, | ||
| ) | ||
|
|
||
| with suppress(SysCallError, DiskError): | ||
| luks_handler.lock() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ | |
| get_unique_path_for_device, | ||
| mount, | ||
| swapon, | ||
| teardown, | ||
| ) | ||
| from archinstall.lib.exceptions import DiskError, HardwareIncompatibilityError, RequirementError, ServiceException, SysCallError | ||
| from archinstall.lib.hardware import SysInfo | ||
|
|
@@ -131,45 +132,51 @@ def __init__( | |
|
|
||
| self._zram_enabled = False | ||
| self._disable_fstrim = False | ||
|
|
||
| self._layout_teardown_required = False | ||
| self.pacman = Pacman(self.target, silent) | ||
|
|
||
| def __enter__(self) -> Self: | ||
| return self | ||
|
|
||
| def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None) -> bool | None: | ||
| if exc_type is not None: | ||
| error(str(exc_value)) | ||
| try: | ||
| if exc_type is not None: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move this whle thing out of the exit as that will only be run when Installer is used in the that can be called |
||
| error(str(exc_value)) | ||
|
|
||
| self.sync_log_to_install_medium() | ||
| self.sync_log_to_install_medium() | ||
|
|
||
| # We avoid printing /mnt/<log path> because that might confuse people if they note it down | ||
| # and then reboot, and an identical log file will be found in the ISO medium anyway. | ||
| print(tr('[!] A log file has been created here: {}').format(logger.path)) | ||
| print(tr('Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues')) | ||
| # We avoid printing /mnt/<log path> because that might confuse people if they note it down | ||
| # and then reboot, and an identical log file will be found in the ISO medium anyway. | ||
| print(tr('[!] A log file has been created here: {}').format(logger.path)) | ||
| print(tr('Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues')) | ||
|
|
||
| # Return None to propagate the exception | ||
| return None | ||
| # Return None to propagate the exception | ||
| return None | ||
|
|
||
| info(tr('Syncing the system...')) | ||
| os.sync() | ||
| info(tr('Syncing the system...')) | ||
| os.sync() | ||
|
|
||
| if not (missing_steps := self.post_install_check()): | ||
| msg = f'Installation completed without any errors.\nLog files temporarily available at {logger.directory}.\nYou may reboot when ready.\n' | ||
| log(msg, fg='green') | ||
| self.sync_log_to_install_medium() | ||
| return True | ||
| else: | ||
| warn('Some required steps were not successfully installed/configured before leaving the installer:') | ||
| if not (missing_steps := self.post_install_check()): | ||
| msg = f'Installation completed without any errors.\nLog files temporarily available at {logger.directory}.\nYou may reboot when ready.\n' | ||
| log(msg, fg='green') | ||
| self.sync_log_to_install_medium() | ||
| return True | ||
| else: | ||
| warn('Some required steps were not successfully installed/configured before leaving the installer:') | ||
|
|
||
| for step in missing_steps: | ||
| warn(f' - {step}') | ||
| for step in missing_steps: | ||
| warn(f' - {step}') | ||
|
|
||
| warn(f'Detailed error logs can be found at: {logger.directory}') | ||
| warn('Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues') | ||
| warn(f'Detailed error logs can be found at: {logger.directory}') | ||
| warn('Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues') | ||
|
|
||
| self.sync_log_to_install_medium() | ||
| return False | ||
| self.sync_log_to_install_medium() | ||
| return False | ||
| finally: | ||
| try: | ||
| teardown(self) | ||
| except Exception as err: | ||
| warn(f'Failed to teardown installation target: {err}') | ||
|
|
||
| def remove_mod(self, mod: str) -> None: | ||
| if mod in self._modules: | ||
|
|
@@ -257,6 +264,7 @@ def sanity_check( | |
|
|
||
| def mount_ordered_layout(self) -> None: | ||
| debug('Mounting ordered layout') | ||
| self._layout_teardown_required = True | ||
|
|
||
| luks_handlers: dict[Any, Luks2] = {} | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like an error handling might be useful here for debugging purposes