Fix zvol-iscsi backup: fd conflict, case-insensitive zvol lookup, and snapdev handling
- Use fd 3 for domain list to avoid stdin conflicts with inner reads - Resolve zvol names case-insensitively to match lowercased iSCSI IQNs - Toggle snapdev=visible/hidden so snapshot block devices appear for dd - Wait for snapshot device with udevadm settle before reading - Pass volsize to pv for accurate progress display - Document volmode=dev prerequisite to avoid zvol_create_minors deadlock
This commit is contained in:
parent
25f11d5535
commit
e9cae05639
@ -5,6 +5,13 @@
|
|||||||
# Runs on the ZFS server where the zvols live. VM domains are
|
# Runs on the ZFS server where the zvols live. VM domains are
|
||||||
# discovered via virsh on remote libvirt hosts connected over iSCSI,
|
# discovered via virsh on remote libvirt hosts connected over iSCSI,
|
||||||
# matching the domain-driven approach of the other kvm-backup scripts.
|
# matching the domain-driven approach of the other kvm-backup scripts.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# Set volmode=dev on each zvol before running this script to prevent
|
||||||
|
# kernel partition scanning on snapshot devices. This must be done
|
||||||
|
# once per zvol (not toggled at runtime) to avoid a deadlock in
|
||||||
|
# zvol_create_minors when combined with snapdev toggling.
|
||||||
|
# zfs set volmode=dev tank/kvm/<name>
|
||||||
|
|
||||||
# A file to prevent overlapping runs.
|
# A file to prevent overlapping runs.
|
||||||
PIDFILE="/tmp/backup-zvol-iscsi.pid"
|
PIDFILE="/tmp/backup-zvol-iscsi.pid"
|
||||||
@ -133,7 +140,7 @@ backupDomains() {
|
|||||||
local HOST_LABEL="$2"
|
local HOST_LABEL="$2"
|
||||||
|
|
||||||
DOMLIST_STATUS_TMP="/tmp/backup-zvol-domlist-$HOST_LABEL-tmp"
|
DOMLIST_STATUS_TMP="/tmp/backup-zvol-domlist-$HOST_LABEL-tmp"
|
||||||
while read -r _ DOMAIN _; do
|
while read -r _ DOMAIN _ <&3; do
|
||||||
# If the domain is empty, skip.
|
# If the domain is empty, skip.
|
||||||
if [[ -z "$DOMAIN" ]]; then
|
if [[ -z "$DOMAIN" ]]; then
|
||||||
continue
|
continue
|
||||||
@ -166,12 +173,21 @@ backupDomains() {
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify the zvol exists locally.
|
# Resolve the zvol name case-insensitively, since iSCSI IQN
|
||||||
ZVOL="$ZFS_PARENT/$ZVOL_NAME"
|
# target names are typically lowercased.
|
||||||
if ! zfs list "$ZVOL" &>/dev/null; then
|
RESOLVED_NAME=$(zfs list -o name -H -r "$ZFS_PARENT" -d 1 | while read -r ZN; do
|
||||||
echo "Warning: zvol $ZVOL does not exist locally, skipping"
|
BN="${ZN##*/}"
|
||||||
|
if [[ "${BN,,}" == "${ZVOL_NAME,,}" ]]; then
|
||||||
|
echo "$BN"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done)
|
||||||
|
if [[ -z "$RESOLVED_NAME" ]]; then
|
||||||
|
echo "Warning: zvol matching $ZFS_PARENT/$ZVOL_NAME does not exist locally, skipping"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
ZVOL_NAME="$RESOLVED_NAME"
|
||||||
|
ZVOL="$ZFS_PARENT/$ZVOL_NAME"
|
||||||
|
|
||||||
DEVS+=("$DEV")
|
DEVS+=("$DEV")
|
||||||
ZVOL_NAMES+=("$ZVOL_NAME")
|
ZVOL_NAMES+=("$ZVOL_NAME")
|
||||||
@ -202,19 +218,33 @@ backupDomains() {
|
|||||||
ZVOL_NAME=${ZVOL_NAMES[$i]}
|
ZVOL_NAME=${ZVOL_NAMES[$i]}
|
||||||
ZVOL="$ZFS_PARENT/$ZVOL_NAME"
|
ZVOL="$ZFS_PARENT/$ZVOL_NAME"
|
||||||
|
|
||||||
|
# Make snapshot block devices visible so we can read them.
|
||||||
|
zfs set snapdev=visible "$ZVOL"
|
||||||
|
|
||||||
# Create a ZFS snapshot.
|
# Create a ZFS snapshot.
|
||||||
echo "Creating snapshot: $ZVOL@$SNAPSHOT_NAME"
|
echo "Creating snapshot: $ZVOL@$SNAPSHOT_NAME"
|
||||||
if ! zfs snapshot "$ZVOL@$SNAPSHOT_NAME"; then
|
if ! zfs snapshot "$ZVOL@$SNAPSHOT_NAME"; then
|
||||||
fail "Failed to create snapshot for $ZVOL"
|
fail "Failed to create snapshot for $ZVOL"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Wait for the snapshot block device to appear.
|
||||||
|
SNAP_DEV="/dev/zvol/$ZVOL@$SNAPSHOT_NAME"
|
||||||
|
udevadm settle
|
||||||
|
for _ in $(seq 1 30); do
|
||||||
|
[[ -e "$SNAP_DEV" ]] && break
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
if [[ ! -e "$SNAP_DEV" ]]; then
|
||||||
|
fail "Snapshot device $SNAP_DEV did not appear"
|
||||||
|
fi
|
||||||
|
|
||||||
# Read the raw disk image from the snapshot's block device.
|
# Read the raw disk image from the snapshot's block device.
|
||||||
# This produces a portable raw image that can be restored to
|
# This produces a portable raw image that can be restored to
|
||||||
# any virtual disk or converted with qemu-img, without
|
# any virtual disk or converted with qemu-img, without
|
||||||
# requiring ZFS on the restore target.
|
# requiring ZFS on the restore target.
|
||||||
SNAP_DEV="/dev/zvol/$ZVOL@$SNAPSHOT_NAME"
|
ZVOL_SIZE=$(zfs list -Hp -o volsize "$ZVOL")
|
||||||
echo "Creating backup for $DOMAIN ($DEV [$ZVOL])"
|
echo "Creating backup for $DOMAIN ($DEV [$ZVOL])"
|
||||||
if ! dd if="$SNAP_DEV" bs=4M status=none | pv | borg create \
|
if ! dd if="$SNAP_DEV" bs=4M status=none | pv -s "$ZVOL_SIZE" | borg create \
|
||||||
--verbose \
|
--verbose \
|
||||||
--stats \
|
--stats \
|
||||||
--show-rc \
|
--show-rc \
|
||||||
@ -233,6 +263,9 @@ backupDomains() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Hide snapshot devices again.
|
||||||
|
zfs set snapdev=hidden "$ZVOL"
|
||||||
|
|
||||||
# Cleanup old ZFS snapshots.
|
# Cleanup old ZFS snapshots.
|
||||||
cleanupSnapshots "$ZVOL"
|
cleanupSnapshots "$ZVOL"
|
||||||
done
|
done
|
||||||
@ -267,7 +300,7 @@ backupDomains() {
|
|||||||
fail "Failed to prune $DOMAIN xml"
|
fail "Failed to prune $DOMAIN xml"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done < <(
|
done 3< <(
|
||||||
if [[ -n "$SSH_PREFIX" ]]; then
|
if [[ -n "$SSH_PREFIX" ]]; then
|
||||||
$SSH_PREFIX "virsh list --all" 2>/dev/null | tail -n +3
|
$SSH_PREFIX "virsh list --all" 2>/dev/null | tail -n +3
|
||||||
else
|
else
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user