aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xzfs-squash-datasets.sh183
1 files changed, 64 insertions, 119 deletions
diff --git a/zfs-squash-datasets.sh b/zfs-squash-datasets.sh
index 3623edf..0e6d602 100755
--- a/zfs-squash-datasets.sh
+++ b/zfs-squash-datasets.sh
@@ -1,139 +1,84 @@
1#!/usr/local/bin/bash 1#!/bin/sh
2# This script expects a source dataset and a target dataset as arguments. It
3# will then copy each snapshot of the source dataset into the target as a
4# subfolder with the name of the dataset. This is being archived by cloning the
5# source-dataset first into a sub-dataset of the source dataset named after the
6# snapshot. From there rsync is used to copy files into the target location.
7# After that finishes a snapshot will be created in the target with the original
8# snapshot-name and a suffix, which is the last part of the source dataset.
9#
10# Known Bugs:
11#
12# * This script will immidiatly stop when any command returns an error and it
13# will not clean up automatically the clones it created. But these clones are
14# not promoted by the script and can be deleted without altering the source
15# dataset and in order to re-execute the script after fixing the reason for the
16# error.
17#
18# * There is currently no sanity checking. Please do not copy files from a
19# dataset, which is a child of the source dataset.
2 20
3# DO NOT USE THIS SCRIPT 21#
4# It is in a state of hot garbage and will probably be deleted soon 22# Example:
23#
24# ./zfs-squash-datasets.sh zpool/software zpool/projects
25#
26# would create a folder /zpool/projects/software within the dataset
27# zpool/projects
5 28
6 29
7print_help_and_exit() {
8 echo "Please run this program with a source and a target dataset name as arguments"
9 echo "e.g. $0 zpool/dataset1 zpool/dataset2"
10 exit
11}
12
13SIMULATION=0
14 30
15args=$(getopt n $*) 31set -o errexit
16test $? == 0 || print_help_and_exit
17 32
18set -- $args 33getMountPoint() {
34 zfs get -H -o value mountpoint "$1" ||\
35 (echo "mountpoint for dataset '$1' not found" ; exit 1)
36}
19 37
20while :; do 38listAllSnapshots() {
21 sleep 1 39 zfs list -H -o name -tsnap -r "$1" ||\
22 echo $1 40 (echo "Could not find snapshots under $1" ; exit 2)
23 case "$1" in 41}
24 --) shift; break ;;
25 -n) SIMULATION=1 ; shift ;;
26 esac
27done
28 42
29DATASET_ROOT=$1 43DATASET=$1
44DATASET_NAME=`basename $DATASET`
30DATASET_TARGET=$2 45DATASET_TARGET=$2
46TARGET_PATH=`basename $DATASET_TARGET`
31 47
32if ! zfs get mountpoint $DATASET_TARGET >/dev/null; then 48MP_TARGET=`getMountPoint $2`
33 echo "Dataset $DATASET_TARGET does not exist and will be created."
34 echo "You have 10 seconds to abort this with CTRL-C"
35 sleep 10
36 if ! $SIMULATION; then
37 zfs create $DATASET_TARGET
38 fi
39fi
40
41if zfs get -H -t filesystem -o name,value -r snapdir zeus/data/projects | grep -v visible >/dev/null; then
42 echo Please make all snapdirs visible:
43 zfs get -H -t filesystem -o name,value -r snapdir zeus/data/projects
44 exit 2
45fi
46
47
48MOUNTPOINT_TARGET=$(zfs get -o value -H mountpoint $DATASET_TARGET)
49test ${SIMULATION:0} = 1 && test $MOUNTPOINT_TARGET == "" && MOUNTPOINT_TARGET="datapool/simulation"
50
51test -d "$MOUNTPOINT_TARGET" || print_help_and_exit
52
53# make sure, that ctrl-c also works in the inner loops
54trap exit INT
55
56# recursively contains all snapshot names (the part after the @) from root on, sorted and only once
57SNAPSHOTS=$(zfs list -H -oname -tsnap -r $DATASET_ROOT | cut -d@ -f2 | sort -u)
58DATASETS=$(zfs list -H -oname -t filesystem -r $DATASET_ROOT)
59 49
60test "${SIMULATION:-0}" = 1 && echo "SIMULATION MODE" 50test -d "$MP_TARGET" || (echo "failed to find mountpoint for $DATASET_TARGET: Its not $MP_TARGET" ; exit 3)
61 51
62$DATASETS 52for i in `listAllSnapshots $DATASET`; do
53 SNAPSHOT=`echo "$i" | cut -d@ -f2`
63 54
55 CLONETMP="$DATASET/$SNAPSHOT"
64 56
65echo "We are going to flatten the hierachy of this dataset:" 57 zfs clone "$i" "$CLONETMP" ||\
66printf "\t%s\n" $DATASETS 58 (echo "Failed to create clone" ; exit 4)
67echo -e "into the dataset with the name:"
68echo -e "\t$DATASET_TARGET"
69echo -e "mounted under:"
70echo -e "\t$MOUNTPOINT_TARGET"
71echo -e "with the following snapshots:"
72printf "\t%s\n" $SNAPSHOTS
73echo ""
74echo "You have 10 seconds to abort this with CTRL-C"
75sleep 10
76 59
77set -- $SNAPSHOTS 60 MP_CLONE=`getMountPoint $CLONETMP`
78SNAPSHOTS_TOTAL=$#
79SNAPSHOT_CURRENT=0
80 61
81set -- $DATASETS 62 test -d "$MP_CLONE" ||\
82shift 63 (echo "$CLONETMP: clones mount point not found" ; exit 5)
83SUBDATASETS=$@
84 64
85for SNAPSHOT in $SNAPSHOTS; do
86 echo ""
87 echo ""
88 SNAPSHOT_CURRENT=$((SNAPSHOT_CURRENT + 1))
89 echo "[$SNAPSHOT_CURRENT/$SNAPSHOTS_TOTAL] Next snapshot is: $SNAPSHOT"
90 65
91 for DATASET in $SUBDATASETS; do 66 # copy all files to the target location
67 (set -x ; rsync -ahox --info=progress2 --delete "$MP_CLONE/" "$MP_TARGET/$TARGET_PATH" ||\
68 (echo "rsync exit status was $?" ; exit 6)
69 )
70
71 (set -x ; zfs snap "$DATASET_TARGET@$SNAPSHOT-$DATASET_NAME" ||\
72 (echo "failed to create a snapshot $DATASET_TARGET: $SNAPSHOT-$DATASET_NAME" ; exit 7)
73 )
92 74
93 DATASET_RELATIVE=${DATASET##$DATASET_ROOT} 75 # move clone out of the source dataset, so that we can resume
76 # zfs promote "$CLONETMP" ||\
77 # (echo "Failed to promote clone '$CLONETMP'" ; exit 8)
78 zfs destroy "$CLONETMP" ||\
79 (echo "Failed to destroy clone '$CLONETMP'" ; exit 8)
94 80
95 MOUNTPOINT=$(zfs get -H -o value mountpoint $DATASET) 81 # for better readability add two blank lines...
96 echo "RELATIVE=$DATASET_RELATIVE" 82 echo
97 83 echo
98 if [[ $MOUNTPOINT == "legacy" ]]; then
99 MOUNTPOINT=$(findmnt -n -o target $DATASET)
100 if test -d $MOUNTPOINT; then
101 >&2 echo "Dataset has a legacy mountpoint, but was not found in the fstab: $DATASET"
102 continue
103 fi
104 fi
105
106 if [[ $MOUNTPOINT == "-" ]]; then
107 >&2 echo "Dataset does not have a mount point or is a vdev: $DATASET"
108 continue
109 fi
110
111 SNAPDIR=$MOUNTPOINT/.zfs/snapshot/$SNAPSHOT
112 if test -d $SNAPDIR; then
113
114 TARGET_DIR="${MOUNTPOINT_TARGET}${DATASET_RELATIVE}"
115
116 echo -e \\trsync --info=stats3 --delete -a "$SNAPDIR/" "$TARGET_DIR/"
117 if test "${SIMULATION:-0}" = 0; then
118
119 mkdir -p "$TARGET_DIR"
120
121 if ! rsync --info=stats3 --delete -a "$SNAPDIR/" "$TARGET_DIR/" ; then
122 >&2 echo "rsync could not entirely copy from $SNAPDIR to $DATASET_TARGET"
123 fi
124 fi
125 else
126 echo -e "\tSKIP: Snapshot $SNAPSHOT does not exist for dataset $DATASET"
127 fi
128 done
129
130 NEW_SNAPSHOT="$DATASET_TARGET@$SNAPSHOT"
131 echo ""
132 echo -e "\t> Concluding snapshot: ${NEW_SNAPSHOT} <"
133 echo ""
134
135 if test "${SIMULATION:-0}" = 0; then
136 zfs snap $NEW_SNAPSHOT
137 fi
138done 84done
139
..