Update bashclub-zfs

This commit is contained in:
cpzengel 2022-01-31 16:49:26 +01:00 committed by GitHub
parent 3f681bce20
commit 6830ddf05f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -63,216 +63,220 @@ ZFS() {
fi fi
} }
### (
### defaults flock -n 9 || exit 1
### ###
tag="$prog" ### defaults
dateopts="+%F_%T" ###
keep=5 tag="$prog"
verbose=false dateopts="+%F_%T"
quiet=false keep=5
tossh=false verbose=false
fromssh=false quiet=false
port=22 tossh=false
sshcompress="" fromssh=false
reinit="" port=22
intermediate="-i " sshcompress=""
### reinit=""
### parse options intermediate="-i "
### ###
while getopts "hvqk:p:t:d:srCIRg:" opt ; do ### parse options
case $opt in ###
h) usage 0 ;; while getopts "hvqk:p:t:d:srCIRg:" opt ; do
v) case $opt in
verbose=true h) usage 0 ;;
send_opts="-v" v)
recv_opts="-v" verbose=true
;; send_opts="-v"
q) quiet=true ;; recv_opts="-v"
k) keep=$OPTARG ;; ;;
p) port=$OPTARG ;; q) quiet=true ;;
t) tag=$OPTARG ;; k) keep=$OPTARG ;;
d) dateopts=$OPTARG ;; p) port=$OPTARG ;;
s) tossh=true ;; t) tag=$OPTARG ;;
r) fromssh=true ;; d) dateopts=$OPTARG ;;
C) sshcompress="-C " ;; s) tossh=true ;;
R) reinit="-R " ;; r) fromssh=true ;;
I) intermediate="-I " ;; C) sshcompress="-C " ;;
g) gpgid="$OPTARG" ;; R) reinit="-R " ;;
*) usage 1 ;; I) intermediate="-I " ;;
esac g) gpgid="$OPTARG" ;;
done *) usage 1 ;;
shift $((OPTIND-1)) esac
date="$(date $dateopts)" done
$tossh && $fromssh && die 1 "-s and -r are mutually exclusive" shift $((OPTIND-1))
if ! $tossh && [[ -n $gpgid ]] ; then date="$(date $dateopts)"
die 1 "-g can only be used with -s" $tossh && $fromssh && die 1 "-s and -r are mutually exclusive"
fi if ! $tossh && [[ -n $gpgid ]] ; then
die 1 "-g can only be used with -s"
fi
### ###
### parse src & dest host/fs info ### parse src & dest host/fs info
### ###
# fail if there's ever >1 colon # fail if there's ever >1 colon
if [[ $1 =~ :.*: || $2 =~ :.*: ]] ; then if [[ $1 =~ :.*: || $2 =~ :.*: ]] ; then
die 1 "invalid fsspec: '$1' or '$2'" die 1 "invalid fsspec: '$1' or '$2'"
fi fi
# fail if src or dest isn't specified # fail if src or dest isn't specified
if [[ -z $1 || -z $2 ]] ; then if [[ -z $1 || -z $2 ]] ; then
usage 1 usage 1
fi fi
src="$1" src="$1"
dest="$2" dest="$2"
### ###
### ssh mode - output snaps from local fs to ssh or read snaps from ssh to local fs ### ssh mode - output snaps from local fs to ssh or read snaps from ssh to local fs
if $tossh ; then if $tossh ; then
log "sending from local zfs filesystem to SSH server" log "sending from local zfs filesystem to SSH server"
# make sure src exists # make sure src exists
if [[ $src =~ : ]] ; then if [[ $src =~ : ]] ; then
die 1 "$src must be a local zfs filesystem" die 1 "$src must be a local zfs filesystem"
elif [[ $(ZFS "" list -H -o name "$src" 2>/dev/null) != $src ]] ; then elif [[ $(ZFS "" list -H -o name "$src" 2>/dev/null) != $src ]] ; then
die 1 "$src must be a local zfs filesystem" die 1 "$src must be a local zfs filesystem"
fi fi
# split dest to components # split dest to components
if [[ $dest =~ : ]] ; then if [[ $dest =~ : ]] ; then
desthost="${dest%:*}" desthost="${dest%:*}"
destpath="${dest#*:}" destpath="${dest#*:}"
else else
die 1 "$dest must be ssh host:path" die 1 "$dest must be ssh host:path"
fi fi
# get the last src component # get the last src component
srcbase="${src##*/}" srcbase="${src##*/}"
### ###
### create new snapshot on src ### create new snapshot on src
### ###
snap="${tag}_$date" snap="${tag}_$date"
cur="$src@$snap" cur="$src@$snap"
ZFS "$srchost" snapshot -r "$cur" || die $? "zfs snapshot failed" ZFS "$srchost" snapshot -r "$cur" || die $? "zfs snapshot failed"
### ###
### get newest snapshot on dest - it must exist on src ### get newest snapshot on dest - it must exist on src
### ###
#last="$(ZFS "$desthost" list -d 1 -t snapshot -H -S creation -o name $destfs/$srcbase | head -n1 | cut -f2 -d@)" #last="$(ZFS "$desthost" list -d 1 -t snapshot -H -S creation -o name $destfs/$srcbase | head -n1 | cut -f2 -d@)"
last="$(ssh "$desthost" zfslast)" last="$(ssh "$desthost" zfslast)"
### ###
### send ### send
### ###
# refuse to send without a valid .last maker # refuse to send without a valid .last maker
if [[ -z $last ]] ; then if [[ -z $last ]] ; then
die 1 "ssh path contains no .last file" die 1 "ssh path contains no .last file"
# special case: tagged snapshots exist on dest, but src has rotated through all # special case: tagged snapshots exist on dest, but src has rotated through all
elif ! ZFS "$srchost" list $src@$last &>/dev/null ; then elif ! ZFS "$srchost" list $src@$last &>/dev/null ; then
die 1 "no incremental path from from $src to $dest" die 1 "no incremental path from from $src to $dest"
# normal case: send incremental # normal case: send incremental
else else
log "sending $([[ -n $gpgid ]] && echo "encrypted ")incremental snapshot from $src to $dest (${last#${tag}_}..${cur#*@${tag}_})" log "sending $([[ -n $gpgid ]] && echo "encrypted ")incremental snapshot from $src to $dest (${last#${tag}_}..${cur#*@${tag}_})"
#ZFS "$srchost" send $send_opts -R $intermediate "$last" "$cur" | ZFS "$desthost" receive $recv_opts -Fue "$destfs" || die $? "zfs incremental send failed" #ZFS "$srchost" send $send_opts -R $intermediate "$last" "$cur" | ZFS "$desthost" receive $recv_opts -Fue "$destfs" || die $? "zfs incremental send failed"
if [[ -n $gpgid ]] ; then if [[ -n $gpgid ]] ; then
ZFS "$srchost" send $send_opts -R $intermediate "$last" "$cur" \ ZFS "$srchost" send $send_opts -R $intermediate "$last" "$cur" \
| gpg --trust-model always --encrypt --recipient "$gpgid" \ | gpg --trust-model always --encrypt --recipient "$gpgid" \
| ssh "$desthost" zfswrite "${tag}_$date.zfssnap.gpg" \ | ssh "$desthost" zfswrite "${tag}_$date.zfssnap.gpg" \
|| die $? "zfs incremental send failed" || die $? "zfs incremental send failed"
ssh "$desthost" zfslast "$snap" ssh "$desthost" zfslast "$snap"
else else
ZFS "$srchost" send $send_opts -R $intermediate "$last" "$cur" \ ZFS "$srchost" send $send_opts -R $intermediate "$last" "$cur" \
| ssh "$desthost" zfswrite "${tag}_$date.zfssnap" \ | ssh "$desthost" zfswrite "${tag}_$date.zfssnap" \
|| die $? "zfs incremental send failed" || die $? "zfs incremental send failed"
ssh "$desthost" zfslast "$snap" ssh "$desthost" zfslast "$snap"
fi fi
fi fi
exit exit
elif $fromssh ; then elif $fromssh ; then
log "receving from SSH server to local zfs filesystem" log "receving from SSH server to local zfs filesystem"
# make sure dest exists # make sure dest exists
if [[ $dest =~ : ]] ; then if [[ $dest =~ : ]] ; then
die 1 "$dest must be a local zfs filesystem" die 1 "$dest must be a local zfs filesystem"
elif [[ $(ZFS "" list -H -o name "$dest" 2>/dev/null) != $dest ]] ; then elif [[ $(ZFS "" list -H -o name "$dest" 2>/dev/null) != $dest ]] ; then
die 1 "$dest must be a local zfs filesystem" die 1 "$dest must be a local zfs filesystem"
fi fi
# split src into components # split src into components
if [[ $src =~ : ]] ; then if [[ $src =~ : ]] ; then
srchost="${src%:*}" srchost="${src%:*}"
srcpath="${src#*:}" srcpath="${src#*:}"
else else
die 1 "$src must be ssh host:path" die 1 "$src must be ssh host:path"
fi fi
### ###
### receive ### receive
### ###
log "receiving incremental snapshot from $src to $dest" log "receiving incremental snapshot from $src to $dest"
#ZFS "$srchost" send $send_opts -R $intermediate "$last" "$cur" | ZFS "$desthost" receive $recv_opts -Fue "$destfs" || die $? "zfs incremental send failed" #ZFS "$srchost" send $send_opts -R $intermediate "$last" "$cur" | ZFS "$desthost" receive $recv_opts -Fue "$destfs" || die $? "zfs incremental send failed"
for file in $(ssh "$srchost" zfsfind | sort) ; do for file in $(ssh "$srchost" zfsfind | sort) ; do
log "receiving $file from $srchost" log "receiving $file from $srchost"
if [[ $file =~ \.gpg$ ]] ; then if [[ $file =~ \.gpg$ ]] ; then
ssh "$srchost" zfsget "$file" | gpg | ZFS "$desthost" receive $recv_opts -Fue "$dest" \ ssh "$srchost" zfsget "$file" | gpg | ZFS "$desthost" receive $recv_opts -Fue "$dest" \
&& ssh "$srchost" rm "$file" && ssh "$srchost" rm "$file"
else else
ssh "$srchost" zfsget "$file" | ZFS "$desthost" receive $recv_opts -Fue "$dest" \ ssh "$srchost" zfsget "$file" | ZFS "$desthost" receive $recv_opts -Fue "$dest" \
&& ssh "$srchost" rm "$file" && ssh "$srchost" rm "$file"
fi fi
done done
exit exit
fi fi
# discard anything before a colon to get the fs # discard anything before a colon to get the fs
srcfs="${src#*:}" srcfs="${src#*:}"
destfs="${dest#*:}" destfs="${dest#*:}"
# iff there is a colon, discard everything after it to get the host # iff there is a colon, discard everything after it to get the host
[[ $src =~ : ]] && srchost="${src%:*}" [[ $src =~ : ]] && srchost="${src%:*}"
[[ $dest =~ : ]] && desthost="${dest%:*}" [[ $dest =~ : ]] && desthost="${dest%:*}"
# get the last src component # get the last src component
srcbase="${srcfs##*/}" srcbase="${srcfs##*/}"
# ensure the destination fs exists before proceeding # ensure the destination fs exists before proceeding
if [[ $(ZFS "$desthost" list -H -o name "$destfs" 2>/dev/null) != $destfs ]] ; then if [[ $(ZFS "$desthost" list -H -o name "$destfs" 2>/dev/null) != $destfs ]] ; then
die 1 "destination fs '$destfs' doesn't exist" die 1 "destination fs '$destfs' doesn't exist"
fi fi
### ###
### create new snapshot on src ### create new snapshot on src
### ###
cur="$srcfs@${tag}_$date" cur="$srcfs@${tag}_$date"
ZFS "$srchost" snapshot -r "$cur" || die $? "zfs snapshot failed" ZFS "$srchost" snapshot -r "$cur" || die $? "zfs snapshot failed"
### ###
### get newest snapshot on dest - it must exist on src ### get newest snapshot on dest - it must exist on src
### ###
last="$(ZFS "$desthost" list -d 1 -t snapshot -H -S creation -o name $destfs/$srcbase | head -n1 | cut -f2 -d@)" last="$(ZFS "$desthost" list -d 1 -t snapshot -H -S creation -o name $destfs/$srcbase | head -n1 | cut -f2 -d@)"
### ###
### send & receive ### send & receive
### ###
# 1st time: send full snapshot # 1st time: send full snapshot
if [[ -z $last ]] ; then if [[ -z $last ]] ; then
log "sending full recursive snapshot from $src to $dest" log "sending full recursive snapshot from $src to $dest"
ZFS "$srchost" send $send_opts $reinit "$cur" | ZFS "$desthost" receive $recv_opts -Fue "$destfs" || die $? "zfs full send failed" ZFS "$srchost" send $send_opts $reinit "$cur" | ZFS "$desthost" receive $recv_opts -Fue "$destfs" || die $? "zfs full send failed"
# special case: tagged snapshots exist on dest, but src has rotated through all # special case: tagged snapshots exist on dest, but src has rotated through all
elif ! ZFS "$srchost" list $srcfs@$last &>/dev/null ; then elif ! ZFS "$srchost" list $srcfs@$last &>/dev/null ; then
die 1 "no incremental path from from $src to $dest" die 1 "no incremental path from from $src to $dest"
# normal case: send incremental # normal case: send incremental
else else
log "sending incremental snapshot from $src to $dest (${last#${tag}_}..${cur#*@${tag}_})" log "sending incremental snapshot from $src to $dest (${last#${tag}_}..${cur#*@${tag}_})"
ZFS "$srchost" send $send_opts -R $intermediate "$last" "$cur" | ZFS "$desthost" receive $recv_opts -Fue "$destfs" || die $? "zfs incremental send failed" ZFS "$srchost" send $send_opts -R $intermediate "$last" "$cur" | ZFS "$desthost" receive $recv_opts -Fue "$destfs" || die $? "zfs incremental send failed"
fi fi
### ###
### clean up old snapshots ### clean up old snapshots
### ###
for snap in $(ZFS "$srchost" list -d 1 -t snapshot -H -S creation -o name $srcfs \ for snap in $(ZFS "$srchost" list -d 1 -t snapshot -H -S creation -o name $srcfs \
| grep -F "@${tag}_" | cut -f2 -d@ | tail -n+$((keep+1)) ) ; | grep -F "@${tag}_" | cut -f2 -d@ | tail -n+$((keep+1)) ) ;
do do
ZFS "$srchost" destroy -r $srcfs@$snap ZFS "$srchost" destroy -r $srcfs@$snap
done done
) 9>/var/lock/bashclub-zfs.lock