a1a17e81a1
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
599 lines
18 KiB
Bash
599 lines
18 KiB
Bash
#!/bin/bash
|
|
################################################################################
|
|
# Script Name: nfs-server-exporter.sh
|
|
# Version: 1.0
|
|
# Description: Prometheus exporter for NFS server metrics — export status,
|
|
# thread pool utilization, per-operation counters, RPC errors,
|
|
# connected clients, and server health
|
|
#
|
|
# Author: Phil Connor
|
|
# Contact: contact@mylinux.work
|
|
# Website: https://mylinux.work
|
|
# License: MIT
|
|
#
|
|
# Prerequisites:
|
|
# - NFS server running (nfs-kernel-server / nfs-utils)
|
|
# - /proc/net/rpc/nfsd available
|
|
# - /proc/fs/nfsd available
|
|
# - exportfs command available
|
|
# - netcat (nc) for HTTP mode
|
|
#
|
|
# Usage:
|
|
# sudo ./nfs-server-exporter.sh
|
|
# sudo ./nfs-server-exporter.sh --http -p 9588
|
|
# sudo ./nfs-server-exporter.sh --textfile
|
|
#
|
|
# Metrics Exported:
|
|
# - nfsd_up - Exporter status (1=up, 0=down)
|
|
# - nfsd_exports_total - Number of active exports
|
|
# - nfsd_export_info{export,client,options} - Export configuration
|
|
# - nfsd_threads_total - Configured NFS server threads
|
|
# - nfsd_threads_busy - Threads currently handling requests
|
|
# - nfsd_pool_packets_total{pool} - Packets received per pool
|
|
# - nfsd_pool_sockets_enqueued_total{pool} - Sockets enqueued per pool
|
|
# - nfsd_pool_threads_woken_total{pool} - Threads woken per pool
|
|
# - nfsd_pool_threads_timedout_total{pool} - Thread timeouts per pool
|
|
# - nfsd_rpc_calls_total - Total RPC calls received
|
|
# - nfsd_rpc_bad_calls_total - Total bad RPC calls
|
|
# - nfsd_rpc_bad_auth_total - Total bad auth attempts
|
|
# - nfsd_rpc_bad_client_total - Total bad client calls
|
|
# - nfsd_v3_ops_total{op} - NFSv3 per-operation counters
|
|
# - nfsd_v4_ops_total{op} - NFSv4 per-operation counters
|
|
# - nfsd_io_read_bytes_total - Total bytes read by server
|
|
# - nfsd_io_write_bytes_total - Total bytes written by server
|
|
# - nfsd_clients_total - Connected NFSv4 clients
|
|
# - nfsd_file_handles_stale_total - Total stale file handle replies
|
|
# - nfsd_exporter_duration_seconds - Script execution time
|
|
# - nfsd_exporter_last_run_timestamp - Last successful run time
|
|
#
|
|
# Configuration:
|
|
# Default HTTP port: 9588
|
|
# Textfile directory: /var/lib/node_exporter
|
|
#
|
|
################################################################################
|
|
|
|
set -o pipefail
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION VARIABLES
|
|
# ============================================================================
|
|
|
|
TEXTFILE_DIR="/var/lib/node_exporter"
|
|
OUTPUT_FILE=""
|
|
HTTP_MODE=false
|
|
HTTP_PORT=9588
|
|
LOCK_FILE="/tmp/nfs-server-exporter.lock"
|
|
PROC_NFSD="/proc/net/rpc/nfsd"
|
|
NFSD_DIR="/proc/fs/nfsd"
|
|
|
|
# ============================================================================
|
|
# LOGGING FUNCTIONS
|
|
# ============================================================================
|
|
|
|
RED='\033[0;31m'
|
|
YELLOW='\033[1;33m'
|
|
GREEN='\033[0;32m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[INFO]${NC} $*" >&2; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*" >&2; }
|
|
error(){ echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
|
|
|
# ============================================================================
|
|
# HELPER FUNCTIONS
|
|
# ============================================================================
|
|
|
|
show_usage() {
|
|
cat <<EOF
|
|
Usage: $0 [OPTIONS]
|
|
|
|
Export NFS server statistics as Prometheus metrics.
|
|
|
|
MODES:
|
|
--textfile Write to node_exporter textfile collector
|
|
--http Run HTTP server on port $HTTP_PORT
|
|
|
|
OPTIONS:
|
|
-p, --port HTTP port (default: 9588)
|
|
-o, --output Output file path
|
|
|
|
EXAMPLES:
|
|
sudo $0 --textfile
|
|
sudo $0 --http --port 9588
|
|
sudo $0 -o /tmp/nfsd.prom
|
|
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--help) show_usage ;;
|
|
--textfile) OUTPUT_FILE="$TEXTFILE_DIR/nfsd.prom"; shift ;;
|
|
--http) HTTP_MODE=true; shift ;;
|
|
-p|--port) HTTP_PORT="$2"; shift 2 ;;
|
|
-o|--output) OUTPUT_FILE="$2"; shift 2 ;;
|
|
*) error "Unknown option: $1"; exit 1 ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
acquire_lock() {
|
|
if [ -f "$LOCK_FILE" ]; then
|
|
local lock_pid
|
|
lock_pid=$(cat "$LOCK_FILE" 2>/dev/null)
|
|
if [ -n "$lock_pid" ] && kill -0 "$lock_pid" 2>/dev/null; then
|
|
error "Another instance is running (PID $lock_pid)"
|
|
return 1
|
|
fi
|
|
rm -f "$LOCK_FILE"
|
|
fi
|
|
echo $$ > "$LOCK_FILE"
|
|
trap 'rm -f "$LOCK_FILE"' EXIT
|
|
return 0
|
|
}
|
|
|
|
# Check if NFS server is running and proc files are available
|
|
check_nfsd() {
|
|
if [ ! -f "$PROC_NFSD" ]; then
|
|
error "$PROC_NFSD not found — is the NFS server running?"
|
|
return 1
|
|
fi
|
|
if [ ! -d "$NFSD_DIR" ]; then
|
|
error "$NFSD_DIR not found"
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# Get number of configured NFS threads
|
|
# Returns: Thread count
|
|
get_thread_count() {
|
|
if [ -f "$NFSD_DIR/threads" ]; then
|
|
cat "$NFSD_DIR/threads" 2>/dev/null | head -1
|
|
else
|
|
echo "0"
|
|
fi
|
|
}
|
|
|
|
# Get pool stats from /proc/fs/nfsd/pool_stats
|
|
# Returns: Lines with "pool packets sockets_enqueued threads_woken threads_timedout"
|
|
get_pool_stats() {
|
|
if [ -f "$NFSD_DIR/pool_stats" ]; then
|
|
# Skip header line
|
|
tail -n +2 "$NFSD_DIR/pool_stats" 2>/dev/null
|
|
fi
|
|
}
|
|
|
|
# Get RPC stats from /proc/net/rpc/nfsd
|
|
# The "rc" line: hits misses nocache
|
|
# The "io" line: read write
|
|
# The "rpc" line: calls badcalls badfmt badauth badclnt
|
|
get_rpc_calls() {
|
|
awk '/^rpc / { print $2 }' "$PROC_NFSD" 2>/dev/null || echo "0"
|
|
}
|
|
|
|
get_rpc_bad_calls() {
|
|
awk '/^rpc / { print $3 }' "$PROC_NFSD" 2>/dev/null || echo "0"
|
|
}
|
|
|
|
get_rpc_bad_auth() {
|
|
awk '/^rpc / { print $5 }' "$PROC_NFSD" 2>/dev/null || echo "0"
|
|
}
|
|
|
|
get_rpc_bad_client() {
|
|
awk '/^rpc / { print $6 }' "$PROC_NFSD" 2>/dev/null || echo "0"
|
|
}
|
|
|
|
# Get server I/O bytes from /proc/net/rpc/nfsd "io" line
|
|
get_io_read_bytes() {
|
|
awk '/^io / { print $2 }' "$PROC_NFSD" 2>/dev/null || echo "0"
|
|
}
|
|
|
|
get_io_write_bytes() {
|
|
awk '/^io / { print $3 }' "$PROC_NFSD" 2>/dev/null || echo "0"
|
|
}
|
|
|
|
# Get stale file handle count from /proc/net/rpc/nfsd "fh" line
|
|
# fh: stale total_lookups anonlookups dirnocache nondir_nocache
|
|
get_stale_handles() {
|
|
awk '/^fh / { print $2 }' "$PROC_NFSD" 2>/dev/null || echo "0"
|
|
}
|
|
|
|
# Get NFSv3 per-operation counters from /proc/net/rpc/nfsd
|
|
# proc3 line: num_ops null getattr setattr lookup access readlink read write create mkdir symlink ...
|
|
get_v3_ops() {
|
|
awk '/^proc3 / {
|
|
ops[1]="null"; ops[2]="getattr"; ops[3]="setattr"; ops[4]="lookup"
|
|
ops[5]="access"; ops[6]="readlink"; ops[7]="read"; ops[8]="write"
|
|
ops[9]="create"; ops[10]="mkdir"; ops[11]="symlink"; ops[12]="mknod"
|
|
ops[13]="remove"; ops[14]="rmdir"; ops[15]="rename"; ops[16]="link"
|
|
ops[17]="readdir"; ops[18]="readdirplus"; ops[19]="fsstat"; ops[20]="fsinfo"
|
|
ops[21]="pathconf"; ops[22]="commit"
|
|
n = $2 + 0
|
|
for (i = 1; i <= n && i <= 22; i++) {
|
|
printf "%s %s\n", ops[i], $(i + 2)
|
|
}
|
|
}' "$PROC_NFSD" 2>/dev/null
|
|
}
|
|
|
|
# Get NFSv4 per-operation counters from /proc/net/rpc/nfsd
|
|
# proc4ops line: num_ops op0 op1 op2 ...
|
|
get_v4_ops() {
|
|
awk '/^proc4ops / {
|
|
ops[1]="op0-unused"; ops[2]="op1-unused"; ops[3]="access"; ops[4]="close"
|
|
ops[5]="commit"; ops[6]="create"; ops[7]="delegpurge"; ops[8]="delegreturn"
|
|
ops[9]="getattr"; ops[10]="getfh"; ops[11]="link"; ops[12]="lock"
|
|
ops[13]="lockt"; ops[14]="locku"; ops[15]="lookup"; ops[16]="lookupp"
|
|
ops[17]="nverify"; ops[18]="open"; ops[19]="openattr"; ops[20]="open_confirm"
|
|
ops[21]="open_downgrade"; ops[22]="putfh"; ops[23]="putpubfh"; ops[24]="putrootfh"
|
|
ops[25]="read"; ops[26]="readdir"; ops[27]="readlink"; ops[28]="remove"
|
|
ops[29]="rename"; ops[30]="renew"; ops[31]="restorefh"; ops[32]="savefh"
|
|
ops[33]="secinfo"; ops[34]="setattr"; ops[35]="setclientid"; ops[36]="setclientid_confirm"
|
|
ops[37]="verify"; ops[38]="write"; ops[39]="release_lockowner"
|
|
n = $2 + 0
|
|
for (i = 1; i <= n && i <= 39; i++) {
|
|
if (ops[i] !~ /unused/) {
|
|
printf "%s %s\n", ops[i], $(i + 2)
|
|
}
|
|
}
|
|
}' "$PROC_NFSD" 2>/dev/null
|
|
}
|
|
|
|
# Get active exports
|
|
# Tries /proc/fs/nfsd/exports first, then /var/lib/nfs/etab, then exportfs -v
|
|
# Returns: Lines with "export client(options)" — one per line
|
|
get_exports() {
|
|
local raw=""
|
|
|
|
# /proc/fs/nfsd/exports is most reliable (kernel export table)
|
|
if [ -f "$NFSD_DIR/exports" ]; then
|
|
raw=$(grep '^/' "$NFSD_DIR/exports" 2>/dev/null)
|
|
fi
|
|
|
|
# Fall back to etab (userspace export table)
|
|
if [ -z "$raw" ] && [ -f /var/lib/nfs/etab ]; then
|
|
raw=$(grep '^/' /var/lib/nfs/etab 2>/dev/null)
|
|
fi
|
|
|
|
# Fall back to exportfs command
|
|
if [ -z "$raw" ]; then
|
|
raw=$(/usr/sbin/exportfs -v 2>/dev/null || exportfs -v 2>/dev/null)
|
|
# Merge continuation lines
|
|
raw=$(echo "$raw" | awk '
|
|
/^\// {
|
|
if (line) print line
|
|
line = $0
|
|
next
|
|
}
|
|
{ line = line " " $0 }
|
|
END { if (line) print line }
|
|
')
|
|
fi
|
|
|
|
echo "$raw" | sed 's/[[:space:]]\+/ /g' | sed 's/^ //' | grep -v '^$'
|
|
}
|
|
|
|
# Count active exports
|
|
get_export_count() {
|
|
local count
|
|
count=$(get_exports | wc -l)
|
|
echo "${count:-0}"
|
|
}
|
|
|
|
# Count connected NFSv4 clients
|
|
get_client_count() {
|
|
if [ -d "$NFSD_DIR/clients" ]; then
|
|
local count
|
|
count=$(ls -1 "$NFSD_DIR/clients" 2>/dev/null | wc -l)
|
|
echo "${count:-0}"
|
|
else
|
|
echo "0"
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# METRIC GENERATION
|
|
# ============================================================================
|
|
|
|
generate_metrics() {
|
|
local script_start
|
|
script_start=$(date +%s)
|
|
|
|
if ! check_nfsd; then
|
|
cat <<EOF
|
|
# HELP nfsd_up NFS server exporter status
|
|
# TYPE nfsd_up gauge
|
|
nfsd_up 0
|
|
EOF
|
|
return
|
|
fi
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_up NFS server exporter status
|
|
# TYPE nfsd_up gauge
|
|
nfsd_up 1
|
|
|
|
EOF
|
|
|
|
# Export count and info
|
|
local export_count
|
|
export_count=$(get_export_count)
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_exports_total Number of active NFS exports
|
|
# TYPE nfsd_exports_total gauge
|
|
nfsd_exports_total $export_count
|
|
|
|
# HELP nfsd_export_info Export configuration information
|
|
# TYPE nfsd_export_info gauge
|
|
EOF
|
|
|
|
while IFS= read -r line; do
|
|
[ -z "$line" ] && continue
|
|
local export_path client_opts client opts
|
|
export_path=$(echo "$line" | awk '{print $1}')
|
|
client_opts=$(echo "$line" | awk '{print $2}')
|
|
client="${client_opts%%(*}"
|
|
opts="${client_opts#*(}"
|
|
opts="${opts%)}"
|
|
[ -z "$export_path" ] && continue
|
|
echo "nfsd_export_info{export=\"$export_path\",client=\"$client\",options=\"$opts\"} 1"
|
|
done < <(get_exports)
|
|
|
|
echo ""
|
|
|
|
# Thread pool metrics
|
|
local threads
|
|
threads=$(get_thread_count)
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_threads_total Configured NFS server threads
|
|
# TYPE nfsd_threads_total gauge
|
|
nfsd_threads_total ${threads:-0}
|
|
|
|
EOF
|
|
|
|
# Pool stats
|
|
local has_pool_stats=false
|
|
if [ -f "$NFSD_DIR/pool_stats" ]; then
|
|
has_pool_stats=true
|
|
fi
|
|
|
|
if [ "$has_pool_stats" = true ]; then
|
|
cat <<EOF
|
|
# HELP nfsd_pool_packets_total Packets received per thread pool
|
|
# TYPE nfsd_pool_packets_total counter
|
|
EOF
|
|
# pool_stats format: pool_num packets sockets_enqueued threads_woken threads_timedout
|
|
while read -r pool_num packets sockets_enqueued threads_woken threads_timedout; do
|
|
[ -z "$pool_num" ] && continue
|
|
echo "nfsd_pool_packets_total{pool=\"$pool_num\"} $packets"
|
|
done < <(get_pool_stats)
|
|
|
|
echo ""
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_pool_sockets_enqueued_total Sockets enqueued per thread pool
|
|
# TYPE nfsd_pool_sockets_enqueued_total counter
|
|
EOF
|
|
while read -r pool_num packets sockets_enqueued threads_woken threads_timedout; do
|
|
[ -z "$pool_num" ] && continue
|
|
echo "nfsd_pool_sockets_enqueued_total{pool=\"$pool_num\"} $sockets_enqueued"
|
|
done < <(get_pool_stats)
|
|
|
|
echo ""
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_pool_threads_woken_total Threads woken per thread pool
|
|
# TYPE nfsd_pool_threads_woken_total counter
|
|
EOF
|
|
while read -r pool_num packets sockets_enqueued threads_woken threads_timedout; do
|
|
[ -z "$pool_num" ] && continue
|
|
echo "nfsd_pool_threads_woken_total{pool=\"$pool_num\"} $threads_woken"
|
|
done < <(get_pool_stats)
|
|
|
|
echo ""
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_pool_threads_timedout_total Thread timeouts per thread pool
|
|
# TYPE nfsd_pool_threads_timedout_total counter
|
|
EOF
|
|
while read -r pool_num packets sockets_enqueued threads_woken threads_timedout; do
|
|
[ -z "$pool_num" ] && continue
|
|
echo "nfsd_pool_threads_timedout_total{pool=\"$pool_num\"} $threads_timedout"
|
|
done < <(get_pool_stats)
|
|
|
|
echo ""
|
|
fi
|
|
|
|
# RPC stats
|
|
local rpc_calls rpc_bad rpc_bad_auth rpc_bad_client
|
|
rpc_calls=$(get_rpc_calls)
|
|
rpc_bad=$(get_rpc_bad_calls)
|
|
rpc_bad_auth=$(get_rpc_bad_auth)
|
|
rpc_bad_client=$(get_rpc_bad_client)
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_rpc_calls_total Total RPC calls received
|
|
# TYPE nfsd_rpc_calls_total counter
|
|
nfsd_rpc_calls_total ${rpc_calls:-0}
|
|
|
|
# HELP nfsd_rpc_bad_calls_total Total bad RPC calls
|
|
# TYPE nfsd_rpc_bad_calls_total counter
|
|
nfsd_rpc_bad_calls_total ${rpc_bad:-0}
|
|
|
|
# HELP nfsd_rpc_bad_auth_total Total bad authentication attempts
|
|
# TYPE nfsd_rpc_bad_auth_total counter
|
|
nfsd_rpc_bad_auth_total ${rpc_bad_auth:-0}
|
|
|
|
# HELP nfsd_rpc_bad_client_total Total bad client calls
|
|
# TYPE nfsd_rpc_bad_client_total counter
|
|
nfsd_rpc_bad_client_total ${rpc_bad_client:-0}
|
|
|
|
EOF
|
|
|
|
# Server I/O bytes
|
|
local io_read io_write
|
|
io_read=$(get_io_read_bytes)
|
|
io_write=$(get_io_write_bytes)
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_io_read_bytes_total Total bytes read by NFS server
|
|
# TYPE nfsd_io_read_bytes_total counter
|
|
nfsd_io_read_bytes_total ${io_read:-0}
|
|
|
|
# HELP nfsd_io_write_bytes_total Total bytes written by NFS server
|
|
# TYPE nfsd_io_write_bytes_total counter
|
|
nfsd_io_write_bytes_total ${io_write:-0}
|
|
|
|
EOF
|
|
|
|
# Stale file handles
|
|
local stale
|
|
stale=$(get_stale_handles)
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_file_handles_stale_total Total stale file handle replies
|
|
# TYPE nfsd_file_handles_stale_total counter
|
|
nfsd_file_handles_stale_total ${stale:-0}
|
|
|
|
EOF
|
|
|
|
# NFSv3 per-operation counters
|
|
local v3_data
|
|
v3_data=$(get_v3_ops)
|
|
|
|
if [ -n "$v3_data" ]; then
|
|
cat <<EOF
|
|
# HELP nfsd_v3_ops_total NFSv3 per-operation call count
|
|
# TYPE nfsd_v3_ops_total counter
|
|
EOF
|
|
while read -r op count; do
|
|
[ -z "$op" ] && continue
|
|
echo "nfsd_v3_ops_total{op=\"$op\"} ${count:-0}"
|
|
done <<< "$v3_data"
|
|
|
|
echo ""
|
|
fi
|
|
|
|
# NFSv4 per-operation counters
|
|
local v4_data
|
|
v4_data=$(get_v4_ops)
|
|
|
|
if [ -n "$v4_data" ]; then
|
|
cat <<EOF
|
|
# HELP nfsd_v4_ops_total NFSv4 per-operation call count
|
|
# TYPE nfsd_v4_ops_total counter
|
|
EOF
|
|
while read -r op count; do
|
|
[ -z "$op" ] && continue
|
|
echo "nfsd_v4_ops_total{op=\"$op\"} ${count:-0}"
|
|
done <<< "$v4_data"
|
|
|
|
echo ""
|
|
fi
|
|
|
|
# Connected clients
|
|
local clients
|
|
clients=$(get_client_count)
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_clients_total Connected NFSv4 clients
|
|
# TYPE nfsd_clients_total gauge
|
|
nfsd_clients_total ${clients:-0}
|
|
|
|
EOF
|
|
|
|
# Exporter runtime
|
|
local script_end script_duration
|
|
script_end=$(date +%s)
|
|
script_duration=$((script_end - script_start))
|
|
|
|
cat <<EOF
|
|
# HELP nfsd_exporter_duration_seconds Time to generate all metrics
|
|
# TYPE nfsd_exporter_duration_seconds gauge
|
|
nfsd_exporter_duration_seconds $script_duration
|
|
|
|
# HELP nfsd_exporter_last_run_timestamp Unix timestamp of last successful run
|
|
# TYPE nfsd_exporter_last_run_timestamp gauge
|
|
nfsd_exporter_last_run_timestamp $script_end
|
|
EOF
|
|
|
|
echo ""
|
|
}
|
|
|
|
# ============================================================================
|
|
# HTTP SERVER MODE
|
|
# ============================================================================
|
|
|
|
run_http_server() {
|
|
log "Starting NFS server exporter on port $HTTP_PORT..."
|
|
|
|
if ! command -v nc >/dev/null 2>&1; then
|
|
error "netcat (nc) required for HTTP mode"
|
|
exit 1
|
|
fi
|
|
|
|
while true; do
|
|
{
|
|
read -r request
|
|
if [[ "$request" =~ ^GET\ /metrics ]]; then
|
|
echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain; version=0.0.4\r\n\r"
|
|
generate_metrics
|
|
else
|
|
echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r"
|
|
echo "<html><head><title>NFS Server Exporter</title></head><body><h1>NFS Server Prometheus Exporter</h1><p><a href=\"/metrics\">Metrics</a></p></body></html>"
|
|
fi
|
|
} | nc -l -p "$HTTP_PORT" -q 1 2>/dev/null
|
|
done
|
|
}
|
|
|
|
# ============================================================================
|
|
# MAIN EXECUTION
|
|
# ============================================================================
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
|
|
if ! acquire_lock; then
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$HTTP_MODE" = true ]; then
|
|
run_http_server
|
|
elif [ -n "$OUTPUT_FILE" ]; then
|
|
local output_dir
|
|
output_dir="$(dirname "$OUTPUT_FILE")"
|
|
mkdir -p "$output_dir"
|
|
|
|
local temp_file
|
|
temp_file=$(mktemp "${output_dir}/.nfsd_metrics.XXXXXX")
|
|
|
|
if ! generate_metrics > "$temp_file" 2>/dev/null; then
|
|
rm -f "$temp_file"
|
|
error "Failed to generate metrics"
|
|
exit 1
|
|
fi
|
|
|
|
local file_lines
|
|
file_lines=$(wc -l < "$temp_file" 2>/dev/null || echo 0)
|
|
|
|
if [ "$file_lines" -lt 5 ]; then
|
|
rm -f "$temp_file"
|
|
error "Metrics file too small ($file_lines lines), keeping previous"
|
|
exit 1
|
|
fi
|
|
|
|
chmod 644 "$temp_file"
|
|
mv -f "$temp_file" "$OUTPUT_FILE"
|
|
|
|
log "Metrics written to $OUTPUT_FILE ($file_lines lines)"
|
|
else
|
|
generate_metrics
|
|
fi
|
|
}
|
|
|
|
main "$@"
|