Skip to content

Commit

Permalink
Enable upgrades on AWS and Azure
Browse files Browse the repository at this point in the history
- add actor to check for particular cloud package (provided from
special repository) to determine whether we are on public
cloud.

- disable DNF plugin with a message

- use the resolved mirrorlist on AWS

- copy RHUI data from special Leapp package when on
  public cloud and running without RHSM

- add hybrid (BIOS, UEFI) image grubby workaround
  • Loading branch information
Rezney committed Aug 27, 2020
1 parent 510f0e7 commit e32ab33
Show file tree
Hide file tree
Showing 23 changed files with 689 additions and 158 deletions.
21 changes: 18 additions & 3 deletions repos/system_upgrade/el7toel8/actors/addupgradebootentry/actor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import os

from leapp.actors import Actor
from leapp.libraries.actor.addupgradebootentry import add_boot_entry, fix_grub_config_error
from leapp.models import BootContent, GrubConfigError
from leapp.models import BootContent, GrubConfigError, FirmwareFacts
from leapp.exceptions import StopActorExecutionError
from leapp.tags import InterimPreparationPhaseTag, IPUWorkflowTag


Expand All @@ -12,7 +15,7 @@ class AddUpgradeBootEntry(Actor):
"""

name = 'add_upgrade_boot_entry'
consumes = (BootContent, GrubConfigError)
consumes = (BootContent, GrubConfigError, FirmwareFacts)
produces = ()
tags = (IPUWorkflowTag, InterimPreparationPhaseTag)

Expand All @@ -21,4 +24,16 @@ def process(self):
if grub_config_error_detected:
fix_grub_config_error('/etc/default/grub')

add_boot_entry()
configs = None
ff = next(self.consume(FirmwareFacts), None)
if not ff:
raise StopActorExecutionError(
'Could not identify system firmware',
details={'details': 'Actor did not receive FirmwareFacts message.'}
)

# related to issue with hybrid BIOS and UEFI images
# https://bugzilla.redhat.com/show_bug.cgi?id=1667028
if ff.firmware == 'bios' and os.path.ismount('/boot/efi'):
configs = ['/boot/grub2/grub.cfg', '/boot/efi/EFI/redhat/grub.cfg']
add_boot_entry(configs)
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@
from leapp.models import BootContent


def add_boot_entry():
def add_boot_entry(configs=None):
debug = 'debug' if os.getenv('LEAPP_DEBUG', '0') == '1' else ''

kernel_dst_path, initram_dst_path = get_boot_file_paths()
try:
_remove_old_upgrade_boot_entry(kernel_dst_path)
run([
_remove_old_upgrade_boot_entry(kernel_dst_path, configs=configs)
cmd = [
'/usr/sbin/grubby',
'--add-kernel', '{0}'.format(kernel_dst_path),
'--initrd', '{0}'.format(initram_dst_path),
'--title', 'RHEL-Upgrade-Initramfs',
'--copy-default',
'--make-default',
'--args', '{DEBUG} enforcing=0 rd.plymouth=0 plymouth.enable=0'.format(DEBUG=debug)
])
]
if configs:
for config in configs:
run(cmd + ['-c', config])
else:
run(cmd)

if architecture.matches_architecture(architecture.ARCH_S390X):
# on s390x we need to call zipl explicitly because of issue in grubby,
Expand All @@ -35,17 +40,22 @@ def add_boot_entry():
)


def _remove_old_upgrade_boot_entry(kernel_dst_path):
def _remove_old_upgrade_boot_entry(kernel_dst_path, configs=None):
"""
Remove entry referring to the upgrade kernel.
We have to ensure there are no duplicit boot entries. Main reason is crash
of zipl when duplicit entries exist.
"""
run([
cmd = [
'/usr/sbin/grubby',
'--remove-kernel', '{0}'.format(kernel_dst_path)
])
]
if configs:
for config in configs:
run(cmd + ['-c', config])
else:
run(cmd)


def get_boot_file_paths():
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import os
from collections import namedtuple

import pytest

from leapp.exceptions import StopActorExecutionError
from leapp.libraries.actor import addupgradebootentry
from leapp.libraries.common.config import architecture
from leapp.libraries.common.config.architecture import ARCH_X86_64, ARCH_S390X
from leapp.libraries.common.testutils import CurrentActorMocked
from leapp.libraries.stdlib import api
from leapp.models import BootContent
Expand All @@ -28,61 +29,72 @@ def __call__(self, filename, content):
self.content = content


def test_add_boot_entry_non_s390x(monkeypatch):
CONFIGS = ['/boot/grub2/grub.cfg', '/boot/efi/EFI/redhat/grub.cfg']

RunArgs = namedtuple('RunArgs', 'args_remove args_add args_zipl args_len')

run_args_remove = [
'/usr/sbin/grubby',
'--remove-kernel', '/abc'
]

run_args_add = [
'/usr/sbin/grubby',
'--add-kernel', '/abc',
'--initrd', '/def',
'--title', 'RHEL-Upgrade-Initramfs',
'--copy-default',
'--make-default',
'--args',
'debug enforcing=0 rd.plymouth=0 plymouth.enable=0'
]

run_args_zipl = ['/usr/sbin/zipl']


@pytest.mark.parametrize('run_args, arch', [
# non s390x
(RunArgs(run_args_remove, run_args_add, None, 2), ARCH_X86_64),
# s390x
(RunArgs(run_args_remove, run_args_add, run_args_zipl, 3), ARCH_S390X),
# config file specified
(RunArgs(run_args_remove, run_args_add, None, 2), ARCH_X86_64),
])
def test_add_boot_entry(monkeypatch, run_args, arch):
def get_boot_file_paths_mocked():
return '/abc', '/def'

monkeypatch.setattr(addupgradebootentry, 'get_boot_file_paths', get_boot_file_paths_mocked)
monkeypatch.setenv('LEAPP_DEBUG', '1')
monkeypatch.setattr(addupgradebootentry, 'run', run_mocked())
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(architecture.ARCH_X86_64))
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch))

addupgradebootentry.add_boot_entry()

assert len(addupgradebootentry.run.args) == 2
assert addupgradebootentry.run.args[0] == [
'/usr/sbin/grubby',
'--remove-kernel', '/abc'
]
assert addupgradebootentry.run.args[1] == [
'/usr/sbin/grubby',
'--add-kernel', '/abc',
'--initrd', '/def',
'--title', 'RHEL-Upgrade-Initramfs',
'--copy-default',
'--make-default',
'--args',
'debug enforcing=0 rd.plymouth=0 plymouth.enable=0'
]
assert len(addupgradebootentry.run.args) == run_args.args_len
assert addupgradebootentry.run.args[0] == run_args.args_remove
assert addupgradebootentry.run.args[1] == run_args.args_add

if run_args.args_zipl:
assert addupgradebootentry.run.args[2] == run_args.args_zipl


def test_add_boot_entry_s390x(monkeypatch):
def test_add_boot_entry_configs(monkeypatch):
def get_boot_file_paths_mocked():
return '/abc', '/def'

monkeypatch.setattr(addupgradebootentry, 'get_boot_file_paths', get_boot_file_paths_mocked)
monkeypatch.setenv('LEAPP_DEBUG', '1')
monkeypatch.setattr(addupgradebootentry, 'run', run_mocked())
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(architecture.ARCH_S390X))
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(ARCH_X86_64))

addupgradebootentry.add_boot_entry()
addupgradebootentry.add_boot_entry(CONFIGS)

assert len(addupgradebootentry.run.args) == 3
assert addupgradebootentry.run.args[0] == [
'/usr/sbin/grubby',
'--remove-kernel', '/abc'
]
assert addupgradebootentry.run.args[1] == [
'/usr/sbin/grubby',
'--add-kernel', '/abc',
'--initrd', '/def',
'--title', 'RHEL-Upgrade-Initramfs',
'--copy-default',
'--make-default',
'--args',
'debug enforcing=0 rd.plymouth=0 plymouth.enable=0'
]
assert addupgradebootentry.run.args[2] == ['/usr/sbin/zipl']
assert len(addupgradebootentry.run.args) == 4
assert addupgradebootentry.run.args[0] == run_args_remove + ['-c', CONFIGS[0]]
assert addupgradebootentry.run.args[1] == run_args_remove + ['-c', CONFIGS[1]]
assert addupgradebootentry.run.args[2] == run_args_add + ['-c', CONFIGS[0]]
assert addupgradebootentry.run.args[3] == run_args_add + ['-c', CONFIGS[1]]


def test_get_boot_file_paths(monkeypatch):
Expand Down
73 changes: 73 additions & 0 deletions repos/system_upgrade/el7toel8/actors/checkrhui/actor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from leapp.actors import Actor
from leapp.libraries.common.rpms import has_package
from leapp.models import (
DNFPluginTask,
InstalledRPM,
KernelCmdlineArg,
RHUIInfo,
RequiredTargetUserspacePackages,
RpmTransactionTasks,
)
from leapp.reporting import Report, create_report
from leapp import reporting
from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
from leapp.libraries.common import rhsm, rhui


class CheckRHUI(Actor):
"""
Check if system is using RHUI infrastructure (on public cloud) and send messages to
provide additional data needed for upgrade.
"""

name = 'checkrhui'
consumes = (InstalledRPM)
produces = (
KernelCmdlineArg,
RHUIInfo,
RequiredTargetUserspacePackages,
Report, DNFPluginTask,
RpmTransactionTasks,
)
tags = (ChecksPhaseTag, IPUWorkflowTag)

def process(self):
for provider, info in rhui.RHUI_CLOUD_MAP.items():
if has_package(InstalledRPM, info['el7_pkg']):
if not rhsm.skip_rhsm():
create_report([
reporting.Title('Upgrade initiated with RHSM on public cloud with RHUI infrastructure'),
reporting.Summary(
'Leapp detected this system is on public cloud with RHUI infrastructure '
'but the process was initiated without "--no-rhsm" command line option '
'which implies RHSM usage (valid subscription is needed).'
),
reporting.Severity(reporting.Severity.INFO),
reporting.Tags([reporting.Tags.PUBLIC_CLOUD]),
])
return
# AWS RHUI package is provided and signed by RH but the Azure one not
if not has_package(InstalledRPM, info['leapp_pkg']):
create_report([
reporting.Title('Package "{}" is missing'.format(info['leapp_pkg'])),
reporting.Summary(
'On {} using RHUI infrastructure, a package "{}" is needed for'
'in-place upgrade'.format(provider.upper(), info['leapp_pkg'])
),
reporting.Severity(reporting.Severity.HIGH),
reporting.RelatedResource('package', info['leapp_pkg']),
reporting.Flags([reporting.Flags.INHIBITOR]),
reporting.Tags([reporting.Tags.PUBLIC_CLOUD, reporting.Tags.RHUI]),
reporting.Remediation(commands=[['yum', 'install', '-y', info['leapp_pkg']]])
])
return
if provider == 'aws':
self.produce(DNFPluginTask(name='amazon-id', disable_in=['upgrade']))
if provider == 'azure':
# Azure RHEL8 package has different name and it is not signed
self.produce(RpmTransactionTasks(to_install=[info['el8_pkg']]))
self.produce(RpmTransactionTasks(to_remove=[info['el7_pkg']]))
self.produce(KernelCmdlineArg(**{'key': 'rootdelay', 'value': '300'}))
self.produce(RHUIInfo(provider=provider))
self.produce(RequiredTargetUserspacePackages(packages=[info['el8_pkg']]))
return
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from collections import namedtuple

import pytest

from leapp.snactor.fixture import current_actor_context
from leapp.models import (
InstalledRedHatSignedRPM,
InstalledRPM,
RPM,
RHUIInfo,
RequiredTargetUserspacePackages,
)
from leapp.reporting import Report
from leapp.libraries.common.config import mock_configs
from leapp.libraries.common import rhsm


RH_PACKAGER = 'Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>'

NO_RHUI = [
RPM(name='yolo', version='0.1', release='1.sm01', epoch='1', packager=RH_PACKAGER, arch='noarch',
pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51'),
]

ON_AWS_WITHOUT_LEAPP_PKG = [
RPM(name='rh-amazon-rhui-client', version='0.1', release='1.sm01', epoch='1', packager=RH_PACKAGER,
arch='noarch', pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51'),
]

ON_AWS_WITH_LEAPP_PKG = [
RPM(name='rh-amazon-rhui-client', version='0.1', release='1.sm01', epoch='1', packager=RH_PACKAGER,
arch='noarch', pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51'),
RPM(name='leapp-rhui-aws', version='0.1', release='1.sm01', epoch='1', packager=RH_PACKAGER,
arch='noarch', pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51')
]


def create_modulesfacts(installed_rpm):
return InstalledRPM(items=installed_rpm)


msgs_received = namedtuple('MsgsReceived', ['report', 'rhui_info', 'req_target_userspace'])


@pytest.mark.parametrize('skip_rhsm, msgs_received, installed_rpms', [
(False, msgs_received(False, False, False), NO_RHUI),
(True, msgs_received(True, False, False), ON_AWS_WITHOUT_LEAPP_PKG),
(True, msgs_received(False, True, True), ON_AWS_WITH_LEAPP_PKG),
(False, msgs_received(True, False, False), ON_AWS_WITH_LEAPP_PKG)
])
def test_check_rhui_actor(
monkeypatch, current_actor_context, skip_rhsm, msgs_received, installed_rpms
):
monkeypatch.setattr(rhsm, 'skip_rhsm', lambda: skip_rhsm)

current_actor_context.feed(create_modulesfacts(installed_rpm=installed_rpms))
current_actor_context.run(config_model=mock_configs.CONFIG)
assert bool(current_actor_context.consume(Report)) is msgs_received.report
assert bool(current_actor_context.consume(RHUIInfo)) is msgs_received.rhui_info
assert bool(current_actor_context.consume(
RequiredTargetUserspacePackages)) is msgs_received.req_target_userspace
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from leapp.actors import Actor
from leapp.libraries.actor import checktargetrepos
from leapp.models import CustomTargetRepositoryFile, Report, TargetRepositories
from leapp.models import CustomTargetRepositoryFile, Report, TargetRepositories, RHUIInfo
from leapp.tags import IPUWorkflowTag, ChecksPhaseTag


Expand Down Expand Up @@ -29,14 +29,17 @@ class Checktargetrepos(Actor):
CTRF - the expected CustomTargetRepositoryFile found
RHSM - RHSM is used (it is not skipped)
There can also RHUI (public clouds) come into play, however as it is
handled the same way as RHSM, we just skip this check.
This is not 100 % reliable check. This cover just the most obvious cases
that are expected to fail. Reporting of such issues in this way, here,
will be probably much more clear, without additional errors that could
be raised.
"""

name = 'checktargetrepos'
consumes = (CustomTargetRepositoryFile, TargetRepositories)
consumes = (CustomTargetRepositoryFile, TargetRepositories, RHUIInfo)
produces = (Report)
tags = (IPUWorkflowTag, ChecksPhaseTag)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from leapp.models import CustomTargetRepositoryFile, TargetRepositories
from leapp.models import CustomTargetRepositoryFile, TargetRepositories, RHUIInfo
from leapp.libraries.stdlib import api
from leapp import reporting
from leapp.libraries.common import config, rhsm
Expand Down Expand Up @@ -29,7 +29,10 @@ def _the_enablerepo_option_used():


def process():
if not rhsm.skip_rhsm():

rhui_info = next(api.consume(RHUIInfo), None)

if not rhsm.skip_rhsm() or rhui_info:
# getting RH repositories through RHSM; resolved by seatbelts
# implemented in other actors
return
Expand Down
Loading

0 comments on commit e32ab33

Please sign in to comment.