forked from rpms/leapp-repository
294 lines
13 KiB
Diff
294 lines
13 KiB
Diff
|
From e4fa8671351a73ddd6b56c70a7834a2c304df9cc Mon Sep 17 00:00:00 2001
|
||
|
From: Petr Stodulka <pstodulk@redhat.com>
|
||
|
Date: Mon, 10 Jul 2023 15:20:24 +0200
|
||
|
Subject: [PATCH 37/42] overlay lib: Deprecate old ovl internal functions
|
||
|
(refactoring)
|
||
|
|
||
|
We are going to redesign the use of overlay images during the upgrade
|
||
|
to resolve number of issues we have with the old solution. However,
|
||
|
we need to keep the old solution as a fallback (read below). This
|
||
|
is small preparation to keep the new and old code separated safely.
|
||
|
|
||
|
Reasoning for the fallback:
|
||
|
* There is a chance the new solution could raise also some problems
|
||
|
mainly for systems with many partitions/volumes in fstab, or when
|
||
|
they are using many loop devices already - as the new solution will
|
||
|
require to create loop device for each partition/volume noted in
|
||
|
the fstab.
|
||
|
* Also RHEL 7 is going to switch to ELS on Jun 2024 after which the
|
||
|
project will be fixing just critical bugfixes for in-place upgrades.
|
||
|
This problem blocking the upgrade is not considered to be critical.
|
||
|
---
|
||
|
.../common/libraries/overlaygen.py | 223 +++++++++---------
|
||
|
1 file changed, 117 insertions(+), 106 deletions(-)
|
||
|
|
||
|
diff --git a/repos/system_upgrade/common/libraries/overlaygen.py b/repos/system_upgrade/common/libraries/overlaygen.py
|
||
|
index b544f88c..e0d88fe5 100644
|
||
|
--- a/repos/system_upgrade/common/libraries/overlaygen.py
|
||
|
+++ b/repos/system_upgrade/common/libraries/overlaygen.py
|
||
|
@@ -13,15 +13,6 @@ OVERLAY_DO_NOT_MOUNT = ('tmpfs', 'devpts', 'sysfs', 'proc', 'cramfs', 'sysv', 'v
|
||
|
MountPoints = namedtuple('MountPoints', ['fs_file', 'fs_vfstype'])
|
||
|
|
||
|
|
||
|
-def _ensure_enough_diskimage_space(space_needed, directory):
|
||
|
- stat = os.statvfs(directory)
|
||
|
- if (stat.f_frsize * stat.f_bavail) < (space_needed * 1024 * 1024):
|
||
|
- message = ('Not enough space available for creating required disk images in {directory}. ' +
|
||
|
- 'Needed: {space_needed} MiB').format(space_needed=space_needed, directory=directory)
|
||
|
- api.current_logger().error(message)
|
||
|
- raise StopActorExecutionError(message)
|
||
|
-
|
||
|
-
|
||
|
def _get_mountpoints(storage_info):
|
||
|
mount_points = set()
|
||
|
for entry in storage_info.fstab:
|
||
|
@@ -43,41 +34,6 @@ def _mount_dir(mounts_dir, mountpoint):
|
||
|
return os.path.join(mounts_dir, _mount_name(mountpoint))
|
||
|
|
||
|
|
||
|
-def _prepare_required_mounts(scratch_dir, mounts_dir, mount_points, xfs_info):
|
||
|
- result = {
|
||
|
- mount_point.fs_file: mounting.NullMount(
|
||
|
- _mount_dir(mounts_dir, mount_point.fs_file)) for mount_point in mount_points
|
||
|
- }
|
||
|
-
|
||
|
- if not xfs_info.mountpoints_without_ftype:
|
||
|
- return result
|
||
|
-
|
||
|
- space_needed = _overlay_disk_size() * len(xfs_info.mountpoints_without_ftype)
|
||
|
- disk_images_directory = os.path.join(scratch_dir, 'diskimages')
|
||
|
-
|
||
|
- # Ensure we cleanup old disk images before we check for space constraints.
|
||
|
- run(['rm', '-rf', disk_images_directory])
|
||
|
- _create_diskimages_dir(scratch_dir, disk_images_directory)
|
||
|
- _ensure_enough_diskimage_space(space_needed, scratch_dir)
|
||
|
-
|
||
|
- mount_names = [mount_point.fs_file for mount_point in mount_points]
|
||
|
-
|
||
|
- # TODO(pstodulk): this (adding rootfs into the set always) is hotfix for
|
||
|
- # bz #1911802 (not ideal one..). The problem occurs one rootfs is ext4 fs,
|
||
|
- # but /var/lib/leapp/... is under XFS without ftype; In such a case we can
|
||
|
- # see still the very same problems as before. But letting you know that
|
||
|
- # probably this is not the final solution, as we could possibly see the
|
||
|
- # same problems on another partitions too (needs to be tested...). However,
|
||
|
- # it could fit for now until we provide the complete solution around XFS
|
||
|
- # workarounds (including management of required spaces for virtual FSs per
|
||
|
- # mountpoints - without that, we cannot fix this properly)
|
||
|
- for mountpoint in set(xfs_info.mountpoints_without_ftype + ['/']):
|
||
|
- if mountpoint in mount_names:
|
||
|
- image = _create_mount_disk_image(disk_images_directory, mountpoint)
|
||
|
- result[mountpoint] = mounting.LoopMount(source=image, target=_mount_dir(mounts_dir, mountpoint))
|
||
|
- return result
|
||
|
-
|
||
|
-
|
||
|
@contextlib.contextmanager
|
||
|
def _build_overlay_mount(root_mount, mounts):
|
||
|
if not root_mount:
|
||
|
@@ -96,21 +52,6 @@ def _build_overlay_mount(root_mount, mounts):
|
||
|
yield mount
|
||
|
|
||
|
|
||
|
-def _overlay_disk_size():
|
||
|
- """
|
||
|
- Convenient function to retrieve the overlay disk size
|
||
|
- """
|
||
|
- try:
|
||
|
- env_size = os.getenv('LEAPP_OVL_SIZE', default='2048')
|
||
|
- disk_size = int(env_size)
|
||
|
- except ValueError:
|
||
|
- disk_size = 2048
|
||
|
- api.current_logger().warning(
|
||
|
- 'Invalid "LEAPP_OVL_SIZE" environment variable "%s". Setting default "%d" value', env_size, disk_size
|
||
|
- )
|
||
|
- return disk_size
|
||
|
-
|
||
|
-
|
||
|
def cleanup_scratch(scratch_dir, mounts_dir):
|
||
|
"""
|
||
|
Function to cleanup the scratch directory
|
||
|
@@ -128,52 +69,6 @@ def cleanup_scratch(scratch_dir, mounts_dir):
|
||
|
api.current_logger().debug('Recursively removed scratch directory %s.', scratch_dir)
|
||
|
|
||
|
|
||
|
-def _create_mount_disk_image(disk_images_directory, path):
|
||
|
- """
|
||
|
- Creates the mount disk image, for cases when we hit XFS with ftype=0
|
||
|
- """
|
||
|
- diskimage_path = os.path.join(disk_images_directory, _mount_name(path))
|
||
|
- disk_size = _overlay_disk_size()
|
||
|
-
|
||
|
- api.current_logger().debug('Attempting to create disk image with size %d MiB at %s', disk_size, diskimage_path)
|
||
|
- utils.call_with_failure_hint(
|
||
|
- cmd=['/bin/dd', 'if=/dev/zero', 'of={}'.format(diskimage_path), 'bs=1M', 'count={}'.format(disk_size)],
|
||
|
- hint='Please ensure that there is enough diskspace in {} at least {} MiB are needed'.format(
|
||
|
- diskimage_path, disk_size)
|
||
|
- )
|
||
|
-
|
||
|
- api.current_logger().debug('Creating ext4 filesystem in disk image at %s', diskimage_path)
|
||
|
- try:
|
||
|
- utils.call_with_oserror_handled(cmd=['/sbin/mkfs.ext4', '-F', diskimage_path])
|
||
|
- except CalledProcessError as e:
|
||
|
- api.current_logger().error('Failed to create ext4 filesystem %s', exc_info=True)
|
||
|
- raise StopActorExecutionError(
|
||
|
- message=str(e)
|
||
|
- )
|
||
|
-
|
||
|
- return diskimage_path
|
||
|
-
|
||
|
-
|
||
|
-def _create_diskimages_dir(scratch_dir, diskimages_dir):
|
||
|
- """
|
||
|
- Prepares directories for disk images
|
||
|
- """
|
||
|
- api.current_logger().debug('Creating disk images directory.')
|
||
|
- try:
|
||
|
- utils.makedirs(diskimages_dir)
|
||
|
- api.current_logger().debug('Done creating disk images directory.')
|
||
|
- except OSError:
|
||
|
- api.current_logger().error('Failed to create disk images directory %s', diskimages_dir, exc_info=True)
|
||
|
-
|
||
|
- # This is an attempt for giving the user a chance to resolve it on their own
|
||
|
- raise StopActorExecutionError(
|
||
|
- message='Failed to prepare environment for package download while creating directories.',
|
||
|
- details={
|
||
|
- 'hint': 'Please ensure that {scratch_dir} is empty and modifiable.'.format(scratch_dir=scratch_dir)
|
||
|
- }
|
||
|
- )
|
||
|
-
|
||
|
-
|
||
|
def _create_mounts_dir(scratch_dir, mounts_dir):
|
||
|
"""
|
||
|
Prepares directories for mounts
|
||
|
@@ -214,7 +109,7 @@ def create_source_overlay(mounts_dir, scratch_dir, xfs_info, storage_info, mount
|
||
|
scratch_dir=scratch_dir, mounts_dir=mounts_dir))
|
||
|
try:
|
||
|
_create_mounts_dir(scratch_dir, mounts_dir)
|
||
|
- mounts = _prepare_required_mounts(scratch_dir, mounts_dir, _get_mountpoints(storage_info), xfs_info)
|
||
|
+ mounts = _prepare_required_mounts_old(scratch_dir, mounts_dir, _get_mountpoints(storage_info), xfs_info)
|
||
|
with mounts.pop('/') as root_mount:
|
||
|
with mounting.OverlayMount(name='system_overlay', source='/', workdir=root_mount.target) as root_overlay:
|
||
|
if mount_target:
|
||
|
@@ -228,3 +123,119 @@ def create_source_overlay(mounts_dir, scratch_dir, xfs_info, storage_info, mount
|
||
|
except Exception:
|
||
|
cleanup_scratch(scratch_dir, mounts_dir)
|
||
|
raise
|
||
|
+
|
||
|
+
|
||
|
+# #############################################################################
|
||
|
+# Deprecated OVL solution ...
|
||
|
+# This is going to be removed in future as the whole functionality is going to
|
||
|
+# be replaced by new one. The problem is that the new solution can potentially
|
||
|
+# negatively affect systems with many loop mountpoints, so let's keep this
|
||
|
+# as a workaround for now. I am separating the old and new code in this way
|
||
|
+# to make the future removal easy.
|
||
|
+# IMPORTANT: Before an update of functions above, ensure the functionality of
|
||
|
+# the code below is not affected, otherwise copy the function below with the
|
||
|
+# "_old" suffix.
|
||
|
+# #############################################################################
|
||
|
+def _ensure_enough_diskimage_space_old(space_needed, directory):
|
||
|
+ stat = os.statvfs(directory)
|
||
|
+ if (stat.f_frsize * stat.f_bavail) < (space_needed * 1024 * 1024):
|
||
|
+ message = ('Not enough space available for creating required disk images in {directory}. ' +
|
||
|
+ 'Needed: {space_needed} MiB').format(space_needed=space_needed, directory=directory)
|
||
|
+ api.current_logger().error(message)
|
||
|
+ raise StopActorExecutionError(message)
|
||
|
+
|
||
|
+
|
||
|
+def _overlay_disk_size_old():
|
||
|
+ """
|
||
|
+ Convenient function to retrieve the overlay disk size
|
||
|
+ """
|
||
|
+ try:
|
||
|
+ env_size = os.getenv('LEAPP_OVL_SIZE', default='2048')
|
||
|
+ disk_size = int(env_size)
|
||
|
+ except ValueError:
|
||
|
+ disk_size = 2048
|
||
|
+ api.current_logger().warning(
|
||
|
+ 'Invalid "LEAPP_OVL_SIZE" environment variable "%s". Setting default "%d" value', env_size, disk_size
|
||
|
+ )
|
||
|
+ return disk_size
|
||
|
+
|
||
|
+
|
||
|
+def _create_diskimages_dir_old(scratch_dir, diskimages_dir):
|
||
|
+ """
|
||
|
+ Prepares directories for disk images
|
||
|
+ """
|
||
|
+ api.current_logger().debug('Creating disk images directory.')
|
||
|
+ try:
|
||
|
+ utils.makedirs(diskimages_dir)
|
||
|
+ api.current_logger().debug('Done creating disk images directory.')
|
||
|
+ except OSError:
|
||
|
+ api.current_logger().error('Failed to create disk images directory %s', diskimages_dir, exc_info=True)
|
||
|
+
|
||
|
+ # This is an attempt for giving the user a chance to resolve it on their own
|
||
|
+ raise StopActorExecutionError(
|
||
|
+ message='Failed to prepare environment for package download while creating directories.',
|
||
|
+ details={
|
||
|
+ 'hint': 'Please ensure that {scratch_dir} is empty and modifiable.'.format(scratch_dir=scratch_dir)
|
||
|
+ }
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+def _create_mount_disk_image_old(disk_images_directory, path):
|
||
|
+ """
|
||
|
+ Creates the mount disk image, for cases when we hit XFS with ftype=0
|
||
|
+ """
|
||
|
+ diskimage_path = os.path.join(disk_images_directory, _mount_name(path))
|
||
|
+ disk_size = _overlay_disk_size_old()
|
||
|
+
|
||
|
+ api.current_logger().debug('Attempting to create disk image with size %d MiB at %s', disk_size, diskimage_path)
|
||
|
+ utils.call_with_failure_hint(
|
||
|
+ cmd=['/bin/dd', 'if=/dev/zero', 'of={}'.format(diskimage_path), 'bs=1M', 'count={}'.format(disk_size)],
|
||
|
+ hint='Please ensure that there is enough diskspace in {} at least {} MiB are needed'.format(
|
||
|
+ diskimage_path, disk_size)
|
||
|
+ )
|
||
|
+
|
||
|
+ api.current_logger().debug('Creating ext4 filesystem in disk image at %s', diskimage_path)
|
||
|
+ try:
|
||
|
+ utils.call_with_oserror_handled(cmd=['/sbin/mkfs.ext4', '-F', diskimage_path])
|
||
|
+ except CalledProcessError as e:
|
||
|
+ api.current_logger().error('Failed to create ext4 filesystem %s', exc_info=True)
|
||
|
+ raise StopActorExecutionError(
|
||
|
+ message=str(e)
|
||
|
+ )
|
||
|
+
|
||
|
+ return diskimage_path
|
||
|
+
|
||
|
+
|
||
|
+def _prepare_required_mounts_old(scratch_dir, mounts_dir, mount_points, xfs_info):
|
||
|
+ result = {
|
||
|
+ mount_point.fs_file: mounting.NullMount(
|
||
|
+ _mount_dir(mounts_dir, mount_point.fs_file)) for mount_point in mount_points
|
||
|
+ }
|
||
|
+
|
||
|
+ if not xfs_info.mountpoints_without_ftype:
|
||
|
+ return result
|
||
|
+
|
||
|
+ space_needed = _overlay_disk_size_old() * len(xfs_info.mountpoints_without_ftype)
|
||
|
+ disk_images_directory = os.path.join(scratch_dir, 'diskimages')
|
||
|
+
|
||
|
+ # Ensure we cleanup old disk images before we check for space constraints.
|
||
|
+ run(['rm', '-rf', disk_images_directory])
|
||
|
+ _create_diskimages_dir_old(scratch_dir, disk_images_directory)
|
||
|
+ _ensure_enough_diskimage_space_old(space_needed, scratch_dir)
|
||
|
+
|
||
|
+ mount_names = [mount_point.fs_file for mount_point in mount_points]
|
||
|
+
|
||
|
+ # TODO(pstodulk): this (adding rootfs into the set always) is hotfix for
|
||
|
+ # bz #1911802 (not ideal one..). The problem occurs one rootfs is ext4 fs,
|
||
|
+ # but /var/lib/leapp/... is under XFS without ftype; In such a case we can
|
||
|
+ # see still the very same problems as before. But letting you know that
|
||
|
+ # probably this is not the final solution, as we could possibly see the
|
||
|
+ # same problems on another partitions too (needs to be tested...). However,
|
||
|
+ # it could fit for now until we provide the complete solution around XFS
|
||
|
+ # workarounds (including management of required spaces for virtual FSs per
|
||
|
+ # mountpoints - without that, we cannot fix this properly)
|
||
|
+ for mountpoint in set(xfs_info.mountpoints_without_ftype + ['/']):
|
||
|
+ if mountpoint in mount_names:
|
||
|
+ image = _create_mount_disk_image_old(disk_images_directory, mountpoint)
|
||
|
+ result[mountpoint] = mounting.LoopMount(source=image, target=_mount_dir(mounts_dir, mountpoint))
|
||
|
+ return result
|
||
|
--
|
||
|
2.41.0
|
||
|
|