fix: вывод прогресса; add: auto ssh setup
This commit is contained in:
parent
5b1b327e22
commit
874662feb4
@ -14,10 +14,17 @@
|
|||||||
- `-i | --incremental` Режим резервирования (по умолчанию полная копия)
|
- `-i | --incremental` Режим резервирования (по умолчанию полная копия)
|
||||||
Данные по умолчанию можно отредактировать в самом скрипте в рамом начале.
|
Данные по умолчанию можно отредактировать в самом скрипте в рамом начале.
|
||||||
- ` --no-progress` Не выводить прогресс
|
- ` --no-progress` Не выводить прогресс
|
||||||
|
- ` --progress-only` Запустить только вывод прогресса, если уже запущено резервирование в флне
|
||||||
- ` --no-check` Не запрашивать подтверждения. Если нужно запускать скрипт по расписанию
|
- ` --no-check` Не запрашивать подтверждения. Если нужно запускать скрипт по расписанию
|
||||||
- ` --stop` Остановить текщие фоновые процессы резервирования, запучщенные скриптом
|
- ` --stop` Остановить текщие фоновые процессы резервирования, запучщенные скриптом
|
||||||
|
|
||||||
Ожидается, что доступ по SSH на удалённую машину уже настроен
|
### Настройка доступа по ключу SSH
|
||||||
|
|
||||||
|
Ожидается, что доступ по SSH на удалённую машину уже настроен. Доступ нужен с использованием ssh-ключа.
|
||||||
|
Если доступ по паролю, то скприт попытается создать ключ и закинуть его на сервер (по паролю)
|
||||||
|
|
||||||
|
Если после удачной процедуры всё же не получается подсоединиться, то нужно вручную добавлять выведеный на экран публичный ключ на бэкап-сервер,
|
||||||
|
возможно через веб-гуй.
|
||||||
|
|
||||||
### Режим резервирования датасета целиком
|
### Режим резервирования датасета целиком
|
||||||
Когда не использован флаг `-i`
|
Когда не использован флаг `-i`
|
||||||
|
|||||||
165
zfs_send.zsh
165
zfs_send.zsh
@ -7,6 +7,7 @@ incremental=false
|
|||||||
silent=false
|
silent=false
|
||||||
stop=false
|
stop=false
|
||||||
progress=true
|
progress=true
|
||||||
|
progress_only=false
|
||||||
check=true
|
check=true
|
||||||
LOGFILE="log_bak_${inc_snapshot}.log"
|
LOGFILE="log_bak_${inc_snapshot}.log"
|
||||||
unset local_dataset
|
unset local_dataset
|
||||||
@ -17,13 +18,14 @@ echo -ne "Обработка аргументов...\033[2K\r"
|
|||||||
while [[ "$#" -gt 0 ]]; do
|
while [[ "$#" -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
-rs|--remote-server) backup_server="$2"; shift ;;
|
-rs|--remote-server) backup_server="$2"; shift ;;
|
||||||
-гu|--remote-user) backup_user="$2"; shift ;;
|
-ru|--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 ;;
|
-ld|--local-dataset) local_dataset="$2"; shift ;;
|
||||||
-rd|--remote-dataset) remote_dataset="$2"; shift ;;
|
-rd|--remote-dataset) remote_dataset="$2"; shift ;;
|
||||||
-l|--log-file) LOGFILE="$2"; shift ;;
|
-l|--log-file) LOGFILE="$2"; shift ;;
|
||||||
-i|--incremental) incremental=true ;;
|
-i|--incremental) incremental=true ;;
|
||||||
--no-progress) progress=false ;;
|
--no-progress) progress=false ;;
|
||||||
|
--progress-only) progress_only=true ;;
|
||||||
--no-check) check=false ;;
|
--no-check) check=false ;;
|
||||||
--stop) stop=true ;;
|
--stop) stop=true ;;
|
||||||
-h|--help) echo "Использование: ./zfs_send.zsh [OPTIONS]";
|
-h|--help) echo "Использование: ./zfs_send.zsh [OPTIONS]";
|
||||||
@ -34,14 +36,97 @@ while [[ "$#" -gt 0 ]]; do
|
|||||||
echo "-rd | --remote-dataset <remote/dataset>";
|
echo "-rd | --remote-dataset <remote/dataset>";
|
||||||
echo "-l | --log-file <path/filename>";
|
echo "-l | --log-file <path/filename>";
|
||||||
echo " --no-progress";
|
echo " --no-progress";
|
||||||
|
echo " --progress-only";
|
||||||
echo " --no-check";
|
echo " --no-check";
|
||||||
echo " --stop";
|
echo " --stop";
|
||||||
echo "-i | --incremental" ;;
|
echo "-i | --incremental" ;
|
||||||
|
echo "-p | --progress" ;;
|
||||||
*) echo "Неизвестные параметры: $1"; exit 1 ;;
|
*) echo "Неизвестные параметры: $1"; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Функция для записи сообщения в файл и на экран
|
||||||
|
echo_log () {
|
||||||
|
local text=$1
|
||||||
|
echo $text
|
||||||
|
echo $text >> "$LOGFILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Функция для определения SSH-ключа через конфигурацию
|
||||||
|
find_ssh_key() {
|
||||||
|
local key
|
||||||
|
|
||||||
|
# Получаем IdentityFile из конфигурации
|
||||||
|
identity_files=$(ssh -G "$backup_user@$backup_server" 2>/dev/null | awk '/^IdentityFile / {print $2}')
|
||||||
|
|
||||||
|
# Проверяем каждый IdentityFile из конфига
|
||||||
|
for file in $identity_files; do
|
||||||
|
if [[ -f "$file" ]]; then
|
||||||
|
echo "$file"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Если ничего не найдено, ищем стандартные ключи
|
||||||
|
for candidate in ~/.ssh/id_rsa ~/.ssh/id_ecdsa ~/.ssh/id_ed25519; do
|
||||||
|
if [[ -f "$candidate" ]]; then
|
||||||
|
echo "$candidate"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Если ключей нет, создаем новый
|
||||||
|
# echo "Создаём новый ключ..."
|
||||||
|
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 >&2
|
||||||
|
echo "~/.ssh/id_ed25519"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Определение ключа
|
||||||
|
key_path=$(find_ssh_key)
|
||||||
|
key_pub_path="${key_path}.pub"
|
||||||
|
|
||||||
|
|
||||||
|
check_ssh_connection() {
|
||||||
|
ssh -o "BatchMode=yes" -o "ConnectTimeout=5" "$backup_user@$backup_server" exit 2>/dev/null
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setup_ssh_key() {
|
||||||
|
key_content=$(cat "$key_pub_path")
|
||||||
|
echo "Установка SSH-ключа на $backup_user@$backup_server..."
|
||||||
|
|
||||||
|
# Передача публичного ключа вручную
|
||||||
|
ssh $backup_user@$backup_server "mkdir -p ~/.ssh && echo '$key_content' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys && chmod 700 ~/.ssh"
|
||||||
|
|
||||||
|
echo "
|
||||||
|
host $backup_server
|
||||||
|
hostname $backup_server
|
||||||
|
user $backup_user
|
||||||
|
identityFile $key_path
|
||||||
|
identitiesOnly yes
|
||||||
|
" >> ~/.ssh/config
|
||||||
|
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
echo "Ошибка: Не удалось установить SSH-ключ. Попробуйте прописать его на сервер вручную."
|
||||||
|
echo "----------"
|
||||||
|
echo $key_content
|
||||||
|
echo "----------"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "SSH-ключ успешно установлен!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Если нужно вывести только прогресс, то пропускаем все этапы подготовки и резервирования
|
||||||
|
if [[ $progress_only = false ]]; then
|
||||||
|
|
||||||
|
|
||||||
# Завершение запущенного процесса резервирования
|
# Завершение запущенного процесса резервирования
|
||||||
if [[ $stop = true ]]; then
|
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 с командой запуска
|
pids=$(ps -t $(tty | sed 's|/dev/||') -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- '--stop') # Список PIDs с командой запуска
|
||||||
@ -73,14 +158,24 @@ if [[ $stop = true ]]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -n "Резервирование на "
|
|
||||||
# Проверка соединения
|
# Проверка соединения
|
||||||
if ssh $backup_user@$backup_server 'exit'; then;
|
echo "Резервирование на $backup_user@$backup_server"
|
||||||
echo "$backup_user@$backup_server ok";
|
if check_ssh_connection; then
|
||||||
|
echo "Connection OK"
|
||||||
else
|
else
|
||||||
echo "Настройте ssh и запустите скрипт повторно"
|
echo "SSH-ключ не обнаружен на сервере. Начинаем настройку..."
|
||||||
|
setup_ssh_key
|
||||||
|
if check_ssh_connection; then
|
||||||
|
echo "Connection OK"
|
||||||
|
else
|
||||||
|
echo "Ошибка: SSH-ключ не работает. Проверьте настройки."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# ----------
|
||||||
echo "Снимок файловой системы $inc_snapshot"
|
echo "Снимок файловой системы $inc_snapshot"
|
||||||
|
|
||||||
case $incremental in
|
case $incremental in
|
||||||
@ -177,7 +272,7 @@ 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="$(zfs list -t snapshot -o name ${loc_ds} | grep 'manual' | tail -n1 | egrep -o '@.+' )"
|
||||||
loc_snap=${loc_snap:1} # удалить @ в начале строки
|
loc_snap=${loc_snap:1} # удалить @ в начале строки
|
||||||
if [[ $incremental = true ]]; then
|
if [[ $incremental = true ]]; then
|
||||||
bak_snap="$(ssh $backup_user@$backup_server zfs list -t snapshot -o name ${bak_ds} | grep 'manual' | tail -n1 | egrep -o '@.+' )"
|
bak_snap=$(ssh $backup_user@$backup_server zfs list -t snapshot -o name ${bak_ds} | grep 'manual' | tail -n1 | egrep -o '@.+')
|
||||||
bak_snap=${bak_snap:1} # удалить @ в начале строки
|
bak_snap=${bak_snap:1} # удалить @ в начале строки
|
||||||
fi
|
fi
|
||||||
# echo "$loc_ds @ $loc_snap -> $bak_ds @ $bak_snap"
|
# echo "$loc_ds @ $loc_snap -> $bak_ds @ $bak_snap"
|
||||||
@ -262,15 +357,10 @@ 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_start # Сохраняем отметку о времени начала
|
||||||
|
echo $TS0 > /dev/shm/backup_time_circle # Сохраняем отметку о времени круга
|
||||||
echo "Результат работы записываю в файл $LOGFILE"
|
echo "Результат работы записываю в файл $LOGFILE"
|
||||||
|
|
||||||
# Функция для записи сообщения в файл и на экран
|
|
||||||
echo_log () { # $1 = Сообщение
|
|
||||||
echo $1
|
|
||||||
echo $1 >> "$LOGFILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Записать лог список резервируемых датасетов
|
# Записать лог список резервируемых датасетов
|
||||||
echo "--- $(date +'%Y.%m.%d %H:%M.%S') ---" >> "$LOGFILE"
|
echo "--- $(date +'%Y.%m.%d %H:%M.%S') ---" >> "$LOGFILE"
|
||||||
echo "Список резервируемых датасетов:" >> "$LOGFILE"
|
echo "Список резервируемых датасетов:" >> "$LOGFILE"
|
||||||
@ -286,34 +376,55 @@ if [[ $work = "y" || $work = "Y" ]]; then
|
|||||||
if [[ $progress == true ]]; then echo $i_task > /dev/shm/backup_i_task; fi # Сохраняем количество готовых задач в память
|
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)
|
||||||
if [[ $progress == true ]]; then echo $TS1 > /dev/shm/backup_time_stamp; fi # Сохраняем отметку о времени начала
|
if [[ $progress == true ]]; then echo $TS1 > /dev/shm/backup_time_circle; 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')"
|
||||||
case $incremental in
|
case $incremental in
|
||||||
true) zfs send -V -i ${loc_ds}@${last_loc_snaps[$loc_ds]} ${loc_ds}@${inc_snapshot} | ssh "$backup_user@${backup_server}" zfs receive ${bak_ds}@${inc_snapshot} ;;
|
true) zfs send -V -i ${loc_ds}@${last_loc_snaps[$loc_ds]} ${loc_ds}@${inc_snapshot} | ssh $backup_user@$backup_server zfs receive ${bak_ds}@${inc_snapshot} ;;
|
||||||
false) zfs send -V ${loc_ds}@${inc_snapshot} | ssh "$backup_user@${backup_server}" zfs receive ${bak_ds} ;;
|
false) zfs send -V ${loc_ds}@${inc_snapshot} | ssh $backup_user@$backup_server zfs receive ${bak_ds} ;;
|
||||||
esac
|
esac
|
||||||
TS2=$(date +%s)
|
TS2=$(date +%s)
|
||||||
(( i_task++ ))
|
(( i_task++ ))
|
||||||
if [[ $progress == true ]]; then echo $i_task > /dev/shm/backup_i_task; fi
|
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 & # Запустить резервирование в фоне
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Конец блока условия progress_only
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Вывод прогресса
|
||||||
|
if [[ $progress == true ]]; then
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
pids=$(ps -t $(tty | sed 's|/dev/||') -o pid,args | grep -e zfs_send.zsh -e "zfs send" ) # Список 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" )
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Выводить прогресс запрашивая список запущенных процессов
|
# Выводить прогресс запрашивая список запущенных процессов
|
||||||
if [[ $progress == true ]]; then
|
if [[ $pids = "" ]]; then
|
||||||
|
echo "Нет запущенного процесса резервирования"
|
||||||
|
else
|
||||||
echo ""
|
echo ""
|
||||||
last_percent=0
|
last_percent=0
|
||||||
time_changed_percent_value=$(date +%s)
|
time_changed_percent_value=$(date +%s)
|
||||||
while [[ $i_task != $n_tasks ]]; do # выводим статус пока не завершены все задачи
|
while [[ $i_task != $n_tasks ]]; do # выводим статус пока не завершены все задачи
|
||||||
progress=$(ps -u | grep "send" | grep -v "grep" | sed -r "s/(.*) zfs: (.*)/\2/")
|
progress=$(ps -u | grep "sending" | grep -v "grep" | sed -r "s/(.*) zfs: (.*)/\2/")
|
||||||
percent=$(echo $progress | cut -d "(" -f2 | cut -d "%" -f1 )
|
percent=$(echo $progress | cut -d "(" -f2 | cut -d "%" -f1 )
|
||||||
if (( percent > 0 )); then
|
if (( percent > 0 )); then
|
||||||
now=$(date +%s)
|
now=$(date +%s)
|
||||||
if (( percent != last_percent )); then
|
if (( percent != last_percent )); then
|
||||||
time_changed_percent_value=$now
|
time_changed_percent_value=$now
|
||||||
last_percent=$percent
|
last_percent=$percent
|
||||||
TS1=$(< /dev/shm/backup_time_stamp)
|
TS1=$(< /dev/shm/backup_time_circle)
|
||||||
elapsed=$(( $(date +%s) - TS1 ))
|
elapsed=$(( $(date +%s) - TS1 ))
|
||||||
fi
|
fi
|
||||||
time_part_of_percent=$(( now - time_changed_percent_value )) # Время с последнего изменения процента
|
time_part_of_percent=$(( now - time_changed_percent_value )) # Время с последнего изменения процента
|
||||||
@ -335,11 +446,19 @@ if [[ $work = "y" || $work = "Y" ]]; then
|
|||||||
i_task=$(< /dev/shm/backup_i_task)
|
i_task=$(< /dev/shm/backup_i_task)
|
||||||
done
|
done
|
||||||
|
|
||||||
# Удалить временные файлы состояний
|
|
||||||
rm /dev/shm/backup_i_task
|
|
||||||
rm /dev/shm/backup_time_stamp
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -f /dev/shm/backup_time_start ]]; then
|
||||||
|
TS0=$(< /dev/shm/backup_time_start)
|
||||||
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"
|
||||||
|
else
|
||||||
|
echo_log "\n--- Все задания завершены ---\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Удалить временные файлы состояний, если они существуют
|
||||||
|
[ -f /dev/shm/backup_i_task ] && rm /dev/shm/backup_i_task
|
||||||
|
[ -f /dev/shm//dev/shm/backup_time_start ] && rm /dev/shm/backup_time_start
|
||||||
|
[ -f /dev/shm//dev/shm/backup_time_circle ] && rm /dev/shm/backup_time_circle
|
||||||
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user