Skip to content

Instantly share code, notes, and snippets.

@erikw
Last active March 2, 2024 17:43
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save erikw/eeec35be33e847c211acd886ffb145d5 to your computer and use it in GitHub Desktop.
Save erikw/eeec35be33e847c211acd886ffb145d5 to your computer and use it in GitHub Desktop.
znp: Wrap shell command in ZFS pre-post snapshots and log outputs.
#!/usr/bin/env bash
# Runs a command wrapped in ZFS pre-post snapshots. The whole data pool is recursively snapshotted.
# Analogous to my snp script for BTRFS: https://gist.github.com/erikw/5229436
# Usage: $ znp <commands>
# e.g.: $ znp pgk upgrade
# e.g.: $ znp portmaster -aG
# e.g.: $ znp freebsd-upgrade install
zfs_pool=zroot
log_path="/var/local/log/znp"
date=$(date "+%Y-%m-%d-%H%M%S")
snapshot_prefix="znp_${date}"
log_file="${log_path}/znp_${date}.log"
! [ -d $log_path ] && mkdir -p $log_path
# Log stdout and stderr. Reference: http://stackoverflow.com/questions/3173131/redirect-copy-of-stdout-to-log-file-from-within-bash-script-itself
exec > >(tee -a "$log_file")
exec 2> >(tee -a "$log_file" >&2)
cmd="$@"
echo "> Logging to: ${log_file}"
# snapper create --type=pre --cleanup-algorithm=number --print-number --description="${cmd}"
snapshot_pre="${zfs_pool}@${snapshot_prefix}_pre"
zfs snapshot -r $snapshot_pre
echo "> New pre snapshot created: ${snapshot_pre}"
echo -e "> Running command \"${cmd}\".\n"
eval "$cmd"
ecode="$?"
echo -e "\n> Command exit code: ${ecode}"
snapshot_post="${zfs_pool}@${snapshot_prefix}_post"
zfs snapshot -r $snapshot_post
echo "> New post snapshot created: ${snapshot_post}"
echo "> See the diff between them using:"
# Tip: add -t to xargs to print every individual command.
echo "zfs list -t snapshot -o name | grep ${snapshot_prefix} | cut -d@ -f1 | uniq | xargs -I{} -n1 sh -c 'zfs diff -F {}@${snapshot_prefix}_pre {}@${snapshot_prefix}_post 2>/dev/null'"
echo "> Destroy these snapshots with:"
echo "zfs destroy -r ${snapshot_pre} && zfs destroy -r ${snapshot_post}"
echo "> Destroy all snapshots created with znp:"
echo "zfs list -H -o name -t snapshot | grep "${zfs_pool}@znp_" | xargs -n1 zfs destroy -r"
echo "> Destroy the 10 oldest snapshot pairs created with znp:"
echo "zfs list -H -o name -t snapshot | grep "${zfs_pool}@znp_" | head -20 | xargs -n1 zfs destroy -r"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment