#!/bin/bash # Enable strict error handling set -e # Function to log messages with timestamps log() { 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 } # 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 log "No suitable block device found." return 1 } # Function to manage UEFI boot entries manage_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 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 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 } # 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: $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 ;; 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}" 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 } # 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 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 -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 -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 log "Creating directory structure in ${MOUNT_POINT}..." mkdir -p "${MOUNT_POINT}/efi/boot" # 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." # 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 log "Operation completed successfully." } # Execute the main function main "$@"