SHOW:
|
|
- or go back to the newest paste.
| 1 | #!/bin/sh | |
| 2 | ||
| 3 | - | # btrfs-snapshots.sh |
| 3 | + | # btrfs-snapshots.sh 1.0 |
| 4 | - | # Erzeuge hourly, daily, weekly, and monthly Snapshots der btrfs Dateisysteme |
| 4 | + | # Create hourly, daily, weekly, and monthly snapshots of btrfs filesystems |
| 5 | ||
| 6 | - | # Basiert auf http://article.gmane.org/gmane.comp.file-systems.btrfs/12609 |
| 6 | + | # See https://copy.com/WI9AXqTH2nD4 or as a "subversion" of http://pastebin.com/Rz49ANTU |
| 7 | ||
| 8 | - | # 0 * * * * /usr/local/bin/snapshot hourly 6 |
| 8 | + | # Based somewhat on http://article.gmane.org/gmane.comp.file-systems.btrfs/12609 |
| 9 | - | # 0 0 * * * /usr/local/bin/snapshot daily 7 |
| 9 | + | |
| 10 | - | # 0 0 * * 0 /usr/local/bin/snapshot weekly 4 |
| 10 | + | # 00,15,30,45 * * * * /usr/local/bin/btrfs-snapshots.sh frequently 4 |
| 11 | # 38 * * * * /usr/local/bin/btrfs-snapshots.sh hourly 24 | |
| 12 | # 08 00 * * * /usr/local/bin/btrfs-snapshots.sh daily 7 | |
| 13 | - | echo "Usage: $0 SNAPSHOT_PREFIX NUM_SNAPSHOTS" |
| 13 | + | # 08 12 * * 0 /usr/local/bin/btrfs-snapshots.sh weekly 4 |
| 14 | ||
| 15 | if [ "$#" -ne 2 ]; then | |
| 16 | echo "Usage: $0 SNAPSHOT_TAG NUM_SNAPSHOTS | |
| 17 | - | SNAPSHOT_PREFIX="$1" |
| 17 | + | Create hourly, daily, weekly, and monthly snapshots of btrfs filesystems. |
| 18 | ||
| 19 | Based somewhat on http://article.gmane.org/gmane.comp.file-systems.btrfs/12609 | |
| 20 | - | #a@ewzw032:~$ sudo btrfs subv snaps -r /.backup /.backup/.blub ; sudo btrfs subv snaps -r /.backup/Syno /.backup/Syno/.blub |
| 20 | + | Here's my crontab: |
| 21 | 00,15,30,45 * * * * $0 frequently 4 | |
| 22 | - | snap_prefix=".snapshot:$SNAPSHOT_PREFIX:" |
| 22 | + | 38 * * * * $0 hourly 24 |
| 23 | - | snap_name="$snap_prefix`date +%Y-%m-%d--%H.%M.%S.%N`" |
| 23 | + | 08 00 * * * $0 daily 7 |
| 24 | 08 12 * * 0 $0 weekly 4" | |
| 25 | - | # Finde alle btrfs |
| 25 | + | |
| 26 | - | df -T | grep ' btrfs ' | perl -p -e 's,^(.*?) .* (/.*)$,$1 $2,' | while read dev mp; do |
| 26 | + | |
| 27 | - | # Find alle subvolumes dieses btrfs |
| 27 | + | |
| 28 | - | # z.B.: ID 268 gen 3176 top level 5 path foo/bar |
| 28 | + | # "Tag" to use for snapshots |
| 29 | - | # Zeige alle subvolumes und zusätzliche alle "readonly" (snapshot) |
| 29 | + | SNAPSHOT_TAG="$1" |
| 30 | - | # Volumes. Filtere dann das raus, was doppelt angezeigt wurde; das |
| 30 | + | # How many of those snapshots to keep |
| 31 | - | # sind die snapshots. Dadurch werden nur die "richtigen" Volumes |
| 31 | + | |
| 32 | - | # angezeigt. |
| 32 | + | |
| 33 | - | ( btrfs subv list -r "$mp" ; btrfs subv list "$mp" ) | sort | uniq -u | while read id Id gen Gen top level Toplevel path Path; do |
| 33 | + | # Helper veriables |
| 34 | - | # Erzeuge den Snapshot |
| 34 | + | # A "prefix" for all the snapshots |
| 35 | - | #echo "`date +%Y-%m-%d--%H.%M.%S.%N`: Erzeuge Snapshot $mp/$Path/$snap_name fuer $mp/$Path" |
| 35 | + | snap_prefix="snapshot:$SNAPSHOT_TAG:" |
| 36 | - | set -x; btrfs subv snapshot -r "$mp/$Path" "$mp/$Path/$snap_name"; set +x |
| 36 | + | # "Now" |
| 37 | snap_date="`date +%Y-%m-%d--%H.%M.%S.%N`" | |
| 38 | # Name of the script | |
| 39 | - | # Lösche alte Snapshot(s) |
| 39 | + | script_name=`basename "$0"` |
| 40 | - | # Zeige alle read only Subvolumes an. Und zusätzlich "doppelt" die letzten |
| 40 | + | |
| 41 | - | # $NUM_SNAPSHOTS Snapshots. Führt dazu, das die gewünchte Anzahl an zu |
| 41 | + | # Messages are logged to syslog |
| 42 | - | # behaltenden Snapshots doppelt angezeigt wird. |
| 42 | + | # facility: auth, authpriv (for security information of a sensitive nature), cron, daemon, ftp, kern (can't be generated from user process), lpr, mail, news, security (deprecated synonym for auth), syslog, user, uucp, and local0 to local7 |
| 43 | - | # Nun sortiere die Liste und zeige nur die an, die nur 1x vorkommen. |
| 43 | + | log_fac="local5" |
| 44 | - | # Das sind die Snapshots, die gelöscht werden sollen. |
| 44 | + | # tag |
| 45 | - | # ID 257 gen 28590 top level 256 path a |
| 45 | + | log_tag="$script_name" |
| 46 | - | # a@ewzw032:~$ ( sudo btrfs subv list -r /.backup/ | grep ' \.boing:' | tail -$n ; sudo btrfs subv list -r /.backup/ | grep ' \.boing:' ) | sort | uniq -u |
| 46 | + | |
| 47 | # Add path to btrfs-progs dev version to beginning of $PATH | |
| 48 | - | ( btrfs subv list -r "$mp" | grep " path $snap_prefix" | tail -"$n"; sudo btrfs subv list -r "$mp" | grep " path $snap_prefix" ) | sort | uniq -u | while read id Id gen Gen top level Toplevel path Path; do |
| 48 | + | # Needs a post 2013-02-01 15:55:06 (commit 64edc851da59c47b92ee6830101be0854add7f09) |
| 49 | - | #echo "`date +%Y-%m-%d--%H.%M.%S.%N`: Entferne Snapshot $mp/$Path/$snap_name fuer $mp/$Path" |
| 49 | + | # version of btrfs-progs from http://git.kernel.org/cgit/linux/kernel/git/mason/btrfs-progs.git/commit/ |
| 50 | - | set -x; btrfs subv delete "$mp/$Path"; set +x |
| 50 | + | # because of issues with "btrfs subv list" |
| 51 | btrfs_progs_dev_path="/home/a/Copy/Computerkram/Programme/btrfs-progs.dev/bin" | |
| 52 | PATH="$btrfs_progs_dev_path:$PATH" | |
| 53 | - | echo |
| 53 | + | |
| 54 | # Find all btrfs filesystems | |
| 55 | btrfs fi show 2>/dev/null | awk '/ path / {print $NF}' | while read dev; do
| |
| 56 | # Find uuid and label | |
| 57 | set -- `sudo btrfs fi show 2>/dev/null | grep -B2 " path $dev" | grep "Label:" | sed 's,.*: \(.*\) uuid: \(.*\),\1 \2,'` | |
| 58 | label="$1" | |
| 59 | uuid="$2" | |
| 60 | logger -t "$log_tag" -p "$log_fac.info" -- "Processing filesystem with label $label and uuid $uuid on $dev" | |
| 61 | # Change the "dev" name ("path") so, that it is usable; ie. / -> .
| |
| 62 | safe_dev=`echo "$dev" | tr / .` | |
| 63 | # Create a directory for the temporary mountpoint | |
| 64 | tmp_mount_dir=`mktemp -d "/tmp/.btrfs.mount.$uuid.$safe_dev.XXXXXX"` | |
| 65 | # Mount the current btrfs there | |
| 66 | if ! mount -t btrfs "$dev" "$tmp_mount_dir"; then | |
| 67 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: mount -t btrfs $dev $tmp_mount_dir" | |
| 68 | exit 1 | |
| 69 | fi | |
| 70 | ||
| 71 | # Create snapshots for the "root" volume | |
| 72 | if ! btrfs subv snaps -r "$tmp_mount_dir" "$tmp_mount_dir/,$snap_prefix$snap_date" > /dev/null; then | |
| 73 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: btrfs subv snaps -r $tmp_mount_dir $tmp_mount_dir/,$snap_prefix$snap_date" | |
| 74 | exit 1 | |
| 75 | else | |
| 76 | logger -t "$log_tag" -p "$log_fac.info" -- "Created snapshot $Path,$snap_prefix$snap_date for root volume of fs with uuid $uuid" | |
| 77 | fi | |
| 78 | # Find old, no longer wanted snapshots with the "tag" of this subvolume | |
| 79 | # - list all (read only + writable) snapshots of this filesystem | |
| 80 | # - list only those for current "path" (-> subvolume) and prefix | |
| 81 | # - list only the last $NUM_SNAPSHOTS snapshots -> those are to "survive" | |
| 82 | # - again list all snapshots of current path | |
| 83 | # - sort and list only only those, which are shown just once | |
| 84 | # - -> a list of those snapshots, which are to go | |
| 85 | (btrfs subv list -r "$tmp_mount_dir" | grep " path ,$snap_prefix" | tail -"$NUM_SNAPSHOTS"; btrfs subv list -r "$tmp_mount_dir" | grep " path ,$snap_prefix") | sort | uniq -u | while read __id IdDel __gen GenDel __top __level ToplevelDel __path PathDel; do | |
| 86 | # Delete the snapshot | |
| 87 | if ! btrfs subv del "$tmp_mount_dir/$PathDel" > /dev/null; then | |
| 88 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: btrfs subv del $tmp_mount_dir/$PathDel" | |
| 89 | exit 1 | |
| 90 | else | |
| 91 | logger -t "$log_tag" -p "$log_fac.info" -- "Removed snapshot $PathDel" | |
| 92 | fi | |
| 93 | done | |
| 94 | ||
| 95 | # List all non read-only (ie. writable) subvolumes | |
| 96 | # - create a list of ALL subvolumes (read only + writable) | |
| 97 | # - create a list of writable subvolumes | |
| 98 | # - sort those two lists | |
| 99 | # - list only those, which are show just once; ie. the read only subvolumes | |
| 100 | (btrfs subv list -ar $tmp_mount_dir; btrfs subv list -a $tmp_mount_dir) | sort | uniq -u | while read _id Id _gen Gen _top _level Toplevel _path Path; do | |
| 101 | # Create the snapshot | |
| 102 | if ! btrfs subv snaps -r "$tmp_mount_dir/$Path" "$tmp_mount_dir/$Path,$snap_prefix$snap_date" > /dev/null; then | |
| 103 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: btrfs subv snaps -r $tmp_mount_dir/$Path $tmp_mount_dir/$Path,$snap_prefix$snap_date" | |
| 104 | exit 1 | |
| 105 | else | |
| 106 | logger -t "$log_tag" -p "$log_fac.info" -- "Created snapshot $Path,$snap_prefix$snap_date for subvolume $Path" | |
| 107 | fi | |
| 108 | # Find old, no longer wanted snapshots with the "tag" of this subvolume | |
| 109 | # - list all (read only + writable) snapshots of this filesystem | |
| 110 | # - list only those for current "path" (-> subvolume) and prefix | |
| 111 | # - list only the last $NUM_SNAPSHOTS snapshots -> those are to "survive" | |
| 112 | # - again list all snapshots of current path | |
| 113 | # - sort and list only only those, which are shown just once | |
| 114 | # - -> a list of those snapshots, which are to go | |
| 115 | (btrfs subv list -r "$tmp_mount_dir" | grep " path $Path,$snap_prefix" | tail -"$NUM_SNAPSHOTS"; btrfs subv list -r "$tmp_mount_dir" | grep " path $Path,$snap_prefix") | sort | uniq -u | while read __id IdDel __gen GenDel __top __level ToplevelDel __path PathDel; do | |
| 116 | # Delete the snapshot | |
| 117 | if ! btrfs subv del "$tmp_mount_dir/$PathDel" > /dev/null; then | |
| 118 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: btrfs subv del $tmp_mount_dir/$PathDel" | |
| 119 | exit 1 | |
| 120 | else | |
| 121 | logger -t "$log_tag" -p "$log_fac.info" -- "Removed snapshot $PathDel" | |
| 122 | fi | |
| 123 | done | |
| 124 | done | |
| 125 | # Unmount the filesystem | |
| 126 | if ! umount "$tmp_mount_dir"; then | |
| 127 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: umount$tmp_mount_dir" | |
| 128 | # Not fatal | |
| 129 | # exit 1 | |
| 130 | fi | |
| 131 | # Remove the directory where the filesystem was mounted | |
| 132 | if ! rmdir "$tmp_mount_dir"; then | |
| 133 | logger -t "$log_tag" -p "$log_fac.err" -- "Error! Could not do: rmdir $tmp_mount_dir" | |
| 134 | # Not fatal | |
| 135 | # exit 1 | |
| 136 | fi | |
| 137 | done | |
| 138 | ||
| 139 | # Done ☺ | |
| 140 | exit 0 | |
| 141 | # EOF |