snap and mount separation
This commit is contained in:
parent
b181efdb41
commit
4f5c090534
4 changed files with 120 additions and 130 deletions
|
@ -18,7 +18,48 @@ let
|
||||||
];
|
];
|
||||||
|
|
||||||
text = ''
|
text = ''
|
||||||
${builtins.readFile ./snap-and-mount.sh} "${cfg.mountPath}" "${cfg.zfsDataset}" "${cfg.snapshotName}"
|
# Import our functions
|
||||||
|
${builtins.readFile ./functions.sh}
|
||||||
|
|
||||||
|
BACKUP_DIRECTORY="${cfg.mountPath}"
|
||||||
|
ZFS_DATASET="${cfg.zfsDataset}"
|
||||||
|
SNAPSHOT_NAME="${cfg.snapshotName}"
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
echo "Error: This script must be run as root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Main logic
|
||||||
|
zfs_backup_cleanup "$BACKUP_DIRECTORY"
|
||||||
|
|
||||||
|
echo "Previous snapshot:"
|
||||||
|
zfs list -t snapshot | grep "$ZFS_DATASET@$SNAPSHOT_NAME" || true
|
||||||
|
|
||||||
|
echo "Attempting to destroy existing snapshot..."
|
||||||
|
if zfs destroy -r "$ZFS_DATASET@$SNAPSHOT_NAME"; then
|
||||||
|
echo "Successfully destroyed old snapshot"
|
||||||
|
else
|
||||||
|
echo "Failed to destroy existing snapshot"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create new snapshot
|
||||||
|
if ! zfs snapshot -r "$ZFS_DATASET@$SNAPSHOT_NAME"; then
|
||||||
|
echo "Failed to create snapshot"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "New snapshot created:"
|
||||||
|
zfs list -t snapshot | grep "$ZFS_DATASET@$SNAPSHOT_NAME"
|
||||||
|
|
||||||
|
if ! mount_dataset; then
|
||||||
|
echo "Failed to mount snapshot"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Successfully created and mounted snapshot at $BACKUP_DIRECTORY"
|
||||||
|
mount | grep "$BACKUP_DIRECTORY"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
68
nixos/modules/nixos/services/zfs-nightly-snap/functions.sh
Normal file
68
nixos/modules/nixos/services/zfs-nightly-snap/functions.sh
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# functions sourced from: https://github.com/Jip-Hop/zfs-backup-snapshots
|
||||||
|
function mount_dataset() {
|
||||||
|
mkdir -p "$BACKUP_DIRECTORY"
|
||||||
|
mapfile -t fs < <(zfs list "$ZFS_DATASET" -r -H -o name,mountpoint | grep -E "(legacy)$|(none)$" -v | awk '{print gsub("/","/", $2), $1}' | sort -n | cut -d' ' -f2-)
|
||||||
|
|
||||||
|
for fs in "${fs[@]}"; do
|
||||||
|
mount_latest_snap "${fs}" "${BACKUP_DIRECTORY}"
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function zfs_backup_cleanup() {
|
||||||
|
mapfile -t fs < <(tac /etc/mtab | cut -d " " -f 2 | grep "${1}")
|
||||||
|
|
||||||
|
for i in "${fs[@]}"; do
|
||||||
|
echo "Unmounting $i"
|
||||||
|
umount "$i"
|
||||||
|
done
|
||||||
|
|
||||||
|
find "${1}" -type d -empty -delete 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
function zfs_latest_snap() {
|
||||||
|
snapshot=$(zfs list -H -t snapshot -o name -S creation -d1 "${1}" | head -1 | cut -d '@' -f 2)
|
||||||
|
if [[ -z $snapshot ]]; then
|
||||||
|
echo "No snapshot exists for ${1}, it will not be backed up."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "$snapshot"
|
||||||
|
}
|
||||||
|
|
||||||
|
function zfs_snapshot_mountpoint() {
|
||||||
|
mountpoint=$(zfs list -H -o mountpoint "${1}")
|
||||||
|
if [[ $? == 1 ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
path="${mountpoint}/.zfs/snapshot/${2}"
|
||||||
|
if stat "${path}" &>/dev/null; then
|
||||||
|
echo "${path}"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function mount_latest_snap() {
|
||||||
|
BACKUP_DIRECTORY="${2}"
|
||||||
|
filesystem="${1}"
|
||||||
|
|
||||||
|
snapshot=$(zfs_latest_snap "${filesystem}")
|
||||||
|
if [[ $? == 1 ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sourcepath=$(zfs_snapshot_mountpoint "${filesystem}" "${snapshot}")
|
||||||
|
if [[ $? == 1 ]]; then
|
||||||
|
echo "Cannot find snapshot ${snapshot} for ${filesystem}, perhaps it's not mounted?"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mountpath=${BACKUP_DIRECTORY}/${filesystem}
|
||||||
|
mkdir -p "${mountpath}"
|
||||||
|
echo "mount ${sourcepath} => ${mountpath}"
|
||||||
|
mount --bind --read-only "${sourcepath}" "${mountpath}"
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
#!/usr/bin/env nix-shell
|
|
||||||
#!nix-shell -I nixpkgs=/etc/nix/inputs/nixpkgs -i bash -p busybox zfs
|
|
||||||
# shellcheck disable=SC1008
|
|
||||||
|
|
||||||
set -e # Exit on error
|
|
||||||
|
|
||||||
BACKUP_DIRECTORY="/mnt/restic_nightly_backup"
|
|
||||||
ZFS_DATASET="nahar/containers/volumes"
|
|
||||||
SNAPSHOT_NAME="restic_nightly_snap"
|
|
||||||
|
|
||||||
# Execute the main script with our parameters
|
|
||||||
./snap-and-mount.sh \
|
|
||||||
"$BACKUP_DIRECTORY" \
|
|
||||||
"$ZFS_DATASET" \
|
|
||||||
"$SNAPSHOT_NAME"
|
|
124
nixos/modules/nixos/services/zfs-nightly-snap/snap-and-mount.sh
Normal file → Executable file
124
nixos/modules/nixos/services/zfs-nightly-snap/snap-and-mount.sh
Normal file → Executable file
|
@ -1,129 +1,27 @@
|
||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -I nixpkgs=/etc/nix/inputs/nixpkgs -i bash -p busybox zfs
|
||||||
# shellcheck disable=SC1008
|
# shellcheck disable=SC1008
|
||||||
|
|
||||||
# Command line arguments
|
set -e # Exit on error
|
||||||
BACKUP_DIRECTORY="$1"
|
|
||||||
ZFS_DATASET="$2"
|
|
||||||
SNAPSHOT_NAME="$3"
|
|
||||||
|
|
||||||
if [ -z "$BACKUP_DIRECTORY" ] || [ -z "$ZFS_DATASET" ] || [ -z "$SNAPSHOT_NAME" ]; then
|
# Source the functions
|
||||||
echo "Usage: $0 <backup_directory> <zfs_dataset> <snapshot_name>"
|
. ./functions.sh
|
||||||
exit 1
|
|
||||||
fi
|
BACKUP_DIRECTORY="${1:-/mnt/restic_nightly_backup}"
|
||||||
# Check if running as root
|
ZFS_DATASET="${2:-nahar/containers/volumes}"
|
||||||
|
SNAPSHOT_NAME="${3:-restic_nightly_snap}"
|
||||||
|
|
||||||
if [ "$(id -u)" -ne 0 ]; then
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
echo "Error: This script must be run as root."
|
echo "Error: This script must be run as root."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# functions sourced from: https://github.com/Jip-Hop/zfs-backup-snapshots
|
# Main logic
|
||||||
# some enhancements made to the original code to adhere to best practices
|
|
||||||
# mounts all zfs filesystems under $ZFS_DATASET
|
|
||||||
function mount_dataset() {
|
|
||||||
# ensure BACKUP_DIRECTORY exists
|
|
||||||
mkdir -p "$BACKUP_DIRECTORY"
|
|
||||||
# get list of all zfs filesystems under $ZFS_DATASET
|
|
||||||
# exclude if mountpoint "legacy" and "none" mountpoint
|
|
||||||
# order by shallowest mountpoint first (determined by number of slashes)
|
|
||||||
mapfile -t fs < <(zfs list "$ZFS_DATASET" -r -H -o name,mountpoint | grep -E "(legacy)$|(none)$" -v | awk '{print gsub("/","/", $2), $1}' | sort -n | cut -d' ' -f2-)
|
|
||||||
|
|
||||||
for fs in "${fs[@]}"; do
|
|
||||||
mount_latest_snap "${fs}" "${BACKUP_DIRECTORY}"
|
|
||||||
done
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# umounts and cleans up the backup directory
|
|
||||||
# usage: zfs_backup_cleanup BACKUP_DIRECTORY
|
|
||||||
function zfs_backup_cleanup() {
|
|
||||||
# get all filesystems mounted within the backup directory
|
|
||||||
mapfile -t fs < <(tac /etc/mtab | cut -d " " -f 2 | grep "${1}")
|
|
||||||
|
|
||||||
# umount said filesystems
|
|
||||||
for i in "${fs[@]}"; do
|
|
||||||
echo "Unmounting $i"
|
|
||||||
umount "$i"
|
|
||||||
done
|
|
||||||
|
|
||||||
# delete empty directories from within the backup directory
|
|
||||||
find "${1}" -type d -empty -delete 2>/dev/null || true
|
|
||||||
}
|
|
||||||
|
|
||||||
# gets the name of the newest snapshot given a zfs filesystem
|
|
||||||
# usage: get_latest_snap filesystem
|
|
||||||
function zfs_latest_snap() {
|
|
||||||
snapshot=$(zfs list -H -t snapshot -o name -S creation -d1 "${1}" | head -1 | cut -d '@' -f 2)
|
|
||||||
if [[ -z $snapshot ]]; then
|
|
||||||
# if there's no snapshot then let's ignore it
|
|
||||||
echo "No snapshot exists for ${1}, it will not be backed up."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
echo "$snapshot"
|
|
||||||
}
|
|
||||||
|
|
||||||
# gets the path of a snapshot given a zfs filesystem and a snapshot name
|
|
||||||
# usage zfs_snapshot_mountpoint filesystem snapshot
|
|
||||||
function zfs_snapshot_mountpoint() {
|
|
||||||
# get mountpoint for filesystem
|
|
||||||
mountpoint=$(zfs list -H -o mountpoint "${1}")
|
|
||||||
|
|
||||||
# exit if filesystem doesn't exist
|
|
||||||
if [[ $? == 1 ]]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# build out path
|
|
||||||
path="${mountpoint}/.zfs/snapshot/${2}"
|
|
||||||
|
|
||||||
# check to make sure path exists
|
|
||||||
if stat "${path}" &>/dev/null; then
|
|
||||||
echo "${path}"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# mounts latest snapshot in directory
|
|
||||||
# usage: mount_latest_snap filesystem BACKUP_DIRECTORY
|
|
||||||
function mount_latest_snap() {
|
|
||||||
BACKUP_DIRECTORY="${2}"
|
|
||||||
filesystem="${1}"
|
|
||||||
|
|
||||||
# get name of latest snapshot
|
|
||||||
snapshot=$(zfs_latest_snap "${filesystem}")
|
|
||||||
|
|
||||||
# if there's no snapshot then let's ignore it
|
|
||||||
if [[ $? == 1 ]]; then
|
|
||||||
echo "No snapshot exists for ${filesystem}, it will not be backed up."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
sourcepath=$(zfs_snapshot_mountpoint "${filesystem}" "${snapshot}")
|
|
||||||
# if the filesystem is not mounted/path doesn't exist then let's ignore as well
|
|
||||||
if [[ $? == 1 ]]; then
|
|
||||||
echo "Cannot find snapshot ${snapshot} for ${filesystem}, perhaps it's not mounted? Anyways, it will not be backed up."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# mountpath may be inside a previously mounted snapshot
|
|
||||||
mountpath=${BACKUP_DIRECTORY}/${filesystem}
|
|
||||||
|
|
||||||
# mount to backup directory using a bind filesystem
|
|
||||||
mkdir -p "${mountpath}"
|
|
||||||
echo "mount ${sourcepath} => ${mountpath}"
|
|
||||||
mount --bind --read-only "${sourcepath}" "${mountpath}"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Unmount and cleanup if necessary
|
|
||||||
zfs_backup_cleanup "$BACKUP_DIRECTORY"
|
zfs_backup_cleanup "$BACKUP_DIRECTORY"
|
||||||
|
|
||||||
# Check if snapshot exists
|
|
||||||
echo "Previous snapshot:"
|
echo "Previous snapshot:"
|
||||||
zfs list -t snapshot | grep "$ZFS_DATASET@$SNAPSHOT_NAME" || true
|
zfs list -t snapshot | grep "$ZFS_DATASET@$SNAPSHOT_NAME" || true
|
||||||
|
|
||||||
# Attempt to destroy existing snapshot
|
|
||||||
echo "Attempting to destroy existing snapshot..."
|
echo "Attempting to destroy existing snapshot..."
|
||||||
if zfs destroy -r "$ZFS_DATASET@$SNAPSHOT_NAME"; then
|
if zfs destroy -r "$ZFS_DATASET@$SNAPSHOT_NAME"; then
|
||||||
echo "Successfully destroyed old snapshot"
|
echo "Successfully destroyed old snapshot"
|
||||||
|
@ -132,7 +30,6 @@ else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create new snapshot
|
|
||||||
if ! zfs snapshot -r "$ZFS_DATASET@$SNAPSHOT_NAME"; then
|
if ! zfs snapshot -r "$ZFS_DATASET@$SNAPSHOT_NAME"; then
|
||||||
echo "Failed to create snapshot"
|
echo "Failed to create snapshot"
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -141,7 +38,6 @@ fi
|
||||||
echo "New snapshot created:"
|
echo "New snapshot created:"
|
||||||
zfs list -t snapshot | grep "$ZFS_DATASET@$SNAPSHOT_NAME"
|
zfs list -t snapshot | grep "$ZFS_DATASET@$SNAPSHOT_NAME"
|
||||||
|
|
||||||
# Mount the snapshot
|
|
||||||
if ! mount_dataset; then
|
if ! mount_dataset; then
|
||||||
echo "Failed to mount snapshot"
|
echo "Failed to mount snapshot"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
Loading…
Reference in a new issue