#!/bin/bash ################################################ #### Wickr Enterprise Metrics Collector #### #### for Prometheus node_exporter textfile #### #### #### #### Author: Phil Connor #### #### Contact: contact@mylinux.work #### #### Version: 1.00-030726 #### ################################################ set -o pipefail SCRIPT_NAME=$(basename "$0") readonly SCRIPT_NAME # Default configuration readonly DEFAULT_NODE_DIR="/var/lib/node_exporter" readonly DEFAULT_COLLECTION_INTERVAL=300 readonly DEFAULT_CURL_TIMEOUT=30 # Configuration variables (can be overridden by environment) WICKR_ADMIN_URL=${WICKR_ADMIN_URL:-} WICKR_API_TOKEN=${WICKR_API_TOKEN:-} NODE_DIR=${NODE_DIR:-$DEFAULT_NODE_DIR} COLLECTION_INTERVAL=${COLLECTION_INTERVAL:-$DEFAULT_COLLECTION_INTERVAL} DEBUG=${DEBUG:-} # Runtime flags RUN_MODE="once" # Error tracking ERRORS_TOTAL=0 handle_error() { local exit_code=$1 local line_number=$2 echo "Error: $SCRIPT_NAME failed at line $line_number with exit code $exit_code" >&2 exit "$exit_code" } trap 'handle_error $? $LINENO' ERR debug_echo() { if [[ -n "$DEBUG" ]]; then echo "[DEBUG] $*" >&2 fi } show_help() { cat << EOF Usage: $SCRIPT_NAME [OPTIONS] Wickr Enterprise metrics collector for Prometheus node_exporter textfile directory. Collects user, bot, device, security group, and network statistics from the Wickr Enterprise Admin API and writes them as Prometheus metrics. OPTIONS: --once Run collection once and exit (default) --daemon Run continuously at COLLECTION_INTERVAL --help, -h Show this help message ENVIRONMENT VARIABLES: WICKR_ADMIN_URL Wickr Enterprise admin URL (required, e.g. https://wickr.example.com) WICKR_API_TOKEN Admin API token (required — generate in Admin Console → API Access) NODE_DIR Node exporter textfile directory (default: $DEFAULT_NODE_DIR) COLLECTION_INTERVAL Seconds between collections in daemon mode (default: $DEFAULT_COLLECTION_INTERVAL) DEBUG Enable debug output EXAMPLES: WICKR_ADMIN_URL=https://wickr.example.com WICKR_API_TOKEN=abc123 $SCRIPT_NAME --once WICKR_ADMIN_URL=https://wickr.example.com WICKR_API_TOKEN=abc123 $SCRIPT_NAME --daemon OUTPUT: Writes metrics to \$NODE_DIR/textfile_collector/wickr_metrics.prom EOF exit 0 } # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in --once) RUN_MODE="once"; shift ;; --daemon) RUN_MODE="daemon"; shift ;; --help|-h) show_help ;; *) echo "Unknown option: $1" >&2; show_help ;; esac done # Validate required variables validate_config() { if [[ -z "$WICKR_ADMIN_URL" ]]; then echo "Error: WICKR_ADMIN_URL is required" >&2 exit 1 fi if [[ -z "$WICKR_API_TOKEN" ]]; then echo "Error: WICKR_API_TOKEN is required" >&2 exit 1 fi # Strip trailing slash WICKR_ADMIN_URL="${WICKR_ADMIN_URL%/}" # Check textfile collector directory local textfile_dir="${NODE_DIR}/textfile_collector" if [[ ! -d "$textfile_dir" ]]; then echo "Error: Textfile collector directory not found: $textfile_dir" >&2 echo "Create it: sudo mkdir -p $textfile_dir" >&2 exit 1 fi } # API helper — makes authenticated requests to the Wickr Admin API wickr_api() { local endpoint="$1" local url="${WICKR_ADMIN_URL}/api/v1${endpoint}" debug_echo "GET $url" local response response=$(curl -sf --max-time "$DEFAULT_CURL_TIMEOUT" \ -H "Authorization: Bearer ${WICKR_API_TOKEN}" \ -H "Accept: application/json" \ "$url" 2>/dev/null) local exit_code=$? if [[ $exit_code -ne 0 ]]; then debug_echo "API call failed: $url (exit code $exit_code)" ERRORS_TOTAL=$((ERRORS_TOTAL + 1)) return 1 fi echo "$response" } # Count items from a paginated API response # Uses jq to count array items, handles pagination via response headers wickr_api_count() { local endpoint="$1" local url="${WICKR_ADMIN_URL}/api/v1${endpoint}" debug_echo "HEAD $url (count)" # Try to get total from response headers or count array items local response response=$(curl -sf --max-time "$DEFAULT_CURL_TIMEOUT" \ -H "Authorization: Bearer ${WICKR_API_TOKEN}" \ -H "Accept: application/json" \ "$url" 2>/dev/null) if [[ $? -ne 0 ]]; then debug_echo "API count failed: $url" ERRORS_TOTAL=$((ERRORS_TOTAL + 1)) echo "0" return 1 fi # Try to count array items if command -v jq &>/dev/null; then local count count=$(echo "$response" | jq 'if type == "array" then length elif .data? and (.data | type == "array") then .data | length elif .total? then .total elif .count? then .count else 0 end' 2>/dev/null) echo "${count:-0}" else echo "0" fi } # Collect user metrics collect_users() { debug_echo "Collecting user metrics..." local users_response users_response=$(wickr_api "/users") || return 1 if command -v jq &>/dev/null && [[ -n "$users_response" ]]; then local total active suspended pending total=$(echo "$users_response" | jq 'if type == "array" then length elif .data? then (.data | length) elif .total? then .total else 0 end' 2>/dev/null) active=$(echo "$users_response" | jq '[if type == "array" then .[] elif .data? then .data[] else empty end | select(.status? == "active" or .status? == "Active")] | length' 2>/dev/null) suspended=$(echo "$users_response" | jq '[if type == "array" then .[] elif .data? then .data[] else empty end | select(.status? == "suspended" or .status? == "Suspended")] | length' 2>/dev/null) pending=$(echo "$users_response" | jq '[if type == "array" then .[] elif .data? then .data[] else empty end | select(.status? == "pending" or .status? == "Pending")] | length' 2>/dev/null) cat </dev/null && [[ -n "$bots_response" ]]; then local total active total=$(echo "$bots_response" | jq 'if type == "array" then length elif .data? then (.data | length) elif .total? then .total else 0 end' 2>/dev/null) active=$(echo "$bots_response" | jq '[if type == "array" then .[] elif .data? then .data[] else empty end | select(.status? == "active" or .status? == "Active" or .status? == "online" or .status? == "Online")] | length' 2>/dev/null) cat </dev/null && [[ -n "$sg_response" ]]; then local total total=$(echo "$sg_response" | jq 'if type == "array" then length elif .data? then (.data | length) elif .total? then .total else 0 end' 2>/dev/null) cat </dev/null | while IFS= read -r line; do if [[ -z "$_sg_header_printed" ]]; then echo "# HELP wickr_security_group_users Number of users per security group." echo "# TYPE wickr_security_group_users gauge" _sg_header_printed=1 fi echo "$line" done echo "" fi } # Collect network metrics collect_networks() { debug_echo "Collecting network metrics..." local net_response net_response=$(wickr_api "/networks") || return 1 if command -v jq &>/dev/null && [[ -n "$net_response" ]]; then local total total=$(echo "$net_response" | jq 'if type == "array" then length elif .data? then (.data | length) elif .total? then .total else 0 end' 2>/dev/null) cat </dev/null && [[ -n "$users_response" ]]; then local device_count device_count=$(echo "$users_response" | jq ' [if type == "array" then .[] elif .data? then .data[] else empty end | (.devices // .device_count // 0) | if type == "array" then length else . end] | add // 0 ' 2>/dev/null) cat </dev/null; then local up=1 else local up=0 fi end_time=$(date +%s%N) duration_ms=$(( (end_time - start_time) / 1000000 )) cat < "$temp_file" 2>/dev/null mv "$temp_file" "$output_file" debug_echo "Collection complete. Wrote to $output_file (errors: $ERRORS_TOTAL)" } # Main main() { validate_config case "$RUN_MODE" in once) collect_all ;; daemon) echo "$SCRIPT_NAME running in daemon mode (interval: ${COLLECTION_INTERVAL}s)" while true; do collect_all sleep "$COLLECTION_INTERVAL" done ;; esac } main