I created a script to bulk acknowledge alerts

Since the update of June 18th, all LibreNMS alerts were reset to unaknowledged. I had about 180 unacknowledged alerts :exploding_head:

At first I began to acknowledge each alert one by one, but I quickly became tired of it. I looked around but couldn’t find a way to quickly acknowledge all outstanding alerts. So I turned to Claude.ai and it gave me this bash script:

#!/bin/bash

# Configuration
API_TOKEN="YOUR-API-TOKEN-HERE"
LIBRENMS_URL="https://your-librenms-address-here"

# Function to acknowledge a single alert
ack_alert() {
    local alert_id=$1
    local note="$2"

    echo "Acknowledging alert ID: $alert_id"

    if [ -n "$note" ]; then
        RESPONSE=$(curl -s -k -X PUT \
            -H "X-Auth-Token: $API_TOKEN" \
            -d "note=$note" \
            "$LIBRENMS_URL/api/v0/alerts/$alert_id")
    else
        RESPONSE=$(curl -s -k -X PUT \
            -H "X-Auth-Token: $API_TOKEN" \
            "$LIBRENMS_URL/api/v0/alerts/$alert_id")
    fi

    if echo "$RESPONSE" | jq -e '.status == "ok"' > /dev/null 2>&1; then
        echo "✓ Successfully acknowledged alert $alert_id"
    else
        echo "✗ Failed to acknowledge alert $alert_id"
        echo "  Response: $RESPONSE"
    fi
}

# Function to acknowledge multiple alerts
ack_multiple_alerts() {
    local alert_ids=("$@")
    local success_count=0
    local fail_count=0

    echo "=== Acknowledging ${#alert_ids[@]} alerts ==="
    echo ""

    for alert_id in "${alert_ids[@]}"; do
        if ack_alert "$alert_id"; then
            ((success_count++))
        else
            ((fail_count++))
        fi
        sleep 0.5  # Small delay to avoid overwhelming the API
    done

    echo ""
    echo "=== Summary ==="
    echo "Successfully acknowledged: $success_count"
    echo "Failed: $fail_count"
}

# Function to acknowledge all active alerts
ack_all_alerts() {
    echo "=== Fetching all active alerts for acknowledgment ==="

    RESPONSE=$(curl -s -k -H "X-Auth-Token: $API_TOKEN" "$LIBRENMS_URL/api/v0/alerts?state=1")

    if ! echo "$RESPONSE" | jq -e '.status == "ok"' > /dev/null 2>&1; then
        echo "Error: Failed to fetch alerts"
        echo "Response: $RESPONSE"
        exit 1
    fi

    ALERT_IDS=($(echo "$RESPONSE" | jq -r '.alerts[].id'))
    TOTAL_COUNT=${#ALERT_IDS[@]}

    if [ $TOTAL_COUNT -eq 0 ]; then
        echo "No active alerts to acknowledge!"
        exit 0
    fi

    echo "Found $TOTAL_COUNT active alerts"
    echo ""
    read -p "Are you sure you want to acknowledge ALL $TOTAL_COUNT alerts? (y/N): " confirm

    if [[ $confirm =~ ^[Yy]$ ]]; then
        ack_multiple_alerts "${ALERT_IDS[@]}"
    else
        echo "Operation cancelled"
    fi
}

# Function to acknowledge alerts from a specific date
ack_alerts_by_date() {
    local target_date="$1"

    echo "=== Fetching alerts from $target_date ==="

    RESPONSE=$(curl -s -k -H "X-Auth-Token: $API_TOKEN" "$LIBRENMS_URL/api/v0/alerts?state=1")

    if ! echo "$RESPONSE" | jq -e '.status == "ok"' > /dev/null 2>&1; then
        echo "Error: Failed to fetch alerts"
        echo "Response: $RESPONSE"
        exit 1
    fi

    # Filter alerts by date and get their IDs
    ALERT_IDS=($(echo "$RESPONSE" | jq -r --arg date "$target_date" '
        .alerts[] |
        select(.timestamp | startswith($date)) |
        .id'))

    TOTAL_COUNT=${#ALERT_IDS[@]}

    if [ $TOTAL_COUNT -eq 0 ]; then
        echo "No active alerts found for date: $target_date"
        echo ""
        echo "Available dates in current alerts:"
        echo "$RESPONSE" | jq -r '.alerts[].timestamp' | cut -d' ' -f1 | sort | uniq -c | sort -nr
        exit 0
    fi

    echo "Found $TOTAL_COUNT active alerts from $target_date"
    echo ""

    # Show a preview of the alerts
    echo "Preview of alerts from $target_date:"
    echo "$RESPONSE" | jq -r --arg date "$target_date" '
        .alerts[] |
        select(.timestamp | startswith($date)) |
        "  ID: " + (.id | tostring) + " - " + .timestamp +
        (if .hostname then " (" + .hostname + ")" else "" end)' | head -10

    if [ $TOTAL_COUNT -gt 10 ]; then
        echo "  ... and $((TOTAL_COUNT - 10)) more"
    fi

    echo ""
    read -p "Acknowledge all $TOTAL_COUNT alerts from $target_date? (y/N): " confirm

    if [[ $confirm =~ ^[Yy]$ ]]; then
        ack_multiple_alerts "${ALERT_IDS[@]}"
    else
        echo "Operation cancelled"
    fi
}

# Function to acknowledge alerts from a date range
ack_alerts_by_date_range() {
    local start_date="$1"
    local end_date="$2"

    echo "=== Fetching alerts from $start_date to $end_date ==="

    RESPONSE=$(curl -s -k -H "X-Auth-Token: $API_TOKEN" "$LIBRENMS_URL/api/v0/alerts?state=1")

    if ! echo "$RESPONSE" | jq -e '.status == "ok"' > /dev/null 2>&1; then
        echo "Error: Failed to fetch alerts"
        echo "Response: $RESPONSE"
        exit 1
    fi

    # Filter alerts by date range
    ALERT_IDS=($(echo "$RESPONSE" | jq -r --arg start "$start_date" --arg end "$end_date" '
        .alerts[] |
        select((.timestamp | strptime("%Y-%m-%d %H:%M:%S") | mktime) >= ($start | strptime("%Y-%m-%d") | mktime) and
               (.timestamp | strptime("%Y-%m-%d %H:%M:%S") | mktime) <= (($end + " 23:59:59") | strptime("%Y-%m-%d %H:%M:%S") | mktime)) |        .id'))

    TOTAL_COUNT=${#ALERT_IDS[@]}

    if [ $TOTAL_COUNT -eq 0 ]; then
        echo "No active alerts found between $start_date and $end_date"
        exit 0
    fi

    echo "Found $TOTAL_COUNT active alerts between $start_date and $end_date"
    echo ""
    read -p "Acknowledge all $TOTAL_COUNT alerts from this date range? (y/N): " confirm

    if [[ $confirm =~ ^[Yy]$ ]]; then
        ack_multiple_alerts "${ALERT_IDS[@]}"
    else
        echo "Operation cancelled"
    fi
}

# Function to show usage
show_usage() {
    echo "Usage: $0 [OPTIONS]"
    echo ""
    echo "Options:"
    echo "  -a, --all                     Acknowledge all active alerts"
    echo "  -i, --ids ID1,ID2,...         Acknowledge specific alert IDs (comma-separated)"
    echo "  -d, --date YYYY-MM-DD         Acknowledge all alerts from a specific date"
    echo "  -r, --range START_DATE END_DATE  Acknowledge alerts from date range (YYYY-MM-DD format)"
    echo "  -n, --note 'text'             Add a note to the acknowledgment"
    echo "  -h, --help                    Show this help message"
    echo ""
    echo "Examples:"
    echo "  $0 --all                                    # Acknowledge all alerts"
    echo "  $0 --ids 27194,27195,27196                  # Acknowledge specific alerts"
    echo "  $0 --date 2025-06-23                       # Acknowledge all alerts from today"
    echo "  $0 --date 2025-06-22                       # Acknowledge all alerts from yesterday"
    echo "  $0 --range 2025-06-20 2025-06-22           # Acknowledge alerts from date range"
    echo "  $0 --date 2025-06-23 --note 'Maintenance'  # Acknowledge with note"
    echo ""
    echo "Date formats:"
    echo "  - Use YYYY-MM-DD format (e.g., 2025-06-23)"
    echo "  - Times are included automatically (full day for single date)"
    echo ""
}

# Parse command line arguments
ACKNOWLEDGE_ALL=false
ALERT_IDS=()
NOTE=""
TARGET_DATE=""
START_DATE=""
END_DATE=""

while [[ $# -gt 0 ]]; do
    case $1 in
        -a|--all)
            ACKNOWLEDGE_ALL=true
            shift
            ;;
        -i|--ids)
            IFS=',' read -ra ALERT_IDS <<< "$2"
            shift 2
            ;;
        -d|--date)
            TARGET_DATE="$2"
            shift 2
            ;;
        -r|--range)
            START_DATE="$2"
            END_DATE="$3"
            shift 3
            ;;
        -n|--note)
            NOTE="$2"
            shift 2
            ;;
        -h|--help)
            show_usage
            exit 0
            ;;
        *)
            echo "Unknown option: $1"
            show_usage
            exit 1
            ;;
    esac
done

# Main logic
if [ "$ACKNOWLEDGE_ALL" = true ]; then
    ack_all_alerts
elif [ ${#ALERT_IDS[@]} -gt 0 ]; then
    echo "Note: $NOTE"
    ack_multiple_alerts "${ALERT_IDS[@]}"
elif [ -n "$TARGET_DATE" ]; then
    # Validate date format
    if ! date -d "$TARGET_DATE" >/dev/null 2>&1; then
        echo "Error: Invalid date format. Use YYYY-MM-DD (e.g., 2025-06-23)"
        exit 1
    fi
    ack_alerts_by_date "$TARGET_DATE"
elif [ -n "$START_DATE" ] && [ -n "$END_DATE" ]; then
    # Validate date formats
    if ! date -d "$START_DATE" >/dev/null 2>&1 || ! date -d "$END_DATE" >/dev/null 2>&1; then
        echo "Error: Invalid date format. Use YYYY-MM-DD (e.g., 2025-06-23)"
        exit 1
    fi
    ack_alerts_by_date_range "$START_DATE" "$END_DATE"
else
    echo "No action specified."
    show_usage
    exit 1

Usage instructions:

$ ./ack_alerts.sh
No action specified.
Usage: ./ack_alerts.sh [OPTIONS]

Options:
  -a, --all                     Acknowledge all active alerts
  -i, --ids ID1,ID2,...         Acknowledge specific alert IDs (comma-separated)
  -d, --date YYYY-MM-DD         Acknowledge all alerts from a specific date
  -r, --range START_DATE END_DATE  Acknowledge alerts from date range (YYYY-MM-DD format)
  -n, --note 'text'             Add a note to the acknowledgment
  -h, --help                    Show this help message

Examples:
  ./ack_alerts.sh --all                                    # Acknowledge all alerts
  ./ack_alerts.sh --ids 27194,27195,27196                  # Acknowledge specific alerts
  ./ack_alerts.sh --date 2025-06-23                       # Acknowledge all alerts from today
  ./ack_alerts.sh --date 2025-06-22                       # Acknowledge all alerts from yesterday
  ./ack_alerts.sh --range 2025-06-20 2025-06-22           # Acknowledge alerts from date range
  ./ack_alerts.sh --date 2025-06-23 --note 'Maintenance'  # Acknowledge with note

Date formats:
  - Use YYYY-MM-DD format (e.g., 2025-06-23)
  - Times are included automatically (full day for single date)

I ran the script for those alerts and it acknowledged every single one of them.
Here’s the log of another run I did today:

$ ./ack_alerts.sh --date 2025-06-25
=== Fetching alerts from 2025-06-25 ===
Found 6 active alerts from 2025-06-25

Preview of alerts from 2025-06-25:
  ID: 26986 - 2025-06-25 20:12:33 (10.132.32.50)
  ID: 26987 - 2025-06-25 20:12:33 (10.132.32.50)
  ID: 27007 - 2025-06-25 15:23:00 (10.33.64.50)
  ID: 27059 - 2025-06-25 14:40:30 (10.33.200.23)
  ID: 27078 - 2025-06-25 14:37:13 (10.33.200.15)
  ID: 27052 - 2025-06-25 14:27:32 (10.33.200.16)

Acknowledge all 6 alerts from 2025-06-25? (y/N): y
=== Acknowledging 6 alerts ===

Acknowledging alert ID: 26986
✓ Successfully acknowledged alert 26986
Acknowledging alert ID: 26987
✓ Successfully acknowledged alert 26987
Acknowledging alert ID: 27007
✓ Successfully acknowledged alert 27007
Acknowledging alert ID: 27059
✓ Successfully acknowledged alert 27059
Acknowledging alert ID: 27078
✓ Successfully acknowledged alert 27078
Acknowledging alert ID: 27052
✓ Successfully acknowledged alert 27052

=== Summary ===
Successfully acknowledged: 6
Failed: 0
1 Like

Note you need to have jq installed!