Initial commit/revision of a number of ZFS scripts and utilities.
[zfs-ubuntu/.git] / zfs-autosnap
1 #!/bin/bash
2
3 # Author: Carl Baldwin & Alan Pippin
4 # Description: This script takes a snapshot of the given zfs filesystem.
5 #              It also employs an intelligent algorithm to roll off,
6 #              or destroy, old snapshots.
7
8 test=0
9
10 [ $test == 0 ] && exec >> /var/log/zfs-autosnap.log 2>&1
11
12 # This script makes the following assumptions/requirements:
13 #  * this script only handles one zfs filesystem, a wrapper should be created
14 #    to handle more
15 #  * this script handles all snapshots that are named in this format:
16 #    YYYY-MM-DD.hh.mm
17 #  * It ignores other snapshots that don't follow this naming convention
18
19 # This converts the YYYY-MM-DD.hh.mm format to an integer.
20 datetime_to_minutes() {
21   perl -n -e '/(\d+)-(\d+)-(\d+)\.(\d+)\.(\d+)/; print $1 * 527040 + $2 * 44640 + $3 * 1440 + $4 * 60 + $5,"\n"'
22 }
23
24 datetime_to_minutes2() {
25   perl -n -e '/(\d+)-(\d+)-(\d+)\.(\d+)\.(\d+)/; $monthadj=int(($2-1)/3)-1; $minadj=int($5/15); $houradj=int($4/3); print $1 * 1048576 + ( $2 + $monthadj ) * 65536 + $3 * 2048 + ( $4 + $houradj ) * 64 + $5 + $minadj,"\n"'
26 }
27
28 # test: if set to 1, no zfs snapshot commands will be run, they will only be echoed
29 # filesystem: This is the zfs filesystem to snapshot
30 # mountpoint: This is the mountpoint of the zfs filesystem to snapshot
31 # numsnapshots: This number is the number of equally spaced snapshots that should exist over any given period in the past
32 # maxagedays: This is the maximum number of days to keep any snapshot around for (0=infinite) (default=0).
33 filesystem=$1
34 mountpoint=$2
35 numsnapshots=${3-20}
36 maxagedays=${4-0}
37
38 lockdir="/tmp/zfs-admin-lock"
39 pool=`echo "$filesystem" | awk -F '/' '{ print $1 }'`
40
41 if [ -z "$filesystem" ] || [ -z "$mountpoint" ] || [ -z "$numsnapshots" ] || [ -z "$maxagedays" ]; then
42    echo "-E- Usage: $0 <filesystem> <mountpoint> <numsnapshots> <maxagedays>"
43    exit 1
44 fi
45
46 if [ ! -d "$mountpoint" ]; then
47    echo "-E- Unable to find the mountpoint: $mountpoint"
48    exit 1
49 fi
50
51 snapshotdir="${mountpoint}/.zfs/snapshot"
52 if [ ! -d "$snapshotdir" ]; then
53    echo "-E- Unable to find the snapshotdir: $snapshotdir"
54    exit 1
55 fi
56
57 # Check to see if this zfs pool has a scrub being performed on it now.
58 # If it does, we cannot perform any snapshot create or destroy operations.
59 zpool status $pool | grep scrub: | grep "in progress" > /dev/null 2>&1
60 if [ $? == 0 ]; then
61    echo "-W- The zfs pool '$pool' is currently being scrubbed. Skipping all snapshot operations."
62    exit 0
63 fi
64
65 # Get the various components of the date
66 datetime=${ZFSDATETIME:-$(date +%Y-%m-%d.%H.%M)}
67
68 # Create the snapshot for this minute
69 echo "-I- Creating ${filesystem}@${datetime}"
70 [ $test == 0 ] && zfs snapshot "${filesystem}@${datetime}"
71
72 minutes=$(echo $datetime | datetime_to_minutes)
73
74 # Check to ensure the zfs filesystem has not been locked down.
75 # If it has, we cannot perform any snapshot destroy operations.
76 if [ -d "$lockdir" ]; then
77    echo "-W- The zfs filesystem has been locked down. Skipping snapshot cleanup."
78    exit 0
79 fi
80
81 # Trim them down
82 snapshots=$(ls -d ${snapshotdir}/????-??-??.??.?? 2>/dev/null)
83 for snapshot in $snapshots; do
84   snapminutes=$(echo "$snapshot" | sed 's,.*/,,' | datetime_to_minutes)
85   snapminutes2=$(echo "$snapshot" | sed 's,.*/,,' | datetime_to_minutes2)
86   age=$((minutes - snapminutes))
87   window=1
88   while true; do
89     if [ $age -lt $((window * numsnapshots)) ]; then
90       case $((snapminutes2 % window)) in
91         0) ;;
92         *)
93           snapname=${filesystem}$(echo "$snapshot" |
94                        sed 's,/\(.*\)/.zfs/snapshot/\(.*\),@\2,')
95           echo "-I- Destroying $snapname"
96           [ $test == 0 ] && zfs destroy "$snapname"
97         ;;
98       esac
99       break
100     fi
101     window=$((window*2))
102   done
103   if [ $maxagedays -gt 0 ] && [ $age -gt $((maxagedays * 24 * 60)) ]; then
104     snapname=${filesystem}$(echo "$snapshot" |
105                      sed 's,/\(.*\)/.zfs/snapshot/\(.*\),@\2,')
106     echo "-I- Destroying old $snapname"
107     [ $test == 0 ] && zfs destroy "$snapname"
108   fi
109 done
110
111 true