2026-04-01 23:09:51 +02:00
#!/bin/bash
# ==========================================================
# ProxMenux - Local Disk Manager for Proxmox Host
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT
# ==========================================================
# Description:
# Adds local SCSI/SATA/NVMe disks as Proxmox directory storage
2026-04-12 20:32:34 +02:00
# (pvesm add dir) or ZFS pool storage (pvesm add zfspool).
# The disk can be formatted (ext4/xfs/zfs) and registered in Proxmox.
2026-04-01 23:09:51 +02:00
# ==========================================================
2026-04-12 20:32:34 +02:00
SCRIPT_DIR = " $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) " && pwd ) "
LOCAL_SCRIPTS_LOCAL = " $( cd " $SCRIPT_DIR /.. " && pwd ) "
LOCAL_SCRIPTS_DEFAULT = "/usr/local/share/proxmenux/scripts"
LOCAL_SCRIPTS = " $LOCAL_SCRIPTS_DEFAULT "
2026-04-01 23:09:51 +02:00
BASE_DIR = "/usr/local/share/proxmenux"
2026-04-12 20:32:34 +02:00
UTILS_FILE = " $LOCAL_SCRIPTS /utils.sh "
if [ [ -f " $LOCAL_SCRIPTS_LOCAL /utils.sh " ] ] ; then
LOCAL_SCRIPTS = " $LOCAL_SCRIPTS_LOCAL "
UTILS_FILE = " $LOCAL_SCRIPTS /utils.sh "
elif [ [ ! -f " $UTILS_FILE " ] ] ; then
UTILS_FILE = " $BASE_DIR /utils.sh "
fi
2026-04-01 23:09:51 +02:00
if [ [ -f " $UTILS_FILE " ] ] ; then
source " $UTILS_FILE "
fi
2026-04-12 20:32:34 +02:00
if [ [ -f " $LOCAL_SCRIPTS_LOCAL /global/vm_storage_helpers.sh " ] ] ; then
source " $LOCAL_SCRIPTS_LOCAL /global/vm_storage_helpers.sh "
elif [ [ -f " $LOCAL_SCRIPTS_DEFAULT /global/vm_storage_helpers.sh " ] ] ; then
source " $LOCAL_SCRIPTS_DEFAULT /global/vm_storage_helpers.sh "
fi
if [ [ -f " $LOCAL_SCRIPTS_LOCAL /global/disk_ops_helpers.sh " ] ] ; then
source " $LOCAL_SCRIPTS_LOCAL /global/disk_ops_helpers.sh "
elif [ [ -f " $LOCAL_SCRIPTS_DEFAULT /global/disk_ops_helpers.sh " ] ] ; then
source " $LOCAL_SCRIPTS_DEFAULT /global/disk_ops_helpers.sh "
fi
2026-04-01 23:09:51 +02:00
load_language
initialize_cache
if ! command -v pveversion >/dev/null 2>& 1; then
dialog --backtitle "ProxMenux" --title " $( translate "Error" ) " \
--msgbox " $( translate "This script must be run on a Proxmox host." ) " 8 60
exit 1
fi
2026-04-12 20:32:34 +02:00
# ==========================================================
# SYSTEM STORAGE DETECTION
# ==========================================================
# Returns the name of the ZFS pool containing the root filesystem, if any.
_get_system_zfs_pool( ) {
local root_fs
root_fs = $( df / 2>/dev/null | awk 'NR==2 {print $1}' )
if [ [ " $root_fs " != /dev/* && " $root_fs " = = */* ] ] ; then
echo " ${ root_fs %%/* } "
fi
}
# Returns 0 if the given pvesm storage is a user-created disk storage
# that should appear in add/remove menus. Returns 1 for system storages.
_is_user_disk_storage( ) {
local storage_id = " $1 "
local storage_type = " $2 "
local sys_pool
local cfg_path pool
cfg_path = $( get_storage_config " $storage_id " | awk '$1 == "path" {print $2}' )
pool = $( get_storage_config " $storage_id " | awk '$1 == "pool" {print $2}' )
case " $storage_type " in
dir)
# User-created dir storages are always mounted under /mnt/
[ [ " $cfg_path " = = /mnt/* ] ] && return 0
return 1
; ;
zfspool)
# User-created ZFS pool storages are NOT on the root pool or its datasets
sys_pool = $( _get_system_zfs_pool)
if [ [ -n " $sys_pool " ] ] ; then
# Skip if pool is the root pool or a dataset within it (e.g. rpool/data)
[ [ " $pool " = = " $sys_pool " || " $pool " = = " $sys_pool / " * ] ] && return 1
fi
return 0
; ;
*)
return 1
; ;
esac
}
2026-04-01 23:09:51 +02:00
# ==========================================================
# STORAGE CONFIG READER
# ==========================================================
get_storage_config( ) {
local storage_id = " $1 "
awk -v id = " $storage_id " '
/^[ a-z] +: / { found = ( $0 ~ ": " id" $" ) ; next }
found && /^[ ^ \t ] / { exit }
found { print }
' /etc/pve/storage.cfg
}
# ==========================================================
# DISK DETECTION
# ==========================================================
2026-04-12 20:32:34 +02:00
disk_referenced_in_guest_configs( ) {
local disk = " $1 "
if declare -F _disk_used_in_guest_configs >/dev/null 2>& 1; then
_disk_used_in_guest_configs " $disk "
return $?
fi
local real_path config_data link
real_path = $( readlink -f " $disk " 2>/dev/null)
config_data = $( grep -vE '^\s*#' /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null)
[ [ -z " $config_data " ] ] && return 1
if [ [ -n " $real_path " ] ] && grep -Fq " $real_path " <<< " $config_data " ; then
return 0
fi
for link in /dev/disk/by-id/*; do
[ [ -e " $link " ] ] || continue
[ [ " $( readlink -f " $link " 2>/dev/null) " = = " $real_path " ] ] || continue
if grep -Fq " $link " <<< " $config_data " ; then
return 0
fi
done
return 1
}
disk_used_by_host_storage( ) {
local disk = " $1 "
if declare -F _disk_is_host_system_used >/dev/null 2>& 1; then
_disk_is_host_system_used " $disk "
return $?
fi
local mounted_disks swap_disks lvm_devices zfs_disks real_path path base_disk
local part fstype part_path
mounted_disks = $( lsblk -ln -o NAME,MOUNTPOINT | awk '$2!="" {print "/dev/" $1}' )
swap_disks = $( swapon --noheadings --raw --show= NAME 2>/dev/null)
lvm_devices = $( pvs --noheadings -o pv_name 2>/dev/null | xargs -r -n1 readlink -f | sort -u)
zfs_disks = ""
while read -r part fstype; do
[ [ -z " $part " ] ] && continue
part_path = " /dev/ $part "
if grep -qFx " $part_path " <<< " $mounted_disks " ; then
return 0
fi
if grep -qFx " $part_path " <<< " $swap_disks " ; then
return 0
fi
case " $fstype " in
zfs_member| linux_raid_member| LVM2_member)
return 0
; ;
esac
done < <( lsblk -ln -o NAME,FSTYPE " $disk " 2>/dev/null)
while read -r entry; do
[ [ -z " $entry " ] ] && continue
path = ""
if [ [ " $entry " = = wwn-* || " $entry " = = ata-* ] ] ; then
[ [ -e " /dev/disk/by-id/ $entry " ] ] && path = $( readlink -f " /dev/disk/by-id/ $entry " )
elif [ [ " $entry " = = /dev/* ] ] ; then
path = " $entry "
fi
if [ [ -n " $path " ] ] ; then
base_disk = $( lsblk -no PKNAME " $path " 2>/dev/null)
[ [ -n " $base_disk " ] ] && zfs_disks += " /dev/ $base_disk " $'\n'
fi
done < <( zpool list -v -H 2>/dev/null | awk '{print $1}' | grep -v '^NAME$' | grep -v '^-' | grep -v '^mirror' )
real_path = $( readlink -f " $disk " 2>/dev/null)
if [ [ -n " $real_path " && -n " $lvm_devices " ] ] && grep -qFx " $real_path " <<< " $lvm_devices " ; then
return 0
fi
if [ [ -n " $zfs_disks " ] ] && grep -qFx " $disk " <<< " $( echo " $zfs_disks " | sort -u) " ; then
return 0
fi
return 1
}
get_disk_info( ) {
local disk = " $1 "
local model size
model = $( lsblk -dn -o MODEL " $disk " 2>/dev/null | xargs)
size = $( lsblk -dn -o SIZE " $disk " 2>/dev/null | xargs)
[ [ -z " $model " ] ] && model = " $( translate "Unknown model" ) "
[ [ -z " $size " ] ] && size = " $( translate "Unknown size" ) "
printf '%s\t%s\n' " $model " " $size "
}
2026-04-01 23:09:51 +02:00
get_available_disks( ) {
2026-04-12 20:32:34 +02:00
if declare -F _refresh_host_storage_cache >/dev/null 2>& 1; then
_refresh_host_storage_cache
fi
2026-04-01 23:09:51 +02:00
2026-04-12 20:32:34 +02:00
while read -r disk ro type; do
[ [ -z " $disk " ] ] && continue
2026-04-01 23:09:51 +02:00
[ [ " $type " != "disk" ] ] && continue
[ [ " $ro " = = "1" ] ] && continue
2026-04-12 20:32:34 +02:00
[ [ " $disk " = ~ ^/dev/zd ] ] && continue
2026-04-01 23:09:51 +02:00
2026-04-12 20:32:34 +02:00
if disk_used_by_host_storage " $disk " ; then
continue
2026-04-01 23:09:51 +02:00
fi
2026-04-12 20:32:34 +02:00
if disk_referenced_in_guest_configs " $disk " ; then
continue
2026-04-01 23:09:51 +02:00
fi
2026-04-12 20:32:34 +02:00
local model size
IFS = $'\t' read -r model size < <( get_disk_info " $disk " )
[ [ -z " $model " || " $model " = = " " ] ] && model = "-"
echo " $disk | $size — $model "
done < <( lsblk -dn -e 7,11 -o PATH,RO,TYPE 2>/dev/null)
2026-04-01 23:09:51 +02:00
}
select_disk( ) {
local disk_list
disk_list = $( get_available_disks)
if [ [ -z " $disk_list " ] ] ; then
dialog --backtitle "ProxMenux" --title " $( translate "No Disks Found" ) " \
--msgbox " \n $( translate "No available disks found." ) \n\n $( translate "All disks may already be in use or mounted." ) " 10 60
return 1
fi
local options = ( )
while IFS = '|' read -r device info; do
[ [ -n " $device " ] ] && options += ( " $device " " $info " )
done <<< " $disk_list "
if [ [ ${# options [@] } -eq 0 ] ] ; then
dialog --backtitle "ProxMenux" --title " $( translate "No Disks Found" ) " \
--msgbox " \n $( translate "No suitable disks found." ) " 8 60
return 1
fi
SELECTED_DISK = $( dialog --backtitle "ProxMenux" --title " $( translate "Select Disk" ) " \
--menu " \n $( translate "Select the disk to add as Proxmox storage:" ) \n $( translate "WARNING: All data on selected disk will be ERASED if formatted." ) " \
2026-04-12 20:32:34 +02:00
20 84 10 " ${ options [@] } " 3>& 1 1>& 2 2>& 3)
2026-04-01 23:09:51 +02:00
[ [ -z " $SELECTED_DISK " ] ] && return 1
return 0
}
inspect_disk( ) {
local disk = " $1 "
# Check existing partitions/filesystem
local partition_info
partition_info = $( lsblk -no NAME,SIZE,FSTYPE,MOUNTPOINT " $disk " 2>/dev/null | tail -n +2)
2026-04-12 20:32:34 +02:00
local existing_fs existing_node
2026-04-01 23:09:51 +02:00
existing_fs = $( blkid -s TYPE -o value " $disk " 2>/dev/null || true )
2026-04-12 20:32:34 +02:00
existing_node = " $disk "
if [ [ -z " $existing_fs " ] ] ; then
while read -r node fstype mountpoint; do
[ [ -z " $node " || -z " $fstype " ] ] && continue
[ [ -n " $mountpoint " ] ] && continue
existing_fs = " $fstype "
existing_node = " $node "
break
done < <( lsblk -lnpo NAME,FSTYPE,MOUNTPOINT " $disk " 2>/dev/null | awk 'NR>1 {print $1, $2, $3}' )
fi
2026-04-01 23:09:51 +02:00
DISK_HAS_DATA = false
DISK_EXISTING_FS = ""
2026-04-12 20:32:34 +02:00
DISK_EXISTING_NODE = ""
2026-04-01 23:09:51 +02:00
if [ [ -n " $partition_info " || -n " $existing_fs " ] ] ; then
DISK_HAS_DATA = true
DISK_EXISTING_FS = " $existing_fs "
2026-04-12 20:32:34 +02:00
DISK_EXISTING_NODE = " $existing_node "
2026-04-01 23:09:51 +02:00
fi
return 0
}
select_partition_action( ) {
local disk = " $1 "
inspect_disk " $disk "
local disk_size
disk_size = $( lsblk -ndo SIZE " $disk " 2>/dev/null)
2026-04-12 20:32:34 +02:00
local menu_items = ( )
menu_items += ( "format" " $( translate "Format disk (ERASE all data)" ) " )
[ [ -n " $DISK_EXISTING_FS " ] ] && menu_items += ( "use_existing" " $( translate "Use existing filesystem" ) " )
menu_items += ( "cancel" " $( translate "Cancel" ) " )
local menu_text
2026-04-01 23:09:51 +02:00
if [ [ " $DISK_HAS_DATA " = = "true" ] ] ; then
2026-04-12 20:32:34 +02:00
menu_text = " $( translate "Disk:" ) : $disk ( $disk_size )\n "
[ [ -n " $DISK_EXISTING_FS " ] ] && menu_text += " $( translate "Existing filesystem:" ) : $DISK_EXISTING_FS \n "
menu_text += " \n $( translate "Options:" ) \n "
menu_text += " • $( translate "Format: ERASE all data and create new filesystem" ) \n "
[ [ -n " $DISK_EXISTING_FS " ] ] && menu_text += " • $( translate "Use existing: mount without formatting" ) \n "
menu_text += " \n $( translate "Continue?" ) "
2026-04-01 23:09:51 +02:00
else
2026-04-12 20:32:34 +02:00
menu_text = " $( translate "Disk:" ) : $disk ( $disk_size )\n\n $( translate "Disk appears empty. It will be formatted." ) "
2026-04-01 23:09:51 +02:00
fi
2026-04-12 20:32:34 +02:00
DISK_ACTION = $( dialog --backtitle "ProxMenux" --title " $( translate "Disk Setup" ) " \
--menu " $menu_text " 20 84 8 \
" ${ menu_items [@] } " 3>& 1 1>& 2 2>& 3)
2026-04-01 23:09:51 +02:00
[ [ -z " $DISK_ACTION " || " $DISK_ACTION " = = "cancel" ] ] && return 1
return 0
}
select_filesystem( ) {
2026-04-12 20:32:34 +02:00
FILESYSTEM = $( dialog --backtitle "ProxMenux" --title " $( translate "Select Filesystem" ) " \
--menu " \n $( translate "Choose filesystem for the disk:" ) " 16 72 5 \
"ext4" " $( translate "ext4 — Proxmox dir storage (recommended)" ) " \
"xfs" " $( translate "xfs — Proxmox dir storage (large files and VMs)" ) " \
"btrfs" " $( translate "btrfs — Proxmox dir storage (snapshots, compression)" ) " \
"zfs" " $( translate "zfs — Proxmox ZFS pool storage" ) " \
2026-04-01 23:09:51 +02:00
3>& 1 1>& 2 2>& 3)
[ [ -z " $FILESYSTEM " ] ] && return 1
return 0
}
# ==========================================================
# STORAGE CONFIGURATION
# ==========================================================
configure_disk_storage( ) {
local disk_name
disk_name = $( basename " $SELECTED_DISK " )
2026-04-12 20:32:34 +02:00
STORAGE_ID = $( dialog --backtitle "ProxMenux" --title " $( translate "Storage ID" ) " \
--inputbox " $( translate "Enter storage ID for Proxmox:" ) " \
10 60 " disk- ${ disk_name } " 3>& 1 1>& 2 2>& 3)
2026-04-01 23:09:51 +02:00
[ [ $? -ne 0 ] ] && return 1
[ [ -z " $STORAGE_ID " ] ] && STORAGE_ID = " disk- ${ disk_name } "
if [ [ ! " $STORAGE_ID " = ~ ^[ a-zA-Z0-9] [ a-zA-Z0-9_-] *$ ] ] ; then
2026-04-12 20:32:34 +02:00
dialog --backtitle "ProxMenux" --title " $( translate "Invalid ID" ) " \
--msgbox " $( translate "Invalid storage ID. Use only letters, numbers, hyphens and underscores." ) " 8 74
return 1
fi
if [ [ " ${ FILESYSTEM :- } " = = "zfs" && ! " $STORAGE_ID " = ~ ^[ a-zA-Z] [ a-zA-Z0-9_.:-] *$ ] ] ; then
dialog --backtitle "ProxMenux" --title " $( translate "Invalid ID" ) " \
--msgbox " $( translate "For ZFS, storage ID must start with a letter and use only letters, numbers, dot, dash, underscore or colon." ) " 9 86
2026-04-01 23:09:51 +02:00
return 1
fi
MOUNT_PATH = " /mnt/ ${ STORAGE_ID } "
2026-04-12 20:32:34 +02:00
MOUNT_PATH = $( dialog --backtitle "ProxMenux" --title " $( translate "Mount Path" ) " \
--inputbox " $( translate "Enter mount path on host:" ) " \
10 60 " $MOUNT_PATH " 3>& 1 1>& 2 2>& 3)
2026-04-01 23:09:51 +02:00
[ [ $? -ne 0 || -z " $MOUNT_PATH " ] ] && return 1
2026-04-12 20:32:34 +02:00
CONTENT_TYPE = $( dialog --backtitle "ProxMenux" --title " $( translate "Content Types" ) " \
2026-04-01 23:09:51 +02:00
--menu " $( translate "Select content types for this storage:" ) " 16 70 5 \
"1" " $( translate "VM Storage (images, backup)" ) " \
"2" " $( translate "Standard NAS (backup, iso, vztmpl)" ) " \
"3" " $( translate "All types (images, backup, iso, vztmpl, snippets)" ) " \
"4" " $( translate "Custom" ) " \
3>& 1 1>& 2 2>& 3)
[ [ $? -ne 0 ] ] && return 1
case " $CONTENT_TYPE " in
1) MOUNT_CONTENT = "images,backup" ; ;
2) MOUNT_CONTENT = "backup,iso,vztmpl" ; ;
3) MOUNT_CONTENT = "images,backup,iso,vztmpl,snippets" ; ;
4)
2026-04-12 20:32:34 +02:00
MOUNT_CONTENT = $( dialog --backtitle "ProxMenux" --title " $( translate "Custom Content" ) " \
--inputbox " $( translate "Enter content types (comma-separated):" ) " \
10 70 "images,backup" 3>& 1 1>& 2 2>& 3)
2026-04-01 23:09:51 +02:00
[ [ $? -ne 0 || -z " $MOUNT_CONTENT " ] ] && MOUNT_CONTENT = "images,backup"
; ;
*) return 1 ; ;
esac
return 0
}
# ==========================================================
# DISK SETUP AND MOUNT
# ==========================================================
format_and_mount_disk( ) {
local disk = " $1 "
local mount_path = " $2 "
local filesystem = " $3 "
# Final confirmation before any destructive operation
local disk_size
disk_size = $( lsblk -ndo SIZE " $disk " 2>/dev/null)
2026-04-12 20:32:34 +02:00
if ! dialog --backtitle "ProxMenux" --title " $( translate "CONFIRM FORMAT" ) " --yesno \
2026-04-01 23:09:51 +02:00
" $( translate "FINAL CONFIRMATION — DATA WILL BE ERASED" ) \n\n $( translate "Disk:" ) : $disk ( $disk_size )\n $( translate "Filesystem:" ) : $filesystem \n $( translate "Mount path:" ) : $mount_path \n\n $( translate "ALL DATA ON" ) $disk $( translate "WILL BE PERMANENTLY ERASED." ) \n\n $( translate "Are you absolutely sure?" ) " \
2026-04-12 20:32:34 +02:00
14 80; then
2026-04-01 23:09:51 +02:00
return 1
fi
2026-04-12 20:32:34 +02:00
show_proxmenux_logo
msg_title " $( translate "Add Local Disk as Proxmox Storage" ) "
local _disk_model _disk_size _disk_label
IFS = $'\t' read -r _disk_model _disk_size < <( get_disk_info " $disk " )
_disk_label = " $disk "
[ [ -n " $_disk_size " && -n " $_disk_model " ] ] && _disk_label = " $disk — $_disk_size — $_disk_model "
msg_ok " $( translate "Disk:" ) ${ BL } ${ _disk_label } ${ CL } "
msg_ok " $( translate "Action:" ) $DISK_ACTION "
[ [ " $DISK_ACTION " = = "format" ] ] && msg_ok " $( translate "Filesystem:" ) $FILESYSTEM "
msg_ok " $( translate "Mount path:" ) $MOUNT_PATH "
msg_ok " $( translate "Storage ID:" ) $STORAGE_ID "
msg_ok " $( translate "Content:" ) $MOUNT_CONTENT "
2026-04-01 23:09:51 +02:00
msg_info " $( translate "Wiping existing partition table..." ) "
2026-04-12 20:32:34 +02:00
doh_wipe_disk " $disk "
msg_ok " $( translate "Partition table wiped" ) "
2026-04-01 23:09:51 +02:00
msg_info " $( translate "Creating partition..." ) "
2026-04-12 20:32:34 +02:00
if ! doh_create_partition " $disk " ; then
2026-04-01 23:09:51 +02:00
msg_error " $( translate "Failed to create partition table" ) "
2026-04-12 20:32:34 +02:00
[ [ -n " $DOH_PARTITION_ERROR_DETAIL " ] ] && \
msg_error " $( translate "Details" ) : $( printf '%s' " $DOH_PARTITION_ERROR_DETAIL " | head -n1) "
2026-04-01 23:09:51 +02:00
return 1
fi
2026-04-12 20:32:34 +02:00
msg_ok " $( translate "Partition created" ) "
local partition = " $DOH_CREATED_PARTITION "
# ZFS pre-flight checks (pool existence must be verified before format)
if [ [ " $filesystem " = = "zfs" ] ] ; then
if ! command -v zpool >/dev/null 2>& 1; then
msg_error " $( translate "zpool command not found. Install zfsutils-linux and retry." ) "
return 1
fi
if zpool list " $STORAGE_ID " >/dev/null 2>& 1; then
msg_error " $( translate "A ZFS pool with this name already exists:" ) $STORAGE_ID "
return 1
fi
2026-04-01 23:09:51 +02:00
fi
msg_info " $( translate "Formatting as" ) $filesystem ... "
2026-04-12 20:32:34 +02:00
if ! doh_format_partition " $partition " " $filesystem " " $STORAGE_ID " " $STORAGE_ID " " $mount_path " ; then
msg_error " $( translate "Failed to format disk as" ) $filesystem "
return 1
fi
2026-04-01 23:09:51 +02:00
msg_ok " $( translate "Disk formatted as" ) $filesystem "
DISK_PARTITION = " $partition "
return 0
}
mount_disk_permanently( ) {
local partition = " $1 "
local mount_path = " $2 "
local filesystem = " $3 "
2026-04-12 20:32:34 +02:00
if [ [ " $filesystem " = = "zfs" ] ] ; then
if ! zpool list " $STORAGE_ID " >/dev/null 2>& 1; then
msg_error " $( translate "ZFS pool is not available after creation:" ) $STORAGE_ID "
return 1
fi
msg_ok " $( translate "ZFS pool created and mounted at" ) $mount_path "
return 0
fi
2026-04-01 23:09:51 +02:00
msg_info " $( translate "Creating mount point..." ) "
if ! mkdir -p " $mount_path " ; then
msg_error " $( translate "Failed to create mount point:" ) $mount_path "
return 1
fi
2026-04-12 20:32:34 +02:00
msg_ok " $( translate "Mount point created" ) "
2026-04-01 23:09:51 +02:00
msg_info " $( translate "Mounting disk..." ) "
if ! mount -t " $filesystem " " $partition " " $mount_path " 2>/dev/null; then
msg_error " $( translate "Failed to mount disk" ) "
return 1
fi
msg_ok " $( translate "Disk mounted at" ) $mount_path "
msg_info " $( translate "Adding to /etc/fstab for permanent mounting..." ) "
local disk_uuid
disk_uuid = $( blkid -s UUID -o value " $partition " 2>/dev/null)
if [ [ -n " $disk_uuid " ] ] ; then
# Remove any existing fstab entry for this UUID or mount point
sed -i " \|UUID= $disk_uuid |d " /etc/fstab
2026-04-12 20:32:34 +02:00
sed -i " \|[[:space:]] ${ mount_path } [[:space:]]|d " /etc/fstab
2026-04-01 23:09:51 +02:00
echo " UUID= $disk_uuid $mount_path $filesystem defaults,nofail 0 2 " >> /etc/fstab
msg_ok " $( translate "Added to /etc/fstab using UUID" ) "
else
2026-04-12 20:32:34 +02:00
sed -i " \|[[:space:]] ${ mount_path } [[:space:]]|d " /etc/fstab
2026-04-01 23:09:51 +02:00
echo " $partition $mount_path $filesystem defaults,nofail 0 2 " >> /etc/fstab
msg_ok " $( translate "Added to /etc/fstab using device path" ) "
fi
systemctl daemon-reload 2>/dev/null || true
return 0
}
mount_existing_disk( ) {
local disk = " $1 "
local mount_path = " $2 "
local existing_fs
existing_fs = $( blkid -s TYPE -o value " $disk " 2>/dev/null || true )
if [ [ -z " $existing_fs " ] ] ; then
msg_error " $( translate "Cannot detect filesystem on" ) $disk "
return 1
fi
msg_info " $( translate "Creating mount point..." ) "
mkdir -p " $mount_path "
2026-04-12 20:32:34 +02:00
msg_ok " $( translate "Mount point created" ) "
2026-04-01 23:09:51 +02:00
msg_info " $( translate "Mounting existing" ) $existing_fs $( translate "filesystem..." ) "
if ! mount " $disk " " $mount_path " 2>/dev/null; then
msg_error " $( translate "Failed to mount disk" ) "
return 1
fi
msg_ok " $( translate "Disk mounted at" ) $mount_path "
# Add to fstab
local disk_uuid
disk_uuid = $( blkid -s UUID -o value " $disk " 2>/dev/null)
if [ [ -n " $disk_uuid " ] ] ; then
sed -i " \|UUID= $disk_uuid |d " /etc/fstab
2026-04-12 20:32:34 +02:00
sed -i " \|[[:space:]] ${ mount_path } [[:space:]]|d " /etc/fstab
2026-04-01 23:09:51 +02:00
echo " UUID= $disk_uuid $mount_path $existing_fs defaults,nofail 0 2 " >> /etc/fstab
msg_ok " $( translate "Added to /etc/fstab" ) "
fi
DISK_PARTITION = " $disk "
systemctl daemon-reload 2>/dev/null || true
return 0
}
add_proxmox_dir_storage( ) {
local storage_id = " $1 "
local path = " $2 "
local content = " $3 "
2026-04-12 20:32:34 +02:00
local storage_kind = "dir"
local pool_name = " $storage_id "
if [ [ " ${ FILESYSTEM :- } " = = "zfs" ] ] ; then
storage_kind = "zfspool"
fi
2026-04-01 23:09:51 +02:00
if ! command -v pvesm >/dev/null 2>& 1; then
msg_error " $( translate "pvesm command not found. This should not happen on Proxmox." ) "
return 1
fi
if pvesm status " $storage_id " >/dev/null 2>& 1; then
msg_warn " $( translate "Storage ID already exists:" ) $storage_id "
2026-04-12 20:32:34 +02:00
if ! dialog --backtitle "ProxMenux" --title " $( translate "Storage Exists" ) " --yesno \
" $( translate "Storage ID already exists. Do you want to remove and recreate it?" ) " \
8 60; then
2026-04-01 23:09:51 +02:00
return 0
fi
pvesm remove " $storage_id " 2>/dev/null || true
fi
msg_info " $( translate "Registering disk as Proxmox storage..." ) "
local pvesm_output
2026-04-12 20:32:34 +02:00
local add_ok = false
if [ [ " $storage_kind " = = "zfspool" ] ] ; then
if pvesm_output = $( pvesm add zfspool " $storage_id " \
--pool " $pool_name " \
--content " $content " 2>& 1) ; then
add_ok = true
fi
else
if pvesm_output = $( pvesm add dir " $storage_id " \
--path " $path " \
--content " $content " 2>& 1) ; then
add_ok = true
fi
fi
2026-04-01 23:09:51 +02:00
2026-04-12 20:32:34 +02:00
if [ [ " $add_ok " = = "true" ] ] ; then
if [ [ " $storage_kind " = = "zfspool" ] ] ; then
msg_ok " $( translate "ZFS storage added successfully to Proxmox!" ) "
echo -e ""
echo -e " ${ TAB } ${ BOLD } $( translate "Storage Added:" ) ${ CL } "
echo -e " ${ TAB } ${ BGN } $( translate "Storage ID:" ) ${ CL } ${ BL } $storage_id ${ CL } "
echo -e " ${ TAB } ${ BGN } $( translate "Type:" ) ${ CL } ${ BL } zfspool ${ CL } "
echo -e " ${ TAB } ${ BGN } $( translate "Pool:" ) ${ CL } ${ BL } $pool_name ${ CL } "
echo -e " ${ TAB } ${ BGN } $( translate "Content Types:" ) ${ CL } ${ BL } $content ${ CL } "
else
msg_ok " $( translate "Directory storage added successfully to Proxmox!" ) "
echo -e ""
echo -e " ${ TAB } ${ BOLD } $( translate "Storage Added:" ) ${ CL } "
echo -e " ${ TAB } ${ BGN } $( translate "Storage ID:" ) ${ CL } ${ BL } $storage_id ${ CL } "
echo -e " ${ TAB } ${ BGN } $( translate "Path:" ) ${ CL } ${ BL } $path ${ CL } "
echo -e " ${ TAB } ${ BGN } $( translate "Content Types:" ) ${ CL } ${ BL } $content ${ CL } "
fi
2026-04-01 23:09:51 +02:00
echo -e ""
msg_ok " $( translate "Storage is now available in Proxmox web interface under Datacenter > Storage" ) "
return 0
else
msg_error " $( translate "Failed to add storage to Proxmox." ) "
echo -e " ${ TAB } $( translate "Error details:" ) : $pvesm_output "
echo -e ""
msg_info2 " $( translate "You can add it manually through:" ) "
2026-04-12 20:32:34 +02:00
if [ [ " $storage_kind " = = "zfspool" ] ] ; then
echo -e " ${ TAB } • $( translate "Proxmox web interface: Datacenter > Storage > Add > ZFS" ) "
echo -e " ${ TAB } • pvesm add zfspool $storage_id --pool $pool_name --content $content "
else
echo -e " ${ TAB } • $( translate "Proxmox web interface: Datacenter > Storage > Add > Directory" ) "
echo -e " ${ TAB } • pvesm add dir $storage_id --path $path --content $content "
fi
2026-04-01 23:09:51 +02:00
return 1
fi
}
# ==========================================================
# MAIN OPERATIONS
# ==========================================================
add_disk_to_proxmox( ) {
# Check required tools
2026-04-12 20:32:34 +02:00
for tool in parted mkfs.ext4 mkfs.xfs blkid lsblk sgdisk; do
2026-04-01 23:09:51 +02:00
if ! command -v " $tool " >/dev/null 2>& 1; then
2026-04-12 20:32:34 +02:00
show_proxmenux_logo
msg_title " $( translate "Add Local Disk as Proxmox Storage" ) "
2026-04-01 23:09:51 +02:00
msg_info " $( translate "Installing required tools..." ) "
apt-get update & >/dev/null
2026-04-12 20:32:34 +02:00
apt-get install -y parted e2fsprogs util-linux xfsprogs gdisk btrfs-progs & >/dev/null
stop_spinner
2026-04-01 23:09:51 +02:00
break
fi
done
# Step 1: Select disk
select_disk || return
# Step 2: Inspect and choose action
select_partition_action " $SELECTED_DISK " || return
# Step 3: Filesystem selection (only if formatting)
if [ [ " $DISK_ACTION " = = "format" ] ] ; then
select_filesystem || return
2026-04-12 20:32:34 +02:00
if [ [ " $FILESYSTEM " = = "zfs" ] ] && ! command -v zpool >/dev/null 2>& 1; then
msg_error " $( translate "zpool not found. Install zfsutils-linux and retry." ) "
echo
msg_success " $( translate "Press Enter to continue..." ) "
read -r
return 1
fi
2026-04-01 23:09:51 +02:00
fi
# Step 4: Configure storage options
configure_disk_storage || return
2026-04-12 20:32:34 +02:00
if declare -F _refresh_host_storage_cache >/dev/null 2>& 1; then
_refresh_host_storage_cache
fi
if disk_used_by_host_storage " $SELECTED_DISK " ; then
msg_error " $( translate "Safety check failed: selected disk is now used by host/system." ) "
echo
msg_success " $( translate "Press Enter to continue..." ) "
read -r
return 1
fi
if disk_referenced_in_guest_configs " $SELECTED_DISK " ; then
msg_error " $( translate "Safety check failed: selected disk is referenced by a VM/LXC config." ) "
echo
msg_success " $( translate "Press Enter to continue..." ) "
read -r
return 1
fi
2026-04-01 23:09:51 +02:00
# Step 5: Format/mount
case " $DISK_ACTION " in
format)
format_and_mount_disk " $SELECTED_DISK " " $MOUNT_PATH " " $FILESYSTEM " || {
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
return 1
}
mount_disk_permanently " $DISK_PARTITION " " $MOUNT_PATH " " $FILESYSTEM " || {
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
return 1
}
; ;
use_existing)
2026-04-12 20:32:34 +02:00
local existing_node
existing_node = " ${ DISK_EXISTING_NODE :- $SELECTED_DISK } "
mount_existing_disk " $existing_node " " $MOUNT_PATH " || {
2026-04-01 23:09:51 +02:00
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
return 1
}
; ;
esac
# Step 6: Register in Proxmox
add_proxmox_dir_storage " $STORAGE_ID " " $MOUNT_PATH " " $MOUNT_CONTENT "
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
}
view_disk_storages( ) {
show_proxmenux_logo
msg_title " $( translate "Local Disk Storages in Proxmox" ) "
echo "=================================================="
echo ""
if ! command -v pvesm >/dev/null 2>& 1; then
msg_error " $( translate "pvesm not found." ) "
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
return
fi
2026-04-12 20:32:34 +02:00
# Show local storages managed by this menu (Directory + ZFS Pool), excluding system ones
DIR_STORAGES = $( pvesm status 2>/dev/null | awk '$2 == "dir" || $2 == "zfspool" {print $1, $2, $3}' )
local user_storage_found = false
if [ [ -n " $DIR_STORAGES " ] ] ; then
while IFS = " " read -r s_id s_type _; do
[ [ -z " $s_id " ] ] && continue
_is_user_disk_storage " $s_id " " $s_type " || continue
user_storage_found = true
break
done <<< " $DIR_STORAGES "
fi
if [ [ " $user_storage_found " = = "false" ] ] ; then
msg_warn " $( translate "No local storage configured in Proxmox." ) "
2026-04-01 23:09:51 +02:00
echo ""
msg_info2 " $( translate "Use option 1 to add a local disk as Proxmox storage." ) "
else
2026-04-12 20:32:34 +02:00
echo -e " ${ BOLD } $( translate "Local Storages:" ) ${ CL } "
2026-04-01 23:09:51 +02:00
echo ""
2026-04-12 20:32:34 +02:00
while IFS = " " read -r storage_id storage_type storage_status; do
2026-04-01 23:09:51 +02:00
[ [ -z " $storage_id " ] ] && continue
2026-04-12 20:32:34 +02:00
_is_user_disk_storage " $storage_id " " $storage_type " || continue
local storage_info path content pool
2026-04-01 23:09:51 +02:00
storage_info = $( get_storage_config " $storage_id " )
path = $( echo " $storage_info " | awk '$1 == "path" {print $2}' )
2026-04-12 20:32:34 +02:00
pool = $( echo " $storage_info " | awk '$1 == "pool" {print $2}' )
2026-04-01 23:09:51 +02:00
content = $( echo " $storage_info " | awk '$1 == "content" {print $2}' )
local disk_device = ""
if [ [ -n " $path " ] ] ; then
disk_device = $( findmnt -n -o SOURCE " $path " 2>/dev/null || true )
fi
local disk_size = ""
if [ [ -n " $disk_device " ] ] ; then
disk_size = $( lsblk -ndo SIZE " $disk_device " 2>/dev/null || true )
fi
echo -e " ${ TAB } ${ BOLD } $storage_id ${ CL } "
2026-04-12 20:32:34 +02:00
echo -e " ${ TAB } ${ BGN } $( translate "Type:" ) ${ CL } ${ BL } ${ storage_type :- unknown } ${ CL } "
2026-04-01 23:09:51 +02:00
echo -e " ${ TAB } ${ BGN } $( translate "Path:" ) ${ CL } ${ BL } $path ${ CL } "
2026-04-12 20:32:34 +02:00
[ [ -n " $pool " ] ] && echo -e " ${ TAB } ${ BGN } $( translate "Pool:" ) ${ CL } ${ BL } $pool ${ CL } "
2026-04-01 23:09:51 +02:00
[ [ -n " $disk_device " ] ] && echo -e " ${ TAB } ${ BGN } $( translate "Device:" ) ${ CL } ${ BL } $disk_device ${ CL } "
[ [ -n " $disk_size " ] ] && echo -e " ${ TAB } ${ BGN } $( translate "Size:" ) ${ CL } ${ BL } $disk_size ${ CL } "
echo -e " ${ TAB } ${ BGN } $( translate "Content:" ) ${ CL } ${ BL } $content ${ CL } "
if [ [ " $storage_status " = = "active" ] ] ; then
echo -e " ${ TAB } ${ BGN } $( translate "Status:" ) ${ CL } ${ GN } $( translate "Active" ) ${ CL } "
else
echo -e " ${ TAB } ${ BGN } $( translate "Status:" ) ${ CL } ${ RD } $storage_status ${ CL } "
fi
echo ""
done <<< " $DIR_STORAGES "
fi
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
}
2026-04-12 20:32:34 +02:00
_remove_pvesm_storage( ) {
local storage_id = " $1 "
local path pool content stype
path = $( get_storage_config " $storage_id " | awk '$1 == "path" {print $2}' )
pool = $( get_storage_config " $storage_id " | awk '$1 == "pool" {print $2}' )
content = $( get_storage_config " $storage_id " | awk '$1 == "content" {print $2}' )
stype = $( pvesm status 2>/dev/null | awk -v id = " $storage_id " '$1==id {print $2}' )
local msg
msg = " $( translate "WARNING: You are about to remove this Proxmox storage:" ) \n\n "
msg += " $( translate "Storage ID:" ) $storage_id \n "
msg += " $( translate "Type:" ) ${ stype :- unknown } \n "
[ [ -n " $path " ] ] && msg += " $( translate "Mount path:" ) $path \n "
[ [ -n " $pool " ] ] && msg += " $( translate "ZFS pool:" ) $pool \n "
[ [ -n " $content " ] ] && msg += " $( translate "Content:" ) $content \n "
msg += " \n $( translate "⚠ Disk data will NOT be erased." ) \n "
[ [ -n " $path " ] ] && msg += " $( translate "⚠ Disk will be unmounted and removed from /etc/fstab." ) \n "
[ [ -n " $pool " ] ] && msg += " $( translate " ⚠ ZFS pool stays active — run 'zpool export $pool ' to detach. " ) \n "
msg += " \n $( translate "Continue?" ) "
if ! dialog --backtitle "ProxMenux" --title " $( translate "Confirm Remove" ) " --yesno " $msg " 22 84; then
2026-04-01 23:09:51 +02:00
return
fi
2026-04-12 20:32:34 +02:00
show_proxmenux_logo
msg_title " $( translate "Remove Disk Storage" ) "
# Step 1: Remove from Proxmox
msg_info " $( translate "Removing storage from Proxmox..." ) "
if ! pvesm remove " $storage_id " 2>/dev/null; then
msg_error " $( translate "Failed to remove storage from Proxmox." ) "
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
2026-04-01 23:09:51 +02:00
return
fi
2026-04-12 20:32:34 +02:00
msg_ok " $( translate "Storage" ) $storage_id $( translate "removed from Proxmox" ) "
2026-04-01 23:09:51 +02:00
2026-04-12 20:32:34 +02:00
# Step 2: Unmount if mounted (dir-backed storages only)
if [ [ -n " $path " ] ] && mountpoint -q " $path " 2>/dev/null; then
msg_info " $( translate "Unmounting disk..." ) "
if umount " $path " 2>/dev/null; then
msg_ok " $( translate "Disk unmounted from" ) $path "
else
msg_warn " $( translate "Could not unmount" ) $path $( translate "— disk may be busy. Skipping fstab removal." ) "
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
return
fi
fi
2026-04-01 23:09:51 +02:00
2026-04-12 20:32:34 +02:00
# Step 3: Remove /etc/fstab entry
if [ [ -n " $path " ] ] && grep -q " [[:space:]] ${ path } [[:space:]] " /etc/fstab 2>/dev/null; then
msg_info " $( translate "Removing from /etc/fstab..." ) "
local tmp
tmp = $( mktemp)
awk -v mp = " $path " '$2 != mp' /etc/fstab > " $tmp " && mv " $tmp " /etc/fstab
systemctl daemon-reload 2>/dev/null || true
msg_ok " $( translate "Removed from /etc/fstab" ) "
fi
2026-04-01 23:09:51 +02:00
2026-04-12 20:32:34 +02:00
# Step 3b: Export ZFS pool if applicable
if [ [ -n " $pool " ] ] && zpool list " $pool " >/dev/null 2>& 1; then
msg_info " $( translate "Exporting ZFS pool..." ) $pool "
if zpool export " $pool " 2>/dev/null; then
msg_ok " $( translate "ZFS pool exported:" ) $pool "
else
msg_warn " $( translate "Could not export ZFS pool" ) $pool $( translate " — pool may be busy. Run manually: zpool export $pool " ) "
fi
fi
2026-04-01 23:09:51 +02:00
2026-04-12 20:32:34 +02:00
# Step 4: Reboot prompt
echo ""
if whiptail --title " $( translate "Reboot Required" ) " --yesno \
" \n $( translate "The storage has been removed and the disk unmounted." ) \n\n $( translate "A server reboot is recommended for all changes to take full effect." ) \n\n $( translate "Reboot now?" ) " \
14 72; then
msg_success " $( translate "Press Enter to continue..." ) "
read -r
echo ""
msg_warn " $( translate "Rebooting the system..." ) "
reboot
else
echo ""
msg_info2 " $( translate "Reboot pending — changes will take full effect after the next restart." ) "
fi
2026-04-01 23:09:51 +02:00
2026-04-12 20:32:34 +02:00
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
}
_remove_fstab_entry( ) {
local mount_point = " $1 "
local fs fstype
while IFS = read -r line; do
[ [ " $line " = ~ ^# || -z " $line " ] ] && continue
local f mp ft
read -r f mp ft _ <<< " $line "
if [ [ " $mp " = = " $mount_point " ] ] ; then
fs = " $f " ; fstype = " $ft " ; break
fi
done < /etc/fstab
local device = " $fs "
if [ [ " $fs " = = UUID = * ] ] ; then
device = $( blkid -U " ${ fs #UUID= } " 2>/dev/null || echo " $fs " )
fi
local size = ""
[ [ -b " $device " ] ] && size = $( lsblk -ndo SIZE " $device " 2>/dev/null)
local mounted = false
findmnt -n " $mount_point " >/dev/null 2>& 1 && mounted = true
local msg
msg = " $( translate "WARNING: You are about to remove this disk mount:" ) \n\n "
msg += " $( translate "Mount point:" ) $mount_point \n "
[ [ -n " $device " && " $device " != " $fs " ] ] && msg += " $( translate "Device:" ) $device \n "
msg += " $( translate "Filesystem:" ) $fstype \n "
[ [ -n " $size " ] ] && msg += " $( translate "Size:" ) $size \n "
local mounted_label; $mounted && mounted_label = " $( translate "Yes" ) " || mounted_label = " $( translate "No" ) "
msg += " $( translate "Currently mounted:" ) $mounted_label \n "
msg += " \n $( translate "⚠ The disk will be unmounted." ) \n "
msg += " $( translate "⚠ The /etc/fstab entry will be removed." ) \n "
msg += " $( translate "⚠ Disk data will NOT be erased." ) \n "
msg += " \n $( translate "Continue?" ) "
if dialog --backtitle "ProxMenux" --title " $( translate "Confirm Remove" ) " --yesno " $msg " 20 80; then
2026-04-01 23:09:51 +02:00
show_proxmenux_logo
2026-04-12 20:32:34 +02:00
msg_title " $( translate "Remove Disk from fstab" ) "
2026-04-01 23:09:51 +02:00
2026-04-12 20:32:34 +02:00
if $mounted ; then
msg_info " $( translate "Unmounting" ) $mount_point ... "
if umount " $mount_point " 2>/dev/null; then
msg_ok " $( translate "Unmounted successfully" ) "
else
msg_warn " $( translate "Could not unmount — disk may be busy. Removing fstab entry anyway." ) "
fi
2026-04-01 23:09:51 +02:00
fi
2026-04-12 20:32:34 +02:00
msg_info " $( translate "Removing from /etc/fstab..." ) "
local tmp
tmp = $( mktemp)
awk -v mp = " $mount_point " '$2 != mp' /etc/fstab > " $tmp "
mv " $tmp " /etc/fstab
systemctl daemon-reload 2>/dev/null || true
msg_ok " $( translate "Removed from /etc/fstab" ) "
2026-04-01 23:09:51 +02:00
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
fi
}
2026-04-12 20:32:34 +02:00
remove_disk_storage( ) {
if ! command -v pvesm >/dev/null 2>& 1; then
dialog --backtitle "ProxMenux" --title " $( translate "Error" ) " \
--msgbox " \n $( translate "pvesm not found." ) " 8 60
return
fi
local OPTIONS = ( )
local pvesm_paths = ( )
# --- Source 1: pvesm user-created storages ---
local ALL_STORAGES
ALL_STORAGES = $( pvesm status 2>/dev/null | awk '$2 == "dir" || $2 == "zfspool" {print $1, $2}' )
while IFS = read -r line; do
local storage_id storage_type
storage_id = $( echo " $line " | awk '{print $1}' )
storage_type = $( echo " $line " | awk '{print $2}' )
[ [ -z " $storage_id " ] ] && continue
_is_user_disk_storage " $storage_id " " $storage_type " || continue
local path pool
path = $( get_storage_config " $storage_id " | awk '$1 == "path" {print $2}' )
pool = $( get_storage_config " $storage_id " | awk '$1 == "pool" {print $2}' )
[ [ -n " $path " ] ] && pvesm_paths += ( " $path " )
local label = " [pvesm] ${ storage_type } "
[ [ -n " $path " ] ] && label += " — $path "
[ [ -z " $path " && -n " $pool " ] ] && label += " — pool: $pool "
OPTIONS += ( " pvesm: $storage_id " " $label " )
done <<< " $ALL_STORAGES "
# --- Source 2: fstab /mnt/ entries not already covered by pvesm ---
while IFS = read -r line; do
[ [ " $line " = ~ ^# || -z " $line " ] ] && continue
local fs mp fstype
read -r fs mp fstype _ <<< " $line "
[ [ " $mp " = = /mnt/* ] ] || continue
[ [ " $fstype " = = "proc" || " $fstype " = = "sysfs" || " $fstype " = = "tmpfs" || " $fstype " = = "devtmpfs" || " $fstype " = = "none" ] ] && continue
local covered = false
for p in " ${ pvesm_paths [@] } " ; do [ [ " $p " = = " $mp " ] ] && covered = true && break; done
$covered && continue
local device = " $fs "
[ [ " $fs " = = UUID = * ] ] && device = $( blkid -U " ${ fs #UUID= } " 2>/dev/null || echo " $fs " )
local size = ""
[ [ -b " $device " ] ] && size = $( lsblk -ndo SIZE " $device " 2>/dev/null)
local label = " [fstab] $mp ( $fstype ) "
[ [ -n " $size " ] ] && label += " [ $size ] "
findmnt -n " $mp " >/dev/null 2>& 1 && label += " ✓" || label += " (not mounted)"
OPTIONS += ( " fstab: $mp " " $label " )
done < /etc/fstab
if [ [ ${# OPTIONS [@] } -eq 0 ] ] ; then
dialog --backtitle "ProxMenux" --title " $( translate "No Disk Storage" ) " \
--msgbox " \n $( translate "No user-created disk storage or fstab mount found." ) " 8 68
return
fi
local SELECTED
SELECTED = $( dialog --backtitle "ProxMenux" --title " $( translate "Remove Disk Storage" ) " \
--menu " $( translate "Select storage to remove:" ) " 20 88 12 \
" ${ OPTIONS [@] } " 3>& 1 1>& 2 2>& 3)
[ [ -z " $SELECTED " ] ] && return
case " ${ SELECTED %% : * } " in
pvesm) _remove_pvesm_storage " ${ SELECTED #pvesm : } " ; ;
fstab) _remove_fstab_entry " ${ SELECTED #fstab : } " ; ;
esac
}
2026-04-01 23:09:51 +02:00
list_available_disks( ) {
show_proxmenux_logo
msg_title " $( translate "Available Disks on Host" ) "
echo "=================================================="
echo ""
echo -e " ${ BOLD } $( translate "All block devices:" ) ${ CL } "
echo ""
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,MODEL 2>/dev/null
echo ""
2026-04-12 20:32:34 +02:00
echo -e " ${ BOLD } $( translate "Proxmox local storages:" ) ${ CL } "
2026-04-01 23:09:51 +02:00
if command -v pvesm >/dev/null 2>& 1; then
2026-04-12 20:32:34 +02:00
pvesm status 2>/dev/null | awk '$2 == "dir" || $2 == "zfspool" {print " " $1, $2, $3}' || echo " $( translate "None" ) "
2026-04-01 23:09:51 +02:00
fi
echo ""
msg_success " $( translate "Press Enter to continue..." ) "
read -r
}
# ==========================================================
# MAIN MENU
# ==========================================================
while true; do
CHOICE = $( dialog --backtitle "ProxMenux" \
--title " $( translate "Local Disk Manager - Proxmox Host" ) " \
--menu " $( translate "Choose an option:" ) " 18 70 6 \
"1" " $( translate "Add Local Disk as Proxmox Storage" ) " \
"2" " $( translate "View Disk Storages" ) " \
"3" " $( translate "Remove Disk Storage" ) " \
"4" " $( translate "List Available Disks" ) " \
"5" " $( translate "Exit" ) " \
3>& 1 1>& 2 2>& 3)
RETVAL = $?
if [ [ $RETVAL -ne 0 ] ] ; then
exit 0
fi
case $CHOICE in
1) add_disk_to_proxmox ; ;
2) view_disk_storages ; ;
3) remove_disk_storage ; ;
4) list_available_disks ; ;
5) exit 0 ; ;
*) exit 0 ; ;
esac
done