forked from rpms/leapp-repository
ee57901913
- Fix the calculation of the required free space on each partitions/volume for the upgrade transactions - Create source overlay images with dynamic sizes to optimize disk space consumption - Update GRUB2 when /boot resides on multiple devices aggregated in RAID - Use new leapp CLI API which provides better report summary output - Introduce possibility to add (custom) kernel drivers to initramfs - Detect and report use of deprecated Xorg drivers - Fix the generation of the report about hybrid images - Inhibit the upgrade when unsupported x86-64 microarchitecture is detected - Minor improvements and fixes of various reports - Requires leapp-framework 4.0 - Update leapp data files - Resolves: rhbz#2140011, rhbz#2144304, rhbz#2174095, rhbz#2219544, rhbz#2215997
1193 lines
50 KiB
Diff
1193 lines
50 KiB
Diff
From 030e1fccac6f15d4d5179a42ba43ba373cd727cf Mon Sep 17 00:00:00 2001
|
|
From: David Kubek <dkubek@redhat.com>
|
|
Date: Wed, 24 May 2023 10:40:23 +0200
|
|
Subject: [PATCH 31/42] Add possibility to add kernel drivers to initrd
|
|
|
|
Before this change there was no possibility for developers to specify what
|
|
kernel drivers should be included in the upgrade/target initramfs. This
|
|
includes third-party drivers, which are necessary for system upgrades in some
|
|
spefic use cases.
|
|
|
|
Changes include:
|
|
- A new model `KernelModule` (analogous to the `DracutModule` model) has been
|
|
created, to handle kernel drivers.
|
|
- Added an `include_kernel_drivers` field in the `UpgradeInitramfsTasks` model
|
|
to handle a list of these drivers.
|
|
- Data in the `include_kernel_drivers` field is processed correctly to detect
|
|
conflicting paths.
|
|
- Modified the `generate-iniram.sh` script to accept and process the new data
|
|
- Added checks for kernel drivers, in the `CheckInitramfsTasks` actor.
|
|
- Updated the unit-tests accordingly.
|
|
---
|
|
.../libraries/modscan.py | 1 +
|
|
.../libraries/checkinitramfstasks.py | 91 +++++----
|
|
.../tests/unit_test_checkinitramfstasks.py | 57 +++++-
|
|
.../libraries/targetinitramfsgenerator.py | 97 ++++++++--
|
|
.../tests/test_targetinitramfsgenerator.py | 174 +++++++++++-------
|
|
.../files/generate-initram.sh | 13 ++
|
|
.../libraries/upgradeinitramfsgenerator.py | 141 +++++++++++---
|
|
.../unit_test_upgradeinitramfsgenerator.py | 125 +++++++++----
|
|
.../system_upgrade/common/models/initramfs.py | 47 +++++
|
|
9 files changed, 563 insertions(+), 183 deletions(-)
|
|
|
|
diff --git a/repos/system_upgrade/common/actors/commonleappdracutmodules/libraries/modscan.py b/repos/system_upgrade/common/actors/commonleappdracutmodules/libraries/modscan.py
|
|
index 2b8d78a4..15150a50 100644
|
|
--- a/repos/system_upgrade/common/actors/commonleappdracutmodules/libraries/modscan.py
|
|
+++ b/repos/system_upgrade/common/actors/commonleappdracutmodules/libraries/modscan.py
|
|
@@ -31,6 +31,7 @@ _REQUIRED_PACKAGES = [
|
|
'kernel-core',
|
|
'kernel-modules',
|
|
'keyutils',
|
|
+ 'kmod',
|
|
'lldpad',
|
|
'lvm2',
|
|
'mdadm',
|
|
diff --git a/repos/system_upgrade/common/actors/initramfs/checkinitramfstasks/libraries/checkinitramfstasks.py b/repos/system_upgrade/common/actors/initramfs/checkinitramfstasks/libraries/checkinitramfstasks.py
|
|
index cd87f74d..0d7d8317 100644
|
|
--- a/repos/system_upgrade/common/actors/initramfs/checkinitramfstasks/libraries/checkinitramfstasks.py
|
|
+++ b/repos/system_upgrade/common/actors/initramfs/checkinitramfstasks/libraries/checkinitramfstasks.py
|
|
@@ -6,11 +6,11 @@ from leapp.libraries.stdlib import api
|
|
from leapp.models import TargetInitramfsTasks, UpgradeInitramfsTasks
|
|
|
|
DRACUT_MOD_DIR = '/usr/lib/dracut/modules.d/'
|
|
-SUMMARY_DRACUT_FMT = (
|
|
- 'The requested dracut modules for the initramfs are in conflict.'
|
|
- ' At least one dracut module is specified to be installed from'
|
|
- ' multiple paths. The list of conflicting dracut module names'
|
|
- ' with paths is listed below: {}'
|
|
+SUMMARY_FMT = (
|
|
+ 'The requested {kind} modules for the initramfs are in conflict.'
|
|
+ ' At least one {kind} module is specified to be installed from'
|
|
+ ' multiple paths. The list of conflicting {kind} module names'
|
|
+ ' with paths is listed below: {conflicts}'
|
|
)
|
|
|
|
|
|
@@ -22,51 +22,72 @@ def _printable_modules(conflicts):
|
|
return ''.join(output)
|
|
|
|
|
|
-def _treat_path(dmodule):
|
|
+def _treat_path_dracut(dmodule):
|
|
"""
|
|
In case the path is not set, set the expected path of the dracut module.
|
|
"""
|
|
+
|
|
if not dmodule.module_path:
|
|
return os.path.join(DRACUT_MOD_DIR, dmodule.name)
|
|
return dmodule.module_path
|
|
|
|
|
|
-def _detect_dracut_modules_conflicts(msgtype):
|
|
+def _treat_path_kernel(kmodule):
|
|
+ """
|
|
+ In case the path of a kernel module is not set, indicate that the module is
|
|
+ taken from the current system.
|
|
+ """
|
|
+
|
|
+ if not kmodule.module_path:
|
|
+ return kmodule.name + ' (system)'
|
|
+ return kmodule.module_path
|
|
+
|
|
+
|
|
+def _detect_modules_conflicts(msgtype, kind):
|
|
"""
|
|
Return dict of modules with conflicting tasks
|
|
|
|
- In this case when a dracut module should be applied but different
|
|
- sources are specified. E.g.:
|
|
- include dracut modules X where,
|
|
+ In this case when a module should be applied but different sources are
|
|
+ specified. E.g.:
|
|
+ include modules X where,
|
|
msg A) X
|
|
msg B) X from custom path
|
|
"""
|
|
- dracut_modules = defaultdict(set)
|
|
+
|
|
+ modules_map = {
|
|
+ 'dracut': {
|
|
+ 'msgattr': 'include_dracut_modules',
|
|
+ 'treat_path_fn': _treat_path_dracut,
|
|
+ },
|
|
+ 'kernel': {
|
|
+ 'msgattr': 'include_kernel_modules',
|
|
+ 'treat_path_fn': _treat_path_kernel
|
|
+ },
|
|
+ }
|
|
+
|
|
+ modules = defaultdict(set)
|
|
for msg in api.consume(msgtype):
|
|
- for dmodule in msg.include_dracut_modules:
|
|
- dracut_modules[dmodule.name].add(_treat_path(dmodule))
|
|
- return {key: val for key, val in dracut_modules.items() if len(val) > 1}
|
|
+ for module in getattr(msg, modules_map[kind]['msgattr']):
|
|
+ treat_path_fn = modules_map[kind]['treat_path_fn']
|
|
+ modules[module.name].add(treat_path_fn(module))
|
|
+ return {key: val for key, val in modules.items() if len(val) > 1}
|
|
+
|
|
+
|
|
+def report_conflicts(msgname, kind, msgtype):
|
|
+ conflicts = _detect_modules_conflicts(msgtype, kind)
|
|
+ if not conflicts:
|
|
+ return
|
|
+ report = [
|
|
+ reporting.Title('Conflicting requirements of {kind} modules for the {msgname} initramfs'.format(
|
|
+ kind=kind, msgname=msgname)),
|
|
+ reporting.Summary(SUMMARY_FMT.format(kind=kind, conflicts=_printable_modules(conflicts))),
|
|
+ reporting.Severity(reporting.Severity.HIGH),
|
|
+ reporting.Groups([reporting.Groups.SANITY, reporting.Groups.INHIBITOR]),
|
|
+ ]
|
|
+ reporting.create_report(report)
|
|
|
|
|
|
def process():
|
|
- conflicts = _detect_dracut_modules_conflicts(UpgradeInitramfsTasks)
|
|
- if conflicts:
|
|
- report = [
|
|
- reporting.Title('Conflicting requirements of dracut modules for the upgrade initramfs'),
|
|
- reporting.Summary(SUMMARY_DRACUT_FMT.format(_printable_modules(conflicts))),
|
|
- reporting.Severity(reporting.Severity.HIGH),
|
|
- reporting.Groups([reporting.Groups.SANITY]),
|
|
- reporting.Groups([reporting.Groups.INHIBITOR]),
|
|
- ]
|
|
- reporting.create_report(report)
|
|
-
|
|
- conflicts = _detect_dracut_modules_conflicts(TargetInitramfsTasks)
|
|
- if conflicts:
|
|
- report = [
|
|
- reporting.Title('Conflicting requirements of dracut modules for the target initramfs'),
|
|
- reporting.Summary(SUMMARY_DRACUT_FMT.format(_printable_modules(conflicts))),
|
|
- reporting.Severity(reporting.Severity.HIGH),
|
|
- reporting.Groups([reporting.Groups.SANITY]),
|
|
- reporting.Groups([reporting.Groups.INHIBITOR]),
|
|
- ]
|
|
- reporting.create_report(report)
|
|
+ report_conflicts('upgrade', 'kernel', UpgradeInitramfsTasks)
|
|
+ report_conflicts('upgrade', 'dracut', UpgradeInitramfsTasks)
|
|
+ report_conflicts('target', 'dracut', TargetInitramfsTasks)
|
|
diff --git a/repos/system_upgrade/common/actors/initramfs/checkinitramfstasks/tests/unit_test_checkinitramfstasks.py b/repos/system_upgrade/common/actors/initramfs/checkinitramfstasks/tests/unit_test_checkinitramfstasks.py
|
|
index aad79c73..fca15f73 100644
|
|
--- a/repos/system_upgrade/common/actors/initramfs/checkinitramfstasks/tests/unit_test_checkinitramfstasks.py
|
|
+++ b/repos/system_upgrade/common/actors/initramfs/checkinitramfstasks/tests/unit_test_checkinitramfstasks.py
|
|
@@ -6,7 +6,7 @@ from leapp import reporting
|
|
from leapp.libraries.actor import checkinitramfstasks
|
|
from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked
|
|
from leapp.libraries.stdlib import api
|
|
-from leapp.models import DracutModule, Report, TargetInitramfsTasks, UpgradeInitramfsTasks
|
|
+from leapp.models import DracutModule, KernelModule, TargetInitramfsTasks, UpgradeInitramfsTasks
|
|
from leapp.utils.report import is_inhibitor
|
|
|
|
|
|
@@ -14,7 +14,8 @@ def gen_UIT(modules):
|
|
if not isinstance(modules, list):
|
|
modules = [modules]
|
|
dracut_modules = [DracutModule(name=i[0], module_path=i[1]) for i in modules]
|
|
- return UpgradeInitramfsTasks(include_dracut_modules=dracut_modules)
|
|
+ kernel_modules = [KernelModule(name=i[0], module_path=i[1]) for i in modules]
|
|
+ return UpgradeInitramfsTasks(include_dracut_modules=dracut_modules, include_kernel_modules=kernel_modules)
|
|
|
|
|
|
def gen_TIT(modules):
|
|
@@ -71,9 +72,57 @@ def gen_TIT(modules):
|
|
TargetInitramfsTasks,
|
|
),
|
|
])
|
|
-def test_conflict_detection(monkeypatch, expected_res, input_msgs, test_msg_type):
|
|
+def test_dracut_conflict_detection(monkeypatch, expected_res, input_msgs, test_msg_type):
|
|
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=input_msgs))
|
|
- res = checkinitramfstasks._detect_dracut_modules_conflicts(test_msg_type)
|
|
+ res = checkinitramfstasks._detect_modules_conflicts(test_msg_type, 'dracut')
|
|
+ assert res == expected_res
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize('expected_res,input_msgs,test_msg_type', [
|
|
+ (
|
|
+ {},
|
|
+ [],
|
|
+ UpgradeInitramfsTasks,
|
|
+ ),
|
|
+ (
|
|
+ {},
|
|
+ [gen_UIT([('modA', 'pathA'), ('modB', 'pathB')])],
|
|
+ UpgradeInitramfsTasks,
|
|
+ ),
|
|
+ (
|
|
+ {},
|
|
+ [gen_UIT([('modA', 'pathA'), ('modA', 'pathA')])],
|
|
+ UpgradeInitramfsTasks,
|
|
+ ),
|
|
+ (
|
|
+ {'modA': {'pathA', 'pathB'}},
|
|
+ [gen_UIT([('modA', 'pathA'), ('modA', 'pathB')])],
|
|
+ UpgradeInitramfsTasks,
|
|
+ ),
|
|
+ (
|
|
+ {'modA': {'pathA', 'pathB'}},
|
|
+ [gen_UIT(('modA', 'pathA')), gen_UIT(('modA', 'pathB'))],
|
|
+ UpgradeInitramfsTasks,
|
|
+ ),
|
|
+ (
|
|
+ {'modA': {'pathA', 'pathB'}},
|
|
+ [gen_UIT([('modA', 'pathA'), ('modA', 'pathB'), ('modB', 'pathC')])],
|
|
+ UpgradeInitramfsTasks,
|
|
+ ),
|
|
+ (
|
|
+ {'modA': {'modA (system)', 'pathB'}},
|
|
+ [gen_UIT([('modA', None), ('modA', 'pathB')])],
|
|
+ UpgradeInitramfsTasks,
|
|
+ ),
|
|
+ (
|
|
+ {},
|
|
+ [gen_UIT([('modA', 'pathA'), ('modA', 'pathB')])],
|
|
+ TargetInitramfsTasks,
|
|
+ ),
|
|
+])
|
|
+def test_kernel_conflict_detection(monkeypatch, expected_res, input_msgs, test_msg_type):
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=input_msgs))
|
|
+ res = checkinitramfstasks._detect_modules_conflicts(test_msg_type, 'kernel')
|
|
assert res == expected_res
|
|
|
|
|
|
diff --git a/repos/system_upgrade/common/actors/initramfs/targetinitramfsgenerator/libraries/targetinitramfsgenerator.py b/repos/system_upgrade/common/actors/initramfs/targetinitramfsgenerator/libraries/targetinitramfsgenerator.py
|
|
index 1a7a3e19..39666017 100644
|
|
--- a/repos/system_upgrade/common/actors/initramfs/targetinitramfsgenerator/libraries/targetinitramfsgenerator.py
|
|
+++ b/repos/system_upgrade/common/actors/initramfs/targetinitramfsgenerator/libraries/targetinitramfsgenerator.py
|
|
@@ -1,3 +1,7 @@
|
|
+import errno
|
|
+import os
|
|
+import shutil
|
|
+
|
|
from leapp.exceptions import StopActorExecutionError
|
|
from leapp.libraries.stdlib import api, CalledProcessError, run
|
|
from leapp.models import InitrdIncludes # deprecated
|
|
@@ -7,25 +11,66 @@ from leapp.utils.deprecation import suppress_deprecation
|
|
DRACUT_DIR = '/usr/lib/dracut/modules.d/'
|
|
|
|
|
|
-def copy_dracut_modules(modules):
|
|
+def _get_target_kernel_modules_dir(kernel_version):
|
|
+ """
|
|
+ Return the path where the custom kernel modules should be copied.
|
|
+ """
|
|
+
|
|
+ modules_dir = os.path.join('/', 'lib', 'modules', kernel_version, 'extra', 'leapp')
|
|
+
|
|
+ return modules_dir
|
|
+
|
|
+
|
|
+def _copy_modules(modules, dst_dir, kind):
|
|
"""
|
|
- Copy every dracut module with specified path into the expected directory.
|
|
+ Copy modules of given kind to the specified destination directory.
|
|
+
|
|
+ Attempts to remove an cleanup by removing the existing destination
|
|
+ directory. If the directory does not exist, it is created anew. Then, for
|
|
+ each module message, it checks if the module has a module path specified. If
|
|
+ the module already exists in the destination directory, a debug message is
|
|
+ logged, and the operation is skipped. Otherwise, the module is copied to the
|
|
+ destination directory.
|
|
|
|
- original content is overwritten if exists
|
|
"""
|
|
- # FIXME: use just python functions instead of shell cmds
|
|
+
|
|
+ try:
|
|
+ os.makedirs(dst_dir)
|
|
+ except OSError as exc:
|
|
+ if exc.errno == errno.EEXIST and os.path.isdir(dst_dir):
|
|
+ pass
|
|
+ else:
|
|
+ raise
|
|
+
|
|
for module in modules:
|
|
if not module.module_path:
|
|
continue
|
|
+
|
|
+ dst_path = os.path.join(dst_dir, os.path.basename(module.module_path))
|
|
+ if os.path.exists(dst_path):
|
|
+ api.current_logger().debug(
|
|
+ 'The {name} {kind} module has been already installed. Skipping.'
|
|
+ .format(name=module.name, kind=kind))
|
|
+ continue
|
|
+
|
|
+ copy_fn = shutil.copytree
|
|
+ if os.path.isfile(module.module_path):
|
|
+ copy_fn = shutil.copy2
|
|
+
|
|
try:
|
|
- # context.copytree_to(module.module_path, os.path.join(DRACUT_DIR, os.path.basename(module.module_path)))
|
|
- run(['cp', '-f', '-a', module.module_path, DRACUT_DIR])
|
|
- except CalledProcessError as e:
|
|
- api.current_logger().error('Failed to copy dracut module "{name}" from "{source}" to "{target}"'.format(
|
|
- name=module.name, source=module.module_path, target=DRACUT_DIR), exc_info=True)
|
|
- # FIXME: really do we want to raise the error and stop execution completely??....
|
|
+ api.current_logger().debug(
|
|
+ 'Copying {kind} module "{name}" to "{path}".'
|
|
+ .format(kind=kind, name=module.name, path=dst_path))
|
|
+
|
|
+ copy_fn(module.module_path, dst_path)
|
|
+ except shutil.Error as e:
|
|
+ api.current_logger().error(
|
|
+ 'Failed to copy {kind} module "{name}" from "{source}" to "{target}"'.format(
|
|
+ kind=kind, name=module.name, source=module.module_path, target=dst_dir),
|
|
+ exc_info=True)
|
|
raise StopActorExecutionError(
|
|
- message='Failed to install dracut modules required in the target initramfs. Error: {}'.format(str(e))
|
|
+ message='Failed to install {kind} modules required in the initram. Error: {error}'.format(
|
|
+ kind=kind, error=str(e))
|
|
)
|
|
|
|
|
|
@@ -43,9 +88,11 @@ def _get_modules():
|
|
# supposed to create any such tasks before the reporting phase, so we
|
|
# are able to check it.
|
|
#
|
|
- modules = []
|
|
+ modules = {'dracut': [], 'kernel': []}
|
|
for task in api.consume(TargetInitramfsTasks):
|
|
- modules.extend(task.include_dracut_modules)
|
|
+ modules['dracut'].extend(task.include_dracut_modules)
|
|
+ modules['kernel'].extend(task.include_kernel_modules)
|
|
+
|
|
return modules
|
|
|
|
|
|
@@ -53,7 +100,7 @@ def process():
|
|
files = _get_files()
|
|
modules = _get_modules()
|
|
|
|
- if not files and not modules:
|
|
+ if not files and not modules['kernel'] and not modules['dracut']:
|
|
api.current_logger().debug(
|
|
'No additional files or modules required to add into the target initramfs.')
|
|
return
|
|
@@ -65,15 +112,29 @@ def process():
|
|
details={'Problem': 'Did not receive a message with installed RHEL-8 kernel version'
|
|
' (InstalledTargetKernelVersion)'})
|
|
|
|
- copy_dracut_modules(modules)
|
|
+ _copy_modules(modules['dracut'], DRACUT_DIR, 'dracut')
|
|
+ _copy_modules(modules['kernel'], _get_target_kernel_modules_dir(target_kernel.version), 'kernel')
|
|
+
|
|
+ # Discover any new modules and regenerate modules.dep
|
|
+ should_regenerate = any(module.module_path is not None for module in modules['kernel'])
|
|
+ if should_regenerate:
|
|
+ try:
|
|
+ run(['depmod', target_kernel.version, '-a'])
|
|
+ except CalledProcessError as e:
|
|
+ raise StopActorExecutionError('Failed to generate modules.dep and map files.', details={'details': str(e)})
|
|
+
|
|
try:
|
|
# multiple files|modules need to be quoted, see --install | --add in dracut(8)
|
|
- module_names = list({module.name for module in modules})
|
|
+ dracut_module_names = list({module.name for module in modules['dracut']})
|
|
+ kernel_module_names = list({module.name for module in modules['kernel']})
|
|
cmd = ['dracut', '-f', '--kver', target_kernel.version]
|
|
if files:
|
|
cmd += ['--install', '{}'.format(' '.join(files))]
|
|
- if modules:
|
|
- cmd += ['--add', '{}'.format(' '.join(module_names))]
|
|
+ if modules['dracut']:
|
|
+ cmd += ['--add', '{}'.format(' '.join(dracut_module_names))]
|
|
+ if modules['kernel']:
|
|
+ cmd += ['--add-drivers', '{}'.format(' '.join(kernel_module_names))]
|
|
+
|
|
run(cmd)
|
|
except CalledProcessError as e:
|
|
# just hypothetic check, it should not die
|
|
diff --git a/repos/system_upgrade/common/actors/initramfs/targetinitramfsgenerator/tests/test_targetinitramfsgenerator.py b/repos/system_upgrade/common/actors/initramfs/targetinitramfsgenerator/tests/test_targetinitramfsgenerator.py
|
|
index 8403a431..57daca75 100644
|
|
--- a/repos/system_upgrade/common/actors/initramfs/targetinitramfsgenerator/tests/test_targetinitramfsgenerator.py
|
|
+++ b/repos/system_upgrade/common/actors/initramfs/targetinitramfsgenerator/tests/test_targetinitramfsgenerator.py
|
|
@@ -6,12 +6,9 @@ from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked
|
|
from leapp.libraries.stdlib import api, CalledProcessError
|
|
from leapp.utils.deprecation import suppress_deprecation
|
|
|
|
-from leapp.models import ( # isort:skip
|
|
- InitrdIncludes, # deprecated
|
|
- DracutModule,
|
|
- InstalledTargetKernelVersion,
|
|
- TargetInitramfsTasks
|
|
-)
|
|
+from leapp.models import ( # isort:skip
|
|
+ InitrdIncludes, # deprecated
|
|
+ DracutModule, KernelModule, InstalledTargetKernelVersion, TargetInitramfsTasks)
|
|
|
|
FILES = ['/file1', '/file2', '/dir/subdir/subsubdir/file3', '/file4', '/file5']
|
|
MODULES = [
|
|
@@ -25,13 +22,19 @@ NO_INCLUDE_MSG = 'No additional files or modules required to add into the target
|
|
|
|
|
|
def raise_call_error(args=None):
|
|
- raise CalledProcessError(
|
|
- message='A Leapp Command Error occurred.',
|
|
- command=args,
|
|
- result={'signal': None, 'exit_code': 1, 'pid': 0, 'stdout': 'fake', 'stderr': 'fake'})
|
|
+ raise CalledProcessError(message='A Leapp Command Error occurred.',
|
|
+ command=args,
|
|
+ result={
|
|
+ 'signal': None,
|
|
+ 'exit_code': 1,
|
|
+ 'pid': 0,
|
|
+ 'stdout': 'fake',
|
|
+ 'stderr': 'fake'
|
|
+ })
|
|
|
|
|
|
class RunMocked(object):
|
|
+
|
|
def __init__(self, raise_err=False):
|
|
self.called = 0
|
|
self.args = []
|
|
@@ -44,20 +47,26 @@ class RunMocked(object):
|
|
raise_call_error(args)
|
|
|
|
|
|
-def gen_TIT(modules, files):
|
|
- if not isinstance(modules, list):
|
|
- modules = [modules]
|
|
- if not isinstance(files, list):
|
|
- files = [files]
|
|
- dracut_modules = [DracutModule(name=i[0], module_path=i[1]) for i in modules]
|
|
- return TargetInitramfsTasks(include_files=files, include_dracut_modules=dracut_modules)
|
|
+def _ensure_list(data):
|
|
+ return data if isinstance(data, list) else [data]
|
|
+
|
|
+
|
|
+def gen_TIT(dracut_modules, kernel_modules, files):
|
|
+ files = _ensure_list(files)
|
|
+
|
|
+ dracut_modules = [DracutModule(name=i[0], module_path=i[1]) for i in _ensure_list(dracut_modules)]
|
|
+ kernel_modules = [KernelModule(name=i[0], module_path=i[1]) for i in _ensure_list(kernel_modules)]
|
|
+
|
|
+ return TargetInitramfsTasks(
|
|
+ include_files=files,
|
|
+ include_dracut_modules=dracut_modules,
|
|
+ include_kernel_modules=kernel_modules,
|
|
+ )
|
|
|
|
|
|
@suppress_deprecation(InitrdIncludes)
|
|
def gen_InitrdIncludes(files):
|
|
- if not isinstance(files, list):
|
|
- files = [files]
|
|
- return InitrdIncludes(files=files)
|
|
+ return InitrdIncludes(files=_ensure_list(files))
|
|
|
|
|
|
def test_no_includes(monkeypatch):
|
|
@@ -77,12 +86,12 @@ TEST_CASES = [
|
|
gen_InitrdIncludes(FILES[3:]),
|
|
],
|
|
[
|
|
- gen_TIT([], FILES[0:3]),
|
|
- gen_TIT([], FILES[3:]),
|
|
+ gen_TIT([], [], FILES[0:3]),
|
|
+ gen_TIT([], [], FILES[3:]),
|
|
],
|
|
[
|
|
gen_InitrdIncludes(FILES[0:3]),
|
|
- gen_TIT([], FILES[3:]),
|
|
+ gen_TIT([], [], FILES[3:]),
|
|
],
|
|
]
|
|
|
|
@@ -93,7 +102,7 @@ def test_no_kernel_version(monkeypatch, msgs):
|
|
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
|
|
monkeypatch.setattr(targetinitramfsgenerator, 'run', run_mocked)
|
|
# FIXME
|
|
- monkeypatch.setattr(targetinitramfsgenerator, 'copy_dracut_modules', lambda dummy: None)
|
|
+ monkeypatch.setattr(targetinitramfsgenerator, '_copy_modules', lambda *_: None)
|
|
|
|
with pytest.raises(StopActorExecutionError) as e:
|
|
targetinitramfsgenerator.process()
|
|
@@ -105,11 +114,11 @@ def test_no_kernel_version(monkeypatch, msgs):
|
|
def test_dracut_fail(monkeypatch, msgs):
|
|
run_mocked = RunMocked(raise_err=True)
|
|
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
|
|
- monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(
|
|
- msgs=msgs+[InstalledTargetKernelVersion(version=KERNEL_VERSION)]))
|
|
+ monkeypatch.setattr(api, 'current_actor',
|
|
+ CurrentActorMocked(msgs=msgs + [InstalledTargetKernelVersion(version=KERNEL_VERSION)]))
|
|
monkeypatch.setattr(targetinitramfsgenerator, 'run', run_mocked)
|
|
# FIXME
|
|
- monkeypatch.setattr(targetinitramfsgenerator, 'copy_dracut_modules', lambda dummy: None)
|
|
+ monkeypatch.setattr(targetinitramfsgenerator, '_copy_modules', lambda *_: None)
|
|
|
|
with pytest.raises(StopActorExecutionError) as e:
|
|
targetinitramfsgenerator.process()
|
|
@@ -117,47 +126,71 @@ def test_dracut_fail(monkeypatch, msgs):
|
|
assert run_mocked.called
|
|
|
|
|
|
-@pytest.mark.parametrize('msgs,files,modules', [
|
|
- # deprecated set
|
|
- ([gen_InitrdIncludes(FILES[0])], FILES[0:1], []),
|
|
- ([gen_InitrdIncludes(FILES)], FILES, []),
|
|
- ([gen_InitrdIncludes(FILES[0:3]), gen_InitrdIncludes(FILES[3:])], FILES, []),
|
|
- ([gen_InitrdIncludes(FILES[0:3]), gen_InitrdIncludes(FILES)], FILES, []),
|
|
-
|
|
- # new set for files only
|
|
- ([gen_TIT([], FILES[0])], FILES[0:1], []),
|
|
- ([gen_TIT([], FILES)], FILES, []),
|
|
- ([gen_TIT([], FILES[0:3]), gen_TIT([], FILES[3:])], FILES, []),
|
|
- ([gen_TIT([], FILES[0:3]), gen_TIT([], FILES)], FILES, []),
|
|
-
|
|
- # deprecated and new msgs for files only
|
|
- ([gen_InitrdIncludes(FILES[0:3]), gen_TIT([], FILES[3:])], FILES, []),
|
|
-
|
|
- # modules only
|
|
- ([gen_TIT(MODULES[0], [])], [], MODULES[0:1]),
|
|
- ([gen_TIT(MODULES, [])], [], MODULES),
|
|
- ([gen_TIT(MODULES[0:3], []), gen_TIT(MODULES[3], [])], [], MODULES),
|
|
-
|
|
- # modules only - duplicates; see notes in the library
|
|
- ([gen_TIT(MODULES[0:3], []), gen_TIT(MODULES, [])], [], MODULES),
|
|
-
|
|
- # modules + files (new only)
|
|
- ([gen_TIT(MODULES, FILES)], FILES, MODULES),
|
|
- ([gen_TIT(MODULES[0:3], FILES[0:3]), gen_TIT(MODULES[3:], FILES[3:])], FILES, MODULES),
|
|
- ([gen_TIT(MODULES, []), gen_TIT([], FILES)], FILES, MODULES),
|
|
-
|
|
- # modules + files with deprecated msgs
|
|
- ([gen_TIT(MODULES, []), gen_InitrdIncludes(FILES)], FILES, MODULES),
|
|
- ([gen_TIT(MODULES, FILES[0:3]), gen_InitrdIncludes(FILES[3:])], FILES, MODULES),
|
|
-
|
|
-])
|
|
-def test_flawless(monkeypatch, msgs, files, modules):
|
|
+@pytest.mark.parametrize(
|
|
+ 'msgs,files,dracut_modules,kernel_modules',
|
|
+ [
|
|
+ # deprecated set
|
|
+ ([gen_InitrdIncludes(FILES[0])], FILES[0:1], [], []),
|
|
+ ([gen_InitrdIncludes(FILES)], FILES, [], []),
|
|
+ ([gen_InitrdIncludes(FILES[0:3]), gen_InitrdIncludes(FILES[3:])], FILES, [], []),
|
|
+ ([gen_InitrdIncludes(FILES[0:3]), gen_InitrdIncludes(FILES)], FILES, [], []),
|
|
+
|
|
+ # new set for files only
|
|
+ ([gen_TIT([], [], FILES[0])], FILES[0:1], [], []),
|
|
+ ([gen_TIT([], [], FILES)], FILES, [], []),
|
|
+ ([gen_TIT([], [], FILES[0:3]), gen_TIT([], [], FILES[3:])], FILES, [], []),
|
|
+ ([gen_TIT([], [], FILES[0:3]), gen_TIT([], [], FILES)], FILES, [], []),
|
|
+
|
|
+ # deprecated and new msgs for files only
|
|
+ ([gen_InitrdIncludes(FILES[0:3]), gen_TIT([], [], FILES[3:])], FILES, [], []),
|
|
+
|
|
+ # dracut modules only
|
|
+ ([gen_TIT(MODULES[0], [], [])], [], MODULES[0:1], []),
|
|
+ ([gen_TIT(MODULES, [], [])], [], MODULES, []),
|
|
+ ([gen_TIT(MODULES[0:3], [], []), gen_TIT(MODULES[3], [], [])], [], MODULES, []),
|
|
+
|
|
+ # kernel modules only
|
|
+ ([gen_TIT([], MODULES[0], [])], [], [], MODULES[0:1]),
|
|
+ ([gen_TIT([], MODULES, [])], [], [], MODULES),
|
|
+ ([gen_TIT([], MODULES[0:3], []), gen_TIT([], MODULES[3], [])], [], [], MODULES),
|
|
+
|
|
+ # modules only - duplicates; see notes in the library
|
|
+ ([gen_TIT(MODULES[0:3], [], []), gen_TIT(MODULES, [], [])], [], MODULES, []),
|
|
+ ([gen_TIT([], MODULES[0:3], []), gen_TIT([], MODULES, [])], [], [], MODULES),
|
|
+
|
|
+ # modules + files (new only)
|
|
+ ([gen_TIT(MODULES, [], FILES)], FILES, MODULES, []),
|
|
+ ([gen_TIT([], MODULES, FILES)], FILES, [], MODULES),
|
|
+
|
|
+ ([gen_TIT(MODULES[0:3], [], FILES[0:3]), gen_TIT(MODULES[3:], [], FILES[3:])], FILES, MODULES, []),
|
|
+ ([gen_TIT([], MODULES[0:3], FILES[0:3]), gen_TIT([], MODULES[3:], FILES[3:])], FILES, [], MODULES),
|
|
+
|
|
+ ([gen_TIT(MODULES, [], []), gen_TIT([], [], FILES)], FILES, MODULES, []),
|
|
+ ([gen_TIT([], MODULES, []), gen_TIT([], [], FILES)], FILES, [], MODULES),
|
|
+
|
|
+ # kernel + dracut modules
|
|
+ (
|
|
+ [
|
|
+ gen_TIT(MODULES[0:3], MODULES[0:3], FILES[0:3]),
|
|
+ gen_TIT(MODULES[3:], MODULES[3:], FILES[3:])
|
|
+ ],
|
|
+ FILES, MODULES, MODULES
|
|
+ ),
|
|
+
|
|
+ # modules + files with deprecated msgs
|
|
+ ([gen_TIT(MODULES, [], []), gen_InitrdIncludes(FILES)], FILES, MODULES, []),
|
|
+ ([gen_TIT([], MODULES, []), gen_InitrdIncludes(FILES)], FILES, [], MODULES),
|
|
+
|
|
+ ([gen_TIT(MODULES, [], FILES[0:3]), gen_InitrdIncludes(FILES[3:])], FILES, MODULES, []),
|
|
+ ([gen_TIT([], MODULES, FILES[0:3]), gen_InitrdIncludes(FILES[3:])], FILES, [], MODULES),
|
|
+ ])
|
|
+def test_flawless(monkeypatch, msgs, files, dracut_modules, kernel_modules):
|
|
_msgs = msgs + [InstalledTargetKernelVersion(version=KERNEL_VERSION)]
|
|
run_mocked = RunMocked()
|
|
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=_msgs))
|
|
monkeypatch.setattr(targetinitramfsgenerator, 'run', run_mocked)
|
|
# FIXME
|
|
- monkeypatch.setattr(targetinitramfsgenerator, 'copy_dracut_modules', lambda dummy: None)
|
|
+ monkeypatch.setattr(targetinitramfsgenerator, '_copy_modules', lambda *_: None)
|
|
|
|
targetinitramfsgenerator.process()
|
|
assert run_mocked.called
|
|
@@ -171,11 +204,20 @@ def test_flawless(monkeypatch, msgs, files, modules):
|
|
else:
|
|
assert '--install' not in run_mocked.args
|
|
|
|
- # check modules
|
|
- if modules:
|
|
+ # check dracut modules
|
|
+ if dracut_modules:
|
|
assert '--add' in run_mocked.args
|
|
arg = run_mocked.args[run_mocked.args.index('--add') + 1]
|
|
- for m in modules:
|
|
+ for m in dracut_modules:
|
|
assert m[0] in arg
|
|
else:
|
|
assert '--add' not in run_mocked.args
|
|
+
|
|
+ # check kernel modules
|
|
+ if kernel_modules:
|
|
+ assert '--add-drivers' in run_mocked.args
|
|
+ arg = run_mocked.args[run_mocked.args.index('--add-drivers') + 1]
|
|
+ for m in kernel_modules:
|
|
+ assert m[0] in arg
|
|
+ else:
|
|
+ assert '--add-drivers' not in run_mocked.args
|
|
diff --git a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/files/generate-initram.sh b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/files/generate-initram.sh
|
|
index d6934147..9648234c 100755
|
|
--- a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/files/generate-initram.sh
|
|
+++ b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/files/generate-initram.sh
|
|
@@ -29,6 +29,9 @@ dracut_install_modules()
|
|
}
|
|
|
|
|
|
+# KERNEL_MODULES_ADD and DRACUT_MODULES_ADD are expected to be expanded and
|
|
+# we do not want to prevent word splitting in that case.
|
|
+# shellcheck disable=SC2086
|
|
build() {
|
|
dracut_install_modules
|
|
|
|
@@ -67,6 +70,15 @@ build() {
|
|
DRACUT_MODULES_ADD=$(echo "--add $LEAPP_ADD_DRACUT_MODULES" | sed 's/,/ --add /g')
|
|
fi
|
|
|
|
+ KERNEL_MODULES_ADD=""
|
|
+ if [[ -n "$LEAPP_ADD_KERNEL_MODULES" ]]; then
|
|
+ depmod "${KERNEL_VERSION}" -a
|
|
+ KERNEL_MODULES_ADD=$(
|
|
+ echo "--add-drivers $LEAPP_ADD_KERNEL_MODULES" |
|
|
+ sed 's/,/ --add-drivers /g'
|
|
+ )
|
|
+ fi
|
|
+
|
|
DRACUT_INSTALL="systemd-nspawn"
|
|
if [[ -n "$LEAPP_DRACUT_INSTALL_FILES" ]]; then
|
|
DRACUT_INSTALL="$DRACUT_INSTALL $LEAPP_DRACUT_INSTALL_FILES"
|
|
@@ -89,6 +101,7 @@ build() {
|
|
--confdir "$DRACUT_CONF_DIR" \
|
|
--install "$DRACUT_INSTALL" \
|
|
$DRACUT_MODULES_ADD \
|
|
+ $KERNEL_MODULES_ADD \
|
|
"$DRACUT_MDADMCONF_ARG" \
|
|
"$DRACUT_LVMCONF_ARG" \
|
|
--no-hostonly \
|
|
diff --git a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py
|
|
index 2f145217..f141d9e3 100644
|
|
--- a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py
|
|
+++ b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py
|
|
@@ -1,10 +1,11 @@
|
|
import os
|
|
import shutil
|
|
+from distutils.version import LooseVersion
|
|
|
|
from leapp.exceptions import StopActorExecutionError
|
|
from leapp.libraries.common import dnfplugin, mounting
|
|
from leapp.libraries.common.config.version import get_target_major_version
|
|
-from leapp.libraries.stdlib import api
|
|
+from leapp.libraries.stdlib import api, CalledProcessError
|
|
from leapp.models import RequiredUpgradeInitramPackages # deprecated
|
|
from leapp.models import UpgradeDracutModule # deprecated
|
|
from leapp.models import (
|
|
@@ -21,6 +22,45 @@ INITRAM_GEN_SCRIPT_NAME = 'generate-initram.sh'
|
|
DRACUT_DIR = '/dracut'
|
|
|
|
|
|
+def _get_target_kernel_version(context):
|
|
+ """
|
|
+ Get the version of the most recent kernel version within the container.
|
|
+ """
|
|
+
|
|
+ kernel_version = None
|
|
+ try:
|
|
+ results = context.call(['rpm', '-qa', 'kernel-core'], split=True)
|
|
+
|
|
+ versions = [ver.replace('kernel-core-', '') for ver in results['stdout']]
|
|
+ api.current_logger().debug(
|
|
+ 'Versions detected {versions}.'
|
|
+ .format(versions=versions))
|
|
+ sorted_versions = sorted(versions, key=LooseVersion, reverse=True)
|
|
+ kernel_version = next(iter(sorted_versions), None)
|
|
+ except CalledProcessError:
|
|
+ raise StopActorExecutionError(
|
|
+ 'Cannot get version of the installed kernel.',
|
|
+ details={'Problem': 'Could not query the currently installed kernel through rmp.'})
|
|
+
|
|
+ if not kernel_version:
|
|
+ raise StopActorExecutionError(
|
|
+ 'Cannot get version of the installed kernel.',
|
|
+ details={'Problem': 'A rpm query for the available kernels did not produce any results.'})
|
|
+
|
|
+ return kernel_version
|
|
+
|
|
+
|
|
+def _get_target_kernel_modules_dir(context):
|
|
+ """
|
|
+ Return the path where the custom kernel modules should be copied.
|
|
+ """
|
|
+
|
|
+ kernel_version = _get_target_kernel_version(context)
|
|
+ modules_dir = os.path.join('/', 'lib', 'modules', kernel_version, 'extra', 'leapp')
|
|
+
|
|
+ return modules_dir
|
|
+
|
|
+
|
|
def _reinstall_leapp_repository_hint():
|
|
"""
|
|
Convenience function for creating a detail for StopActorExecutionError with a hint to reinstall the
|
|
@@ -31,39 +71,81 @@ def _reinstall_leapp_repository_hint():
|
|
}
|
|
|
|
|
|
-def copy_dracut_modules(context, modules):
|
|
+def _copy_modules(context, modules, dst_dir, kind):
|
|
"""
|
|
- Copy dracut modules into the target userspace.
|
|
+ Copy modules of given kind to the specified destination directory.
|
|
+
|
|
+ Attempts to remove an cleanup by removing the existing destination
|
|
+ directory. If the directory does not exist, it is created anew. Then, for
|
|
+ each module message, it checks if the module has a module path specified. If
|
|
+ the module already exists in the destination directory, a debug message is
|
|
+ logged, and the operation is skipped. Otherwise, the module is copied to the
|
|
+ destination directory.
|
|
|
|
- If duplicated requirements to copy a dracut module are detected,
|
|
- log the debug msg and skip any try to copy a dracut module into the
|
|
- target userspace that already exists inside DRACTUR_DIR.
|
|
"""
|
|
+
|
|
try:
|
|
- context.remove_tree(DRACUT_DIR)
|
|
+ context.remove_tree(dst_dir)
|
|
except EnvironmentError:
|
|
pass
|
|
+
|
|
+ context.makedirs(dst_dir)
|
|
+
|
|
for module in modules:
|
|
if not module.module_path:
|
|
continue
|
|
- dst_path = os.path.join(DRACUT_DIR, os.path.basename(module.module_path))
|
|
+
|
|
+ dst_path = os.path.join(dst_dir, os.path.basename(module.module_path))
|
|
if os.path.exists(context.full_path(dst_path)):
|
|
- # we are safe to skip it as we now the module is from the same path
|
|
- # regarding the actor checking all initramfs tasks
|
|
api.current_logger().debug(
|
|
- 'The {name} dracut module has been already installed. Skipping.'
|
|
- .format(name=module.name))
|
|
+ 'The {name} {kind} module has been already installed. Skipping.'
|
|
+ .format(name=module.name, kind=kind))
|
|
continue
|
|
+
|
|
+ copy_fn = context.copytree_to
|
|
+ if os.path.isfile(module.module_path):
|
|
+ copy_fn = context.copy_to
|
|
+
|
|
try:
|
|
- context.copytree_to(module.module_path, dst_path)
|
|
+ api.current_logger().debug(
|
|
+ 'Copying {kind} module "{name}" to "{path}".'
|
|
+ .format(kind=kind, name=module.name, path=dst_path))
|
|
+
|
|
+ copy_fn(module.module_path, dst_path)
|
|
except shutil.Error as e:
|
|
- api.current_logger().error('Failed to copy dracut module "{name}" from "{source}" to "{target}"'.format(
|
|
- name=module.name, source=module.module_path, target=context.full_path(DRACUT_DIR)), exc_info=True)
|
|
+ api.current_logger().error(
|
|
+ 'Failed to copy {kind} module "{name}" from "{source}" to "{target}"'.format(
|
|
+ kind=kind, name=module.name, source=module.module_path, target=context.full_path(dst_dir)),
|
|
+ exc_info=True)
|
|
raise StopActorExecutionError(
|
|
- message='Failed to install dracut modules required in the initram. Error: {}'.format(str(e))
|
|
+ message='Failed to install {kind} modules required in the initram. Error: {error}'.format(
|
|
+ kind=kind, error=str(e))
|
|
)
|
|
|
|
|
|
+def copy_dracut_modules(context, modules):
|
|
+ """
|
|
+ Copy dracut modules into the target userspace.
|
|
+
|
|
+ If a module cannot be copied, an error message is logged, and a
|
|
+ StopActorExecutionError exception is raised.
|
|
+ """
|
|
+
|
|
+ _copy_modules(context, modules, DRACUT_DIR, 'dracut')
|
|
+
|
|
+
|
|
+def copy_kernel_modules(context, modules):
|
|
+ """
|
|
+ Copy kernel modules into the target userspace.
|
|
+
|
|
+ If a module cannot be copied, an error message is logged, and a
|
|
+ StopActorExecutionError exception is raised.
|
|
+ """
|
|
+
|
|
+ dst_dir = _get_target_kernel_modules_dir(context)
|
|
+ _copy_modules(context, modules, dst_dir, 'kernel')
|
|
+
|
|
+
|
|
@suppress_deprecation(UpgradeDracutModule)
|
|
def _get_dracut_modules():
|
|
return list(api.consume(UpgradeDracutModule))
|
|
@@ -153,30 +235,43 @@ def generate_initram_disk(context):
|
|
"""
|
|
Function to actually execute the init ramdisk creation.
|
|
|
|
- Includes handling of specified dracut modules from the host when needed.
|
|
- The check for the 'conflicting' dracut modules is in a separate actor.
|
|
+ Includes handling of specified dracut and kernel modules from the host when
|
|
+ needed. The check for the 'conflicting' modules is in a separate actor.
|
|
"""
|
|
env = {}
|
|
if get_target_major_version() == '9':
|
|
env = {'SYSTEMD_SECCOMP': '0'}
|
|
+
|
|
# TODO(pstodulk): Add possibility to add particular drivers
|
|
# Issue #645
|
|
- modules = _get_dracut_modules() # deprecated
|
|
+ modules = {
|
|
+ 'dracut': _get_dracut_modules(), # deprecated
|
|
+ 'kernel': [],
|
|
+ }
|
|
files = set()
|
|
for task in api.consume(UpgradeInitramfsTasks):
|
|
- modules.extend(task.include_dracut_modules)
|
|
+ modules['dracut'].extend(task.include_dracut_modules)
|
|
+ modules['kernel'].extend(task.include_kernel_modules)
|
|
files.update(task.include_files)
|
|
- copy_dracut_modules(context, modules)
|
|
+
|
|
+ copy_dracut_modules(context, modules['dracut'])
|
|
+ copy_kernel_modules(context, modules['kernel'])
|
|
+
|
|
# FIXME: issue #376
|
|
context.call([
|
|
'/bin/sh', '-c',
|
|
- 'LEAPP_ADD_DRACUT_MODULES="{modules}" LEAPP_KERNEL_ARCH={arch} '
|
|
+ 'LEAPP_KERNEL_VERSION={kernel_version} '
|
|
+ 'LEAPP_ADD_DRACUT_MODULES="{dracut_modules}" LEAPP_KERNEL_ARCH={arch} '
|
|
+ 'LEAPP_ADD_KERNEL_MODULES="{kernel_modules}" '
|
|
'LEAPP_DRACUT_INSTALL_FILES="{files}" {cmd}'.format(
|
|
- modules=','.join([mod.name for mod in modules]),
|
|
+ kernel_version=_get_target_kernel_version(context),
|
|
+ dracut_modules=','.join([mod.name for mod in modules['dracut']]),
|
|
+ kernel_modules=','.join([mod.name for mod in modules['kernel']]),
|
|
arch=api.current_actor().configuration.architecture,
|
|
files=' '.join(files),
|
|
cmd=os.path.join('/', INITRAM_GEN_SCRIPT_NAME))
|
|
], env=env)
|
|
+
|
|
copy_boot_files(context)
|
|
|
|
|
|
diff --git a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/tests/unit_test_upgradeinitramfsgenerator.py b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/tests/unit_test_upgradeinitramfsgenerator.py
|
|
index cd9d0546..a2f1c837 100644
|
|
--- a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/tests/unit_test_upgradeinitramfsgenerator.py
|
|
+++ b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/tests/unit_test_upgradeinitramfsgenerator.py
|
|
@@ -10,12 +10,12 @@ from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked,
|
|
from leapp.utils.deprecation import suppress_deprecation
|
|
|
|
from leapp.models import ( # isort:skip
|
|
- FIPSInfo,
|
|
RequiredUpgradeInitramPackages, # deprecated
|
|
UpgradeDracutModule, # deprecated
|
|
BootContent,
|
|
CopyFile,
|
|
DracutModule,
|
|
+ KernelModule,
|
|
TargetUserSpaceUpgradeTasks,
|
|
UpgradeInitramfsTasks,
|
|
)
|
|
@@ -42,30 +42,36 @@ def adjust_cwd():
|
|
os.chdir(previous_cwd)
|
|
|
|
|
|
+def _ensure_list(data):
|
|
+ return data if isinstance(data, list) else [data]
|
|
+
|
|
+
|
|
def gen_TUSU(packages, copy_files=None):
|
|
- if not isinstance(packages, list):
|
|
- packages = [packages]
|
|
+ packages = _ensure_list(packages)
|
|
+
|
|
if not copy_files:
|
|
copy_files = []
|
|
- elif not isinstance(copy_files, list):
|
|
- copy_files = [copy_files]
|
|
+ copy_files = _ensure_list(copy_files)
|
|
+
|
|
return TargetUserSpaceUpgradeTasks(install_rpms=packages, copy_files=copy_files)
|
|
|
|
|
|
@suppress_deprecation(RequiredUpgradeInitramPackages)
|
|
def gen_RUIP(packages):
|
|
- if not isinstance(packages, list):
|
|
- packages = [packages]
|
|
+ packages = _ensure_list(packages)
|
|
return RequiredUpgradeInitramPackages(packages=packages)
|
|
|
|
|
|
-def gen_UIT(modules, files):
|
|
- if not isinstance(modules, list):
|
|
- modules = [modules]
|
|
- if not isinstance(files, list):
|
|
- files = [files]
|
|
- dracut_modules = [DracutModule(name=i[0], module_path=i[1]) for i in modules]
|
|
- return UpgradeInitramfsTasks(include_files=files, include_dracut_modules=dracut_modules)
|
|
+def gen_UIT(dracut_modules, kernel_modules, files):
|
|
+ files = _ensure_list(files)
|
|
+
|
|
+ dracut_modules = [DracutModule(name=i[0], module_path=i[1]) for i in _ensure_list(dracut_modules)]
|
|
+ kernel_modules = [KernelModule(name=i[0], module_path=i[1]) for i in _ensure_list(kernel_modules)]
|
|
+
|
|
+ return UpgradeInitramfsTasks(include_files=files,
|
|
+ include_dracut_modules=dracut_modules,
|
|
+ include_kernel_modules=kernel_modules,
|
|
+ )
|
|
|
|
|
|
@suppress_deprecation(UpgradeDracutModule)
|
|
@@ -81,6 +87,7 @@ class MockedContext(object):
|
|
self.called_copytree_from = []
|
|
self.called_copy_to = []
|
|
self.called_call = []
|
|
+ self.called_makedirs = []
|
|
self.content = set()
|
|
self.base_dir = "/base/dir"
|
|
"""
|
|
@@ -108,6 +115,9 @@ class MockedContext(object):
|
|
self.called_copy_to.append((src, dst))
|
|
self.content.add(dst)
|
|
|
|
+ def makedirs(self, path):
|
|
+ self.called_makedirs.append(path)
|
|
+
|
|
def remove_tree(self, path):
|
|
# make list for iteration as change of the set is expected during the
|
|
# iteration, which could lead to runtime error
|
|
@@ -240,38 +250,50 @@ def test_prepare_userspace_for_initram(monkeypatch, adjust_cwd, input_msgs, pkgs
|
|
assert _sort_files(upgradeinitramfsgenerator._copy_files.args[1]) == _files
|
|
|
|
|
|
-@pytest.mark.parametrize('input_msgs,modules', [
|
|
+@pytest.mark.parametrize('input_msgs,dracut_modules,kernel_modules', [
|
|
# test dracut modules with UpgradeDracutModule(s) - orig functionality
|
|
- (gen_UDM_list(MODULES[0]), MODULES[0]),
|
|
- (gen_UDM_list(MODULES), MODULES),
|
|
+ (gen_UDM_list(MODULES[0]), MODULES[0], []),
|
|
+ (gen_UDM_list(MODULES), MODULES, []),
|
|
|
|
# test dracut modules with UpgradeInitramfsTasks - new functionality
|
|
- ([gen_UIT(MODULES[0], [])], MODULES[0]),
|
|
- ([gen_UIT(MODULES, [])], MODULES),
|
|
+ ([gen_UIT(MODULES[0], MODULES[0], [])], MODULES[0], MODULES[0]),
|
|
+ ([gen_UIT(MODULES, MODULES, [])], MODULES, MODULES),
|
|
|
|
# test dracut modules with old and new models
|
|
- (gen_UDM_list(MODULES[1]) + [gen_UIT(MODULES[2], [])], MODULES[1:3]),
|
|
- (gen_UDM_list(MODULES[2:]) + [gen_UIT(MODULES[0:2], [])], MODULES),
|
|
+ (gen_UDM_list(MODULES[1]) + [gen_UIT(MODULES[2], [], [])], MODULES[1:3], []),
|
|
+ (gen_UDM_list(MODULES[2:]) + [gen_UIT(MODULES[0:2], [], [])], MODULES, []),
|
|
+ (gen_UDM_list(MODULES[1]) + [gen_UIT([], MODULES[2], [])], MODULES[1], MODULES[2]),
|
|
+ (gen_UDM_list(MODULES[2:]) + [gen_UIT([], MODULES[0:2], [])], MODULES[2:], MODULES[0:2]),
|
|
|
|
# TODO(pstodulk): test include files missing (relates #376)
|
|
])
|
|
-def test_generate_initram_disk(monkeypatch, input_msgs, modules):
|
|
+def test_generate_initram_disk(monkeypatch, input_msgs, dracut_modules, kernel_modules):
|
|
context = MockedContext()
|
|
curr_actor = CurrentActorMocked(msgs=input_msgs, arch=architecture.ARCH_X86_64)
|
|
monkeypatch.setattr(upgradeinitramfsgenerator.api, 'current_actor', curr_actor)
|
|
monkeypatch.setattr(upgradeinitramfsgenerator, 'copy_dracut_modules', MockedCopyArgs())
|
|
+ monkeypatch.setattr(upgradeinitramfsgenerator, '_get_target_kernel_version', lambda _: '')
|
|
+ monkeypatch.setattr(upgradeinitramfsgenerator, 'copy_kernel_modules', MockedCopyArgs())
|
|
monkeypatch.setattr(upgradeinitramfsgenerator, 'copy_boot_files', lambda dummy: None)
|
|
upgradeinitramfsgenerator.generate_initram_disk(context)
|
|
|
|
# test now just that all modules have been passed for copying - so we know
|
|
# all modules have been consumed
|
|
- detected_modules = set()
|
|
- _modules = set(modules) if isinstance(modules, list) else set([modules])
|
|
+ detected_dracut_modules = set()
|
|
+ _dracut_modules = set(dracut_modules) if isinstance(dracut_modules, list) else set([dracut_modules])
|
|
for dracut_module in upgradeinitramfsgenerator.copy_dracut_modules.args[1]:
|
|
module = (dracut_module.name, dracut_module.module_path)
|
|
- assert module in _modules
|
|
- detected_modules.add(module)
|
|
- assert detected_modules == _modules
|
|
+ assert module in _dracut_modules
|
|
+ detected_dracut_modules.add(module)
|
|
+ assert detected_dracut_modules == _dracut_modules
|
|
+
|
|
+ detected_kernel_modules = set()
|
|
+ _kernel_modules = set(kernel_modules) if isinstance(kernel_modules, list) else set([kernel_modules])
|
|
+ for kernel_module in upgradeinitramfsgenerator.copy_kernel_modules.args[1]:
|
|
+ module = (kernel_module.name, kernel_module.module_path)
|
|
+ assert module in _kernel_modules
|
|
+ detected_kernel_modules.add(module)
|
|
+ assert detected_kernel_modules == _kernel_modules
|
|
|
|
# TODO(pstodulk): this test is not created properly, as context.call check
|
|
# is skipped completely. Testing will more convenient with fixed #376
|
|
@@ -300,7 +322,8 @@ def test_copy_dracut_modules_rmtree_ignore(monkeypatch):
|
|
assert context.content
|
|
|
|
|
|
-def test_copy_dracut_modules_fail(monkeypatch):
|
|
+@pytest.mark.parametrize('kind', ['dracut', 'kernel'])
|
|
+def test_copy_modules_fail(monkeypatch, kind):
|
|
context = MockedContext()
|
|
|
|
def copytree_to_error(src, dst):
|
|
@@ -313,15 +336,30 @@ def test_copy_dracut_modules_fail(monkeypatch):
|
|
context.copytree_to = copytree_to_error
|
|
monkeypatch.setattr(os.path, 'exists', mock_context_path_exists)
|
|
monkeypatch.setattr(upgradeinitramfsgenerator.api, 'current_logger', MockedLogger())
|
|
- dmodules = [DracutModule(name='foo', module_path='/path/foo')]
|
|
+ monkeypatch.setattr(upgradeinitramfsgenerator, '_get_target_kernel_modules_dir', lambda _: '/kernel_modules')
|
|
+
|
|
+ module_class = None
|
|
+ copy_fn = None
|
|
+ if kind == 'dracut':
|
|
+ module_class = DracutModule
|
|
+ copy_fn = upgradeinitramfsgenerator.copy_dracut_modules
|
|
+ dst_path = 'dracut'
|
|
+ elif kind == 'kernel':
|
|
+ module_class = KernelModule
|
|
+ copy_fn = upgradeinitramfsgenerator.copy_kernel_modules
|
|
+ dst_path = 'kernel_modules'
|
|
+
|
|
+ modules = [module_class(name='foo', module_path='/path/foo')]
|
|
with pytest.raises(StopActorExecutionError) as err:
|
|
- upgradeinitramfsgenerator.copy_dracut_modules(context, dmodules)
|
|
- assert err.value.message.startswith('Failed to install dracut modules')
|
|
- expected_err_log = 'Failed to copy dracut module "foo" from "/path/foo" to "/base/dir/dracut"'
|
|
+ copy_fn(context, modules)
|
|
+ assert err.value.message.startswith('Failed to install {kind} modules'.format(kind=kind))
|
|
+ expected_err_log = 'Failed to copy {kind} module "foo" from "/path/foo" to "/base/dir/{dst_path}"'.format(
|
|
+ kind=kind, dst_path=dst_path)
|
|
assert expected_err_log in upgradeinitramfsgenerator.api.current_logger.errmsg
|
|
|
|
|
|
-def test_copy_dracut_modules_duplicate_skip(monkeypatch):
|
|
+@pytest.mark.parametrize('kind', ['dracut', 'kernel'])
|
|
+def test_copy_modules_duplicate_skip(monkeypatch, kind):
|
|
context = MockedContext()
|
|
|
|
def mock_context_path_exists(path):
|
|
@@ -330,10 +368,23 @@ def test_copy_dracut_modules_duplicate_skip(monkeypatch):
|
|
|
|
monkeypatch.setattr(os.path, 'exists', mock_context_path_exists)
|
|
monkeypatch.setattr(upgradeinitramfsgenerator.api, 'current_logger', MockedLogger())
|
|
- dm = DracutModule(name='foo', module_path='/path/foo')
|
|
- dmodules = [dm, dm]
|
|
- debugmsg = 'The foo dracut module has been already installed. Skipping.'
|
|
- upgradeinitramfsgenerator.copy_dracut_modules(context, dmodules)
|
|
+ monkeypatch.setattr(upgradeinitramfsgenerator, '_get_target_kernel_modules_dir', lambda _: '/kernel_modules')
|
|
+
|
|
+ module_class = None
|
|
+ copy_fn = None
|
|
+ if kind == 'dracut':
|
|
+ module_class = DracutModule
|
|
+ copy_fn = upgradeinitramfsgenerator.copy_dracut_modules
|
|
+ elif kind == 'kernel':
|
|
+ module_class = KernelModule
|
|
+ copy_fn = upgradeinitramfsgenerator.copy_kernel_modules
|
|
+
|
|
+ module = module_class(name='foo', module_path='/path/foo')
|
|
+ modules = [module, module]
|
|
+
|
|
+ copy_fn(context, modules)
|
|
+
|
|
+ debugmsg = 'The foo {kind} module has been already installed. Skipping.'.format(kind=kind)
|
|
assert context.content
|
|
assert len(context.called_copy_to) == 1
|
|
assert debugmsg in upgradeinitramfsgenerator.api.current_logger.dbgmsg
|
|
diff --git a/repos/system_upgrade/common/models/initramfs.py b/repos/system_upgrade/common/models/initramfs.py
|
|
index a5d1416e..03b71125 100644
|
|
--- a/repos/system_upgrade/common/models/initramfs.py
|
|
+++ b/repos/system_upgrade/common/models/initramfs.py
|
|
@@ -40,6 +40,46 @@ class DracutModule(Model):
|
|
"""
|
|
|
|
|
|
+class KernelModule(Model):
|
|
+ """
|
|
+ Specify a kernel module that should be included into the initramfs
|
|
+
|
|
+ The specified kernel module has to be compatible with the target system.
|
|
+
|
|
+ See the description of UpgradeInitramfsTasks and TargetInitramfsTasks
|
|
+ for more information about the role of initramfs in the in-place upgrade
|
|
+ process.
|
|
+ """
|
|
+ topic = BootPrepTopic
|
|
+
|
|
+ name = fields.String()
|
|
+ """
|
|
+ The kernel module that should be added (--add-drivers option of dracut)
|
|
+ when a initramfs is built. The possible options are
|
|
+
|
|
+ 1. ``=<kernel subdir>[/<kernel subdir>...]`` like ``=drivers/hid``
|
|
+ 2. ``<module name>``
|
|
+ """
|
|
+
|
|
+ module_path = fields.Nullable(fields.String(default=None))
|
|
+ """
|
|
+ module_path specifies kernel modules that are supposed to be copied
|
|
+
|
|
+ If the path is not set, the given name will just be activated. IOW,
|
|
+ if the kernel module is stored outside the /usr/lib/modules/$(uname -r)/
|
|
+ directory, set the absolute path to it, so leapp will manage it during
|
|
+ the upgrade to ensure the module will be added into the initramfs.
|
|
+
|
|
+ The module has to be stored on the local storage mounted in a persistent
|
|
+ fashion (/etc/fstab entry). In such a case, it is recommended to store it
|
|
+ into the 'files' directory of an actor generating this object.
|
|
+
|
|
+ Note: It's expected to set the full path from the host POV. In case
|
|
+ of actions inside containers, the module is still copied from the HOST
|
|
+ into the container workspace.
|
|
+ """
|
|
+
|
|
+
|
|
class UpgradeInitramfsTasks(Model):
|
|
"""
|
|
Influence generating of the (leapp) upgrade initramfs
|
|
@@ -73,6 +113,13 @@ class UpgradeInitramfsTasks(Model):
|
|
See the DracutModule model for more information.
|
|
"""
|
|
|
|
+ include_kernel_modules = fields.List(fields.Model(KernelModule), default=[])
|
|
+ """
|
|
+ List of kernel modules that should be installed in the initramfs.
|
|
+
|
|
+ See the KernelModule model for more information.
|
|
+ """
|
|
+
|
|
|
|
class TargetInitramfsTasks(UpgradeInitramfsTasks):
|
|
"""
|
|
--
|
|
2.41.0
|
|
|