2021-05-11 22:55:56 +00:00
#!/bin/bash
#
# migrate2rocky - Migrate another EL8 distribution to RockyLinux 8.
# By: Peter Ajamian <peter@pajamian.dhs.org>
# Adapted from centos2rocky.sh by label <label@rockylinux.org>
#
2021-05-31 15:13:33 +00:00
# The latest version of this script can be found at:
# https://github.com/rocky-linux/rocky-tools
#
2021-06-01 05:11:37 +00:00
# Copyright (c) 2021 Rocky Enterprise Software Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
2021-05-11 22:55:56 +00:00
## Rocky is RC status. Using this script means you accept all risks of system
## instability.
2021-06-12 12:30:05 +00:00
# These checks need to be right at the top because we start with bash-isms right
2021-06-12 11:58:38 +00:00
# away in this script.
if [ -n " $POSIXLY_CORRECT " ] || [ -z " $BASH_VERSION " ] ; then
printf '%s\n' "bash >= 4.0 is required for this script." >& 2
exit 1
fi
2021-06-12 12:30:05 +00:00
# We need bash version >= 4 for associative arrays.
if ( ( BASH_VERSINFO < 4 ) ) ; then
printf '%s\n' "bash >= 4.0 is required for this script." >& 2
exit 1
fi
2021-06-12 12:36:43 +00:00
# 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
exit 1
fi
2021-06-12 11:58:38 +00:00
2021-05-11 22:55:56 +00:00
# Path to logfile
2021-05-29 09:22:01 +00:00
logfile = /var/log/migrate2rocky.log
2021-05-11 22:55:56 +00:00
# Send all output to the logfile as well as stdout.
2021-06-20 12:01:02 +00:00
# After the following we get:
# Output to 1 goes to stdout and the logfile.
# Output to 2 goes to stderr and the logfile.
# Output to 3 just goes to stdout.
# Output to 4 just goes to stderr.
# Output to 5 just goes to the logfile.
2021-05-11 22:55:56 +00:00
truncate -s0 " $logfile "
2021-06-20 12:01:02 +00:00
exec \
3>& 1 \
4>& 2 \
5>> " $logfile " \
> >( tee -a " $logfile " ) \
2> >( tee -a " $logfile " >& 2)
2021-05-11 22:55:56 +00:00
2021-05-18 12:05:01 +00:00
# List nocolor last here so that -x doesn't bork the display.
2021-06-20 12:01:02 +00:00
errcolor = $( tput setaf 1)
infocolor = $( tput setaf 6)
nocolor = $( tput op)
# Single arg just gets returned verbatim, multi arg gets formatted via printf.
# First arg is the name of a variable to store the results.
msg_format ( ) {
local _var
_var = " $1 "
shift
if ( ( $# > 1 ) ) ; then
printf -v " $_var " " $@ "
else
printf -v " $_var " "%s" " $1 "
fi
}
# Send an info message to the log file and stdout (with color)
infomsg ( ) {
local msg
msg_format msg " $@ "
printf '%s' " $msg " >& 5
printf '%s%s%s' " $infocolor " " $msg " " $nocolor " >& 3
}
# Send an error message to the log file and stderr (with color)
errmsg ( ) {
local msg
msg_format msg " $@ "
printf '%s' " $msg " >& 5
printf '%s%s%s' " $errcolor " " $msg " " $nocolor " >& 4
}
2021-05-11 22:55:56 +00:00
export LANG = en_US.UTF-8
2021-05-13 14:26:20 +00:00
shopt -s nullglob
2021-05-11 22:55:56 +00:00
SUPPORTED_MAJOR = "8"
SUPPORTED_PLATFORM = " platform:el $SUPPORTED_MAJOR "
ARCH = $( arch)
2021-05-31 16:41:27 +00:00
gpg_key_url = "https://dl.rockylinux.org/pub/rocky/RPM-GPG-KEY-rockyofficial"
gpg_key_sha512 = "88fe66cf0a68648c2371120d56eb509835266d9efdf7c8b9ac8fc101bdf1f0e0197030d3ea65f4b5be89dc9d1ef08581adb068815c88d7b1dc40aa1c32990f6a"
2021-06-20 17:10:24 +00:00
sm_ca_dir = /etc/rhsm/ca
unset tmp_sm_ca_dir
2021-05-31 16:41:27 +00:00
# all repos must be signed with the same key given in $gpg_key_url
declare -A repo_urls
2021-05-11 22:55:56 +00:00
repo_urls = (
2021-05-31 16:41:27 +00:00
[ rockybaseos] = " https://dl.rockylinux.org/pub/rocky/ ${ SUPPORTED_MAJOR } /BaseOS/ $ARCH /os/ "
[ rockyappstream] = " https://dl.rockylinux.org/pub/rocky/ ${ SUPPORTED_MAJOR } /AppStream/ $ARCH /os/ "
2021-05-11 22:55:56 +00:00
)
unset CDPATH
exit_message( ) {
2021-06-20 12:01:02 +00:00
errmsg $'\n' " $1 " $'\n\n'
2021-05-11 22:55:56 +00:00
final_message
exit 1
2021-06-20 12:01:02 +00:00
}
2021-05-11 22:55:56 +00:00
final_message( ) {
2021-06-20 12:01:02 +00:00
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'
logmessage
2021-05-11 22:55:56 +00:00
}
logmessage( ) {
2021-06-20 12:01:02 +00:00
printf '%s%s%s\n' " $infocolor " \
" A log of this installation can be found at $logfile " \
" $nocolor " >& 3
2021-05-11 22:55:56 +00:00
}
# This just grabs a field from os-release and returns it.
2021-05-11 23:11:36 +00:00
os-release ( ) (
2021-05-11 22:55:56 +00:00
. /etc/os-release
if ! [ [ ${ !1 } ] ] ; then
return 1
fi
printf '%s\n' " ${ !1 } "
2021-05-11 23:11:36 +00:00
)
2021-05-11 22:55:56 +00:00
2021-05-29 02:17:06 +00:00
# Check the version of a package against a supplied version number. Note that
# this uses sort -V to compare the versions which isn't perfect for rpm package
# versions, but to do a proper comparison we would need to use rpmdev-vercmp in
# the rpmdevtools package which we don't want to force-install. sort -V should
# be adequate for our needs here.
pkg_ver( ) (
ver = $( rpm -q --qf '%{VERSION}\n' " $1 " ) || return 2
2021-06-03 14:16:43 +00:00
if [ [ $( sort -V <<< " $ver " $'\n' " $2 " | head -1) != " $2 " ] ] ; then
2021-05-29 02:17:06 +00:00
return 1
fi
return 0
)
2021-06-20 17:10:24 +00:00
# Set up a temporary directory.
pre_setup ( ) {
if ! tmp_dir = $( mktemp -d) || [ [ ! -d " $tmp_dir " ] ] ; then
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"
fi
}
# Cleanup function gets rid of the temporary directory.
exit_clean ( ) {
if [ [ -d " $tmp_dir " ] ] ; then
rm -rf " $tmp_dir "
fi
}
2021-06-04 23:51:02 +00:00
pre_check ( ) {
2021-06-20 21:08:30 +00:00
if [ [ -e /etc/yum.repos.d/redhat.repo ] ] ; then
exit_message "Migration from Katello-modified systems is not supported by migrate2rocky.\nPlease unconfigure all Katello configured repos, remove the /etc/yum.repos.d/redhat.repo file and re-enable the original repos."
fi
if [ [ -e /etc/yum.repos.d/susemanager\: channels.repo ] ] ; then
exit_message "Migration from Uyuni-modified systems is not supported by migrate2rocky.\nPlease unconfigure all Uyuni configured repos, remove the /etc/yum.repos.d/susemanager:channels.repo file and re-enable the original repos."
2021-06-04 23:51:02 +00:00
fi
}
2021-05-11 22:55:56 +00:00
# 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
# the script to bail if any expected system utilities are missing.
bin_check( ) {
2021-06-12 11:58:38 +00:00
# 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."
fi
2021-05-29 06:33:57 +00:00
local -a missing bins
bins = (
rpm dnf awk column tee tput mkdir
cat arch sort uniq rmdir rm head
2021-05-31 16:41:27 +00:00
curl sha512sum mktemp
2021-05-29 06:33:57 +00:00
)
if [ [ $update_efi ] ] ; then
2021-05-31 15:06:31 +00:00
bins += ( findmnt grub2-mkconfig efibootmgr grep mokutil)
2021-05-29 06:33:57 +00:00
fi
for bin in " ${ bins [@] } " ; do
2021-05-11 22:55:56 +00:00
if ! type " $bin " >/dev/null 2>& 1; then
missing += ( " $bin " )
fi
done
2021-05-29 02:17:06 +00:00
if ! pkg_ver dnf 4.2; then
exit_message 'dnf >= 4.2 is required for this script. Please run "dnf update" first.'
fi
2021-05-11 22:55:56 +00:00
if ( ( ${# missing [@] } ) ) ; then
2021-06-03 14:16:43 +00:00
exit_message " Commands not found: ${ missing [*] } . Possible bad PATH setting or corrupt installation. "
2021-05-11 22:55:56 +00:00
fi
}
# This function will overwrite the repoquery_results associative array with the
# info for the resulting package. Note that we explicitly disable the epel repo
# as a special-case below to avoid having the extras repository map to epel.
repoquery ( ) {
2021-06-03 14:16:43 +00:00
local name val prev result
result = $(
2021-05-11 22:55:56 +00:00
dnf -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
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
done <<< " $result "
}
# This function will overwrite the repoinfo_results associative array with the
# info for the resulting repository.
repoinfo ( ) {
local name val result
result = $( dnf -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
fi
declare -gA repoinfo_results = ( )
while IFS = " :" read -r name val; do
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
# it from the repo file itself.
2021-05-13 14:26:20 +00:00
# "end_of_file" is a hack here. Since it is not a valid dnf setting we know
# it won't appear in a .repo file on a line by itself, so it's safe to
# search for the string to make the awk parser look all the way to the end
# of the file.
2021-05-11 22:55:56 +00:00
repoinfo_results[ Repo-gpgkey] = $(
awk '
2021-05-13 14:26:20 +00:00
$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] } "
2021-05-11 22:55:56 +00:00
)
2021-05-14 03:18:43 +00:00
# Add an indicator of whether this is a subscription-manager managed
# repository.
repoinfo_results[ Repo-managed] = $(
awk '
BEGIN { FS = "[)(]" }
/^# Managed by \( .*\) subscription-manager$/ { print $2 }
' < " ${ repoinfo_results [Repo-filename] } "
)
2021-05-11 22:55:56 +00:00
}
2021-05-13 14:26:20 +00:00
provides_pkg ( ) (
2021-05-14 03:18:43 +00:00
if [ [ ! $1 ] ] ; then
return 0
fi
2021-05-13 14:26:20 +00:00
set -o pipefail
provides = $( dnf -q provides " $1 " | awk '{print $1; nextfile}' ) ||
return 1
set +o pipefail
2021-06-04 10:11:15 +00:00
pkg = $( rpm -q --queryformat '%{NAME}\n' " $provides " ) ||
pkg = $( dnf -q repoquery --queryformat '%{NAME}\n' " $provides " ) ||
2021-05-13 14:26:20 +00:00
exit_message " Can't get package name for $provides . "
printf '%s\n' " $pkg "
)
2021-06-04 10:11:15 +00:00
# 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
# and passes the rest to rpm to avoid this side-effect.
saferpm ( ) (
args = ( )
for a in " $@ " ; do
if [ [ $a ] ] ; then
args += ( " $a " )
fi
done
rpm " ${ args [@] } "
)
# And a similar function for dnf
safednf ( ) (
args = ( )
for a in " $@ " ; do
if [ [ $a ] ] ; then
args += ( " $a " )
fi
done
dnf " ${ args [@] } "
)
2021-05-11 22:55:56 +00:00
collect_system_info ( ) {
2021-05-29 06:33:57 +00:00
# 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
declare -g efi_mount
efi_mount = $( findmnt --mountpoint /boot/efi --output SOURCE \
--noheadings) ||
exit_message "Can't find EFI mount. No EFI boot detected."
fi
2021-05-31 15:06:31 +00:00
# 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
fi
2021-05-18 11:43:58 +00:00
# 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
)
2021-05-11 22:55:56 +00:00
# We need to map rockylinux repository names to the equivalent repositories
# in the source distro. To do that we look for known packages in each
# repository and see what repo they came from. We need to use repoquery for
# this which requires downloading the package, so we pick relatively small
# packages for this.
declare -g -A repo_map pkg_repo_map
2021-05-14 03:18:43 +00:00
declare -g -a managed_repos
2021-05-11 22:55:56 +00:00
pkg_repo_map = (
[ baseos] = rootfiles.noarch
[ appstream] = apr-util-ldap.$ARCH
[ ha] = pacemaker-doc.noarch
[ powertools] = libaec-devel.$ARCH
[ extras] = epel-release.noarch
)
2021-05-16 02:05:42 +00:00
# [devel]=quota-devel.$ARCH
2021-05-11 22:55:56 +00:00
PRETTY_NAME = $( os-release PRETTY_NAME)
2021-06-20 12:01:02 +00:00
infomsg '%s' \
" Preparing to migrate $PRETTY_NAME to Rocky Linux 8. " $'\n\n' \
" Determining repository names for $PRETTY_NAME "
2021-05-11 22:55:56 +00:00
for r in " ${ !pkg_repo_map[@] } " ; do
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: "
2021-06-04 10:11:15 +00:00
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 "
2021-05-11 22:55:56 +00:00
done )
2021-06-20 12:01:02 +00:00
infomsg $'\n' " Getting system package names for $PRETTY_NAME "
2021-05-11 22:55:56 +00:00
# We don't know what the names of these packages are, we have to discover
# them via various means. The most common means is to look for either a
# distro-agnostic provides or a filename. In a couple of cases we need to
# jump through hoops to get a filename that is provided specifically by the
# source distro.
2021-05-14 03:18:43 +00:00
# Get info for each repository to determine which ones are subscription
# managed.
# 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 [ [ ! ${ 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
2021-05-11 22:55:56 +00:00
# First get info for the baseos repo
repoinfo " ${ repo_map [baseos] } "
declare -g -A pkg_map provides_pkg_map
2021-05-13 14:26:20 +00:00
declare -g -a addl_provide_removes addl_pkg_removes
2021-05-11 22:55:56 +00:00
provides_pkg_map = (
[ rocky-backgrounds] = system-backgrounds
[ rocky-indexhtml] = redhat-indexhtml
2021-05-14 03:18:43 +00:00
[ rocky-repos] = " $baseos_filename "
2021-05-11 22:55:56 +00:00
[ rocky-logos] = system-logos
2021-05-14 03:18:43 +00:00
[ rocky-gpg-keys] = " $baseos_gpgkey "
2021-05-11 22:55:56 +00:00
[ rocky-release] = system-release
)
2021-05-13 14:26:20 +00:00
addl_provide_removes = (
redhat-release-eula
)
2021-05-11 22:55:56 +00:00
for pkg in " ${ !provides_pkg_map[@] } " ; do
printf '.'
prov = ${ provides_pkg_map [ $pkg ] }
2021-06-03 14:16:43 +00:00
pkg_map[ $pkg ] = $( provides_pkg " $prov " ) ||
2021-05-11 22:55:56 +00:00
exit_message " Can't get package that provides $prov . "
2021-05-13 14:26:20 +00:00
done
for prov in " ${ addl_provide_removes [@] } " ; do
printf '.'
local pkg;
2021-06-03 14:16:43 +00:00
pkg = $( provides_pkg " $prov " ) || continue
2021-05-13 14:26:20 +00:00
addl_pkg_removes += ( " $pkg " )
2021-05-11 22:55:56 +00:00
done
printf '%s\n' '' '' " Found the following system packages which map from $PRETTY_NAME to Rocky Linux 8: "
2021-06-04 10:11:15 +00:00
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 "
2021-05-11 22:55:56 +00:00
done )
2021-06-20 12:01:02 +00:00
infomsg $'\n' "Getting list of installed system packages." $'\n'
2021-06-04 10:11:15 +00:00
readarray -t installed_packages < <( saferpm -qa --queryformat= "%{NAME}\n" " ${ pkg_map [@] } " )
2021-05-11 22:55:56 +00:00
declare -g -A installed_pkg_check installed_pkg_map
for p in " ${ installed_packages [@] } " ; do
installed_pkg_check[ $p ] = 1
done
for p in " ${ !pkg_map[@] } " ; do
2021-06-04 10:11:15 +00:00
if [ [ ${ pkg_map [ $p ] } && ${ installed_pkg_check [ ${ pkg_map [ $p ] } ] } ] ] ; then
2021-05-11 22:55:56 +00:00
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 "
2021-06-04 10:11:15 +00:00
column -t -s $'\t' -N "Packages to be Removed,Packages to be Installed" < <(
2021-05-11 22:55:56 +00:00
for p in " ${ !installed_pkg_map[@] } " ; do
2021-06-04 10:11:15 +00:00
printf '%s\t%s\n' " ${ installed_pkg_map [ $p ] } " " $p "
2021-05-11 22:55:56 +00:00
done
)
2021-05-13 14:26:20 +00:00
if ( ( ${# addl_pkg_removes [@] } ) ) ; then
printf '%s\n' '' "In addition to the above the following system packages will be removed:" \
" ${ addl_pkg_removes [@] } "
fi
2021-05-11 22:55:56 +00:00
# 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.
#sigs_to_swap=()
2021-06-20 12:01:02 +00:00
infomsg '%s' $'\n' \
$'Getting a list of enabled modules for the system repositories.\n'
2021-05-11 22:55:56 +00:00
# Get a list of system enabled modules.
readarray -t enabled_modules < <(
set -e -o pipefail
2021-06-04 10:11:15 +00:00
safednf -q " ${ repo_map [@]/#/--repo= } " module list --enabled |
2021-05-11 22:55:56 +00:00
awk '
$1 = = "@modulefailsafe" , /^$/ { next}
2021-05-16 02:05:42 +00:00
$1 = = "Name" , /^$/ { if ( $1 != "Name" && !/^$/) print $1 ":" $2 }
' | sort -u
2021-05-11 22:55:56 +00:00
set +e +o pipefail
)
2021-05-18 11:43:58 +00:00
# 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 [@] } " )
fi
2021-05-11 22:55:56 +00:00
2021-05-14 03:18:43 +00:00
printf '%s\n' '' "Found the following modules to re-enable at completion:" \
" ${ 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 [@] } "
fi
2021-05-11 22:55:56 +00:00
}
convert_info_dir = /root/convert
2021-05-29 06:33:57 +00:00
unset convert_to_rocky reinstall_all_rpms verify_all_rpms update_efi
2021-05-11 22:55:56 +00:00
usage( ) {
printf '%s\n' \
" Usage: ${ 0 ##*/ } [OPTIONS] " \
'' \
'Options:' \
2021-05-29 06:33:57 +00:00
'-h Display this help' \
'-r Convert to rocky' \
'-V Verify switch' \
2021-05-11 22:55:56 +00:00
' !! USE WITH CAUTION !!'
exit 1
} >& 2
generate_rpm_info( ) {
mkdir /root/convert
2021-06-20 12:01:02 +00:00
infomsg " Creating a list of RPMs installed: $1 " $'\n'
2021-05-11 22:55:56 +00:00
rpm -qa --qf "%{NAME}|%{VERSION}|%{RELEASE}|%{INSTALLTIME}|%{VENDOR}|%{BUILDTIME}|%{BUILDHOST}|%{SOURCERPM}|%{LICENSE}|%{PACKAGER}\n" | sort > " ${ convert_info_dir } / $HOSTNAME -rpm-list- $1 .log "
2021-06-20 12:01:02 +00:00
infomsg " Verifying RPMs installed against RPM database: $1 " $'\n\n'
2021-05-11 22:55:56 +00:00
rpm -Va | sort -k3 > " ${ convert_info_dir } / $HOSTNAME -rpm-list-verified- $1 .log "
}
package_swaps( ) {
2021-06-20 17:10:24 +00:00
# Save off any subscription-manger 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 "
fi
2021-05-31 16:41:27 +00:00
# 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 } " )
done
2021-05-11 22:55:56 +00:00
# Use dnf shell to swap the system packages out.
2021-06-04 10:11:15 +00:00
safednf -y shell --disablerepo= \* --noautoremove \
2021-05-13 14:26:20 +00:00
--setopt= protected_packages = --setopt= keepcache = True \
2021-05-31 16:41:27 +00:00
" ${ dnfparameters [@] } " \
<<EOF
2021-05-13 14:26:20 +00:00
remove ${ installed_pkg_map [@] } ${ addl_pkg_removes [@] }
2021-05-11 22:55:56 +00:00
install ${ !installed_pkg_map[@] }
run
exit
EOF
2021-05-13 14:26:20 +00:00
2021-05-31 16:41:27 +00:00
# rocky-repos and rocky-gpg-keys are now installed, so we don't need the key file anymore
rm -rf " $gpg_tmp_dir "
2021-05-13 14:26:20 +00:00
# We need to check to make sure that all of the original system packages
# have been removed and all of the new ones have been added. If a package
# was supposed to be removed and one with the same name added back then
# we're kind of screwed for this check, as we can't be certain, but all the
# packages we're adding start with "rocky-*" so this really shouldn't happen
# and we can safely not check for it. The worst that will happen is a rocky
# linux package will be removed and then installed again.
local -a check_removed check_installed
readarray -t check_removed < <(
2021-06-04 10:11:15 +00:00
saferpm -qa --qf '%{NAME}\n' " ${ installed_pkg_map [@] } " \
2021-05-13 14:26:20 +00:00
" ${ addl_pkg_removes [@] } " | sort -u
)
if ( ( ${# check_removed [@] } ) ) ; then
2021-06-20 12:01:02 +00:00
infomsg '%s' $'\n' \
"Packages found on system that should still be removed. Forcibly" \
" removing them with rpm:" $'\n'
2021-05-13 14:26:20 +00:00
# Removed packages still found on the system. Forcibly remove them.
for pkg in " ${ check_removed [@] } " ; do
2021-06-04 10:11:15 +00:00
# Extra safety measure, skip if empty string
if [ [ -z $pkg ] ] ; then
continue
fi
2021-05-13 14:26:20 +00:00
printf '%s\n' " $pkg "
2021-06-04 10:11:15 +00:00
saferpm -e --allmatches --nodeps " $pkg " ||
saferpm -e --allmatches --nodeps --noscripts --notriggers " $pkg "
2021-05-13 14:26:20 +00:00
done
fi
# Check to make sure we installed everything we were supposed to.
readarray -t check_installed < <(
{
printf '%s\n' " ${ !installed_pkg_map[@] } " | sort -u
2021-06-04 10:11:15 +00:00
saferpm -qa --qf '%{NAME}\n' " ${ !installed_pkg_map[@] } " | sort -u
2021-05-13 14:26:20 +00:00
} | sort | uniq -u
)
if ( ( ${# check_installed [@] } ) ) ; then
2021-06-20 12:01:02 +00:00
infomsg '%s' $'\n' \
"Some required packages were not installed by dnf. Attempting to" \
" force with rpm:" $'\n'
2021-05-13 14:26:20 +00:00
# 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
2021-05-14 03:18:43 +00:00
rpm_map[ $(
rpm -q --qf '%{NAME}\n' --nodigest " $rpm " 2>/dev/null
) ] = $rpm
2021-05-13 14:26:20 +00:00
done
# Attempt to install.
for pkg in " ${ check_installed [@] } " ; do
printf '%s\n' " $pkg "
2021-05-14 03:18:43 +00:00
if ! rpm -i --force --nodeps --nodigest " ${ rpm_map [ $pkg ] } " \
2>/dev/null; then
2021-05-13 14:26:20 +00:00
# Try to install the package in just the db, then clean it up.
2021-05-14 03:18:43 +00:00
rpm -i --force --justdb --nodeps --nodigest " ${ rpm_map [ $pkg ] } " \
2>/dev/null
2021-05-13 14:26:20 +00:00
# Get list of files that are still causing problems and donk
# them.
readarray -t file_list < <(
2021-05-14 03:18:43 +00:00
rpm -V " $pkg " 2>/dev/null | awk '$1!="missing" {print $2}'
2021-05-13 14:26:20 +00:00
)
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 \
2021-05-14 03:18:43 +00:00
" ${ rpm_map [ $pkg ] } " 2>/dev/null
2021-05-13 14:26:20 +00:00
fi
done
2021-05-11 22:55:56 +00:00
fi
# Distrosync
2021-06-20 12:01:02 +00:00
infomsg $'\nRemoving dnf cache\n'
2021-05-11 22:55:56 +00:00
rm -rf /var/cache/{ yum,dnf}
2021-06-20 12:01:02 +00:00
infomsg $'Ensuring repos are enabled before the package swap\n'
2021-06-04 10:11:15 +00:00
safednf -y config-manager --set-enabled " ${ !repo_map[@] } " || {
2021-05-11 22:55:56 +00:00
printf '%s\n' 'Repo name missing?'
exit 25
}
2021-05-14 03:18:43 +00:00
if ( ( ${# managed_repos [@] } ) ) ; then
# Filter the managed repos for ones still in the system.
readarray -t managed_repos < <(
2021-06-04 10:11:15 +00:00
safednf -q repolist " ${ managed_repos [@] } " | awk '$1!="repo" {print $1}'
2021-05-14 03:18:43 +00:00
)
if ( ( ${# managed_repos [@] } ) ) ; then
2021-06-20 12:01:02 +00:00
infomsg $'\nDisabling subscription managed repos\n'
2021-06-04 10:11:15 +00:00
safednf -y config-manager --disable " ${ managed_repos [@] } "
2021-05-14 03:18:43 +00:00
fi
fi
2021-05-12 01:44:41 +00:00
if ( ( ${# enabled_modules [@] } ) ) ; then
2021-06-20 12:01:02 +00:00
infomsg $'Enabling modules\n\n'
2021-06-04 10:11:15 +00:00
safednf -y module enable " ${ enabled_modules [@] } " ||
2021-06-03 14:16:43 +00:00
exit_message " Can't enable modules ${ enabled_modules [*] } "
2021-05-12 01:44:41 +00:00
fi
2021-05-18 11:43:58 +00:00
# Make sure that excluded repos are disabled.
2021-06-20 12:01:02 +00:00
infomsg $'Disabling excluded modules\n\n'
2021-06-04 10:11:15 +00:00
safednf -y module disable " ${ module_excludes [@] } " ||
2021-06-03 14:16:43 +00:00
exit_message " Can't disable modules ${ module_excludes [*] } "
2021-05-18 11:43:58 +00:00
2021-06-20 12:01:02 +00:00
infomsg $'\nSyncing packages\n\n'
2021-05-11 22:55:56 +00:00
dnf -y distro-sync || exit_message "Error during distro-sync."
2021-06-20 17:10:24 +00:00
if rpm --quiet -q subscription-manager; then
infomsg $'Subscription Manager found on system.\n'
cat <<EOF
If you' re converting from a subscription-managed distribution such as RHEL then
you may no longer need subscription-manager. While it won' t hurt anything to
have it on your system you may be able to safely remove it with
"dnf remove subscription-manager" . Take care that it doesn' t remove something
that you want to keep.
The subscription-manager dnf plugin may be enabled for the benefit of
Subscription Management. If no longer desired, you can use
"subscription-manager config --rhsm.auto_enable_yum_plugins=0" to block this
behavior.
EOF
fi
if [ [ $tmp_sm_ca_dir ] ] ; then
# Check to see if there's Subscription Manager certs which have been
# removed
local -a removed_certs
readarray -t removed_certs < <( (
shopt -s nullglob dotglob
local -a certs
cd " $sm_ca_dir " && certs = ( *)
cd " $tmp_sm_ca_dir " && certs += ( *)
IFS = $'\n'
printf '%s' " ${ certs [*] } "
) | sort | uniq -u)
if ( ( ${# removed_certs [@] } ) ) ; then
cp -n -dR --preserve= all " $tmp_sm_ca_dir " /* " $sm_ca_dir / " ||
exit_message " Could not copy certs back to $sm_ca_dir "
infomsg '%s' \
$'Some Subscription Manager certificates ' \
" were restored to $sm_ca_dir after " $'\n' \
$'migration so that the subscription-manager ' \
$'command will continue to work:\n\n'
printf '%s\n' " ${ removed_certs [@] } " ''
cat <<EOF
If you no longer need to use the subscription-manager command then you may
safely remove these files.
EOF
fi
fi
2021-05-11 22:55:56 +00:00
}
2021-05-30 12:15:59 +00:00
# Check if this system is running on EFI
# If yes, we'll need to run fix_efi() at the end of the conversion
efi_check ( ) {
# Check if we have /sys mounted and it is looking sane
if ! [ [ -d /sys/class/block ] ] ; then
exit_message "/sys is not accessible."
fi
# Now that we know /sys is reliable, use it to check if we are running on EFI or not
if [ [ -d /sys/firmware/efi/ ] ] ; then
declare -g update_efi
update_efi = true
fi
}
2021-05-29 06:33:57 +00:00
# Called to update the EFI boot.
fix_efi ( ) (
grub2-mkconfig -o /boot/efi/EFI/rocky/grub.cfg ||
exit_message "Error updating the grub config."
2021-05-30 11:47:25 +00:00
efibootmgr -c -d " $efi_mount " -L "Rocky Linux" -l /EFI/rocky/grubx64.efi ||
2021-05-29 06:33:57 +00:00
exit_message "Error updating uEFI firmware."
)
2021-05-31 16:41:27 +00:00
# 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
declare -g gpg_tmp_dir
2021-06-20 17:10:24 +00:00
gpg_tmp_dir = $tmp_dir /gpg
if ! mkdir " $gpg_tmp_dir " || [ [ ! -d " $gpg_tmp_dir " ] ] ; then
2021-05-31 16:41:27 +00:00
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; : " $gpg_tmp_dir " /* ) 2>/dev/null ; then
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 -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."
fi
if ! sha512sum --quiet -c <<< " $gpg_key_sha512 $gpg_key_file " ; then
rm -rf " $gpg_tmp_dir "
exit_message "Error validating the signing key."
fi
}
2021-05-11 22:55:56 +00:00
## End actual work
noopts = 0
while getopts "hrVR" option; do
( ( noopts++ ) )
case " $option " in
h)
usage
; ;
r)
convert_to_rocky = true
; ;
V)
verify_all_rpms = true
; ;
*)
2021-06-20 12:01:02 +00:00
errmsg $'Invalid switch\n'
2021-05-11 22:55:56 +00:00
usage
; ;
esac
done
if ( ( ! noopts ) ) ; then
usage
fi
2021-06-20 17:10:24 +00:00
pre_setup
trap exit_clean EXIT
2021-06-04 23:55:09 +00:00
pre_check
2021-05-30 12:15:59 +00:00
efi_check
2021-05-11 22:55:56 +00:00
bin_check
if [ [ $verify_all_rpms ] ] ; then
generate_rpm_info begin
fi
if [ [ $convert_to_rocky ] ] ; then
collect_system_info
2021-05-31 16:41:27 +00:00
establish_gpg_trust
2021-05-11 22:55:56 +00:00
package_swaps
fi
if [ [ $verify_all_rpms && $convert_to_rocky ] ] ; then
generate_rpm_info finish
2021-06-20 12:01:02 +00:00
infomsg $'You may review the following files:\n'
2021-05-11 22:55:56 +00:00
find /root/convert -type f -name " $HOSTNAME -rpms-*.log "
fi
2021-05-29 06:33:57 +00:00
if [ [ $update_efi && $convert_to_rocky ] ] ; then
fix_efi
fi
2021-05-11 22:55:56 +00:00
printf '\n\n\n'
if [ [ $convert_to_rocky ] ] ; then
2021-06-20 12:01:02 +00:00
infomsg $'\nDone, please reboot your system.\n'
2021-05-11 22:55:56 +00:00
fi
logmessage