This script has been designed to handle both 7 to 8 / 7 to 9 Migration

#!/usr/bin/env bash
# Azure Leapp Migration Script (single file) with flags:
# --preconfig => take pre-upgrade OS config backup (to /backup) and exit
# --monitorlog => live monitor PACKAGE Modified lines in /var/log/leapp/leapp-upgrade.log and exit
# --postcleanup => run OS-aware post-upgrade cleanup and exit (RHEL8 => postclean_rhel8, RHEL9 => postclean_rhel9)
# --skip-preupgrade => (7->8 and 8->9) skip leapp preupgrade and run leapp upgrade directly
# --sha1 => list RPMs with SHA1 in SIGGPG and export CSV (run-and-exit)
# --restore-root-hash => restore original encrypted root password hash during --postcleanup
# or as standalone run-and-exit operation
# --advanced-precheck => read-only precheck for package/conflict risks before Leapp
#
# Root password behavior:
# - On first password-handling run, script ALWAYS captures current encrypted root hash
# into $ROOT_HASH_FILE before setting temporary root password.
# - Root password handled only ONCE (stamp file)
# - Temporary root password is hardcoded
# - Saved original hash is never overwritten
# - --restore-root-hash is used only to trigger restore workflow later
#
# Other behaviors:
# - /var extend Option 1: extend only if free space < threshold (supports ext4 + xfs)
# - If --extend-var-gb is explicitly passed, /var is force-extended by that amount without threshold check
# - /usr extend only if free space < 2 GB (extend by 1 GB)
# - /opt extend only if free space < 1 GB (extend by 512 MB)
# - /tmp extend only if free space < 1 GB (extend by 2 GB)
# - Mandatory reboot enforcement after yum update (RHEL7), with stamp that auto-clears if reboot no longer required
# - Stop nfs-client.target + autofs during upgrade phases; enable them back in post-cleanup where applicable
# - /etc/fstab NFS/CIFS lines are backed up and commented only once; restored from backup in RHEL9 post-cleanup
# - /etc/auto.direct entries are backed up and commented only once; restored from backup in RHEL9 post-cleanup
# - RHEL8 post-cleanup cleans EL7 artifacts; RHEL9 post-cleanup removes VDO tooling (vdo/kmod-kvdo)
# - Raise open file limit to 65535 before each phase to avoid upgrade failures due to lower current ulimit
# - Set one-time next boot entry to Leapp upgrade entry only for 8->9 phase
# - Structured syslog/journald milestone logging via logger
set -euo pipefail
# -------------------------
# Defaults
# -------------------------
PHASE="all" # 7to8 | 8to9 | all
PHASE_SET="no"
TARGET_9="" # e.g. 9.6 (optional)
DO_REBOOT="no" # yes | no
EXTEND_VAR_GB="10"
EXTEND_VAR_GB_SET="no"
VAR_LV="/dev/mapper/rootvg-varlv"
VAR_FREE_THRESHOLD_GB="5" # if /var free < this (in GiB), extend LV
# Additional mount thresholds / extend sizes
USR_LV="/dev/mapper/rootvg-usrlv"
USR_FREE_THRESHOLD_GB="2" # if /usr free < this (in GiB), extend LV
USR_EXTEND_MB="1024" # extend /usr by 1 GB
OPT_LV="/dev/mapper/rootvg-optlv"
OPT_FREE_THRESHOLD_GB="1" # if /opt free < this (in GiB), extend LV
OPT_EXTEND_MB="512" # extend /opt by 512 MB
TMP_LV="/dev/mapper/rootvg-tmplv"
TMP_FREE_THRESHOLD_GB="1" # if /tmp free < this (in GiB), extend LV
TMP_EXTEND_MB="2048" # extend /tmp by 2 GB
# Flags
PRECONFIG="no"
MONITORLOG="no"
POSTCLEANUP="no"
SKIP_PREUPGRADE="no"
SHA1="no"
RESTORE_ROOT_HASH="no"
ADVANCED_PRECHECK="no"
# Stamps / state
STATE_DIR="/var/lib/azure_leapp_migration"
REBOOT_STAMP_FILE="$STATE_DIR/reboot_required.stamp"
ROOTPW_STAMP_PREFIX="$STATE_DIR/root_password_set_"
ROOTPW_STAMP_GLOB="$STATE_DIR/root_password_set_*"
# Root password temporary set/restore
TEMP_ROOT_PASSWORD="root123456"
ROOT_HASH_FILE="$STATE_DIR/root_password_original.hash"
# fstab + autofs state files
FSTAB_BAK_PATH_FILE="$STATE_DIR/fstab_backup_path"
AUTO_DIRECT_FILE="/etc/auto.direct"
AUTO_DIRECT_BAK_PATH_FILE="$STATE_DIR/auto.direct_backup_path"
# Main log
LOG="/var/log/azure_leapp_migration_$(date +%Y%m%d_%H%M%S).log"
exec > >(tee -a "$LOG") 2>&1
# Syslog / journald logger
SYSLOG_TAG="LEAPP-MIGRATION"
usage() {
cat <<EOF
Usage: $0 [--phase 7to8|8to9|all] [--target9 9.6] [--reboot yes|no] [--extend-var-gb 10] [--var-lv /dev/mapper/rootvg-varlv]
[--preconfig] [--monitorlog] [--postcleanup] [--skip-preupgrade] [--sha1] [--restore-root-hash]
[--advanced-precheck]
Script Execution Order:
Ensure the following steps are completed before sending APP/DB stop communication:
$0 --sha1
$0 --preconfig
$0 --advanced-precheck
Ensure the following steps are executed after APP/DB brought down and confirmation of SHA1 package removal (where applicable):
$0 --phase 7to8 --reboot no
$0 --phase 8to9 --target9 9.6 --skip-preupgrade --reboot no
$0 --postcleanup
$0 --restore-root-hash
Optional:
$0 --phase 7to8 --skip-preupgrade --reboot no
$0 --phase 8to9 --target9 9.6 --reboot no
$0 --monitorlog
Notes:
- The original encrypted root hash is automatically captured during the first password-handling run.
- --restore-root-hash is used only for restoration and does not control the capture process.
- --extend-var-gb <size> force extends /var by the specified GB amount when explicitly passed.
- --advanced-precheck is READ-ONLY and does not remove or modify anything.
EOF
}
log() { echo -e "\n==== $* ====\n"; }
die() { echo "ERROR: $*" >&2; exit 1; }
cmd_exists() { command -v "$1" >/dev/null 2>&1; }
log_sys() {
local level="${1:-info}"
local event="${2:-GENERAL}"
shift 2 || true
local msg="$*"
local host phase_now
host="$(hostname -s 2>/dev/null || hostname)"
phase_now="${PHASE:-unknown}"
echo "[$(date '+%F %T')] [$level] [$event] $msg"
if command -v logger >/dev/null 2>&1; then
logger -t "$SYSLOG_TAG" -p "user.${level}" -- "host=$host phase=$phase_now event=$event msg=$msg"
fi
}
log_info() { log_sys info "$@"; }
log_notice() { log_sys notice "$@"; }
log_warn() { log_sys warn "$@"; }
log_err() { log_sys err "$@"; }
require_root() { [[ $(id -u) -eq 0 ]] || die "Run as root."; }
detect_major() { rpm -E %rhel 2>/dev/null || die "Unable to detect RHEL major version."; }
confirm_or_exit() {
local msg="$1"
read -r -p "$msg (yes/no): " ans
[[ "$ans" == "yes" ]] || die "User aborted."
}
set_open_files_ulimit() {
log "Set open file limit"
local before after
before="$(ulimit -n 2>/dev/null || true)"
echo "INFO: Current ulimit -n: ${before:-unknown}"
if ulimit -n 65535 2>/dev/null; then
after="$(ulimit -n 2>/dev/null || true)"
echo "INFO: Updated ulimit -n: ${after:-unknown}"
else
echo "WARN: Failed to set ulimit -n to 65535. Continuing with current value: ${before:-unknown}"
log_warn ULIMIT "Failed to set open file limit to 65535"
fi
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--phase) PHASE="${2:-}"; PHASE_SET="yes"; shift 2 ;;
--target9) TARGET_9="${2:-}"; shift 2 ;;
--reboot) DO_REBOOT="${2:-}"; shift 2 ;;
--extend-var-gb) EXTEND_VAR_GB="${2:-}"; EXTEND_VAR_GB_SET="yes"; shift 2 ;;
--var-lv) VAR_LV="${2:-}"; shift 2 ;;
--preconfig) PRECONFIG="yes"; shift 1 ;;
--monitorlog) MONITORLOG="yes"; shift 1 ;;
--postcleanup) POSTCLEANUP="yes"; shift 1 ;;
--skip-preupgrade) SKIP_PREUPGRADE="yes"; shift 1 ;;
--sha1|--SHA1) SHA1="yes"; shift 1 ;;
--restore-root-hash) RESTORE_ROOT_HASH="yes"; shift 1 ;;
--advanced-precheck) ADVANCED_PRECHECK="yes"; shift 1 ;;
-h|--help) usage; exit 0 ;;
*) die "Unknown arg: $1" ;;
esac
done
if [[ "$PRECONFIG" == "yes" || "$MONITORLOG" == "yes" || "$POSTCLEANUP" == "yes" || "$SHA1" == "yes" || "$ADVANCED_PRECHECK" == "yes" ]]; then
return 0
fi
if [[ "$RESTORE_ROOT_HASH" == "yes" && "$PHASE_SET" == "no" ]]; then
return 0
fi
case "$PHASE" in
7to8|8to9|all) ;;
*) die "--phase must be 7to8|8to9|all" ;;
esac
case "$DO_REBOOT" in
yes|no) ;;
*) die "--reboot must be yes|no" ;;
esac
}
print_context() {
log "Context"
echo "Log file : $LOG"
echo "Phase : $PHASE"
echo "Target9 : ${TARGET_9:-<not set>}"
echo "Reboot : $DO_REBOOT"
echo "Skip preupgrade (7->8 / 8->9): $SKIP_PREUPGRADE"
echo "Restore root hash requested : $RESTORE_ROOT_HASH"
echo "Advanced precheck : $ADVANCED_PRECHECK"
echo "Var LV : $VAR_LV"
echo "Extend /var : +${EXTEND_VAR_GB}G (explicit flag passed: $EXTEND_VAR_GB_SET)"
echo "Var threshold : ${VAR_FREE_THRESHOLD_GB}G (used only when --extend-var-gb is not explicitly passed)"
echo "Usr LV : $USR_LV (extend +${USR_EXTEND_MB}M only if /usr free < ${USR_FREE_THRESHOLD_GB}G)"
echo "Opt LV : $OPT_LV (extend +${OPT_EXTEND_MB}M only if /opt free < ${OPT_FREE_THRESHOLD_GB}G)"
echo "Tmp LV : $TMP_LV (extend +${TMP_EXTEND_MB}M only if /tmp free < ${TMP_FREE_THRESHOLD_GB}G)"
echo "Flags : preconfig=$PRECONFIG monitorlog=$MONITORLOG postcleanup=$POSTCLEANUP sha1=$SHA1 advanced_precheck=$ADVANCED_PRECHECK"
echo
cat /etc/redhat-release || true
uname -r || true
df -h / /var /usr /opt /tmp || true
}
# =============================================================================
# Flag: --advanced-precheck (READ-ONLY, no dry-run, no repo check)
# =============================================================================
advanced_precheck_v2() {
log_notice PRECHECK "Advanced Precheck v2 started (READ-ONLY MODE)"
log "INFO: This mode does NOT modify system. Safe to run."
local OUT_DIR TS
TS="$(date +%Y%m%d_%H%M%S)"
OUT_DIR="$STATE_DIR/advanced_precheck_$TS"
mkdir -p "$OUT_DIR"
local SUMMARY_FILE OPENSSL_FILE NONRH_FILE DUP_FILE SHA1_FILE
SUMMARY_FILE="$OUT_DIR/summary.txt"
OPENSSL_FILE="$OUT_DIR/openssl_packages.txt"
NONRH_FILE="$OUT_DIR/non_redhat.txt"
DUP_FILE="$OUT_DIR/duplicates.txt"
SHA1_FILE="$OUT_DIR/sha1.txt"
local WARN_COUNT=0
local FAIL_COUNT=0
write_summary() {
echo "$*" | tee -a "$SUMMARY_FILE"
}
add_warn() {
WARN_COUNT=$((WARN_COUNT + 1))
write_summary "WARN : $*"
log_warn PRECHECK "$*"
}
add_fail() {
FAIL_COUNT=$((FAIL_COUNT + 1))
write_summary "FAIL : $*"
log_err PRECHECK "$*"
}
add_pass() {
write_summary "PASS : $*"
log_info PRECHECK "$*"
}
write_summary "Advanced Precheck v2 started at: $(date)"
write_summary "Host: $(hostname -s 2>/dev/null || hostname)"
write_summary "Release: $(cat /etc/redhat-release 2>/dev/null || echo unknown)"
write_summary "Kernel: $(uname -r)"
write_summary "Output directory: $OUT_DIR"
write_summary
log "Checking OpenSSL related packages..."
rpm -qa | grep -Ei '(^openssl|openssl11|compat-openssl|libcrypto|libssl)' | sort > "$OPENSSL_FILE" || true
if [[ -s "$OPENSSL_FILE" ]]; then
add_warn "OpenSSL-related packages found. Review: $OPENSSL_FILE"
else
add_pass "No OpenSSL-related packages matched the scan."
fi
if rpm -q openssl11-libs &>/dev/null; then
add_fail "Conflicting package detected: openssl11-libs installed. This can break EL8 upgrade."
fi
if rpm -q compat-openssl10 &>/dev/null; then
add_warn "compat-openssl10 installed. Review application dependency before upgrade."
fi
log "Checking non-Red Hat vendor packages..."
rpm -qa --qf '%{NAME}|%{VERSION}-%{RELEASE}|%{ARCH}|%{VENDOR}\n' \
| awk -F'|' '
BEGIN {
IGNORECASE=1
# Approved package exclusions from non-Red Hat report
exclude["atop"]
exclude["auoms"]
exclude["besagent"]
exclude["chef"]
exclude["chefdk"]
exclude["chef-manage"]
exclude["chef-server-core"]
exclude["chef-workstation"]
exclude["epel-release"]
exclude["htop"]
exclude["hwinfo"]
exclude["iftop"]
exclude["katello"]
exclude["mdatp"]
exclude["mde-netfilter"]
exclude["omi"]
exclude["omsagent"]
exclude["omsconfig"]
exclude["redhat-release-server"]
exclude["scx"]
exclude["dependency-agent"]
exclude["lgtoclnt"]
exclude["lgtoxtdclnt"]
exclude["lgtonmda"]
exclude["axon-agent"]
exclude["tw-ebpf-loader"]
exclude["tw-eg-driver-rhel"]
exclude["tw-eg-service"]
exclude["wazuh-agent"]
}
{
pkg=tolower($1)
vendor=$4
# Skip approved exclusions
if (pkg in exclude) next
# Show only non-Red Hat vendor packages
if (vendor !~ /Red Hat/ && vendor !~ /^\(none\)$/ && vendor !~ /^$/) {
print
}
}
' | sort > "$NONRH_FILE" || true
local NONRH_COUNT
NONRH_COUNT=$(wc -l < "$NONRH_FILE" 2>/dev/null || echo 0)
echo "INFO: Non-Red Hat packages after approved exclusions: $NONRH_COUNT"
if [[ -s "$NONRH_FILE" ]]; then
add_warn "Non-Red Hat vendor packages found. Review: $NONRH_FILE"
else
add_pass "No non-Red Hat vendor packages detected."
fi
log "Checking duplicate installed package names..."
rpm -qa --qf '%{NAME}\n' | sort | uniq -d > "$DUP_FILE" || true
if [[ -s "$DUP_FILE" ]]; then
add_warn "Duplicate installed package names found. Review: $DUP_FILE"
else
add_pass "No duplicate installed package names found."
fi
log "Checking SHA1 signed packages..."
rpm -qa --qf '%{NAME},%{VERSION},%{VENDOR},%{SIGGPG:pgpsig}\n' 2>/dev/null | grep -i SHA1 >> "$SHA1_FILE" || true
rpm -qa --qf '%{NAME},%{VERSION},%{RELEASE},%{SIGPGP:pgpsig}\n' 2>/dev/null | grep -i SHA1 >> "$SHA1_FILE" || true
if [[ -s "$SHA1_FILE" ]]; then
add_warn "SHA1 signed packages found. Review: $SHA1_FILE"
else
add_pass "No SHA1 signed packages found in SIGGPG scan."
fi
write_summary
write_summary "Warnings : $WARN_COUNT"
write_summary "Failures : $FAIL_COUNT"
write_summary "Summary : $SUMMARY_FILE"
write_summary "OpenSSL : $OPENSSL_FILE"
write_summary "NonRH : $NONRH_FILE"
write_summary "Duplicate: $DUP_FILE"
write_summary "SHA1 : $SHA1_FILE"
log_notice PRECHECK "Advanced Precheck v2 completed"
log "Review reports under: $OUT_DIR"
}
# =============================================================================
# Flag: --sha1 (list SHA1-signed RPMs)
# =============================================================================
sha1_rpm_report() {
log_notice SHA1_SCAN "SHA1 RPM scan started"
log "SHA1: Detect RPMs signed with SHA1 (SIGGPG)"
mkdir -p "$STATE_DIR"
local OUT
OUT="$STATE_DIR/rpm_sha1_$(hostname)_$(date +%Y%m%d_%H%M%S).csv"
echo "name,version,vendor,pgpsig" > "$OUT"
rpm -qa --qf '%{NAME},%{VERSION},%{VENDOR},%{SIGGPG:pgpsig}\n' 2>/dev/null | grep -i SHA1 >> "$OUT" || true
rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE} %{SIGPGP:pgpsig}\n' 2>/dev/null | grep -i SHA1 >> "$OUT" || true
echo "INFO: SHA1 report generated: $OUT"
echo "Preview (first 50 lines):"
sed -n '1,50p' "$OUT" || true
log_notice SHA1_SCAN "SHA1 RPM scan completed: $OUT"
}
# =============================================================================
# Flag: --preconfig (backup)
# =============================================================================
preconfig_backup() {
log_notice PRECONFIG "Preconfig backup started"
log "PRECONFIG: OS config and process backup (to /backup)"
local DATE HOST BACKUP_DIR ARCHIVE
DATE=$(date +%F_%H%M)
HOST=$(hostname)
BACKUP_DIR="/backup/os_config_${HOST}_${DATE}"
ARCHIVE="/backup/os_config_${HOST}_${DATE}.tar.gz"
mkdir -p "$BACKUP_DIR"
echo "Starting OS config and process backup..."
echo "Backup dir: $BACKUP_DIR"
uname -a > "$BACKUP_DIR/uname.txt"
cat /etc/redhat-release > "$BACKUP_DIR/os-release.txt" 2>/dev/null || true
rpm -qa --qf '%{NAME} %{VENDOR}\n' | sort -u > "$BACKUP_DIR/rpm_installed_list-vendor.txt"
rpm -qa > "$BACKUP_DIR/rpm_installed_list.txt"
rpm -qa --qf '%{VENDOR}\n' | sort | uniq -c > "$BACKUP_DIR/rpm_installed_list-vendor-summary.txt"
yum repolist all > "$BACKUP_DIR/yum_repolist.txt" 2>/dev/null || dnf repolist > "$BACKUP_DIR/dnf_repolist.txt" 2>/dev/null || true
systemctl list-units --type=service --state=running > "$BACKUP_DIR/running_services.txt" 2>/dev/null || true
ps -ef > "$BACKUP_DIR/running_processes_ps-ef.txt"
ps aux > "$BACKUP_DIR/running_processes_ps-aux.txt"
top -b -n 1 > "$BACKUP_DIR/top_snapshot.txt" 2>/dev/null || true
ss -tulnp > "$BACKUP_DIR/listening_ports_ss.txt" 2>/dev/null || true
netstat -tulnp > "$BACKUP_DIR/listening_ports_netstat.txt" 2>/dev/null || true
ip addr show > "$BACKUP_DIR/ip_addr.txt" 2>/dev/null || true
ip route show > "$BACKUP_DIR/ip_route.txt" 2>/dev/null || true
cp -p /etc/resolv.conf "$BACKUP_DIR/resolv.conf" 2>/dev/null || true
lsblk > "$BACKUP_DIR/lsblk.txt" 2>/dev/null || true
df -hT > "$BACKUP_DIR/df.txt"
cp -p /etc/fstab "$BACKUP_DIR/fstab" 2>/dev/null || true
getenforce > "$BACKUP_DIR/selinux_status.txt" 2>/dev/null || true
cp -p /etc/selinux/config "$BACKUP_DIR/" 2>/dev/null || true
crontab -l > "$BACKUP_DIR/root_crontab.txt" 2>/dev/null || true
cp -rp /etc/cron* "$BACKUP_DIR/" 2>/dev/null || true
cp -rp /etc/ssh "$BACKUP_DIR/" 2>/dev/null || true
cp -p /etc/sudoers "$BACKUP_DIR/" 2>/dev/null || true
cp -rp /etc/sudoers.d "$BACKUP_DIR/" 2>/dev/null || true
cp -rp /etc/sysconfig/network-scripts "$BACKUP_DIR/" 2>/dev/null || true
cp -rp /etc/NetworkManager "$BACKUP_DIR/" 2>/dev/null || true
cp -rp /etc/yum.repos.d "$BACKUP_DIR/" 2>/dev/null || true
cp -p /etc/hosts "$BACKUP_DIR/" 2>/dev/null || true
cp -p /etc/passwd "$BACKUP_DIR/" 2>/dev/null || true
cp -p /etc/group "$BACKUP_DIR/" 2>/dev/null || true
cp -p /etc/shadow "$BACKUP_DIR/" 2>/dev/null || true
ulimit -a > "$BACKUP_DIR/ulimit_a.txt"
su oracle -c "ulimit -a" > "$BACKUP_DIR/ulimit_a_oracle.txt" 2>/dev/null || true
su grid -c "ulimit -a" > "$BACKUP_DIR/ulimit_a_grid.txt" 2>/dev/null || true
umask > "$BACKUP_DIR/umask.txt"
sysctl -a > "$BACKUP_DIR/sysctl_a.txt" 2>/dev/null || true
cp -p /etc/sysctl.conf "$BACKUP_DIR/" 2>/dev/null || true
if command -v oracleasm >/dev/null 2>&1; then
oracleasm listdisks > "$BACKUP_DIR/oracleasm-disks.txt" 2>&1
for asm in $(oracleasm listdisks 2>/dev/null); do
oracleasm querydisk -p "$asm"
done > "$BACKUP_DIR/oracleasm_querydisk.txt" 2>&1
else
echo "oracleasm command not found - skipping ASM capture" > "$BACKUP_DIR/oracleasm_skipped.txt"
log_warn PRECONFIG "oracleasm command not found, ASM capture skipped"
fi
tar -czf "$ARCHIVE" -C "$(dirname "$BACKUP_DIR")" "$(basename "$BACKUP_DIR")"
echo "Backup completed successfully:"
echo " DIR: $BACKUP_DIR"
echo " TAR: $ARCHIVE"
log_notice PRECONFIG "Preconfig backup completed: $ARCHIVE"
}
# =============================================================================
# Flag: --monitorlog
# =============================================================================
monitor_leapp_pkg_log() {
log_notice MONITORLOG "Live monitoring of Leapp package log started"
log "MONITORLOG: Watching PACKAGE Modified lines (Ctrl+C to stop)"
local leapp_log="/var/log/leapp/leapp-upgrade.log"
process_logs() {
echo "Date,Time,Name,Arch,Ver,Rel,Epoch,RepoId,State"
grep "PACKAGE Modified" "$leapp_log" 2>/dev/null | awk '
{
date=$1
time=$2
sub(/\..*/, "", time)
if (match($0, /'\''name'\'': '\''([^'\'']+)/, a)) name=a[1]
if (match($0, /'\''arch'\'': '\''([^'\'']+)/, a)) arch=a[1]
if (match($0, /'\''ver'\'': '\''([^'\'']+)/, a)) ver=a[1]
if (match($0, /'\''rel'\'': '\''([^'\'']+)/, a)) rel=a[1]
if (match($0, /'\''epoch'\'': '\''([^'\'']+)/, a)) epoch=a[1]
if (match($0, /'\''repoid'\'': '\''([^'\'']+)/, a)) repoid=a[1]
if (match($0, /'\''state'\'': '\''([^'\'']+)/, a)) state=a[1]
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s\n", date,time,name,arch,ver,rel,epoch,repoid,state
}'
}
while true; do
process_logs | cat -n | tail -n 2
sleep 5
echo "------------------------------------------"
done
}
# =============================================================================
# Reboot enforcement (RHEL7 after yum update)
# =============================================================================
ensure_reboot_tools() {
if ! cmd_exists needs-restarting; then
yum install -y yum-utils >/dev/null 2>&1 || true
fi
}
latest_installed_kernel() {
rpm -q --last kernel 2>/dev/null | head -n1 | awk '{print $1}' | sed 's/^kernel-//'
}
reboot_is_required() {
if cmd_exists needs-restarting; then
needs-restarting -r >/dev/null 2>&1
local rc=$?
[[ $rc -eq 1 ]] && return 0
fi
local latest
latest="$(latest_installed_kernel)"
[[ -n "$latest" && "$(uname -r)" != "$latest" ]] && return 0
return 1
}
mark_reboot_required() {
mkdir -p "$STATE_DIR"
{
echo "STAMP_TS=$(date -Is)"
echo "KERNEL_BEFORE=$(uname -r)"
echo "RELEASE_BEFORE=$(cat /etc/redhat-release 2>/dev/null || true)"
} > "$REBOOT_STAMP_FILE"
}
clear_reboot_stamp() { rm -f "$REBOOT_STAMP_FILE" 2>/dev/null || true; }
enforce_reboot_if_needed() {
local where="$1"
ensure_reboot_tools
if reboot_is_required; then
log_notice REBOOT_REQUIRED "Reboot required detected at: $where"
log "Reboot required detected ($where)"
mark_reboot_required
if [[ "$DO_REBOOT" == "yes" ]]; then
log_notice REBOOT "Automatic reboot triggered at: $where"
log "Rebooting now (mandatory) ..."
reboot
else
log_warn REBOOT "Manual reboot required at: $where"
die "Reboot is mandatory ($where). Reboot now and re-run the script. Stamp: $REBOOT_STAMP_FILE"
fi
fi
}
ensure_rebooted_or_clear_stamp() {
[[ -f "$REBOOT_STAMP_FILE" ]] || return 0
ensure_reboot_tools
if ! reboot_is_required; then
echo "INFO: Reboot stamp exists but reboot is NOT required anymore. Clearing stamp: $REBOOT_STAMP_FILE"
clear_reboot_stamp
log_info REBOOT_STAMP "Reboot stamp cleared because reboot is no longer required"
return 0
fi
local prev_kernel
prev_kernel="$(awk -F= '/^KERNEL_BEFORE=/{print $2}' "$REBOOT_STAMP_FILE" 2>/dev/null || true)"
if [[ -n "$prev_kernel" && "$(uname -r)" == "$prev_kernel" ]]; then
log_err REBOOT "Mandatory reboot has not happened yet"
die "Reboot is mandatory and has NOT happened yet. Previous kernel: $prev_kernel, current: $(uname -r). Reboot and rerun."
fi
echo "INFO: Reboot detected (kernel changed or state advanced). Clearing stamp: $REBOOT_STAMP_FILE"
clear_reboot_stamp
log_notice REBOOT "Required reboot detected as completed; stamp cleared"
}
# =============================================================================
# Root password handling
# =============================================================================
capture_original_root_hash_once() {
mkdir -p "$STATE_DIR"
local current_hash
current_hash="$(getent shadow root | cut -d: -f2)"
if [[ -z "${current_hash:-}" ]]; then
log_err ROOT_HASH "Unable to capture current encrypted root password hash"
die "Unable to capture current encrypted root password hash."
fi
if [[ ! -f "$ROOT_HASH_FILE" ]]; then
echo "$current_hash" > "$ROOT_HASH_FILE"
chmod 600 "$ROOT_HASH_FILE"
echo "INFO: Original encrypted root hash saved to $ROOT_HASH_FILE"
log_notice ROOT_HASH "Original encrypted root password hash captured"
else
echo "INFO: Original encrypted root hash already present at $ROOT_HASH_FILE. Skipping overwrite."
log_info ROOT_HASH "Original encrypted root hash already present, skipping overwrite"
fi
}
set_root_password_once() {
log "Root password: capture original hash and set temporary password only once"
mkdir -p "$STATE_DIR"
capture_original_root_hash_once
if ls -1 $ROOTPW_STAMP_GLOB >/dev/null 2>&1; then
echo "INFO: Root password step already completed earlier. Stamp:"
ls -1t $ROOTPW_STAMP_GLOB | head -n 1 || true
echo "Skipping root password update."
log_info ROOT_PASSWORD "Temporary root password step already completed earlier"
return 0
fi
echo "INFO: Setting temporary root password."
echo "root:${TEMP_ROOT_PASSWORD}" | chpasswd
local ts stamp
ts="$(date +%d%b%Y%H%M%S | tr '[:upper:]' '[:lower:]')"
stamp="${ROOTPW_STAMP_PREFIX}${ts}"
{
echo "STAMP_TS=$(date -Is)"
echo "HOST=$(hostname)"
echo "KERNEL=$(uname -r)"
echo "RELEASE=$(cat /etc/redhat-release 2>/dev/null || true)"
echo "ROOT_HASH_FILE=$ROOT_HASH_FILE"
echo "ROOT_HASH_CAPTURED=$( [[ -f "$ROOT_HASH_FILE" ]] && echo yes || echo no )"
echo "RESTORE_ROOT_HASH_REQUESTED_AT_RUN=$RESTORE_ROOT_HASH"
} > "$stamp"
echo "INFO: Root password stamp created: $stamp"
log_notice ROOT_PASSWORD "Temporary root password set and stamp created: $stamp"
}
restore_original_root_hash() {
log "Restore original encrypted root password hash"
[[ -f "$ROOT_HASH_FILE" ]] || {
echo "WARN: Original root hash file not found: $ROOT_HASH_FILE"
echo "WARN: Cannot restore original root password automatically."
log_warn ROOT_HASH "Original root hash file not found; restore skipped"
return 0
}
local saved_hash
saved_hash="$(cat "$ROOT_HASH_FILE" 2>/dev/null || true)"
[[ -n "${saved_hash:-}" ]] || {
echo "WARN: Saved root hash is empty. Skipping restore."
log_warn ROOT_HASH "Saved root hash is empty; restore skipped"
return 0
}
/usr/sbin/usermod -p "$saved_hash" root
echo "INFO: Original encrypted root password restored successfully."
rm -f "$ROOT_HASH_FILE" 2>/dev/null || true
echo "INFO: Removed saved root hash file: $ROOT_HASH_FILE"
log_notice ROOT_HASH "Original encrypted root password restored successfully"
}
# =============================================================================
# Boot loader entries preparation before Leapp upgrade
# =============================================================================
prepare_boot_loader_entries() {
log "BOOT: Validate /boot/loader/entries before Leapp upgrade"
if [[ ! -d /boot/loader ]]; then
echo "INFO: /boot/loader does not exist. Creating..."
mkdir -p /boot/loader
fi
if [[ ! -d /boot/loader/entries ]]; then
echo "INFO: /boot/loader/entries does not exist. Creating..."
mkdir -p /boot/loader/entries
fi
chmod 700 /boot/loader 2>/dev/null || true
chmod 700 /boot/loader/entries 2>/dev/null || true
echo "INFO: Current /boot/loader/entries:"
ls -la /boot/loader/entries || true
if [[ -z "$(find /boot/loader/entries -maxdepth 1 -type f -name '*.conf' 2>/dev/null)" ]]; then
echo "WARN: /boot/loader/entries exists but no BLS .conf files found."
echo "WARN: Continuing, but Leapp may not create/find the upgrade boot entry."
log_warn BOOT_ENTRY "/boot/loader/entries exists but is empty before Leapp upgrade"
else
echo "INFO: BLS entries found under /boot/loader/entries"
log_info BOOT_ENTRY "/boot/loader/entries validated before Leapp upgrade"
fi
}
# =============================================================================
# Set one-time boot entry for Leapp upgrade
# =============================================================================
set_upgrade_boot_entry() {
log "Set next boot to Leapp upgrade entry"
mkdir -p /boot/loader/entries
chmod 700 /boot/loader 2>/dev/null || true
chmod 700 /boot/loader/entries 2>/dev/null || true
local upgrade_file=""
local UPGRADE_ID=""
upgrade_file="$(find /boot/loader/entries -maxdepth 1 -type f -name '*upgrade*.conf' 2>/dev/null | sort | head -n1 || true)"
if [[ -n "$upgrade_file" && -f "$upgrade_file" ]]; then
UPGRADE_ID="$(basename "$upgrade_file" .conf)"
log_notice BOOT_ENTRY "Leapp upgrade boot entry found: $UPGRADE_ID"
echo "Setting next boot to upgrade entry: $UPGRADE_ID"
grub2-reboot "$UPGRADE_ID"
grub2-editenv list || true
sync
echo "Please proceed with reboot manually..."
else
log_err BOOT_ENTRY "Leapp upgrade boot entry not found"
echo "ERROR: Upgrade entry not found"
echo "DEBUG: No '*upgrade*.conf' file found under /boot/loader/entries"
echo "DEBUG: Available boot loader entries:"
ls -l /boot/loader/entries/ 2>/dev/null || true
echo "DEBUG: Check Leapp logs:"
echo " /var/log/leapp/leapp-report.txt"
echo " /var/log/leapp/leapp-upgrade.log"
exit 1
fi
}
# =============================================================================
# NFS/AutoFS handling (stop in phases)
# =============================================================================
stop_nfs_autofs_services() {
log "NFS/AUTOFS: status + stop (common for phases 7->8 and 8->9)"
if ! cmd_exists systemctl; then
echo "WARN: systemctl not available. Skipping nfs-client.target/autofs handling."
log_warn AUTOFS "systemctl not available; NFS/AutoFS handling skipped"
return 0
fi
systemctl status nfs-client.target 2>/dev/null || true
systemctl status autofs 2>/dev/null || true
systemctl stop nfs-client.target 2>/dev/null || true
systemctl stop autofs 2>/dev/null || true
echo "Post-check (is-active):"
systemctl is-active nfs-client.target 2>/dev/null || true
systemctl is-active autofs 2>/dev/null || true
log_info AUTOFS "NFS client target and autofs stop attempted"
}
# =============================================================================
# RHEL9 ONLY: Enable + start NFS/AutoFS (post cleanup only)
# =============================================================================
enable_nfs_autofs_rhel9_only() {
local rhel
rhel="$(detect_major)"
[[ "$rhel" -eq 9 ]] || { echo "INFO: Skipping enable of nfs-client.target/autofs (requires RHEL9). Detected RHEL $rhel"; return 0; }
log "RHEL9 ONLY: Enable + start nfs-client.target and autofs"
if ! cmd_exists systemctl; then
echo "WARN: systemctl not available. Skipping."
log_warn AUTOFS "systemctl not available; enable/start skipped"
return 0
fi
if systemctl list-unit-files | awk '{print $1}' | grep -qx 'nfs-client.target'; then
systemctl enable --now nfs-client.target 2>/dev/null || true
systemctl status nfs-client.target 2>/dev/null || true
else
echo "INFO: Unit not found: nfs-client.target (skipping)"
log_warn AUTOFS "Unit not found: nfs-client.target"
fi
if systemctl list-unit-files | awk '{print $1}' | grep -qx 'autofs.service'; then
systemctl enable --now autofs 2>/dev/null || true
systemctl status autofs 2>/dev/null || true
else
echo "INFO: Unit not found: autofs.service (skipping)"
log_warn AUTOFS "Unit not found: autofs.service"
fi
echo "Post-check (is-enabled / is-active):"
systemctl is-enabled nfs-client.target 2>/dev/null || true
systemctl is-active nfs-client.target 2>/dev/null || true
systemctl is-enabled autofs 2>/dev/null || true
systemctl is-active autofs 2>/dev/null || true
log_notice AUTOFS "RHEL9 NFS/AutoFS enable/start phase completed"
}
# =============================================================================
# RHEL9 ONLY: Remove VDO tooling (post cleanup)
# =============================================================================
remove_vdo_rhel9_only() {
local rhel
rhel="$(detect_major)"
[[ "$rhel" -eq 9 ]] || { echo "INFO: Skipping VDO removal (requires RHEL9). Detected RHEL $rhel"; return 0; }
log "RHEL9 ONLY: Removing VDO tooling (vdo/kmod-kvdo) and disabling services"
if cmd_exists systemctl; then
systemctl stop vdo 2>/dev/null || true
systemctl disable vdo 2>/dev/null || true
systemctl stop vdo.service 2>/dev/null || true
systemctl disable vdo.service 2>/dev/null || true
systemctl stop kvdo 2>/dev/null || true
systemctl disable kvdo 2>/dev/null || true
fi
if cmd_exists dnf; then
dnf remove -y vdo kmod-kvdo 2>/dev/null || true
else
yum remove -y vdo kmod-kvdo 2>/dev/null || true
fi
rpm -qa | egrep -i '^(vdo|kmod-kvdo)(-|$)' && echo "WARN: Some VDO packages still present" || echo "INFO: VDO packages removed (or not present)"
log_info VDO "VDO removal step completed"
}
# =============================================================================
# /etc/auto.direct: comment all entries (pre-upgrade); restore backup in RHEL9 post-cleanup
# =============================================================================
comment_auto_direct_entries() {
log "AUTOFS: Comment all entries in $AUTO_DIRECT_FILE (pre-upgrade safety, one-time only)"
[[ -f "$AUTO_DIRECT_FILE" ]] || { echo "INFO: $AUTO_DIRECT_FILE not found. Skipping."; log_warn AUTOFS "$AUTO_DIRECT_FILE not found, skipping"; return 0; }
mkdir -p "$STATE_DIR"
if [[ -f "$AUTO_DIRECT_BAK_PATH_FILE" ]] && grep -q '^#LEAPP_DISABLED#' "$AUTO_DIRECT_FILE" 2>/dev/null; then
echo "INFO: $AUTO_DIRECT_FILE already backed up and commented earlier. Skipping."
echo "INFO: Existing backup path file: $AUTO_DIRECT_BAK_PATH_FILE"
echo "INFO: Backup file: $(cat "$AUTO_DIRECT_BAK_PATH_FILE" 2>/dev/null || true)"
log_info AUTOFS "$AUTO_DIRECT_FILE already backed up and commented earlier"
return 0
fi
if [[ ! -f "$AUTO_DIRECT_BAK_PATH_FILE" ]]; then
local backup="/etc/auto.direct.preleapp.$(date +%F-%H%M%S)"
cp -p "$AUTO_DIRECT_FILE" "$backup"
echo "$backup" > "$AUTO_DIRECT_BAK_PATH_FILE"
echo "INFO: Backup created: $backup"
echo "INFO: Backup path saved: $AUTO_DIRECT_BAK_PATH_FILE"
log_notice AUTOFS "Backup created for $AUTO_DIRECT_FILE: $backup"
else
echo "INFO: Backup path file already exists: $AUTO_DIRECT_BAK_PATH_FILE"
echo "INFO: Backup file: $(cat "$AUTO_DIRECT_BAK_PATH_FILE" 2>/dev/null || true)"
fi
if grep -q '^#LEAPP_DISABLED#' "$AUTO_DIRECT_FILE" 2>/dev/null; then
echo "INFO: $AUTO_DIRECT_FILE already contains LEAPP comment markers. Skipping comment step."
log_info AUTOFS "$AUTO_DIRECT_FILE already contains LEAPP comment markers"
return 0
fi
sed -i \
-e '/^[[:space:]]*#/b' \
-e '/^[[:space:]]*$/b' \
-e 's/^[[:space:]]*/#LEAPP_DISABLED# &/' \
"$AUTO_DIRECT_FILE"
echo "INFO: Updated $AUTO_DIRECT_FILE (all entries commented once)."
echo "Preview:"
head -n 30 "$AUTO_DIRECT_FILE" || true
log_notice AUTOFS "$AUTO_DIRECT_FILE entries commented for upgrade safety"
}
restore_auto_direct_backup_rhel9_only() {
local rhel
rhel="$(detect_major)"
[[ "$rhel" -eq 9 ]] || { echo "INFO: Skipping auto.direct restore (requires RHEL9). Detected RHEL $rhel"; return 0; }
log "RHEL9 ONLY: Restoring $AUTO_DIRECT_FILE from backup"
if [[ ! -f "$AUTO_DIRECT_BAK_PATH_FILE" ]]; then
echo "WARN: Backup path file not found ($AUTO_DIRECT_BAK_PATH_FILE). Cannot restore."
log_warn AUTOFS "Backup path file not found for auto.direct restore"
return 0
fi
local backup
backup="$(cat "$AUTO_DIRECT_BAK_PATH_FILE" 2>/dev/null || true)"
if [[ -z "$backup" || ! -f "$backup" ]]; then
echo "WARN: Backup file listed but not found: '$backup'"
log_warn AUTOFS "Backup file for auto.direct restore not found: $backup"
return 0
fi
local restore_backup="/etc/auto.direct.before_restore.$(date +%F-%H%M%S)"
if [[ -f "$AUTO_DIRECT_FILE" ]]; then
cp -p "$AUTO_DIRECT_FILE" "$restore_backup"
echo "INFO: Current file saved as: $restore_backup"
fi
cp -p "$backup" "$AUTO_DIRECT_FILE"
echo "INFO: Restored $AUTO_DIRECT_FILE from $backup"
if cmd_exists systemctl; then
systemctl reload autofs 2>/dev/null || systemctl restart autofs 2>/dev/null || true
systemctl status autofs 2>/dev/null || true
fi
rm -f "$AUTO_DIRECT_BAK_PATH_FILE" 2>/dev/null || true
log_notice AUTOFS "$AUTO_DIRECT_FILE restored from backup"
}
# =============================================================================
# /etc/fstab: comment NFS/CIFS entries in 7->8; restore from backup in RHEL9 post-cleanup
# =============================================================================
backup_and_comment_fstab_nfs_cifs() {
log "fstab: Comment NFS/CIFS entries (one-time only with backup + restore path stamp)"
[[ -f /etc/fstab ]] || { echo "WARN: /etc/fstab not found. Skipping."; log_warn FSTAB "/etc/fstab not found, skipping"; return 0; }
if ! grep -Eqi 'nfs|cifs' /etc/fstab; then
log "No NFS or CIFS entries found in /etc/fstab."
log_info FSTAB "No NFS/CIFS entries found in /etc/fstab"
return 0
fi
mkdir -p "$STATE_DIR"
if [[ -f "$FSTAB_BAK_PATH_FILE" ]] && grep -qE '^#LEAPP_DISABLED# .*([[:space:]]nfs[[:space:],]|[[:space:]]cifs[[:space:],])' /etc/fstab 2>/dev/null; then
echo "INFO: /etc/fstab already backed up and NFS/CIFS entries already commented earlier. Skipping."
echo "INFO: Existing backup path file: $FSTAB_BAK_PATH_FILE"
echo "INFO: Backup file: $(cat "$FSTAB_BAK_PATH_FILE" 2>/dev/null || true)"
log_info FSTAB "/etc/fstab already backed up and NFS/CIFS entries already commented"
return 0
fi
if [[ ! -f "$FSTAB_BAK_PATH_FILE" ]]; then
local backup="/etc/fstab.preleapp.$(date +%F-%H%M%S)"
cp -p /etc/fstab "$backup"
echo "$backup" > "$FSTAB_BAK_PATH_FILE"
log "Backup created: $backup"
log "Backup path saved: $FSTAB_BAK_PATH_FILE"
log_notice FSTAB "Backup created for /etc/fstab: $backup"
else
log "Backup path file already exists: $FSTAB_BAK_PATH_FILE"
log "Backup file: $(cat "$FSTAB_BAK_PATH_FILE" 2>/dev/null || true)"
fi
if grep -qE '^[[:space:]]*[^#].*[[:space:]]+(nfs|cifs)[[:space:],]' /etc/fstab; then
log "Network filesystem entries detected. Disabling in fstab for upgrade."
sed -i '/^[[:space:]]*#/!{/nfs\|cifs/s/^/#LEAPP_DISABLED# /;}' /etc/fstab
log "NFS/CIFS entries commented in /etc/fstab"
log_notice FSTAB "NFS/CIFS entries commented in /etc/fstab"
else
log "No active uncommented NFS/CIFS entries remain in /etc/fstab. Skipping comment step."
log_info FSTAB "No active uncommented NFS/CIFS entries remain in /etc/fstab"
fi
log "Checking for active NFS/CIFS mounts"
local active_netfs
active_netfs=$(mount | grep -Ei 'type (nfs|cifs)' || true)
if [[ -n "$active_netfs" ]]; then
log "Active network filesystem mounts detected:"
echo "$active_netfs"
log "ACTION REQUIRED: Please unmount these before proceeding with Leapp upgrade."
log "Suggested command: umount <mountpoint>"
log_warn FSTAB "Active NFS/CIFS mounts detected; manual unmount required before Leapp upgrade"
else
log "No active NFS/CIFS mounts detected."
log_info FSTAB "No active NFS/CIFS mounts detected"
fi
}
restore_fstab_backup_rhel9_only() {
local rhel
rhel="$(detect_major)"
[[ "$rhel" -eq 9 ]] || { echo "INFO: Skipping fstab restore (requires RHEL9). Detected RHEL $rhel"; return 0; }
log "RHEL9 ONLY: Restoring original /etc/fstab from pre-leapp backup"
if [[ ! -f "$FSTAB_BAK_PATH_FILE" ]]; then
echo "WARN: Backup path file not found ($FSTAB_BAK_PATH_FILE). Cannot restore."
log_warn FSTAB "Backup path file not found; fstab restore skipped"
return 0
fi
local backup
backup="$(cat "$FSTAB_BAK_PATH_FILE" 2>/dev/null || true)"
if [[ -z "$backup" || ! -f "$backup" ]]; then
echo "WARN: Backup file listed but not found: '$backup'"
log_warn FSTAB "Backup file not found for fstab restore: $backup"
return 0
fi
local restore_backup="/etc/fstab.before_restore.$(date +%F-%H%M%S)"
cp -p /etc/fstab "$restore_backup"
cp -p "$backup" /etc/fstab
echo "INFO: /etc/fstab restored successfully from $backup"
echo "INFO: Previous /etc/fstab saved as: $restore_backup"
echo "Preview:"
sed -n '1,200p' /etc/fstab || true
rm -f "$FSTAB_BAK_PATH_FILE" 2>/dev/null || true
log_notice FSTAB "/etc/fstab restored successfully from backup"
}
# =============================================================================
# PAM: pam_tally2 -> pam_faillock
# =============================================================================
pam_tally2_to_faillock() {
log "PAM: Replace pam_tally2 with pam_faillock (system-auth-ac)"
grep -R pam_tally2 /etc/pam.d || true
cp -p /etc/pam.d/system-auth-ac "/tmp/system-auth-ac_$(date +%d%b%Y%H%M%S | tr '[:upper:]' '[:lower:]')"
sed -i 's/pam_tally2.so/pam_faillock.so/g' /etc/pam.d/system-auth-ac
grep -R faillock /etc/pam.d || true
log_info PAM "pam_tally2 to pam_faillock replacement attempted"
}
# =============================================================================
# Common inhibitor remediation
# =============================================================================
remediate_common_inhibitors() {
log "Remediate common inhibitors"
if [[ -f /etc/ssh/sshd_config ]]; then
sed -i 's/,hmac-ripemd160//g' /etc/ssh/sshd_config || true
grep -q '^PermitRootLogin' /etc/ssh/sshd_config || echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
sshd -t && systemctl reload sshd || die "sshd config test failed"
fi
modprobe -r floppy pata_acpi 2>/dev/null || true
if cmd_exists getenforce; then
setenforce 0 2>/dev/null || true
fi
if [[ -f /etc/selinux/config ]]; then
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
fi
log_info INHIBITORS "Common inhibitor remediation completed"
}
# =============================================================================
# Filesystem extend helpers (supports ext4 + xfs)
# =============================================================================
get_mount_fstype() {
local mnt="$1"
findmnt -n -o FSTYPE "$mnt" 2>/dev/null || df -T "$mnt" 2>/dev/null | awk 'NR==2{print $2}'
}
get_mount_source() {
local mnt="$1"
findmnt -n -o SOURCE "$mnt" 2>/dev/null || df "$mnt" 2>/dev/null | awk 'NR==2{print $1}'
}
ensure_pkg() {
local pkg="$1"
if cmd_exists dnf; then
dnf install -y "$pkg" >/dev/null 2>&1 || true
else
yum install -y "$pkg" >/dev/null 2>&1 || true
fi
}
grow_filesystem_for_mount() {
local mnt="$1"
local dev="$2"
local fstype="$3"
case "$fstype" in
xfs)
log "Growing XFS filesystem on $mnt"
cmd_exists xfs_growfs || ensure_pkg xfsprogs
xfs_growfs "$mnt"
;;
ext4|ext3|ext2)
log "Growing ext filesystem on $dev"
cmd_exists resize2fs || ensure_pkg e2fsprogs
resize2fs "$dev"
;;
*)
echo "WARN: Unknown/unsupported fstype '$fstype'. LV extended, but filesystem NOT grown."
echo " For XFS run: xfs_growfs $mnt"
echo " For ext4 run: resize2fs $dev"
log_warn FILESYSTEM "Unsupported filesystem type '$fstype' for mount $mnt"
;;
esac
}
extend_mount_force() {
local mount_point="$1"
local configured_lv="$2"
local extend_mb="$3"
local label="$4"
log "Force extend $label ($mount_point) by ${extend_mb}M"
local fstype mount_src target_dev
fstype="$(get_mount_fstype "$mount_point" || true)"
mount_src="$(get_mount_source "$mount_point" || true)"
echo "Detected $mount_point fstype : ${fstype:-unknown}"
echo "Detected $mount_point source : ${mount_src:-unknown}"
target_dev=""
if [[ -n "${configured_lv:-}" && -b "$configured_lv" ]]; then
target_dev="$configured_lv"
elif [[ -n "${mount_src:-}" && -b "$mount_src" ]]; then
target_dev="$mount_src"
fi
if [[ -z "$target_dev" ]]; then
echo "WARN: No block device found for $mount_point (configured_lv='$configured_lv', source='$mount_src'). Skipping."
log_warn FILESYSTEM "No block device found for $mount_point; force extend skipped"
return 0
fi
if ! lvs "$target_dev" >/dev/null 2>&1; then
echo "WARN: Device '$target_dev' for $mount_point is not an LVM LV. Auto-extend not supported by this script. Skipping."
log_warn FILESYSTEM "Device '$target_dev' for $mount_point is not an LVM LV; force extend skipped"
return 0
fi
log "Extending $mount_point LV by +${extend_mb}M on $target_dev (explicit request)"
lvextend -L "+${extend_mb}M" "$target_dev"
grow_filesystem_for_mount "$mount_point" "$target_dev" "$fstype"
df -h "$mount_point"
log_notice FILESYSTEM "$mount_point force-extended by ${extend_mb}M on $target_dev"
}
extend_mount_if_needed() {
local mount_point="$1"
local configured_lv="$2"
local threshold_gb="$3"
local extend_mb="$4"
local label="$5"
log "Extend $label ($mount_point): only if free < ${threshold_gb}G"
local avail_bytes threshold_bytes fstype mount_src target_dev
threshold_bytes=$(( threshold_gb * 1024 * 1024 * 1024 ))
avail_bytes="$(df -B1 --output=avail "$mount_point" 2>/dev/null | awk 'NR==2{print $1}')"
fstype="$(get_mount_fstype "$mount_point" || true)"
mount_src="$(get_mount_source "$mount_point" || true)"
echo "Detected $mount_point fstype : ${fstype:-unknown}"
echo "Detected $mount_point source : ${mount_src:-unknown}"
if [[ -z "${avail_bytes:-}" || ! "${avail_bytes}" =~ ^[0-9]+$ ]]; then
echo "WARN: Unable to read $mount_point free space in bytes. Skipping."
df -h "$mount_point" || true
log_warn FILESYSTEM "Unable to read free space for $mount_point; extend skipped"
return 0
fi
echo "Available $mount_point free space : $(numfmt --to=iec --suffix=B "${avail_bytes}" 2>/dev/null || echo "${avail_bytes} bytes")"
echo "Threshold : ${threshold_gb}G ($(numfmt --to=iec --suffix=B "${threshold_bytes}" 2>/dev/null || echo "${threshold_bytes} bytes"))"
if (( avail_bytes >= threshold_bytes )); then
log "$mount_point has sufficient free space. Skipping extend."
log_info FILESYSTEM "$mount_point has sufficient free space; extend skipped"
return 0
fi
target_dev=""
if [[ -n "${configured_lv:-}" && -b "$configured_lv" ]]; then
target_dev="$configured_lv"
elif [[ -n "${mount_src:-}" && -b "$mount_src" ]]; then
target_dev="$mount_src"
fi
if [[ -z "$target_dev" ]]; then
echo "WARN: No block device found for $mount_point (configured_lv='$configured_lv', source='$mount_src'). Skipping."
log_warn FILESYSTEM "No block device found for $mount_point; extend skipped"
return 0
fi
if ! lvs "$target_dev" >/dev/null 2>&1; then
echo "WARN: Device '$target_dev' for $mount_point is not an LVM LV. Auto-extend not supported by this script. Skipping."
log_warn FILESYSTEM "Device '$target_dev' for $mount_point is not an LVM LV; extend skipped"
return 0
fi
log "Extending $mount_point LV by +${extend_mb}M on $target_dev (free is below ${threshold_gb}G)"
lvextend -L "+${extend_mb}M" "$target_dev"
grow_filesystem_for_mount "$mount_point" "$target_dev" "$fstype"
df -h "$mount_point"
log_notice FILESYSTEM "$mount_point extended by ${extend_mb}M on $target_dev"
}
extend_var_if_needed() {
if [[ "$EXTEND_VAR_GB_SET" == "yes" ]]; then
log "Extend /var explicitly requested via --extend-var-gb ${EXTEND_VAR_GB}"
extend_mount_force "/var" "$VAR_LV" "$(( EXTEND_VAR_GB * 1024 ))" "/var"
else
extend_mount_if_needed "/var" "$VAR_LV" "$VAR_FREE_THRESHOLD_GB" "$(( EXTEND_VAR_GB * 1024 ))" "/var"
fi
}
extend_usr_if_needed() {
extend_mount_if_needed "/usr" "$USR_LV" "$USR_FREE_THRESHOLD_GB" "$USR_EXTEND_MB" "/usr"
}
extend_opt_if_needed() {
extend_mount_if_needed "/opt" "$OPT_LV" "$OPT_FREE_THRESHOLD_GB" "$OPT_EXTEND_MB" "/opt"
}
extend_tmp_if_needed() {
extend_mount_if_needed "/tmp" "$TMP_LV" "$TMP_FREE_THRESHOLD_GB" "$TMP_EXTEND_MB" "/tmp"
}
# =============================================================================
# PHASE: RHEL 7 -> 8
# =============================================================================
phase_7to8() {
log_notice PHASE_START "Phase 7 to 8 started"
ensure_rebooted_or_clear_stamp
set_open_files_ulimit
log "PHASE: RHEL 7 -> 8 (Azure RHUI + Leapp)"
local rhel
rhel="$(detect_major)"
[[ "$rhel" -eq 7 ]] || die "This phase requires RHEL 7. Detected RHEL $rhel"
set_root_password_once
log "Update to latest RHEL 7.9"
yum clean all
yum update -y
enforce_reboot_if_needed "after yum update (RHEL7)"
ensure_rebooted_or_clear_stamp
stop_nfs_autofs_services
comment_auto_direct_entries
backup_and_comment_fstab_nfs_cifs
log "Stop/disable/remove Chef package (SHA1 inhibitor)"
if rpm -qa | grep -qi chef; then
log "Chef package detected. Removing..."
systemctl stop chef-client 2>/dev/null || true
systemctl disable chef-client 2>/dev/null || true
yum remove -y chef chef-client 2>/dev/null || true
log "Chef removed successfully."
log_notice PACKAGE_CLEANUP "Chef package removed"
else
log "Chef package not present. Skipping removal."
log_info PACKAGE_CLEANUP "Chef package not present"
fi
log "Remove EMC NetWorker packages (RSA/SHA1 signature inhibitor)"
if rpm -qa | egrep -qi '^(lgtoxtdclnt|lgtoclnt|lgtonmda)(-|$)'; then
log "EMC NetWorker packages detected. Removing..."
systemctl stop networker 2>/dev/null || true
systemctl stop nsrexecd 2>/dev/null || true
systemctl stop lgtoclnt 2>/dev/null || true
yum remove -y lgtoxtdclnt lgtoclnt lgtonmda 2>/dev/null || true
log "NetWorker packages removed successfully."
log_notice PACKAGE_CLEANUP "EMC NetWorker packages removed"
else
log "EMC NetWorker packages not present. Skipping removal."
log_info PACKAGE_CLEANUP "EMC NetWorker packages not present"
fi
local current_kver old_pkgs
current_kver="$(uname -r)"
log "Current kernel: $current_kver"
log "Checking for old kernel-devel packages..."
old_pkgs="$(rpm -qa 'kernel-devel*' | grep -Fv "kernel-devel-${current_kver}" || true)"
if [[ -z "$old_pkgs" ]]; then
log "No old kernel-devel packages found. Skipping."
log_info PACKAGE_CLEANUP "No old kernel-devel packages found"
else
log "Removing old kernel-devel packages..."
while IFS= read -r pkg; do
[[ -n "$pkg" ]] || continue
log "Removing $pkg"
yum remove -y "$pkg"
done <<< "$old_pkgs"
log "Cleanup completed."
log_notice PACKAGE_CLEANUP "Old kernel-devel packages removed"
fi
pam_tally2_to_faillock
extend_var_if_needed
extend_usr_if_needed
extend_opt_if_needed
extend_tmp_if_needed
log "Install Azure RHUI repo (rhel7)"
yum --config='https://rhelimage.blob.core.windows.net/repositories/rhui-microsoft-azure-rhel7.config' install -y rhui-azure-rhel7
log "Install Leapp packages (RHUI Azure)"
yum -y remove leapp*
yum -y remove rhui*
yum -y --config='https://rhelimage.blob.core.windows.net/repositories/rhui-microsoft-azure-rhel7.config' install rhui-azure-rhel7
yum -y install leapp*
yum -y reinstall rhui-azure-rhel7
rpm -qa | grep -E '^leapp|rhui-azure' || true
remediate_common_inhibitors
if [[ "$SKIP_PREUPGRADE" == "yes" ]]; then
log_warn PREUPGRADE "Skipping leapp preupgrade for 7 to 8 as requested"
log "Skipping leapp preupgrade for 7->8 (per --skip-preupgrade)"
else
log "Leapp preupgrade (7->8, no RHSM)"
leapp preupgrade --no-rhsm || true
log "Show key leapp outputs"
[[ -f /var/log/leapp/leapp-report.txt ]] && tail -n 80 /var/log/leapp/leapp-report.txt || true
[[ -f /var/log/leapp/answerfile ]] && sed -n '1,160p' /var/log/leapp/answerfile || true
log_notice PREUPGRADE "Leapp preupgrade for 7 to 8 completed"
fi
log "Apply Leapp answers"
leapp answer --section remove_pam_pkcs11_module_check.confirm=True || true
leapp answer --section authselect_check.confirm=True || true
prepare_boot_loader_entries
log "Run Leapp upgrade (7->8)"
confirm_or_exit "Ready to run: leapp upgrade --no-rhsm --nogpgcheck ?"
log_notice UPGRADE "Leapp upgrade command for 7 to 8 started"
leapp upgrade --no-rhsm --nogpgcheck
log_notice UPGRADE "Leapp upgrade command for 7 to 8 completed"
log_info BOOT_ENTRY "Skipping set_upgrade_boot_entry for 7 to 8 phase"
log_notice PHASE_END "Phase 7 to 8 completed"
if [[ "$DO_REBOOT" == "yes" ]]; then
log_notice REBOOT "System reboot initiated for 7 to 8 transition"
log "Rebooting now (7 -> 8)"
reboot
else
log_notice REBOOT "Manual reboot required for 7 to 8 transition"
log "Reboot required to continue. Please reboot manually, then run with --phase 8to9."
fi
}
# =============================================================================
# Post cleanup RHEL8 after 7->8 (runs at start of phase 8to9)
# =============================================================================
post_cleanup_rhel8_from7() {
log "Post reboot cleanup helper (RHEL 8 after 7->8)"
local rhel
rhel="$(detect_major)"
[[ "$rhel" -eq 8 ]] || { echo "Skipping cleanup: not on RHEL8 (detected $rhel)"; log_info POST_7TO8_CLEANUP "Skipped because system is not on RHEL8"; return 0; }
rpm -qa | grep kernel | grep el7 || true
yum remove --oldinstallonly -y || true
alternatives --set python /usr/bin/python3 2>/dev/null || true
cmd_exists yum-config-manager && yum-config-manager --save --setopt exclude='' 2>/dev/null || true
cmd_exists yum && yum config-manager --save --setopt exclude='' 2>/dev/null || true
ls -ld /lib/modules/*el7* 2>/dev/null || true
rm -rf /lib/modules/*el7* 2>/dev/null || true
rm -rf /var/log/leapp /root/tmp_leapp_py3 /var/lib/leapp 2>/dev/null || true
yum remove -y leapp-deps-el8 leapp-repository-deps-el8 2>/dev/null || true
yum install -y leapp-upgrade leapp-deps 2>/dev/null || true
log_notice POST_7TO8_CLEANUP "RHEL8 post-7to8 cleanup helper completed"
}
# =============================================================================
# PHASE: RHEL 8 -> 9
# =============================================================================
phase_8to9() {
log_notice PHASE_START "Phase 8 to 9 started"
set_open_files_ulimit
log "PHASE: RHEL 8 -> 9 (Azure RHUI + Leapp)"
local rhel
rhel="$(detect_major)"
[[ "$rhel" -eq 8 ]] || die "This phase requires RHEL 8. Detected RHEL $rhel"
stop_nfs_autofs_services
comment_auto_direct_entries
log "Ensure RHUI is enabled + reinstall leapp-rhui-azure"
cmd_exists yum-config-manager && yum config-manager --set-enabled rhui-microsoft-azure-rhel8 || true
yum -y remove rhui-azure-rhel8 || true
yum -y --config='https://rhelimage.blob.core.windows.net/repositories/rhui-microsoft-azure-rhel8.config' install rhui-azure-rhel8
yum -y reinstall rhui-azure-rhel8 leapp-rhui-azure
remediate_common_inhibitors
extend_tmp_if_needed
if [[ "$SKIP_PREUPGRADE" == "yes" ]]; then
log_warn PREUPGRADE "Skipping leapp preupgrade for 8 to 9 as requested"
log "Skipping leapp preupgrade for 8->9 (per --skip-preupgrade)"
else
log "Leapp preupgrade (8->9)"
if [[ -n "$TARGET_9" ]]; then
leapp preupgrade --no-rhsm --nogpgcheck --target "$TARGET_9" || true
else
leapp preupgrade --no-rhsm || true
fi
log_notice PREUPGRADE "Leapp preupgrade for 8 to 9 completed"
fi
log "Installing VDO packages"
if command -v yum >/dev/null 2>&1; then
yum install -y vdo kmod-kvdo 2>/dev/null || true
elif command -v dnf >/dev/null 2>&1; then
dnf install -y vdo kmod-kvdo 2>/dev/null || true
fi
log "Apply Leapp answers (safe to re-apply)"
leapp answer --section remove_pam_pkcs11_module_check.confirm=True || true
leapp answer --section authselect_check.confirm=True || true
leapp answer --section check_vdo.confirm=true 2>/dev/null || true
prepare_boot_loader_entries
log "Leapp upgrade (8->9)"
confirm_or_exit "Ready to run leapp upgrade to RHEL9 now?"
log_notice UPGRADE "Leapp upgrade command for 8 to 9 started"
if [[ -n "$TARGET_9" ]]; then
leapp upgrade --no-rhsm --nogpgcheck --target "$TARGET_9"
else
leapp upgrade --no-rhsm --nogpgcheck
fi
log_notice UPGRADE "Leapp upgrade command for 8 to 9 completed"
set_upgrade_boot_entry
log "Change PasswordAuthentication to Yes"
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
sshd -t && systemctl reload sshd
log_notice PHASE_END "Phase 8 to 9 completed"
if [[ "$DO_REBOOT" == "yes" ]]; then
log_notice REBOOT "System reboot initiated for 8 to 9 transition"
log "Rebooting now (8 -> 9)"
reboot
else
log_notice REBOOT "Manual reboot required for 8 to 9 transition"
log "Reboot required to complete 8->9. Please reboot manually."
fi
}
# =============================================================================
# Flag: --postcleanup (RHEL8 post-upgrade cleanup after RHEL 7.9 -> 8.10)
# IMPORTANT: SKIPS if not on RHEL8 (no error)
# =============================================================================
postclean_rhel8() {
local rhel
rhel="$(detect_major)"
[[ "$rhel" -eq 8 ]] || {
log_warn POSTCLEANUP "RHEL8 post-cleanup skipped because system is not on RHEL 8"
echo "INFO: RHEL8 post-cleanup skipped. Detected RHEL $rhel"
return 0
}
log_notice POSTCLEANUP "RHEL 8 post-cleanup started"
local PCLOG="/var/log/rhel8_cleanup.log"
exec > >(tee -a "$PCLOG") 2>&1
timestamp() { date +"%Y-%m-%d %H:%M:%S%z"; }
info() { echo -e "\n[$(timestamp)] [INFO] $*"; }
warn() { echo -e "\n[$(timestamp)] [WARN] $*"; }
info "=== PRE-VALIDATION: System Identity & Kernel ==="
whoami
cat /etc/redhat-release || true
uname -r
info "=== PRE-VALIDATION: Current Boot Entries ==="
grubby --info=ALL | grep kernel || true
ls -l /boot/loader/entries 2>/dev/null || true
info "=== PRE-VALIDATION: YUM/DNF Excludes ==="
grep -i '^exclude' /etc/yum.conf 2>/dev/null || echo "No yum excludes set"
grep -i '^exclude' /etc/dnf/dnf.conf 2>/dev/null || echo "No dnf excludes set"
info "=== PRE-VALIDATION: Old EL7 RPMs ==="
rpm -qa | grep -E '\.el7' | sort || echo "No EL7 RPMs found"
info "=== PRE-VALIDATION: Old EL7 Kernels ==="
rpm -qa | grep kernel | grep el7 || echo "No EL7 kernels found"
info "=== PRE-VALIDATION: Old EL7 Kernel Payloads & Modules ==="
ls -l /boot 2>/dev/null | egrep '3\.10|el7|0-rescue' || echo "No old EL7 boot payloads detected"
ls -ld /lib/modules/*el7* 2>/dev/null || echo "No EL7 module directories found"
info "=== ACTION: Remove old install-only kernels ==="
yum remove --oldinstallonly -y || warn "Old install-only kernel cleanup failed or nothing to remove"
info "=== ACTION: Set python alternative to Python3 ==="
alternatives --set python /usr/bin/python3 2>/dev/null || warn "Could not set python alternative to /usr/bin/python3"
info "=== ACTION: Clear yum/dnf excludes ==="
if cmd_exists yum-config-manager; then
yum-config-manager --save --setopt exclude='' 2>/dev/null || warn "yum-config-manager exclude clear failed"
fi
if cmd_exists yum; then
yum config-manager --save --setopt exclude='' 2>/dev/null || true
fi
if cmd_exists dnf; then
dnf config-manager --save --setopt exclude='' 2>/dev/null || true
fi
info "=== ACTION: Remove old EL7 kernel module directories ==="
ls -ld /lib/modules/*el7* 2>/dev/null || true
rm -rf /lib/modules/*el7* 2>/dev/null || warn "Could not remove /lib/modules/*el7*"
info "=== ACTION: Remove old Leapp state from 7->8 upgrade ==="
rm -rf /var/log/leapp /root/tmp_leapp_py3 /var/lib/leapp 2>/dev/null || true
info "=== ACTION: Remove old Leapp dependency packages ==="
yum remove -y leapp-deps-el8 leapp-repository-deps-el8 2>/dev/null || true
info "=== ACTION: Install fresh Leapp packages for next phase if available ==="
yum install -y leapp-upgrade leapp-deps 2>/dev/null || warn "Leapp package install skipped/failed"
info "=== POST-VALIDATION: Remaining EL7 RPMs ==="
rpm -qa | grep -E '\.el7' | sort || echo "No EL7 RPMs found"
info "=== POST-VALIDATION: Remaining EL7 Kernel Modules ==="
ls -ld /lib/modules/*el7* 2>/dev/null || echo "No EL7 module directories found"
info "=== POST-VALIDATION: Current Boot Entries ==="
grubby --info=ALL | grep kernel || true
ls -l /boot/loader/entries 2>/dev/null || true
log_notice POSTCLEANUP "RHEL 8 post-cleanup completed successfully"
info "=== RHEL8 CLEANUP COMPLETE ==="
echo "Log: $PCLOG"
}
# =============================================================================
# Flag: --postcleanup (RHEL9 post-upgrade cleanup)
# IMPORTANT: SKIPS if not on RHEL9 (no error)
# =============================================================================
postclean_rhel9() {
local rhel
rhel="$(detect_major)"
[[ "$rhel" -eq 9 ]] || {
log_warn POSTCLEANUP "Post-cleanup skipped because system is not on RHEL 9"
echo "INFO: --postcleanup skipped (requires RHEL9). Detected RHEL $rhel"
return 0
}
log_notice POSTCLEANUP "RHEL 9 post-cleanup started"
local PCLOG="/var/log/rhel9_cleanup.log"
exec > >(tee -a "$PCLOG") 2>&1
timestamp() { date +"%Y-%m-%d %H:%M:%S%z"; }
info() { echo -e "\n[$(timestamp)] [INFO] $*"; }
warn() { echo -e "\n[$(timestamp)] [WARN] $*"; }
local BOOT_MODE GRUB_OUT
if [[ -d /sys/firmware/efi ]]; then
BOOT_MODE="UEFI"
GRUB_OUT="/boot/efi/EFI/redhat/grub.cfg"
else
BOOT_MODE="BIOS"
GRUB_OUT="/boot/grub2/grub.cfg"
fi
info "Boot mode detected: $BOOT_MODE"
info "GRUB output path: $GRUB_OUT"
info "=== PRE-VALIDATION: System Identity & Kernel ==="
whoami
cat /etc/redhat-release || true
uname -r
info "=== PRE-VALIDATION: Current Boot Entries ==="
grubby --info=ALL | grep kernel || true
ls -l /boot/loader/entries || true
info "=== PRE-VALIDATION: DNF Excludes ==="
grep -i '^exclude' /etc/dnf/dnf.conf || echo "No excludes set"
info "=== PRE-VALIDATION: Leapp Packages ==="
rpm -q leapp-upgrade-el8toel9 leapp python3-leapp || true
info "=== PRE-VALIDATION: Old RPMs (el7/el8) ==="
rpm -qa | grep -E '\.el[78]' | sort || echo "No el7/el8 RPMs found"
info "=== PRE-VALIDATION: Old Kernel Payloads & Modules ==="
ls -l /boot | egrep '3\.10|4\.18|0-rescue' || echo "No old boot payloads detected"
ls -l /lib/modules
info "=== PRE-VALIDATION: BLS Entries for el7/el8 ==="
ls -l /boot/loader/entries | egrep 'el7|el8' || echo "No el7/el8 BLS entries detected"
info "=== PRE-VALIDATION: Dracut Config ==="
grep -R "add_drivers" /etc/dracut.conf.d/ || echo "No dracut add_drivers overrides detected"
info "=== ACTION: Clear DNF excludes ==="
dnf config-manager --save --setopt exclude='' || warn "Could not clear excludes; proceeding"
grep -i '^exclude' /etc/dnf/dnf.conf || echo "No excludes set"
info "=== ACTION: Remove Leapp tooling ==="
dnf remove -y leapp-upgrade-el8toel9 leapp python3-leapp \
|| rpm -e --nodeps leapp-upgrade-el8toel9 leapp python3-leapp \
|| warn "Leapp packages not found or already removed"
info "=== ACTION: Remove el7/el8 RPMs (review printed list) ==="
local EL_78_PKGS
EL_78_PKGS=$(rpm -qa | grep -E '\.el[78]' | grep -vE 'gpg-pubkey|libmodulemd|katello-ca-consumer' || true)
if [[ -n "${EL_78_PKGS}" ]]; then
echo "Packages to remove:"
echo "${EL_78_PKGS}"
dnf remove -y ${EL_78_PKGS} || warn "Some el7/el8 packages could not be removed"
else
echo "No el7/el8 packages found."
fi
info "=== ACTION: Purge old kernel payloads & module directories (el7/el8) ==="
rm -f /boot/vmlinuz-3.10* /boot/initramfs-3.10* /boot/vmlinuz-0-rescue* /boot/initramfs-0-rescue* || true
rm -f /boot/vmlinuz-4.18* /boot/initramfs-4.18* || true
rm -rf /lib/modules/3.10.0-* /lib/modules/4.18.0-* || true
info "=== ACTION: Remove el7/el8 BLS snippets ==="
rm -f /boot/loader/entries/*el7*.conf /boot/loader/entries/*el8*.conf || true
local OLD_KERNELS
OLD_KERNELS=$(grubby --info=ALL | awk -F'"' '/kernel="\/boot\/vmlinuz-3\.10|\/boot\/vmlinuz-4\.18/ {print $2}' || true)
if [[ -n "${OLD_KERNELS}" ]]; then
info "Removing old kernels from GRUB via grubby:"
echo "${OLD_KERNELS}"
while read -r k; do
[[ -n "$k" ]] && grubby --remove-kernel="$k" || true
done <<< "${OLD_KERNELS}"
fi
info "=== ACTION: Normalize or remove dracut config warning file ==="
if [[ -f /etc/dracut.conf.d/vmware-tools.conf ]]; then
echo 'add_drivers+=" "' > /etc/dracut.conf.d/vmware-tools.conf
fi
info "=== ACTION: Rebuild initramfs for the current kernel only ==="
local KVER
KVER="$(uname -r)"
dracut --force /boot/initramfs-"${KVER}".img "${KVER}"
info "=== ACTION: Regenerate GRUB (BLS-aware on RHEL 9) ==="
grub2-mkconfig --update-bls-cmdline -o "${GRUB_OUT}"
info "=== ACTION: Clean DNF cache & show repos ==="
dnf clean all
dnf repolist
info "=== POST-VALIDATION: System Health ==="
getenforce || true
systemctl --failed || true
info "=== ACTION: Restore original /etc/fstab from pre-leapp backup (RHEL9 only) ==="
restore_fstab_backup_rhel9_only
info "=== ACTION: Restore /etc/auto.direct from pre-leapp backup (RHEL9 only) ==="
restore_auto_direct_backup_rhel9_only
info "=== ACTION: Remove VDO packages (RHEL9 only) ==="
remove_vdo_rhel9_only
info "=== ACTION: Enable NFS/AutoFS on RHEL9 only ==="
enable_nfs_autofs_rhel9_only
if [[ "$RESTORE_ROOT_HASH" == "yes" ]]; then
log_notice ROOT_HASH "Root password hash restore requested"
info "=== ACTION: Restore original root password hash ==="
restore_original_root_hash
log_notice ROOT_HASH "Root password hash restored"
else
log_info ROOT_HASH "Root password hash restore skipped"
info "=== ACTION: Restore original root password hash ==="
info "Skipped because --restore-root-hash was not requested"
fi
log_notice POSTCLEANUP "RHEL 9 post-cleanup completed successfully"
info "=== CLEANUP COMPLETE ==="
echo "Log: $PCLOG"
}
# =============================================================================
# Main
# =============================================================================
main() {
[[ $# -eq 0 ]] && { usage; exit 0; }
require_root
parse_args "$@"
log_notice SCRIPT_START "Azure Leapp migration script started"
print_context
if [[ "$PRECONFIG" == "yes" ]]; then
preconfig_backup
log_notice SCRIPT_END "Azure Leapp migration script completed successfully"
exit 0
fi
if [[ "$MONITORLOG" == "yes" ]]; then
monitor_leapp_pkg_log
exit 0
fi
if [[ "$SHA1" == "yes" ]]; then
sha1_rpm_report
log_notice SCRIPT_END "Azure Leapp migration script completed successfully"
exit 0
fi
if [[ "$ADVANCED_PRECHECK" == "yes" ]]; then
advanced_precheck_v2
log_notice SCRIPT_END "Advanced precheck completed"
exit 0
fi
if [[ "$POSTCLEANUP" == "yes" ]]; then
rhel="$(detect_major)"
case "$rhel" in
8)
postclean_rhel8
;;
9)
postclean_rhel9
;;
*)
log_warn POSTCLEANUP "Post-cleanup skipped because system is not on RHEL 8 or RHEL 9"
echo "INFO: --postcleanup skipped. Detected RHEL $rhel. Supported: RHEL8, RHEL9"
;;
esac
log_notice SCRIPT_END "Azure Leapp migration script completed successfully"
exit 0
fi
if [[ "$RESTORE_ROOT_HASH" == "yes" && "$PHASE_SET" == "no" ]]; then
restore_original_root_hash
log_notice SCRIPT_END "Azure Leapp migration script completed successfully"
exit 0
fi
case "$PHASE" in
7to8)
phase_7to8
;;
8to9)
post_cleanup_rhel8_from7
phase_8to9
;;
all)
phase_7to8
;;
esac
log_notice SCRIPT_END "Azure Leapp migration script completed successfully"
log "Done"
}
main "$@"