59 lines
3.5 KiB
Bash
59 lines
3.5 KiB
Bash
#!/bin/bash
|
|
# Вторая копия /mnt/backup на 1 ТБ диск в роутере Huawei (SFTP :8022, remote `router:` в
|
|
# ~rus/.config/rclone/rclone.conf). Диск питается от роутера — не нагружает USB малины.
|
|
#
|
|
# router-backup run — ежедневно (таймер 06:00)
|
|
# router-backup verify — по воскресеньям после run: размеры + перечитать свежие архивы серверов по sha256
|
|
#
|
|
# Пишем прямо в remote, а не через FUSE /mnt/router: так надёжнее и есть коды ошибок.
|
|
# Роутер не умеет хэши (shell_type=none) → сравнение по размеру+mtime, целостность — перечиткой.
|
|
set -uo pipefail
|
|
C=/home/rus/.config/rclone/rclone.conf
|
|
R=router:rpi-backup
|
|
SRC=/mnt/backup
|
|
TS=$(date +%F_%H%M)
|
|
LOGDIR=$SRC/router-logs; mkdir -p "$LOGDIR"
|
|
LOG="$LOGDIR/router-$TS.log"
|
|
rc() { rclone --config "$C" --log-file "$LOG" --log-level INFO --stats 10m --stats-log-level NOTICE \
|
|
--transfers 2 --checkers 4 --sftp-concurrency 8 --retries 5 --low-level-retries 20 "$@"; }
|
|
|
|
mountpoint -q "$SRC" || { echo "$SRC не смонтирован — пропуск"; exit 1; }
|
|
rclone --config "$C" lsd router: >/dev/null 2>&1 || { echo "роутер недоступен по SFTP"; exit 1; }
|
|
|
|
run() {
|
|
local r=0
|
|
# 1) то, что никогда не удаляется: архив версий, манифесты, логи — только copy
|
|
for d in archive manifests logs; do
|
|
[ -d "$SRC/onedrive/$d" ] && { rc copy "$SRC/onedrive/$d" "$R/onedrive/$d" || r=1; }
|
|
done
|
|
# 2) зеркало current. Удалённое из current локально уже лежит в archive (скопированном выше).
|
|
# --max-delete: если локально «пропало» много файлов (сбой диска/монтирования) — стоп, а не зачистка копии
|
|
rc sync "$SRC/onedrive/current" "$R/onedrive/current" --exclude '*.partial' --max-delete 1000 || r=1
|
|
# 3) зашифрованные бэкапы серверов — зеркало (ротация 14/8/12 → удаляется пара файлов в день)
|
|
rc sync "$SRC/servers" "$R/servers" --max-delete 50 || r=1
|
|
echo "run: $([ $r = 0 ] && echo OK || echo 'ЕСТЬ ОШИБКИ') — лог $LOG"
|
|
return $r
|
|
}
|
|
|
|
verify() {
|
|
local r=0 s rel want got
|
|
# размеры всего дерева (только листинг, без чтения данных)
|
|
rc check "$SRC/onedrive/current" "$R/onedrive/current" --size-only --one-way --exclude '*.partial' || r=1
|
|
rc check "$SRC/servers" "$R/servers" --size-only --one-way || r=1
|
|
# перечитать с роутера последний daily каждого сервера и сверить с sha256
|
|
for s in $(ls -1t "$SRC"/servers/*/daily/*.age.sha256 2>/dev/null | awk -F/ '!seen[$(NF-2)]++'); do
|
|
rel=${s#"$SRC/servers/"}; rel=${rel%.sha256}
|
|
want=$(cut -d' ' -f1 "$s")
|
|
got=$(rclone --config "$C" cat "$R/servers/$rel" 2>>"$LOG" | sha256sum | cut -d' ' -f1)
|
|
if [ "$want" = "$got" ]; then echo "read-back OK $rel"; else echo "read-back BAD $rel"; r=1; fi
|
|
done
|
|
echo "verify: $([ $r = 0 ] && echo OK || echo 'ЕСТЬ РАСХОЖДЕНИЯ')"
|
|
return $r
|
|
}
|
|
|
|
case "${1:-run}" in
|
|
run) run; r=$?; [ "$(date +%u)" = 7 ] && { verify || r=1; }; exit $r ;;
|
|
verify) verify ;;
|
|
*) echo "usage: router-backup run|verify" >&2; exit 2 ;;
|
|
esac
|