Advertisement
ighormaia

ZFS Send Backup

Mar 5th, 2025 (edited)
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 7.36 KB | None | 0 0
  1. #!/bin/bash
  2.  
  3. # Load environment variables from the .env file
  4. export $(grep -v '^#' "$(dirname "$0")/../.env" | xargs)
  5.  
  6. ###########################################################################################################################################
  7.  
  8. # Variables
  9. SOURCE_POOL="nas"
  10. BACKUP_POOL="backup-ssd"
  11. DATASET="immich"
  12. SNAPSHOT_NAME="daily-$(date +%Y-%m-%d)"
  13. SNAPSHOT_PATTERN="@daily-"
  14. TELEGRAM_MESSAGE="📢 *Immich Backup*%0A%0ASnapshot: $SNAPSHOT_NAME%0A%0A"
  15.  
  16. ###########################################################################################################################################
  17.  
  18. # Function to accumulate messages
  19. append_message() {
  20.     local MESSAGE="$1"
  21.     TELEGRAM_MESSAGE+="$MESSAGE%0A"
  22. }
  23.  
  24. # Function to send telegram message
  25. send_telegram_message() {
  26.     local MESSAGE="$1"
  27.     $TELEGRAM_MESSAGE_SCRIPT "$TELEGRAM_MESSAGE" "$TELEGRAM_ZFS_TOKEN" "$TELEGRAM_ZFS_CHAT_ID"
  28. }
  29.  
  30. ###########################################################################################################################################
  31.  
  32. # Record the start time
  33. START_TIME=$(date +%s)
  34.  
  35. ###########################################################################################################################################
  36.  
  37. # Check if the snapshot exists
  38. SNAPSHOT_OUTPUT=$(zfs list -t snapshot | grep "$SOURCE_POOL/$DATASET@$SNAPSHOT_NAME" 2>&1)
  39. SNAPSHOT_STATUS=$?
  40.  
  41. # Check if the command was successful
  42. if [ $SNAPSHOT_STATUS -eq 0 ]; then
  43.     append_message "✅ Snapshot found"
  44. else
  45.     append_message "❌ Snapshot not found"
  46. fi
  47.  
  48. ###########################################################################################################################################
  49.  
  50. # Try to unmount before sending snapshot
  51. if [ "$(zfs get -H -o value mounted "$BACKUP_POOL/$DATASET")" = "yes" ]; then
  52.     UNMOUNT_OUTPUT=$(zfs unmount "$BACKUP_POOL/$DATASET" 2>&1)
  53.     UNMOUNT_STATUS=$?
  54.  
  55.     # Check if the command was successful
  56.     if [ $UNMOUNT_STATUS -eq 0 ]; then
  57.         append_message "✅ Unmounted filesystem"
  58.     else
  59.         append_message "❌ Failed to unmount"
  60.     fi
  61. else
  62.     append_message "🔹 Unmount not executed"
  63. fi
  64.  
  65. ###########################################################################################################################################
  66.  
  67. if [ $UNMOUNT_STATUS -eq 0 ]; then
  68.     # Get the latest snapshot on the source pool
  69.     LATEST_SOURCE_SNAPSHOT=$(zfs list -H -t snapshot -o name -S creation "$SOURCE_POOL/$DATASET" | head -n 1)
  70.  
  71.     # Get all snapshots on source pool sorted by creation time
  72.     ALL_SOURCE_SNAPSHOTS=($(zfs list -t snapshot -o name -s creation | grep "$SOURCE_DATASET@"))
  73.  
  74.     # Determine the oldest snapshot from the source
  75.     OLDEST_SOURCE_SNAPSHOT="${ALL_SOURCE_SNAPSHOTS[0]}"
  76.  
  77.     # Get the latest snapshot on the destination pool
  78.     LATEST_BACKUP_SNAPSHOT=$(zfs list -H -t snapshot -o name -S creation "$BACKUP_POOL/$DATASET" 2>/dev/null | grep $SNAPSHOT_PATTERN | head -n 1)
  79.  
  80.     # Ensure the source has at least one snapshot
  81.     if [[ -z "$LATEST_SOURCE_SNAPSHOT" ]]; then
  82.         append_message "❌ No snapshots found"
  83.     fi
  84.  
  85.     # If the destination has no snapshots, a full send is required
  86.     if [[ -z "$LATEST_BACKUP_SNAPSHOT" ]]; then
  87.         append_message "⚠️ No previous snapshot"
  88.         append_message "🔹 Manually equalize"
  89.     fi
  90.  
  91.     # Extract the snapshot names
  92.     LATEST_SOURCE_NAME="${LATEST_SOURCE_SNAPSHOT#*@}"
  93.     LATEST_BACKUP_NAME="${LATEST_BACKUP_SNAPSHOT#*@}"
  94.  
  95.     # If the latest snapshots are the same, no transfer is needed
  96.     if [[ "$LATEST_SOURCE_NAME" == "$LATEST_BACKUP_NAME" ]]; then
  97.         append_message "✅ Snapshots are already synced"
  98.     else
  99.         # Send incremental backup
  100.         SEND_OUTPUT=$( { zfs send -i "$SOURCE_POOL/$DATASET@$LATEST_BACKUP_NAME" "$SOURCE_POOL/$DATASET@$LATEST_SOURCE_NAME" | zfs receive -F "$BACKUP_POOL/$DATASET"; } 2>&1 )
  101.         SEND_STATUS=$?
  102.  
  103.         # Check if the command was successful
  104.         if [[ $SEND_STATUS -eq 0 ]]; then
  105.             append_message "✅ Transferred snapshot"
  106.         else
  107.             append_message "❌ Error transferring partial backup"
  108.         fi
  109.     fi
  110.  
  111.     # Get the logicalreferenced values
  112.     SOURCE_LOGICAL=$(zfs list -o logicalreferenced "$SOURCE_POOL/$DATASET" | awk 'NR==2 {print $1}')
  113.     BACKUP_LOGICAL=$(zfs list -o logicalreferenced "$BACKUP_POOL/$DATASET" | awk 'NR==2 {print $1}')
  114.  
  115.     # Compare values
  116.     if [[ "$SOURCE_LOGICAL" == "$BACKUP_LOGICAL" ]]; then
  117.         append_message "✅ Backup is identical"
  118.     else
  119.         append_message "❌ Backup is not identical"
  120.     fi
  121. else
  122.     append_message "🔹 Transferring not executed"
  123. fi
  124.  
  125. ###########################################################################################################################################
  126.  
  127. # Remount the dataset
  128. if [ "$(zfs get -H -o value mounted "$BACKUP_POOL/$DATASET")" != "yes" ]; then
  129.     MOUNT_OUTPUT=$(zfs mount "$BACKUP_POOL/$DATASET" 2>&1)
  130.     MOUNT_STATUS=$?
  131.  
  132.     if [ "$MOUNT_STATUS" -eq 0 ]; then
  133.         append_message "✅ Remounted filesystem"
  134.     else
  135.         append_message "❌ Remounted failed"
  136.     fi
  137. else
  138.     append_message "🔹 Remounted not executed"
  139. fi
  140.  
  141. ###########################################################################################################################################
  142.  
  143. # Record the end time
  144. END_TIME=$(date +%s)
  145.  
  146. # Calculate total execution time in seconds
  147. ELAPSED_TIME=$((END_TIME - START_TIME))
  148.  
  149. # Convert to minutes and seconds format
  150. MINUTES=$((ELAPSED_TIME / 60))
  151. SECONDS=$((ELAPSED_TIME % 60))
  152.  
  153. # Append execution time to the message log
  154. append_message "%0A⏳ Total time: ${MINUTES}m ${SECONDS}s"
  155.  
  156. ###########################################################################################################################################
  157.  
  158. # Append backup size
  159. TOTAL_SIZE=$(zfs list -o logicalreferenced "$SOURCE_POOL/$DATASET" | awk 'NR==2 {print $1}')
  160. append_message "📂 Total size: $TOTAL_SIZE"
  161.  
  162. ###########################################################################################################################################
  163.  
  164. # Append snapshots total size
  165. SNAPSHOTS_TOTAL_SIZE=$(zfs get -Hp usedbysnapshots | awk '{s+=$3} END {printf "%.2fG\n", s/(1024*1024*1024)}')
  166. append_message "📸 Snapshots size: $SNAPSHOTS_TOTAL_SIZE"
  167.  
  168. ###########################################################################################################################################
  169.  
  170. # Append unmount output logs to the message log
  171. if [[ $UNMOUNT_STATUS -ne 0 ]]; then
  172.     append_message "%0A\`\`\`Error $UNMOUNT_OUTPUT\`\`\`"
  173. fi
  174.  
  175. ###########################################################################################################################################
  176.  
  177. # Append send output logs to the message log
  178. if [[ $SEND_STATUS -ne 0 ]]; then
  179.     append_message "%0A\`\`\`Error $SEND_OUTPUT\`\`\`"
  180. fi
  181.  
  182. ###########################################################################################################################################
  183.  
  184. # Append mount output logs to the message log
  185. if [[ $MOUNT_STATUS -ne 0 ]]; then
  186.     append_message "%0A\`\`\`Error $MOUNT_OUTPUT\`\`\`"
  187. fi
  188.  
  189. ###########################################################################################################################################
  190.  
  191. # Send the telegram message
  192. send_telegram_message
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement