From: Darshit Shah Date: Sat, 31 Dec 2022 02:36:53 +0000 (+0100) Subject: Add new exec-borg script to contrib/ X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=26448fc20dad02664ed7607137dedc5cdae6f2b0;p=thirdparty%2Fcollectd.git Add new exec-borg script to contrib/ --- diff --git a/contrib/README b/contrib/README index 6e2ea19e2..72c6baf04 100644 --- a/contrib/README +++ b/contrib/README @@ -64,6 +64,15 @@ Then run it with the required bind-mounts: --name collectd my_collectd $ docker exec -it collectd collectdctl listval +exec-borg +--------- + Script to be used with the exec-plugin (see collectd-exec(5)) for collecting +information about Borg Backup repositories. Often, one is unable to run collectd +on the Borg Server since it is a remote machine without shell access; hence this +script allows accessing the repository over SSH to extract the statistics. +It doesn't currently use a config file and will need to be explicitly modified to +provide the repository information + exec-munin.px ------------- Script to be used with the exec-plugin (see collectd-exec(5) for details) diff --git a/contrib/exec-borg b/contrib/exec-borg new file mode 100755 index 000000000..26ca9c4b7 --- /dev/null +++ b/contrib/exec-borg @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +# collectd - contrib/exec-borg +# Copyright (C) 2022 Darshit Shah +# License: GPLv2 + +# Script to collect BorgBackup statistics for collectd using the exec plugin (collectd-exec(5)) +# +# This script uses borg(1) to collect information about BorgBackup repositories and exports them. +# Since Borg in written in Python and does not export any API, the exec plugin is likely +# the best (and only) way to collect this information. The Borg commands may take quite long +# to execute and this must be considered when executing this script. +# +# Dependencies: +# * Bash 5+ (Uses Associative Arrays) +# * Jq + +readonly HOSTAME="${COLLECTD_HOSTNAME:-$(hostname -f)}" +# Modify this to your needs. For mine, collecting backup stats once an hour is enough +readonly INTERVAL="${COLLECTD_INTERVAL:-3600}" + +readonly REMOTE_HOST="ssh://example.com" +readonly BASEDIR="." +declare -A REPOS +REPOS["MyRepoName"]='MySuperSecretPasscode' + +while sleep "$INTERVAL"; do + # Since this is a long running script, we can expect the date to change during its execution + TODAY="$(date +%Y-%m-%d)" + + for repo_name in "${!REPOS[@]}"; do + export BORG_PASSPHRASE="${REPOS[$repo_name]}" + + borg_repo="$REMOTE_HOST/$BASEDIR/$repo_name" + + archives="$(borg list --json "$borg_repo")" + borg_info="$(borg info --json "$borg_repo")" + + last_archive_name="$(jq -r '.archives[-1].name' <<< "$archives")" + last_archive="$(borg info --json "${borg_repo}::${last_archive_name}")" + + # Get the UNIX timestamp for the last archive. + # It is unfortunate that Borg does not provide a way to get the raw timestamp directly. + # It will always convert to a human-readable timestamp which we now convert back + last_archive_ts="$(jq '.archives[-1].time | split(".")[0] + "Z" | fromdate' <<< "$archives")" + + total_size="$(jq '.cache.stats.total_size' <<< "$borg_info")" + total_size_compressed="$(jq '.cache.stats.total_csize' <<< "$borg_info")" + total_size_dedup="$(jq '.cache.stats.unique_csize' <<< "$borg_info")" + + num_archives=$(jq '.archives | length' <<< "$archives") + num_archives_today=$(jq ".archives | map(select(.start | test(\"$today\"))) | length" <<< "$archives") + + num_files="$(jq '.archives[0].stats.nfiles' <<< "$last_archive")" + num_chunks="$(jq '.cache.stats.total_chunks' <<< "$last_archive")" + num_uchunks="$(jq '.cache.stats.total_unique_chunks' <<< "$last_archive")" + + archive_size="$(jq '.archives[0].stats.original_size' <<< "$last_archive")" + archive_compressed_size="$(jq '.archives[0].stats.compressed_size' <<< "$last_archive")" + archive_dedup_size="$(jq '.archives[0].stats.deduplicated_size' <<< "$last_archive")" + + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/timestamp-last_archive interval=$INTERVAL N:$last_archive_ts" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/bytes-total_size interval=$INTERVAL N:$total_size" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/bytes-total_size_compressed interval=$INTERVAL N:$total_size_compressed" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/bytes-total_size_dedup interval=$INTERVAL N:$total_size_dedup" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/counter-archives_count interval=$INTERVAL N:$num_archives" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/counter-archives_count_today interval=$INTERVAL N:$num_archives_today" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/count-files_count interval=$INTERVAL N:$num_files" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/count-chunks interval=$INTERVAL N:$num_chunks" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/count-unique_chunks interval=$INTERVAL N:$num_uchunks" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/bytes-last_size interval=$INTERVAL N:$archive_size" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/bytes-last_size_compressed interval=$INTERVAL N:$archive_compressed_size" + echo "PUTVAL $HOSTNAME/exec-borg_${repo_name}/bytes-last_size_dedup interval=$INTERVAL N:$archive_dedup_size" + done + # Don't leave the passphrase in memory when we don't need it + # More importantly, ensure that we don't accidentally reuse a passphrase for the wrong repository + unset BORG_PASSPHRASE +done