]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
Add new exec-borg script to contrib/
authorDarshit Shah <git@darnir.net>
Sat, 31 Dec 2022 02:36:53 +0000 (03:36 +0100)
committerMatthias Runge <mrunge@matthias-runge.de>
Thu, 5 Jan 2023 15:44:53 +0000 (16:44 +0100)
contrib/README
contrib/exec-borg [new file with mode: 0755]

index 6e2ea19e2ab676daaf182082415f967895297466..72c6baf0468ca2285224b9c89aa6b5dc78e2c8ba 100644 (file)
@@ -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 (executable)
index 0000000..26ca9c4
--- /dev/null
@@ -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