From 06374a0d9c2a3b8c9beae41c847a5746d65a951f Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Tue, 13 May 2025 15:29:54 -0600 Subject: [PATCH 1/2] CP-12082 Install and configure systemd-boot during appliance image build CP-12084 Enable EFI firmware boot for ESX CP-12483 Handle upgrade scenarios, EFI to EFI and BIOS to BIOS PR URL: https://www.github.com/delphix/appliance-build/pull/816 --- .../vm-artifacts/90-raw-disk-image.binary | 69 +++++++--- .../config/hooks/vm-artifacts/template.ovf | 3 +- upgrade/upgrade-scripts/common.sh | 7 +- upgrade/upgrade-scripts/rootfs-container | 118 ++++++++++++------ 4 files changed, 138 insertions(+), 59 deletions(-) diff --git a/live-build/config/hooks/vm-artifacts/90-raw-disk-image.binary b/live-build/config/hooks/vm-artifacts/90-raw-disk-image.binary index 29331233..82c1c06d 100755 --- a/live-build/config/hooks/vm-artifacts/90-raw-disk-image.binary +++ b/live-build/config/hooks/vm-artifacts/90-raw-disk-image.binary @@ -89,17 +89,16 @@ truncate -s "${RAW_DISK_SIZE_GB}G" "$ARTIFACT_NAME.img" sgdisk --zap-all "$ARTIFACT_NAME.img" # -# Here we're creating the boot partition. When installing grub, this -# partition will be used and automatically detected by "grub-install" -# based on the partitions typecode. This partition is required since we're -# partitioning using GPT; if we used MBR, an explicit boot partition -# wouldn't be required. Also we leave the first 1MB unallocated since -# some clouds (i.e. Azure) may require space for their own internal -# purposes. +# Here we're creating the boot partition. A 250MB EFI boot partition should +# be sufficient to store 5 initrd and vmlinuz versions (if necessary). GRUB +# files are ~11MB, initrd and vmlinuz are ~40MB, and all boot loaders EFI +# directory are under 500K. # -sgdisk "$ARTIFACT_NAME.img" \ - --set-alignment=1 --new=2:1m:+1m --typecode=2:EF02 - +# Also we leave the first 1MB unallocated since some clouds (i.e. Azure) may +# require space for their own internal purposes. +# +sgdisk "$ARTIFACT_NAME.img" --set-alignment=1 \ + --new=2:1m:+250m --typecode=2:EF00 -c:2:"EFI Boot" # # Now we create the partition that we'll use for the zpool that will be # used for the root pool. We use a generic typecode for this partition @@ -293,6 +292,10 @@ mount -t zfs "$FSNAME/ROOT/$FSNAME/vartmp" "$DIRECTORY/var/tmp" mkdir -p "/var/crash" mount -t zfs "$FSNAME/crashdump" "/var/crash" +# Make the vfat partition and get its FSID +mkfs.vfat -F32 /dev/mapper/${LOOPNAME}p2 +efi_part_fsid=`blkid -s UUID -o value /dev/mapper/${LOOPNAME}p2` + # # Populate the root filesystem with the contents of the "binary" directory # that (we assume) was previously generated by live-build. @@ -318,6 +321,7 @@ cat <<-EOF >"$DIRECTORY/etc/fstab" rpool/ROOT/$FSNAME/tmp /tmp zfs defaults,nosuid,nodev,exec,x-systemd.before=zfs-import-cache.service 0 0 rpool/ROOT/$FSNAME/vartmp /var/tmp zfs defaults,nosuid,nodev,exec,x-systemd.before=zfs-import-cache.service 0 0 rpool/crashdump /var/crash zfs defaults,x-systemd.before=zfs-import-cache.service,x-systemd.before=kdump-tools.service 0 0 + UUID=${efi_part_fsid} /boot/efi vfat defaults 0 0 EOF # @@ -343,13 +347,46 @@ for dir in /dev /proc /sys; do done # -# We need to use the dedicated grub dataset when running "grub-install" -# and "grub-mkconfig", so we need to mount this dataset first. +# Mount /boot/efi and do grub and bootctl install # -chroot "$DIRECTORY" mount -t zfs "$FSNAME/grub" /mnt -chroot "$DIRECTORY" grub-install --root-directory=/mnt "/dev/$LOOPNAME" -chroot "$DIRECTORY" grub-mkconfig -o /mnt/boot/grub/grub.cfg -chroot "$DIRECTORY" umount /mnt +EFI_DIR="/mnt/boot/efi" +chroot "$DIRECTORY" mkdir -p $EFI_DIR +chroot "$DIRECTORY" mount /dev/mapper/${LOOPNAME}p2 $EFI_DIR + +# Copy the latest kernel into EFI boot directory +chroot "$DIRECTORY" cp /boot/initrd.img $EFI_DIR +chroot "$DIRECTORY" cp /boot/vmlinuz $EFI_DIR +chroot "$DIRECTORY" bootctl --esp-path=$EFI_DIR install --no-variables + +# Use GRUB_CMDLINE_LINUX_DEFAULT boot options +source $DIRECTORY/etc/default/grub.d/override.cfg +cat <<-EOF >"${DIRECTORY}/${EFI_DIR}/loader/entries/delphix.conf" +title Delphix Engine +linux /vmlinuz +initrd /initrd.img +options root=ZFS=rpool/ROOT/${FSNAME}/root ro $GRUB_CMDLINE_LINUX_DEFAULT +EOF + +# Single user mode. Need the console options from GRUB +console_options=$(awk -F'"' '/console=tty/ {print $2}' $DIRECTORY/etc/default/grub.d/override.cfg) +cat <<-EOF >"${DIRECTORY}/${EFI_DIR}/loader/entries/delphix-single.conf" +title Delphix Engine Single User mode +linux /vmlinuz +initrd /initrd.img +options root=ZFS=rpool/ROOT/${FSNAME}/root ro single ${console_options} nomodeset dis_ucode_ldr +EOF + +# Set loader configurations +cat <<-EOF >"${DIRECTORY}/${EFI_DIR}/loader/loader.conf" +timeout 10 +console-mode max +default delphix.conf +editor yes +auto-entries yes +auto-firmware yes +EOF + +chroot "$DIRECTORY" umount $EFI_DIR for dir in /dev /proc /sys; do retry 5 10 umount -R "${DIRECTORY}${dir}" diff --git a/live-build/config/hooks/vm-artifacts/template.ovf b/live-build/config/hooks/vm-artifacts/template.ovf index 54ae0ca2..96b70042 100644 --- a/live-build/config/hooks/vm-artifacts/template.ovf +++ b/live-build/config/hooks/vm-artifacts/template.ovf @@ -132,7 +132,8 @@ - + + diff --git a/upgrade/upgrade-scripts/common.sh b/upgrade/upgrade-scripts/common.sh index 9eba1d41..f7b19690 100644 --- a/upgrade/upgrade-scripts/common.sh +++ b/upgrade/upgrade-scripts/common.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright 2018 Delphix +# Copyright 2018, 2025 Delphix # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ UPDATE_DIR="/var/dlpx-update" LOG_DIRECTORY="/var/tmp/delphix-upgrade" HOTFIX_PATH="/etc/hotfix" +EFI_DIR="/boot/efi" # # The virtualization service uses a different umask than the default. Thus to @@ -376,6 +377,10 @@ function verify_upgrade_not_in_progress() { [[ -z "$UPGRADE_TYPE" ]] || die "upgrade currently in-progress" } +function is_bootmode_uefi() { + [ -d /sys/firmware/efi/efivars ] && return 0 || return 1 +} + function mask_service() { local svc="$1" local container="$2" diff --git a/upgrade/upgrade-scripts/rootfs-container b/upgrade/upgrade-scripts/rootfs-container index e685a7d4..5e2c5799 100755 --- a/upgrade/upgrade-scripts/rootfs-container +++ b/upgrade/upgrade-scripts/rootfs-container @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright 2019 Delphix +# Copyright 2019, 2025 Delphix # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -102,6 +102,28 @@ function get_bootloader_devices() { done } +function update_systemd_boot_entries() { + cp "${EFI_DIR}/loader/entries/delphix.conf" "${EFI_DIR}/loader/entries/delphix.conf.bak" + cp "${EFI_DIR}/loader/entries/delphix-single.conf" "${EFI_DIR}/loader/entries/delphix-single.conf.bak" + + # Update default and single user entries + source /etc/default/grub.d/override.cfg + cat <<-EOF >"${EFI_DIR}/loader/entries/delphix.conf" + title Delphix Engine + linux /vmlinuz + initrd /initrd.img + options root=ZFS=rpool/ROOT/${CONTAINER}/root ro $GRUB_CMDLINE_LINUX_DEFAULT + EOF + + # Single user entry + cat <<-EOF >"${EFI_DIR}/loader/entries/delphix-single.conf" + title Delphix Engine Single User mode + linux /vmlinuz + initrd /initrd.img + options root=ZFS=rpool/ROOT/${CONTAINER}/root ro single nomodeset dis_ucode_ldr + EOF +} + function set_bootfs_not_mounted_cleanup() { umount "/var/lib/machines/$CONTAINER/mnt" || warn "'umount' of '/var/lib/machines/$CONTAINER/mnt' failed" @@ -147,30 +169,37 @@ function set_bootfs_not_mounted() { PLATFORM=$(get-appliance-platform) - for dev in $(get_bootloader_devices); do - [[ -e "/dev/$dev" ]] || - die "bootloader device '/dev/$dev' not found" - - [[ -b "/dev/$dev" ]] || - die "bootloader device '/dev/$dev' not block device" - - opts="--root-directory=/mnt" - - if [[ "$PLATFORM" == "esx" ]] || [[ "$PLATFORM" == "oci" ]]; then - opts+=" --debug-image=all" - opts+=" -v" - fi + # If using EFI firmware, update boot entries, vmlinuz, initrd, and bootloader + if is_bootmode_uefi; then + update_systemd_boot_entries + cp "/var/lib/machines/${CONTAINER}/boot/initrd.img" "$EFI_DIR" + cp "/var/lib/machines/${CONTAINER}/boot/vmlinuz" "$EFI_DIR" + chroot "/var/lib/machines/$CONTAINER" bootctl update + else + for dev in $(get_bootloader_devices); do + [[ -e "/dev/$dev" ]] || + die "bootloader device '/dev/$dev' not found" + + [[ -b "/dev/$dev" ]] || + die "bootloader device '/dev/$dev' not block device" + + opts="--root-directory=/mnt" + + if [[ "$PLATFORM" == "esx" ]] || [[ "$PLATFORM" == "oci" ]]; then + opts+=" --debug-image=all" + opts+=" -v" + fi + + # shellcheck disable=SC2086 + chroot "/var/lib/machines/$CONTAINER" \ + grub-install $opts "/dev/$dev" || + die "'grub-install' for '$dev' failed in '$CONTAINER'" + done - # shellcheck disable=SC2086 chroot "/var/lib/machines/$CONTAINER" \ - grub-install $opts "/dev/$dev" || - die "'grub-install' for '$dev' failed in '$CONTAINER'" - done - - chroot "/var/lib/machines/$CONTAINER" \ - grub-mkconfig -o /mnt/boot/grub/grub.cfg || - die "'grub-mkconfig' failed in '$CONTAINER'" - + grub-mkconfig -o /mnt/boot/grub/grub.cfg || + die "'grub-mkconfig' failed in '$CONTAINER'" + fi set_bootfs_not_mounted_cleanup trap - EXIT @@ -211,28 +240,35 @@ function set_bootfs_mounted() { PLATFORM=$(get-appliance-platform) - for dev in $(get_bootloader_devices); do - [[ -e "/dev/$dev" ]] || - die "bootloader device '/dev/$dev' not found" + # If using EFI firmware, update boot entries, vmlinuz, initrd, and bootloader + if is_bootmode_uefi; then + update_systemd_boot_entries + cp /boot/initrd.img "$EFI_DIR" + cp /boot/vmlinuz "$EFI_DIR" + bootctl update + else + for dev in $(get_bootloader_devices); do + [[ -e "/dev/$dev" ]] || + die "bootloader device '/dev/$dev' not found" - [[ -b "/dev/$dev" ]] || - die "bootloader device '/dev/$dev' not block device" + [[ -b "/dev/$dev" ]] || + die "bootloader device '/dev/$dev' not block device" - opts="--root-directory=/mnt" + opts="--root-directory=/mnt" - if [[ "$PLATFORM" == "esx" ]] || [[ "$PLATFORM" == "oci" ]]; then - opts+=" --debug-image=all" - opts+=" -v" - fi - - # shellcheck disable=SC2086 - grub-install $opts "/dev/$dev" || - die "'grub-install' for '$dev' failed in '$CONTAINER'" - done + if [[ "$PLATFORM" == "esx" ]] || [[ "$PLATFORM" == "oci" ]]; then + opts+=" --debug-image=all" + opts+=" -v" + fi - grub-mkconfig -o /mnt/boot/grub/grub.cfg || - die "'grub-mkconfig' failed in '$CONTAINER'" + # shellcheck disable=SC2086 + grub-install $opts "/dev/$dev" || + die "'grub-install' for '$dev' failed in '$CONTAINER'" + done + grub-mkconfig -o /mnt/boot/grub/grub.cfg || + die "'grub-mkconfig' failed in '$CONTAINER'" + fi set_bootfs_mounted_cleanup trap - EXIT } @@ -367,7 +403,7 @@ set-bootfs) # We only have a single bootloader on any given appliance, so we # need to ensure that only a single process is attempting to # update the bootloader at any given time. The locking done here - # is to help prevent accidential corruption of the bootloader, + # is to help prevent accidental corruption of the bootloader, # by ensuring only a single invocation of this script can set # the boot filesystem at any given time. # From 913b88ccd92cefcdeca68262f9d3638719cea1e2 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Tue, 29 Jul 2025 09:24:16 -0600 Subject: [PATCH 2/2] Keep raw image for OCI and KVM platforms PR URL: https://www.github.com/delphix/appliance-build/pull/841 --- .../hooks/vm-artifacts/90-raw-disk-image.binary | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/live-build/config/hooks/vm-artifacts/90-raw-disk-image.binary b/live-build/config/hooks/vm-artifacts/90-raw-disk-image.binary index 82c1c06d..717bb5fb 100755 --- a/live-build/config/hooks/vm-artifacts/90-raw-disk-image.binary +++ b/live-build/config/hooks/vm-artifacts/90-raw-disk-image.binary @@ -77,7 +77,7 @@ internal-buildserver) RAW_DISK_SIZE_GB=512 ;; internal-*) - RAW_DISK_SIZE_GB=70 + RAW_DISK_SIZE_GB=48 ;; *) die "Invalid variant specified: '$APPLIANCE_VARIANT'" @@ -98,14 +98,14 @@ sgdisk --zap-all "$ARTIFACT_NAME.img" # require space for their own internal purposes. # sgdisk "$ARTIFACT_NAME.img" --set-alignment=1 \ - --new=2:1m:+250m --typecode=2:EF00 -c:2:"EFI Boot" + --new=1:1m:+250m --typecode=1:EF00 -c:1:"EFI Boot" # # Now we create the partition that we'll use for the zpool that will be # used for the root pool. We use a generic typecode for this partition # that simply maps to "Linux filesystem". The typecode here is simply # cosmetic, as there's nothing that relies on it being set. # -sgdisk "$ARTIFACT_NAME.img" --new=1:: --typecode=1:8300 +sgdisk "$ARTIFACT_NAME.img" --new=2:: --typecode=2:8300 # # This is done simply for debugging and/or diagnostic purposes. When @@ -146,7 +146,7 @@ zpool create -d \ -O compression=on \ -R "$DIRECTORY" \ -t "$FSNAME" \ - rpool "/dev/mapper/${LOOPNAME}p1" + rpool "/dev/mapper/${LOOPNAME}p2" zfs create \ -o canmount=off \ @@ -293,8 +293,8 @@ mkdir -p "/var/crash" mount -t zfs "$FSNAME/crashdump" "/var/crash" # Make the vfat partition and get its FSID -mkfs.vfat -F32 /dev/mapper/${LOOPNAME}p2 -efi_part_fsid=`blkid -s UUID -o value /dev/mapper/${LOOPNAME}p2` +mkfs.vfat -F32 /dev/mapper/${LOOPNAME}p1 +efi_part_fsid=`blkid -s UUID -o value /dev/mapper/${LOOPNAME}p1` # # Populate the root filesystem with the contents of the "binary" directory @@ -351,7 +351,7 @@ done # EFI_DIR="/mnt/boot/efi" chroot "$DIRECTORY" mkdir -p $EFI_DIR -chroot "$DIRECTORY" mount /dev/mapper/${LOOPNAME}p2 $EFI_DIR +chroot "$DIRECTORY" mount /dev/mapper/${LOOPNAME}p1 $EFI_DIR # Copy the latest kernel into EFI boot directory chroot "$DIRECTORY" cp /boot/initrd.img $EFI_DIR