| @@ -0,0 +1,308 @@ | |||||
| #!/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}" | |||||
| EFI_FILE_URL="https://v4.bootstrap.grid.tf/uefi/${ENVIRONMENT}/${FARM}/debug/zero-os-development-zos-v4-rollback-zosbase-debug-9dddfa0299.efi" | |||||
| 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 "$@" | |||||