Files

114 lines
3.3 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
export LC_ALL=C
# May be overridden by Telegraf's environment or with a positional argument.
FSPATH="${1:-${FSPATH:-/mnt}}"
UUID="${BCACHEFS_UUID:-3d3a0763-4dfe-41e6-93c1-8c791ec98176}"
# Convert the rounded, binary human-readable values printed by bcachefs to
# bytes. The result is exact for the displayed value; bcachefs does not expose
# the additional precision that it rounded away (for example, in "4.73T").
human_to_bytes()
{
local value="$1"
local whole fraction unit multiplier scale bytes
if [[ ! $value =~ ^([0-9]+)([.]([0-9]+))?([KMGTPEkmgtpe]?)(i?[Bb])?$ ]]; then
return 1
fi
whole="${BASH_REMATCH[1]}"
fraction="${BASH_REMATCH[3]}"
unit="${BASH_REMATCH[4]^^}"
case "$unit" in
"") multiplier=1 ;;
K) multiplier=$((1024)) ;;
M) multiplier=$((1024 ** 2)) ;;
G) multiplier=$((1024 ** 3)) ;;
T) multiplier=$((1024 ** 4)) ;;
P) multiplier=$((1024 ** 5)) ;;
E) multiplier=$((1024 ** 6)) ;;
*) return 1 ;;
esac
bytes=$((10#$whole * multiplier))
if [[ -n $fraction ]]; then
scale=$((10 ** ${#fraction}))
# Round to the nearest byte instead of dropping the fractional byte.
bytes=$((bytes + (10#$fraction * multiplier + scale / 2) / scale))
fi
printf '%s\n' "$bytes"
}
escape_tag()
{
local value="$1"
value="${value//\\/\\\\}"
value="${value//,/\\,}"
value="${value// /\\ }"
value="${value//=/\\=}"
printf '%s' "$value"
}
declare -A data_bytes=()
declare -A metadata_bytes=()
types=(replicas checksum erasure_code compression target high_priority pending stripes)
scan_pending=""
rows=0
# Capture everything before printing a metric: on command or parser failure,
# Telegraf gets no partial point that could look like a valid zero reading.
if ! status="$(bcachefs reconcile status "$FSPATH")"; then
echo "bcachefs reconcile status failed for $FSPATH" >&2
exit 1
fi
while IFS= read -r line; do
if [[ $line =~ ^Scan[[:space:]]pending:[[:space:]]*([0-9]+)[[:space:]]*$ ]]; then
scan_pending="${BASH_REMATCH[1]}"
elif [[ $line =~ ^[[:space:]]*([[:alnum:]_]+):[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]*$ ]]; then
type="${BASH_REMATCH[1]}"
raw_data="${BASH_REMATCH[2]}"
raw_metadata="${BASH_REMATCH[3]}"
if ! data_bytes["$type"]="$(human_to_bytes "$raw_data")" ||
! metadata_bytes["$type"]="$(human_to_bytes "$raw_metadata")"; then
echo "Unable to parse reconcile values for $type: $raw_data $raw_metadata" >&2
exit 1
fi
((rows += 1))
fi
done <<< "$status"
if ((rows == 0)); then
echo "No reconcile values found for $FSPATH" >&2
exit 1
fi
printf 'bcachefs_reconcile,fspath=%s,uuid=%s ' \
"$(escape_tag "$FSPATH")" "$(escape_tag "$UUID")"
first=1
if [[ -n $scan_pending ]]; then
printf 'scan_pending=%s' "$scan_pending"
first=0
fi
for type in "${types[@]}"; do
# Current bcachefs versions print all types, including zero-valued ones.
# Skipping a missing type is safer than inventing a zero on format changes.
[[ -v data_bytes["$type"] ]] || continue
((first)) || printf ','
printf '%s_data=%s,%s_metadata=%s' \
"$type" "${data_bytes[$type]}" \
"$type" "${metadata_bytes[$type]}"
first=0
done
printf '\n'