Вечер добрый.
Когда-то кто-то интересовался перекидыванием бекапов с одной машины на
другую. Я приводил старые скрипты работающие через rsync и говорил что
работаю над тем же но используя возможности ZFS.
Вобщем кого интересует - прошу тестить. funcs выделен отдельно так как
его место ИМХО в /etc/rc.subr, а пропихивание туда патчей суть процесс
занимательный. Оба файла можно сложить в отдельную директорию а в
/usr/local/etc/periodic/daily добавить ссылку на synczfs. Превосходно
работает в паре с zfSnap, по возможности им же постараюсь сдать.
Чего оно умеет:
1. Синхронизироваться отсутствующие снепшоты.
2. Сжимать поток (опционально).
3. Откатываться на одинаковые снепшоты. Т.е. если в исходнике есть
снепшоты 1245 а на приёмнике 123 оно откатит третий и продолжит со второго.
У меня уже хост с бекапами насосал 150G и чувствует себя хорошо. И в
скрипте мне уже почти всё нравится.
--
Sphinx of black quartz judge my vow.
#!/bin/sh -
: ${daily_synczfs_enable="NO"}
: ${daily_synczfs_root=""}
: ${daily_synczfs_user="backup"}
# daily_synczfs_servers:
# space separated list of server names (resolvable)
#
# daily_synczfs_<server>_compress:
# all data exchange would be compressed with xz
#
# daily_synczfs_<server>_fs:
# space separated list of remote filesystem aliases
#
# daily_synczfs_<server>_hostname:
# full host name of remote system, if not present substituted by
<server>
#
# daily_synczfs_<server>_<alias>_rfsname:
# full zfs filesystem name on remote system
#
# daily_synczfs_<server>_<alias>_fsname:
# last part of zfs filesystem name on local system (resides in
daily_synczfs_root)
# Except configuring task you also should:
# 1. Create key (ssh-keygen -t dsa -b 1024), add it to remote
.ssh/authorized_keys and auth system in .ssh/known_hosts
# 2. On remote server allow named user to dump selected filesystem (zfs allow
user hold,send rfsname)
# If there is a global system configuration file, suck it in.
if [ -r /etc/defaults/periodic.conf ]; then
. /etc/defaults/periodic.conf
source_periodic_confs
fi
echo "Synchronizing ZFS snapshots:"
unwind=`readlink -nf N $0`
. `dirname $unwind`/funcs
if checkyesno daily_synczfs_enable; then
checknotempty daily_synczfs_servers && exit 2
checknotempty daily_synczfs_root && exit 2
if ! zfs list -Ht filesystem "$daily_synczfs_root" >/dev/null 2>&1; then
echo " \$daily_synczfs_enable is set but \$daily_synczfs_root
doesn't point to zfs filesystem."
exit 2
fi
if ! su "$daily_synczfs_user" -c : >/dev/null 2>&1; then
echo " \$daily_synczfs_enable is set but \$daily_synczfs_user
point to unknown or unavailable user."
exit 2
fi
zfs allow "$daily_synczfs_user" create,mount,receive
"$daily_synczfs_root"
for server in $daily_synczfs_servers; do
# getting hostname, default to server name
eval hostname=\"\${daily_synczfs_${server}_hostname}\"
if [ -z $hostname ]; then
hostname="$server"
fi
# check compression status
eval compress=\"\${daily_synczfs_${server}_compress="NO"}\"
pack=
unpack=
if checkyesno compress; then
pack='| xz -9e'
unpack='| xz -d'
fi
# check server access
if ! ping -oc 5 $hostname >/dev/null 2>&1; then
echo " Server $server[$hostname] is mentioned in
\$daily_synczfs_servers but can't be pinged."
exit 2
fi
eval aliases=\"\${daily_synczfs_${server}_fs}\"
for alias in $aliases; do
# check remote access
if ! su "$daily_synczfs_user" -c "ssh -oBatchMode=yes
$daily_synczfs_user@$hostname :" >/dev/null 2>&1; then
echo "Server $server is mentioned in
\$daily_synczfs_servers but provides no access."
exit 2
fi
# check remote filesystem
checknotempty daily_synczfs_${server}_${alias}_rfsname
&& exit 2
eval
rfsname=\"\${daily_synczfs_${server}_${alias}_rfsname}\"
if ! echo $rfsname | grep -q '^[a-zA-Z0-9/\._-]\+$';
then
echo " Server $server is mentioned but
\$daily_synczfs_${server}_${alias}_rfsname [$rfsname] contains banned chars."
exit 2
elif ! su "$daily_synczfs_user" -c "ssh -oBatchMode=yes
$daily_synczfs_user@$hostname \"zfs list -Ht filesystem $rfsname\"" >/dev/null
2>&1; then
echo " Server $server is mentioned but
\$daily_synczfs_${server}_${alias}_rfsname doesn't exist."
exit 2
fi
rsnaps=`mktemp -t synczfs`
su "$daily_synczfs_user" -c "ssh -oBatchMode=yes
$daily_synczfs_user@$hostname \"zfs list -Hrt snapshot -d1 $rfsname\"" | awk
'BEGIN{FS="[ @\t]+"}{print$2}' > $rsnaps
# check local filesystem
checknotempty daily_synczfs_${server}_${alias}_fsname
&& exit 2
eval
fsname=\"\${daily_synczfs_${server}_${alias}_fsname}\"
if ! echo $fsname | grep -q '^[a-zA-Z0-9\._-]\+$'; then
echo " Server $server is mentioned in
\$daily_synczfs_${server}_${alias}_fsname contains banned chars."
exit 2
elif ! zfs list -Ht filesystem
$daily_synczfs_root/$fsname >/dev/null 2>&1; then
su "$daily_synczfs_user" -c "zfs create
"$daily_synczfs_root/$fsname" ; ssh -oBatchMode=yes
$daily_synczfs_user@$hostname \"zfs send -R $rfsname@`tail -1 $rsnaps` $pack\"
$unpack | zfs receive -Fu $daily_synczfs_root/$fsname ; zfs inherit mountpoint
$daily_synczfs_root/$fsname"
else
lsnaps=`mktemp -t synczfs`
zfs list -Hrt snapshot -d1
"$daily_synczfs_root/$fsname" | awk 'BEGIN{FS="[ @\t]+"}{print$2}' > $lsnaps
common=`join $rsnaps $lsnaps | tail -1`
if [ -z "$common" ]; then
echo " $rfsname on $server shares no
snapshots with $fsname."
exit 2
else
# dropping local surplus
lastone=no
for snapshot in `cat $lsnaps`; do
if [ "_$snapshot" = "_$common"
]; then
lastone=yes
continue
fi
if [ "_$lastone" = "_yes" ];
then
zfs destroy
"$daily_synczfs_root/$fsname@$snapshot"
fi
done
fi
rm -rf $lsnaps
su "$daily_synczfs_user" -c "ssh
-oBatchMode=yes $daily_synczfs_user@$hostname \"zfs send -I $rfsname@$common
$rfsname@`tail -1 $rsnaps` $pack\" $unpack | zfs receive -F
$daily_synczfs_root/$fsname"
fi
rm -rf $rsnaps
done
done
fi
#!/bin/sh -
. /etc/rc.subr
# checknumber var
# Test $1 variable, and warn if not set to unsigned number.
# Return 0 if it's not a number, nonzero otherwise.
checknumber() {
eval _value=\$${1}
debug "checknumber: $1 is set to $_value."
case $_value in
*[!0-9]*)
warn "$1 is not set properly."
return 0
;;
*)
return 1
esac
}
# checknotempty var
# Test $1 variable and warn if it doesn't contain anything.
# Return 0 if it has no data, nonzero otherwise.
checknotempty() {
eval _value=\$${1}
debug "checknotempty: $1 is set to $_value."
if [ -z "$_value" ]; then
warn "$1 is not set properly."
return 0
else
return 1
fi
}