Update import-disk-image.sh

This commit is contained in:
MacRimi 2025-02-04 17:07:48 +01:00 committed by GitHub
parent bf5eab6092
commit 9f54f18356
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -10,23 +10,21 @@
# Last Updated: 28/01/2025 # Last Updated: 28/01/2025
# ========================================================== # ==========================================================
# Description: # Description:
# This script allows users to assign physical disks to existing # This script automates the process of importing disk images
# Proxmox virtual machines (VMs) through an interactive menu. # into existing Proxmox virtual machines (VMs). It:
# - Detects the system disk and excludes it from selection. # - Scans the storage directory for compatible disk images (.img, .qcow2, .vmdk)
# - Lists all available VMs for the user to choose from. # - Allows the user to select a target VM for disk import
# - Identifies and displays unassigned physical disks. # - Lists available Proxmox storage volumes for disk placement
# - Allows the user to select multiple disks and attach them to a VM. # - Enables users to select one or multiple disk images for import
# - Supports interface types: SATA, SCSI, VirtIO, and IDE. # - Assigns a suitable disk interface (SATA, SCSI, VirtIO, IDE)
# - Ensures that disks are not already assigned to active VMs. # - Handles SSD emulation and bootable disk configuration (optional)
# - Warns about disk sharing between multiple VMs to avoid data corruption. # - Ensures disks are properly attached to the VM and configured
# - Configures the selected disks for the VM and verifies the assignment.
# #
# The goal of this script is to simplify the process of assigning # This script simplifies the process of managing virtual disk
# physical disks to Proxmox VMs, reducing manual configurations # imports, making it easier to integrate pre-existing disk images
# and preventing potential errors. # into Proxmox virtual machines without manual configuration.
# ========================================================== # ==========================================================
# Configuration ============================================ # Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux" BASE_DIR="/usr/local/share/proxmenux"
@ -40,214 +38,208 @@ load_language
initialize_cache initialize_cache
# ========================================================== # ==========================================================
# Path where disk images are stored
IMAGES_DIR="/var/lib/vz/template/images/"
# Function to identify the physical disk where Proxmox is installed # Ensure directory exists
get_physical_disk() { if [ ! -d "$IMAGES_DIR" ]; then
local lv_path=$1 mkdir -p "$IMAGES_DIR"
local pv_name chmod 755 "$IMAGES_DIR"
pv_name=$(pvs --noheadings -o pv_name 2>/dev/null | grep -v "/dev/mapper" | head -n1 | tr -d ' ') || true msg_info "Created missing directory: $IMAGES_DIR"
if [ -z "$pv_name" ]; then fi
echo "$(translate "Could not determine the physical disk. Is LVM installed?")" >&2 # Check if there are any images in the directory
return 1 IMAGES=$(ls -A "$IMAGES_DIR" | grep -E "\.(img|qcow2|vmdk)$")
fi if [ -z "$IMAGES" ]; then
echo "$pv_name" | sed 's/[0-9]*$//' msg_error "$(translate 'No images available for import in') $IMAGES_DIR"
} echo -e "${YW}$(translate 'Supported formats: .img, .qcow2, .vmdk')${CL}"
echo -e "${YW}$(translate 'Please add some images and try again.')${CL}"
# Function to get detailed disk information exit 0
get_disk_info() {
local disk=$1
lsblk -ndo NAME,MODEL,SIZE "$disk" | awk '{print $1 " " $2 " " $3}'
}
# Detect the root partition and associated physical disk
root_device=$(findmnt -n -o SOURCE / 2>/dev/null) || { echo "$(translate "Could not determine the root device.")" >&2; exit 1; }
if [[ $root_device == /dev/mapper/* ]]; then
physical_disk=$(get_physical_disk "$root_device")
else
physical_disk=$(echo "$root_device" | sed 's/[0-9]*$//')
fi fi
if [ -z "$physical_disk" ]; then
echo "$(translate "Could not determine the physical disk.")" >&2
exit 1 # Initial setup
if ! [ -d "$IMAGES_DIR" ]; then
msg_info "$(translate 'Creating images directory')"
mkdir -p "$IMAGES_DIR"
msg_ok "$(translate 'Images directory created')"
fi fi
msg_ok "$(translate "System physical disk identified"): $physical_disk. $(translate "This disk will not be shown.")" # Display initial message
whiptail --title "$(translate 'Import Disk Image')" --msgbox "$(translate 'Make sure the disk images you want to import are located in:')\n\n$IMAGES_DIR\n\n$(translate 'Supported formats: .img, .qcow2, .vmdk.')" 12 60
# Display list of available VMs
VM_LIST=$(qm list | awk 'NR>1 {print $1, $2}')
# 1. Select VM
msg_info "$(translate 'Getting VM list')"
VM_LIST=$(qm list | awk 'NR>1 {print $1" "$2}')
if [ -z "$VM_LIST" ]; then if [ -z "$VM_LIST" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No VMs available in the system.")" 8 40 msg_error "$(translate 'No VMs available in the system')"
exit 1 exit 1
fi fi
msg_ok "$(translate 'VM list obtained')"
# Select VM VMID=$(whiptail --title "$(translate 'Select VM')" --menu "$(translate 'Select the VM where you want to import the disk image:')" 15 60 8 $VM_LIST 3>&1 1>&2 2>&3)
VMID=$(whiptail --title "$(translate "Select VM")" --menu "$(translate "Select the VM to which you want to add disks:")" 15 60 8 $VM_LIST 3>&1 1>&2 2>&3)
if [ -z "$VMID" ]; then if [ -z "$VMID" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No VM was selected.")" 8 40 msg_error "$(translate 'No VM selected')"
exit 1 exit 1
fi fi
VMID=$(echo "$VMID" | tr -d '"')
# Verify that VMID is a number
if ! [[ "$VMID" =~ ^[0-9]+$ ]]; then # 2. Select storage volume
whiptail --title "$(translate "Error")" --msgbox "$(translate "The selected VM ID is not valid.")" 8 40 msg_info "$(translate 'Getting storage volumes')"
STORAGE_LIST=$(pvesm status -content images | awk 'NR>1 {print $1}')
if [ -z "$STORAGE_LIST" ]; then
msg_error "$(translate 'No storage volumes available')"
exit 1
fi
msg_ok "$(translate 'Storage volumes obtained')"
# Create an array of storage options for whiptail
STORAGE_OPTIONS=()
while read -r storage; do
STORAGE_OPTIONS+=("$storage" "")
done <<< "$STORAGE_LIST"
STORAGE=$(whiptail --title "$(translate 'Select Storage')" --menu "$(translate 'Select the storage volume for disk import:')" 15 60 8 "${STORAGE_OPTIONS[@]}" 3>&1 1>&2 2>&3)
if [ -z "$STORAGE" ]; then
msg_error "$(translate 'No storage selected')"
exit 1 exit 1
fi fi
clear
msg_ok "$(translate "VM selected successfully.")"
# Check if the VM is powered on
VM_STATUS=$(qm status "$VMID" | awk '{print $2}') # 3. Select disk images
if [ "$VM_STATUS" == "running" ]; then msg_info "$(translate 'Scanning disk images')"
whiptail --title "$(translate "Warning")" --msgbox "$(translate "The VM is powered on. Turn it off before adding disks.")" 12 60 if [ -z "$IMAGES" ]; then
msg_error "$(translate 'No compatible disk images found in') $IMAGES_DIR"
exit 0
fi
msg_ok "$(translate 'Disk images found')"
IMAGE_OPTIONS=()
while read -r img; do
IMAGE_OPTIONS+=("$img" "" "OFF")
done <<< "$IMAGES"
SELECTED_IMAGES=$(whiptail --title "$(translate 'Select Disk Images')" --checklist "$(translate 'Select the disk images to import:')" 20 60 10 "${IMAGE_OPTIONS[@]}" 3>&1 1>&2 2>&3)
if [ -z "$SELECTED_IMAGES" ]; then
msg_error "$(translate 'No images selected')"
exit 1 exit 1
fi fi
msg_info "$(translate "Detecting available disks...")" # 4. Import each selected image
for IMAGE in $SELECTED_IMAGES; do
# Detect free disks, excluding the system disk and those already assigned to the selected VM # Remove quotes from selected image
FREE_DISKS=() IMAGE=$(echo "$IMAGE" | tr -d '"')
while read -r LINE; do
DISK=$(echo "$LINE" | awk '{print $1}')
if [[ "/dev/$DISK" != "$physical_disk" ]] && ! qm config "$VMID" | grep -q "/dev/$DISK"; then
DESCRIPTION=$(echo "$LINE" | awk '{$1=""; print $0}' | xargs)
FREE_DISKS+=("/dev/$DISK" "$DESCRIPTION" "OFF")
fi
done < <(lsblk -d -n -e 7,11 -o NAME,MODEL,SIZE)
msg_ok "$(translate "Available disks detected.")" # 5. Select interface type for each image
INTERFACE=$(whiptail --title "$(translate 'Interface Type')" --menu "$(translate 'Select the interface type for the image:') $IMAGE" 15 40 4 \
"sata" "SATA" \
"scsi" "SCSI" \
"virtio" "VirtIO" \
"ide" "IDE" 3>&1 1>&2 2>&3)
if [ "${#FREE_DISKS[@]}" -eq 0 ]; then if [ -z "$INTERFACE" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No disks available for this VM.")" 8 40 msg_error "$(translate 'No interface type selected for') $IMAGE"
clear continue
exit 1
fi
# Calculate maximum content length
MAX_WIDTH=$(printf "%s\n" "${FREE_DISKS[@]}" | awk '{print length}' | sort -nr | head -n1)
TOTAL_WIDTH=$((MAX_WIDTH + 20)) # Add additional margin
# Set a reasonable minimum width
if [ $TOTAL_WIDTH -lt 70 ]; then
TOTAL_WIDTH=70
fi
# Display menu to select free disks with dynamically calculated width
SELECTED=$(whiptail --title "$(translate "Select Disks")" --checklist \
"$(translate "Select the disks you want to add:")" 20 $TOTAL_WIDTH 10 "${FREE_DISKS[@]}" 3>&1 1>&2 2>&3)
# Check if disks were selected
if [ -z "$SELECTED" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No disks were selected.")" 10 $TOTAL_WIDTH
clear
exit 1
fi
msg_ok "$(translate "Disks selected successfully.")"
# Select interface type once for all disks
INTERFACE=$(whiptail --title "$(translate "Interface Type")" --menu "$(translate "Select the interface type for all disks:")" 15 40 4 \
"sata" "$(translate "Add as SATA")" \
"scsi" "$(translate "Add as SCSI")" \
"virtio" "$(translate "Add as VirtIO")" \
"ide" "$(translate "Add as IDE")" 3>&1 1>&2 2>&3)
if [ -z "$INTERFACE" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No interface type was selected for the disks.")" 8 40
clear
exit 1
fi
msg_ok "$(translate "Interface type selected: $INTERFACE")"
# Verify selected disks
DISKS_ADDED=0
ERROR_MESSAGES=""
SUCCESS_MESSAGES=""
msg_info "$(translate "Processing selected disks...")"
for DISK in $SELECTED; do
DISK=$(echo "$DISK" | tr -d '"')
DISK_INFO=$(get_disk_info "$DISK")
# Check if the disk is already assigned to another VM
ASSIGNED_TO=""
while read -r VM_ID VM_NAME; do
if [[ "$VM_ID" =~ ^[0-9]+$ ]] && qm config "$VM_ID" | grep -q "$DISK"; then
ASSIGNED_TO+="$VM_ID $VM_NAME\n"
fi
done < <(qm list | awk 'NR>1 {print $1, $2}')
CONTINUE=true
if [ -n "$ASSIGNED_TO" ]; then
RUNNING_VMS=""
while read -r VM_ID VM_NAME; do
if [[ "$VM_ID" =~ ^[0-9]+$ ]] && [ "$(qm status "$VM_ID" | awk '{print $2}')" == "running" ]; then
RUNNING_VMS+="$VM_ID $VM_NAME\n"
fi
done < <(echo -e "$ASSIGNED_TO")
if [ -n "$RUNNING_VMS" ]; then
ERROR_MESSAGES+="$(translate "The disk") $DISK_INFO $(translate "is in use by the following running VM(s):")\\n$RUNNING_VMS\\n\\n"
CONTINUE=false
fi
fi fi
if $CONTINUE; then FULL_PATH="$IMAGES_DIR/$IMAGE"
INDEX=0
while qm config "$VMID" | grep -q "${INTERFACE}${INDEX}"; do # Show initial message
((INDEX++)) msg_info "$(translate 'Importing image:')"
# Temporary file to capture the imported disk
TEMP_DISK_FILE=$(mktemp)
# Execute the command and process its output in real-time
qm importdisk "$VMID" "$FULL_PATH" "$STORAGE" 2>&1 | while read -r line; do
if [[ "$line" =~ transferred ]]; then
# Extract the progress percentage
PERCENT=$(echo "$line" | grep -oP "\(\d+\.\d+%\)" | tr -d '()%')
# Show progress with custom format without translation
echo -ne "\r${TAB}${YW}-$(translate 'Importing image:') $IMAGE-${CL} ${PERCENT}%"
elif [[ "$line" =~ successfully\ imported\ disk ]]; then
# Extract the imported disk name and save it to the temporary file
echo "$line" | grep -oP "(?<=successfully imported disk ').*(?=')" > "$TEMP_DISK_FILE"
fi
done done
echo -ne "\n"
# Perform the assignment
RESULT=$(qm set "$VMID" -${INTERFACE}${INDEX} "$DISK" 2>&1)
if [ $? -eq 0 ]; then IMPORT_STATUS=${PIPESTATUS[0]} # Capture the exit status of the main command
MESSAGE="$(translate "The disk") $DISK_INFO $(translate "has been successfully added to VM") $VMID."
if [ -n "$ASSIGNED_TO" ]; then if [ $IMPORT_STATUS -eq 0 ]; then
MESSAGE+="\n$(translate "WARNING: This disk is also assigned to the following VM(s):")\n$ASSIGNED_TO" msg_ok "$(translate 'Image imported successfully')"
MESSAGE+="$(translate "Make sure not to power on VMs that share this disk simultaneously to avoid data corruption.")\n"
fi # Read the imported disk from the temporary file
SUCCESS_MESSAGES+="$MESSAGE\\n\\n" IMPORTED_DISK=$(cat "$TEMP_DISK_FILE")
((DISKS_ADDED++)) rm -f "$TEMP_DISK_FILE" # Delete the temporary file
if [ -n "$IMPORTED_DISK" ]; then
# Find the next available disk slot
EXISTING_DISKS=$(qm config "$VMID" | grep -oP "${INTERFACE}\d+" | sort -n)
if [ -z "$EXISTING_DISKS" ]; then
# If there are no existing disks, start from 0
NEXT_SLOT=0
else else
ERROR_MESSAGES+="$(translate "Could not add disk") $DISK_INFO $(translate "to VM") $VMID.\\n$(translate "Error:") $RESULT\\n\\n" # If there are existing disks, take the last one and add 1
LAST_SLOT=$(echo "$EXISTING_DISKS" | tail -n1 | sed "s/${INTERFACE}//")
NEXT_SLOT=$((LAST_SLOT + 1))
fi fi
# Ask if SSD emulation is desired (only for non-VirtIO interfaces)
if [ "$INTERFACE" != "virtio" ]; then
if (whiptail --title "$(translate 'SSD Emulation')" --yesno "$(translate 'Do you want to use SSD emulation for this disk?')" 10 60); then
SSD_OPTION=",ssd=1"
else
SSD_OPTION=""
fi
else
SSD_OPTION=""
fi
msg_info "$(translate 'Configuring disk')"
# Configure the disk in the VM
if qm set "$VMID" --${INTERFACE}${NEXT_SLOT} "$IMPORTED_DISK${SSD_OPTION}" &>/dev/null; then
msg_ok "$(translate 'Image') $IMAGE $(translate 'configured as') ${INTERFACE}${NEXT_SLOT}"
# Ask if the disk should be bootable
if (whiptail --title "$(translate 'Make Bootable')" --yesno "$(translate 'Do you want to make this disk bootable?')" 10 60); then
msg_info "$(translate 'Configuring disk as bootable')"
if qm set "$VMID" --boot c --bootdisk ${INTERFACE}${NEXT_SLOT} &>/dev/null; then
msg_ok "$(translate 'Disk configured as bootable')"
else
msg_error "$(translate 'Could not configure the disk as bootable')"
fi
fi
else
msg_error "$(translate 'Could not configure disk') ${INTERFACE}${NEXT_SLOT} $(translate 'for VM') $VMID"
fi
else
msg_error "$(translate 'Could not find the imported disk')"
fi
else
msg_error "$(translate 'Could not import') $IMAGE"
fi fi
done done
msg_ok "$(translate "Disk processing completed.")" msg_ok "$(translate 'All selected images have been processed')"
sleep 2
# Display success messages
if [ -n "$SUCCESS_MESSAGES" ]; then
MSG_LINES=$(echo "$SUCCESS_MESSAGES" | wc -l)
whiptail --title "$(translate "Successful Operations")" --scrolltext --msgbox "$SUCCESS_MESSAGES" 20 70
fi
# Display error or warning messages if any
if [ -n "$ERROR_MESSAGES" ]; then
whiptail --title "$(translate "Warnings and Errors")" --scrolltext --msgbox "$ERROR_MESSAGES" 20 70
fi
# Operation completed message
if [ $DISKS_ADDED -gt 0 ]; then
whiptail --title "$(translate "Operation Completed")" --msgbox "$(translate "$DISKS_ADDED disk(s) were successfully added to VM") $VMID." 8 60
else
whiptail --title "$(translate "Information")" --msgbox "$(translate "No disks were added to VM") $VMID." 8 60
fi
clear
exit 0