diff --git a/script/getlast.sh b/script/getlast.sh new file mode 100644 index 0000000..714ee66 --- /dev/null +++ b/script/getlast.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash +# +# zrepl_last_snapshot_age.sh +# +# Показывает, сколько минут назад был создан последний снапшот +# (по умолчанию с префиксом "zrepl_") на резервной (приёмной) стороне. +# +# Использование: +# ./zrepl_last_snapshot_age.sh <датасет> [<датасет2> ...] +# ./zrepl_last_snapshot_age.sh -r <датасет> # рекурсивно по всем дочерним +# ./zrepl_last_snapshot_age.sh -p zrepl_ -r chunk_9XG1J8T0/nomad +# +# Опции: +# -r рекурсивно проверять все дочерние датасеты +# -p PREFIX фильтровать снапшоты по префиксу имени (по умолчанию "zrepl_") +# -w MINUTES задать порог в минутах: если снапшот старше — exit code 1 +# (удобно для мониторинга / cron) +# -q тихий режим: не печатать построчный вывод, только итог +# +# Коды возврата: +# 0 — всё ок (снапшот найден, и если задан -w, он не старше порога) +# 1 — снапшот найден, но старше порога -w +# 2 — снапшот не найден вообще (для датасета/выборки) +# 3 — ошибка использования / zfs недоступен + +set -euo pipefail + +PREFIX="zrepl_" +RECURSIVE=0 +WARN_MINUTES="" +QUIET=0 + +usage() { + grep '^#' "$0" | sed -e '1d' -e 's/^# \{0,1\}//' + exit 3 +} + +while getopts ":rp:w:qh" opt; do + case "$opt" in + r) RECURSIVE=1 ;; + p) PREFIX="$OPTARG" ;; + w) WARN_MINUTES="$OPTARG" ;; + q) QUIET=1 ;; + h) usage ;; + *) usage ;; + esac +done +shift $((OPTIND - 1)) + +if [[ $# -lt 1 ]]; then + echo "Ошибка: укажите хотя бы один датасет." >&2 + usage +fi + +if ! command -v zfs >/dev/null 2>&1; then + echo "Ошибка: команда zfs не найдена." >&2 + exit 3 +fi + +now_epoch=$(date +%s) + +overall_newest_epoch=0 +overall_newest_name="" +any_found=0 + +for dataset in "$@"; do + zfs_opts=(-H -p -o name,creation -t snapshot -s creation) + if [[ $RECURSIVE -eq 1 ]]; then + zfs_opts+=(-r) + else + zfs_opts+=(-d 1) + fi + + # -H без заголовков, -p значения в "машинном" виде (creation -> epoch seconds) + mapfile -t lines < <(zfs list "${zfs_opts[@]}" "$dataset" 2>/dev/null | grep "@${PREFIX}" || true) + + if [[ ${#lines[@]} -eq 0 ]]; then + if [[ $QUIET -eq 0 ]]; then + echo "[$dataset] снапшотов с префиксом '${PREFIX}' не найдено" + fi + continue + fi + + # последняя строка — самый свежий снапшот (сортировка -s creation) + last_line="${lines[-1]}" + snap_name="${last_line%%$'\t'*}" + snap_epoch="${last_line##*$'\t'}" + + age_seconds=$(( now_epoch - snap_epoch )) + age_minutes=$(( age_seconds / 60 )) + + any_found=1 + if (( snap_epoch > overall_newest_epoch )); then + overall_newest_epoch=$snap_epoch + overall_newest_name=$snap_name + fi + + if [[ $QUIET -eq 0 ]]; then + printf "[%s] последний снапшот: %s (%d мин. назад)\n" \ + "$dataset" "$snap_name" "$age_minutes" + fi +done + +if [[ $any_found -eq 0 ]]; then + echo "Ни одного снапшота с префиксом '${PREFIX}' не найдено ни в одном из указанных датасетов." >&2 + exit 2 +fi + +overall_age_minutes=$(( (now_epoch - overall_newest_epoch) / 60 )) + +echo "---" +printf "Самый свежий снапшот в выборке: %s (%d мин. назад)\n" \ + "$overall_newest_name" "$overall_age_minutes" + +if [[ -n "$WARN_MINUTES" ]]; then + if (( overall_age_minutes > WARN_MINUTES )); then + echo "ВНИМАНИЕ: последний снапшот старше порога ${WARN_MINUTES} мин." >&2 + exit 1 + fi +fi + +exit 0 \ No newline at end of file