Browse Source

Keep netboot entries in efiboot

when defining Zero-OS as first boot, we can not get the node back
into rescue mode as that is the way Hetzner functions:
tne netboot get an ipxe that doe a local reload or, in case of rescue
starts a full netboot
master
Jan De Landtsheer 1 week ago
parent
commit
d0fd7fcc7b
1 changed files with 259 additions and 221 deletions
  1. +259
    -221
      zosinstalluefipxeondisk.sh

+ 259
- 221
zosinstalluefipxeondisk.sh View File

@@ -5,264 +5,302 @@ set -e

# Function to log messages with timestamps
log() {
echo "$(date '+%F %T') - $@"
echo "$(date '+%F %T') - $@"
}

# Function to display usage information
usage() {
echo "Usage: $0 [-d DEVICE] [-f FARM] [-e ENVIRONMENT]"
echo " -d, --device DEVICE Specify the block device to use (e.g., sda, nvme0n1)."
echo " -f, --farm FARM Specify the FARM ID."
echo " -e, --environment ENV Specify the environment (dev, qa, test, prod)."
echo " -h, --help Display this help message."
exit 1
echo "Usage: $0 [-d DEVICE] [-f FARM] [-e ENVIRONMENT]"
echo " -d, --device DEVICE Specify the block device to use (e.g., sda, nvme0n1)."
echo " -f, --farm FARM Specify the FARM ID."
echo " -e, --environment ENV Specify the environment (dev, qa, test, prod)."
echo " -h, --help Display this help message."
exit 1
}

# Function to find a suitable block device
find_blockdev() {
# Minimum size in bytes, e.g., 10 GiB
MIN_SIZE=$((10 * 1024 * 1024 * 1024))
# Search for NVMe devices first
log "Searching for NVMe devices..."
for DEV in /dev/nvme*n1; do
if [ -b "$DEV" ]; then
SIZE_BYTES=$(blockdev --getsize64 "$DEV")
if [ "$SIZE_BYTES" -ge "$MIN_SIZE" ]; then
log "Found NVMe device: $DEV ($(numfmt --to=iec $SIZE_BYTES))"
BLOCKDEV="${DEV##*/}"
return 0
else
log "$DEV is smaller than $(numfmt --to=iec $MIN_SIZE). Skipping."
fi
fi
done
# If no NVMe device found, search for SSD devices
log "No suitable NVMe device found. Searching for SSD devices..."
for DEV_PATH in /sys/block/*; do
DEV_NAME=$(basename "$DEV_PATH")
# Exclude unwanted devices
case "$DEV_NAME" in
loop*|ram*|sr*|fd*|md*|dm-*)
continue
;;
esac
DEVICE="/dev/$DEV_NAME"
if [ -b "$DEVICE" ] && [ -e "/sys/block/$DEV_NAME/queue/rotational" ] && [ "$(cat /sys/block/$DEV_NAME/queue/rotational)" == "0" ]; then
SIZE_BYTES=$(blockdev --getsize64 "$DEVICE")
if [ "$SIZE_BYTES" -ge "$MIN_SIZE" ]; then
log "Found SSD device: $DEVICE ($(numfmt --to=iec $SIZE_BYTES))"
BLOCKDEV="$DEV_NAME"
return 0
else
log "$DEVICE is smaller than $(numfmt --to=iec $MIN_SIZE). Skipping."
fi
fi
done
# Minimum size in bytes, e.g., 10 GiB
MIN_SIZE=$((10 * 1024 * 1024 * 1024))
# Search for NVMe devices first
log "Searching for NVMe devices..."
for DEV in /dev/nvme*n1; do
if [ -b "$DEV" ]; then
SIZE_BYTES=$(blockdev --getsize64 "$DEV")
if [ "$SIZE_BYTES" -ge "$MIN_SIZE" ]; then
log "Found NVMe device: $DEV ($(numfmt --to=iec $SIZE_BYTES))"
BLOCKDEV="${DEV##*/}"
return 0
else
log "$DEV is smaller than $(numfmt --to=iec $MIN_SIZE). Skipping."
fi
fi
done
# If no NVMe device found, search for SSD devices
log "No suitable NVMe device found. Searching for SSD devices..."
for DEV_PATH in /sys/block/*; do
DEV_NAME=$(basename "$DEV_PATH")
# Exclude unwanted devices
case "$DEV_NAME" in
loop* | ram* | sr* | fd* | md* | dm-*)
continue
;;
esac
DEVICE="/dev/$DEV_NAME"
if [ -b "$DEVICE" ] && [ -e "/sys/block/$DEV_NAME/queue/rotational" ] && [ "$(cat /sys/block/$DEV_NAME/queue/rotational)" == "0" ]; then
SIZE_BYTES=$(blockdev --getsize64 "$DEVICE")
if [ "$SIZE_BYTES" -ge "$MIN_SIZE" ]; then
log "Found SSD device: $DEVICE ($(numfmt --to=iec $SIZE_BYTES))"
BLOCKDEV="$DEV_NAME"
return 0
else
log "$DEVICE is smaller than $(numfmt --to=iec $MIN_SIZE). Skipping."
fi
fi
done

log "No suitable block device found."
return 1
log "No suitable block device found."
return 1
}

# Function to manage UEFI boot entries
manage_boot_entries() {
log "Managing UEFI boot entries..."
log "Managing UEFI boot entries..."

# Check if efibootmgr is available
if ! command -v efibootmgr &> /dev/null; then
log "Error: 'efibootmgr' is not installed. Please install it and rerun the script."
exit 1
# Check if efibootmgr is available
if ! command -v efibootmgr &>/dev/null; then
log "Error: 'efibootmgr' is not installed. Please install it and rerun the script."
exit 1
fi

# Get the list of current boot entries
log "Retrieving current boot entries..."
BOOT_ENTRIES_INFO=$(efibootmgr -v | grep -E "^Boot[0-9A-F]{4}")

# Arrays to hold boot entry numbers
NETBOOT_ENTRIES=()
ENTRIES_TO_DELETE=()

# Identify netboot entries and entries to delete
while read -r LINE; do
ENTRY_NUM=$(echo "$LINE" | awk '{print $1}' | sed 's/Boot//;s/\*//')
ENTRY_DESC=$(echo "$LINE" | cut -d' ' -f2-)
ENTRY_PATH=$(echo "$LINE" | awk -F'File' '{print $2}')

# Check if the entry is a netboot entry (e.g., contains "PXE", "Network", or "IPv4"/"IPv6")
if echo "$ENTRY_DESC" | grep -qiE "(PXE|Network|IPV4|IPV6)"; then
NETBOOT_ENTRIES+=("$ENTRY_NUM")
log "Identified netboot entry: Boot$ENTRY_NUM - $ENTRY_DESC"
else
ENTRIES_TO_DELETE+=("$ENTRY_NUM")
log "Marking entry for deletion: Boot$ENTRY_NUM - $ENTRY_DESC"
fi

# Get the list of current boot entries
CURRENT_BOOT_ENTRIES=$(efibootmgr | grep "BootOrder")
log "Current BootOrder: $CURRENT_BOOT_ENTRIES"

# Delete all existing boot entries except the EFI Shell (if present)
log "Deleting existing boot entries..."
BOOT_ENTRIES=$(efibootmgr | grep -E "^Boot[0-9A-F]{4}" | awk '{print $1}' | sed 's/\*//')
for ENTRY in $BOOT_ENTRIES; do
efibootmgr -b ${ENTRY#Boot} -B
log "Deleted boot entry $ENTRY"
done <<<"$BOOT_ENTRIES_INFO"

# Delete non-netboot entries
if [ ${#ENTRIES_TO_DELETE[@]} -gt 0 ]; then
log "Deleting non-netboot boot entries..."
for ENTRY_NUM in "${ENTRIES_TO_DELETE[@]}"; do
efibootmgr -b "$ENTRY_NUM" -B
log "Deleted boot entry Boot$ENTRY_NUM"
done
else
log "No non-netboot entries to delete."
fi

# Create a new boot entry pointing to BOOTX64.EFI on the first partition
log "Creating new boot entry for /dev/${PARTPREFIX}1..."
# Adjust the partition number if necessary
ESP_PART_NUM=1
efibootmgr -c -d /dev/${BLOCKDEV} -p $ESP_PART_NUM -L "Zero-OS" -l "\\EFI\\BOOT\\BOOTX64.EFI" || {
log "Error: Failed to create new boot entry."
exit 1
}

# Create a new boot entry pointing to BOOTX64.EFI on the first partition
log "Creating new boot entry for /dev/${PARTPREFIX}1..."
# Adjust the partition number if necessary
ESP_PART_NUM=1
efibootmgr -c -d /dev/${BLOCKDEV} -p $ESP_PART_NUM -L "Zero-OS" -l "\\EFI\\BOOT\\BOOTX64.EFI" || {
log "Error: Failed to create new boot entry."
exit 1
}

# Set the boot order to include only the new boot entry
NEW_BOOT_NUM=$(efibootmgr | grep "Zero-OS" | awk '{print $1}' | sed 's/\*//;s/Boot//')
if [ -z "$NEW_BOOT_NUM" ]; then
log "Error: Failed to retrieve new boot entry number."
exit 1
fi
log "Setting BootOrder to only include Boot$NEW_BOOT_NUM..."
efibootmgr -o $NEW_BOOT_NUM || {
log "Error: Failed to set new BootOrder."
exit 1
}
# Retrieve the new boot entry number
NEW_BOOT_NUM=$(efibootmgr | grep "Zero-OS" | awk '{print $1}' | sed 's/\*//;s/Boot//')
if [ -z "$NEW_BOOT_NUM" ]; then
log "Error: Failed to retrieve new boot entry number."
exit 1
fi

# Set the new boot order: netboot entries first, followed by the new boot entry
log "Setting new BootOrder with netboot entries first..."
BOOT_ORDER=""
for ENTRY_NUM in "${NETBOOT_ENTRIES[@]}"; do
BOOT_ORDER+="$ENTRY_NUM,"
done
BOOT_ORDER+="$NEW_BOOT_NUM"

efibootmgr -o "$BOOT_ORDER" || {
log "Error: Failed to set new BootOrder."
exit 1
}

log "New BootOrder set successfully."
log "New BootOrder set successfully: $BOOT_ORDER"
}

# Main script execution
main() {
# Default values
BLOCKDEV_SPECIFIED=""
FARM="1234"
ENVIRONMENT="prod"

# Parse command-line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-d|--device)
shift
if [ -z "$1" ]; then
log "Error: '--device' requires a non-empty option argument."
usage
fi
BLOCKDEV_SPECIFIED="$1"
shift
;;
-f|--farm)
shift
if [ -z "$1" ]; then
log "Error: '--farm' requires a non-empty option argument."
usage
fi
FARM="$1"
shift
;;
-e|--environment)
shift
if [ -z "$1" ]; then
log "Error: '--environment' requires a non-empty option argument."
usage
fi
ENVIRONMENT="$1"
shift
;;
-h|--help)
usage
;;
*)
log "Error: Unknown option: $1"
usage
;;
esac
done

# Validate environment
case "$ENVIRONMENT" in
dev|qa|test|prod)
log "Using environment: $ENVIRONMENT"
;;
*)
log "Error: Invalid environment '$ENVIRONMENT'. Valid options are 'dev', 'qa', 'test', 'prod'."
exit 1
;;
# Default values
BLOCKDEV_SPECIFIED=""
FARM="1234"
ENVIRONMENT="prod"

# Parse command-line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-d | --device)
shift
if [ -z "$1" ]; then
log "Error: '--device' requires a non-empty option argument."
usage
fi
BLOCKDEV_SPECIFIED="$1"
shift
;;
-f | --farm)
shift
if [ -z "$1" ]; then
log "Error: '--farm' requires a non-empty option argument."
usage
fi
FARM="$1"
shift
;;
-e | --environment)
shift
if [ -z "$1" ]; then
log "Error: '--environment' requires a non-empty option argument."
usage
fi
ENVIRONMENT="$1"
shift
;;
-h | --help)
usage
;;
*)
log "Error: Unknown option: $1"
usage
;;
esac
done

# Validate environment
case "$ENVIRONMENT" in
dev | qa | test | prod)
log "Using environment: $ENVIRONMENT"
;;
*)
log "Error: Invalid environment '$ENVIRONMENT'. Valid options are 'dev', 'qa', 'test', 'prod'."
exit 1
;;
esac

if [ -n "$BLOCKDEV_SPECIFIED" ]; then
# Use the specified block device
if [[ "$BLOCKDEV_SPECIFIED" != /dev/* ]]; then
BLOCKDEV_SPECIFIED="/dev/$BLOCKDEV_SPECIFIED"
fi
if [ ! -b "$BLOCKDEV_SPECIFIED" ]; then
log "Error: Specified device $BLOCKDEV_SPECIFIED does not exist or is not a block device."
exit 1
fi
BLOCKDEV="${BLOCKDEV_SPECIFIED##*/}"
log "Using specified block device: $BLOCKDEV_SPECIFIED"
else
# Find a suitable block device
if ! find_blockdev; then
log "Error: No suitable block device found."
exit 1
fi
log "Selected block device: /dev/${BLOCKDEV}"
if [ -n "$BLOCKDEV_SPECIFIED" ]; then
# Use the specified block device
if [[ "$BLOCKDEV_SPECIFIED" != /dev/* ]]; then
BLOCKDEV_SPECIFIED="/dev/$BLOCKDEV_SPECIFIED"
fi

# Confirm with the user before proceeding
read -p "This will erase all data on /dev/${BLOCKDEV}. Are you sure you want to proceed? (yes/[no]): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
log "Operation cancelled by user."
exit 0
if [ ! -b "$BLOCKDEV_SPECIFIED" ]; then
log "Error: Specified device $BLOCKDEV_SPECIFIED does not exist or is not a block device."
exit 1
fi

# Determine partition naming convention
if [[ $BLOCKDEV =~ [0-9]$ ]]; then
PARTPREFIX="${BLOCKDEV}p"
else
PARTPREFIX="${BLOCKDEV}"
BLOCKDEV="${BLOCKDEV_SPECIFIED##*/}"
log "Using specified block device: $BLOCKDEV_SPECIFIED"
else
# Find a suitable block device
if ! find_blockdev; then
log "Error: No suitable block device found."
exit 1
fi
log "Selected block device: /dev/${BLOCKDEV}"
fi

# Confirm with the user before proceeding
read -p "This will erase all data on /dev/${BLOCKDEV}. Are you sure you want to proceed? (yes/[no]): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
log "Operation cancelled by user."
exit 0
fi

# Determine partition naming convention
if [[ $BLOCKDEV =~ [0-9]$ ]]; then
PARTPREFIX="${BLOCKDEV}p"
else
PARTPREFIX="${BLOCKDEV}"
fi

# Wipe filesystem signatures
log "Wiping filesystem signatures on /dev/${BLOCKDEV}..."
wipefs -a -f /dev/${BLOCKDEV} || {
log "Failed to wipe filesystem signatures."
exit 1
}

# Wipe filesystem signatures
log "Wiping filesystem signatures on /dev/${BLOCKDEV}..."
wipefs -a -f /dev/${BLOCKDEV} || { log "Failed to wipe filesystem signatures."; exit 1; }

# Create GPT partition table
log "Creating GPT partition table on /dev/${BLOCKDEV}..."
parted -s /dev/${BLOCKDEV} mklabel gpt || { log "Failed to create GPT partition table."; exit 1; }
# Create GPT partition table
log "Creating GPT partition table on /dev/${BLOCKDEV}..."
parted -s /dev/${BLOCKDEV} mklabel gpt || {
log "Failed to create GPT partition table."
exit 1
}

# Create partitions
log "Creating partitions on /dev/${BLOCKDEV}..."
if ! parted -s /dev/${BLOCKDEV} "mkpart zosboot fat32 1MiB 300MiB set 1 esp on mkpart zoscache btrfs 300MiB 100%"; then
log "Failed to create partitions."
exit 1
fi
# Create partitions
log "Creating partitions on /dev/${BLOCKDEV}..."
if ! parted -s /dev/${BLOCKDEV} "mkpart zosboot fat16 1MiB 100MiB set 1 esp on mkpart zoscache btrfs 100MiB 100%"; then
log "Failed to create partitions."
exit 1
fi

# Format partitions
log "Formatting /dev/${PARTPREFIX}1 as FAT32..."
if ! mkfs.vfat -F32 /dev/${PARTPREFIX}1; then
log "Failed to format /dev/${PARTPREFIX}1 as FAT32."
exit 1
fi
# Format partitions
log "Formatting /dev/${PARTPREFIX}1 as FAT32..."
if ! mkfs.vfat -F32 -nZOSPXE /dev/${PARTPREFIX}1; then
log "Failed to format /dev/${PARTPREFIX}1 as FAT32."
exit 1
fi

log "Formatting /dev/${PARTPREFIX}2 as BTRFS..."
if ! mkfs.btrfs -f /dev/${PARTPREFIX}2; then
log "Failed to format /dev/${PARTPREFIX}2 as BTRFS."
exit 1
fi
log "Formatting /dev/${PARTPREFIX}2 as BTRFS..."
if ! mkfs.btrfs -f /dev/${PARTPREFIX}2 -LZOSCACHE; then
log "Failed to format /dev/${PARTPREFIX}2 as BTRFS."
exit 1
fi

# Mount and prepare directories
MOUNT_POINT="zospxe"
log "Creating and mounting directory ./${MOUNT_POINT}..."
mkdir -p "${MOUNT_POINT}"
if ! mount /dev/${PARTPREFIX}1 -t vfat "${MOUNT_POINT}"; then
log "Failed to mount /dev/${PARTPREFIX}1 to ${MOUNT_POINT}."
exit 1
fi

# Mount and prepare directories
MOUNT_POINT="zospxe"
log "Creating and mounting directory ./${MOUNT_POINT}..."
mkdir -p "${MOUNT_POINT}"
if ! mount /dev/${PARTPREFIX}1 -t vfat "${MOUNT_POINT}"; then
log "Failed to mount /dev/${PARTPREFIX}1 to ${MOUNT_POINT}."
exit 1
fi
log "Creating directory structure in ${MOUNT_POINT}..."
mkdir -p "${MOUNT_POINT}/efi/boot"

log "Creating directory structure in ${MOUNT_POINT}..."
mkdir -p "${MOUNT_POINT}/efi/boot"

# Download the required BOOTX64.EFI file
# NOTE:: EDIT THIS WHEN APPROPRIATE AND IN PROD
EFI_FILE_URL="https://bootstrap.grid.tf/uefi/${ENVIRONMENT}/${FARM}/debug/zero-os-development-zos-v4-debug-7d2de62033.efi?version=v4"
# EFI_FILE_URL="https://bootstrap.grid.tf/uefi/${ENVIRONMENT}/${FARM}"
log "Downloading BOOTX64.EFI from ${EFI_FILE_URL}..."
if ! wget -q "${EFI_FILE_URL}" -O "${MOUNT_POINT}/efi/boot/BOOTX64.EFI"; then
log "Failed to download BOOTX64.EFI."
exit 1
fi
# Download the required BOOTX64.EFI file
EFI_FILE_URL="https://bootstrap.grid.tf/uefi/${ENVIRONMENT}/${FARM}"
log "Downloading BOOTX64.EFI from ${EFI_FILE_URL}..."
if ! wget -q "${EFI_FILE_URL}" -O "${MOUNT_POINT}/efi/boot/BOOTX64.EFI"; then
log "Failed to download BOOTX64.EFI."
exit 1
fi

log "Download successful."
log "Download successful."

# Unmount the partition
log "Unmounting ${MOUNT_POINT}..."
umount -r "${MOUNT_POINT}" || { log "Failed to unmount ${MOUNT_POINT}."; exit 1; }
# Unmount the partition
log "Unmounting ${MOUNT_POINT}..."
umount -r "${MOUNT_POINT}" || {
log "Failed to unmount ${MOUNT_POINT}."
exit 1
}

# Manage UEFI boot entries
manage_boot_entries
# Manage UEFI boot entries
manage_boot_entries

log "Operation completed successfully."
log "Operation completed successfully."
}

# Execute the main function


Loading…
Cancel
Save