Skip to content
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

Refactor handling of kernel information #1107

Merged
merged 1 commit into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from leapp.actors import Actor
from leapp.libraries.actor import forcedefaultboot
from leapp.models import InstalledTargetKernelVersion
from leapp.models import InstalledTargetKernelInfo
from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag


Expand All @@ -14,7 +14,7 @@ class ForceDefaultBootToTargetKernelVersion(Actor):
"""

name = 'force_default_boot_to_target_kernel_version'
consumes = (InstalledTargetKernelVersion,)
consumes = (InstalledTargetKernelInfo,)
produces = ()
tags = (FinalizationPhaseTag, IPUWorkflowTag)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,33 @@
import os
from collections import namedtuple

from leapp.libraries import stdlib
from leapp.libraries.common.config import architecture
from leapp.libraries.stdlib import api, config
from leapp.models import InstalledTargetKernelVersion

KernelInfo = namedtuple('KernelInfo', ('kernel_path', 'initrd_path'))


def get_kernel_info(message):
kernel_name = 'vmlinuz-{}'.format(message.version)
initrd_name = 'initramfs-{}.img'.format(message.version)
kernel_path = os.path.join('/boot', kernel_name)
initrd_path = os.path.join('/boot', initrd_name)

target_version_bootable = True
if not os.path.exists(kernel_path):
target_version_bootable = False
api.current_logger().warning('Mandatory kernel %s does not exist', kernel_path)
if not os.path.exists(initrd_path):
target_version_bootable = False
api.current_logger().warning('Mandatory initrd %s does not exist', initrd_path)

if target_version_bootable:
return KernelInfo(kernel_path=kernel_path, initrd_path=initrd_path)

api.current_logger().warning('Skipping check due to missing mandatory files')
return None
from leapp.models import InstalledTargetKernelInfo


def update_default_kernel(kernel_info):
try:
stdlib.run(['grubby', '--info', kernel_info.kernel_path])
stdlib.run(['grubby', '--info', kernel_info.kernel_img_path])
except stdlib.CalledProcessError:
api.current_logger().error('Expected kernel %s to be installed at the boot loader but cannot be found.',
kernel_info.kernel_path)
kernel_info.kernel_img_path)
except OSError:
api.current_logger().error('Could not check for kernel existence in boot loader. Is grubby installed?')
else:
try:
stdlib.run(['grubby', '--set-default', kernel_info.kernel_path])
stdlib.run(['grubby', '--set-default', kernel_info.kernel_img_path])
if architecture.matches_architecture(architecture.ARCH_S390X):
# on s390x we need to call zipl explicitly because of issue in grubby,
# otherwise the new boot entry will not be set as default
# See https://bugzilla.redhat.com/show_bug.cgi?id=1764306
stdlib.run(['/usr/sbin/zipl'])
except (OSError, stdlib.CalledProcessError):
api.current_logger().error('Failed to set default kernel to: %s', kernel_info.kernel_path, exc_info=True)
api.current_logger().error('Failed to set default kernel to: %s',
kernel_info.kernel_img_path, exc_info=True)


def process():
if (config.is_debug and not
architecture.matches_architecture(architecture.ARCH_S390X)): # pylint: disable=using-constant-test
is_system_s390x = architecture.matches_architecture(architecture.ARCH_S390X)
if config.is_debug and not is_system_s390x: # pylint: disable=using-constant-test
try:
# the following command prints output of grubenv for debugging purposes and is repeated after setting
# default kernel so we can be sure we have the right saved entry
Expand All @@ -65,12 +40,18 @@ def process():
stdlib.run(['grub2-editenv', 'list'])
except stdlib.CalledProcessError:
api.current_logger().error('Failed to execute "grub2-editenv list" command')
message = next(api.consume(InstalledTargetKernelVersion), None)
if not message:

kernel_info = next(api.consume(InstalledTargetKernelInfo), None)
if not kernel_info:
api.current_logger().warning(('Skipped - Forcing checking and setting default boot entry to target kernel'
' version due to missing message'))
return

if not kernel_info.kernel_img_path: # Should be always set
api.current_logger().warning(('Skipping forcing of default boot entry - target kernel info '
'does not contain a kernel image path.'))
return

try:
current_default_kernel = stdlib.run(['grubby', '--default-kernel'])['stdout'].strip()
except (OSError, stdlib.CalledProcessError):
Expand All @@ -84,17 +65,12 @@ def process():
api.current_logger().warning('Failed to query grubby for default {}'.format(type_), exc_info=True)
return

kernel_info = get_kernel_info(message)
if not kernel_info:
return

if current_default_kernel != kernel_info.kernel_path:
if current_default_kernel != kernel_info.kernel_img_path:
api.current_logger().warning(('Current default boot entry not target kernel version: Current default: %s.'
'Forcing default kernel to %s'),
current_default_kernel, kernel_info.kernel_path)
current_default_kernel, kernel_info.kernel_img_path)
update_default_kernel(kernel_info)
if (config.is_debug and not
architecture.matches_architecture(architecture.ARCH_S390X)): # pylint: disable=using-constant-test
if config.is_debug and not is_system_s390x: # pylint: disable=using-constant-test
try:
stdlib.run(['grub2-editenv', 'list'])
except stdlib.CalledProcessError:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from leapp.libraries.common.config import architecture
from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked
from leapp.libraries.stdlib import api
from leapp.models import InstalledTargetKernelVersion
from leapp.models import InstalledTargetKernelInfo

Expected = namedtuple(
'Expected', (
Expand All @@ -19,15 +19,15 @@

Case = namedtuple(
'Case',
('initrd_exists',
'kernel_exists',
('kernel_exists',
'entry_default',
'entry_exists',
'message_available',
'arch_s390x'
)
)

TARGET_KERNEL_NEVRA = 'kernel-core-1.2.3.4.el8.x86_64'
TARGET_KERNEL_VERSION = '1.2.3.4.el8.x86_64'
TARGET_KERNEL_TITLE = 'Red Hat Enterprise Linux ({}) 8.1 (Ootpa)'.format(TARGET_KERNEL_VERSION)
TARGET_KERNEL_PATH = '/boot/vmlinuz-{}'.format(TARGET_KERNEL_VERSION)
Expand All @@ -37,48 +37,27 @@
OLD_KERNEL_TITLE = 'Red Hat Enterprise Linux ({}) 7.6 (Maipo)'.format(OLD_KERNEL_VERSION)
OLD_KERNEL_PATH = '/boot/vmlinuz-{}'.format(OLD_KERNEL_VERSION)


CASES = (
(Case(initrd_exists=True, kernel_exists=True, entry_default=True, entry_exists=True, message_available=True,
arch_s390x=False),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=False, kernel_exists=True, entry_default=False, entry_exists=True, message_available=True,
arch_s390x=False),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=True, kernel_exists=False, entry_default=False, entry_exists=True, message_available=True,
arch_s390x=False),
(Case(kernel_exists=True, entry_default=True, entry_exists=True, message_available=True, arch_s390x=False),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=False, kernel_exists=False, entry_default=False, entry_exists=True, message_available=True,
arch_s390x=False),
(Case(kernel_exists=False, entry_default=False, entry_exists=True, message_available=True, arch_s390x=False),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=True, kernel_exists=True, entry_default=False, entry_exists=True, message_available=False,
arch_s390x=False),
(Case(kernel_exists=True, entry_default=False, entry_exists=True, message_available=False, arch_s390x=False),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=True, kernel_exists=True, entry_default=False, entry_exists=False, message_available=False,
arch_s390x=False),
(Case(kernel_exists=True, entry_default=False, entry_exists=False, message_available=False, arch_s390x=False),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=True, kernel_exists=True, entry_default=False, entry_exists=True, message_available=True,
arch_s390x=False),
(Case(kernel_exists=True, entry_default=False, entry_exists=True, message_available=True, arch_s390x=False),
Expected(grubby_setdefault=True, zipl_called=False)),
(Case(initrd_exists=True, kernel_exists=True, entry_default=True, entry_exists=True, message_available=True,
arch_s390x=True),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=False, kernel_exists=True, entry_default=False, entry_exists=True, message_available=True,
arch_s390x=True),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=True, kernel_exists=False, entry_default=False, entry_exists=True, message_available=True,
arch_s390x=True),
(Case(kernel_exists=True, entry_default=True, entry_exists=True, message_available=True, arch_s390x=True),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=False, kernel_exists=False, entry_default=False, entry_exists=True, message_available=True,
arch_s390x=True),
(Case(kernel_exists=False, entry_default=False, entry_exists=True, message_available=True, arch_s390x=True),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=True, kernel_exists=True, entry_default=False, entry_exists=True, message_available=False,
arch_s390x=True),
(Case(kernel_exists=True, entry_default=False, entry_exists=True, message_available=False, arch_s390x=True),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=True, kernel_exists=True, entry_default=False, entry_exists=False, message_available=False,
arch_s390x=True),
(Case(kernel_exists=True, entry_default=False, entry_exists=False, message_available=False, arch_s390x=True),
Expected(grubby_setdefault=False, zipl_called=False)),
(Case(initrd_exists=True, kernel_exists=True, entry_default=False, entry_exists=True, message_available=True,
arch_s390x=True),
(Case(kernel_exists=True, entry_default=False, entry_exists=True, message_available=True, arch_s390x=True),
Expected(grubby_setdefault=True, zipl_called=True))
)

Expand Down Expand Up @@ -143,7 +122,12 @@ def grubby_set_default(self, cmd):
def mocked_consume(case):
def impl(*args):
if case.message_available:
return iter((InstalledTargetKernelVersion(version=TARGET_KERNEL_VERSION),))
kernel_img_path = TARGET_KERNEL_PATH if case.kernel_exists else ''
msg = InstalledTargetKernelInfo(pkg_nevra=TARGET_KERNEL_NEVRA,
kernel_img_path=kernel_img_path,
uname_r='',
initramfs_path=TARGET_INITRD_PATH)
return iter((msg,))
return iter(())
return impl

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
from leapp.actors import Actor
from leapp.libraries.actor import targetinitramfsgenerator
from leapp.models import (
InitrdIncludes, # deprecated
InstalledTargetKernelVersion,
TargetInitramfsTasks
)
from leapp.models import InitrdIncludes # deprecated
from leapp.models import InstalledTargetKernelInfo, TargetInitramfsTasks
from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag
from leapp.utils.deprecation import suppress_deprecation

Expand All @@ -16,7 +13,7 @@ class TargetInitramfsGenerator(Actor):
"""

name = 'target_initramfs_generator'
consumes = (InitrdIncludes, InstalledTargetKernelVersion, TargetInitramfsTasks)
consumes = (InitrdIncludes, InstalledTargetKernelInfo, TargetInitramfsTasks)
produces = ()
tags = (FinalizationPhaseTag, IPUWorkflowTag)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from leapp.exceptions import StopActorExecutionError
from leapp.libraries.stdlib import api, CalledProcessError, run
from leapp.models import InitrdIncludes # deprecated
from leapp.models import InstalledTargetKernelVersion, TargetInitramfsTasks
from leapp.models import InstalledTargetKernelInfo, TargetInitramfsTasks
from leapp.utils.deprecation import suppress_deprecation

DRACUT_DIR = '/usr/lib/dracut/modules.d/'
Expand Down Expand Up @@ -105,29 +105,29 @@ def process():
'No additional files or modules required to add into the target initramfs.')
return

target_kernel = next(api.consume(InstalledTargetKernelVersion), None)
if not target_kernel:
target_kernel_info = next(api.consume(InstalledTargetKernelInfo), None)
if not target_kernel_info:
raise StopActorExecutionError(
'Cannot get version of the installed RHEL-8 kernel',
details={'Problem': 'Did not receive a message with installed RHEL-8 kernel version'
' (InstalledTargetKernelVersion)'})

_copy_modules(modules['dracut'], DRACUT_DIR, 'dracut')
_copy_modules(modules['kernel'], _get_target_kernel_modules_dir(target_kernel.version), 'kernel')
_copy_modules(modules['kernel'], _get_target_kernel_modules_dir(target_kernel_info.uname_r), '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'])
run(['depmod', target_kernel_info.uname_r, '-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)
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]
cmd = ['dracut', '-f', '--kver', target_kernel_info.uname_r]
if files:
cmd += ['--install', '{}'.format(' '.join(files))]
if modules['dracut']:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from leapp.models import ( # isort:skip
InitrdIncludes, # deprecated
DracutModule, KernelModule, InstalledTargetKernelVersion, TargetInitramfsTasks)
DracutModule, KernelModule, InstalledTargetKernelInfo, TargetInitramfsTasks)

FILES = ['/file1', '/file2', '/dir/subdir/subsubdir/file3', '/file4', '/file5']
MODULES = [
Expand Down Expand Up @@ -110,12 +110,20 @@ def test_no_kernel_version(monkeypatch, msgs):
assert not run_mocked.called


def mk_kernel_info(kernel_ver):
kernel_info = InstalledTargetKernelInfo(pkg_nevra='nevra',
kernel_img_path='vmlinuz',
uname_r=kernel_ver,
initramfs_path='initramfs')
return kernel_info


@pytest.mark.parametrize('msgs', TEST_CASES)
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)]))
CurrentActorMocked(msgs=msgs + [mk_kernel_info(KERNEL_VERSION)]))
monkeypatch.setattr(targetinitramfsgenerator, 'run', run_mocked)
# FIXME
monkeypatch.setattr(targetinitramfsgenerator, '_copy_modules', lambda *_: None)
Expand Down Expand Up @@ -185,7 +193,7 @@ def test_dracut_fail(monkeypatch, msgs):
([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)]
_msgs = msgs + [mk_kernel_info(KERNEL_VERSION)]
run_mocked = RunMocked()
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=_msgs))
monkeypatch.setattr(targetinitramfsgenerator, 'run', run_mocked)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def produce_ipu_config(actor):
flavour = os.environ.get('LEAPP_UPGRADE_PATH_FLAVOUR')
target_version = os.environ.get('LEAPP_UPGRADE_PATH_TARGET_RELEASE')
os_release = get_os_release('/etc/os-release')

actor.produce(IPUConfig(
leapp_env_vars=get_env_vars(),
os_release=os_release,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from leapp.actors import Actor
from leapp.libraries.actor import checkinstalledkernels
from leapp.models import InstalledRedHatSignedRPM
from leapp.models import InstalledRedHatSignedRPM, KernelInfo
from leapp.reporting import Report
from leapp.tags import ChecksPhaseTag, IPUWorkflowTag

Expand Down Expand Up @@ -30,7 +30,7 @@ class CheckInstalledKernels(Actor):
"""

name = 'check_installed_kernels'
consumes = (InstalledRedHatSignedRPM,)
consumes = (InstalledRedHatSignedRPM, KernelInfo)
produces = (Report,)
tags = (IPUWorkflowTag, ChecksPhaseTag)

Expand Down
Loading
Loading