Files
homey/scripts/backup-longhorn-to-disk.sh
T
2026-04-15 16:49:18 +03:00

185 lines
5.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SRC="${SRC:-/mnt/replicas}"
DEST="${DEST:-/mnt2/homey-backup}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
MANIFEST="$DEST/manifest.json"
PVC_MAPPING=(
"pvc-0310a337-9642-464b-a458-fcb3439328e7-fbc07d5a:ldap-pvc"
"pvc-1cdc51ee-b965-4cab-baf7-077cc6df6f11-0fcfb9cd:authelia-pvc"
"pvc-4888bf84-62c8-4340-adbc-cb31073d8fd2-d065d20b:gitea-pvc"
"pvc-5c1f48e3-346f-4c35-8e6a-8fc0c4c3a842-96d72815:nextcloud-data-pvc"
"pvc-c5b28179-1b9c-462a-be5b-05c4f0bb36ca-5f2dbf4d:nextcloud-postgres-pvc"
"pvc-7f73ee94-5583-4e4a-9788-cba054214b1c-f767850a:radicale-pvc"
"pvc-9e75f35a-27c3-4251-b25a-1a876f82f6c7-c9c8b185:jellyfin-config-pvc"
"pvc-dfe2aa08-bbb8-423b-9001-fb6aea181597-baf06a7f:jellyfin-data-pvc"
"pvc-dd4a069a-a638-49c0-8c95-f954510816e5-7e81a6f6:transmission-config-pvc"
"pvc-e4ba414d-d9c2-4927-b0ae-f6bfb90ce311-a0963101:unknown-pvc-1"
"pvc-ec6afe10-aca3-42ce-9d89-32fc4ac77f9a-8d6baa34:unknown-pvc-2"
)
progress_bar() {
local current=$1
local total=$2
local width=40
local percent=$((current * 100 / total))
local filled=$((current * width / total))
local empty=$((width - filled))
printf "\r["
printf "%${filled}s" | tr ' ' '='
printf "%${empty}s" | tr ' ' ' '
printf "] %3d%% (%d/%d)" "$percent" "$current" "$total"
}
get_pvc_name() {
local pvc_id="$1"
for mapping in "${PVC_MAPPING[@]}"; do
if [[ "$mapping" == "$pvc_id:"* ]]; then
echo "${mapping#*:}"
return
fi
done
echo "unknown"
}
echo "========================================"
echo " Longhorn Volume Backup Tool"
echo "========================================"
echo ""
echo "Source: $SRC"
echo "Destination: $DEST"
echo "Timestamp: $TIMESTAMP"
echo ""
mkdir -p "$DEST/volumes"
mkdir -p "$DEST/metadata"
VOLUMES=()
TOTAL_SIZE=0
echo "Scanning volumes..."
for pvc_dir in "$SRC"/*/; do
pvc_name=$(basename "$pvc_dir")
friendly_name=$(get_pvc_name "$pvc_name")
VOLUMES+=("$pvc_name:$friendly_name")
size=$(sudo du -sb "$pvc_dir" 2>/dev/null | awk '{print $1}' || echo "0")
TOTAL_SIZE=$((TOTAL_SIZE + size))
printf " %-50s %s\n" "$friendly_name" "$(numfmt --to=iec-i --suffix=B "$size" 2>/dev/null || echo "${size}B")"
done
TOTAL_VOLUMES=${#VOLUMES[@]}
echo ""
echo "Found $TOTAL_VOLUMES volumes, total size: $(numfmt --to=iec-i --suffix=B "$TOTAL_SIZE")"
echo ""
read -p "Continue with backup? [y/N] " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 1
fi
echo ""
echo "Starting backup..."
echo ""
COPIED_SIZE=0
START_TIME=$(date +%s)
for i in "${!VOLUMES[@]}"; do
volume="${VOLUMES[$i]}"
pvc_name="${volume%%:*}"
friendly_name="${volume#*:}"
CURRENT=$((i + 1))
progress_bar "$CURRENT" "$TOTAL_VOLUMES"
echo " - $friendly_name"
sudo rsync -a --no-owner --no-group --info=progress2 \
"$SRC/$pvc_name/" \
"$DEST/volumes/$pvc_name/" 2>&1 | while read -r line; do
if [[ "$line" =~ to-chk=*([0-9]+)/([0-9]+) ]]; then
printf "\r %s" "$line"
fi
done
sudo chown -R "$USER:$USER" "$DEST/volumes/$pvc_name" 2>/dev/null || true
if [[ -f "$SRC/$pvc_name/volume.meta" ]]; then
sudo cp "$SRC/$pvc_name/volume.meta" "$DEST/metadata/${pvc_name}.meta" 2>/dev/null || true
fi
echo ""
done
echo ""
echo "Generating manifest..."
cat > "$MANIFEST" << EOF
{
"backup_timestamp": "$TIMESTAMP",
"source_path": "$SRC",
"destination_path": "$DEST",
"total_volumes": $TOTAL_VOLUMES,
"total_size_bytes": $TOTAL_SIZE,
"volumes": [
EOF
FIRST=true
for volume in "${VOLUMES[@]}"; do
pvc_name="${volume%%:*}"
friendly_name="${volume#*:}"
vol_size=$(sudo du -sb "$SRC/$pvc_name" 2>/dev/null | awk '{print $1}' || echo "0")
vol_size_hr=$(numfmt --to=iec-i --suffix=B "$vol_size" 2>/dev/null || echo "${vol_size}B")
head_file=$(sudo find "$DEST/volumes/$pvc_name" -name "volume-head-*.img" 2>/dev/null | head -1)
head_file=$(basename "$head_file" 2>/dev/null || echo "")
if [[ "$FIRST" == "true" ]]; then
FIRST=false
else
echo "," >> "$MANIFEST"
fi
cat >> "$MANIFEST" << EOF
{
"pvc_id": "$pvc_name",
"friendly_name": "$friendly_name",
"size_bytes": $vol_size,
"size_human": "$vol_size_hr",
"volume_head": "$head_file",
"backup_path": "volumes/$pvc_name"
}
EOF
done
cat >> "$MANIFEST" << EOF
]
}
EOF
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo ""
echo "========================================"
echo " Backup Complete!"
echo "========================================"
echo ""
echo "Duration: $((DURATION / 60))m $((DURATION % 60))s"
echo "Location: $DEST"
echo "Manifest: $MANIFEST"
echo ""
echo "Backup size:"
sudo du -sh "$DEST/volumes"
echo ""
echo "To mount a volume, run:"
echo " ./scripts/mount-longhorn-volume.sh <pvc-name-or-friendly-name>"
echo ""
echo "To restore a volume, run:"
echo " ./scripts/restore-longhorn-volume.sh <pvc-name-or-friendly-name>"