def process():
    if (config.is_debug and not
            architecture.matches_architecture(architecture.ARCH_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
            #
            # saved_entry=63...
            # kernelopts=root=/dev/mapper...
            #
            #
            # boot_success and boot_indeterminate parameters are added later by one-shot systemd service
            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:
        api.current_logger().warning(('Skipped - Forcing checking and setting default boot entry to target kernel'
                                      ' version due to missing message'))
        return

    try:
        current_default_kernel = stdlib.run(['grubby', '--default-kernel'])['stdout'].strip()
    except (OSError, stdlib.CalledProcessError):
        api.current_logger().warning('Failed to query grubby for default kernel', exc_info=True)
        return

    for type_ in ('index', 'title'):
        try:
            stdlib.run(['grubby', '--default-{}'.format(type_)])
        except (OSError, stdlib.CalledProcessError):
            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:
        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)
        update_default_kernel(kernel_info)
    if (config.is_debug and not
            architecture.matches_architecture(architecture.ARCH_S390X)):  # pylint: disable=using-constant-test
        try:
            stdlib.run(['grub2-editenv', 'list'])
        except stdlib.CalledProcessError:
            api.current_logger().error('Failed to execute "grub2-editenv list" command')
Example #2
0
    def process(self):
        if not architecture.matches_architecture(architecture.ARCH_S390X):
            return

        cmdline = library.get_kernel_cmdline()
        if library.znet_is_set(cmdline):
            _report = [
                reporting.Title(
                    'Detected the rd.znet parameter in kernel cmdline'),
                reporting.Severity(reporting.Severity.HIGH),
                reporting.Tags(([reporting.Tags.SANITY])),
                reporting.Flags(([reporting.Flags.INHIBITOR])),
                reporting.Summary(
                    'Upgrade on s390x machines with the rd.znet kernel'
                    ' parameter is not supported and the upgrade has been'
                    ' inhibited.')
            ]

            if not library.vlan_is_used():
                hint = (
                    'If you want to continue, remove the rd.znet parameter from'
                    ' the kernel cmdline using grubby and zipl tools and reboot.'
                    ' But only in case you are sure you do not the parameter'
                    ' specified to have working network. E.g. in case you are'
                    ' using VLAN, you should not do that.')
                _report.append(reporting.Remediation(hint=hint))
            reporting.create_report(_report)
Example #3
0
def process():
    if not architecture.matches_architecture(architecture.ARCH_S390X):
        return
    cpuinfo = next(api.consume(CPUInfo), None)
    if cpuinfo is None:
        raise StopActorExecutionError(message=("Missing information about CPU."))

    if not cpuinfo.machine_type:
        # this is not expected to happen, but in case...
        api.curernt_logger().warning("The machine (CPU) type is empty.")

    if cpuinfo.machine_type not in SUPPORTED_MACHINE_TYPES:
        summary = ("The machine is not possible to upgrade because of unsupported"
                   " type of the processor. Regarding the official documentation,"
                   " z13 and z14 processors are supported on the Red Had Enterprise"
                   " Linux 8 system for the IBM Z architecture. If you have one of"
                   " the supported processors, you should see provided the machine"
                   " type in the /proc/cpuinfo file with one of those values: {}."
                   " Detected machine type of the CPU is '{}'."
                   .format(", ".join([str(i) for i in SUPPORTED_MACHINE_TYPES]), cpuinfo.machine_type))
        report = [
            reporting.Title("The processor is not supported by the target system."),
            reporting.Summary(summary),
            reporting.Severity(reporting.Severity.HIGH),
            reporting.Tags([reporting.Tags.SANITY]),
            reporting.Flags([reporting.Flags.INHIBITOR]),
            reporting.ExternalLink(
                title="Considerations in adopting RHEL 8",
                url=("https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/"
                     "html-single/considerations_in_adopting_rhel_8/"
                     "index#changes-in-gcc-in-rhel-8_changes-in-toolchain-since-rhel-7"))
        ]
        reporting.create_report(report)
Example #4
0
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, 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,
            # otherwise the new boot entry will not be set as default
            # See https://bugzilla.redhat.com/show_bug.cgi?id=1764306
            run(['/usr/sbin/zipl'])
    except CalledProcessError as e:
        raise StopActorExecutionError(
            'Cannot configure bootloader.',
            details={'details': '{}: {}'.format(str(e), e.stderr)})
def remove_boot_entry():
    # we need to make sure /boot is mounted before trying to remove the boot entry

    facts_msg = api.consume(FirmwareFacts)
    facts = next(facts_msg, None)
    if not facts:
        raise StopActorExecutionError(
            'Could not identify system firmware',
            details={
                'details': 'Actor did not receive FirmwareFacts message.'
            })

    mount_points_per_firmware = {
        'bios': ['/boot'],
        'efi': ['/boot', '/boot/efi']
    }

    for mp in mount_points_per_firmware[facts.firmware]:
        try:
            run(['/bin/mount', mp])
        except CalledProcessError:
            # partitions have been most likely already mounted
            pass
    kernel_filepath = get_upgrade_kernel_filepath()
    run(['/usr/sbin/grubby', '--remove-kernel={0}'.format(kernel_filepath)])
    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
        run(['/usr/sbin/zipl'])

    # TODO: Move calling `mount -a` to a separate actor as it is not really related to removing the upgrade boot entry.
    #       It's worth to call it after removing the boot entry to avoid boot loop in case mounting fails.
    run(['/bin/mount', '-a'])
def process(configs=None):
    kernel_version = next(api.consume(InstalledTargetKernelVersion), None)
    if kernel_version:
        while True:
            config = []
            if configs:
                config = ['-c', configs.pop(0)]
            for arg in api.consume(KernelCmdlineArg):
                cmd = ['grubby', '--update-kernel=/boot/vmlinuz-{}'.format(kernel_version.version),
                       '--args={}={}'.format(arg.key, arg.value)]
                cmd += config
                try:
                    stdlib.run(cmd)
                    if architecture.matches_architecture(architecture.ARCH_S390X):
                        # on s390x we need to call zipl explicitly because of issue in grubby,
                        # otherwise the entry is not updated in the ZIPL bootloader
                        # See https://bugzilla.redhat.com/show_bug.cgi?id=1764306
                        stdlib.run(['/usr/sbin/zipl'])

                except (OSError, stdlib.CalledProcessError) as e:
                    raise StopActorExecutionError(
                        "Failed to append extra arguments to kernel command line.",
                        details={"details": str(e)})
            if not configs:
                break
Example #7
0
    def process(self):
        if architecture.matches_architecture(architecture.ARCH_S390X):
            # s390x archs use ZIPL instead of GRUB
            return

        ff = next(self.consume(FirmwareFacts), None)
        if ff and ff.firmware == 'bios':
            dev = next(self.consume(GrubDevice), None)
            if dev:
                self.produce(UpdateGrub(grub_device=dev.grub_device))
                create_report([
                    reporting.Title(
                        'GRUB core will be updated during upgrade'),
                    reporting.Summary(GRUB_SUMMARY),
                    reporting.Severity(reporting.Severity.HIGH),
                    reporting.Tags([reporting.Tags.BOOT]),
                ])
            else:
                create_report([
                    reporting.Title(
                        'Leapp could not identify where GRUB core is located'),
                    reporting.Summary(
                        'We assume GRUB core is located on the same device as /boot. Leapp needs to '
                        'update GRUB core as it is not done automatically on legacy (BIOS) systems. '
                    ),
                    reporting.Severity(reporting.Severity.HIGH),
                    reporting.Tags([reporting.Tags.BOOT]),
                    reporting.Remediation(
                        hint=
                        'Please run "grub2-install <GRUB_DEVICE> command manually after upgrade'
                    ),
                ])
Example #8
0
def process():
    if not architecture.matches_architecture(architecture.ARCH_S390X):
        return

    pkgs = _get_kernel_rpms()
    if not pkgs:
        # Hypothatical, user is not allowed to install any kernel that is not signed by RH
        # In case we would like to be cautious, we could check whether there are no other
        # kernels installed as well.
        api.current_logger().log.error(
            'Cannot find any installed kernel signed by Red Hat.')
        raise StopActorExecutionError(
            'Cannot find any installed kernel signed by Red Hat.')
    if len(pkgs) > 1:
        # It's temporary solution, so no need to try automatize everything.
        title = 'Multiple kernels installed'
        summary = (
            'The upgrade process does not handle well the case when multiple kernels'
            ' are installed on s390x. There is a severe risk of the bootloader configuration'
            ' getting corrupted during the upgrade.')
        remediation = (
            'Boot into the most up-to-date kernel and remove all older'
            ' kernels installed on the machine before running Leapp again.')
        reporting.create_report([
            reporting.Title(title),
            reporting.Summary(summary),
            reporting.Severity(reporting.Severity.HIGH),
            reporting.Tags([reporting.Tags.KERNEL, reporting.Tags.BOOT]),
            reporting.Flags([reporting.Flags.INHIBITOR]),
            reporting.Remediation(hint=remediation),
            reporting.RelatedResource('package', 'kernel')
        ])
def process():
    kernel_name = 'kernel'
    if version.is_rhel_realtime():
        api.current_logger().info('The Real Time kernel boot detected.')
        kernel_name = 'kernel-rt'

    pkgs = get_pkgs(kernel_name)
    if not pkgs:
        # Hypothatical, user is not allowed to install any kernel that is not signed by RH
        # In case we would like to be cautious, we could check whether there are no other
        # kernels installed as well.
        api.current_logger().error(
            'Cannot find any installed kernel signed by Red Hat.')
        raise StopActorExecutionError(
            'Cannot find any installed kernel signed by Red Hat.')

    if len(pkgs) > 1 and architecture.matches_architecture(
            architecture.ARCH_S390X):
        # It's temporary solution, so no need to try automatize everything.
        title = 'Multiple kernels installed'
        summary = (
            'The upgrade process does not handle well the case when multiple kernels'
            ' are installed on s390x. There is a severe risk of the bootloader configuration'
            ' getting corrupted during the upgrade.')
        remediation = (
            'Boot into the most up-to-date kernel and remove all older'
            ' kernels installed on the machine before running Leapp again.')
        reporting.create_report([
            reporting.Title(title),
            reporting.Summary(summary),
            reporting.Severity(reporting.Severity.HIGH),
            reporting.Tags([reporting.Tags.KERNEL, reporting.Tags.BOOT]),
            reporting.Flags([reporting.Flags.INHIBITOR]),
            reporting.Remediation(hint=remediation),
            reporting.RelatedResource('package', 'kernel')
        ])

    current_evr = get_current_kernel_evr()
    newest_evr = get_newest_evr(pkgs)

    api.current_logger().debug('Current kernel EVR: {}'.format(current_evr))
    api.current_logger().debug('Newest kernel EVR: {}'.format(newest_evr))

    if current_evr != newest_evr:
        title = 'Newest installed kernel not in use'
        summary = ('To ensure a stable upgrade, the machine needs to be'
                   ' booted into the latest installed kernel.')
        remediation = ('Boot into the most up-to-date kernel installed'
                       ' on the machine before running Leapp again.')
        reporting.create_report([
            reporting.Title(title),
            reporting.Summary(summary),
            reporting.Severity(reporting.Severity.HIGH),
            reporting.Tags([reporting.Tags.KERNEL, reporting.Tags.BOOT]),
            reporting.Flags([reporting.Flags.INHIBITOR]),
            reporting.Remediation(hint=remediation),
            reporting.RelatedResource('package', 'kernel')
        ])
Example #10
0
def _create_initram_packages():
    # copy the list as we do not want to affect the constant because of tests
    required_pkgs = _REQUIRED_PACKAGES[:]
    if architecture.matches_architecture(architecture.ARCH_X86_64):
        required_pkgs.append('biosdevname')
    if version.get_target_major_version() == '9':
        required_pkgs += ['policycoreutils', 'rng-tools']
    return (RequiredUpgradeInitramPackages(packages=required_pkgs),
            TargetUserSpaceUpgradeTasks(install_rpms=required_pkgs))
Example #11
0
    def process(self):
        if not architecture.matches_architecture(architecture.ARCH_S390X):
            return
        userspace = next(self.consume(TargetUserSpaceInfo), None)
        if not userspace:
            # actually this should not happen, but in such case, we want to still
            # rather continue even if we boot into the old kernel, but in such
            # case, people will have to do manual actions.
            # NOTE: it is really just hypothetical
            self.log_error(
                'TargetUserSpaceInfo is missing. Cannot execute zipl-switch-to-blscfg'
                ' to convert the zipl configuration to BLS.')
            raise StopActorExecutionError(
                'GENERAL FAILURE: Input data for the actor are missing.')

        # replace the original boot directory inside the container by the host one
        # - as we cannot use zipl* pointing anywhere else than default directory
        # - no, --bls-directory is not solution
        with mounting.BindMount(source='/boot',
                                target=os.path.join(userspace.path, 'boot')):
            userspace_zipl_conf = os.path.join(userspace.path, 'etc',
                                               'zipl.conf')
            if os.path.exists(userspace_zipl_conf):
                os.remove(userspace_zipl_conf)
            with mounting.NullMount(target=userspace.path) as userspace:
                with userspace.nspawn() as context:
                    context.copy_to('/etc/zipl.conf', '/etc/zipl.conf')
                    # zipl needs this one as well
                    context.copy_to('/etc/machine-id', '/etc/machine-id')
                    try:
                        context.call(['/usr/sbin/zipl-switch-to-blscfg'])
                        if filecmp.cmp('/etc/zipl.conf', userspace_zipl_conf):
                            # When the files are same, zipl failed - see the switch script
                            raise OSError(
                                'Failed to convert the ZIPL configuration to BLS.'
                            )
                        context.copy_from('/etc/zipl.conf', '/etc/zipl.conf')
                    except OSError as e:
                        self.log.error(
                            'Could not call zipl-switch-to-blscfg command.',
                            exc_info=True)
                        raise StopActorExecutionError(
                            message='Failed to execute zipl-switch-to-blscfg.',
                            details={'details': str(e)})
                    except CalledProcessError as e:
                        self.log.error(
                            'zipl-switch-to-blscfg execution failed,',
                            exc_info=True)
                        raise StopActorExecutionError(
                            message=
                            'zipl-switch-to-blscfg execution failed with non zero exit code.',
                            details={
                                'details': str(e),
                                'stdout': e.stdout,
                                'stderr': e.stderr
                            })
Example #12
0
def _check_memory(mem_info):
    msg = {}

    for arch, min_req in iter(min_req_memory.items()):
        if architecture.matches_architecture(arch):
            is_ok = mem_info.mem_total >= min_req
            msg = {} if is_ok else {'detected': mem_info.mem_total,
                                    'minimal_req': min_req}

    return msg
Example #13
0
def process():
    if not architecture.matches_architecture(architecture.ARCH_S390X):
        return
    if os.path.isfile(DASD_CONF):
        # the file has to be copied into the targetuserspace container first,
        # then it can be included into the initramfs ==> both messages are
        # needed to be produced
        api.produce(TargetUserSpaceUpgradeTasks(copy_files=[CopyFile(src=DASD_CONF)]))
        api.produce(UpgradeInitramfsTasks(include_files=[DASD_CONF]))
    else:
        api.current_logger().warning(
            "The {} file has not been discovered. DASD not used?"
            .format(DASD_CONF)
        )
def update_default_kernel(kernel_info):
    try:
        stdlib.run(['grubby', '--info', kernel_info.kernel_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)
    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])
            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)
Example #15
0
def _install_files(context):
    """
    Copy the required files into the `context` userspace and return the list
    of those files.

    Those files will be copied from the source to the userspace used for
    generating of the upgrade initrd. These files may need to be installed
    in the initrd explicitly, so return the list of those files for other
    purposes.
    """
    # TODO: currently we need this just for /etc/dasd.conf, but it's expected
    # we will need this for more files in future. The concept used here will
    # need to be changed, as we should consume specific messages instead.
    # But for now this should be enough. Keeping that for future.
    if architecture.matches_architecture(architecture.ARCH_S390X):
        # we don't need to check existence of the file - it is required on this
        # this architecture
        context.copy_to('/etc/dasd.conf', '/etc/dasd.conf')
        return ['/etc/dasd.conf']
    return []
Example #16
0
def perform_check():
    if not architecture.matches_architecture(architecture.ARCH_S390X):
        return

    if os.path.ismount('/boot'):
        return

    data = [
        reporting.Title('Leapp detected known issue related to /boot on s390x architecture'),
        reporting.Summary((
            'Due to a bug in the Leapp code, there is a situation when the upgrade process'
            ' removes content of /boot when the directory is not on a separate partition and'
            ' the system is running on S390x architecture. To avoid this from happening, we'
            ' are inhibiting the upgrade process in this release until the issue has been fixed.'
        )),
        reporting.Flags([reporting.Flags.INHIBITOR]),
        reporting.Tags([reporting.Tags.FILESYSTEM, reporting.Tags.UPGRADE_PROCESS, reporting.Tags.BOOT]),
        reporting.Severity(reporting.Severity.HIGH),
    ]

    reporting.create_report(data)
Example #17
0
    def process(self):
        if architecture.matches_architecture(architecture.ARCH_S390X):
            # For now, skip just s390x, that's only one that is failing now
            # because ZIPL is used there
            return
        config = '/etc/default/grub'
        error_detected = detect_config_error(config)
        if error_detected:
            create_report([
                reporting.Title('Syntax error detected in grub configuration'),
                reporting.Summary(
                    'Syntax error was detected in GRUB_CMDLINE_LINUX value of grub configuration. '
                    'This error is causing booting and other issues. '
                    'Error is automatically fixed by add_upgrade_boot_entry actor.'
                ),
                reporting.Severity(reporting.Severity.LOW),
                reporting.Tags([reporting.Tags.BOOT]),
                reporting.RelatedResource('file', config)
            ])

        self.produce(GrubConfigError(error_detected=error_detected))
Example #18
0
def platform_check():
    """ Creates an inhibitor report in case the system is not running on x86_64 """
    if not architecture.matches_architecture(architecture.ARCH_X86_64):
        reporting.create_report([
            reporting.Title(
                'SAP HANA upgrades are only supported on X86_64 systems'),
            reporting.Summary(
                ('Upgrades for SAP HANA are only supported on X86_64 systems.'
                 ' For more information please consult the documentation.')),
            reporting.Severity(reporting.Severity.HIGH),
            reporting.Tags([reporting.Tags.SANITY]),
            reporting.Flags([reporting.Flags.INHIBITOR]),
            reporting.Audience('sysadmin'),
            reporting.ExternalLink(
                url='https://access.redhat.com/solutions/5533441',
                title=
                'How do I upgrade from Red Hat Enterprise Linux 7 to Red Hat Enterprise Linux 8 with SAP HANA'
            )
        ])
        return False

    return True
Example #19
0
def process():
    if not architecture.matches_architecture(architecture.ARCH_S390X):
        return
    cpuinfo = next(api.consume(CPUInfo), None)
    if cpuinfo is None:
        raise StopActorExecutionError(
            message=("Missing information about CPU."))

    if not cpuinfo.machine_type:
        # this is not expected to happen, but in case...
        api.current_logger().warning("The machine (CPU) type is empty.")

    if cpuinfo.machine_type not in SUPPORTED_MACHINE_TYPES:
        summary = (
            "The system is not possible to upgrade because of unsupported"
            " type of the processor. Based on the official documentation,"
            " z14 and z15 processors are supported on the Red Hat Enterprise"
            " Linux 9 system for the IBM Z architecture. The supported processors"
            " have machine types {}. The detected machine type of the CPU is '{}'."
            .format(", ".join([str(i) for i in SUPPORTED_MACHINE_TYPES]),
                    cpuinfo.machine_type))
        report = [
            reporting.Title(
                "The processor is not supported by the target system."),
            reporting.Summary(summary),
            reporting.Severity(reporting.Severity.HIGH),
            reporting.Tags([reporting.Tags.SANITY]),
            reporting.Flags([reporting.Flags.INHIBITOR]),
            reporting.ExternalLink(
                title="Considerations in adopting RHEL 8",
                url=
                ("https://access.redhat.com/ecosystem/hardware/#/search?p=1&"
                 "c_version=Red%20Hat%20Enterprise%20Linux%208&ch_architecture=s390x"
                 ))
        ]
        # FIXME(dhorak): update the URL to the document once it exists
        reporting.create_report(report)
def test_matches_architecture_fail(monkeypatch):
    monkeypatch.setattr(api, 'current_actor', CurrentActorMocked)
    assert not architecture.matches_architecture()
    assert not architecture.matches_architecture(
        *architecture.ARCH_ACCEPTED[1:])
Example #21
0
def check_architecture():
    """Check if given architecture is supported by upgrade process"""
    if not architecture.matches_architecture(*architecture.ARCH_SUPPORTED):
        inhibit_upgrade()
def test_matches_architecture_pass(monkeypatch):
    monkeypatch.setattr(api, 'current_actor',
                        CurrentActorMocked(arch=architecture.ARCH_ACCEPTED[0]))
    assert architecture.matches_architecture(*architecture.ARCH_ACCEPTED)
def test_matches_architecture_wrong_args():
    with pytest.raises(TypeError):
        architecture.matches_architecture('aarch64', 1)
Example #24
0
def _create_initram_packages():
    required_pkgs = _REQUIRED_PACKAGES
    if architecture.matches_architecture(architecture.ARCH_X86_64):
        required_pkgs.append('biosdevname')
    return RequiredUpgradeInitramPackages(packages=required_pkgs)
Example #25
0
def _create_initram_packages():
    required_pkgs = _REQUIRED_PACKAGES
    if architecture.matches_architecture(architecture.ARCH_X86_64):
        required_pkgs.append('biosdevname')
    return (RequiredUpgradeInitramPackages(packages=required_pkgs),
            TargetUserSpaceUpgradeTasks(install_rpms=required_pkgs))
Example #26
0
 def process(self):
     if architecture.matches_architecture(architecture.ARCH_S390X):
         return
     get_grub_device()