nas_scripts/backup_data.zsh

160 lines
7.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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