diff --git a/SOURCES/leapp-repository-0.22.0-elevate.patch b/SOURCES/leapp-repository-0.22.0-elevate.patch index f52b039..dc824ad 100644 --- a/SOURCES/leapp-repository-0.22.0-elevate.patch +++ b/SOURCES/leapp-repository-0.22.0-elevate.patch @@ -1,3 +1,16 @@ +diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml +index b8da5ebb..3e595e32 100644 +--- a/.github/workflows/codespell.yml ++++ b/.github/workflows/codespell.yml +@@ -17,7 +17,7 @@ jobs: + - uses: actions/checkout@v4 + - uses: codespell-project/actions-codespell@v2 + with: +- ignore_words_list: ro,fo,couldn,repositor,zeor ++ ignore_words_list: ro,fo,couldn,repositor,zeor,bootup + skip: "./repos/system_upgrade/common/actors/storagescanner/tests/files/mounts,\ + ./repos/system_upgrade/common/actors/networkmanagerreadconfig/tests/files/nm_cfg_file_error,\ + ./repos/system_upgrade/el8toel9/actors/xorgdrvfact/tests/files/journalctl-xorg-intel,\ diff --git a/.gitignore b/.gitignore index 0bb92d3d..a04c7ded 100644 --- a/.gitignore @@ -3434,6 +3447,320 @@ index 84b9de1b..387468f3 100644 def check_version(version): +diff --git a/commands/preupgrade/__init__.py b/commands/preupgrade/__init__.py +index c1fabbbd..e52b6561 100644 +--- a/commands/preupgrade/__init__.py ++++ b/commands/preupgrade/__init__.py +@@ -14,6 +14,10 @@ from leapp.utils.output import beautify_actor_exception, report_errors, report_i + + @command('preupgrade', help='Generate preupgrade report') + @command_opt('whitelist-experimental', action='append', metavar='ActorName', help='Enables experimental actors') ++@command_opt('enable-experimental-feature', action='append', metavar='Feature', ++ help=('Enable experimental feature. ' ++ 'Available experimental features: {}').format(util.get_help_str_with_avail_experimental_features()), ++ choices=list(util.EXPERIMENTAL_FEATURES), default=[]) + @command_opt('debug', is_flag=True, help='Enable debug mode', inherit=False) + @command_opt('verbose', is_flag=True, help='Enable verbose logging', inherit=False) + @command_opt('no-rhsm', is_flag=True, help='Use only custom repositories and skip actions' +diff --git a/commands/rerun/__init__.py b/commands/rerun/__init__.py +index a06dd266..842178af 100644 +--- a/commands/rerun/__init__.py ++++ b/commands/rerun/__init__.py +@@ -71,6 +71,7 @@ def rerun(args): + nogpgcheck=False, + channel=None, + report_schema='1.1.0', ++ enable_experimental_feature=[], + whitelist_experimental=[], + enablerepo=[])) + +diff --git a/commands/upgrade/__init__.py b/commands/upgrade/__init__.py +index 608099ac..6f7504bf 100644 +--- a/commands/upgrade/__init__.py ++++ b/commands/upgrade/__init__.py +@@ -20,6 +20,10 @@ from leapp.utils.output import beautify_actor_exception, report_errors, report_i + @command_opt('resume', is_flag=True, help='Continue the last execution after it was stopped (e.g. after reboot)') + @command_opt('reboot', is_flag=True, help='Automatically performs reboot when requested.') + @command_opt('whitelist-experimental', action='append', metavar='ActorName', help='Enable experimental actors') ++@command_opt('enable-experimental-feature', action='append', metavar='Feature', ++ help=('Enable experimental feature. ' ++ 'Available experimental features: {}').format(util.get_help_str_with_avail_experimental_features()), ++ choices=list(util.EXPERIMENTAL_FEATURES), default=[]) + @command_opt('debug', is_flag=True, help='Enable debug mode', inherit=False) + @command_opt('verbose', is_flag=True, help='Enable verbose logging', inherit=False) + @command_opt('no-rhsm', is_flag=True, help='Use only custom repositories and skip actions' +diff --git a/commands/upgrade/util.py b/commands/upgrade/util.py +index b20c316d..6cdfa6d8 100644 +--- a/commands/upgrade/util.py ++++ b/commands/upgrade/util.py +@@ -16,6 +16,24 @@ from leapp.utils.audit import get_checkpoints, get_connection, get_messages + from leapp.utils.output import report_unsupported + from leapp.utils.report import fetch_upgrade_report_messages, generate_report_file + ++EXPERIMENTAL_FEATURES = { ++ 'livemode': [ ++ 'live_image_generator', ++ 'live_mode_config_scanner', ++ 'live_mode_reporter', ++ 'prepare_live_image', ++ 'emit_livemode_requirements', ++ 'remove_live_image', ++ ] ++} ++""" Maps experimental features to a set of experimental actors that need to be enabled. """ ++ ++ ++def get_help_str_with_avail_experimental_features(): ++ if EXPERIMENTAL_FEATURES: ++ return ', '.join(EXPERIMENTAL_FEATURES) ++ return 'There are no experimental features available' ++ + + def disable_database_sync(): + def disable_db_sync_decorator(f): +@@ -184,11 +202,25 @@ def handle_output_level(args): + # the latest supported release because of target_version discovery attempt. + def prepare_configuration(args): + """Returns a configuration dict object while setting a few env vars as a side-effect""" ++ + if args.whitelist_experimental: + args.whitelist_experimental = list(itertools.chain(*[i.split(',') for i in args.whitelist_experimental])) + os.environ['LEAPP_EXPERIMENTAL'] = '1' + else: + os.environ['LEAPP_EXPERIMENTAL'] = '0' ++ args.whitelist_experimental = [] ++ ++ for experimental_feature in set(args.enable_experimental_feature): ++ # It might happen that there are no experimental features, which would allow user ++ # to pass us any string as an experimental feature. ++ if experimental_feature not in EXPERIMENTAL_FEATURES: ++ continue ++ ++ actors_needed_for_feature = EXPERIMENTAL_FEATURES[experimental_feature] ++ args.whitelist_experimental.extend(actors_needed_for_feature) ++ if args.enable_experimental_feature: ++ os.environ['LEAPP_EXPERIMENTAL'] = '1' ++ + os.environ['LEAPP_UNSUPPORTED'] = '0' if os.getenv('LEAPP_UNSUPPORTED', '0') == '0' else '1' + if args.no_rhsm: + os.environ['LEAPP_NO_RHSM'] = '1' +@@ -235,7 +267,7 @@ def prepare_configuration(args): + configuration = { + 'debug': os.getenv('LEAPP_DEBUG', '0'), + 'verbose': os.getenv('LEAPP_VERBOSE', '0'), +- 'whitelist_experimental': args.whitelist_experimental or (), ++ 'whitelist_experimental': args.whitelist_experimental or (), # Modified to also contain exp. features + 'environment': {env: os.getenv(env) for env in os.environ if env.startswith('LEAPP_')}, + 'cmd': sys.argv, + } +diff --git a/docs/source/configuring-ipu.md b/docs/source/configuring-ipu/envars.md +similarity index 93% +rename from docs/source/configuring-ipu.md +rename to docs/source/configuring-ipu/envars.md +index 6b838b8f..61a50b82 100644 +--- a/docs/source/configuring-ipu.md ++++ b/docs/source/configuring-ipu/envars.md +@@ -52,9 +52,6 @@ The alternative to the --channel leapp option. As a parameter accepts a channel + To use development variables, the LEAPP_UNSUPPORTED variable has to be set. + ``` + +-#### LEAPP_DEVEL_ENABLE_LIVE_MODE +-If set to `1`, enable the use of the experimental live mode +- + #### LEAPP_DEVEL_DM_DISABLE_UDEV + Setting the environment variable provides a more convenient way of disabling udev support in libdevmapper, dmsetup and LVM2 tools globally without a need to modify any existing configuration settings. This is mostly useful if the system environment does not use udev. + +@@ -81,11 +78,3 @@ Change the default target RHEL version. Format: `MAJOR.MINOR`. + + #### LEAPP_DEVEL_USE_PERSISTENT_PACKAGE_CACHE + Caches downloaded packages when set to `1`. This will reduce the time needed by leapp when executed multiple times, because it will not have to download already downloaded packages. However, this can lead to a random issues in case the data is not up-to-date or when setting or repositories change. The environment variable is meant to be used only for the part of the upgrade before the reboot and has no effect or use otherwise. +- +-## Actor configuration +-```{warning} +-Actor configuration is currently a preview of the feature, it might change in future releases. +-``` +-The actor configuration is to be placed in the `/etc/leapp/actor_conf.d/` directory. An actor configuration is a file in YAML format. +- +-To define configuration options on your own actor refer to this tutorial TODO link. +diff --git a/docs/source/configuring-ipu/experimental-features/index.rst b/docs/source/configuring-ipu/experimental-features/index.rst +new file mode 100644 +index 00000000..37de2fed +--- /dev/null ++++ b/docs/source/configuring-ipu/experimental-features/index.rst +@@ -0,0 +1,24 @@ ++Experimental features ++======================================================== ++ ++This section provides descriptions of all available experimental ++features. Low-level details and design decisions of these features ++are provided. ++ ++.. warning:: ++ Actor configuration is currently a preview of the feature, it might change in future releases. ++ ++.. toctree:: ++ :maxdepth: 4 ++ :caption: Contents: ++ :glob: ++ ++ livemode ++ ++ ++.. Indices and tables ++.. ================== ++.. ++.. * :ref:`genindex` ++.. * :ref:`modindex` ++.. * :ref:`search` +diff --git a/docs/source/configuring-ipu/experimental-features/livemode.md b/docs/source/configuring-ipu/experimental-features/livemode.md +new file mode 100644 +index 00000000..44200e80 +--- /dev/null ++++ b/docs/source/configuring-ipu/experimental-features/livemode.md +@@ -0,0 +1,86 @@ ++# LiveMode ++ ++_LiveMode_ is an experimental feature that partially replaces ++leapp's custom upgrade environment with a bootable squashfs image of the target ++system. Intuitively, this squashfs-based mechanism is similar to using a live ++CD (hence the name LiveMode) from which the DNF transaction and other ++post-reboot steps will be applied. Such an upgrade environment closely ++resembles an ordinary Linux installation, making developing desired ++functionality (e.g. supporting network-based storage) much easier. ++ ++## Technical details ++During an upgrade, prior to rebooting, leapp constructs a minimal target system ++container in order to obtain a version of the DNF stack expected by the new ++packages installed during the upgrade. After the container is created, the new ++DNF stack is used to download packages that will be installed during the ++upgrade. Having all necessary packages, leapp checks the RPM transaction to be ++performed during the upgrade. Finally, the upgrade environment is created - an ++initramfs containing custom dracut modules that ultimately execute leapp very ++early in the boot process. Such an upgrade environment guarantees isolation ++from other system services as there is essentially only the upgrade process ++running. However, the downside of using such an approach is that the bootup ++process of the upgrade environment is non-standard, meaning that almost none of ++the classical system initialisation services (e.g., LVM autoactivation) are ++running. Developing advanced features such as support for network-based ++storage, is, therefore demanding as only a little of the usual initialisation ++is present and executed during bootup. ++ ++The LiveMode feature obtains a similar isolation level of the upgrade process ++in a different way. Instead of using an initramfs image that executes leapp ++early, the system boots into a read-only squashfs system built from the target ++system container build previously to check the upgrade RPM transaction. Since ++leapp controls the creation of the target system container, it is also in ++control of what will be running alongside the upgrade process, limiting the ++possibility of arbitrary user-defined services interfering with the upgrade. ++The upgrade environment boots into the `multi-user.target` target and leapp is ++started as an ordinary systemd service. However, the squashfs image needs to be ++stored on the disk, and, hence, the using feature **requires about 700mb of ++additional disk space**. ++ ++## Using the feature ++It is possible to use the LiveMode feature by having set `LEAPP_UNSUPPORTED=1` ++and running leapp as `leapp upgrade --enable-experimental-feature livemode`. ++``` ++LEAPP_UNSUPPORTED=1 leapp upgrade --enable-experimental-feature livemode ++``` ++### Configuration ++The feature offers an extensive list of configuration options that can be set ++by creating a YAML file in `/etc/leapp/actor_conf.d/` with the extension ++`.yaml`. The content of the configuration file must be a mapping defining the ++`livemode` key with a value that is a mapping with (some) of the following ++keys: ++ ++| Configuration field | Value type | Default | Semantics | ++|---------------------|------------|---------|-----------| ++| `squashfs_image_path` | `str` | `/var/lib/leapp/live-upgrade.img` | Location where the squashfs image of the minimal target system will be placed. | ++| `additional_packages` | `List[str]` | `[]` | Additional packages to be installed into the squashfs image. | ++| `autostart_upgrade_after_reboot` | `bool` | `True` | If set to True, the upgrade will start automatically after the reboot. Otherwise a manual trigger is required. | ++| `setup_network_manager` | `bool` | `False` | Try enabling Network Manager in the squashfs image. | ++| `dracut_network` | `str` | `''` | Dracut network arguments, required if the `url_to_load_squashfs_from` option is set. | ++| `url_to_load_squashfs_image_from` | `str` | `''` | URL pointing to the squashfs image that should be used for the upgrade environment. | ++| `setup_passwordless_root` | `bool` | `False` | If set to True, the root account of the squashfs image will have empty password. Use with caution. | ++| `setup_opensshd_using_auth_keys` | `str` | `''` | If set to a non-empty string, openssh daemon will be setup within the squashfs image using the provided authorized keys file. | ++| `capture_strace_info_into` | `str` | `''` | If set to a non-empty string, leapp will be executed under strace and results will be stored within the provided file path. | ++ ++#### Configuration example ++Consider the file `/etc/leapp/actor_conf.d/livemode.yaml` with the following contents. ++``` ++livemode: ++ additional_packages : [ vim ] ++ autostart_upgrade_after_reboot : false ++ setup_network_manager : true ++ setup_opensshd_using_auth_keys : /root/.ssh/authorized_keys ++``` ++ ++The configuration results in the following actions: ++- Leapp will install the `vim` package into the upgrade environment. ++- The upgrade will not be started automatically after reboot. Instead, user ++ needs to resume the upgrade manually. Therefore, it is possible to manually ++ inspect the system and verify that everything is in order, e.g., all of the ++ necessary storage is mounted. ++- Leapp will attempt to enable `NetworkManager` inside the upgrade environment ++ using source system's network profiles. This attempt is best-effort, meaning ++ that there is no guarantee that the network will be functional. ++- Leapp will enable the `opensshd` service. If a network access is established ++ successfully, it will be possible to login using ssh into the upgrade ++ environment using the `root` account and interact with the system. +diff --git a/docs/source/configuring-ipu/index.rst b/docs/source/configuring-ipu/index.rst +new file mode 100644 +index 00000000..6490d6fd +--- /dev/null ++++ b/docs/source/configuring-ipu/index.rst +@@ -0,0 +1,23 @@ ++Configuring the in-place upgrade ++======================================================== ++ ++This section covers possible ways of modifying the in-place upgrade ++without making any code changes. Leapp offers multiple mechanism ++to affect the upgrade ranging from simple environmental variables ++to more robust configuration files. This section also covers available ++experimental features. ++ ++.. toctree:: ++ :maxdepth: 4 ++ :caption: Contents: ++ :glob: ++ ++ envars ++ experimental-features/index ++ ++.. Indices and tables ++.. ================== ++.. ++.. * :ref:`genindex` ++.. * :ref:`modindex` ++.. * :ref:`search` +diff --git a/docs/source/index.rst b/docs/source/index.rst +index 33a920ec..27537ca4 100644 +--- a/docs/source/index.rst ++++ b/docs/source/index.rst +@@ -19,7 +19,7 @@ providing Red Hat Enterprise Linux in-place upgrade functionality. + tutorials/index + project-structure/index + upgrade-architecture-and-workflow/index +- configuring-ipu ++ configuring-ipu/index + libraries-and-api/index + contrib-and-devel-guidelines + faq +diff --git a/etc/leapp/files/devel-livemode.ini b/etc/leapp/files/devel-livemode.ini +deleted file mode 100644 +index b79ed4df..00000000 +--- a/etc/leapp/files/devel-livemode.ini ++++ /dev/null +@@ -1,9 +0,0 @@ +-# Configuration for the *experimental* livemode feature +-# It is likely that this entire configuration file will be replaced by some +-# other mechanism/file in the future. For the full list of configuration options, +-# see models/livemode.py +-[livemode] +-squashfs_fullpath=/var/lib/leapp/live-upgrade.img +-setup_network_manager=no +-autostart_upgrade_after_reboot=yes +-setup_passwordless_root=no diff --git a/etc/leapp/transaction/to_reinstall b/etc/leapp/transaction/to_reinstall new file mode 100644 index 00000000..c6694a8e @@ -3443,6 +3770,19 @@ index 00000000..c6694a8e +### List of packages (each on new line) to be reinstalled to the upgrade transaction +### Useful for packages that have identical version strings but contain binary changes between major OS versions +### Packages that aren't installed will be skipped +diff --git a/packaging/leapp-repository.spec b/packaging/leapp-repository.spec +index f45fda68..34768de1 100644 +--- a/packaging/leapp-repository.spec ++++ b/packaging/leapp-repository.spec +@@ -120,7 +120,7 @@ Requires: leapp-repository-dependencies = %{leapp_repo_deps} + + # IMPORTANT: this is capability provided by the leapp framework rpm. + # Check that 'version' instead of the real framework rpm version. +-Requires: leapp-framework >= 6.0, leapp-framework < 7 ++Requires: leapp-framework >= 6.1, leapp-framework < 7 + + # Since we provide sub-commands for the leapp utility, we expect the leapp + # tool to be installed as well. diff --git a/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py b/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py index b28ec57c..6882488a 100644 --- a/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py @@ -3741,7 +4081,7 @@ index 582a5821..18f2c33f 100644 modules_to_reset=list(modules_to_reset.values()), modules_to_enable=list(modules_to_enable.values()))) diff --git a/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py b/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py -index 749b3347..39e4487f 100644 +index 749b3347..34ece8a1 100644 --- a/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py +++ b/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py @@ -1,4 +1,5 @@ @@ -3778,6 +4118,545 @@ index 749b3347..39e4487f 100644 variant=data.get('VARIANT', '').strip('"') or None, variant_id=data.get('VARIANT_ID', '').strip('"') or None ) +@@ -85,6 +96,27 @@ def check_target_major_version(curr_version, target_version): + ) + + ++def load_raw_upgrade_paths_for_flavour(flavour='default', paths_definition_file='upgrade_paths.json'): ++ with open(api.get_common_file_path(paths_definition_file)) as fp: ++ data = json.loads(fp.read()) ++ ++ raw_upgrade_paths = data.get(flavour, {}) ++ ++ if not raw_upgrade_paths: ++ api.current_logger().warning('Cannot discover any upgrade paths for flavour: {}'.format(flavour)) ++ ++ return raw_upgrade_paths ++ ++ ++def construct_models_for_paths_matching_source_major(raw_paths, src_major_version): ++ multipaths_matching_source = [] ++ for src_version, target_versions in ipu_paths.items(): ++ if src_version.split('.')[0] == src_major_version: ++ source_to_targets = IPUSourceToPossibleTargets(source_version=src_version, target_versions=target_versions) ++ multipaths_matching_source.append() ++ return multipaths_matching_source ++ ++ + def produce_ipu_config(actor): + flavour = os.environ.get('LEAPP_UPGRADE_PATH_FLAVOUR') + target_version = os.environ.get('LEAPP_UPGRADE_PATH_TARGET_RELEASE') +@@ -93,6 +125,10 @@ def produce_ipu_config(actor): + + check_target_major_version(source_version, target_version) + ++ raw_upgrade_paths = load_raw_upgrade_paths_for_flavour(flavour) ++ source_major_version = source_version.split('.')[0] ++ exposed_supported_paths = construct_models_for_paths_matching_source_major(raw_upgrade_paths, source_major_version) ++ + actor.produce(IPUConfig( + leapp_env_vars=get_env_vars(), + os_release=os_release, +@@ -102,5 +138,6 @@ def produce_ipu_config(actor): + target=target_version + ), + kernel=get_booted_kernel(), +- flavour=flavour ++ flavour=flavour, ++ supported_upgrade_paths=exposed_supported_paths + )) +diff --git a/repos/system_upgrade/common/actors/livemode/liveimagegenerator/libraries/liveimagegenerator.py b/repos/system_upgrade/common/actors/livemode/liveimagegenerator/libraries/liveimagegenerator.py +index af8981d8..46118630 100644 +--- a/repos/system_upgrade/common/actors/livemode/liveimagegenerator/libraries/liveimagegenerator.py ++++ b/repos/system_upgrade/common/actors/livemode/liveimagegenerator/libraries/liveimagegenerator.py +@@ -24,7 +24,7 @@ def lighten_target_userpace(context): + tree_to_prune, error) + + +-def build_squashfs(livemode_config, userspace_info): ++def build_squashfs(livemode_config, userspace_info, paths_to_exclude=None): + """ + Generate the live rootfs image based on the target userspace + +@@ -34,8 +34,11 @@ def build_squashfs(livemode_config, userspace_info): + target_userspace_fullpath = userspace_info.path + squashfs_fullpath = livemode_config.squashfs_fullpath + +- api.current_logger().info('Building the squashfs image %s from target userspace located at %s', +- squashfs_fullpath, target_userspace_fullpath) ++ if not paths_to_exclude: ++ paths_to_exclude = [] ++ ++ api.current_logger().info('Building the squashfs image %s from target userspace located at %s with excludes: %s', ++ squashfs_fullpath, target_userspace_fullpath, ', '.join(paths_to_exclude)) + + try: + if os.path.exists(squashfs_fullpath): +@@ -44,8 +47,13 @@ def build_squashfs(livemode_config, userspace_info): + api.current_logger().warning('Failed to remove already existing %s. Full error: %s', + squashfs_fullpath, error) + ++ mksquashfs_command = ['mksquashfs', target_userspace_fullpath, squashfs_fullpath] ++ if paths_to_exclude: ++ mksquashfs_command.append('-e') ++ mksquashfs_command.extend(paths_to_exclude) ++ + try: +- run(['mksquashfs', target_userspace_fullpath, squashfs_fullpath]) ++ run(mksquashfs_command) + except CalledProcessError as error: + raise StopActorExecutionError( + 'Cannot pack the target userspace into a squash image. ', +@@ -68,5 +76,9 @@ def generate_live_image_if_enabled(): + + with mounting.NspawnActions(base_dir=userspace_info.path) as context: + lighten_target_userpace(context) +- squashfs_path = build_squashfs(livemode_config, userspace_info) ++ ++ # Exclude the DNF cache - we do not need it, leapp mounts /sysroot and uses userspace's dnf cache from there ++ paths_to_exclude = [os.path.join(userspace_info.path, 'var/cache/dnf')] ++ ++ squashfs_path = build_squashfs(livemode_config, userspace_info, paths_to_exclude=paths_to_exclude) + api.produce(LiveModeArtifacts(squashfs_path=squashfs_path)) +diff --git a/repos/system_upgrade/common/actors/livemode/liveimagegenerator/tests/test_image_generation.py b/repos/system_upgrade/common/actors/livemode/liveimagegenerator/tests/test_image_generation.py +index 5c434a6b..16ae0a09 100644 +--- a/repos/system_upgrade/common/actors/livemode/liveimagegenerator/tests/test_image_generation.py ++++ b/repos/system_upgrade/common/actors/livemode/liveimagegenerator/tests/test_image_generation.py +@@ -78,10 +78,12 @@ def test_generate_live_image_if_enabled(monkeypatch, livemode_config, should_pro + def __exit__(self, *args, **kwargs): + pass + ++ def build_squashfs_image_mock(livemode_config, userspace_info, *args, **kwargs): ++ return '/squashfs' ++ + monkeypatch.setattr(mounting, 'NspawnActions', NspawnMock) + monkeypatch.setattr(live_image_generator_lib, 'lighten_target_userpace', lambda context: None) +- monkeypatch.setattr(live_image_generator_lib, 'build_squashfs', +- lambda livemode_config, userspace_info: '/squashfs') ++ monkeypatch.setattr(live_image_generator_lib, 'build_squashfs', build_squashfs_image_mock) + monkeypatch.setattr(api, 'produce', produce_mocked()) + + live_image_generator_lib.generate_live_image_if_enabled() +diff --git a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/actor.py b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/actor.py +index dc79ecff..bd909736 100644 +--- a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/actor.py ++++ b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/actor.py +@@ -1,4 +1,5 @@ + from leapp.actors import Actor ++from leapp.configs.actor import livemode as livemode_config_lib + from leapp.libraries.actor import scan_livemode_config as scan_livemode_config_lib + from leapp.models import InstalledRPM, LiveModeConfig + from leapp.tags import ExperimentalTag, FactsPhaseTag, IPUWorkflowTag +@@ -10,6 +11,7 @@ class LiveModeConfigScanner(Actor): + """ + + name = 'live_mode_config_scanner' ++ config_schemas = livemode_config_lib.livemode_cfg_fields + consumes = (InstalledRPM,) + produces = (LiveModeConfig,) + tags = (ExperimentalTag, FactsPhaseTag, IPUWorkflowTag,) +diff --git a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/configs/livemode.py b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/configs/livemode.py +new file mode 100644 +index 00000000..eeef03f8 +--- /dev/null ++++ b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/configs/livemode.py +@@ -0,0 +1,127 @@ ++""" ++Configuration keys for the 'livemode' feature. ++""" ++ ++from leapp.actors.config import Config ++from leapp.models import fields ++ ++LIVEMODE_CONFIG_SECTION = 'livemode' ++ ++ ++class SquashfsImagePath(Config): ++ section = LIVEMODE_CONFIG_SECTION ++ name = "squashfs_image_path" ++ type_ = fields.String() ++ default = '/var/lib/leapp/live-upgrade.img' ++ description = """ ++ Location where the squashfs image of the minimal target system will be placed. ++ """ ++ ++ ++class AdditionalPackages(Config): ++ section = LIVEMODE_CONFIG_SECTION ++ name = "additional_packages" ++ type_ = fields.List(fields.String()) ++ default = [] ++ description = """ ++ Additional packages to be installed into the squashfs image. ++ ++ Can be used to install various debugging utilities when connecting to the upgrade environment. ++ """ ++ ++ ++class AutostartUpgradeAfterReboot(Config): ++ section = LIVEMODE_CONFIG_SECTION ++ name = "autostart_upgrade_after_reboot" ++ type_ = fields.Boolean() ++ default = True ++ description = """ ++ If set to True, the upgrade will start automatically after the reboot. Otherwise a manual trigger is required. ++ """ ++ ++ ++class SetupNetworkManager(Config): ++ section = LIVEMODE_CONFIG_SECTION ++ name = "setup_network_manager" ++ type_ = fields.Boolean() ++ default = False ++ description = """ ++ Try enabling Network Manager in the squashfs image. ++ ++ If set to True, leapp will copy source system's Network Manager profiles into the squashfs image and ++ enable the Network Manager service. ++ """ ++ ++ ++class DracutNetwork(Config): ++ section = LIVEMODE_CONFIG_SECTION ++ name = "dracut_network" ++ type_ = fields.String() ++ default = '' ++ description = """ ++ Dracut network arguments, required if the `url_to_load_squashfs_from` option is set. ++ ++ Example: ++ ip=192.168.122.146::192.168.122.1:255.255.255.0:foo::none ++ """ ++ ++ ++class URLToLoadSquashfsImageFrom(Config): ++ section = LIVEMODE_CONFIG_SECTION ++ name = "url_to_load_squashfs_image_from" ++ type_ = fields.String() ++ default = '' ++ description = """ ++ Url pointing to the squashfs image that should be used for the upgrade environment. ++ ++ Example: ++ http://192.168.122.1/live-upgrade.img ++ """ ++ ++ ++class SetupPasswordlessRoot(Config): ++ section = LIVEMODE_CONFIG_SECTION ++ name = "setup_passwordless_root" ++ type_ = fields.Boolean() ++ default = False ++ description = """ ++ If set to True, the root account of the squashfs image will have empty password. Use with caution. ++ """ ++ ++ ++class SetupOpenSSHDUsingAuthKeys(Config): ++ section = LIVEMODE_CONFIG_SECTION ++ name = "setup_opensshd_using_auth_keys" ++ type_ = fields.String() ++ default = '' ++ description = """ ++ If set to a non-empty string, openssh daemon will be setup within the squashfs image using the provided ++ authorized keys. ++ ++ Example: ++ /root/.ssh/authorized_keys ++ """ ++ ++ ++class CaptureSTraceInfoInto(Config): ++ section = LIVEMODE_CONFIG_SECTION ++ name = "capture_strace_info_into" ++ type_ = fields.String() ++ default = '' ++ description = """ ++ If set to a non-empty string, leapp will be executed under strace and results will be stored within ++ the provided file path. ++ """ ++ ++ ++livemode_cfg_fields = ( ++ AdditionalPackages, ++ AutostartUpgradeAfterReboot, ++ CaptureSTraceInfoInto, ++ DracutNetwork, ++ SetupNetworkManager, ++ SetupOpenSSHDUsingAuthKeys, ++ SetupPasswordlessRoot, ++ SquashfsImagePath, ++ URLToLoadSquashfsImageFrom, ++) +diff --git a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/libraries/scan_livemode_config.py b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/libraries/scan_livemode_config.py +index b2f0af7f..26fd9d09 100644 +--- a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/libraries/scan_livemode_config.py ++++ b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/libraries/scan_livemode_config.py +@@ -1,14 +1,9 @@ +-try: +- import configparser +-except ImportError: +- import ConfigParser as configparser +- ++from leapp.configs.actor import livemode as livemode_config_lib + from leapp.exceptions import StopActorExecutionError + from leapp.libraries.common.config import architecture, get_env + from leapp.libraries.common.rpms import has_package + from leapp.libraries.stdlib import api + from leapp.models import InstalledRPM, LiveModeConfig +-from leapp.models.fields import ModelViolationError + + LIVEMODE_CONFIG_LOCATION = '/etc/leapp/files/devel-livemode.ini' + DEFAULT_SQUASHFS_PATH = '/var/lib/leapp/live-upgrade.img' +@@ -16,16 +11,11 @@ DEFAULT_SQUASHFS_PATH = '/var/lib/leapp/live-upgrade.img' + + def should_scan_config(): + is_unsupported = get_env('LEAPP_UNSUPPORTED', '0') == '1' +- is_livemode_enabled = get_env('LEAPP_DEVEL_ENABLE_LIVE_MODE', '0') == '1' + + if not is_unsupported: + api.current_logger().debug('Will not scan livemode config - the upgrade is not unsupported.') + return False + +- if not is_livemode_enabled: +- api.current_logger().debug('Will not scan livemode config - the live mode is not enabled.') +- return False +- + if not architecture.matches_architecture(architecture.ARCH_X86_64): + api.current_logger().debug('Will not scan livemode config - livemode is currently limited to x86_64.') + details = 'Live upgrades are currently limited to x86_64 only.' +@@ -50,76 +40,32 @@ def scan_config_and_emit_message(): + return + + api.current_logger().info('Loading livemode config from %s', LIVEMODE_CONFIG_LOCATION) +- parser = configparser.ConfigParser() +- +- try: +- parser.read((LIVEMODE_CONFIG_LOCATION, )) +- except configparser.ParsingError as error: +- api.current_logger().error('Failed to parse live mode configuration due to the following error: %s', error) +- +- details = 'Failed to read livemode configuration due to the following error: {0}.' +- raise StopActorExecutionError( +- 'Failed to read livemode configuration', +- details={'Problem': details.format(error)} +- ) +- +- livemode_section = 'livemode' +- if not parser.has_section(livemode_section): +- details = 'The configuration is missing the \'[{0}]\' section'.format(livemode_section) +- raise StopActorExecutionError( +- 'Live mode configuration does not have the required structure', +- details={'Problem': details} +- ) + +- config_kwargs = { +- 'is_enabled': True, +- 'url_to_load_squashfs_from': None, +- 'squashfs_fullpath': DEFAULT_SQUASHFS_PATH, +- 'dracut_network': None, +- 'setup_network_manager': False, +- 'additional_packages': [], +- 'autostart_upgrade_after_reboot': True, +- 'setup_opensshd_with_auth_keys': None, +- 'setup_passwordless_root': False, +- 'capture_upgrade_strace_into': None ++ config = api.current_actor().config[livemode_config_lib.LIVEMODE_CONFIG_SECTION] ++ ++ # Mapping from model field names to configuration fields - because we might have ++ # changed some configuration field names for configuration to be more ++ # comprehensible for our users. ++ model_fields_to_config_options_map = { ++ 'url_to_load_squashfs_from': livemode_config_lib.URLToLoadSquashfsImageFrom, ++ 'squashfs_fullpath': livemode_config_lib.SquashfsImagePath, ++ 'dracut_network': livemode_config_lib.DracutNetwork, ++ 'setup_network_manager': livemode_config_lib.SetupNetworkManager, ++ 'additional_packages': livemode_config_lib.AdditionalPackages, ++ 'autostart_upgrade_after_reboot': livemode_config_lib.AutostartUpgradeAfterReboot, ++ 'setup_opensshd_with_auth_keys': livemode_config_lib.SetupOpenSSHDUsingAuthKeys, ++ 'setup_passwordless_root': livemode_config_lib.SetupPasswordlessRoot, ++ 'capture_upgrade_strace_into': livemode_config_lib.CaptureSTraceInfoInto + } + +- config_str_options = ( +- 'url_to_load_squashfs_from', +- 'squashfs_fullpath', +- 'dracut_network', +- 'setup_opensshd_with_auth_keys', +- 'capture_upgrade_strace_into' +- ) +- +- config_list_options = ( +- 'additional_packages', +- ) +- +- config_bool_options = ( +- 'setup_network_manager', +- 'setup_passwordless_root', +- 'autostart_upgrade_after_reboot', +- ) +- +- for config_option in config_str_options: +- if parser.has_option(livemode_section, config_option): +- config_kwargs[config_option] = parser.get(livemode_section, config_option) +- +- for config_option in config_bool_options: +- if parser.has_option(livemode_section, config_option): +- config_kwargs[config_option] = parser.getboolean(livemode_section, config_option) +- +- for config_option in config_list_options: +- if parser.has_option(livemode_section, config_option): +- option_val = parser.get(livemode_section, config_option) +- option_list = (opt_val.strip() for opt_val in option_val.split(',')) +- option_list = [opt for opt in option_list if opt] +- config_kwargs[config_option] = option_list ++ # Read values of model fields from user-supplied configuration according to the above mapping ++ config_msg_init_kwargs = {} ++ for model_field_name, config_field in model_fields_to_config_options_map.items(): ++ config_msg_init_kwargs[model_field_name] = config[config_field.name] + +- try: +- config = LiveModeConfig(**config_kwargs) +- except ModelViolationError as error: +- raise StopActorExecutionError('Failed to parse livemode configuration.', details={'Problem': str(error)}) ++ # Some fields of the LiveModeConfig are historical and can no longer be changed by the user ++ # in the config. Therefore, we just hard-code them here. ++ config_msg_init_kwargs['is_enabled'] = True + +- api.produce(config) ++ config_msg = LiveModeConfig(**config_msg_init_kwargs) ++ api.produce(config_msg) +diff --git a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/tests/test_config_scanner.py b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/tests/test_config_scanner.py +index 016f6c04..e24aa366 100644 +--- a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/tests/test_config_scanner.py ++++ b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/tests/test_config_scanner.py +@@ -29,19 +29,16 @@ EnablementTestCase = namedtuple('EnablementTestCase', ('env_vars', 'arch', 'pkgs + @pytest.mark.parametrize( + 'case_descr', + ( +- EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '1', 'LEAPP_DEVEL_ENABLE_LIVE_MODE': '1'}, ++ EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '1'}, + arch=architecture.ARCH_X86_64, pkgs=('squashfs-tools', ), + result=EnablementResult.SCAN_CONFIG), +- EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '0', 'LEAPP_DEVEL_ENABLE_LIVE_MODE': '1'}, ++ EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '0'}, + arch=architecture.ARCH_X86_64, pkgs=('squashfs-tools', ), + result=EnablementResult.DO_NOTHING), +- EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '1', 'LEAPP_DEVEL_ENABLE_LIVE_MODE': '0'}, +- arch=architecture.ARCH_X86_64, pkgs=('squashfs-tools', ), +- result=EnablementResult.DO_NOTHING), +- EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '1', 'LEAPP_DEVEL_ENABLE_LIVE_MODE': '1'}, ++ EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '1'}, + arch=architecture.ARCH_ARM64, pkgs=('squashfs-tools', ), + result=EnablementResult.RAISE), +- EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '1', 'LEAPP_DEVEL_ENABLE_LIVE_MODE': '1'}, ++ EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '1'}, + arch=architecture.ARCH_ARM64, pkgs=tuple(), + result=EnablementResult.RAISE), + ) +@@ -52,7 +49,6 @@ def test_enablement_conditions(monkeypatch, case_descr): + + Enablement conditions: + - LEAPP_UNSUPPORTED=1 +- - LEAPP_DEVEL_ENABLE_LIVE_MODE=1 + + Not meeting enablement conditions should prevent config message from being produced. + +@@ -86,29 +82,21 @@ def test_enablement_conditions(monkeypatch, case_descr): + def test_config_scanning(monkeypatch): + """ Test whether scanning a valid config is properly transcribed into a config message. """ + +- config_lines = [ +- '[livemode]', +- 'squashfs_fullpath=IMG', +- 'setup_network_manager=yes', +- 'autostart_upgrade_after_reboot=no', +- 'setup_opensshd_with_auth_keys=/root/.ssh/authorized_keys', +- 'setup_passwordless_root=no', +- 'additional_packages=pkgA,pkgB' +- ] +- config_content = '\n'.join(config_lines) + '\n' +- +- if sys.version[0] == '2': +- config_content = config_content.decode('utf-8') # python2 compat +- +- class ConfigParserMock(configparser.ConfigParser): # pylint: disable=too-many-ancestors +- def read(self, file_paths, *args, **kwargs): +- self.read_string(config_content) +- return file_paths +- +- monkeypatch.setattr(configparser, 'ConfigParser', ConfigParserMock) +- ++ config = { ++ 'livemode': { ++ 'squashfs_image_path': '/var/lib/leapp/live-upgrade2.img', ++ 'additional_packages': ['petri-nets'], ++ 'autostart_upgrade_after_reboot': True, ++ 'setup_network_manager': True, ++ 'setup_passwordless_root': True, ++ 'dracut_network': '', ++ 'url_to_load_squashfs_image_from': '', ++ 'setup_opensshd_using_auth_keys': '/root/.ssh/authorized_keys', ++ 'capture_strace_info_into': '' ++ } ++ } ++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(config=config)) + monkeypatch.setattr(scan_livemode_config_lib, 'should_scan_config', lambda: True) +- + monkeypatch.setattr(api, 'produce', produce_mocked()) + + scan_livemode_config_lib.scan_config_and_emit_message() +@@ -119,7 +107,7 @@ def test_config_scanning(monkeypatch): + produced_message = api.produce.model_instances[0] + assert isinstance(produced_message, LiveModeConfig) + +- assert produced_message.additional_packages == ['pkgA', 'pkgB'] +- assert produced_message.squashfs_fullpath == 'IMG' ++ assert produced_message.additional_packages == ['petri-nets'] ++ assert produced_message.squashfs_fullpath == '/var/lib/leapp/live-upgrade2.img' + assert produced_message.setup_opensshd_with_auth_keys == '/root/.ssh/authorized_keys' + assert produced_message.setup_network_manager +diff --git a/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/libraries/prepareliveimage.py b/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/libraries/prepareliveimage.py +index c573c84a..686c4cd6 100644 +--- a/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/libraries/prepareliveimage.py ++++ b/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/libraries/prepareliveimage.py +@@ -1,3 +1,4 @@ ++import errno + import grp + import os + import os.path +@@ -253,16 +254,30 @@ def enable_dbus(context): + Enable dbus-daemon into the target userspace + Looks like it's not enabled by default when installing into a container. + """ +- api.current_logger().info('Configuring the dbus services') ++ dbus_daemon_service = '/usr/lib/systemd/system/dbus-daemon.service' + + links = ['/etc/systemd/system/multi-user.target.wants/dbus-daemon.service', + '/etc/systemd/system/dbus.service', + '/etc/systemd/system/messagebus.service'] + ++ api.current_logger().info(('Enabling dbus services. Leapp will attempt to create the following ' ++ 'symlinks: {0}, all pointing to {1}').format(', '.join(links), ++ dbus_daemon_service)) ++ + for link in links: ++ api.current_logger().debug('Creating symlink at {0} that points to {1}'.format(link, dbus_daemon_service)) + try: + os.symlink('/usr/lib/systemd/system/dbus-daemon.service', context.full_path(link)) + except OSError as err: ++ if err.errno == errno.EEXIST: ++ # @Note: We are not catching FileExistsError because of python2 (there is no such error class) ++ # We are performing installations within container, so the systemd symlinks that are created ++ # during installation should have correct destination ++ api.current_logger().debug( ++ 'A file already exists at {0}, assuming it is a symlink with a correct content.' ++ ) ++ continue ++ + details = {'Problem': 'An error occurred while creating the systemd symlink', 'source_error': str(err)} + raise StopActorExecutionError('Cannot enable the dbus services', details=details) + diff --git a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py index 32e4527b..1e595e9a 100644 --- a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py @@ -4456,6 +5335,21 @@ index 00000000..cb5c7ab7 + msg = "The {} file exists, but is empty. Nothing to do.".format(scancustomrepofile.CUSTOM_REPO_PATH) + assert api.current_logger.infomsg == msg + assert not api.produce.called +diff --git a/repos/system_upgrade/common/actors/selinux/selinuxapplycustom/actor.py b/repos/system_upgrade/common/actors/selinux/selinuxapplycustom/actor.py +index 55c64c3e..4856f36a 100644 +--- a/repos/system_upgrade/common/actors/selinux/selinuxapplycustom/actor.py ++++ b/repos/system_upgrade/common/actors/selinux/selinuxapplycustom/actor.py +@@ -92,6 +92,10 @@ class SELinuxApplyCustom(Actor): + + command.extend(['-X', str(module.priority), '-i', cil_filename]) + ++ if command == ['semodule']: ++ # no modules selected for installation ++ continue ++ + try: + run(command) + except CalledProcessError as e: diff --git a/repos/system_upgrade/common/actors/setuptargetrepos/actor.py b/repos/system_upgrade/common/actors/setuptargetrepos/actor.py index 767fa00c..bc1d5bfa 100644 --- a/repos/system_upgrade/common/actors/setuptargetrepos/actor.py @@ -4590,7 +5484,7 @@ index 59b12c87..85d4a09e 100644 def process(self): self.produce(systemfacts.get_sysctls_status()) diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py -index 12736ab7..f90bf6e3 100644 +index 12736ab7..3b873b68 100644 --- a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py +++ b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py @@ -152,9 +152,10 @@ def _import_gpg_keys(context, install_root_dir, target_major_version): @@ -4615,6 +5509,43 @@ index 12736ab7..f90bf6e3 100644 run(['rm', '-rf', os.path.join(target_etc, 'rhsm')]) context.copytree_from('/etc/rhsm', os.path.join(target_etc, 'rhsm')) +@@ -1239,7 +1241,35 @@ def setup_target_rhui_access_if_needed(context, indata): + 'shell' + ] + +- context.call(cmd, callback_raw=utils.logging_handler, stdin='\n'.join(dnf_transaction_steps)) ++ try: ++ dnf_shell_instructions = '\n'.join(dnf_transaction_steps) ++ api.current_logger().debug( ++ 'Supplying the following instructions to the `dnf shell`: {}'.format(dnf_shell_instructions) ++ ) ++ context.call(cmd, callback_raw=utils.logging_handler, stdin=dnf_shell_instructions) ++ except CalledProcessError as error: ++ api.current_logger().debug( ++ 'Failed to swap RHUI clients. This is likely because there are no repositories ' ++ ' containing RHUI clients enabled, or we cannot access them.' ++ ) ++ api.current_logger().debug(error) ++ ++ swapping_clients_info_msg = 'Failed to swap `{0}` (source client{1}) with {2} (target client{3}).' ++ swapping_clients_info_msg = swapping_clients_info_msg.format( ++ ' '.join(indata.rhui_info.src_client_pkg_names), ++ '' if len(indata.rhui_info.src_client_pkg_names) == 1 else 's', ++ ' '.join(indata.rhui_info.target_client_pkg_names), ++ '' if len(indata.rhui_info.target_client_pkg_names) == 1 else 's', ++ ) ++ ++ details = { ++ 'details': swapping_clients_info_msg, ++ 'error': str(error) ++ } ++ raise StopActorExecutionError( ++ 'Failed to swap RHUI clients to establish content access', ++ details=details ++ ) + + _apply_rhui_access_postinstall_tasks(context, setup_info) + diff --git a/repos/system_upgrade/common/actors/trustedgpgkeysscanner/libraries/trustedgpgkeys.py b/repos/system_upgrade/common/actors/trustedgpgkeysscanner/libraries/trustedgpgkeys.py index 6377f767..4c5420f6 100644 --- a/repos/system_upgrade/common/actors/trustedgpgkeysscanner/libraries/trustedgpgkeys.py @@ -5089,6 +6020,57 @@ index 1c54dae8..5ce5a666 100644 "9": ["10.0"] }, "saphana": { +diff --git a/repos/system_upgrade/common/libraries/config/mock_configs.py b/repos/system_upgrade/common/libraries/config/mock_configs.py +index ee9dd760..a1c9c0fd 100644 +--- a/repos/system_upgrade/common/libraries/config/mock_configs.py ++++ b/repos/system_upgrade/common/libraries/config/mock_configs.py +@@ -6,7 +6,7 @@ The library is supposed to be used only for testing purposes. Import of the + library is expected only inside test files. + """ + +-from leapp.models import EnvVar, IPUConfig, OSRelease, Version ++from leapp.models import EnvVar, IPUConfig, IPUSourceToPossibleTargets, OSRelease, Version + + CONFIG = IPUConfig( + leapp_env_vars=[EnvVar(name='LEAPP_DEVEL', value='0')], +@@ -23,6 +23,9 @@ CONFIG = IPUConfig( + ), + architecture='x86_64', + kernel='3.10.0-957.43.1.el7.x86_64', ++ supported_upgrade_paths=[ ++ IPUSourceToPossibleTargets(source_version='7.6', target_versions=['8.0']) ++ ] + ) + + CONFIG_NO_NETWORK_RENAMING = IPUConfig( +@@ -40,6 +43,9 @@ CONFIG_NO_NETWORK_RENAMING = IPUConfig( + ), + architecture='x86_64', + kernel='3.10.0-957.43.1.el7.x86_64', ++ supported_upgrade_paths=[ ++ IPUSourceToPossibleTargets(source_version='7.6', target_versions=['8.0']) ++ ] + ) + + CONFIG_ALL_SIGNED = IPUConfig( +@@ -57,6 +63,9 @@ CONFIG_ALL_SIGNED = IPUConfig( + ), + architecture='x86_64', + kernel='3.10.0-957.43.1.el7.x86_64', ++ supported_upgrade_paths=[ ++ IPUSourceToPossibleTargets(source_version='7.6', target_versions=['8.0']) ++ ] + ) + + CONFIG_S390X = IPUConfig( +@@ -73,4 +82,7 @@ CONFIG_S390X = IPUConfig( + ), + architecture='s390x', + kernel='3.10.0-957.43.1.el7.x86_64', ++ supported_upgrade_paths=[ ++ IPUSourceToPossibleTargets(source_version='7.6', target_versions=['8.0']) ++ ] + ) diff --git a/repos/system_upgrade/common/libraries/config/version.py b/repos/system_upgrade/common/libraries/config/version.py index febeed36..0d075535 100644 --- a/repos/system_upgrade/common/libraries/config/version.py @@ -5411,6 +6393,51 @@ index e7b074aa..0b260c86 100644 except CalledProcessError: raise StopActorExecutionError( message='Cannot set the container mode for the subscription-manager.') +diff --git a/repos/system_upgrade/common/libraries/testutils.py b/repos/system_upgrade/common/libraries/testutils.py +index afeb360a..1b3c3683 100644 +--- a/repos/system_upgrade/common/libraries/testutils.py ++++ b/repos/system_upgrade/common/libraries/testutils.py +@@ -6,7 +6,7 @@ from collections import namedtuple + from leapp import reporting + from leapp.actors.config import _normalize_config, normalize_schemas + from leapp.libraries.common.config import architecture +-from leapp.models import EnvVar ++from leapp.models import EnvVar, IPUSourceToPossibleTargets + from leapp.utils.deprecation import deprecated + + +@@ -76,7 +76,11 @@ def _make_default_config(actor_config_schema): + + class CurrentActorMocked(object): # pylint:disable=R0904 + def __init__(self, arch=architecture.ARCH_X86_64, envars=None, kernel='3.10.0-957.43.1.el7.x86_64', +- release_id='rhel', src_ver='7.8', dst_ver='8.1', msgs=None, flavour='default', config=None): ++ release_id='rhel', src_ver='7.8', dst_ver='8.1', msgs=None, flavour='default', config=None, ++ supported_upgrade_paths=None): ++ """ ++ :param List[IPUSourceToPossibleTargets] supported_upgrade_paths: List of supported upgrade paths. ++ """ + envarsList = [EnvVar(name=k, value=v) for k, v in envars.items()] if envars else [] + version = namedtuple('Version', ['source', 'target'])(src_ver, dst_ver) + release = namedtuple('OS_release', ['release_id', 'version_id'])(release_id, src_ver) +@@ -85,9 +89,15 @@ class CurrentActorMocked(object): # pylint:disable=R0904 + self._common_tools_folder = '../../tools' + self._actor_folder = 'files' + self._actor_tools_folder = 'tools' +- self.configuration = namedtuple( +- 'configuration', ['architecture', 'kernel', 'leapp_env_vars', 'os_release', 'version', 'flavour'] +- )(arch, kernel, envarsList, release, version, flavour) ++ ++ if not supported_upgrade_paths: ++ supported_upgrade_paths = [IPUSourceToPossibleTargets(source_version=src_ver, target_versions=[dst_ver])] ++ ++ ipu_conf_fields = ['architecture', 'kernel', 'leapp_env_vars', 'os_release', ++ 'version', 'flavour', 'supported_upgrade_paths'] ++ config_type = namedtuple('configuration', ipu_conf_fields) ++ self.configuration = config_type(arch, kernel, envarsList, release, version, flavour, supported_upgrade_paths) ++ + self._msgs = msgs or [] + self.config = {} if config is None else config + diff --git a/repos/system_upgrade/common/models/activevendorlist.py b/repos/system_upgrade/common/models/activevendorlist.py new file mode 100644 index 00000000..de4056fb @@ -5424,6 +6451,43 @@ index 00000000..de4056fb +class ActiveVendorList(Model): + topic = VendorTopic + data = fields.List(fields.String()) +diff --git a/repos/system_upgrade/common/models/ipuconfig.py b/repos/system_upgrade/common/models/ipuconfig.py +index 6e7e21b5..0a16b603 100644 +--- a/repos/system_upgrade/common/models/ipuconfig.py ++++ b/repos/system_upgrade/common/models/ipuconfig.py +@@ -34,6 +34,21 @@ class Version(Model): + """Version of the target system. E.g. '8.2.'.""" + + ++class IPUSourceToPossibleTargets(Model): ++ """ ++ Represents upgrade paths from a source system version. ++ ++ This model is not supposed to be produced nor consumed directly by any actor. ++ """ ++ topic = SystemInfoTopic ++ ++ source_version = fields.String() ++ """Source system version.""" ++ ++ target_versions = fields.List(fields.String()) ++ """List of defined target system versions for the `source_version` system.""" ++ ++ + class IPUConfig(Model): + """ + IPU workflow configuration model +@@ -59,3 +74,10 @@ class IPUConfig(Model): + + flavour = fields.StringEnum(('default', 'saphana'), default='default') + """Flavour of the upgrade - Used to influence changes in supported source/target release""" ++ ++ supported_upgrade_paths = fields.List(fields.Model(IPUSourceToPossibleTargets)) ++ """ ++ List of supported upgrade paths. ++ ++ The list contains only upgrade paths for the `flavour` of the source system. ++ """ diff --git a/repos/system_upgrade/common/models/repositoriesmap.py b/repos/system_upgrade/common/models/repositoriesmap.py index 7192a60d..2144090d 100644 --- a/repos/system_upgrade/common/models/repositoriesmap.py @@ -5541,6 +6605,31 @@ index c076fe6b..2455a2f6 100644 UPGRADE_BLS_DIR = '/boot/upgrade-loader' CONTAINER_DOWNLOAD_DIR = '/tmp_pkg_download_dir' +diff --git a/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/actor.py b/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/actor.py +index 0002f6aa..6f1c8f43 100644 +--- a/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/actor.py ++++ b/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/actor.py +@@ -7,9 +7,9 @@ from leapp.tags import ChecksPhaseTag, IPUWorkflowTag + + class FirewalldCheckAllowZoneDrifting(Actor): + """ +- This actor will check if AllowZoneDrifiting=yes in firewalld.conf. This ++ This actor will check if AllowZoneDrifting=yes in firewalld.conf. This + option has been removed in RHEL-9 and behavior is as if +- AllowZoneDrifiting=no. ++ AllowZoneDrifting=no. + """ + + name = 'firewalld_check_allow_zone_drifting' +@@ -37,7 +37,7 @@ class FirewalldCheckAllowZoneDrifting(Actor): + reporting.Summary('Firewalld has enabled configuration option ' + '"{conf_key}" which has been removed in RHEL-9. ' + 'New behavior is as if "{conf_key}" was set to "no".'.format( +- conf_key='AllowZoneDrifiting')), ++ conf_key='AllowZoneDrifting')), + reporting.Severity(reporting.Severity.HIGH), + reporting.Groups([reporting.Groups.SANITY, reporting.Groups.FIREWALL]), + reporting.Groups([reporting.Groups.INHIBITOR]), diff --git a/repos/system_upgrade/el8toel9/actors/removeupgradeefientry/libraries/removeupgradeefientry.py b/repos/system_upgrade/el8toel9/actors/removeupgradeefientry/libraries/removeupgradeefientry.py index daa7b2ca..dd604d8b 100644 --- a/repos/system_upgrade/el8toel9/actors/removeupgradeefientry/libraries/removeupgradeefientry.py diff --git a/SPECS/leapp-repository.spec b/SPECS/leapp-repository.spec index 904feef..c16d8c0 100644 --- a/SPECS/leapp-repository.spec +++ b/SPECS/leapp-repository.spec @@ -53,7 +53,7 @@ py2_byte_compile "%1" "%2"} Epoch: 1 Name: leapp-repository Version: 0.22.0 -Release: 1%{?dist}.elevate.1 +Release: 1%{?dist}.elevate.2 Summary: Repositories for leapp License: ASL 2.0 @@ -320,6 +320,9 @@ done; # no files here %changelog +* Man May 05 2025 Yuriy Kohut - 0.22.0-1.elevate.2 +- Rebase upstream 0.22.0 to 57dce775de28615260189a6612fe65e44a7d3bc9 + * Thu Feb 27 2025 Yuriy Kohut - 0.22.0-1.elevate.1 - ELevate vendors support for upstream 0.22.0-1 version - Update ELevate patch: