This script handled my initial snapshots on a single host.
authorCarl N. Baldwin <carl@ecbaldwin.net>
Thu, 21 Feb 2008 00:29:29 +0000 (17:29 -0700)
committerCarl N. Baldwin <carl@ecbaldwin.net>
Thu, 21 Feb 2008 00:29:29 +0000 (17:29 -0700)
zfs-replicate [new file with mode: 0755]

diff --git a/zfs-replicate b/zfs-replicate
new file mode 100755 (executable)
index 0000000..20c480f
--- /dev/null
@@ -0,0 +1,104 @@
+#!/bin/bash
+
+: Usage: zfs-backup [filesystem] [destination_pool]
+
+tmpdir="/export/home/tmp"
+
+lockdir="/tmp/zfs-admin-lock"
+if ! mkdir "$lockdir" >/dev/null 2>&1; then
+  echo >&2 "ZFS admin lock directory is present."
+  exit 1
+fi
+
+cleanup() { rm -rf "$lockdir"; }
+trap cleanup EXIT
+
+fs=$1
+sourcepool=${1%%/*}
+fsname=${1#*/}
+destinationpool=$2
+
+if ! zpool list -H "$sourcepool" >/dev/null 2>&1; then
+  echo >&2 "The source pool, '$sourcepool' doesn't seem to exist."
+  exit 1
+fi
+
+if ! zpool list -H "$destinationpool" >/dev/null 2>&1; then
+  echo >&2 "The destination pool, '$destinationpool' doesn't seem to exist."
+  exit 1
+fi
+
+if ! zfs list -H "$fs" >/dev/null 2>&1; then
+  echo >&2 "The source filesytem, '$fs' doesn't seem to exist."
+  exit 1
+fi
+
+printsnaps() {
+sed 's,.*/,,' | awk '{print $1}' 
+}
+
+zfs list -rH -t snapshot $sourcepool/$fsname | printsnaps > /tmp/source-list
+zfs list -rH -t snapshot $destinationpool/$fsname | printsnaps > /tmp/destination-list
+diff -u /tmp/source-list /tmp/destination-list | grep -v '^+++' | awk '/^\+/ {print}' | sed "s,^\+,$destinationpool/," > /tmp/obsolete-snapshots
+rm -f /tmp/source-list /tmp/destination-list
+
+echo >&2 "Removing obsolete backups from the destination pool" 
+for snapshot in $(cat /tmp/obsolete-snapshots); do
+  echo >&2 "Removing '$snapshot' from destination."
+  zfs destroy "$snapshot"
+done
+
+echo >&2 "Rolling back to the most recent snapshot on the destination." 
+zfs rollback $(zfs list -rH -t snapshot $destinationpool/$fsname | awk '{snap=$1} END {print snap}')
+
+echo >&2 "Calculating the most recent common snapshot between the two filesystems." 
+common=""
+if zfs list -H "$destinationpool/$fsname" >/dev/null 2>&1; then
+  for snap in $(zfs list -rH -t snapshot "$destinationpool/$fsname" |
+                  sed 's,.*@,,' | awk '{print$1}'); do
+    if zfs list -rH -t snapshot "$fs" | sed 's,.*@,,' | awk '{print$1}' | grep "^${snap}$" >/dev/null 2>&1; then
+      common=$snap
+    fi
+  done
+fi
+
+base=$common
+foundcommon=false
+if [ -z "$common" ]; then
+  foundcommon=true
+fi
+
+for snap in $(zfs list -rH -t snapshot "$fs" |
+                sed 's,.*@,,' | awk '{print$1}'); do
+  if [ "$snap" = "$common" ]; then
+    foundcommon=true
+    continue
+  fi
+
+  if $foundcommon; then
+    if [ -z "$base" ]; then
+      echo >&2 "Sending '$1/$snap'"
+      zfs send "$1@$snap" > $tmpdir/zfs.part
+      mv $tmpdir/zfs.part $tmpdir/zfs.data
+      zfs recv "$destinationpool/$fsname" < $tmpdir/zfs.data
+      rm -f $tmpdir/zfs.data
+      zfs set readonly=on "$destinationpool"
+      zfs set atime=off "$destinationpool"
+      zfs set sharenfs=off "$destinationpool"
+      zfs set mountpoint=legacy "$destinationpool"
+      zfs unmount "$destinationpool/$fsname"
+      zfs rollback "$destinationpool/$fsname@$snap"
+    else
+      echo >&2 "Sending '$1@$base' -> '$1/$snap'"
+      zfs send -i "$1@$base" "$1@$snap" > $tmpdir/zfs.part
+      mv $tmpdir/zfs.part $tmpdir/zfs.data
+      zfs recv "$destinationpool/$fsname" < $tmpdir/zfs.data
+      rm -f $tmpdir/zfs.data
+      # zfs unmount "$destinationpool/$fsname"
+      # zfs rollback "$destinationpool/$fsname@$snap"
+    fi
+    base=$snap
+  fi
+done
+
+true