diff --git a/migrate2rocky/README.md b/migrate2rocky/README.md index de36ade..512fec1 100644 --- a/migrate2rocky/README.md +++ b/migrate2rocky/README.md @@ -71,7 +71,7 @@ dnf command or the migration. You may safely ignore this message. #### Grub still shows kernel entries from previous installation This is normal. The running kernel cannot be safely removed when migrate2rocky -is run. The RockyLinux kernel should come up as the default highlighed kernel +is run. The RockyLinux kernel should come up as the default highlighted kernel on reboot but the other ones will remain until they are removed or replaced by newer kernels. If you want you can manually remove the old kernels after reboot with dnf or rpm. @@ -96,6 +96,21 @@ fix this issue run the following command after migration: ``` ipa-server-upgrade --skip-version-check ``` +> Note: Since ipa-server-upgrade is a java program you will likely have to run +> the command to mitigate the "Symbolic links to Java programs..." issue above +> before running this command. + +#### CentOS SIG repositories disappear after migrating to RockyLinux. + +This is because the centos-release-* packages that contain the .repo files for +the individual repositories depend on centos-release. Storage sig and related +release packages should be available soon from RockyLinux. In the meantine you +can use a command like the following to install the .repo files and continue to +use the repository from CentOS (note please substitute the URL to the release +package for the repo that you need): +``` +rpm2cpio <(curl http://mirror.centos.org/centos/8/extras/x86_64/os/Packages/centos-release-gluster9-1.0-1.el8.noarch.rpm) | cpio -iD/ \*.repo +``` ### Latest Version diff --git a/migrate2rocky/migrate2rocky.sh b/migrate2rocky/migrate2rocky.sh index 370c139..4123473 100644 --- a/migrate2rocky/migrate2rocky.sh +++ b/migrate2rocky/migrate2rocky.sh @@ -46,7 +46,8 @@ fi # Make sure we're root. if (( EUID != 0 )); then - printf '%s\n' "You must run this script as root. Either use sudo or 'su -c ${0}'" >&2 + printf '%s\n' \ + "You must run this script as root. Either use sudo or 'su -c ${0}'" >&2 exit 1 fi @@ -82,10 +83,10 @@ msg_format () { _var="$1" shift if (( $# > 1 )); then - # shellcheck disable=SC2059 - printf -v "$_var" "$@" + # shellcheck disable=SC2059 + printf -v "$_var" "$@" else - printf -v "$_var" "%s" "$1" + printf -v "$_var" "%s" "$1" fi } @@ -105,7 +106,8 @@ errmsg () { printf '%s%s%s' "$errcolor" "$msg" "$nocolor" >&4 } -export LC_ALL=en_US.UTF-8 LANGUAGE=en_US +export LC_ALL=C.UTF-8 +unset LANGUAGE shopt -s nullglob SUPPORTED_MAJOR="8" @@ -121,6 +123,15 @@ gpg_key_sha512="88fe66cf0a68648c2371120d56eb509835266d9efdf7c8b9ac8fc101bdf1f0e0 sm_ca_dir=/etc/rhsm/ca unset tmp_sm_ca_dir +# The repos package for CentOS stream requires special handling. +declare -g -A stream_repos_pkgs +stream_repos_pkgs=( + [rocky-repos]=centos-stream-repos + [epel-release]=epel-next-release +) +# Prefix to add to CentOS stream repo names when renaming them. +stream_prefix=stream- + unset CDPATH exit_message() { @@ -131,23 +142,23 @@ exit_message() { final_message() { errmsg '%s ' \ - "An error occurred while we were attempting to convert your system to" \ - "Rocky Linux. Your system may be unstable. Script will now exit to" \ - "prevent possible damage."$'\n\n' + "An error occurred while we were attempting to convert your system to" \ + "Rocky Linux. Your system may be unstable. Script will now exit to" \ + "prevent possible damage."$'\n\n' logmessage } logmessage(){ printf '%s%s%s\n' "$infocolor" \ - "A log of this installation can be found at $logfile" \ - "$nocolor" >&3 + "A log of this installation can be found at $logfile" \ + "$nocolor" >&3 } # This just grabs a field from os-release and returns it. os-release () ( . /etc/os-release if ! [[ ${!1} ]]; then - return 1 + return 1 fi printf '%s\n' "${!1}" ) @@ -160,7 +171,7 @@ os-release () ( pkg_ver() ( ver=$(rpm -q --qf '%{VERSION}\n' "$1") || return 2 if [[ $(sort -V <<<"$ver"$'\n'"$2" | head -1) != "$2" ]]; then - return 1 + return 1 fi return 0 ) @@ -168,28 +179,35 @@ pkg_ver() ( # Set up a temporary directory. pre_setup () { if ! tmp_dir=$(mktemp -d) || [[ ! -d "$tmp_dir" ]]; then - exit_message "Error creating temp dir" + exit_message "Error creating temp dir" fi # failglob makes pathname expansion fail if empty, dotglob adds files # starting with . to pathname expansion if ( shopt -s failglob dotglob; : "$tmp_dir"/* ) 2>/dev/null ; then - exit_message "Temp dir not empty" + exit_message "Temp dir not empty" fi } # Cleanup function gets rid of the temporary directory. exit_clean () { if [[ -d "$tmp_dir" ]]; then - rm -rf "$tmp_dir" + rm -rf "$tmp_dir" + fi + if [[ -f "$container_macros" ]]; then + rm -f "$container_macros" fi } pre_check () { if [[ -e /etc/rhsm/ca/katello-server-ca.pem ]]; then - exit_message "Migration from Katello-modified systems is not supported by migrate2rocky. See the README file for details." + exit_message \ +'Migration from Katello-modified systems is not supported by migrate2rocky. '\ +'See the README file for details.' fi if [[ -e /etc/salt/minion.d/susemanager.conf ]]; then - exit_message "Migration from Uyuni/SUSE Manager-modified systems is not supported by migrate2rocky. See the README file for details." + exit_message \ +'Migration from Uyuni/SUSE Manager-modified systems is not supported by '\ +'migrate2rocky. See the README file for details.' fi } @@ -259,47 +277,50 @@ create_repo_files () { # All of the binaries used by this script are available in a EL8 minimal install # and are in /bin, so we should not encounter a system where the script doesn't -# work unless it's severly broken. This is just a simple check that will cause +# work unless it's severely broken. This is just a simple check that will cause # the script to bail if any expected system utilities are missing. bin_check() { # Check the platform. if [[ $(os-release PLATFORM_ID) != "$SUPPORTED_PLATFORM" ]]; then - exit_message "This script must be run on an EL8 distribution. Migration from other distributions is not supported." + exit_message \ +'This script must be run on an EL8 distribution. Migration from other '\ +'distributions is not supported.' fi local -a missing bins bins=( - rpm dnf awk column tee tput mkdir - cat arch sort uniq rmdir rm head - curl sha512sum mktemp + rpm dnf awk column tee tput mkdir cat arch sort uniq rmdir + rm head curl sha512sum mktemp systemd-detect-virt sed ) if [[ $update_efi ]]; then - bins+=(findmnt grub2-mkconfig efibootmgr grep mokutil lsblk) + bins+=(findmnt grub2-mkconfig efibootmgr grep mokutil lsblk) fi for bin in "${bins[@]}"; do - if ! type "$bin" >/dev/null 2>&1; then - missing+=("$bin") - fi + if ! type "$bin" >/dev/null 2>&1; then + missing+=("$bin") + fi done local -A pkgs pkgs=( - [dnf]=4.2 - [dnf-plugins-core]=0 + [dnf]=4.2 + [dnf-plugins-core]=0 ) for pkg in "${!pkgs[@]}"; do - ver=${pkgs[$pkg]} - if ! pkg_ver "$pkg" "$ver"; then - # shellcheck disable=SC2140 - exit_message \ + ver=${pkgs[$pkg]} + if ! pkg_ver "$pkg" "$ver"; then + # shellcheck disable=SC2140 + exit_message \ "$pkg >= $ver is required for this script. Please run "\ "\"dnf install $pkg; dnf update\" first." - fi + fi done; if (( ${#missing[@]} )); then - exit_message "Commands not found: ${missing[*]}. Possible bad PATH setting or corrupt installation." + exit_message \ +"Commands not found: ${missing[*]}. Possible bad PATH setting or corrupt "\ +"installation." fi } @@ -309,21 +330,21 @@ bin_check() { repoquery () { local name val prev result result=$( - dnf -q --setopt=epel.excludepkgs=epel-release repoquery -i "$1" || - exit_message "Failed to fetch info for package $1." + dnf -y -q --setopt=epel.excludepkgs=epel-release repoquery -i "$1" || + exit_message "Failed to fetch info for package $1." ) if ! [[ $result ]]; then - # We didn't match this package, the repo could be disabled. - return 1 + # We didn't match this package, the repo could be disabled. + return 1 fi declare -gA repoquery_results=() while IFS=" :" read -r name val; do - if [[ -z $name ]]; then - repoquery_results[$prev]+=" $val" - else - prev=$name - repoquery_results[$name]=$val - fi + if [[ -z $name ]]; then + repoquery_results[$prev]+=" $val" + else + prev=$name + repoquery_results[$name]=$val + fi done <<<"$result" } @@ -331,23 +352,23 @@ repoquery () { # info for the resulting repository. repoinfo () { local name val result - result=$(dnf -q repoinfo "$1") || - exit_message "Failed to fetch info for repository $1." + result=$(dnf -y -q repoinfo "$1") || + exit_message "Failed to fetch info for repository $1." if [[ $result == 'Total packages: 0' ]]; then - # We didn't match this repo. - return 1 + # We didn't match this repo. + return 1 fi declare -gA repoinfo_results=() while IFS=" :" read -r name val; do - if [[ ! ( $name || $val) ]]; then - continue - fi - if [[ -z $name ]]; then - repoinfo_results[$prev]+=" $val" - else - prev=$name - repoinfo_results[$name]=$val - fi + if [[ ! ( $name || $val) ]]; then + continue + fi + if [[ -z $name ]]; then + repoinfo_results[$prev]+=" $val" + else + prev=$name + repoinfo_results[$name]=$val + fi done <<<"$result" # dnf repoinfo doesn't return the gpgkey, but we need that so we have to get @@ -358,21 +379,21 @@ repoinfo () { # of the file. # shellcheck disable=SC2154 repoinfo_results[Repo-gpgkey]=$( - awk ' - $0=="['"${repoinfo_results[Repo-id]}"']",$0=="end_of_file" { - if (l++ < 1) {next} - else if (/^\[.*\]$/) {nextfile} - else if (sub(/^gpgkey\s*=\s*file:\/\//,"")) {print; nextfile} - else {next} - } - ' < "${repoinfo_results[Repo-filename]}" + awk ' + $0=="['"${repoinfo_results[Repo-id]}"']",$0=="end_of_file" { + if (l++ < 1) {next} + else if (/^\[.*\]$/) {nextfile} + else if (sub(/^gpgkey\s*=\s*file:\/\//,"")) {print; nextfile} + else {next} + } + ' < "${repoinfo_results[Repo-filename]}" ) # Add an indicator of whether this is a subscription-manager managed # repository. # shellcheck disable=SC2154 repoinfo_results[Repo-managed]=$( - awk ' + awk ' BEGIN {FS="[)(]"} /^# Managed by \(.*\) subscription-manager$/ {print $2} ' < "${repoinfo_results[Repo-filename]}" @@ -381,28 +402,28 @@ repoinfo () { provides_pkg () ( if [[ ! $1 ]]; then - return 0 + return 0 fi set -o pipefail - provides=$(dnf -q provides "$1" | awk '{print $1; nextfile}') || - return 1 + provides=$(dnf -y -q provides "$1" | awk '{print $1; nextfile}') || + return 1 set +o pipefail pkg=$(rpm -q --queryformat '%{NAME}\n' "$provides") || - pkg=$(dnf -q repoquery --queryformat '%{NAME}\n' "$provides") || - exit_message "Can't get package name for $provides." + pkg=$(dnf -y -q repoquery --queryformat '%{NAME}\n' "$provides") || + exit_message "Can't get package name for $provides." printf '%s\n' "$pkg" ) # If you pass an empty arg as one of the package specs to rpm it will match -# every package on the system. This funtion simply strips out any empty args +# every package on the system. This function simply strips out any empty args # and passes the rest to rpm to avoid this side-effect. saferpm () ( args=() for a in "$@"; do - if [[ $a ]]; then - args+=("$a") - fi + if [[ $a ]]; then + args+=("$a") + fi done rpm "${args[@]}" ) @@ -411,9 +432,9 @@ saferpm () ( safednf () ( args=() for a in "$@"; do - if [[ $a ]]; then - args+=("$a") - fi + if [[ $a ]]; then + args+=("$a") + fi done dnf "${args[@]}" ) @@ -425,60 +446,62 @@ collect_system_info () { # Check the efi mount first, so we can bail before wasting time on all these # other checks if it's not there. if [[ $update_efi ]]; then - local efi_mount kname - declare -g -a efi_disk efi_partition - efi_mount=$(findmnt --mountpoint /boot/efi --output SOURCE \ - --noheadings) || - exit_message "Can't find EFI mount. No EFI boot detected." - kname=$(lsblk -dno kname "$efi_mount") - efi_disk=$(lsblk -dno pkname "/dev/$kname") + local efi_mount kname + declare -g -a efi_disk efi_partition + efi_mount=$(findmnt --mountpoint /boot/efi --output SOURCE \ + --noheadings) || + exit_message "Can't find EFI mount. No EFI boot detected." + kname=$(lsblk -dno kname "$efi_mount") + efi_disk=$(lsblk -dno pkname "/dev/$kname") - if [[ $efi_disk ]]; then - efi_partition=$(<"/sys/block/$efi_disk/$kname/partition") - else - # This is likely an md-raid or other type of virtual disk, we need - # to dig a little deeper to find the actual physical disks and - # partitions. - kname=$(lsblk -dno kname "$efi_mount") - cd "/sys/block/$kname/slaves" || exit_message \ + if [[ $efi_disk ]]; then + efi_partition=$(<"/sys/block/$efi_disk/$kname/partition") + else + # This is likely an md-raid or other type of virtual disk, we need + # to dig a little deeper to find the actual physical disks and + # partitions. + kname=$(lsblk -dno kname "$efi_mount") + cd "/sys/block/$kname/slaves" || exit_message \ "Unable to gather EFI data: Can't cd to /sys/block/$kname/slaves." - if ! (shopt -s failglob; : ./*) 2>/dev/null; then - exit_message \ + if ! (shopt -s failglob; : ./*) 2>/dev/null; then + exit_message \ "Unable to gather EFI data: No slaves found in /sys/block/$kname/slaves." - fi - efi_disk=() - for d in *; do - efi_disk+=("$(lsblk -dno pkname "/dev/$d")") - efi_partition+=("$(<"$d/partition")") - if [[ ! ${efi_disk[-1]} || ! ${efi_partition[-1]} ]]; then - exit_message \ + fi + efi_disk=() + for d in *; do + efi_disk+=("$(lsblk -dno pkname "/dev/$d")") + efi_partition+=("$(<"$d/partition")") + if [[ ! ${efi_disk[-1]} || ! ${efi_partition[-1]} ]]; then + exit_message \ "Unable to gather EFI data: Can't find disk name or partition number for $d." - fi - done - cd - - fi + fi + done + cd - + fi fi # check if EFI secure boot is enabled if [[ $update_efi ]]; then - if mokutil --sb-state 2>&1 | grep -q "SecureBoot enabled"; then - exit_message "EFI Secure Boot is enabled but Rocky Linux doesn't provide a signed shim yet. Disable EFI Secure Boot and reboot." - fi + if mokutil --sb-state 2>&1 | grep -q "SecureBoot enabled"; then + exit_message \ +"EFI Secure Boot is enabled but Rocky Linux doesn't provide a signed shim yet."\ +" Disable EFI Secure Boot and reboot." + fi fi # Don't enable these module streams, even if they are enabled in the source # distro. declare -g -a module_excludes module_excludes=( - libselinux-python:2.8 + libselinux-python:2.8 ) # Some OracleLinux modules have stream names of ol8 instead of rhel8 and ol # instead of rhel. This is a map that does a glob match and replacement. local -A module_glob_map module_glob_map=( - ['%:ol8']=:rhel8 - ['%:ol']=:rhel + ['%:ol8']=:rhel8 + ['%:ol']=:rhel ); # We need to map rockylinux repository names to the equivalent repositories @@ -489,30 +512,33 @@ collect_system_info () { declare -g -A repo_map pkg_repo_map declare -g -a managed_repos pkg_repo_map=( - [baseos]=rootfiles.noarch - [appstream]=apr-util-ldap.$ARCH - [ha]=pacemaker-doc.noarch - [powertools]=libaec-devel.$ARCH - [extras]=epel-release.noarch + [baseos]=rootfiles.noarch + [appstream]=apr-util-ldap.$ARCH + [ha]=pacemaker-doc.noarch + [powertools]=libaec-devel.$ARCH + [extras]=epel-release.noarch ) -# [devel]=quota-devel.$ARCH +# [devel]=quota-devel.$ARCH PRETTY_NAME=$(os-release PRETTY_NAME) infomsg '%s' \ - "Preparing to migrate $PRETTY_NAME to Rocky Linux 8."$'\n\n' \ - "Determining repository names for $PRETTY_NAME" + "Preparing to migrate $PRETTY_NAME to Rocky Linux 8."$'\n\n' \ + "Determining repository names for $PRETTY_NAME" for r in "${!pkg_repo_map[@]}"; do - printf '.' - p=${pkg_repo_map[$r]} - repoquery "$p" || continue - repo_map[$r]=${repoquery_results[Repository]} + printf '.' + p=${pkg_repo_map[$r]} + repoquery "$p" || continue + repo_map[$r]=${repoquery_results[Repository]} done - printf '%s\n' '' '' "Found the following repositories which map from $PRETTY_NAME to Rocky Linux 8:" - column -t -s $'\t' -N "$PRETTY_NAME,Rocky Linux 8" < <(for r in "${!repo_map[@]}"; do - printf '%s\t%s\n' "${repo_map[$r]}" "$r" - done) + printf '%s\n' '' '' \ +"Found the following repositories which map from $PRETTY_NAME to Rocky Linux 8:" + column -t -s $'\t' -N "$PRETTY_NAME,Rocky Linux 8" < <( + for r in "${!repo_map[@]}"; do + printf '%s\t%s\n' "${repo_map[$r]}" "$r" + done + ) infomsg $'\n'"Getting system package names for $PRETTY_NAME" @@ -526,22 +552,22 @@ collect_system_info () { # system-release here is a bit of a hack, but it ensures that the # rocky-repos package will get installed. for r in "${!repo_map[@]}"; do - repoinfo "${repo_map[$r]}" - if [[ $r == "baseos" ]]; then - local baseos_filename=system-release - if [ $use_repo_mirror ]; then - # use repoinfo of default repo baseos not of internal repo mirror, - # to get the correct repo package to remove - repoinfo "baseos" + repoinfo "${repo_map[$r]}" + if [[ $r == "baseos" ]]; then + local baseos_filename=system-release + if [ $use_repo_mirror ]; then + # use repoinfo of default repo baseos not of internal repo mirror, + # to get the correct repo package to remove + repoinfo "baseos" + fi + if [[ ! ${repoinfo_results[Repo-managed]} ]]; then + baseos_filename="${repoinfo_results[Repo-filename]}" + fi + local baseos_gpgkey="${repoinfo_results[Repo-gpgkey]}" + fi + if [[ ${repoinfo_results[Repo-managed]} ]]; then + managed_repos+=("${repo_map[$r]}") fi - if [[ ! ${repoinfo_results[Repo-managed]} ]]; then - baseos_filename="${repoinfo_results[Repo-filename]}" - fi - local baseos_gpgkey="${repoinfo_results[Repo-gpgkey]}" - fi - if [[ ${repoinfo_results[Repo-managed]} ]]; then - managed_repos+=("${repo_map[$r]}") - fi done # First get info for the baseos repo @@ -549,133 +575,176 @@ collect_system_info () { declare -g -A pkg_map provides_pkg_map declare -g -a addl_provide_removes addl_pkg_removes provides_pkg_map=( - [rocky-backgrounds]=system-backgrounds - [rocky-indexhtml]=redhat-indexhtml - [rocky-repos]="$baseos_filename" - [rocky-logos]=system-logos - [rocky-logos-httpd]=system-logos-httpd - [rocky-logos-ipa]=system-logos-ipa - [rocky-gpg-keys]="$baseos_gpgkey" - [rocky-release]=system-release + [rocky-backgrounds]=system-backgrounds + [rocky-indexhtml]=redhat-indexhtml + [rocky-repos]="$baseos_filename" + [rocky-logos]=system-logos + [rocky-logos-httpd]=system-logos-httpd + [rocky-logos-ipa]=system-logos-ipa + [rocky-gpg-keys]="$baseos_gpgkey" + [rocky-release]=system-release ) addl_provide_removes=( - redhat-release - redhat-release-eula + redhat-release + redhat-release-eula ) # Check to make sure that we don't already have a full or partial # RockyLinux install. if [[ $(rpm -qa "${!provides_pkg_map[@]}") ]]; then - exit_message \ + exit_message \ $'Found a full or partial RockyLinux install already in place. Aborting\n' $'because continuing with the migration could cause further damage to system.' fi for pkg in "${!provides_pkg_map[@]}"; do - printf '.' - prov=${provides_pkg_map[$pkg]} - pkg_map[$pkg]=$(provides_pkg "$prov") || - exit_message "Can't get package that provides $prov." + printf '.' + prov=${provides_pkg_map[$pkg]} + pkg_map[$pkg]=$(provides_pkg "$prov") || + exit_message "Can't get package that provides $prov." done for prov in "${addl_provide_removes[@]}"; do - printf '.' - local pkg; - pkg=$(provides_pkg "$prov") || continue - addl_pkg_removes+=("$pkg") + printf '.' + local pkg; + pkg=$(provides_pkg "$prov") || continue + addl_pkg_removes+=("$pkg") done - printf '%s\n' '' '' "Found the following system packages which map from $PRETTY_NAME to Rocky Linux 8:" - column -t -s $'\t' -N "$PRETTY_NAME,Rocky Linux 8" < <(for p in "${!pkg_map[@]}"; do - printf '%s\t%s\n' "${pkg_map[$p]}" "$p" - done) + printf '%s\n' '' '' \ +"Found the following system packages which map from $PRETTY_NAME to Rocky "\ +"Linux 8:" + column -t -s $'\t' -N "$PRETTY_NAME,Rocky Linux 8" < <( + for p in "${!pkg_map[@]}"; do + printf '%s\t%s\n' "${pkg_map[$p]}" "$p" + done + ) infomsg $'\n'"Getting list of installed system packages."$'\n' - readarray -t installed_packages < <(saferpm -qa --queryformat="%{NAME}\n" "${pkg_map[@]}") + readarray -t installed_packages < <( + saferpm -qa --queryformat="%{NAME}\n" "${pkg_map[@]}" + ) declare -g -A installed_pkg_check installed_pkg_map for p in "${installed_packages[@]}"; do - installed_pkg_check[$p]=1 + installed_pkg_check[$p]=1 done for p in "${!pkg_map[@]}"; do - if [[ ${pkg_map[$p]} && ${installed_pkg_check[${pkg_map[$p]}]} ]]; then - installed_pkg_map[$p]=${pkg_map[$p]} - fi + if [[ ${pkg_map[$p]} && ${installed_pkg_check[${pkg_map[$p]}]} ]]; then + installed_pkg_map[$p]=${pkg_map[$p]} + fi done; - printf '%s\n' '' "We will replace the following $PRETTY_NAME packages with their Rocky Linux 8 equivalents" + # Special Handling for CentOS Stream Repos + installed_sys_stream_repos_pkgs=() + installed_stream_repos_pkgs=() + for p in "${!stream_repos_pkgs[@]}"; do + if [[ ${installed_pkg_map[$p]} && + ${installed_pkg_map[$p]} == "${stream_repos_pkgs[$p]}" ]] + then + # System package that needs to be swapped / disabled + installed_pkg_map[$p]= + installed_sys_stream_repos_pkgs+=( ${stream_repos_pkgs[$p]} ) + elif rpm --quiet -q "${stream_repos_pkgs[$p]}"; then + # Non-system package, repos just need to be disabled. + installed_stream_repos_pkgs+=( ${stream_repos_pkgs[$p]} ) + fi + done + + printf '%s\n' '' \ +"We will replace the following $PRETTY_NAME packages with their Rocky Linux 8 "\ +"equivalents" column -t -s $'\t' -N "Packages to be Removed,Packages to be Installed" < <( - for p in "${!installed_pkg_map[@]}"; do - printf '%s\t%s\n' "${installed_pkg_map[$p]}" "$p" - done + for p in "${!installed_pkg_map[@]}"; do + printf '%s\t%s\n' "${installed_pkg_map[$p]}" "$p" + done ) + if (( ${#installed_sys_stream_repos_pkgs[@]} )); then + printf '%s\n' '' \ +'Also to aid the transition from CentOS Stream the following packages will be '\ +'removed from the rpm database but the included repos will be renamed and '\ +'retained but disabled:' \ + "${installed_sys_stream_repos_pkgs[@]}" + fi + + if (( ${#installed_stream_repos_pkgs[@]} )); then + printf '%s\n' '' \ +'Also to aid the transition from CentOS Stream the repos included in the '\ +'following packages will be renamed and retained but disabled:' \ + "${installed_stream_repos_pkgs[@]}" + fi + if (( ${#addl_pkg_removes[@]} )); then - printf '%s\n' '' "In addition to the above the following system packages will be removed:" \ - "${addl_pkg_removes[@]}" + printf '%s\n' '' \ +"In addition to the above the following system packages will be removed:" \ + "${addl_pkg_removes[@]}" fi # Release packages that are part of SIG's should be listed below when they # are available. # UPDATE: We may or may not do something with SIG's here, it could just be - # left as a separate excersize to swap out the sig repos. + # left as a separate exercise to swap out the sig repos. #sigs_to_swap=() infomsg '%s' $'\n' \ - $'Getting a list of enabled modules for the system repositories.\n' + $'Getting a list of enabled modules for the system repositories.\n' # Get a list of system enabled modules. readarray -t enabled_modules < <( - set -e -o pipefail - safednf -q "${repo_map[@]/#/--repo=}" module list --enabled | - awk ' - $1 == "@modulefailsafe", /^$/ {next} - $1 == "Name", /^$/ {if ($1!="Name" && !/^$/) print $1":"$2} - ' | sort -u - set +e +o pipefail + set -e -o pipefail + safednf -y -q "${repo_map[@]/#/--repo=}" module list --enabled | + awk ' + $1 == "@modulefailsafe", /^$/ {next} + $1 == "Name", /^$/ {if ($1!="Name" && !/^$/) print $1":"$2} + ' | sort -u + set +e +o pipefail ) # Map the known module name differences. disable_modules=() local i gl repl mod for i in "${!enabled_modules[@]}"; do - mod=${enabled_modules[$i]} - for gl in "${!module_glob_map[@]}"; do - repl=${module_glob_map[$gl]} - mod=${mod/$gl/$repl} - done - if [[ $mod != "${enabled_modules[$i]}" ]]; then - disable_modules+=(${enabled_modules[$i]}) - enabled_modules[$i]=$mod - fi + mod=${enabled_modules[$i]} + for gl in "${!module_glob_map[@]}"; do + repl=${module_glob_map[$gl]} + mod=${mod/$gl/$repl} + done + if [[ $mod != "${enabled_modules[$i]}" ]]; then + disable_modules+=(${enabled_modules[$i]}) + enabled_modules[$i]=$mod + fi done # Remove entries matching any excluded modules. if (( ${#module_excludes[@]} )); then - printf '%s\n' '' "Excluding modules:" "${module_excludes[@]}" - local -A module_check='()' - local -a tmparr='()' - for m in "${module_excludes[@]}"; do - module_check[$m]=1 - done - for m in "${enabled_modules[@]}"; do - if [[ ! ${module_check[$m]} ]]; then - tmparr+=("$m") - fi - done - enabled_modules=("${tmparr[@]}") + printf '%s\n' '' "Excluding modules:" "${module_excludes[@]}" + local -A module_check='()' + local -a tmparr='()' + for m in "${module_excludes[@]}"; do + module_check[$m]=1 + done + for m in "${enabled_modules[@]}"; do + if [[ ! ${module_check[$m]} ]]; then + tmparr+=("$m") + fi + done + enabled_modules=("${tmparr[@]}") fi printf '%s\n' '' "Found the following modules to re-enable at completion:" \ - "${enabled_modules[@]}" '' + "${enabled_modules[@]}" '' if (( ${#managed_repos[@]} )); then - printf '%s\n' '' "In addition, since this system uses subscription-manger the following managed repos will be disabled:" \ - "${managed_repos[@]}" + printf '%s\n' '' \ +'In addition, since this system uses subscription-manager the following '\ +'managed repos will be disabled:' \ + "${managed_repos[@]}" fi } convert_info_dir=/root/convert -unset convert_to_rocky reinstall_all_rpms verify_all_rpms update_efi +unset convert_to_rocky reinstall_all_rpms verify_all_rpms update_efi \ + container_macros usage() { printf '%s\n' \ @@ -695,11 +764,15 @@ usage() { } >&2 generate_rpm_info() { - mkdir /root/convert - infomsg "Creating a list of RPMs installed: $1"$'\n' - rpm -qa --qf "%{NAME}|%{VERSION}|%{RELEASE}|%{INSTALLTIME}|%{VENDOR}|%{BUILDTIME}|%{BUILDHOST}|%{SOURCERPM}|%{LICENSE}|%{PACKAGER}\n" | sort > "${convert_info_dir}/$HOSTNAME-rpm-list-$1.log" - infomsg "Verifying RPMs installed against RPM database: $1"$'\n\n' - rpm -Va | sort -k3 > "${convert_info_dir}/$HOSTNAME-rpm-list-verified-$1.log" + mkdir /root/convert + infomsg "Creating a list of RPMs installed: $1"$'\n' + rpm -qa --qf \ +"%{NAME}|%{VERSION}|%{RELEASE}|%{INSTALLTIME}|%{VENDOR}|%{BUILDTIME}|"\ +"%{BUILDHOST}|%{SOURCERPM}|%{LICENSE}|%{PACKAGER}\n" | + sort > "${convert_info_dir}/$HOSTNAME-rpm-list-$1.log" + infomsg "Verifying RPMs installed against RPM database: $1"$'\n\n' + rpm -Va | sort -k3 > \ + "${convert_info_dir}/$HOSTNAME-rpm-list-verified-$1.log" } # Run a dnf update before the actual migration. @@ -711,35 +784,55 @@ $'unstable state. Please correct the issues shown here and try again.' } package_swaps() { - # Save off any subscription-manger keys, just in case. + # Save off any subscription-manager keys, just in case. if ( shopt -s failglob dotglob; : "$sm_ca_dir"/* ) 2>/dev/null ; then - tmp_sm_ca_dir=$tmp_dir/sm-certs - mkdir "$tmp_sm_ca_dir" || - exit_message "Could not create directory: $tmp_sm_ca_dir" - cp -f -dR --preserve=all "$sm_ca_dir"/* "$tmp_sm_ca_dir/" || - exit_message "Could not copy certs to $tmp_sm_ca_dir" + tmp_sm_ca_dir=$tmp_dir/sm-certs + mkdir "$tmp_sm_ca_dir" || + exit_message "Could not create directory: $tmp_sm_ca_dir" + cp -f -dR --preserve=all "$sm_ca_dir"/* "$tmp_sm_ca_dir/" || + exit_message "Could not copy certs to $tmp_sm_ca_dir" fi # prepare repo parameters local -a dnfparameters for repo in "${!repo_urls[@]}"; do - dnfparameters+=( "--repofrompath=${repo},${repo_urls[${repo}]}" ) - dnfparameters+=( "--setopt=${repo}.gpgcheck=1" ) - dnfparameters+=( "--setopt=${repo}.gpgkey=file://${gpg_key_file}" ) + dnfparameters+=( "--repofrompath=${repo},${repo_urls[${repo}]}" ) + dnfparameters+=( "--setopt=${repo}.gpgcheck=1" ) + dnfparameters+=( "--setopt=${repo}.gpgkey=file://${gpg_key_file}" ) done + # CentOS Stream specific processing + if (( ${#installed_stream_repos_pkgs[@]} )); then + # Get a list of the repo files. + local -a repos_files + readarray -t repos_files < <( + saferpm -ql "${installed_sys_stream_repos_pkgs[@]}" \ + "${installed_stream_repos_pkgs[@]}" | + grep '^/etc/yum\.repos\.d/.\+\.repo$' + ) + + # Remove the package from the rpm db. + saferpm -e --justdb --nodeps -a "${installed_sys_stream_repos_pkgs[@]}" || + exit_message \ +"Could not remove packages from the rpm db: ${installed_sys_stream_repos_pkgs[@]}" + + # Rename the stream repos with a prefix. + sed -i 's/^\[/['"$stream_prefix"'/' "${repos_files[@]}" + fi + # Use dnf shell to swap the system packages out. safednf -y shell --disablerepo=\* --noautoremove \ - --setopt=protected_packages= --setopt=keepcache=True \ - "${dnfparameters[@]}" \ - </dev/null - )]=$rpm - done + # Get a list of rpm packages to package names + local -A rpm_map + local -a file_list + for rpm in /var/cache/dnf/{rockybaseos,rockyappstream}-*/packages/*.rpm + do + rpm_map[$( + rpm -q --qf '%{NAME}\n' --nodigest "$rpm" 2>/dev/null + )]=$rpm + done - # Attempt to install. - for pkg in "${check_installed[@]}"; do - printf '%s\n' "$pkg" - if ! rpm -i --force --nodeps --nodigest "${rpm_map[$pkg]}" \ - 2>/dev/null; then - # Try to install the package in just the db, then clean it up. - rpm -i --force --justdb --nodeps --nodigest "${rpm_map[$pkg]}" \ - 2>/dev/null + # Attempt to install. + for pkg in "${check_installed[@]}"; do + printf '%s\n' "$pkg" + if ! rpm -i --force --nodeps --nodigest "${rpm_map[$pkg]}" \ + 2>/dev/null; then + # Try to install the package in just the db, then clean it up. + rpm -i --force --justdb --nodeps --nodigest "${rpm_map[$pkg]}" \ + 2>/dev/null - # Get list of files that are still causing problems and donk - # them. - readarray -t file_list < <( - rpm -V "$pkg" 2>/dev/null | awk '$1!="missing" {print $2}' - ) - for file in "${file_list[@]}"; do - rmdir "$file" || - rm -f "$file" || - rm -rf "$file" - done + # Get list of files that are still causing problems and donk + # them. + readarray -t file_list < <( + rpm -V "$pkg" 2>/dev/null | awk '$1!="missing" {print $2}' + ) + for file in "${file_list[@]}"; do + rmdir "$file" || + rm -f "$file" || + rm -rf "$file" + done - # Now try re-installing the package to replace the missing - # files. Regardless of the outcome here we just accept it and - # move on and hope for the best. - rpm -i --reinstall --force --nodeps --nodigest \ - "${rpm_map[$pkg]}" 2>/dev/null - fi - done + # Now try re-installing the package to replace the missing + # files. Regardless of the outcome here we just accept it and + # move on and hope for the best. + rpm -i --reinstall --force --nodeps --nodigest \ + "${rpm_map[$pkg]}" 2>/dev/null + fi + done fi # Distrosync if [ -z $use_repo_mirror ]; then infomsg $'Ensuring repos are enabled before the package swap\n' safednf -y --enableplugin=config-manager config-manager \ - --set-enabled "${!repo_map[@]}" || { - printf '%s\n' 'Repo name missing?' - exit 25 + --set-enabled "${!repo_map[@]}" || { + printf '%s\n' 'Repo name missing?' + exit 25 } fi if (( ${#managed_repos[@]} )); then - # Filter the managed repos for ones still in the system. - readarray -t managed_repos < <( - safednf -q repolist "${managed_repos[@]}" | awk '$1!="repo" {print $1}' - ) + # Filter the managed repos for ones still in the system. + readarray -t managed_repos < <( + safednf -y -q repolist "${managed_repos[@]}" | + awk '$1!="repo" {print $1}' + ) - if (( ${#managed_repos[@]} )); then - infomsg $'\nDisabling subscription managed repos\n' - safednf -y --enableplugin=config-manager config-manager \ - --disable "${managed_repos[@]}" - fi + if (( ${#managed_repos[@]} )); then + infomsg $'\nDisabling subscription managed repos\n' + safednf -y --enableplugin=config-manager config-manager \ + --disable "${managed_repos[@]}" + fi fi if (( ${#disable_modules[@]} )); then - infomsg $'Disabling modules\n\n' - safednf -y module disable "${disable_modules[@]}" || - exit_message "Can't disable modules ${disable_modules[*]}" + infomsg $'Disabling modules\n\n' + safednf -y module disable "${disable_modules[@]}" || + exit_message "Can't disable modules ${disable_modules[*]}" fi if (( ${#enabled_modules[@]} )); then - infomsg $'Enabling modules\n\n' - safednf -y module enable "${enabled_modules[@]}" || - exit_message "Can't enable modules ${enabled_modules[*]}" + infomsg $'Enabling modules\n\n' + safednf -y module enable "${enabled_modules[@]}" || + exit_message "Can't enable modules ${enabled_modules[*]}" fi - # Make sure that excluded repos are disabled. + # Make sure that excluded modules are disabled. infomsg $'Disabling excluded modules\n\n' safednf -y module disable "${module_excludes[@]}" || - exit_message "Can't disable modules ${module_excludes[*]}" + exit_message "Can't disable modules ${module_excludes[*]}" infomsg $'\nSyncing packages\n\n' dnf -y distro-sync || exit_message "Error during distro-sync." + # Disable Stream repos. + if (( ${#installed_sys_stream_repos_pkgs[@]} || + ${#installed_stream_repos_pkgs[@]} )); then + dnf -y --enableplugin=config_manager config-manager --set-disabled \ + "$stream_prefix*" || + errmsg \ +$'Failed to disable CentOS Stream repos, please check and disable manually.\n' + + infomsg $'\nCentOS Stream Migration Notes:\n\n' + cat < "$container_macros" + elif [[ -d /sys/firmware/efi/ ]]; then + declare -g update_efi + update_efi=true fi } # Called to update the EFI boot. fix_efi () ( grub2-mkconfig -o /boot/efi/EFI/rocky/grub.cfg || - exit_message "Error updating the grub config." + exit_message "Error updating the grub config." for i in "${!efi_disk[@]}"; do - efibootmgr -c -d "/dev/${efi_disk[$i]}" -p "${efi_partition[$i]}" \ - -L "Rocky Linux" -l /EFI/rocky/grubx64.efi || - exit_message "Error updating uEFI firmware." + efibootmgr -c -d "/dev/${efi_disk[$i]}" -p "${efi_partition[$i]}" \ + -L "Rocky Linux" -l /EFI/rocky/grubx64.efi || + exit_message "Error updating uEFI firmware." done ) # Download and verify the Rocky Linux package signing key establish_gpg_trust () { - # create temp dir and verify it is really created and empty, so we are sure deleting it afterwards won't cause any harm + # create temp dir and verify it is really created and empty, so we are sure + # deleting it afterwards won't cause any harm declare -g gpg_tmp_dir gpg_tmp_dir=$tmp_dir/gpg if ! mkdir "$gpg_tmp_dir" || [[ ! -d "$gpg_tmp_dir" ]]; then - exit_message "Error creating temp dir" + exit_message "Error creating temp dir" fi - # failglob makes pathname expansion fail if empty, dotglob adds files starting with . to pathname expansion + # failglob makes pathname expansion fail if empty, dotglob adds files + # starting with . to pathname expansion if ( shopt -s failglob dotglob; : "$gpg_tmp_dir"/* ) 2>/dev/null ; then - exit_message "Temp dir not empty" + exit_message "Temp dir not empty" fi # extract the filename from the url, use the temp dir just created declare -g gpg_key_file="$gpg_tmp_dir/${gpg_key_url##*/}" if ! curl -L -o "$gpg_key_file" --silent --show-error "$gpg_key_url"; then - rm -rf "$gpg_tmp_dir" - exit_message "Error downloading the Rocky Linux signing key." + rm -rf "$gpg_tmp_dir" + exit_message "Error downloading the Rocky Linux signing key." fi if ! sha512sum --quiet -c <<<"$gpg_key_sha512 $gpg_key_file"; then - rm -rf "$gpg_tmp_dir" - exit_message "Error validating the signing key." + rm -rf "$gpg_tmp_dir" + exit_message "Error validating the signing key." fi }