mirror of
https://github.com/bashclub/bashclub-zfs-push-pull.git
synced 2024-11-07 21:01:58 +01:00
Update bashclub-zfs
This commit is contained in:
parent
3f681bce20
commit
6830ddf05f
386
bashclub-zfs
386
bashclub-zfs
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user