diff --git a/backup_data.zsh b/backup_data.zsh index 3b3e043..20d3fba 100755 --- a/backup_data.zsh +++ b/backup_data.zsh @@ -1,107 +1,158 @@ #!/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" # --------- Собираем данные ---------- -backup_server="192.168.0.162" +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}) # Конвертирование в массив -srcpool="main_pool/" -inc_snapshot="manual-$(date +%Y%m%d)" - -# Словарь, содержащий списки резервируемых датасетов -declare -A datasets -# ["${srcpool}3D_Design"]="NOD/Backup/3D_Design" -# ["${srcpool}Backups"]="NOD/Backup/Backups" -# ["${srcpool}Documents"]="NOD/Backup/Documents" -# ["${srcpool}Family"]="NOD/Backup/Family" -# ["${srcpool}FTP_users"]="NOD/Backup/FTP_users" -datasets=( - ["${srcpool}Media"]="NOD/Backup/Media" - ["${srcpool}NOD/GenShtab"]="NOD/GenShtab" - ["${srcpool}NOD/MinDiz"]="NOD/MinDiz" - ["${srcpool}NOD/nod34"]="NOD/nod34" - ["${srcpool}NOD/nod34_secret"]="NOD/nod34_secret" - ["${srcpool}PhotoWork"]="NOD/Backup/PhotoWork" -) +# Высчитать что на что резервировать +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_local_snaps -declare -A last_backup_snaps -pre_snapshots=() -for ds in ${(k)datasets}; do - tmp="$(zfs list -r -t snapshot -H -o name ${srcpool::-1} | grep ${ds} | grep "manual" | egrep -o '@.+' | tail -n1)" - tmp=${tmp:1} - if [ "$tmp" = "$inc_snapshot" ]; then - read "del_snapshot?Датасет $ds уже имеет последний снимок $inc_snapshot. Удалить? (y/N)" - if [ "$del_snapshot" = "y" ]; then - zfs destroy "${ds}@${tmp}" - tmp="$(zfs list -r -t snapshot -H -o name ${srcpool::-1} | grep ${ds} | grep "manual" | egrep -o '@.+' | tail -n1)" - pre_snapshots[$ds]="${tmp:1}" +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 - # Если не удалять, то просто исключить из резервирования - unset "datasets[$ds]" + # Если на удалённой машине нет такого снимка, то предложить удалить. + 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 - pre_snapshots[$ds]=$tmp + # добавить в список снапшотов + last_loc_snaps[$loc_ds]=$loc_snap + # last_bak_snaps[$bak_ds]=$bak_snap # не нужно, нигде не используется fi done - - # --------- Запрашиваем подтверждение пользователя ---------- -echo -echo "Резервирование на $backup_server" +echo " " echo "Список резервируемых датасетов:" -for key in ${(k)datasets}; do - echo "$key \t ${pre_snapshots[$key]} \t -> \t${datasets[$key]}" +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/N) " +# --------- Выбор датасетов ---------- + +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" ] -then +if [[ $work = "y" || $work = "Y" ]]; then TS0=$(date +%s) LOGFILE="log_${inc_snapshot}_backup.txt" echo "Результат работы записываю в файл $LOGFILE" - echo "Список резервируемых датасетов:" >> $LOGFILE - for key in ${(k)datasets}; do - echo "$key \t ${pre_snapshots[$key]} \t -> \t${datasets[$key]}" >> $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 + exec > >(tee $LOGFILE) # stdout replaced with file - echo "------------------------\nСоздаю снимки @${inc_snapshot}\n" - TS1=$(date +%s) - for ds in ${(k)datasets}; do - echo " * ${ds}"; - zfs snapshot ${ds}@${inc_snapshot}; - done - TS2=$(date +%s) - echo "Готово за: $(date -d@$(($TS2-$TS1)) -u +'%H:%M.%S')" - echo "------------------------\nРезервирую данные\n" - for ds in ${(k)datasets}; do + echo "\n--- Резервирую данные ---" + for loc_ds bak_ds in ${(kv)work_datasets}; do TS1=$(date +%s) - echo " * Start sending ${ds} at $(date +'%Y.%m.%d %H:%M.%S')" - zfs send -V -i ${ds}@${pre_snapshots[$ds]} ${ds}@${inc_snapshot} | ssh "root@${backup_server}" zfs receive ${datasets[$ds]}@${inc_snapshot} + 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 "Закончил ${ds} за: $(date -d@$(($TS2-$TS1)) -u +%H:%M:%S)" - done & + 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" - # echo -ne "$(ps -u | grep "send" | grep -v "grep" | sed -r "s/(.*) zfs: (.*)/\2/")\033[0K\r" sleep 60 done echo -e - exec >> >(tee $LOGFILE) - echo "------------------------\nВсе завершено за $(date -d@$(($TS2-$TS0)) -u +%H:%M.%S)" + exec > >(tee $LOGFILE) + echo "\n---Все завершено за $(date -d@$(($TS2-$TS0)) -u +%H:%M.%S) ---" exec 1>&6 6>&- # Restore stdout and close file descriptor #6.