Valentin Lab
3 weeks ago
4 changed files with 206 additions and 2 deletions
-
17rsync-backup-target/build/Dockerfile
-
108rsync-backup-target/build/src/usr/local/sbin/import-log-chunks
-
75rsync-backup-target/hooks/init
-
8rsync-backup-target/metadata.yml
@ -0,0 +1,108 @@ |
|||
#!/bin/bash |
|||
|
|||
RSYNC_LOG_PATH="${RSYNC_LOG_PATH:-/var/log/rsync}" |
|||
RSYNC_DB_FILE="${RSYNC_DB_FILE:-$RSYNC_LOG_PATH/logchunks.db}" |
|||
RSYNC_FAILED_CHUNKS_PATH="${RSYNC_FAILED_CHUNKS_PATH:-$RSYNC_LOG_PATH/failed_chunks}" |
|||
|
|||
is_btrfs_subvolume() { |
|||
local dir=$1 |
|||
[ "$(stat -f --format="%T" "$dir")" == "btrfs" ] || return 1 |
|||
inode="$(stat --format="%i" "$dir")" |
|||
case "$inode" in |
|||
2|256) |
|||
return 0;; |
|||
*) |
|||
return 1;; |
|||
esac |
|||
} |
|||
|
|||
time_now() { date +%s.%3N; } |
|||
time_elapsed() { echo "scale=3; $2 - $1" | bc; } |
|||
|
|||
|
|||
|
|||
if ! [ -d "$RSYNC_LOG_PATH" ]; then |
|||
echo "Error: RSYNC_LOG_PATH is not a directory: $RSYNC_LOG_PATH" >&2 |
|||
exit 1 |
|||
fi |
|||
|
|||
if ! is_btrfs_subvolume "$RSYNC_LOG_PATH"; then |
|||
echo "Error: RSYNC_LOG_PATH is not a Btrfs subvolume: $RSYNC_LOG_PATH" >&2 |
|||
exit 1 |
|||
fi |
|||
|
|||
for cmd in btrfs logchunk; do |
|||
if ! type -p "$cmd" >/dev/null; then |
|||
echo "Error: $cmd command not found" >&2 |
|||
exit 1 |
|||
fi |
|||
done |
|||
|
|||
if ! [ -d "$RSYNC_FAILED_CHUNKS_PATH" ]; then |
|||
mkdir -p "$RSYNC_FAILED_CHUNKS_PATH" || { |
|||
echo "Error: Failed to create RSYNC_FAILED_CHUNKS_PATH directory: $RSYNC_FAILED_CHUNKS_PATH" >&2 |
|||
exit 1 |
|||
} |
|||
fi |
|||
|
|||
rsync_log_work_dir="${RSYNC_LOG_PATH}.logchunk" |
|||
if [ -e "$rsync_log_work_dir" ]; then |
|||
echo "Error: RSYNC_LOG_PATH work directory already exists: $rsync_log_work_dir" >&2 |
|||
exit 1 |
|||
fi |
|||
|
|||
btrfs subvolume snapshot -r "$RSYNC_LOG_PATH" "$rsync_log_work_dir" || { |
|||
echo "Error: Failed to create snapshot of RSYNC_LOG_PATH" >&2 |
|||
exit 1 |
|||
} |
|||
trap "btrfs subvolume delete '$rsync_log_work_dir'" EXIT |
|||
|
|||
start=$(time_now) |
|||
|
|||
for log_file in "$rsync_log_work_dir"/target_*_rsync.log; do |
|||
ident="${log_file##*/}" |
|||
ident="${ident#target_}" |
|||
ident="${ident%_rsync.log}" |
|||
errors=0 |
|||
chunks=0 |
|||
start_ident=$(time_now) |
|||
start_log_line="${start_ident%.*}" |
|||
echo "$ident:" |
|||
last_chunk_count=0 |
|||
last_error_count=0 |
|||
while true; do |
|||
logchunk next -c logchunk "$log_file" | |
|||
logchunk import "${RSYNC_DB_FILE}" "$ident" "$RSYNC_FAILED_CHUNKS_PATH" 2>&1 | |
|||
sed -r "s/^/ | /" |
|||
pipe_status=("${PIPESTATUS[@]}") |
|||
if [ "${pipe_status[0]}" == 1 ]; then |
|||
## no new chunks |
|||
break |
|||
fi |
|||
if [ "${pipe_status[0]}" == 127 ]; then |
|||
echo "Error: fatal !" >&2 |
|||
exit 1 |
|||
fi |
|||
errlvl="${pipe_status[1]}" |
|||
if [ "$errlvl" != 0 ]; then |
|||
errors=$((errors + 1)) |
|||
fi |
|||
chunks=$((chunks + 1)) |
|||
now=$(time_now) |
|||
now="${now%.*}" |
|||
if [ $((now - start_log_line)) -gt 15 ]; then |
|||
rate=$(echo "scale=2; ($chunks - $last_chunk_count) / ($now - $start_log_line)" | bc) |
|||
echo " |~ processed $((chunks - last_chunk_count)) chunks with $((errors - last_error_count)) errors ($rate chunks/s)" |
|||
start_log_line="$now" |
|||
last_chunk_count=$chunks |
|||
last_error_count=$errors |
|||
fi |
|||
done |
|||
if [ "$chunks" != 0 ]; then |
|||
elapsed_ident="$(time_elapsed "$start_ident" "$(time_now)")" || exit 1 |
|||
echo " .. processed $chunks chunks with $errors errors in ${elapsed_ident}s" |
|||
fi |
|||
done |
|||
|
|||
elapsed="$(time_elapsed "$start" "$(time_now)")" || exit 1 |
|||
echo "Processed all logs in ${elapsed}s" |
Write
Preview
Loading…
Cancel
Save
Reference in new issue