| @@ -0,0 +1,294 @@ | |||
| #!/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, sdb)." | |||
| 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 suitable block devices | |||
| find_blockdev() { | |||
| # Minimum size in bytes, e.g., 10 GiB | |||
| MIN_SIZE=$((10 * 1024 * 1024 * 1024)) | |||
| BLOCKDEVICES=() | |||
| # Exclude the boot disk | |||
| BOOT_DISK=$(findmnt -n -o SOURCE / | sed 's/[0-9]*$//;s|^/dev/||') | |||
| log "Boot disk detected: /dev/${BOOT_DISK}" | |||
| # Search for NVMe devices first | |||
| log "Searching for NVMe devices..." | |||
| for DEV in /dev/nvme*n1; do | |||
| DEV_BASE=$(basename "$DEV") | |||
| if [ -b "$DEV" ] && [ "$DEV_BASE" != "$BOOT_DISK" ]; then | |||
| SIZE_BYTES=$(blockdev --getsize64 "$DEV") | |||
| if [ "$SIZE_BYTES" -ge "$MIN_SIZE" ]; then | |||
| log "Found NVMe device: $DEV ($(numfmt --to=iec $SIZE_BYTES))" | |||
| BLOCKDEVICES+=("$DEV_BASE") | |||
| else | |||
| log "$DEV is smaller than $(numfmt --to=iec $MIN_SIZE). Skipping." | |||
| fi | |||
| else | |||
| [ "$DEV_BASE" == "$BOOT_DISK" ] && log "Skipping boot disk: $DEV" | |||
| fi | |||
| done | |||
| # Search for SSD devices | |||
| log "Searching for SSD devices..." | |||
| for DEV_PATH in /sys/block/*; do | |||
| DEV_NAME=$(basename "$DEV_PATH") | |||
| # Exclude unwanted devices and the boot disk | |||
| case "$DEV_NAME" in | |||
| loop* | ram* | sr* | fd* | md* | dm-*) | |||
| continue | |||
| ;; | |||
| "$BOOT_DISK") | |||
| log "Skipping boot disk: /dev/$DEV_NAME" | |||
| 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))" | |||
| BLOCKDEVICES+=("$DEV_NAME") | |||
| else | |||
| log "$DEVICE is smaller than $(numfmt --to=iec $MIN_SIZE). Skipping." | |||
| fi | |||
| fi | |||
| done | |||
| if [ ${#BLOCKDEVICES[@]} -eq 0 ]; then | |||
| log "No suitable block devices found." | |||
| return 1 | |||
| fi | |||
| log "List of block devices to be wiped:" | |||
| for DEV in "${BLOCKDEVICES[@]}"; do | |||
| log "/dev/$DEV" | |||
| done | |||
| # Select the first device as the block device to partition | |||
| BLOCKDEV="${BLOCKDEVICES[0]}" | |||
| } | |||
| # 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##*/}" | |||
| BLOCKDEVICES=("$BLOCKDEV") | |||
| log "Using specified block device: $BLOCKDEV_SPECIFIED" | |||
| else | |||
| # Find suitable block devices | |||
| if ! find_blockdev; then | |||
| log "Error: No suitable block devices found." | |||
| exit 1 | |||
| fi | |||
| fi | |||
| # Confirm with the user before proceeding | |||
| log "The following disks will be wiped:" | |||
| for DEV in "${BLOCKDEVICES[@]}"; do | |||
| log "/dev/$DEV" | |||
| done | |||
| read -p "This will erase all data on the above disks. Are you sure you want to proceed? Type 'WIPE ALL' to confirm: " CONFIRM | |||
| if [ "$CONFIRM" != "WIPE ALL" ]; then | |||
| log "Operation cancelled by user." | |||
| exit 0 | |||
| fi | |||
| # Wipe filesystem signatures on all disks | |||
| log "Wiping filesystem signatures on all selected disks..." | |||
| for DEV in "${BLOCKDEVICES[@]}"; do | |||
| log "Wiping /dev/$DEV..." | |||
| wipefs -a -f /dev/$DEV || { | |||
| log "Failed to wipe /dev/$DEV" | |||
| exit 1 | |||
| } | |||
| done | |||
| # Proceed with partitioning and formatting the first disk | |||
| # Determine partition naming convention | |||
| if [[ $BLOCKDEV =~ [0-9]$ ]]; then | |||
| PARTPREFIX="${BLOCKDEV}p" | |||
| else | |||
| PARTPREFIX="${BLOCKDEV}" | |||
| fi | |||
| # Create GPT partition table on the selected block device | |||
| log "Creating GPT partition table on /dev/${BLOCKDEV}..." | |||
| parted -s /dev/${BLOCKDEV} mklabel gpt || { | |||
| log "Failed to create GPT partition table." | |||
| exit 1 | |||
| } | |||
| # Create BIOS Boot Partition | |||
| log "Creating BIOS Boot partition on /dev/${BLOCKDEV}..." | |||
| parted -s /dev/${BLOCKDEV} mkpart primary 1MiB 2MiB || { | |||
| log "Failed to create BIOS Boot partition." | |||
| exit 1 | |||
| } | |||
| parted -s /dev/${BLOCKDEV} set 1 bios_grub on | |||
| # Create additional partitions as needed | |||
| log "Creating data partitions on /dev/${BLOCKDEV}..." | |||
| parted -s /dev/${BLOCKDEV} mkpart primary 2MiB 100MiB || { | |||
| log "Failed to create partition." | |||
| exit 1 | |||
| } | |||
| parted -s /dev/${BLOCKDEV} mkpart primary 100MiB 100% || { | |||
| log "Failed to create partition." | |||
| exit 1 | |||
| } | |||
| # Format partitions | |||
| log "Formatting /dev/${PARTPREFIX}2 as FAT16..." | |||
| if ! mkfs.vfat -F16 /dev/${PARTPREFIX}2; then | |||
| log "Failed to format /dev/${PARTPREFIX}2 as FAT16." | |||
| exit 1 | |||
| fi | |||
| log "Formatting /dev/${PARTPREFIX}3 as BTRFS..." | |||
| if ! mkfs.btrfs -f /dev/${PARTPREFIX}3; then | |||
| log "Failed to format /dev/${PARTPREFIX}3 as BTRFS." | |||
| exit 1 | |||
| fi | |||
| # Install iPXE bootloader to MBR | |||
| log "Installing iPXE bootloader to MBR of /dev/${BLOCKDEV}..." | |||
| # Download the iPXE bootloader suitable for BIOS | |||
| log "Downloading iPXE bootloader..." | |||
| if ! wget -q -O ipxe.lkrn http://boot.ipxe.org/ipxe.lkrn; then | |||
| log "Failed to download iPXE bootloader." | |||
| exit 1 | |||
| fi | |||
| # Write iPXE bootloader to the MBR | |||
| log "Writing iPXE bootloader to MBR of /dev/${BLOCKDEV}..." | |||
| dd if=ipxe.lkrn of=/dev/${BLOCKDEV} bs=440 count=1 conv=notrunc || { | |||
| log "Failed to write iPXE to MBR." | |||
| exit 1 | |||
| } | |||
| # Clean up the downloaded file | |||
| rm -f ipxe.lkrn | |||
| # Prepare the iPXE script | |||
| log "Preparing iPXE script..." | |||
| # Mount the FAT16 partition to store the iPXE script | |||
| MOUNT_POINT="zospxe" | |||
| log "Creating and mounting directory ./${MOUNT_POINT}..." | |||
| mkdir -p "${MOUNT_POINT}" | |||
| if ! mount /dev/${PARTPREFIX}2 -t vfat "${MOUNT_POINT}"; then | |||
| log "Failed to mount /dev/${PARTPREFIX}2 to ${MOUNT_POINT}." | |||
| exit 1 | |||
| fi | |||
| # Download the BOOTX64.EFI file (optional, depending on your setup) | |||
| # Since this is a BIOS system, we might not need this step | |||
| # However, if required, adjust the script accordingly | |||
| # Alternatively, create an embedded iPXE script | |||
| log "Creating iPXE script..." | |||
| cat >"${MOUNT_POINT}/boot.ipxe" <<EOF | |||
| #!ipxe | |||
| dhcp | |||
| set farm ${FARM} | |||
| chain https://bootstrap.grid.tf/uefi/${ENVIRONMENT}/\${FARM}/debug/zero-os-development-zos-v4-debug-7d2de62033.efi?version=v4" | |||
| # chain https://bootstrap.grid.tf/ipxe/${ENVIRONMENT}/\${farm} | |||
| EOF | |||
| log "iPXE script created at ${MOUNT_POINT}/boot.ipxe" | |||
| # Unmount the partition | |||
| log "Unmounting ${MOUNT_POINT}..." | |||
| umount "${MOUNT_POINT}" || { | |||
| log "Failed to unmount ${MOUNT_POINT}." | |||
| exit 1 | |||
| } | |||
| log "Operation completed successfully." | |||
| } | |||
| # Execute the main function | |||
| main "$@" | |||