#!/bin/bash
#############################################################
#### Nagios to Prometheus Bridge                         ####
#### Run Nagios check plugins and convert output to      ####
#### Prometheus textfile collector format                 ####
####                                                     ####
#### Author: Phil Connor                                 ####
#### Contact: contact@mylinux.work                       ####
#### License: MIT                                        ####
#### Version: 1.0                                        ####
####                                                     ####
#### Usage: ./nagios-to-prometheus-bridge.sh [OPTIONS]    ####
#############################################################
#
# Converts Nagios plugin output (exit codes + performance data)
# into Prometheus .prom files for the node_exporter textfile collector.
#
# Metrics generated:
#   - nagios_check_status           (exit code: 0=OK, 1=WARNING, 2=CRITICAL, 3=UNKNOWN)
#   - nagios_check_duration_seconds (execution time)
#   - nagios_check_last_run_timestamp (unix timestamp)
#   - nagios_check_perfdata_<label> (one gauge per performance data label)
#
# Requirements:
#   - Bash 4.0+
#   - Standard Linux utilities (grep, awk, sed, bc)
#   - The target Nagios plugin must be installed and executable
#
set -euo pipefail

#########################
### Configuration ###
#########################

NODE_DIR="${NODE_DIR:-/var/lib/node_exporter}"
METRIC_PREFIX="${METRIC_PREFIX:-nagios_check}"
DEBUG="${DEBUG:-}"

PLUGIN_CMD=""
CHECK_NAME=""
OUTPUT_FILE=""

#########################
### Logging ###
#########################

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log_info() {
    echo -e "${GREEN}[INFO]${NC} $1" >&2
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1" >&2
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1" >&2
}

log_debug() {
    [[ -n "$DEBUG" ]] && echo "[DEBUG] $1" >&2
}

#########################
### Parse Arguments ###
#########################

show_help() {
    cat <<EOF
Nagios to Prometheus Bridge

Runs a Nagios check plugin and converts its output to Prometheus
textfile collector format (.prom file).

Usage: $0 [OPTIONS]

OPTIONS:
  --plugin "COMMAND"     Nagios plugin command to execute (required)
  --name NAME            Check name for metric labels (required)
  --output PATH          Output .prom file path (default: <NODE_DIR>/<name>.prom)
  --help                 Show this help

ENVIRONMENT:
  NODE_DIR               Textfile collector directory (default: /var/lib/node_exporter)
  METRIC_PREFIX          Metric name prefix (default: nagios_check)
  DEBUG                  Enable debug output if set

EXAMPLES:
  $0 --plugin "/usr/lib/nagios/plugins/check_disk -w 20% -c 10% -p /" --name check_disk_root
  $0 --plugin "/usr/local/nagios/libexec/check_memory -w 80 -c 90" --name check_memory
  $0 --plugin "/usr/lib/nagios/plugins/check_http -H example.com -S -C 30" --name check_ssl --output /tmp/ssl.prom

EOF
    exit 0
}

parse_args() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --plugin)  PLUGIN_CMD="$2"; shift 2 ;;
            --name)    CHECK_NAME="$2"; shift 2 ;;
            --output)  OUTPUT_FILE="$2"; shift 2 ;;
            --help)    show_help ;;
            *)         log_error "Unknown option: $1"; exit 1 ;;
        esac
    done

    if [[ -z "$PLUGIN_CMD" ]]; then
        log_error "Missing required option: --plugin"
        exit 1
    fi

    if [[ -z "$CHECK_NAME" ]]; then
        log_error "Missing required option: --name"
        exit 1
    fi

    if [[ -z "$OUTPUT_FILE" ]]; then
        OUTPUT_FILE="${NODE_DIR}/${CHECK_NAME}.prom"
    fi
}

#########################
### Metric Helpers ###
#########################

sanitize_name() {
    local name="$1"
    name="${name,,}"                    # lowercase
    name="${name// /_}"                 # spaces to underscores
    name=$(echo "$name" | sed 's/[^a-z0-9_]/_/g')  # remove invalid chars
    name=$(echo "$name" | sed 's/__*/_/g')          # collapse multiple underscores
    name=$(echo "$name" | sed 's/^_//; s/_$//')     # trim leading/trailing underscores
    echo "$name"
}

strip_unit() {
    local value="$1"
    # Remove common unit suffixes (MB, GB, KB, %, s, ms, us, B, etc.)
    value=$(echo "$value" | sed 's/[a-zA-Z%]*$//')
    echo "$value"
}

#########################
### Execute Plugin ###
#########################

run_plugin() {
    log_debug "Executing: $PLUGIN_CMD"

    local start_time end_time duration
    local plugin_output=""
    local exit_code=0

    start_time=$(date +%s%N)

    # Run the plugin command, capturing output and exit code
    plugin_output=$(eval "$PLUGIN_CMD" 2>&1) || exit_code=$?

    end_time=$(date +%s%N)
    duration=$(echo "scale=3; ($end_time - $start_time) / 1000000000" | bc 2>/dev/null || echo "0")

    log_debug "Exit code: $exit_code"
    log_debug "Output: $plugin_output"
    log_debug "Duration: ${duration}s"

    # Build metrics output
    local metrics=""
    local timestamp
    timestamp=$(date +%s)

    # Status metric
    metrics+="# HELP ${METRIC_PREFIX}_status Nagios check exit code (0=OK 1=WARNING 2=CRITICAL 3=UNKNOWN)
# TYPE ${METRIC_PREFIX}_status gauge
${METRIC_PREFIX}_status{check=\"${CHECK_NAME}\"} ${exit_code}
"

    # Duration metric
    metrics+="# HELP ${METRIC_PREFIX}_duration_seconds Time taken to execute the check
# TYPE ${METRIC_PREFIX}_duration_seconds gauge
${METRIC_PREFIX}_duration_seconds{check=\"${CHECK_NAME}\"} ${duration}
"

    # Last run timestamp
    metrics+="# HELP ${METRIC_PREFIX}_last_run_timestamp Unix timestamp of last check execution
# TYPE ${METRIC_PREFIX}_last_run_timestamp gauge
${METRIC_PREFIX}_last_run_timestamp{check=\"${CHECK_NAME}\"} ${timestamp}
"

    # Parse performance data
    local perfdata=""
    if echo "$plugin_output" | grep -q '|'; then
        perfdata=$(echo "$plugin_output" | sed 's/^[^|]*|//' | sed 's/^ *//')
        log_debug "Perfdata raw: $perfdata"
    fi

    if [[ -n "$perfdata" ]]; then
        # Parse each perfdata item: label=value;warn;crit;min;max
        local item label raw_value value metric_name
        for item in $perfdata; do
            [[ "$item" == *"="* ]] || continue

            label="${item%%=*}"
            raw_value="${item#*=}"

            # Extract the value (first field before semicolons)
            value="${raw_value%%;*}"

            # Strip quotes from label
            label="${label//\'/}"
            label="${label//\"/}"

            # Strip unit suffix from value
            value=$(strip_unit "$value")

            # Validate value is numeric
            if ! echo "$value" | grep -qE '^-?[0-9]*\.?[0-9]+$'; then
                log_debug "Skipping non-numeric perfdata: $label=$value"
                continue
            fi

            metric_name=$(sanitize_name "$label")

            metrics+="# HELP ${METRIC_PREFIX}_perfdata_${metric_name} Performance data: ${label}
# TYPE ${METRIC_PREFIX}_perfdata_${metric_name} gauge
${METRIC_PREFIX}_perfdata_${metric_name}{check=\"${CHECK_NAME}\"} ${value}
"
        done
    fi

    # Atomic write: write to temp file then move
    local tmpfile
    tmpfile=$(mktemp "${OUTPUT_FILE}.XXXXXX")

    echo "$metrics" > "$tmpfile"
    mv "$tmpfile" "$OUTPUT_FILE"

    log_info "Metrics written to ${OUTPUT_FILE} (exit_code=${exit_code}, perfdata_items=$(echo "$perfdata" | wc -w))"
}

#########################
### Main ###
#########################

main() {
    parse_args "$@"

    # Ensure output directory exists
    local output_dir
    output_dir=$(dirname "$OUTPUT_FILE")
    mkdir -p "$output_dir"

    run_plugin
}

main "$@"
