diff --git a/repos/system_upgrade/cloudlinux/actors/handleelspackages/actor.py b/repos/system_upgrade/cloudlinux/actors/handleelspackages/actor.py new file mode 100644 index 0000000000..1cc54c11ff --- /dev/null +++ b/repos/system_upgrade/cloudlinux/actors/handleelspackages/actor.py @@ -0,0 +1,30 @@ +import os + +from leapp.actors import Actor +from leapp.libraries.common.cllaunch import run_on_cloudlinux +from leapp.libraries.stdlib import run +from leapp.tags import DownloadPhaseTag, IPUWorkflowTag + + +class HandleElsPackages(Actor): + name = "handle_els_packages" + consumes = () + produces = () + tags = (DownloadPhaseTag.Before, IPUWorkflowTag) + + @run_on_cloudlinux + def process(self): + try: + script_path = self.get_file_path("els_script_handler.sh") + os.chmod(script_path, 0o755) + result = run([script_path]) + if result["exit_code"] != 0: + self.log.error( + "Cleanup script failed with exit code: %d\nError: %s", + result["exit_code"], + result["stderr"], + ) + else: + self.log.info("Cleanup script executed successfully") + except Exception as e: + self.log.error("Failed to execute cleanup script: %s", str(e)) diff --git a/repos/system_upgrade/cloudlinux/actors/handleelspackages/files/els_script_handler.sh b/repos/system_upgrade/cloudlinux/actors/handleelspackages/files/els_script_handler.sh new file mode 100644 index 0000000000..a0574836ac --- /dev/null +++ b/repos/system_upgrade/cloudlinux/actors/handleelspackages/files/els_script_handler.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +BACKUP="/etc/cl-elevate-saved" +RUNNING_KERNEL=$(uname -r) +TMP="${BACKUP}/tmp" + +echo "Starting ELS package handling" +echo "Running kernel: ${RUNNING_KERNEL}" + +mkdir -p "${BACKUP}" "${TMP}" 2>/dev/null || { + echo "Failed to create backup/temp directories" + exit 1 +} + +# Check if ELS repositories are present +if ! ls /etc/yum.repos.d/centos*els*.repo &> /dev/null; then + echo "No ELS repositories found, skipping processing" + rm -rf "${TMP}" + exit 0 +fi + +echo "ELS repositories detected. Processing ELS packages before conversion..." + +# Create package backup list +rpm -qa | sort > "${BACKUP}/els_packages_backup_$(date +%Y%m%d_%H%M%S).txt" || { + echo "Warning: Failed to create package backup list" +} + +# Function to check if kernel is in use +is_kernel_in_use() { + local kernel_pkg=$1 + local kernel_ver=$(rpm -q --queryformat '%{VERSION}-%{RELEASE}' "$kernel_pkg") + [[ "$kernel_ver" == "$RUNNING_KERNEL" ]] +} + +# Prevent ELS packages from being reinstalled +if ! grep -q "exclude=\*.tuxcare.els\*" /etc/yum.conf; then + { + echo + echo "# Added by cloudlinux Elevate" + echo "exclude=*.tuxcare.els*" + } >> /etc/yum.conf || echo "Warning: Failed to add exclusion to yum.conf" +fi + +# Save and disable ELS repos +cp /etc/yum.repos.d/centos*els*.repo "${BACKUP}/" 2>&1 +echo "Disabling ELS repositories..." +yum-config-manager --disable centos7-els 2>&1 +yum-config-manager --disable 'centos7els-rollout-*' 2>&1 + +# Remove els-define package first if present +if rpm -q els-define &> /dev/null; then + yum remove -y els-define 2>&1 || echo "Warning: Failed to remove els-define" +fi + +# Get list of installed ELS packages +echo "Checking for ELS packages..." +els_pkgs=$(rpm -qa | grep -E '\.tuxcare\.els[0-9]') || true + +if [ -n "$els_pkgs" ]; then + echo "Found ELS packages:" + echo "$els_pkgs" + + # Handle kernel packages separately and more aggressively + els_kernel_pkgs=$(echo "$els_pkgs" | grep "^kernel") || true + if [ -n "$els_kernel_pkgs" ]; then + echo "Found ELS kernel packages:" + echo "$els_kernel_pkgs" + + # Remove all ELS kernel packages, regardless of running kernel + # since we already have the LVE kernel installed + echo "Removing all ELS kernel packages since LVE kernel is present..." + for kernel_pkg in $els_kernel_pkgs; do + echo "Removing ELS kernel package: $kernel_pkg" + rpm -e --nodeps "$kernel_pkg" 2>&1 || { + echo "Warning: Failed to remove $kernel_pkg" + # If rpm -e fails, try with yum as fallback + yum remove -y "$kernel_pkg" 2>&1 + } + done + fi + + # Process remaining non-kernel ELS packages + non_kernel_els_pkgs=$(echo "$els_pkgs" | grep -v "^kernel") || true + if [ -n "$non_kernel_els_pkgs" ]; then + echo "Processing non-kernel ELS packages..." + + # Create a yum shell script for batch processing + TMPFILE="${TMP}/yum_commands_$$" + echo "# yum shell commands" > "$TMPFILE" + + echo "$non_kernel_els_pkgs" | while read -r pkg; do + base_name=$(echo "$pkg" | awk -F'-[0-9]' '{print $1}') + if [ -n "$base_name" ]; then + echo "remove $pkg" >> "$TMPFILE" + echo "install $base_name" >> "$TMPFILE" + fi + done + + echo "run" >> "$TMPFILE" + echo "exit" >> "$TMPFILE" + + # Execute the yum shell script + echo "Replacing non-kernel ELS packages with base versions..." + if ! yum shell -y "$TMPFILE" 2>&1; then + echo "Trying alternative approach for non-kernel packages..." + echo "$non_kernel_els_pkgs" | while read -r pkg; do + base_name=$(echo "$pkg" | awk -F'-[0-9]' '{print $1}') + if [ -n "$base_name" ]; then + yum downgrade -y "$base_name" 2>&1 + fi + done + fi + rm -f "$TMPFILE" + fi +fi + +# Move ELS repo files to backup and clean up +mv -f /etc/yum.repos.d/centos*els*.repo "${BACKUP}/" 2>&1 + +# Remove TuxCare GPG key if it exists +if [ -f "/etc/pki/rpm-gpg/RPM-GPG-KEY-TuxCare" ]; then + mv -f "/etc/pki/rpm-gpg/RPM-GPG-KEY-TuxCare" "${BACKUP}/" 2>&1 +fi + +# Clean up +yum clean all 2>&1 +rm -rf "${TMP}" + +echo "ELS package handling complete" +exit 0 diff --git a/repos/system_upgrade/cloudlinux/libraries/cllaunch.py b/repos/system_upgrade/cloudlinux/libraries/cllaunch.py index 6cbab5de5c..d9981ea244 100644 --- a/repos/system_upgrade/cloudlinux/libraries/cllaunch.py +++ b/repos/system_upgrade/cloudlinux/libraries/cllaunch.py @@ -1,11 +1,49 @@ import functools from leapp.libraries.common.config import version +def run_on_cloudlinux(func_or_version=None): + """ + Decorator that runs a function only on specified CloudLinux versions. -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 + Can be used as: + @run_on_cloudlinux # Runs on any CloudLinux version + @run_on_cloudlinux('7') # Runs only on CloudLinux 7 + @run_on_cloudlinux(['7', '8']) # Runs on CloudLinux 7 or 8 + """ + + # If used without parentheses + if callable(func_or_version): + @functools.wraps(func_or_version) + def direct_wrapper(*args, **kwargs): + if version.current_version()[0] != "cloudlinux": + return None + return func_or_version(*args, **kwargs) + return direct_wrapper + + # If used with version specification + versions = func_or_version + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + release_id, version_id = version.current_version() + + # Check if running on CloudLinux + if release_id != "cloudlinux": + return None + + # If no versions specified, run on any CloudLinux version + if versions is None: + return func(*args, **kwargs) + + # Convert versions to list if string was provided + version_list = [versions] if isinstance(versions, str) else versions + + # Compare only major version number + current_major = version.get_major_version(version_id) + if current_major in version_list: + return func(*args, **kwargs) + + return None + + return wrapper + return decorator