Merge pull request #188 from saveriomiroddi/add_script_functions_hotswapping

Add script functions hot swapping
This commit is contained in:
Saverio Miroddi 2021-04-27 18:26:29 +02:00 committed by GitHub
commit 72f1f38970
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -43,6 +43,8 @@ v_suitable_disks=() # (/dev/by-id/disk_id, ...); scope: find_suitable_d
# Note that Linux Mint is "Linuxmint" from v20 onwards. This actually helps, since some operations are
# specific to it.
c_hotswap_file=$PWD/install-zfs.hotswap.sh # see hotswap() for an explanation.
c_bpool_name=bpool
c_ppa=ppa:jonathonf/zfs
c_efi_system_partition_size=512 # megabytes
@ -107,49 +109,74 @@ c_udevadm_settle_timeout=10 # seconds
# HELPER FUNCTIONS #############################################################
# Chooses a function and invokes it depending on the O/S distribution.
# Invoke a function, with a primitive dynamic dispatch based on the distribution.
#
# Example:
# Format: `invoke "function" [--optional]`.
#
# A target function must exist, otherwise a error is raised, unless `--optional` is specified.
# `--optional` is useful when a step is specific to a single distribution, e.g. Debian's root password.
#
# Examples:
#
# $ function install_jail_zfs_packages { :; }
# $ function install_jail_zfs_packages_Debian { :; }
# $ distro_dependent_invoke "install_jail_zfs_packages"
#
# If the distribution is `Debian`, the second will be invoked, otherwise, the
# first.
#
# If the function is invoked with `--noforce` as second parameter, and there is
# no matching function:
# If the distribution is `Debian`, the second will be invoked, otherwise, the first.
#
# $ function update_zed_cache_Ubuntu { :; }
# $ distro_dependent_invoke "install_jail_zfs_packages" --noforce
# $ distro_dependent_invoke "update_zed_cache" --optional
#
# then nothing happens. Without `--noforce`, this invocation will cause an
# error.
# If the distribution is `Debian`, nothing will happen.
#
function distro_dependent_invoke {
local distro_specific_fx_name="$1_$v_linux_distribution"
# $ function update_zed_cache_Ubuntu { :; }
# $ distro_dependent_invoke "update_zed_cache"
#
# If the distribution is `Debian`, an error will be raised.
#
function invoke {
local base_fx_name=$1
local distro_specific_fx_name=$1_$v_linux_distribution
local invoke_option=${2:-}
if [[ ! $invoke_option =~ ^(|--optional)$ ]]; then
>&2 echo "Invalid invoke() option: $invoke_option"
exit 1
fi
hot_swap_script
# Invoke it regardless when it's not optional.
if declare -f "$distro_specific_fx_name" > /dev/null; then
print_step_info_header "$distro_specific_fx_name"
"$distro_specific_fx_name"
else
if ! declare -f "$1" > /dev/null && [[ "${2:-}" == "--noforce" ]]; then
: # do nothing
else
"$1"
fi
elif declare -f "$base_fx_name" > /dev/null || [[ ! $invoke_option == "--optional" ]]; then
print_step_info_header "$base_fx_name"
"$base_fx_name"
fi
}
# Tee-hee-hee!!
#
# This is extremely useful for debugging long procedures. Since bash scripts can't be modified while
# running, this allows the dev to create a snapshot, and if the script fails after that, resume and
# add the hotswap script, so that the new code will be loaded automatically.
#
function hot_swap_script {
if [[ -f $c_hotswap_file ]]; then
source "$c_hotswap_file"
fi
}
# shellcheck disable=SC2120 # allow parameters passing even if no calls pass any
function print_step_info_header {
local function_name=$1
echo -n "
###############################################################################
# ${FUNCNAME[1]}"
[[ "${1:-}" != "" ]] && echo -n " $1" || true
echo "
# $function_name
###############################################################################
"
}
@ -231,8 +258,6 @@ Root pool default create options: '"${c_default_rpool_create_options[*]/#-/$'\n'
}
function activate_debug {
print_step_info_header
mkdir -p "$c_log_dir"
exec 5> "$c_install_log"
@ -251,8 +276,6 @@ function set_distribution_data {
}
function store_os_distro_information {
print_step_info_header
lsb_release --all > "$c_os_information_log"
# Madness, in order not to force the user to invoke "sudo -E".
@ -276,8 +299,6 @@ function store_running_processes {
}
function check_prerequisites {
print_step_info_header
local distro_version_regex=\\b${v_linux_version//./\\.}\\b
if [[ ! -d /sys/firmware/efi ]]; then
@ -308,8 +329,6 @@ function check_prerequisites {
}
function display_intro_banner {
print_step_info_header
local dialog_message='Hello!
This script will prepare the ZFS pools on the system, install Ubuntu, and configure the boot.
@ -341,8 +360,6 @@ In such cases, the module building may fail abruptly, either without visible err
}
function save_disks_log {
print_step_info_header
# shellcheck disable=SC2012 # `ls` may clean the output, but in this case, it doesn't matter
ls -l /dev/disk/by-id | tail -n +2 | perl -lane 'print "@F[8..10]"' > "$c_disks_log"
@ -360,8 +377,6 @@ LOG
}
function find_suitable_disks {
print_step_info_header
# In some freaky cases, `/dev/disk/by-id` is not up to date, so we refresh. One case is after
# starting a VirtualBox VM that is a full clone of a suspended VM with snapshots.
#
@ -426,8 +441,6 @@ If you think this is a bug, please open an issue on https://github.com/saveriomi
# install_host_packages() and install_host_packages_UbuntuServer().
#
function set_zfs_ppa_requirement {
print_step_info_header
apt update
local zfs_package_version
@ -450,25 +463,19 @@ function set_zfs_ppa_requirement_Debian {
# to it being incorrectly setup).
#
function set_zfs_ppa_requirement_Linuxmint {
print_step_info_header
perl -i -pe 's/^(deb cdrom)/# $1/' /etc/apt/sources.list
set_zfs_ppa_requirement
invoke "set_zfs_ppa_requirement"
}
# By using a FIFO, we avoid having to hide statements like `echo $v_passphrase | zpoool create ...`
# from the logs.
#
function create_passphrase_named_pipe {
print_step_info_header
mkfifo "$c_passphrase_named_pipe"
}
function register_exit_hook {
print_step_info_header
function _exit_hook {
rm -f "$c_passphrase_named_pipe"
@ -517,8 +524,6 @@ export ZFS_FREE_TAIL_SPACE=12
}
function select_disks {
print_step_info_header
if [[ "${ZFS_SELECTED_DISKS:-}" != "" ]]; then
mapfile -d, -t v_selected_disks < <(echo -n "$ZFS_SELECTED_DISKS")
else
@ -558,8 +563,6 @@ Devices with mounted partitions, cdroms, and removable devices are not displayed
}
function select_pools_raid_type {
print_step_info_header
local raw_pools_raid_type=
if [[ -v ZFS_POOLS_RAID_TYPE ]]; then
@ -603,8 +606,6 @@ function select_pools_raid_type {
}
function ask_root_password_Debian {
print_step_info_header
set +x
if [[ ${ZFS_DEBIAN_ROOT_PASSWORD:-} != "" ]]; then
v_root_password="$ZFS_DEBIAN_ROOT_PASSWORD"
@ -623,8 +624,6 @@ function ask_root_password_Debian {
}
function ask_encryption {
print_step_info_header
set +x
if [[ -v ZFS_PASSPHRASE ]]; then
@ -655,8 +654,6 @@ Leave blank to keep encryption disabled.
}
function ask_boot_partition_size {
print_step_info_header
if [[ ${ZFS_BOOT_PARTITION_SIZE:-} != "" ]]; then
v_boot_partition_size=$ZFS_BOOT_PARTITION_SIZE
else
@ -675,8 +672,6 @@ Supported formats: '512M', '3G'" 30 100 ${c_default_boot_partition_size}M 3>&1 1
}
function ask_swap_size {
print_step_info_header
if [[ ${ZFS_SWAP_SIZE:-} != "" ]]; then
v_swap_size=$ZFS_SWAP_SIZE
else
@ -693,8 +688,6 @@ function ask_swap_size {
}
function ask_free_tail_space {
print_step_info_header
if [[ ${ZFS_FREE_TAIL_SPACE:-} != "" ]]; then
v_free_tail_space=$ZFS_FREE_TAIL_SPACE
else
@ -719,8 +712,6 @@ For detailed informations, see the wiki page: https://github.com/saveriomiroddi/
}
function ask_rpool_name {
print_step_info_header
if [[ ${ZFS_RPOOL_NAME:-} != "" ]]; then
v_rpool_name=$ZFS_RPOOL_NAME
else
@ -737,8 +728,6 @@ function ask_rpool_name {
}
function ask_pool_create_options {
print_step_info_header
local bpool_create_options_message='Insert the create options for the boot pool
The mount-related options are automatically added, and must not be specified.'
@ -759,8 +748,6 @@ The encryption/mount-related options are automatically added, and must not be sp
}
function install_host_packages {
print_step_info_header
if [[ $v_use_ppa == "1" ]]; then
if [[ ${ZFS_SKIP_LIVE_ZFS_MODULE_INSTALL:-} != "1" ]]; then
add-apt-repository --yes "$c_ppa"
@ -785,8 +772,6 @@ function install_host_packages {
}
function install_host_packages_Debian {
print_step_info_header
if [[ ${ZFS_SKIP_LIVE_ZFS_MODULE_INSTALL:-} != "1" ]]; then
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
@ -807,27 +792,21 @@ function install_host_packages_Debian {
# Differently from Ubuntu, Mint doesn't have the package installed in the live version.
#
function install_host_packages_Linuxmint {
print_step_info_header
apt install --yes zfsutils-linux
install_host_packages
invoke "install_host_packages"
}
function install_host_packages_elementary {
print_step_info_header
if [[ ${ZFS_SKIP_LIVE_ZFS_MODULE_INSTALL:-} != "1" ]]; then
apt update
apt install --yes software-properties-common
fi
install_host_packages
invoke "install_host_packages"
}
function install_host_packages_UbuntuServer {
print_step_info_header
if [[ $v_use_ppa != "1" ]]; then
apt install --yes zfsutils-linux efibootmgr
@ -859,8 +838,6 @@ function install_host_packages_UbuntuServer {
}
function setup_partitions {
print_step_info_header
local required_tail_space=$((v_free_tail_space > c_temporary_volume_size ? v_free_tail_space : c_temporary_volume_size))
for selected_disk in "${v_selected_disks[@]}"; do
@ -916,8 +893,6 @@ function setup_partitions {
}
function install_operating_system {
print_step_info_header
local dialog_message='The Ubuntu GUI installer will now be launched.
Proceed with the configuration as usual, then, at the partitioning stage:
@ -959,8 +934,6 @@ Proceed with the configuration as usual, then, at the partitioning stage:
}
function install_operating_system_Debian {
print_step_info_header
# The temporary volume size displayed is an approximation of the format used by the installer,
# but it's acceptable - the complexity required is not worth (eg. converting hypothetical units,
# etc.).
@ -1013,8 +986,6 @@ CONF
}
function install_operating_system_UbuntuServer {
print_step_info_header
# O/S Installation
#
# Subiquity is designed to prevent the user from opening a terminal, which is (to say the least)
@ -1056,8 +1027,6 @@ You can switch anytime to this terminal, and back, in order to read the instruct
}
function custom_install_operating_system {
print_step_info_header
sudo "$ZFS_OS_INSTALLATION_SCRIPT"
}
@ -1108,8 +1077,6 @@ function create_pools {
}
function create_swap_volume {
print_step_info_header
if [[ $v_swap_size -gt 0 ]]; then
zfs create \
-V "${v_swap_size}G" -b "$(getconf PAGESIZE)" \
@ -1121,15 +1088,11 @@ function create_swap_volume {
}
function copy_zpool_cache {
print_step_info_header
mkdir -p "$c_zfs_mount_dir/etc/zfs"
cp /etc/zfs/zpool.cache "$c_zfs_mount_dir/etc/zfs/"
}
function sync_os_temp_installation_dir_to_rpool {
print_step_info_header
# On Ubuntu Server, `/boot/efi` and `/cdrom` (!!!) are mounted, but they're not needed.
#
local mount_dir_submounts
@ -1162,8 +1125,6 @@ function sync_os_temp_installation_dir_to_rpool {
}
function remove_temp_partition_and_expand_rpool {
print_step_info_header
if (( v_free_tail_space < c_temporary_volume_size )); then
if [[ $v_free_tail_space -eq 0 ]]; then
local resize_reference=100%
@ -1198,8 +1159,6 @@ function remove_temp_partition_and_expand_rpool {
}
function prepare_jail {
print_step_info_header
for virtual_fs_dir in proc sys dev; do
mount --rbind "/$virtual_fs_dir" "$c_zfs_mount_dir/$virtual_fs_dir"
done
@ -1210,8 +1169,6 @@ function prepare_jail {
# See install_host_packages() for some comments.
#
function install_jail_zfs_packages {
print_step_info_header
if [[ $v_use_ppa == "1" ]]; then
chroot_execute "add-apt-repository --yes $c_ppa"
@ -1236,8 +1193,6 @@ function install_jail_zfs_packages {
}
function install_jail_zfs_packages_Debian {
print_step_info_header
chroot_execute 'echo "deb http://deb.debian.org/debian buster main contrib" >> /etc/apt/sources.list'
chroot_execute 'echo "deb-src http://deb.debian.org/debian buster main contrib" >> /etc/apt/sources.list'
@ -1257,26 +1212,20 @@ APT'
}
function install_jail_zfs_packages_elementary {
print_step_info_header
chroot_execute "apt install --yes software-properties-common"
install_jail_zfs_packages
invoke "install_jail_zfs_packages"
}
function install_jail_zfs_packages_UbuntuServer {
print_step_info_header
if [[ $v_use_ppa != "1" ]]; then
chroot_execute "apt install --yes zfsutils-linux zfs-initramfs grub-efi-amd64-signed shim-signed"
else
install_jail_zfs_packages
invoke "install_jail_zfs_packages"
fi
}
function prepare_efi_partition {
print_step_info_header
# The other mounts are configured/synced in the EFI partitions sync stage.
#
chroot_execute "echo /dev/disk/by-uuid/$(blkid -s UUID -o value "${v_selected_disks[0]}"-part1) /boot/efi vfat defaults 0 0 > /etc/fstab"
@ -1288,8 +1237,6 @@ function prepare_efi_partition {
}
function configure_and_update_grub {
print_step_info_header
chroot_execute "perl -i -pe 's/GRUB_CMDLINE_LINUX_DEFAULT=\"\K/init_on_alloc=0 /' /etc/default/grub"
# Silence warning during the grub probe (source: https://git.io/JenXF).
@ -1316,8 +1263,6 @@ function configure_and_update_grub {
}
function sync_efi_partitions {
print_step_info_header
for ((i = 1; i < ${#v_selected_disks[@]}; i++)); do
local synced_efi_partition_path="/boot/efi$((i + 1))"
@ -1340,14 +1285,10 @@ function sync_efi_partitions {
# See issue https://github.com/saveriomiroddi/zfs-installer/issues/110.
#
function update_initramfs {
print_step_info_header
chroot_execute "update-initramfs -u"
}
function fix_filesystem_mount_ordering {
print_step_info_header
chroot_execute "mkdir /etc/zfs/zfs-list.cache"
chroot_execute "touch /etc/zfs/zfs-list.cache/$c_bpool_name /etc/zfs/zfs-list.cache/$v_rpool_name"
@ -1406,8 +1347,6 @@ function fix_filesystem_mount_ordering {
# The code is a straight copy of the `fstrim` service.
#
function configure_pools_trimming {
print_step_info_header
chroot_execute "cat > /lib/systemd/system/zfs-trim.service << UNIT
[Unit]
Description=Discard unused ZFS blocks
@ -1438,15 +1377,11 @@ TIMER"
}
function configure_remaining_settings {
print_step_info_header
[[ $v_swap_size -gt 0 ]] && chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab" || true
chroot_execute "echo RESUME=none > /etc/initramfs-tools/conf.d/resume"
}
function prepare_for_system_exit {
print_step_info_header
for virtual_fs_dir in dev sys proc; do
umount --recursive --force --lazy "$c_zfs_mount_dir/$virtual_fs_dir"
done
@ -1479,8 +1414,6 @@ function prepare_for_system_exit {
}
function display_exit_banner {
print_step_info_header
local dialog_message="The system has been successfully prepared and installed.
You now need to perform a hard reset, then enjoy your ZFS system :-)"
@ -1496,59 +1429,59 @@ if [[ $# -ne 0 ]]; then
display_help_and_exit
fi
activate_debug
set_distribution_data
distro_dependent_invoke "store_os_distro_information"
store_running_processes
check_prerequisites
display_intro_banner
check_system_memory
save_disks_log
find_suitable_disks
distro_dependent_invoke "set_zfs_ppa_requirement"
register_exit_hook
create_passphrase_named_pipe
invoke "activate_debug"
invoke "set_distribution_data"
invoke "store_os_distro_information"
invoke "store_running_processes"
invoke "check_prerequisites"
invoke "display_intro_banner"
invoke "check_system_memory"
invoke "save_disks_log"
invoke "find_suitable_disks"
invoke "set_zfs_ppa_requirement"
invoke "register_exit_hook"
invoke "create_passphrase_named_pipe"
select_disks
select_pools_raid_type
distro_dependent_invoke "ask_root_password" --noforce
ask_encryption
ask_boot_partition_size
ask_swap_size
ask_free_tail_space
ask_rpool_name
ask_pool_create_options
invoke "select_disks"
invoke "select_pools_raid_type"
invoke "ask_root_password" --optional
invoke "ask_encryption"
invoke "ask_boot_partition_size"
invoke "ask_swap_size"
invoke "ask_free_tail_space"
invoke "ask_rpool_name"
invoke "ask_pool_create_options"
distro_dependent_invoke "install_host_packages"
setup_partitions
invoke "install_host_packages"
invoke "setup_partitions"
if [[ "${ZFS_OS_INSTALLATION_SCRIPT:-}" == "" ]]; then
# Includes the O/S extra configuration, if necessary (network, root pwd, etc.)
distro_dependent_invoke "install_operating_system"
invoke "install_operating_system"
create_pools
create_swap_volume
copy_zpool_cache
sync_os_temp_installation_dir_to_rpool
remove_temp_partition_and_expand_rpool
invoke "create_pools"
invoke "create_swap_volume"
invoke "copy_zpool_cache"
invoke "sync_os_temp_installation_dir_to_rpool"
invoke "remove_temp_partition_and_expand_rpool"
else
create_pools
create_swap_volume
copy_zpool_cache
remove_temp_partition_and_expand_rpool
invoke "create_pools"
invoke "create_swap_volume"
invoke "copy_zpool_cache"
invoke "remove_temp_partition_and_expand_rpool"
custom_install_operating_system
invoke "custom_install_operating_system"
fi
prepare_jail
distro_dependent_invoke "install_jail_zfs_packages"
prepare_efi_partition
configure_and_update_grub
sync_efi_partitions
update_initramfs
fix_filesystem_mount_ordering
configure_pools_trimming
configure_remaining_settings
invoke "prepare_jail"
invoke "install_jail_zfs_packages"
invoke "prepare_efi_partition"
invoke "configure_and_update_grub"
invoke "sync_efi_partitions"
invoke "update_initramfs"
invoke "fix_filesystem_mount_ordering"
invoke "configure_pools_trimming"
invoke "configure_remaining_settings"
prepare_for_system_exit
display_exit_banner
invoke "prepare_for_system_exit"
invoke "display_exit_banner"