I like this dirty little script! Yes, I knew about the PCIe option but I like the script better: it allows me to fetch SMART data and perform smartctl tests without having to mess with the Proxmox config (again).
I did some tweaks to the script: your version is rock solid, but suffer one major problem
sda, sdb devices (and so on) are not immutable ⚠️
So what happens at host or guest reboot? If the device symlinks do not match between host and guest, we end up fetching SMART data from the wrong disk!
One major drawback for this is the smartctl test (short or long, doesn't matter): you may end up testing the wrong disk! This could also impact the RAID status and so on.
What I did was integrate a strict mapping between guest-level disks-by-id and host-level disks-by-id: using a map file, I can be 100% sure that the disks OMV thinks to be testing is its real counterpart at host level.
At the moment, I'm using a static mapping file: dynamic solution are welcome, as long as they do not over-load, over-complicate the setup.
Here's the script:
#!/bin/bash
MAP_FILE="/opt/host-guest-disk.map"
new_args=()
fetch-smart() {
islocal=$1
shift
if [[ "$islocal" == "1" ]] ; then
orig.smartctl "$@"
else
ssh -q <proxmox user>@<proxmox-hostname/ip> "smartctl $@"
fi
}
# Default: run locally unless the map says otherwise
localRun="1"
get_disk_byid() {
dev="$1"
# Get ID_SERIAL from udevadm
serial=$(udevadm info --query=property --name="$dev" | awk -F= '/^ID_SERIAL=/{print $2}')
bus=$(udevadm info --query=property --name="$dev" | awk -F= '/^ID_BUS=/{print $2}')
type=$(udevadm info --query=property --name="$dev" | awk -F= '/^ID_TYPE=/{print $2}')
# Construct expected by-id path
if [[ -n "$serial" && -n "$bus" ]]; then
echo "/dev/disk/by-id/${bus}-${serial}"
else
# Fallback: just return the original device
echo "$dev"
fi
}
for arg in "$@"; do
if [[ "$arg" == /dev/* ]]; then
# If user gave plain /dev/sdX, convert to /dev/disk/by-id/...
if [[ "$(basename "$arg")" =~ ^sd[a-z]+$ ]]; then
byid=$(get_disk_byid "$arg")
short_id=$(basename "$byid")
else
short_id=$(basename "$arg")
fi
# Look up mapping
line=$(grep -m1 -F "$short_id" "$MAP_FILE")
if [[ -n "$line" ]]; then
local_id=$(echo "$line" | cut -d: -f1)
is_local=$(echo "$line" | cut -d: -f2)
remote_id=$(echo "$line" | cut -d: -f3)
new_args+=("/dev/disk/by-id/$remote_id")
localRun="$is_local"
else
# No mapping → keep original and force local run
new_args+=("$arg")
localRun="1"
fi
else
new_args+=("$arg")
fi
done
fetch-smart "$localRun" "${new_args[@]}"
An example of the mapping file:
scsi-0QEMU_QEMU_HARDDISK_drive-scsi1:0:ata-ST000000000-XXXXX_XXXXXX
scsi-0QEMU_QEMU_HARDDISK_drive-scsi2:0:ata-ST0000V0000-YYYYY_YYYYYYY
scsi-0QEMU_QEMU_HARDDISK_drive-scsi3:0:ata-ST000000000-ZZZZZ_ZZZZZZZ
ata-QEMU_HARDDISK_QM00001:0:nvme-SPCC_M.2_PCIe_SSD_000000_XXXXXX
ata-QEMU_DVD-ROM_QM00003:1:ata-QEMU_DVD-ROM_QM00000
The structure is really simple
<guest-level-disk-id>:<is-local>:<proxmox-level-disk-id>
The middle parameter, <is-local>, is just used to decide if the smartctl command needs to be run locally or remotely.
Overall, this seems to work! 🎉

Thanks a lot for your help!
@fonzdm / a human