Изменены ключи запуска

Изменены
-rs | --remote-server
-гu | --remote-user
-ld | --local-dataset
-rd | --remote-dataset
Добавлены:
-l | --log-file
--no-progress
--no-check
--stop

#FIXME работоспособность не протестирована
This commit is contained in:
pankovea 2024-11-08 19:52:00 +00:00
parent 7658041e3d
commit 2541094bdf

View File

@ -4,27 +4,82 @@ backup_server="192.168.0.162" # Сервер для резервирования
backup_user="root" # Пользователь на сервере backup_user="root" # Пользователь на сервере
inc_snapshot="manual-$(date +%Y%m%d)" # Новый создаваемый снисмок inc_snapshot="manual-$(date +%Y%m%d)" # Новый создаваемый снисмок
incremental=false incremental=false
silent=false
stop=false
progress=true
check=true
LOGFILE="log_bak_${inc_snapshot}.log"
unset local_dataset
unset remote_dataset
echo -ne "Обработка аргументов...\033[2K\r" echo -ne "Обработка аргументов...\033[2K\r"
while [[ "$#" -gt 0 ]]; do while [[ "$#" -gt 0 ]]; do
case $1 in case $1 in
-r|--backup-server) backup_server="$2"; shift ;; -rs|--remote-server) backup_server="$2"; shift ;;
-г|--baсkup-user) backup_user="$2"; shift ;; -гu|--remote-user) backup_user="$2"; shift ;;
-s|--snapshot) inc_snapshot="$2"; shift ;; -s|--snapshot) inc_snapshot="$2"; shift ;;
-ld|--local-dataset) local_dataset="$2"; shift ;;
-rd|--remote-dataset) remote_dataset="$2"; shift ;;
-l|--log-file) LOGFILE="$2"; shift ;;
-i|--incremental) incremental=true ;; -i|--incremental) incremental=true ;;
*) echo "Неизвестные параметры: $1"; exit 1 ;; --no-progress) progress=false ;;
esac --no-check) check=false ;;
shift --stop) stop=true ;;
-h|--help) echo "Использование: ./zfs_send.zsh [OPTIONS]";
echo "-rs | --remote-server <ip/dns>";
echo "-гu | --remote-user <user>";
echo "-s | --snapshot <snapshot_name>";
echo "-ld | --local-dataset <local/dataset>";
echo "-rd | --remote-dataset <remote/dataset>";
echo "-l | --log-file <path/filename";
echo " --no-progress";
echo " --no-check";
echo " --stop";
echo "-i | --incremental" ;;
*) echo "Неизвестные параметры: $1"; exit 1 ;;
esac
shift
done done
# Завершение запущенного процесса резервирования
if [[ $stop = true ]]; then
pids=$(ps -t $(tty | sed 's|/dev/||') -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- '--stop') # Список PIDs с командой запуска
if [[ $pids = "" ]]; then
echo "Нет запущенного процесса резервирования в этом терминале."
read "srch?Икать во всех процессах системы? (y/N) "
if [[ $srch = y ]]; then
pids=$(ps -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- '--stop' )
fi
fi
# Подтверждение
if [[ $pids = "" ]]; then
echo "Нет запущенного процесса резервирования"
else
echo "Будут остановлены следующие процессы:"
echo $pids
read "work? Продолжать (y/N)"
if [[ $work = y ]]; then
pids=$(echo $pids | awk '{print $1}')
kill $pids
echo "ok"
else
echo "Операция прервана"
fi
fi
exit 0
fi
echo -n "Резервирование на " echo -n "Резервирование на "
# Проверка соединения # Проверка соединения
if ssh $backup_user@$backup_server 'exit'; then; if ssh $backup_user@$backup_server 'exit'; then;
echo "$backup_user@$backup_server ok"; echo "$backup_user@$backup_server ok";
else else
echo "Настройте ssh и запустите скрипт повторно" echo "Настройте ssh и запустите скрипт повторно"
exit 1 exit 1
fi fi
echo "Снимок файловой системы $inc_snapshot" echo "Снимок файловой системы $inc_snapshot"
@ -33,6 +88,8 @@ case $incremental in
false) echo "Режим полной копии";; false) echo "Режим полной копии";;
esac esac
echo "Дополнительные опции: -h|--help"
# --------- Собираем данные ---------- # --------- Собираем данные ----------
echo -ne "Собираю данные...\033[2K\r" echo -ne "Собираю данные...\033[2K\r"
@ -45,67 +102,71 @@ backup_datasets=(${=backup_datasets}) # Конвертирование в мас
declare -A work_datasets # Словарь ключ = резервируемый локальный датасет, значение = имя датасета на удалённом сервере declare -A work_datasets # Словарь ключ = резервируемый локальный датасет, значение = имя датасета на удалённом сервере
if [[ $incremental = true ]]; then # Если заданы local_dataset & remote_dataset
# Высчитать что на что резервировать if [[ -v local_dataset && -v remote_dataset ]]; then
for ((i = 1; i <= ${#local_datasets}; i++)); do work_datasets[$local_dataset]=$remote_dataset
for ((j = 1; j <= ${#backup_datasets}; j++)); do else # Если нет, то вычислить
a=$(sed -r "s/(.*)\/(.*)/\2/" <<<"$local_datasets[i]") # Определить нижний по иерархии if [[ $incremental = true ]]; then
b=$(sed -r "s/(.*)\/(.*)/\2/" <<<"$backup_datasets[j]") # тоже для второго # Высчитать что на что резервировать
if [[ "$a" == "$b" ]]; then for ((i = 1; i <= ${#local_datasets}; i++)); do
work_datasets[${local_datasets[i]}]=${backup_datasets[j]} for ((j = 1; j <= ${#backup_datasets}; j++)); do
fi a=$(sed -r "s/(.*)\/(.*)/\2/" <<<"$local_datasets[i]") # Определить нижний по иерархии
done b=$(sed -r "s/(.*)\/(.*)/\2/" <<<"$backup_datasets[j]") # тоже для второго
done if [[ "$a" == "$b" ]]; then
else # Если не инкрементная копия, то запросить мео для резервирования work_datasets[${local_datasets[i]}]=${backup_datasets[j]}
# Вывести на экран запрос
column_widths="| %-4s%-35s | %-4s%-35s\n"
i_max=$(( ${#local_datasets} > ${#backup_datasets} ? ${#local_datasets} : ${#backup_datasets} )) # Максимальное количество элементов
printf $column_widths "№" "Локальные датасеты" "№" "На $backup_server"
printf $column_widths "----" "------------------" "----" "-------------------"
# echo "№ - Локальные\t\t№ - Удалённые"
for ((i = 1; i <= $i_max; i++)); do
if [[ -v local_datasets[i] ]]; then i1=$i; else; i1=""; fi
if [[ -v backup_datasets[i] ]]; then i2=$i; else; i2=""; fi
printf $column_widths "$i1" "${local_datasets[i]}" "$i2" "${backup_datasets[i]}"
done
wrong_enter=true
while $wrong_enter = true ; do
read "loc_ds_indx_list?Локальные индексы через пробел (0=выход; all=режим исключения): "
if [[ $loc_ds_indx_list = "all" ]]; then
loc_ds_indx_list=({1..${#local_datasets}})
break
fi
if [[ $loc_ds_indx_list = *[[:digit:]]* ]]; then
loc_ds_indx_list=(${=loc_ds_indx_list}) # Convert to list
for i in $loc_ds_indx_list; do
if [[ $i = 0 ]]; then exit; fi
if (( $i < 1 )) || (( $i > ${#local_datasets} )); then
echo "$i ! Выход за диапазон"
wrong_enter=true
break
else
wrong_enter=false
fi fi
done done
fi done
done else # Если не инкрементная копия, то запросить мео для резервирования
# Вывести на экран запрос
column_widths="| %-4s%-35s | %-4s%-35s\n"
i_max=$(( ${#local_datasets} > ${#backup_datasets} ? ${#local_datasets} : ${#backup_datasets} )) # Максимальное количество элементов
printf $column_widths "№" "Локальные датасеты" "№" "На $backup_server"
printf $column_widths "----" "------------------" "----" "-------------------"
# echo "№ - Локальные\t\t№ - Удалённые"
for ((i = 1; i <= $i_max; i++)); do
if [[ -v local_datasets[i] ]]; then i1=$i; else; i1=""; fi
if [[ -v backup_datasets[i] ]]; then i2=$i; else; i2=""; fi
printf $column_widths "$i1" "${local_datasets[i]}" "$i2" "${backup_datasets[i]}"
done
echo "Введите номер датасета в котором создать кописю исходного (- исключить, 0 выйти)" wrong_enter=true
for i in $loc_ds_indx_list; do while $wrong_enter = true ; do
read "bak_ds_indx?$i - ${local_datasets[i]} -> " read "loc_ds_indx_list?Локальные индексы через пробел (0=выход; all=режим исключения): "
case $bak_ds_indx in if [[ $loc_ds_indx_list = "all" ]]; then
0) exit;; loc_ds_indx_list=({1..${#local_datasets}})
-) ;; break
*) if (( $bak_ds_indx >= 1 )) && (( $bak_ds_indx <= ${#backup_datasets} )); then fi
loc_name_ds=$(sed -r "s/(.*)\/(.*)/\2/" <<<"$local_datasets[$i]") # Определить нижний по иерархии if [[ $loc_ds_indx_list = *[[:digit:]]* ]]; then
bak_ds="$bak_ds/$loc_name_ds" # Добавить имя создаваемого датасета loc_ds_indx_list=(${=loc_ds_indx_list}) # Convert to list
work_datasets[$local_datasets[$i]]=$bak_ds for i in $loc_ds_indx_list; do
else; exit 1; fi ;; if [[ $i = 0 ]]; then exit; fi
esac if (( $i < 1 )) || (( $i > ${#local_datasets} )); then
done echo "$i ! Выход за диапазон"
fi # Конец блока запроса места резервирования wrong_enter=true
break
else
wrong_enter=false
fi
done
fi
done
echo "Введите номер датасета в котором создать кописю исходного (- исключить, 0 выйти)"
for i in $loc_ds_indx_list; do
read "bak_ds_indx?$i - ${local_datasets[i]} -> "
case $bak_ds_indx in
0) exit;;
-) ;;
*) if (( $bak_ds_indx >= 1 )) && (( $bak_ds_indx <= ${#backup_datasets} )); then
loc_name_ds=$(sed -r "s/(.*)\/(.*)/\2/" <<<"$local_datasets[$i]") # Определить нижний по иерархии
bak_ds="$bak_ds/$loc_name_ds" # Добавить имя создаваемого датасета
work_datasets[$local_datasets[$i]]=$bak_ds
else; exit 1; fi ;;
esac
done
fi # Конец блока запроса места резервирования
fi # Конец блока наличия переданных параметров датасетов в скрипт
# Словарь со списком последких снапшотов для датасетов # Словарь со списком последких снапшотов для датасетов
@ -173,33 +234,35 @@ fi
# --------- Выбор датасетов ---------- # --------- Выбор датасетов ----------
read 'work?Утвердите данные. "y" в работу | "1 2.." выбрать через пробел | НЕТ по умолчанию : ' if [[ $check = true ]]; then
if [[ $work = *[[:digit:]]* ]]; then read 'work?Утвердите данные. "y" в работу | "1 2.." выбрать через пробел | НЕТ по умолчанию : '
indx_list=(${=work}) if [[ $work = *[[:digit:]]* ]]; then
# Если нет индекса в списке, то удалить из словаря для резервирования indx_list=(${=work})
i=0; for loc_ds bak_ds in ${(kv)work_datasets}; do # Если нет индекса в списке, то удалить из словаря для резервирования
i=$((i+1)) i=0; for loc_ds bak_ds in ${(kv)work_datasets}; do
if [[ ${indx_list[(r)$i]} != $i ]]; then # Если нет $i в списке $indx_list i=$((i+1))
unset "work_datasets[$loc_ds]" if [[ ${indx_list[(r)$i]} != $i ]]; then # Если нет $i в списке $indx_list
else unset "work_datasets[$loc_ds]"
echo "$i - $loc_ds \t ${last_loc_snaps[$loc_ds]} \t -> \t${bak_ds}" else
fi echo "$i - $loc_ds \t ${last_loc_snaps[$loc_ds]} \t -> \t${bak_ds}"
done fi
fi done
fi
# Если был выведен список, то запросить запуск в работу нового списка # Если был выведен список, то запросить запуск в работу нового списка
if [[ ${#indx_list} > 0 ]]; then if [[ ${#indx_list} > 0 ]]; then
work="N" work="N"
read "work?В работу (y/N) " read "work?В работу (y/N) "
fi
else
work="y"
fi fi
# --------- Резервирование ---------- # --------- Резервирование ----------
if [[ $work = "y" || $work = "Y" ]]; then if [[ $work = "y" || $work = "Y" ]]; then
TS0=$(date +%s) TS0=$(date +%s)
echo $TS0 > /dev/shm/backup_time_stamp # Сохраняем отметку о времени начала echo $TS0 > /dev/shm/backup_time_stamp # Сохраняем отметку о времени начала
LOGFILE="log_bak_${inc_snapshot}.log"
echo "Результат работы записываю в файл $LOGFILE" echo "Результат работы записываю в файл $LOGFILE"
# Функция для записи сообщения в файл и на экран # Функция для записи сообщения в файл и на экран
@ -220,10 +283,10 @@ if [[ $work = "y" || $work = "Y" ]]; then
echo_log echo_log
echo_log "--- Резервирую данные ---" echo_log "--- Резервирую данные ---"
i_task=0 # Выполненных зачач i_task=0 # Выполненных зачач
echo $i_task > /dev/shm/backup_i_task # Сохраняем количество готовых задач в память if [[ $progress == true ]]; then echo $i_task > /dev/shm/backup_i_task; fi # Сохраняем количество готовых задач в память
for loc_ds bak_ds in ${(kv)work_datasets}; do for loc_ds bak_ds in ${(kv)work_datasets}; do
TS1=$(date +%s) TS1=$(date +%s)
echo $TS1 > /dev/shm/backup_time_stamp # Сохраняем отметку о времени начала if [[ $progress == true ]]; then echo $TS1 > /dev/shm/backup_time_stamp; fi # Сохраняем отметку о времени начала
echo_log " * snapshot ${loc_ds}@${inc_snapshot}" echo_log " * snapshot ${loc_ds}@${inc_snapshot}"
zfs snapshot ${loc_ds}@${inc_snapshot}; #FIXME выдаёт ошибку если мы оставили существующий снимок zfs snapshot ${loc_ds}@${inc_snapshot}; #FIXME выдаёт ошибку если мы оставили существующий снимок
echo_log " * Start sending ${loc_ds} at $(date +'%Y.%m.%d %H:%M.%S')" echo_log " * Start sending ${loc_ds} at $(date +'%Y.%m.%d %H:%M.%S')"
@ -233,47 +296,49 @@ if [[ $work = "y" || $work = "Y" ]]; then
esac esac
TS2=$(date +%s) TS2=$(date +%s)
(( i_task++ )) (( i_task++ ))
echo $i_task > /dev/shm/backup_i_task if [[ $progress == true ]]; then echo $i_task > /dev/shm/backup_i_task; fi
echo_log "--- готово за: $(date -d@$(($TS2-$TS1)) -u '+%H:%M.%S') ---" echo_log "--- готово за: $(date -d@$(($TS2-$TS1)) -u '+%H:%M.%S') ---"
done & # Запустить резервирование в фоне done & # Запустить резервирование в фоне
# Выводить прогресс запрашивая список запущенныйх процессов # Выводить прогресс запрашивая список запущенных процессов
echo "" if [[ $progress == true ]]; then
last_percent=0 echo ""
time_changed_percent_value=$(date +%s) last_percent=0
while [[ $i_task != $n_tasks ]]; do # выводим статус пока не завершены все задачи time_changed_percent_value=$(date +%s)
progress=$(ps -u | grep "send" | grep -v "grep" | sed -r "s/(.*) zfs: (.*)/\2/") while [[ $i_task != $n_tasks ]]; do # выводим статус пока не завершены все задачи
percent=$(echo $progress | cut -d "(" -f2 | cut -d "%" -f1 ) progress=$(ps -u | grep "send" | grep -v "grep" | sed -r "s/(.*) zfs: (.*)/\2/")
if (( percent > 0 )); then percent=$(echo $progress | cut -d "(" -f2 | cut -d "%" -f1 )
now=$(date +%s) if (( percent > 0 )); then
if (( percent != last_percent )); then now=$(date +%s)
time_changed_percent_value=$now if (( percent != last_percent )); then
last_percent=$percent time_changed_percent_value=$now
TS1=$(< /dev/shm/backup_time_stamp) last_percent=$percent
elapsed=$(( $(date +%s) - TS1 )) TS1=$(< /dev/shm/backup_time_stamp)
fi elapsed=$(( $(date +%s) - TS1 ))
time_part_of_percent=$(( now - time_changed_percent_value )) # Время с последнего изменения процента fi
estimated_total=$(( 100 / percent * elapsed )) # Всего времени на задачу time_part_of_percent=$(( now - time_changed_percent_value )) # Время с последнего изменения процента
estimated_remain=$(( estimated_total - elapsed - time_part_of_percent )) # Осталось времени на задачу estimated_total=$(( 100 / percent * elapsed )) # Всего времени на задачу
if (( estimated_remain >= 86400 )) || (( estimated_total >= 86400 )); then # Если осталось более чем сутки, то отобразить дни estimated_remain=$(( estimated_total - elapsed - time_part_of_percent )) # Осталось времени на задачу
estimated_remain_days=$(( estimated_remain / 86400 )); estimated_remain_time=$(( estimated_remain % 86400 )) if (( estimated_remain >= 86400 )) || (( estimated_total >= 86400 )); then # Если осталось более чем сутки, то отобразить дни
estimated_total_days=$(( estimated_total / 86400 )); estimated_total_time=$(( estimated_total % 86400 )) estimated_remain_days=$(( estimated_remain / 86400 )); estimated_remain_time=$(( estimated_remain % 86400 ))
echo -n "\033[2K\r$progress $estimated_remain_days д. $(date -d@$estimated_remain_time -u '+%H:%M.%S') ост. / $estimated_total_days д. $(date -d@$estimated_total_time -u '+%H:%M.%S') " estimated_total_days=$(( estimated_total / 86400 )); estimated_total_time=$(( estimated_total % 86400 ))
echo -n "\033[2K\r$progress $estimated_remain_days д. $(date -d@$estimated_remain_time -u '+%H:%M.%S') ост. / $estimated_total_days д. $(date -d@$estimated_total_time -u '+%H:%M.%S') "
else
echo -n "\033[2K\r$progress $(date -d@$estimated_remain -u '+%H:%M.%S') ост. / $(date -d@$estimated_total -u '+%H:%M.%S') "
fi
sleep 1
else else
echo -n "\033[2K\r$progress $(date -d@$estimated_remain -u '+%H:%M.%S') ост. / $(date -d@$estimated_total -u '+%H:%M.%S') " echo -ne "\033[2K\r$progress "
sleep 1
for ((i = 1; i < 3 ; i++)); do echo -n "."; sleep 1; done
fi fi
sleep 1 i_task=$(< /dev/shm/backup_i_task)
else done
echo -ne "\033[2K\r$progress "
sleep 1
for ((i = 1; i < 3 ; i++)); do echo -n "."; sleep 1; done
fi
i_task=$(< /dev/shm/backup_i_task)
done
# Удалить временные файлы состояний # Удалить временные файлы состояний
rm /dev/shm/backup_i_task rm /dev/shm/backup_i_task
rm /dev/shm/backup_time_stamp rm /dev/shm/backup_time_stamp
fi
echo_log "\n--- Все завершено за $(date -d@$(( $(date +%s) - $TS0 )) -u '+%H:%M.%S') ---\n" echo_log "\n--- Все завершено за $(date -d@$(( $(date +%s) - $TS0 )) -u '+%H:%M.%S') ---\n"