Add btrfs script
This commit is contained in:
parent
92edef969f
commit
5beb3fb9e2
2 changed files with 154 additions and 0 deletions
11
nixos/common/software/cli/scripts.nix
Normal file
11
nixos/common/software/cli/scripts.nix
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
let
|
||||||
|
clean-hm = pkgs.writeScriptBin "clean-hm" "${builtins.readFile ./scripts/clean-hm.sh}";
|
||||||
|
btrfs-backup = pkgs.writeScriptBin "btrfs-backup" "${builtins.readFile ./scripts/btrfs-backup.sh}";
|
||||||
|
in {
|
||||||
|
environment.systemPackages = [
|
||||||
|
clean-hm
|
||||||
|
btrfs-backup
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
143
nixos/common/software/cli/scripts/btrfs-backup.sh
Normal file
143
nixos/common/software/cli/scripts/btrfs-backup.sh
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Check for required argument
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo "Usage: $(basename "$0") SNAPPER_CONFIG"
|
||||||
|
echo "Example: $(basename "$0") root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
SNAPPER_CONFIG="$1"
|
||||||
|
SOURCE_PATH="/.snapshots"
|
||||||
|
REMOTE_HOST="root@synology"
|
||||||
|
DEST_PATH="/volume1/backups/${SNAPPER_CONFIG}" # Added config name to path
|
||||||
|
STATE_FILE="/var/lib/snapper-backup-${SNAPPER_CONFIG}.state" # Made state file unique per config
|
||||||
|
LOG_FILE="/var/log/snapper-backup-${SNAPPER_CONFIG}.log" # Made log file unique per config
|
||||||
|
KEEP_SNAPSHOTS=5
|
||||||
|
|
||||||
|
# Ensure we exit on any error
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Verify snapper config exists
|
||||||
|
if ! snapper -c "$SNAPPER_CONFIG" list &>/dev/null; then
|
||||||
|
echo "ERROR: Snapper config '$SNAPPER_CONFIG' does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Logging function
|
||||||
|
log() {
|
||||||
|
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to verify snapshot exists
|
||||||
|
verify_snapshot() {
|
||||||
|
local snapshot_num="$1"
|
||||||
|
if [ ! -d "$SOURCE_PATH/$snapshot_num/snapshot" ]; then
|
||||||
|
log "ERROR: Snapshot $snapshot_num does not exist"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to verify remote connectivity
|
||||||
|
verify_remote() {
|
||||||
|
if ! ssh -q "$REMOTE_HOST" "exit"; then
|
||||||
|
log "ERROR: Cannot connect to remote host"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to get remote snapshots
|
||||||
|
get_remote_snapshots() {
|
||||||
|
ssh "$REMOTE_HOST" "find '$DEST_PATH' -maxdepth 1 -type d -name 'snapshot*' | sort -n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to cleanup old remote snapshots
|
||||||
|
cleanup_remote_snapshots() {
|
||||||
|
local snapshots=($(get_remote_snapshots))
|
||||||
|
local count=${#snapshots[@]}
|
||||||
|
|
||||||
|
if [ $count -gt $KEEP_SNAPSHOTS ]; then
|
||||||
|
local to_delete=$((count - KEEP_SNAPSHOTS))
|
||||||
|
log "Cleaning up $to_delete old snapshots on remote system"
|
||||||
|
|
||||||
|
for ((i=0; i<$to_delete; i++)); do
|
||||||
|
local snapshot="${snapshots[$i]}"
|
||||||
|
log "Deleting remote snapshot: $snapshot"
|
||||||
|
ssh "$REMOTE_HOST" "btrfs subvolume delete '$snapshot'" || \
|
||||||
|
log "WARNING: Failed to delete snapshot $snapshot"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start backup process
|
||||||
|
log "Starting backup for snapper config: $SNAPPER_CONFIG"
|
||||||
|
|
||||||
|
# Verify remote connectivity first
|
||||||
|
verify_remote
|
||||||
|
|
||||||
|
# Get latest successful transfer number
|
||||||
|
LAST_TRANSFERRED=$(cat "$STATE_FILE" 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
# Get latest snapshot number from snapper
|
||||||
|
LATEST_SNAPSHOT=$(snapper -c "$SNAPPER_CONFIG" list | tail -n 1 | awk '{print $1}')
|
||||||
|
|
||||||
|
# Verify snapshots exist
|
||||||
|
verify_snapshot "$LATEST_SNAPSHOT" || exit 1
|
||||||
|
if [ -n "$LAST_TRANSFERRED" ]; then
|
||||||
|
verify_snapshot "$LAST_TRANSFERRED" || exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Exit if no new snapshots to transfer
|
||||||
|
if [ "$LAST_TRANSFERRED" = "$LATEST_SNAPSHOT" ]; then
|
||||||
|
log "No new snapshots to transfer"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create destination directory if it doesn't exist
|
||||||
|
ssh "$REMOTE_HOST" "mkdir -p '$DEST_PATH'"
|
||||||
|
|
||||||
|
# Perform the transfer
|
||||||
|
if [ -z "$LAST_TRANSFERRED" ]; then
|
||||||
|
# First time backup - full send
|
||||||
|
log "Performing full send of snapshot $LATEST_SNAPSHOT"
|
||||||
|
sudo btrfs send "$SOURCE_PATH/$LATEST_SNAPSHOT/snapshot" | \
|
||||||
|
pv -bytes | \
|
||||||
|
ssh "$REMOTE_HOST" "btrfs receive '$DEST_PATH'" && {
|
||||||
|
echo "$LATEST_SNAPSHOT" > "$STATE_FILE"
|
||||||
|
log "Full send completed successfully"
|
||||||
|
} || {
|
||||||
|
log "ERROR: Full send failed"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# Incremental send
|
||||||
|
log "Performing incremental send from $LAST_TRANSFERRED to $LATEST_SNAPSHOT"
|
||||||
|
sudo btrfs send -p "$SOURCE_PATH/$LAST_TRANSFERRED/snapshot" \
|
||||||
|
"$SOURCE_PATH/$LATEST_SNAPSHOT/snapshot" | \
|
||||||
|
pv -bytes | \
|
||||||
|
ssh "$REMOTE_HOST" "btrfs receive '$DEST_PATH'" && {
|
||||||
|
echo "$LATEST_SNAPSHOT" > "$STATE_FILE"
|
||||||
|
log "Incremental send completed successfully"
|
||||||
|
} || {
|
||||||
|
log "ERROR: Incremental send failed"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup old snapshots if transfer was successful
|
||||||
|
cleanup_remote_snapshots
|
||||||
|
|
||||||
|
# Verify remote snapshots
|
||||||
|
log "Current remote snapshots:"
|
||||||
|
get_remote_snapshots | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
# Final verification
|
||||||
|
if ! ssh "$REMOTE_HOST" "btrfs subvolume show '$DEST_PATH/snapshot'" &>/dev/null; then
|
||||||
|
log "WARNING: Final verification failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Backup completed successfully"
|
||||||
|
|
Loading…
Reference in a new issue