160 lines
7.1 KiB
Bash
Executable File
160 lines
7.1 KiB
Bash
Executable File
#!/bin/zsh
|
||
|
||
backup_server="192.168.0.162" # Сервер для резервирования
|
||
inc_snapshot="manual-$(date +%Y%m%d)" # Новый создаваемый снисмок
|
||
|
||
echo -ne "Обработка аргументов...\033[0K\r"
|
||
|
||
if [[ $# == 1 ]]; then
|
||
backup_server=$1
|
||
fi
|
||
if [[ $# == 2 ]]; then
|
||
backup_server=$1
|
||
inc_snapshot=$2
|
||
fi
|
||
if [[ $# > 2 ]]; then
|
||
echo "Ожидается 0 или 2 аргумента: backup_server или ни одного для сервера по умолчанию: $backup_server"
|
||
exit
|
||
fi
|
||
echo "Резервирование на $backup_server"
|
||
echo "Снимок файловой системы $inc_snapshot"
|
||
# --------- Собираем данные ----------
|
||
|
||
echo -ne "Собираю данные...\033[0K\r"
|
||
# Получить список датасетов на локальной машине
|
||
local_datasets=$(zfs list -o name | grep -v -e ix-app -e boot-pool -e system-data -e jails | grep /) # Отрезаем всё лишнее, отделяем нужные данные по 4 пробелам в выводе.
|
||
local_datasets=(${=local_datasets}) # Конвертирование в массив
|
||
# Получить список датасетов на удалённой машине
|
||
backup_datasets=$(ssh root@$backup_server zfs list -o name | grep -v -e ix-app -e boot-pool -e system-data -e jails | grep /) # Отрезаем всё лишнее, отделяем нужные данные по 4 пробелам в выводе.
|
||
backup_datasets=(${=backup_datasets}) # Конвертирование в массив
|
||
|
||
# Высчитать что на что резервировать
|
||
declare -A work_datasets
|
||
for ((i = 1; i <= ${#local_datasets}; i++)); do
|
||
for ((j = 1; j <= ${#backup_datasets}; j++)); do
|
||
a=$(sed -r "s/(.*)\/(.*)/\2/" <<<"$local_datasets[i]") # Определить нижний по иерархии
|
||
b=$(sed -r "s/(.*)\/(.*)/\2/" <<<"$backup_datasets[j]") # тоже дял второго
|
||
if [[ "$a" == "$b" ]]; then
|
||
work_datasets[${local_datasets[i]}]=${backup_datasets[j]}
|
||
fi
|
||
done
|
||
done
|
||
|
||
# Словарь со списком последких снапшотов для датасетов
|
||
declare -A last_loc_snaps
|
||
declare -A last_bak_snaps
|
||
echo -ne "Проверяю на наличие $inc_snapshot...\033[0K\r"
|
||
for loc_ds bak_ds in ${(kv)work_datasets}; do
|
||
# Запросить последние снимки
|
||
loc_snap="$(zfs list -t snapshot -o name ${loc_ds} | grep "manual" | tail -n1 | egrep -o '@.+' )"
|
||
loc_snap=${loc_snap:1} # удалить @ в начале строки
|
||
bak_snap="$(ssh root@$backup_server zfs list -t snapshot -o name ${bak_ds} | grep "manual" | tail -n1 | egrep -o '@.+' )"
|
||
bak_snap=${bak_snap:1} # удалить @ в начале строки
|
||
|
||
# Если последний снимок совпадает с сегодняшним
|
||
if [[ "$loc_snap" = "$inc_snapshot" ]]; then
|
||
# Если последние снимки на обеих машинах совпадают
|
||
if [[ "$loc_snap" = "$bak_snap" ]]; then
|
||
echo "${loc_ds}@${loc_snap} существует на обеих машинах. $loc_ds Исключено из списка"
|
||
unset "work_datasets[$loc_ds]"
|
||
else
|
||
# Если на удалённой машине нет такого снимка, то предложить удалить.
|
||
read "del_snapshot?Датасет $loc_ds уже имеет последний снимок $inc_snapshot. Удалить? (y/N)"
|
||
if [ "$del_snapshot" = "y" ]; then
|
||
zfs destroy "${loc_ds}@${loc_snap}"
|
||
loc_snap="$(zfs list -t snapshot -o name ${loc_ds} | grep "manual" | tail -n1 | egrep -o '@.+' )"
|
||
loc_snap="${loc_snap:1}"
|
||
last_loc_snaps[$loc_ds]=$loc_snap
|
||
# last_bak_snaps[$bak_ds]=$bak_snap # не нужно, нигде не используется
|
||
else
|
||
# Если не удалять, то просто исключить из резервирования
|
||
echo "$ds Исключено из списка"
|
||
unset "work_datasets[$ds]"
|
||
fi
|
||
fi
|
||
else
|
||
# добавить в список снапшотов
|
||
last_loc_snaps[$loc_ds]=$loc_snap
|
||
# last_bak_snaps[$bak_ds]=$bak_snap # не нужно, нигде не используется
|
||
fi
|
||
done
|
||
|
||
|
||
# --------- Запрашиваем подтверждение пользователя ----------
|
||
|
||
echo " "
|
||
echo "Список резервируемых датасетов:"
|
||
i=0; for loc_ds bak_ds in ${(kv)work_datasets}; do
|
||
i=$((i+1))
|
||
echo "$i - $loc_ds \t ${last_loc_snaps[$loc_ds]} \t -> \t${bak_ds}"
|
||
done
|
||
echo
|
||
echo "Инкрементный снимок:\t$inc_snapshot"
|
||
|
||
# --------- Выбор датасетов ----------
|
||
|
||
read "work?Утвердите данные. В работу (y), или перечислить через пробел номера, НЕТ по умолчанию? "
|
||
if [[ $work = *[[:digit:]]* ]]; then
|
||
indx_list=(${=work})
|
||
# Если нет индекса в списке, то удалить из словаря для резервирования
|
||
i=0; for loc_ds bak_ds in ${(kv)work_datasets}; do
|
||
i=$((i+1))
|
||
if [[ ${indx_list[(r)$i]} != $i ]]; then # Если нет $i в списке $indx_list
|
||
unset "work_datasets[$loc_ds]"
|
||
else
|
||
echo "$i - $loc_ds \t ${last_loc_snaps[$loc_ds]} \t -> \t${bak_ds}"
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# Если был выведен список, то запросить запуск в работу нового списка
|
||
if [[ ${#indx_list} > 0 ]]; then
|
||
work="N"
|
||
read "work?В работу (y/N) "
|
||
fi
|
||
|
||
|
||
# --------- Резервирование ----------
|
||
|
||
if [[ $work = "y" || $work = "Y" ]]; then
|
||
TS0=$(date +%s)
|
||
LOGFILE="log_${inc_snapshot}_backup.txt"
|
||
echo "Результат работы записываю в файл $LOGFILE"
|
||
|
||
# Записать в файл список резервируемых датасетов
|
||
echo "Список резервируемых датасетов:" >> "${LOGFILE}"
|
||
for loc_ds in ${(k)work_datasets}; do
|
||
echo "$key \t ${last_loc_snaps[$loc_ds]} \t -> \t${work_datasets[$loc_ds]}" >> "${LOGFILE}"
|
||
done
|
||
|
||
echo
|
||
exec 6>&1 # Saves stdout
|
||
exec > >(tee $LOGFILE) # stdout replaced with file
|
||
|
||
echo "\n--- Резервирую данные ---"
|
||
for loc_ds bak_ds in ${(kv)work_datasets}; do
|
||
TS1=$(date +%s)
|
||
echo " * snapshot ${loc_ds}@${inc_snapshot}"
|
||
zfs snapshot ${loc_ds}@${inc_snapshot};
|
||
echo " * Start sending ${loc_ds} at $(date +'%Y.%m.%d %H:%M.%S')"
|
||
zfs send -V -i ${loc_ds}@${last_loc_snaps[$loc_ds]} ${loc_ds}@${inc_snapshot} | ssh "root@${backup_server}" zfs receive ${bak_ds}@${inc_snapshot}
|
||
TS2=$(date +%s)
|
||
echo "Закончил ${loc_ds} за: $(date -d@$(($TS2-$TS1)) -u +%H:%M:%S)"
|
||
done & # Запустить резервирование в фоне
|
||
|
||
exec 1>&6 6>&-
|
||
|
||
# Выводить прогресс запрашивая список запущенныйх процессов
|
||
while pgrep -u $USER zfs >/dev/null; do
|
||
PROGR=$(ps -u | grep "send" | grep -v "grep" | sed -r "s/(.*) zfs: (.*)/\2/")
|
||
echo -ne "$PROGR\033[0K\r"
|
||
sleep 60
|
||
done
|
||
echo -e
|
||
exec > >(tee $LOGFILE)
|
||
echo "\n---Все завершено за $(date -d@$(($TS2-$TS0)) -u +%H:%M.%S) ---"
|
||
|
||
exec 1>&6 6>&- # Restore stdout and close file descriptor #6.
|
||
|
||
fi
|