From dd24fbc4d1a4c3448b58d93044aa3be791c6d9fc Mon Sep 17 00:00:00 2001 From: MacRimi Date: Sat, 5 Apr 2025 17:31:19 +0200 Subject: [PATCH] Update disk-passthrough.sh --- scripts/disk-passthrough.sh | 222 ++++++++++++++++++++++++++---------- 1 file changed, 163 insertions(+), 59 deletions(-) diff --git a/scripts/disk-passthrough.sh b/scripts/disk-passthrough.sh index dbeb82d..94e5c16 100644 --- a/scripts/disk-passthrough.sh +++ b/scripts/disk-passthrough.sh @@ -42,8 +42,6 @@ initialize_cache - -# Function to get detailed disk information get_disk_info() { local disk=$1 MODEL=$(lsblk -dn -o MODEL "$disk" | xargs) @@ -52,16 +50,13 @@ get_disk_info() { } - - -# Display list of available VMs VM_LIST=$(qm list | awk 'NR>1 {print $1, $2}') if [ -z "$VM_LIST" ]; then whiptail --title "$(translate "Error")" --msgbox "$(translate "No VMs available in the system.")" 8 40 exit 1 fi -# Select VM + 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 @@ -72,8 +67,6 @@ fi VMID=$(echo "$VMID" | tr -d '"') - -clear msg_ok "$(translate "VM selected successfully.")" @@ -84,42 +77,148 @@ if [ "$VM_STATUS" == "running" ]; then fi +########################################## + msg_info "$(translate "Detecting available disks...")" +USED_DISKS=$(lsblk -n -o PKNAME,TYPE | grep 'lvm' | awk '{print "/dev/" $1}') +MOUNTED_DISKS=$(lsblk -ln -o NAME,MOUNTPOINT | awk '$2!="" {print "/dev/" $1}') + +ZFS_DISKS="" +ZFS_RAW=$(zpool list -v -H 2>/dev/null | awk '{print $1}' | grep -v '^NAME$' | grep -v '^-' | grep -v '^mirror') + +for entry in $ZFS_RAW; do + + path="" + if [[ "$entry" == wwn-* || "$entry" == ata-* ]]; then + if [ -e "/dev/disk/by-id/$entry" ]; then + path=$(readlink -f "/dev/disk/by-id/$entry") + fi + elif [[ "$entry" == /dev/* ]]; then + path="$entry" + fi + + + if [ -n "$path" ]; then + base_disk=$(lsblk -no PKNAME "$path" 2>/dev/null) + if [ -n "$base_disk" ]; then + ZFS_DISKS+="/dev/$base_disk"$'\n' + fi + fi +done + +ZFS_DISKS=$(echo "$ZFS_DISKS" | sort -u) + + +is_disk_in_use() { + local disk="$1" + + + while read -r part fstype; do + case "$fstype" in + zfs_member|linux_raid_member) + return 0 ;; + esac + + if echo "$MOUNTED_DISKS" | grep -q "/dev/$part"; then + return 0 + fi + done < <(lsblk -ln -o NAME,FSTYPE "$disk" | tail -n +2) + + + if echo "$USED_DISKS" | grep -q "$disk" || echo "$ZFS_DISKS" | grep -q "$disk"; then + return 0 + fi + + return 1 +} + - USED_DISKS=$(lsblk -n -o PKNAME,TYPE | grep 'lvm' | awk '{print "/dev/" $1}') - MOUNTED_DISKS=$(mount | grep -o '/dev/[a-z]*' | sort | uniq) - -# Detect free disks, excluding the system disk and those already assigned to the selected VM FREE_DISKS=() + +LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -n1 readlink -f | sort -u) +RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{print $1}' | sort -u) + while read -r DISK; do - if ! echo "$USED_DISKS" | grep -q "$DISK" && \ - ! echo "$MOUNTED_DISKS" | grep -q "$DISK" && \ - ! qm config "$VMID" | grep -q "$DISK"; then - - INFO=($(get_disk_info "$DISK")) - MODEL="${INFO[@]::${#INFO[@]}-1}" - SIZE="${INFO[-1]}" - - DESCRIPTION=$(printf "%-40s %10s" "$MODEL" "$SIZE") - + [[ "$DISK" =~ /dev/zd ]] && continue + + INFO=($(get_disk_info "$DISK")) + MODEL="${INFO[@]::${#INFO[@]}-1}" + SIZE="${INFO[-1]}" + LABEL="" + SHOW_DISK=true + + IS_MOUNTED=false + IS_RAID=false + IS_ZFS=false + IS_LVM=false + + while read -r part fstype; do + [[ "$fstype" == "zfs_member" ]] && IS_ZFS=true + [[ "$fstype" == "linux_raid_member" ]] && IS_RAID=true + [[ "$fstype" == "LVM2_member" ]] && IS_LVM=true + if grep -q "/dev/$part" <<< "$MOUNTED_DISKS"; then + IS_MOUNTED=true + fi + done < <(lsblk -ln -o NAME,FSTYPE "$DISK" | tail -n +2) + + REAL_PATH=$(readlink -f "$DISK") + if echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then + IS_MOUNTED=true + fi + + + if $IS_RAID && grep -q "$DISK" <<< "$(cat /proc/mdstat)"; then + if grep -q "active raid" /proc/mdstat; then + SHOW_DISK=false + fi + fi + + + if $IS_ZFS; then + SHOW_DISK=false + fi + + + if $IS_MOUNTED; then + SHOW_DISK=false + fi + + + if qm config "$VMID" | grep -q "$DISK"; then + SHOW_DISK=false + fi + + if $SHOW_DISK; then + [[ "$IS_RAID" == true ]] && LABEL+=" ⚠ with partitions" + [[ "$IS_LVM" == true ]] && LABEL+=" ⚠ LVM" + [[ "$IS_ZFS" == true ]] && LABEL+=" ⚠ ZFS" + + DESCRIPTION=$(printf "%-30s %10s%s" "$MODEL" "$SIZE" "$LABEL") FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF") fi done < <(lsblk -dn -e 7,11 -o PATH) -msg_ok "$(translate "Available disks detected.")" - if [ "${#FREE_DISKS[@]}" -eq 0 ]; then + cleanup whiptail --title "$(translate "Error")" --msgbox "$(translate "No disks available for this VM.")" 8 40 clear exit 1 fi +msg_ok "$(translate "Available disks detected.")" + + + +###################################################### + + + MAX_WIDTH=$(printf "%s\n" "${FREE_DISKS[@]}" | awk '{print length}' | sort -nr | head -n1) TOTAL_WIDTH=$((MAX_WIDTH + 20)) @@ -140,7 +239,7 @@ 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")" \ @@ -159,66 +258,70 @@ 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="" + RUNNING_VMS="" + 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" + VM_STATUS=$(qm status "$VM_ID" | awk '{print $2}') + if [ "$VM_STATUS" == "running" ]; then + RUNNING_VMS+="$VM_ID $VM_NAME\n" + fi 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 + fi - 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 + + if [ -n "$ASSIGNED_TO" ]; then + cleanup + whiptail --title "$(translate "Disk Already Assigned")" --yesno "$(translate "The disk") $DISK_INFO $(translate "is already assigned to the following VM(s):")\\n$ASSIGNED_TO\\n\\n$(translate "Do you want to continue anyway?")" 15 70 + if [ $? -ne 0 ]; then + sleep 1 + exec "$0" fi fi - if $CONTINUE; then - INDEX=0 - while qm config "$VMID" | grep -q "${INTERFACE}${INDEX}"; do - ((INDEX++)) - done - RESULT=$(qm set "$VMID" -${INTERFACE}${INDEX} "$DISK" 2>&1) + INDEX=0 + while qm config "$VMID" | grep -q "${INTERFACE}${INDEX}"; do + ((INDEX++)) + done - if [ $? -eq 0 ]; then - MESSAGE="$(translate "The disk") $DISK_INFO $(translate "has been successfully added to VM") $VMID." - if [ -n "$ASSIGNED_TO" ]; then - MESSAGE+="\n$(translate "WARNING: This disk is also assigned to the following VM(s):")\n$ASSIGNED_TO" - MESSAGE+="$(translate "Make sure not to start VMs that share this disk at the same time to avoid data corruption.")\n" - fi - SUCCESS_MESSAGES+="$MESSAGE\\n\\n" - ((DISKS_ADDED++)) - else - ERROR_MESSAGES+="$(translate "Could not add disk") $DISK_INFO $(translate "to VM") $VMID.\\n$(translate "Error:") $RESULT\\n\\n" + RESULT=$(qm set "$VMID" -${INTERFACE}${INDEX} "$DISK" 2>&1) + + if [ $? -eq 0 ]; then + MESSAGE="$(translate "The disk") $DISK_INFO $(translate "has been successfully added to VM") $VMID." + if [ -n "$ASSIGNED_TO" ]; then + MESSAGE+="\\n\\n$(translate "WARNING: This disk is also assigned to the following VM(s):")\\n$ASSIGNED_TO" + MESSAGE+="\\n$(translate "Make sure not to start VMs that share this disk at the same time to avoid data corruption.")" fi + SUCCESS_MESSAGES+="$MESSAGE\\n\\n" + ((DISKS_ADDED++)) + else + ERROR_MESSAGES+="$(translate "Could not add disk") $DISK_INFO $(translate "to VM") $VMID.\\n$(translate "Error:") $RESULT\\n\\n" fi done msg_ok "$(translate "Disk processing completed.")" + + if [ -n "$SUCCESS_MESSAGES" ]; then - - -MSG_LINES=$(echo "$SUCCESS_MESSAGES" | wc -l) - - whiptail --title "$(translate "Successful Operations")" --msgbox "$SUCCESS_MESSAGES" 16 70 - + MSG_LINES=$(echo "$SUCCESS_MESSAGES" | wc -l) + whiptail --title "$(translate "Successful Operations")" --msgbox "$SUCCESS_MESSAGES" 16 70 fi if [ -n "$ERROR_MESSAGES" ]; then @@ -226,4 +329,5 @@ if [ -n "$ERROR_MESSAGES" ]; then fi + exit 0