From 765e54b2e6df9359e850f5786947a034ade66b22 Mon Sep 17 00:00:00 2001 From: Andrew Lukoshko Date: Thu, 13 Oct 2022 12:16:44 +0000 Subject: [PATCH] Update ELevate patch --- SOURCES/leapp-repository-0.16.0-elevate.patch | 969 +++++++++++++++++- SPECS/leapp-repository.spec | 5 +- 2 files changed, 952 insertions(+), 22 deletions(-) diff --git a/SOURCES/leapp-repository-0.16.0-elevate.patch b/SOURCES/leapp-repository-0.16.0-elevate.patch index 8a2462c..344c0fd 100644 --- a/SOURCES/leapp-repository-0.16.0-elevate.patch +++ b/SOURCES/leapp-repository-0.16.0-elevate.patch @@ -1,18 +1,73 @@ diff --git a/README.md b/README.md -index 4de458b..7b614d8 100644 +index 4de458b..8937111 100644 --- a/README.md +++ b/README.md -@@ -1,8 +1,7 @@ -+# Leapp Elevation Repository - **Before doing anything, please read - [Leapp framework documentation](https://leapp.readthedocs.io/).** +@@ -1,7 +1,61 @@ +-**Before doing anything, please read +-[Leapp framework documentation](https://leapp.readthedocs.io/).** ++# Leapp ELevate Repository ---- -- ++**Before doing anything, please read [Leapp framework documentation](https://leapp.readthedocs.io/).** ++ ++## Running ++Make sure your system is fully updated before starting the upgrade process. ++ ++```bash ++sudo yum update -y ++``` ++ ++Install `elevate-release` package with the project repo and GPG key. ++ ++`sudo yum install -y http://repo.almalinux.org/elevate/elevate-release-latest-el7.noarch.rpm` ++ ++Install leapp packages and migration data for the OS you want to upgrade. Possible options are: ++ - leapp-data-almalinux ++ - leapp-data-centos ++ - leapp-data-eurolinux ++ - leapp-data-oraclelinux ++ - leapp-data-rocky ++ ++`sudo yum install -y leapp-upgrade leapp-data-almalinux` ++ ++Start a preupgrade check. In the meantime, the Leapp utility creates a special /var/log/leapp/leapp-report.txt file that contains possible problems and recommended solutions. No rpm packages will be installed at this phase. ++ ++`sudo leapp preupgrade` ++ ++The preupgrade process may stall with the following message: ++> Inhibitor: Newest installed kernel not in use ++ ++Make sure your system is running the latest kernel before proceeding with the upgrade. If you updated the system recently, a reboot may be sufficient to do so. Otherwise, edit your Grub configuration accordingly. ++ ++> NOTE: In certain configurations, Leapp generates `/var/log/leapp/answerfile` with true/false questions. Leapp utility requires answers to all these questions in order to proceed with the upgrade. ++ ++Once the preupgrade process completes, the results will be contained in `/var/log/leapp/leapp-report.txt` file. ++It's advised to review the report and consider how the changes will affect your system. ++ ++Start an upgrade. You’ll be offered to reboot the system after this process is completed. ++ ++```bash ++sudo leapp upgrade ++sudo reboot ++``` ++ ++> NOTE: The upgrade process after the reboot may take a long time, up to 40-50 minutes, depending on the machine resources. If the machine remains unresponsive for more than 2 hours, assume the upgrade process failed during the post-reboot phase. ++> If it's still possible to access the machine in some way, for example, through remote VNC access, the logs containing the information on what went wrong are located in this folder: `/var/log/leapp` ++ ++A new entry in GRUB called ELevate-Upgrade-Initramfs will appear. The system will be automatically booted into it. Observe the update process in the console. ++ ++After the reboot, login into the system and check the migration report. Verify that the current OS is the one you need. ++ ++```bash ++cat /etc/redhat-release ++cat /etc/os-release ++``` ++ ++Check the leapp logs for .rpmnew configuration files that may have been created during the upgrade process. In some cases os-release or yum package files may not be replaced automatically, requiring the user to rename the .rpmnew files manually. + ## Troubleshooting - ### Where can I report an issue or RFE related to the framework or other actors? -@@ -11,6 +10,13 @@ +@@ -11,6 +65,13 @@ - Leapp framework: [https://github.com/oamg/leapp/issues/new/choose](https://github.com/oamg/leapp/issues/new/choose) - Leapp actors: [https://github.com/oamg/leapp-repository/issues/new/choose](https://github.com/oamg/leapp-repository/issues/new/choose) @@ -26,15 +81,15 @@ index 4de458b..7b614d8 100644 - When filing an issue, include: - Steps to reproduce the issue - *All files in /var/log/leapp* -@@ -25,7 +31,149 @@ +@@ -25,7 +86,232 @@ Then you may attach only the `leapp-logs.tgz` file. ### Where can I seek help? -We’ll gladly answer your questions and lead you to through any troubles with the -actor development. +We’ll gladly answer your questions and lead you to through any troubles with the actor development. - - You can reach us at IRC: `#leapp` on freenode. ++ ++You can reach the primary Leapp development team at IRC: `#leapp` on freenode. + +## Third-party integration + @@ -75,7 +130,7 @@ index 4de458b..7b614d8 100644 + - Red Hat update channel classification. Most of the time you won't need to use these. + +`mapping` establishes connections between described repositories. -+Each entryy in the list defines a mapping between major system versions, and contains the following elements: ++Each entry in the list defines a mapping between major system versions, and contains the following elements: +- source_major_version: major system version from which the system would be upgraded +- target_major_version: major system version to which the system would be elevated +- entries: the list of repository mappings @@ -178,6 +233,90 @@ index 4de458b..7b614d8 100644 +Files should be placed into the `vendors.d` subfolder if the data should be available for all elevation target OS variants, or into the `files//vendors.d/` if intended for a specific one. + +Alternatively, you can deploy the vendor files on a system prior to starting the upgrade. In this case, place the files into the folder `/etc/leapp/files/vendors.d/`. ++ ++## Adding complex changes (custom actors for migration) ++To perform any changes of arbitrary complexity during the migration process, add a component to the existing Leapp pipeline. ++ ++To begin, clone the code repository: https://github.com/AlmaLinux/leapp-repository ++For instructions on how to deploy a development enviroment, refer to [Leapp framework documentation](https://leapp.readthedocs.io/en/latest/devenv-install.html). ++ ++Create an actor inside the main system_upgrade leapp repository: ++ ++```bash ++cd ./leapp-repository/repos/system_upgrade/common ++snactor new-actor testactor ++``` ++ ++Alternatively, you can [create your own repository](https://leapp.readthedocs.io/en/latest/create-repository.html) in the system_upgrade folder, if you wish to keep your actors separate from others. ++Keep in mind that you’ll need to link all other repositories whose functions you will use. ++The created subfolder will contain the main Python file of your new actor. ++ ++The actor’s main class has three fields of interest: ++- consumes ++- produces ++- tags ++ ++consumes and produces defines the [data that the actor may receive or provide to other actors](https://leapp.readthedocs.io/en/latest/messaging.html). ++ ++Tags define the phase of the upgrade process during which the actor runs. ++All actors also must be assigned the `IPUWorkflowTag` to mark them as a part of the in-place upgrade process. ++The file `leapp-repository/repos/system_upgrade/common/workflows/inplace_upgrade.py` lists all phases of the elevation process. ++ ++### Submitting changes ++Changes you want to submit upstream should be sent through pull requests to repositories https://github.com/AlmaLinux/leapp-repository and https://github.com/AlmaLinux/leapp-data. ++The standard GitHub contribution process applies - fork the repository, make your changes inside of it, then submit the pull request to be reviewed. ++ ++### Example ++Suppose you would like to create an actor to copy a pre-supplied repository file into the upgraded system’s /etc/yum.repos.d. ++ ++First, you would create the new actor: ++``` ++cd ./leapp-repository/repos/system_upgrade/common ++snactor new-actor addcustomrepositories ++``` ++ ++Since you'd want to run this actor after the upgrade has successfully completed, you’d use the FirstBootPhase tag in its tags field. ++ ++```python ++ tags = (IPUWorkflowTag, FirstBootPhaseTag) ++``` ++ ++If you wanted to ensure that the actor only runs on AlmaLinux systems, and not on any target OS variant, you could check the release name by importing the corresponding function from leapp API: ++ ++```python ++from leapp.libraries.common.config import version ++ ++ def process(self): ++ # We only want to run this actor on AlmaLinux systems. ++ # current_version returns a tuple (release_name, version_value). ++ if (version.current_version()[0] == "almalinux"): ++ copy_custom_repo_files() ++``` ++ ++Files that are accessible by all actors in the repository should be placed into the files/ subdirectory. For the main leapp repository, that directory has the path leapp-repository/repos/system_upgrade/common/files. ++ ++Create the subfolder `custom-repos` to place repo files into, then finalize the actor’s code: ++ ++```python ++import os ++import os.path ++import shutil ++ ++from leapp.libraries.stdlib import api ++ ++CUSTOM_REPOS_FOLDER = 'custom-repos' ++REPO_ROOT_PATH = "/etc/yum.repos.d" ++ ++def copy_custom_repo_files(): ++ custom_repo_dir = api.get_common_folder_path(CUSTOM_REPOS_FOLDER) ++ ++ for repofile in os.listdir(custom_repo_dir): ++ full_repo_path = os.path.join(custom_repo_dir, repofile) ++ shutil.copy(full_repo_path, REPO_ROOT_PATH) ++``` + +-You can reach us at IRC: `#leapp` on freenode. ++Refer to existing actors and [Leapp documentation](https://leapp.readthedocs.io/) to use more complex functionality in your actors. diff --git a/commands/command_utils.py b/commands/command_utils.py index da62c50..a8e7d76 100644 --- a/commands/command_utils.py @@ -191,6 +330,577 @@ index da62c50..a8e7d76 100644 def check_version(version): +diff --git a/repos/system_upgrade/cloudlinux/.leapp/info b/repos/system_upgrade/cloudlinux/.leapp/info +new file mode 100644 +index 0000000..1f16b9f +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/.leapp/info +@@ -0,0 +1 @@ ++{"name": "cloudlinux", "id": "427ddd90-9b5e-4400-b21e-73d77791f175", "repos": ["644900a5-c347-43a3-bfab-f448f46d9647", "c47fbc3d-ae38-416e-9176-7163d67d94f6", "efcf9016-f2d1-4609-9329-a298e6587b3c"]} +\ No newline at end of file +diff --git a/repos/system_upgrade/cloudlinux/.leapp/leapp.conf b/repos/system_upgrade/cloudlinux/.leapp/leapp.conf +new file mode 100644 +index 0000000..b459134 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/.leapp/leapp.conf +@@ -0,0 +1,6 @@ ++ ++[repositories] ++repo_path=${repository:root_dir} ++ ++[database] ++path=${repository:state_dir}/leapp.db +diff --git a/repos/system_upgrade/cloudlinux/actors/addcustomrepositories/actor.py b/repos/system_upgrade/cloudlinux/actors/addcustomrepositories/actor.py +new file mode 100644 +index 0000000..783e347 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/addcustomrepositories/actor.py +@@ -0,0 +1,21 @@ ++from leapp.actors import Actor ++from leapp.tags import FirstBootPhaseTag, IPUWorkflowTag ++from leapp.libraries.common.cllaunch import run_on_cloudlinux ++from leapp.libraries.actor.addcustomrepositories import add_custom ++ ++ ++class AddCustomRepositories(Actor): ++ """ ++ Move the files inside the custom-repos folder of this leapp repository into the /etc/yum.repos.d repository. ++ """ ++ ++ name = 'add_custom_repositories' ++ consumes = () ++ produces = () ++ tags = (IPUWorkflowTag, FirstBootPhaseTag) ++ ++ @run_on_cloudlinux ++ def process(self): ++ # We only want to run this actor on CloudLinux systems. ++ # current_version returns a tuple (release_name, version_value). ++ add_custom(self.log) +diff --git a/repos/system_upgrade/cloudlinux/actors/addcustomrepositories/libraries/addcustomrepositories.py b/repos/system_upgrade/cloudlinux/actors/addcustomrepositories/libraries/addcustomrepositories.py +new file mode 100644 +index 0000000..74ba425 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/addcustomrepositories/libraries/addcustomrepositories.py +@@ -0,0 +1,26 @@ ++import os ++import os.path ++import shutil ++import logging ++ ++from leapp.libraries.stdlib import api ++ ++CUSTOM_REPOS_FOLDER = 'custom-repos' ++REPO_ROOT_PATH = "/etc/yum.repos.d" ++ ++ ++def add_custom(log): ++ # type: (logging.Logger) -> None ++ custom_repo_dir = api.get_common_folder_path(CUSTOM_REPOS_FOLDER) ++ repofiles = os.listdir(custom_repo_dir) ++ ++ # If any components are missing, halt. ++ if not repofiles or not custom_repo_dir: ++ return ++ ++ for repofile in repofiles: ++ full_repo_path = os.path.join(custom_repo_dir, repofile) ++ ++ log.debug("Copying repo file {} to {}".format(repofile, REPO_ROOT_PATH)) ++ ++ shutil.copy(full_repo_path, REPO_ROOT_PATH) +diff --git a/repos/system_upgrade/cloudlinux/actors/checkcllicense/actor.py b/repos/system_upgrade/cloudlinux/actors/checkcllicense/actor.py +new file mode 100644 +index 0000000..2c935d7 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/checkcllicense/actor.py +@@ -0,0 +1,41 @@ ++from leapp.actors import Actor ++from leapp import reporting ++from leapp.tags import ChecksPhaseTag, IPUWorkflowTag ++from leapp.libraries.stdlib import CalledProcessError, run ++from leapp.libraries.common.cllaunch import run_on_cloudlinux ++ ++import os ++ ++ ++class CheckClLicense(Actor): ++ """ ++ Check if the server has a CL license ++ """ ++ ++ name = 'check_cl_license' ++ consumes = () ++ produces = () ++ tags = (ChecksPhaseTag, IPUWorkflowTag) ++ ++ system_id_path = '/etc/sysconfig/rhn/systemid' ++ rhn_check_bin = '/usr/sbin/rhn_check' ++ ++ @run_on_cloudlinux ++ def process(self): ++ res = None ++ if os.path.exists(self.system_id_path): ++ res = run([self.rhn_check_bin]) ++ self.log.debug('rhn_check result: %s', res) ++ if not res or res['exit_code'] != 0 or res['stderr']: ++ title = 'Server does not have an active CloudLinux license' ++ summary = 'Server does not have an active CloudLinux license. This renders key CloudLinux packages ' \ ++ 'inaccessible, inhibiting the upgrade process.' ++ remediation = 'Activate a CloudLinux license on this machine before running Leapp again.' ++ reporting.create_report([ ++ reporting.Title(title), ++ reporting.Summary(summary), ++ reporting.Severity(reporting.Severity.HIGH), ++ reporting.Tags([reporting.Tags.OS_FACTS]), ++ reporting.Flags([reporting.Flags.INHIBITOR]), ++ reporting.Remediation(hint=remediation), ++ ]) +diff --git a/repos/system_upgrade/cloudlinux/actors/checkrhnclienttools/actor.py b/repos/system_upgrade/cloudlinux/actors/checkrhnclienttools/actor.py +new file mode 100644 +index 0000000..142ec22 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/checkrhnclienttools/actor.py +@@ -0,0 +1,57 @@ ++from leapp.actors import Actor ++from leapp import reporting ++from leapp.tags import ChecksPhaseTag, IPUWorkflowTag ++from leapp.libraries.common.cllaunch import run_on_cloudlinux ++ ++from leapp.libraries.actor.version import ( ++ Version, VersionParsingError, ++) ++ ++import subprocess ++ ++ ++class CheckRhnClientToolsVersion(Actor): ++ """ ++ Check the rhn-client-tools package version ++ """ ++ ++ name = 'check_rhn_client_tools_version' ++ consumes = () ++ produces = () ++ tags = (ChecksPhaseTag, IPUWorkflowTag) ++ ++ minimal_version = Version('2.0.2') ++ minimal_release_int = 43 ++ minimal_release = '%s.el7.cloudlinux' % minimal_release_int ++ ++ @run_on_cloudlinux ++ def process(self): ++ title, summary, remediation = None, None, None ++ # ex: ++ # Version : 2.0.2 ++ # Release : 43.el7.cloudlinux ++ # res is: b'2.0.2\n43.el7.cloudlinux\n' ++ cmd = "yum info installed rhn-client-tools | grep '^Version' -A 1 | awk '{print $3}'" ++ res = subprocess.check_output(cmd, shell=True) ++ rhn_version, rhn_release = res.decode().split() ++ self.log.info('Current rhn-client-tools version: "%s"', rhn_version) ++ try: ++ current_version = Version(rhn_version) ++ except VersionParsingError: ++ title = 'rhn-client-tools: package is not installed' ++ summary = 'rhn-client-tools package is required to perform elevation.' ++ remediation = 'Install rhn-client-tools "%s" version before running Leapp again.' % self.minimal_version ++ else: ++ if current_version < self.minimal_version or int(rhn_release.split('.')[0]) < self.minimal_release_int: ++ title = 'rhn-client-tools: package version is too low' ++ summary = 'Current version of the rhn-client-tools package has no capability to perform elevation.' ++ remediation = 'Update rhn-client-tools to "%s %s" version before running Leapp again.' % (self.minimal_version, self.minimal_release) ++ if title: ++ reporting.create_report([ ++ reporting.Title(title), ++ reporting.Summary(summary), ++ reporting.Severity(reporting.Severity.HIGH), ++ reporting.Tags([reporting.Tags.OS_FACTS]), ++ reporting.Flags([reporting.Flags.INHIBITOR]), ++ reporting.Remediation(hint=remediation), ++ ]) +diff --git a/repos/system_upgrade/cloudlinux/actors/checkrhnclienttools/libraries/version.py b/repos/system_upgrade/cloudlinux/actors/checkrhnclienttools/libraries/version.py +new file mode 100644 +index 0000000..149bce2 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/checkrhnclienttools/libraries/version.py +@@ -0,0 +1,46 @@ ++from six import reraise as raise_ ++import sys ++ ++ ++class VersionException(Exception): ++ pass ++ ++ ++class VersionParsingError(VersionException): ++ pass ++ ++ ++class Version(object): ++ def __init__(self, version): ++ self._raw = version ++ try: ++ self.value = tuple( ++ map(lambda x: int(x), version.split('.')) ++ ) ++ except Exception: ++ tb = sys.exc_info()[2] ++ raise_(VersionParsingError, 'failed to parse version: "%s"' % self._raw, tb) ++ ++ def __eq__(self, other): ++ return self.value == other.value ++ ++ def __gt__(self, other): ++ return any( ++ [v[0] > v[1] for v in zip(self.value, other.value)] ++ ) ++ ++ def __ge__(self, other): ++ return all( ++ [v[0] >= v[1] for v in zip(self.value, other.value)] ++ ) ++ ++ def __lt__(self, other): ++ return any( ++ [v[0] < v[1] for v in zip(self.value, other.value)] ++ ) ++ ++ def __le__(self, other): ++ return all( ++ [v[0] <= v[1] for v in zip(self.value, other.value)] ++ ) ++ +diff --git a/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py b/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py +new file mode 100644 +index 0000000..d832d34 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py +@@ -0,0 +1,38 @@ ++from leapp.actors import Actor ++from leapp import reporting ++from leapp.tags import ChecksPhaseTag, IPUWorkflowTag ++from leapp.libraries.common.cllaunch import run_on_cloudlinux ++ ++ ++class CheckRhnVersionOverride(Actor): ++ """ ++ Check if the up2date versionOverride option has not been set. ++ """ ++ ++ name = 'check_rhn_version_override' ++ consumes = () ++ produces = () ++ tags = (ChecksPhaseTag, IPUWorkflowTag) ++ ++ @run_on_cloudlinux ++ def process(self): ++ up2date_config = '/etc/sysconfig/rhn/up2date' ++ with open(up2date_config, 'r') as f: ++ config_data = f.readlines() ++ for line in config_data: ++ if line.startswith('versionOverride=') and line != 'versionOverride=': ++ title = 'RHN up2date: versionOverride not empty' ++ summary = ('The RHN config file up2date has a set value of the versionOverride option.' ++ ' This value will get overwritten by the upgrade process, and non-supported values' ++ ' carry a risk of causing issues during the upgrade.') ++ remediation = ('Remove the versionOverride value from the up2date config file' ++ ' before running Leapp again.') ++ reporting.create_report([ ++ reporting.Title(title), ++ reporting.Summary(summary), ++ reporting.Severity(reporting.Severity.HIGH), ++ reporting.Tags([reporting.Tags.OS_FACTS]), ++ reporting.Flags([reporting.Flags.INHIBITOR]), ++ reporting.Remediation(hint=remediation), ++ reporting.RelatedResource('file', '/etc/sysconfig/rhn/up2date') ++ ]) +diff --git a/repos/system_upgrade/cloudlinux/actors/checkup2dateconfig/actor.py b/repos/system_upgrade/cloudlinux/actors/checkup2dateconfig/actor.py +new file mode 100644 +index 0000000..bfc0642 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/checkup2dateconfig/actor.py +@@ -0,0 +1,48 @@ ++from leapp.actors import Actor ++from leapp.tags import FirstBootPhaseTag, IPUWorkflowTag ++from leapp import reporting ++from leapp.libraries.common.cllaunch import run_on_cloudlinux ++ ++import os ++ ++ ++class CheckUp2dateConfig(Actor): ++ """ ++ Move up2date.rpmnew config to the old one's place ++ """ ++ ++ name = 'check_up2date_config' ++ consumes = () ++ produces = () ++ tags = (FirstBootPhaseTag, IPUWorkflowTag) ++ ++ original = '/etc/sysconfig/rhn/up2date' ++ new = original + '.rpmnew' ++ ++ @run_on_cloudlinux ++ def process(self): ++ """ ++ For some reason we get new .rpmnew file instead of the modified `original` ++ This actor tries to save the old `serverURL` parameter to new config and move new instead of old one ++ """ ++ replace, old_lines, new_lines = None, None, None ++ if os.path.exists(self.new): ++ self.log.warning('"%s" config found, trying to replace the old one', self.new) ++ with open(self.original) as o, open(self.new) as n: ++ old_lines = o.readlines() ++ new_lines = n.readlines() ++ for l in old_lines: ++ if l.startswith('serverURL=') and l not in new_lines: ++ replace = l ++ break ++ if replace: ++ for i, line in enumerate(new_lines): ++ if line.startswith('serverURL='): ++ new_lines[i] = replace ++ self.log.warning('"serverURL" parameter will be saved as "%s"', line.strip()) ++ break ++ with open(self.original, 'w') as f: ++ f.writelines(new_lines) ++ self.log.info('"%s" config is overwritten by the contents of "%s"', self.original, self.new) ++ os.unlink(self.new) ++ self.log.info('"%s" config deleted', self.new) +diff --git a/repos/system_upgrade/cloudlinux/actors/clearpackageconflicts/actor.py b/repos/system_upgrade/cloudlinux/actors/clearpackageconflicts/actor.py +new file mode 100644 +index 0000000..cd6801b +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/clearpackageconflicts/actor.py +@@ -0,0 +1,36 @@ ++import os ++import errno ++import shutil ++ ++from leapp.actors import Actor ++from leapp.libraries.common.rpms import has_package ++from leapp.models import InstalledRPM ++from leapp.tags import DownloadPhaseTag, IPUWorkflowTag ++from leapp.libraries.common.cllaunch import run_on_cloudlinux ++ ++class ClearPackageConflicts(Actor): ++ """ ++ Remove several python package files manually to resolve conflicts between versions of packages to be upgraded. ++ """ ++ ++ name = 'clear_package_conflicts' ++ consumes = (InstalledRPM,) ++ produces = () ++ tags = (DownloadPhaseTag.Before, IPUWorkflowTag) ++ ++ @run_on_cloudlinux ++ def process(self): ++ problem_packages = ["alt-python37-six", "alt-python37-pytz"] ++ problem_packages_installed = any([has_package(InstalledRPM, pkg) for pkg in problem_packages]) ++ ++ if problem_packages_installed: ++ problem_dirs = [ ++ "/opt/alt/python37/lib/python3.7/site-packages/six-1.15.0-py3.7.egg-info", ++ "/opt/alt/python37/lib/python3.7/site-packages/pytz-2017.2-py3.7.egg-info"] ++ for p_dir in problem_dirs: ++ try: ++ if os.path.isdir(p_dir): ++ shutil.rmtree(p_dir) ++ except OSError as e: ++ if e.errno != errno.ENOENT: ++ raise +diff --git a/repos/system_upgrade/cloudlinux/actors/enableyumspacewalkplugin/actor.py b/repos/system_upgrade/cloudlinux/actors/enableyumspacewalkplugin/actor.py +new file mode 100644 +index 0000000..1e353b1 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/enableyumspacewalkplugin/actor.py +@@ -0,0 +1,55 @@ ++from leapp.actors import Actor ++from leapp.tags import FirstBootPhaseTag, IPUWorkflowTag ++from leapp import reporting ++from leapp.libraries.common.cllaunch import run_on_cloudlinux ++ ++try: ++ # py2 ++ import ConfigParser as configparser ++ ParserClass = configparser.SafeConfigParser ++except Exception: ++ # py3 ++ import configparser ++ ParserClass = configparser.ConfigParser ++ ++ ++class EnableYumSpacewalkPlugin(Actor): ++ """ ++ Enable yum spacewalk plugin if it's disabled ++ Required for the CLN channel functionality to work properly ++ """ ++ ++ name = 'enable_yum_spacewalk_plugin' ++ consumes = () ++ produces = () ++ tags = (FirstBootPhaseTag, IPUWorkflowTag) ++ ++ config = '/etc/yum/pluginconf.d/spacewalk.conf' ++ ++ @run_on_cloudlinux ++ def process(self): ++ summary = 'yum spacewalk plugin must be enabled for the CLN channels to work properly. ' \ ++ 'Please make sure it is enabled. Default config path is "%s"' % self.config ++ title = None ++ ++ parser = ParserClass(allow_no_value=True) ++ try: ++ red = parser.read(self.config) ++ if not red: ++ title = 'yum spacewalk plugin config not found' ++ if parser.get('main', 'enabled') != '1': ++ parser.set('main', 'enabled', '1') ++ with open(self.config, 'w') as f: ++ parser.write(f) ++ self.log.info('yum spacewalk plugin enabled') ++ return ++ except Exception as e: ++ title = 'yum spacewalk plugin config error: %s' % e ++ ++ if title: ++ reporting.create_report([ ++ reporting.Title(title), ++ reporting.Summary(summary), ++ reporting.Severity(reporting.Severity.MEDIUM), ++ reporting.Tags([reporting.Tags.SANITY]) ++ ]) +diff --git a/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py b/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py +new file mode 100644 +index 0000000..21b2164 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py +@@ -0,0 +1,25 @@ ++from leapp.actors import Actor ++from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag ++from leapp.libraries.common.cllaunch import run_on_cloudlinux ++ ++ ++class ResetRhnVersionOverride(Actor): ++ """ ++ Reset the versionOverride value in the RHN up2date config to empty. ++ """ ++ ++ name = 'reset_rhn_version_override' ++ consumes = () ++ produces = () ++ tags = (FinalizationPhaseTag, IPUWorkflowTag) ++ ++ @run_on_cloudlinux ++ def process(self): ++ up2date_config = '/etc/sysconfig/rhn/up2date' ++ with open(up2date_config, 'r') as f: ++ config_data = f.readlines() ++ for line in config_data: ++ if line.startswith('versionOverride='): ++ line = 'versionOverride=' ++ with open(up2date_config, 'w') as f: ++ f.writelines(config_data) +diff --git a/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py b/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py +new file mode 100644 +index 0000000..ae01af1 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py +@@ -0,0 +1,33 @@ ++from leapp.actors import Actor ++from leapp.libraries.stdlib import api ++from leapp.tags import DownloadPhaseTag, IPUWorkflowTag ++from leapp.libraries.stdlib import CalledProcessError, run ++from leapp.libraries.common.cllaunch import run_on_cloudlinux ++ ++ ++class SwitchClnChannel(Actor): ++ """ ++ Switch CLN channel from 7 to 8 to be able to download upgrade packages ++ """ ++ ++ name = 'switch_cln_channel' ++ consumes = () ++ produces = () ++ tags = (IPUWorkflowTag, DownloadPhaseTag.Before) ++ ++ switch_bin = '/usr/sbin/cln-switch-channel' ++ ++ @run_on_cloudlinux ++ def process(self): ++ switch_cmd = [self.switch_bin, '-t', '8', '-o', '-f'] ++ yum_clean_cmd = ['yum', 'clean', 'all'] ++ update_release_cmd = ['yum', 'update', '-y', 'cloudlinux-release'] ++ try: ++ res = run(switch_cmd) ++ self.log.debug('Command "%s" result: %s', switch_cmd, res) ++ res = run(yum_clean_cmd) # required to update the repolist ++ self.log.debug('Command "%s" result: %s', yum_clean_cmd, res) ++ res = run(update_release_cmd) ++ self.log.debug('Command "%s" result: %s', update_release_cmd, res) ++ except OSError as e: ++ api.current_logger().error('Could not call RHN command: Message: %s', str(e), exc_info=True) +diff --git a/repos/system_upgrade/cloudlinux/actors/updatecagefs/actor.py b/repos/system_upgrade/cloudlinux/actors/updatecagefs/actor.py +new file mode 100644 +index 0000000..71e3c66 +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/actors/updatecagefs/actor.py +@@ -0,0 +1,36 @@ ++import os ++ ++from leapp.actors import Actor ++from leapp.libraries.stdlib import run, CalledProcessError ++from leapp.reporting import Report, create_report ++from leapp import reporting ++from leapp.tags import FirstBootPhaseTag, IPUWorkflowTag ++from leapp.libraries.common.cllaunch import run_on_cloudlinux ++ ++ ++class UpdateCagefs(Actor): ++ """ ++ Force update of cagefs. ++ ++ cagefs should reflect massive changes in system made in previous phases ++ """ ++ ++ name = 'update_cagefs' ++ consumes = () ++ produces = () ++ tags = (FirstBootPhaseTag, IPUWorkflowTag) ++ ++ @run_on_cloudlinux ++ def process(self): ++ if os.path.exists('/usr/sbin/cagefsctl'): ++ try: ++ run(['/usr/sbin/cagefsctl', '--force-update'], checked=True) ++ self.log.info('cagefs update was successful') ++ except CalledProcessError as e: ++ # cagefsctl prints errors in stdout ++ self.log.error(e.stdout) ++ self.log.error('Command "cagefsctl --force-update" finished with exit code {}, ' ++ 'the filesystem inside cagefs may be out-of-date.\n' ++ 'Check cagefsctl output above and in /var/log/cagefs-update.log, ' ++ 'rerun "cagefsctl --force-update" after fixing the issues.'.format(e.exit_code) ++ ) +diff --git a/repos/system_upgrade/cloudlinux/libraries/cllaunch.py b/repos/system_upgrade/cloudlinux/libraries/cllaunch.py +new file mode 100644 +index 0000000..6cbab5d +--- /dev/null ++++ b/repos/system_upgrade/cloudlinux/libraries/cllaunch.py +@@ -0,0 +1,11 @@ ++import functools ++from leapp.libraries.common.config import version ++ ++ ++def run_on_cloudlinux(func): ++ @functools.wraps(func) ++ def wrapper(*args, **kwargs): ++ if (version.current_version()[0] != "cloudlinux"): ++ return ++ return func(*args, **kwargs) ++ return wrapper diff --git a/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py b/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py index a2cede0..5ff1c76 100644 --- a/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py @@ -308,10 +1018,10 @@ index 14bd6e3..f6adacf 100755 # This should not happen as /etc/initrd-release is supposed to have API # stability, but check is better than broken system. diff --git a/repos/system_upgrade/common/actors/efibootorderfix/finalization/actor.py b/repos/system_upgrade/common/actors/efibootorderfix/finalization/actor.py -index f42909f..caa94e0 100644 +index f42909f..9f532eb 100644 --- a/repos/system_upgrade/common/actors/efibootorderfix/finalization/actor.py +++ b/repos/system_upgrade/common/actors/efibootorderfix/finalization/actor.py -@@ -1,17 +1,78 @@ +@@ -1,17 +1,86 @@ +import os + +from leapp.libraries.stdlib import run, api @@ -389,8 +1099,16 @@ index f42909f..caa94e0 100644 + with open('/proc/mounts', 'r') as fp: + for line in fp: + if '/boot/efi' in line: -+ efidev = line.split(' ', 1)[0] -+ run(['/sbin/efibootmgr', '-c', '-d', efidev, '-p 1', '-l', bootmgr_path, '-L', efi_bootentry_label]) ++ efidevpath = line.split(' ', 1)[0] ++ efidev = efidevpath.split('/')[-1] ++ if os.path.exists('/proc/mdstat'): ++ with open('/proc/mdstat', 'r') as mds: ++ for line in mds: ++ if line.startswith(efidev): ++ mddev = line.split(' ')[-1] ++ newefidev = mddev.split('[', 1)[0] ++ efidevpath = efidevpath.replace(efidev, newefidev) ++ run(['/sbin/efibootmgr', '-c', '-d', efidevpath, '-p 1', '-l', bootmgr_path, '-L', efi_bootentry_label]) + + if not has_grub_cfg: + run(['/sbin/grub2-mkconfig', '-o', grub_cfg_path]) @@ -866,6 +1584,107 @@ index 3c0b04b..3480432 100644 @pytest.fixture def adjust_cwd(): previous_cwd = os.getcwd() +diff --git a/repos/system_upgrade/common/actors/scancustomrepofile/actor.py b/repos/system_upgrade/common/actors/scancustomrepofile/actor.py +index d46018f..bb49b4e 100644 +--- a/repos/system_upgrade/common/actors/scancustomrepofile/actor.py ++++ b/repos/system_upgrade/common/actors/scancustomrepofile/actor.py +@@ -1,6 +1,9 @@ + from leapp.actors import Actor + from leapp.libraries.actor import scancustomrepofile +-from leapp.models import CustomTargetRepository, CustomTargetRepositoryFile ++from leapp.models import ( ++ CustomTargetRepository, ++ CustomTargetRepositoryFile, ++) + from leapp.tags import FactsPhaseTag, IPUWorkflowTag + + +@@ -18,7 +21,7 @@ class ScanCustomRepofile(Actor): + If the file doesn't exist, nothing happens. + """ + +- name = 'scan_custom_repofile' ++ name = "scan_custom_repofile" + consumes = () + produces = (CustomTargetRepository, CustomTargetRepositoryFile) + tags = (FactsPhaseTag, IPUWorkflowTag) +diff --git a/repos/system_upgrade/common/actors/scancustomrepofile/libraries/scancustomrepofile.py b/repos/system_upgrade/common/actors/scancustomrepofile/libraries/scancustomrepofile.py +index c294193..c0820eb 100644 +--- a/repos/system_upgrade/common/actors/scancustomrepofile/libraries/scancustomrepofile.py ++++ b/repos/system_upgrade/common/actors/scancustomrepofile/libraries/scancustomrepofile.py +@@ -18,18 +18,27 @@ def process(): + """ + if not os.path.isfile(CUSTOM_REPO_PATH): + api.current_logger().debug( +- "The {} file doesn't exist. Nothing to do." +- .format(CUSTOM_REPO_PATH)) ++ "The {} file doesn't exist. Nothing to do.".format(CUSTOM_REPO_PATH) ++ ) + return +- api.current_logger().info("The {} file exists.".format(CUSTOM_REPO_PATH)) ++ + repofile = repofileutils.parse_repofile(CUSTOM_REPO_PATH) + if not repofile.data: ++ api.current_logger().info( ++ "The {} file exists, but is empty. Nothing to do.".format(CUSTOM_REPO_PATH) ++ ) + return + api.produce(CustomTargetRepositoryFile(file=CUSTOM_REPO_PATH)) ++ + for repo in repofile.data: +- api.produce(CustomTargetRepository( +- repoid=repo.repoid, +- name=repo.name, +- baseurl=repo.baseurl, +- enabled=repo.enabled, +- )) ++ api.produce( ++ CustomTargetRepository( ++ repoid=repo.repoid, ++ name=repo.name, ++ baseurl=repo.baseurl, ++ enabled=repo.enabled, ++ ) ++ ) ++ api.current_logger().info( ++ "The {} file exists, custom repositories loaded.".format(CUSTOM_REPO_PATH) ++ ) +diff --git a/repos/system_upgrade/common/actors/scancustomrepofile/tests/test_scancustomrepofile.py b/repos/system_upgrade/common/actors/scancustomrepofile/tests/test_scancustomrepofile.py +index 4ea33a2..aaec273 100644 +--- a/repos/system_upgrade/common/actors/scancustomrepofile/tests/test_scancustomrepofile.py ++++ b/repos/system_upgrade/common/actors/scancustomrepofile/tests/test_scancustomrepofile.py +@@ -4,8 +4,13 @@ from leapp.libraries.actor import scancustomrepofile + from leapp.libraries.common import repofileutils + from leapp.libraries.common.testutils import produce_mocked + from leapp.libraries.stdlib import api +-from leapp.models import CustomTargetRepository, CustomTargetRepositoryFile, RepositoryData, RepositoryFile + ++from leapp.models import ( ++ CustomTargetRepository, ++ CustomTargetRepositoryFile, ++ RepositoryData, ++ RepositoryFile, ++) + + _REPODATA = [ + RepositoryData(repoid="repo1", name="repo1name", baseurl="repo1url", enabled=True), +@@ -57,7 +62,7 @@ def test_valid_repofile_exists(monkeypatch): + monkeypatch.setattr(repofileutils, 'parse_repofile', _mocked_parse_repofile) + monkeypatch.setattr(api, 'current_logger', LoggerMocked()) + scancustomrepofile.process() +- msg = "The {} file exists.".format(scancustomrepofile.CUSTOM_REPO_PATH) ++ msg = "The {} file exists, custom repositories loaded.".format(scancustomrepofile.CUSTOM_REPO_PATH) + assert api.current_logger.infomsg == msg + assert api.produce.called == len(_CUSTOM_REPOS) + 1 + assert _CUSTOM_REPO_FILE_MSG in api.produce.model_instances +@@ -73,6 +78,6 @@ def test_empty_repofile_exists(monkeypatch): + monkeypatch.setattr(repofileutils, 'parse_repofile', _mocked_parse_repofile) + monkeypatch.setattr(api, 'current_logger', LoggerMocked()) + scancustomrepofile.process() +- msg = "The {} file exists.".format(scancustomrepofile.CUSTOM_REPO_PATH) ++ 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/scanvendorrepofiles/actor.py b/repos/system_upgrade/common/actors/scanvendorrepofiles/actor.py new file mode 100644 index 0000000..6c7f3a3 @@ -1356,7 +2175,7 @@ index 03f3cd4..7fcb6aa 100644 diff --git a/repos/system_upgrade/common/libraries/dnfplugin.py b/repos/system_upgrade/common/libraries/dnfplugin.py -index 4010e9f..00323a7 100644 +index 4010e9f..fdb8e66 100644 --- a/repos/system_upgrade/common/libraries/dnfplugin.py +++ b/repos/system_upgrade/common/libraries/dnfplugin.py @@ -4,6 +4,8 @@ import json @@ -1368,19 +2187,21 @@ index 4010e9f..00323a7 100644 from leapp.exceptions import StopActorExecutionError from leapp.libraries.common import dnfconfig, guards, mounting, overlaygen, rhsm, utils from leapp.libraries.common.config.version import get_target_major_version, get_target_version -@@ -213,10 +215,15 @@ def _transaction(context, stage, target_repoids, tasks, plugin_info, test=False, +@@ -213,10 +215,17 @@ def _transaction(context, stage, target_repoids, tasks, plugin_info, test=False, message='Failed to execute dnf. Reason: {}'.format(str(e)) ) except CalledProcessError as e: ++ err_stdout = e.stdout ++ err_stderr = e.stderr + if six.PY2: -+ e.stdout = e.stdout.encode('utf-8', 'xmlcharrefreplace') -+ e.stderr = e.stdout.encode('utf-8', 'xmlcharrefreplace') ++ err_stdout = e.stdout.encode('utf-8', 'xmlcharrefreplace') ++ err_stderr = e.stderr.encode('utf-8', 'xmlcharrefreplace') + api.current_logger().error('DNF execution failed: ') raise StopActorExecutionError( message='DNF execution failed with non zero exit code.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}'.format( - stdout=e.stdout, stderr=e.stderr) -+ stdout=e.stdout, stderr=e.stderr ++ stdout=err_stdout, stderr=err_stderr + ) ) finally: @@ -1629,3 +2450,109 @@ index 0000000..014b7af + +class VendorTopic(Topic): + name = 'vendor_topic' +diff --git a/repos/system_upgrade/el7toel8/actors/networkmanagerupdateconnections/actor.py b/repos/system_upgrade/el7toel8/actors/networkmanagerupdateconnections/actor.py +index 69ca0f0..c488eb0 100644 +--- a/repos/system_upgrade/el7toel8/actors/networkmanagerupdateconnections/actor.py ++++ b/repos/system_upgrade/el7toel8/actors/networkmanagerupdateconnections/actor.py +@@ -2,6 +2,7 @@ from leapp.actors import Actor + from leapp.libraries.stdlib import CalledProcessError, run + from leapp.models import NetworkManagerConfig + from leapp.tags import FirstBootPhaseTag, IPUWorkflowTag ++from leapp import reporting + + + class NetworkManagerUpdateConnections(Actor): +@@ -26,8 +27,22 @@ class NetworkManagerUpdateConnections(Actor): + return + + try: +- r = run(['/usr/bin/python3', 'tools/nm-update-client-ids.py'])['stdout'] +- self.log.info('Updated client-ids: {}'.format(r)) ++ r = run(['/usr/bin/python3', 'tools/nm-update-client-ids.py']) ++ if r['exit_code'] == 79: ++ title = 'NetworkManager connection update failed - PyGObject bindings for NetworkManager not found.' ++ summary = 'When using dhcp=dhclient on Red Hat Enterprise Linux 7, a non-hexadecimal ' \ ++ 'client-id (a string) is sent on the wire as is. On Red Hat Enterprise Linux 8, a zero ' \ ++ 'byte is prepended to string-only client-ids. If you wish to preserve the RHEL 7 behaviour, ' \ ++ 'you may want to convert your client-ids to hexadecimal form manually.' ++ reporting.create_report([ ++ reporting.Title(title), ++ reporting.Summary(summary), ++ reporting.Severity(reporting.Severity.MEDIUM), ++ reporting.Tags([reporting.Tags.NETWORK]) ++ ]) ++ continue ++ ++ self.log.info('Updated client-ids: {}'.format(r['stdout'])) + except (OSError, CalledProcessError) as e: + self.log.warning('Error calling nm-update-client-ids script: {}'.format(e)) + +diff --git a/repos/system_upgrade/el7toel8/actors/networkmanagerupdateconnections/tools/nm-update-client-ids.py b/repos/system_upgrade/el7toel8/actors/networkmanagerupdateconnections/tools/nm-update-client-ids.py +index 923bf80..9972204 100755 +--- a/repos/system_upgrade/el7toel8/actors/networkmanagerupdateconnections/tools/nm-update-client-ids.py ++++ b/repos/system_upgrade/el7toel8/actors/networkmanagerupdateconnections/tools/nm-update-client-ids.py +@@ -3,12 +3,26 @@ from __future__ import print_function + import sys + + import gi +-gi.require_version('NM', '1.0') ++ ++try: ++ gi.require_version("NM", "1.0") ++except ValueError: ++ # If we're missing NetworkManager-libnm, the script won't function. ++ print( ++ "PyGObject bindings for NetworkManager not found - do you have NetworkManager-libnm installed?" ++ ) ++ print( ++ "If you have dhcp=dhclient, you may need to convert your string-formatted client IDs to hexadecimal" ++ "to preserve the format they're sent on the wire with. Otherwise, they will now have a zero byte" ++ "prepended while being sent." ++ ) ++ sys.exit(79) ++ + from gi.repository import NM # noqa: E402; pylint: disable=wrong-import-position + + + def is_hexstring(s): +- arr = s.split(':') ++ arr = s.split(":") + for a in arr: + if len(a) != 1 and len(a) != 2: + return False +@@ -21,8 +35,8 @@ def is_hexstring(s): + + client = NM.Client.new(None) + if not client: +- print('Cannot create NM client instance') +- sys.exit(0) ++ print("Cannot create NM client instance") ++ sys.exit(79) + + processed = 0 + changed = 0 +@@ -35,15 +49,20 @@ for c in client.get_connections(): + client_id = s_ip4.get_dhcp_client_id() + if client_id is not None: + if not is_hexstring(client_id): +- new_client_id = ':'.join(hex(ord(x))[2:] for x in client_id) ++ new_client_id = ":".join(hex(ord(x))[2:] for x in client_id) + s_ip4.set_property(NM.SETTING_IP4_CONFIG_DHCP_CLIENT_ID, new_client_id) + success = c.commit_changes(True, None) + if success: + changed += 1 + else: + errors += 1 +- print('Connection {}: \'{}\' -> \'{}\' ({})'.format(c.get_uuid(), +- client_id, new_client_id, +- 'OK' if success else 'FAIL')) ++ print( ++ "Connection {}: '{}' -> '{}' ({})".format( ++ c.get_uuid(), ++ client_id, ++ new_client_id, ++ "OK" if success else "FAIL", ++ ) ++ ) + + print("{} processed, {} changed, {} errors".format(processed, changed, errors)) diff --git a/SPECS/leapp-repository.spec b/SPECS/leapp-repository.spec index 539a3d9..0176292 100644 --- a/SPECS/leapp-repository.spec +++ b/SPECS/leapp-repository.spec @@ -42,7 +42,7 @@ py2_byte_compile "%1" "%2"} Name: leapp-repository Version: 0.16.0 -Release: 6%{?dist}.elevate.1 +Release: 6%{?dist}.elevate.2 Summary: Repositories for leapp License: ASL 2.0 @@ -262,6 +262,9 @@ done; # no files here %changelog +* Wed Aug 17 2022 Andrew Lukoshko - 0.16.0-6.elevate.2 +- Fix UEFI boot entries + * Wed Aug 17 2022 Andrew Lukoshko - 0.16.0-6.elevate - Apply ELevate modifications