From 2effa78854d37a4f5cbc94e73bcfa824c27c5bdd Mon Sep 17 00:00:00 2001 From: root Date: Fri, 24 Oct 2025 03:05:50 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B2=D0=BB=D0=B5=D0=BD=20?= =?UTF-8?q?=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D0=BE=D0=B1=D1=8A=D1=91=D0=BC?= =?UTF-8?q?=D0=BE=D0=B2:=20*=20=D1=81=D0=B2=D0=B1=D0=BE=D0=B4=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D0=BC=D0=B5=D1=81=D1=82=D0=B0=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=D0=B5=D1=80=D0=B2=D0=BD=D0=BE=D0=BC=20=D1=85?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D0=B8=D0=BB=D0=B8=D1=89=D0=B5=20*=20=D0=BE?= =?UTF-8?q?=D0=B1=D1=8A=D1=91=D0=BC=D0=B0=20=D1=80=D0=B5=D0=B7=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D0=B8=D1=80=D1=83=D0=B5=D0=BC=D0=BE=D0=B3=D0=BE=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D1=82=D1=80=D0=B0=D0=BD=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=20=D0=BA=D0=B0=D0=B6=D0=B4=D0=BE=D0=BC?= =?UTF-8?q?=D1=83=20=D0=B4=D0=B0=D1=82=D0=B0=D1=81=D0=B5=D1=82=D1=83=20(+?= =?UTF-8?q?=D0=B8=D1=82=D0=BE=D0=B3=D0=BE=D0=B2=D0=B0=D1=8F=20=D1=81=D1=83?= =?UTF-8?q?=D0=BC=D0=BC=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zfs_send.zsh | 90 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/zfs_send.zsh b/zfs_send.zsh index b4df0c9..2a70459 100755 --- a/zfs_send.zsh +++ b/zfs_send.zsh @@ -20,6 +20,7 @@ LOGFILE="log_bak_${inc_snapshot}.log" unset local_dataset unset remote_dataset + echo -ne "Обработка аргументов...\033[2K\r" while [[ "$#" -gt 0 ]]; do @@ -210,6 +211,19 @@ local_datasets=(${=local_datasets}) # Конвертирование в масс backup_datasets=$(ssh $backup_user@$backup_server zfs list -o name | grep -v -e ix-app -e boot-pool -e system-data -e jails -e NAME) backup_datasets=(${=backup_datasets}) # Конвертирование в массив +# Свободное место на бэкап-сервере +declare -A backup_pool_free # Словарь: ключ = пул, значение = свободное место +ignored_pools=(boot-pool) + +# Запрашиваем данные о свободном пространстве и парсим +while read -r pool free; do + if (( ${ignored_pools[(Ie)$pool]} )); then + continue + fi + backup_pool_free[$pool]="$free" +done < <(ssh "$backup_user@$backup_server" "zpool list -H -o name,free" 2>/dev/null) + + declare -A work_datasets # Словарь ключ = резервируемый локальный датасет, значение = имя датасета на удалённом сервере # Если заданы local_dataset & remote_dataset @@ -327,25 +341,75 @@ done # --------- Запрашиваем подтверждение пользователя ---------- -echo "" -echo "Список резервируемых датасетов:" -column_widths="%-4s %-35s %-20s -> %-35s\n" +estimate_changes_bytes() { # Рассчитать объём резервируемого пространства + local loc_ds="$1" + local base_snap="$2" + local value + if [[ -z "$base_snap" ]]; then + # Полный бэкап → used в байтах + value=$(zfs get -H -p -o value used "$loc_ds" 2>/dev/null) + else + # Инкремент → written@ в байтах + value=$(zfs get -H -p -o value "written@$base_snap" "$loc_ds" 2>/dev/null) + fi + # Если значение получено — выводим его, иначе — ничего (пусто) + if [[ -n "$value" && "$value" != "-" ]]; then + echo "$value" + fi +} +human_size() { # Перевести байты в читаемый вид + bytes="$1" + if [[ -z "$bytes" ]]; then + echo "N/A" + return + fi + local one_gib=1073741824 + if (( bytes >= one_gib )); then + numfmt --to=iec --format="%.1f" "$bytes" 2>/dev/null || echo "${bytes}B" + else + numfmt --to=iec --format="%.0f" "$bytes" 2>/dev/null || echo "${bytes}B" + fi +} +declare -A est_changes # Словарь хранения необходимого объёма для резервирования. Ключ - датасет, значнеие - объём в байтах + +echo +echo "Список резервируемых датасетов:" +column_widths="%-4s %-35s %-20s %-10s -> %-35s\n" +printf "$column_widths" "#" "Локальный датасет" "Базовый снапшот" "Изменения" "Бэкап-датасет" +total_bytes=0 if [[ $incremental = true ]]; then i=0; for loc_ds bak_ds in ${(kv)work_datasets}; do (( i++ )) - printf $column_widths $i $loc_ds "${last_loc_snaps[$loc_ds]}" $bak_ds + est_changes[$loc_ds]=$(estimate_changes_bytes "$loc_ds" "${last_loc_snaps[$loc_ds]}") + (( total_bytes += est_changes[$loc_ds] )) + printf $column_widths $i $loc_ds "${last_loc_snaps[$loc_ds]}" $(human_size "${est_changes[$loc_ds]}") $bak_ds done printf "%-4s %-35s %-20s\n" "" "Инкрементный снимок:" "$inc_snapshot (*)" else i=0; for loc_ds bak_ds in ${(kv)work_datasets}; do (( i++ )) - printf $column_widths $i $loc_ds "$inc_snapshot (*)" "$bak_ds (*)" + est_changes[$loc_ds]=$(estimate_changes_bytes "$loc_ds" "") + (( total_bytes += est_changes[$loc_ds] )) + printf $column_widths $i $loc_ds "$inc_snapshot (*)" $(human_size "${est_changes[$loc_ds]}") "$bak_ds (*)" done fi -# --------- Выбор датасетов ---------- +# Итоговая сумма +echo +echo "Общий объём изменений: $(human_size $total_bytes)" +echo +echo "Свободное место на пулах бэкап-сервера:" +# Сортируем по имени пула (опционально) +for pool in ${(ko)backup_pool_free}; do + printf " %-20s : %s\n" "$pool" "${backup_pool_free[$pool]}" +done +echo + + +# --------- Выбор датасетов ---------- +total_bytes=0 if [[ $check = true ]]; then read 'work?Утвердите данные. "y" в работу | "1 2.." выбрать через пробел | НЕТ по умолчанию : ' if [[ $work = *[[:digit:]]* ]]; then @@ -356,13 +420,20 @@ if [[ $check = true ]]; then 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}" + (( total_bytes = est_changes[$loc_ds])) + printf $column_widths $i $loc_ds "${last_loc_snaps[$loc_ds]}" $(human_size "${est_changes[$loc_ds]}") $bak_ds fi done fi # Если был выведен список, то запросить запуск в работу нового списка if [[ ${#indx_list} > 0 ]]; then + # Итоговая сумма + total_human=$(numfmt --to=iec --format="%.2f" "$total_bytes" 2>/dev/null || echo "${total_bytes}B") + echo + echo "Общий объём изменений: $total_human" + echo + work="N" read "work?В работу (y/N) " fi @@ -421,7 +492,7 @@ fi # Вывод прогресса # Это отдельный блок программы, который не имеет прямого доступа к переменным процесса в фоне. # Обмен переменными проивится ерез временный файл -if [[ $progress == true ]]; then +if [[ $progress == true ]] && { (( ! ${+work} )) || [[ $work =~ ^[yY]$ ]]; }; then echo "" pids=$(ps -t $(tty | sed 's|/dev/||') -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- 'grep' | grep -v -- 'progress-only' ) # Список PIDs с командой запуска @@ -433,7 +504,8 @@ if [[ $progress == true ]]; then pids=$(ps a -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- 'grep' | grep -v -- 'progress-only' ) fi fi -echo $pids + + echo $pids # Выводить прогресс запрашивая список запущенных процессов if [[ $pids = "" ]]; then echo "Нет запущенного процесса резервирования"