c64266d19b
- Introduce new upgrade path RHEL 8.9 -> 9.3 - Update leapp data files to reflect new changes between systems - Detect and report use of deprecated Xorg drivers - Minor improvements of generated reports - Fix false positive report about invalid symlinks - Inhibit the upgrade when unsupported x86-64 microarchitecture is detected - Resolves: rhbz#2215997
378 lines
17 KiB
Diff
378 lines
17 KiB
Diff
From 777e0a641739add1fca50af774d6d924af5550d7 Mon Sep 17 00:00:00 2001
|
|
From: David Kubek <dkubek@redhat.com>
|
|
Date: Tue, 14 Mar 2023 11:54:18 +0100
|
|
Subject: [PATCH 25/30] Inhibit unsupported x86-64 microarchitecture RHEL9
|
|
|
|
As per [x86-64-ABI][1] In addition to the AMD64 baseline architecture,
|
|
several micro-architecture levels implemented by later CPU modules have
|
|
been defined, starting at level ``x86-64-v2``.
|
|
|
|
RHEL9 has a higher CPU requirement than older versions, it now requires
|
|
a CPU compatible with ``x86-64-v2`` instruction set or higher. Until
|
|
now, there was no check for this and the upgrade crashed unexpectedly.
|
|
This commit handles this issue and provides the user with a report
|
|
explaining the problem.
|
|
|
|
The CPU Features are gathered using the ``lscpu`` command by way of
|
|
using the ``ScanCPU`` actor. The ``ScanCPU`` actor had to be also
|
|
modified to provide the required flags. The mapping of CPU Features to
|
|
flags provided by ``lscpu`` has been determined by using the
|
|
``/arch/x86/include/asm/cpufeatures.h`` file from the linux kernel.
|
|
|
|
[1]: https://gitlab.com/x86-psABIs/x86-64-ABI.git
|
|
---
|
|
.../actors/scancpu/libraries/scancpu.py | 13 +++-
|
|
.../actors/scancpu/tests/test_scancpu.py | 74 +++++++++++++++----
|
|
repos/system_upgrade/common/models/cpuinfo.py | 4 +-
|
|
.../actors/checkmicroarchitecture/actor.py | 63 ++++++++++++++++
|
|
.../libraries/checkmicroarchitecture.py | 47 ++++++++++++
|
|
.../tests/test_checkmicroarchitecture.py | 65 ++++++++++++++++
|
|
6 files changed, 249 insertions(+), 17 deletions(-)
|
|
create mode 100644 repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/actor.py
|
|
create mode 100644 repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/libraries/checkmicroarchitecture.py
|
|
create mode 100644 repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/tests/test_checkmicroarchitecture.py
|
|
|
|
diff --git a/repos/system_upgrade/common/actors/scancpu/libraries/scancpu.py b/repos/system_upgrade/common/actors/scancpu/libraries/scancpu.py
|
|
index 68f5623b..e5555f99 100644
|
|
--- a/repos/system_upgrade/common/actors/scancpu/libraries/scancpu.py
|
|
+++ b/repos/system_upgrade/common/actors/scancpu/libraries/scancpu.py
|
|
@@ -17,6 +17,11 @@ def _get_lscpu_output():
|
|
return ''
|
|
|
|
|
|
+def _get_cpu_flags(lscpu):
|
|
+ flags = lscpu.get('Flags', '')
|
|
+ return flags.split()
|
|
+
|
|
+
|
|
def _get_cpu_entries_for(arch_prefix):
|
|
result = []
|
|
for message in api.consume(DeviceDriverDeprecationData):
|
|
@@ -137,4 +142,10 @@ def process():
|
|
api.produce(*_find_deprecation_data_entries(lscpu))
|
|
# Backwards compatibility
|
|
machine_type = lscpu.get('Machine type')
|
|
- api.produce(CPUInfo(machine_type=int(machine_type) if machine_type else None))
|
|
+ flags = _get_cpu_flags(lscpu)
|
|
+ api.produce(
|
|
+ CPUInfo(
|
|
+ machine_type=int(machine_type) if machine_type else None,
|
|
+ flags=flags
|
|
+ )
|
|
+ )
|
|
diff --git a/repos/system_upgrade/common/actors/scancpu/tests/test_scancpu.py b/repos/system_upgrade/common/actors/scancpu/tests/test_scancpu.py
|
|
index 44d4de87..894fae08 100644
|
|
--- a/repos/system_upgrade/common/actors/scancpu/tests/test_scancpu.py
|
|
+++ b/repos/system_upgrade/common/actors/scancpu/tests/test_scancpu.py
|
|
@@ -1,14 +1,59 @@
|
|
import os
|
|
|
|
+import pytest
|
|
+
|
|
from leapp.libraries.actor import scancpu
|
|
from leapp.libraries.common import testutils
|
|
+from leapp.libraries.common.config.architecture import (
|
|
+ ARCH_ARM64,
|
|
+ ARCH_PPC64LE,
|
|
+ ARCH_S390X,
|
|
+ ARCH_SUPPORTED,
|
|
+ ARCH_X86_64
|
|
+)
|
|
from leapp.libraries.stdlib import api
|
|
from leapp.models import CPUInfo
|
|
|
|
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
+LSCPU = {
|
|
+ ARCH_ARM64: {
|
|
+ "machine_type": None,
|
|
+ "flags": ['fp', 'asimd', 'evtstrm', 'aes', 'pmull', 'sha1', 'sha2', 'crc32', 'cpuid'],
|
|
+ },
|
|
+ ARCH_PPC64LE: {
|
|
+ "machine_type": None,
|
|
+ "flags": []
|
|
+ },
|
|
+ ARCH_S390X: {
|
|
+ "machine_type":
|
|
+ 2827,
|
|
+ "flags": [
|
|
+ 'esan3', 'zarch', 'stfle', 'msa', 'ldisp', 'eimm', 'dfp', 'edat', 'etf3eh', 'highgprs', 'te', 'vx', 'vxd',
|
|
+ 'vxe', 'gs', 'vxe2', 'vxp', 'sort', 'dflt', 'sie'
|
|
+ ]
|
|
+ },
|
|
+ ARCH_X86_64: {
|
|
+ "machine_type":
|
|
+ None,
|
|
+ "flags": [
|
|
+ 'fpu', 'vme', 'de', 'pse', 'tsc', 'msr', 'pae', 'mce', 'cx8', 'apic', 'sep', 'mtrr', 'pge', 'mca', 'cmov',
|
|
+ 'pat', 'pse36', 'clflush', 'dts', 'acpi', 'mmx', 'fxsr', 'sse', 'sse2', 'ss', 'ht', 'tm', 'pbe', 'syscall',
|
|
+ 'nx', 'pdpe1gb', 'rdtscp', 'lm', 'constant_tsc', 'arch_perfmon', 'pebs', 'bts', 'rep_good', 'nopl',
|
|
+ 'xtopology', 'nonstop_tsc', 'cpuid', 'aperfmperf', 'pni', 'pclmulqdq', 'dtes64', 'monitor', 'ds_cpl',
|
|
+ 'vmx', 'smx', 'est', 'tm2', 'ssse3', 'sdbg', 'fma', 'cx16', 'xtpr', 'pdcm', 'pcid', 'dca', 'sse4_1',
|
|
+ 'sse4_2', 'x2apic', 'movbe', 'popcnt', 'tsc_deadline_timer', 'aes', 'xsave', 'avx', 'f16c', 'rdrand',
|
|
+ 'lahf_lm', 'abm', 'cpuid_fault', 'epb', 'invpcid_single', 'pti', 'ssbd', 'ibrs', 'ibpb', 'stibp',
|
|
+ 'tpr_shadow', 'vnmi', 'flexpriority', 'ept', 'vpid', 'ept_ad', 'fsgsbase', 'tsc_adjust', 'bmi1', 'avx2',
|
|
+ 'smep', 'bmi2', 'erms', 'invpcid', 'cqm', 'xsaveopt', 'cqm_llc', 'cqm_occup_llc', 'dtherm', 'ida', 'arat',
|
|
+ 'pln', 'pts', 'md_clear', 'flush_l1d'
|
|
+ ]
|
|
+ },
|
|
+}
|
|
+
|
|
|
|
class mocked_get_cpuinfo(object):
|
|
+
|
|
def __init__(self, filename):
|
|
self.filename = filename
|
|
|
|
@@ -22,24 +67,25 @@ class mocked_get_cpuinfo(object):
|
|
return '\n'.join(fp.read().splitlines())
|
|
|
|
|
|
-def test_machine_type(monkeypatch):
|
|
+@pytest.mark.parametrize("arch", ARCH_SUPPORTED)
|
|
+def test_scancpu(monkeypatch, arch):
|
|
|
|
- # cpuinfo doesn't contain a machine field
|
|
- mocked_cpuinfo = mocked_get_cpuinfo('lscpu_x86_64')
|
|
+ mocked_cpuinfo = mocked_get_cpuinfo('lscpu_' + arch)
|
|
monkeypatch.setattr(scancpu, '_get_lscpu_output', mocked_cpuinfo)
|
|
monkeypatch.setattr(api, 'produce', testutils.produce_mocked())
|
|
- current_actor = testutils.CurrentActorMocked(arch=testutils.architecture.ARCH_X86_64)
|
|
+ current_actor = testutils.CurrentActorMocked(arch=arch)
|
|
monkeypatch.setattr(api, 'current_actor', current_actor)
|
|
- scancpu.process()
|
|
- assert api.produce.called == 1
|
|
- assert CPUInfo() == api.produce.model_instances[0]
|
|
|
|
- # cpuinfo contains a machine field
|
|
- api.produce.called = 0
|
|
- api.produce.model_instances = []
|
|
- current_actor = testutils.CurrentActorMocked(arch=testutils.architecture.ARCH_S390X)
|
|
- monkeypatch.setattr(api, 'current_actor', current_actor)
|
|
- mocked_cpuinfo.filename = 'lscpu_s390x'
|
|
scancpu.process()
|
|
+
|
|
+ expected = CPUInfo(machine_type=LSCPU[arch]["machine_type"], flags=LSCPU[arch]["flags"])
|
|
+ produced = api.produce.model_instances[0]
|
|
+
|
|
assert api.produce.called == 1
|
|
- assert CPUInfo(machine_type=2827) == api.produce.model_instances[0]
|
|
+
|
|
+ # Produced what was expected
|
|
+ assert expected.machine_type == produced.machine_type
|
|
+ assert sorted(expected.flags) == sorted(produced.flags)
|
|
+
|
|
+ # Did not produce anything extra
|
|
+ assert expected == produced
|
|
diff --git a/repos/system_upgrade/common/models/cpuinfo.py b/repos/system_upgrade/common/models/cpuinfo.py
|
|
index e3e52838..ee245563 100644
|
|
--- a/repos/system_upgrade/common/models/cpuinfo.py
|
|
+++ b/repos/system_upgrade/common/models/cpuinfo.py
|
|
@@ -32,8 +32,8 @@ class CPUInfo(Model):
|
|
# byte_order = fields.StringEnum(['Little Endian', 'Big Endian'])
|
|
# """ Byte order of the CPU: 'Little Endian' or 'Big Endian' """
|
|
|
|
- # flags = fields.List(fields.String(), default=[])
|
|
- # """ Specifies flags/features of the CPU. """
|
|
+ flags = fields.List(fields.String(), default=[])
|
|
+ """ Specifies flags/features of the CPU. """
|
|
|
|
# hypervisor = fields.Nullable(fields.String())
|
|
# hypervisor_vendor = fields.Nullable(fields.String())
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/actor.py b/repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/actor.py
|
|
new file mode 100644
|
|
index 00000000..98ffea80
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/actor.py
|
|
@@ -0,0 +1,63 @@
|
|
+import leapp.libraries.actor.checkmicroarchitecture as checkmicroarchitecture
|
|
+from leapp.actors import Actor
|
|
+from leapp.models import CPUInfo
|
|
+from leapp.reporting import Report
|
|
+from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
|
|
+
|
|
+
|
|
+class CheckMicroarchitecture(Actor):
|
|
+ """
|
|
+ Inhibit if RHEL9 microarchitecture requirements are not satisfied
|
|
+
|
|
+
|
|
+ As per `x86-64-ABI`_ In addition to the AMD64 baseline architecture, several
|
|
+ micro-architecture levels implemented by later CPU modules have been
|
|
+ defined, starting at level ``x86-64-v2``. The levels are cumulative in the
|
|
+ sense that features from previous levels are implicitly included in later
|
|
+ levels.
|
|
+
|
|
+ RHEL9 has a higher CPU requirement than older versions, it now requires a
|
|
+ CPU compatible with ``x86-64-v2`` instruction set or higher.
|
|
+
|
|
+ .. table:: Required CPU features by microarchitecure level with a
|
|
+ corresponding flag as shown by ``lscpu``.
|
|
+
|
|
+ +------------+-------------+--------------------+
|
|
+ | Version | CPU Feature | flag (lscpu) |
|
|
+ +============+=============+====================+
|
|
+ | (baseline) | CMOV | cmov |
|
|
+ | | CX8 | cx8 |
|
|
+ | | FPU | fpu |
|
|
+ | | FXSR | fxsr |
|
|
+ | | MMX | mmx |
|
|
+ | | OSFXSR | (common with FXSR) |
|
|
+ | | SCE | syscall |
|
|
+ | | SSE | sse |
|
|
+ | | SSE2 | sse2 |
|
|
+ +------------+-------------+--------------------+
|
|
+ | x86-64-v2 | CMPXCHG16B | cx16 |
|
|
+ | | LAHF-SAHF | lahf_lm |
|
|
+ | | POPCNT | popcnt |
|
|
+ | | SSE3 | pni |
|
|
+ | | SSE4_1 | sse4_1 |
|
|
+ | | SSE4_2 | sse4_2 |
|
|
+ | | SSSE3 | ssse3 |
|
|
+ +------------+-------------+--------------------+
|
|
+ | ... | | |
|
|
+ +------------+-------------+--------------------+
|
|
+
|
|
+ Note: To get the corresponding flag for the CPU feature consult the file
|
|
+ ``/arch/x86/include/asm/cpufeatures.h`` in the linux kernel.
|
|
+
|
|
+
|
|
+ .. _x86-64-ABI: https://gitlab.com/x86-psABIs/x86-64-ABI.git
|
|
+
|
|
+ """
|
|
+
|
|
+ name = 'check_microarchitecture'
|
|
+ consumes = (CPUInfo,)
|
|
+ produces = (Report,)
|
|
+ tags = (ChecksPhaseTag, IPUWorkflowTag,)
|
|
+
|
|
+ def process(self):
|
|
+ checkmicroarchitecture.process()
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/libraries/checkmicroarchitecture.py b/repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/libraries/checkmicroarchitecture.py
|
|
new file mode 100644
|
|
index 00000000..0f1f1fca
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/libraries/checkmicroarchitecture.py
|
|
@@ -0,0 +1,47 @@
|
|
+from leapp import reporting
|
|
+from leapp.libraries.common.config.architecture import ARCH_X86_64, matches_architecture
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import CPUInfo
|
|
+
|
|
+X86_64_BASELINE_FLAGS = ['cmov', 'cx8', 'fpu', 'fxsr', 'mmx', 'syscall', 'sse', 'sse2']
|
|
+X86_64_V2_FLAGS = ['cx16', 'lahf_lm', 'popcnt', 'pni', 'sse4_1', 'sse4_2', 'ssse3']
|
|
+
|
|
+
|
|
+def _inhibit_upgrade(missing_flags):
|
|
+ title = 'Current x86-64 microarchitecture is unsupported in RHEL9'
|
|
+ summary = ('RHEL9 has a higher CPU requirement than older versions, it now requires a CPU '
|
|
+ 'compatible with x86-64-v2 instruction set or higher.\n\n'
|
|
+ 'Missings flags detected are: {}\n'.format(', '.join(missing_flags)))
|
|
+
|
|
+ reporting.create_report([
|
|
+ reporting.Title(title),
|
|
+ reporting.Summary(summary),
|
|
+ reporting.ExternalLink(title='Building Red Hat Enterprise Linux 9 for the x86-64-v2 microarchitecture level',
|
|
+ url=('https://developers.redhat.com/blog/2021/01/05/'
|
|
+ 'building-red-hat-enterprise-linux-9-for-the-x86-64-v2-microarchitecture-level')),
|
|
+ reporting.Severity(reporting.Severity.HIGH),
|
|
+ reporting.Groups([reporting.Groups.INHIBITOR]),
|
|
+ reporting.Groups([reporting.Groups.SANITY]),
|
|
+ reporting.Remediation(hint=('If case of using virtualization, virtualization platforms often allow '
|
|
+ 'configuring a minimum denominator CPU model for compatibility when migrating '
|
|
+ 'between different CPU models. Ensure that minimum requirements are not below '
|
|
+ 'that of RHEL9\n')),
|
|
+ ])
|
|
+
|
|
+
|
|
+def process():
|
|
+ """
|
|
+ Check whether the processor matches the required microarchitecture.
|
|
+ """
|
|
+
|
|
+ if not matches_architecture(ARCH_X86_64):
|
|
+ api.current_logger().info('Architecture not x86-64. Skipping microarchitecture test.')
|
|
+ return
|
|
+
|
|
+ cpuinfo = next(api.consume(CPUInfo))
|
|
+
|
|
+ required_flags = X86_64_BASELINE_FLAGS + X86_64_V2_FLAGS
|
|
+ missing_flags = [flag for flag in required_flags if flag not in cpuinfo.flags]
|
|
+ api.current_logger().debug('Required flags missing: %s', missing_flags)
|
|
+ if missing_flags:
|
|
+ _inhibit_upgrade(missing_flags)
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/tests/test_checkmicroarchitecture.py b/repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/tests/test_checkmicroarchitecture.py
|
|
new file mode 100644
|
|
index 00000000..b7c850d9
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/el8toel9/actors/checkmicroarchitecture/tests/test_checkmicroarchitecture.py
|
|
@@ -0,0 +1,65 @@
|
|
+import pytest
|
|
+
|
|
+from leapp import reporting
|
|
+from leapp.libraries.actor import checkmicroarchitecture
|
|
+from leapp.libraries.common.config.architecture import ARCH_SUPPORTED, ARCH_X86_64
|
|
+from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, logger_mocked
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import CPUInfo
|
|
+from leapp.utils.report import is_inhibitor
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize("arch", [arch for arch in ARCH_SUPPORTED if not arch == ARCH_X86_64])
|
|
+def test_not_x86_64_passes(monkeypatch, arch):
|
|
+ """
|
|
+ Test no report is generated on an architecture different from x86-64
|
|
+ """
|
|
+
|
|
+ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
|
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch=arch))
|
|
+
|
|
+ checkmicroarchitecture.process()
|
|
+
|
|
+ assert 'Architecture not x86-64. Skipping microarchitecture test.' in api.current_logger.infomsg
|
|
+ assert not reporting.create_report.called
|
|
+
|
|
+
|
|
+def test_valid_microarchitecture(monkeypatch):
|
|
+ """
|
|
+ Test no report is generated on a valid microarchitecture
|
|
+ """
|
|
+
|
|
+ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
|
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
|
+
|
|
+ required_flags = checkmicroarchitecture.X86_64_BASELINE_FLAGS + checkmicroarchitecture.X86_64_V2_FLAGS
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch=ARCH_X86_64,
|
|
+ msgs=[CPUInfo(flags=required_flags)]))
|
|
+
|
|
+ checkmicroarchitecture.process()
|
|
+
|
|
+ assert 'Architecture not x86-64. Skipping microarchitecture test.' not in api.current_logger.infomsg
|
|
+ assert not reporting.create_report.called
|
|
+
|
|
+
|
|
+def test_invalid_microarchitecture(monkeypatch):
|
|
+ """
|
|
+ Test report is generated on x86-64 architecture with invalid microarchitecture and the upgrade is inhibited
|
|
+ """
|
|
+
|
|
+ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
|
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch=ARCH_X86_64, msgs=[CPUInfo()]))
|
|
+
|
|
+ checkmicroarchitecture.process()
|
|
+
|
|
+ produced_title = reporting.create_report.report_fields.get('title')
|
|
+ produced_summary = reporting.create_report.report_fields.get('summary')
|
|
+
|
|
+ assert 'Architecture not x86-64. Skipping microarchitecture test.' not in api.current_logger().infomsg
|
|
+ assert reporting.create_report.called == 1
|
|
+ assert 'microarchitecture is unsupported' in produced_title
|
|
+ assert 'RHEL9 has a higher CPU requirement' in produced_summary
|
|
+ assert reporting.create_report.report_fields['severity'] == reporting.Severity.HIGH
|
|
+ assert is_inhibitor(reporting.create_report.report_fields)
|
|
--
|
|
2.40.1
|
|
|