added new scripts
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
# /etc/check_mk/adguard.conf
|
||||||
|
|
||||||
|
ADGUARD_URL="http://127.0.0.1:80"
|
||||||
|
|
||||||
|
ADGUARD_USER="checkmk"
|
||||||
|
ADGUARD_PASSWORD="CHANGE_ME"
|
||||||
|
|
||||||
|
CHECK_UPDATES="yes"
|
||||||
|
|
||||||
|
# If protection gets disabled intentionally, set this to "no".
|
||||||
|
ALERT_PROTECTION_DISABLED="yes"
|
||||||
@@ -0,0 +1,347 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# /usr/lib/check_mk_agent/local/300/check_adguard
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
CONFIG="/etc/check_mk/adguard.conf"
|
||||||
|
|
||||||
|
OK=0
|
||||||
|
WARN=1
|
||||||
|
CRIT=2
|
||||||
|
UNKNOWN=3
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ ! -r "$CONFIG" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"AdGuard Home\" - Configuration file ${CONFIG} missing or unreadable"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CONFIG"
|
||||||
|
|
||||||
|
ADGUARD_URL="${ADGUARD_URL:-http://127.0.0.1:3000}"
|
||||||
|
ADGUARD_USER="${ADGUARD_USER:-}"
|
||||||
|
ADGUARD_PASSWORD="${ADGUARD_PASSWORD:-}"
|
||||||
|
|
||||||
|
CHECK_UPDATES="${CHECK_UPDATES:-yes}"
|
||||||
|
ALERT_PROTECTION_DISABLED="${ALERT_PROTECTION_DISABLED:-yes}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Requirements
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"AdGuard Home\" - curl is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"AdGuard Home\" - jq is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# curl configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
CURL_OPTS=(
|
||||||
|
--silent
|
||||||
|
--show-error
|
||||||
|
--fail
|
||||||
|
--connect-timeout 3
|
||||||
|
--max-time 10
|
||||||
|
)
|
||||||
|
|
||||||
|
AUTH_OPTS=()
|
||||||
|
|
||||||
|
if [[ -n "$ADGUARD_USER" ]]; then
|
||||||
|
AUTH_OPTS=(
|
||||||
|
--user "${ADGUARD_USER}:${ADGUARD_PASSWORD}"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AdGuard status / version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
STATUS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
"${AUTH_OPTS[@]}" \
|
||||||
|
"${ADGUARD_URL%/}/control/status" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
STATUS_RC=$?
|
||||||
|
|
||||||
|
if [[ $STATUS_RC -ne 0 || -z "$STATUS_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"AdGuard Home\" - API unavailable or authentication failed at ${ADGUARD_URL}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -e 'type == "object"' >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"AdGuard Home\" - Status API returned unexpected data"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '.version // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
RUNNING="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '.running // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
PROTECTION_ENABLED="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '.protection_enabled // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
DNS_PORT="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '.dns_port // 53'
|
||||||
|
)"
|
||||||
|
|
||||||
|
DNS_ADDRESSES="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '
|
||||||
|
(.dns_addresses // [])
|
||||||
|
| join(", ")
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$RUNNING" != "true" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"AdGuard Home\" - API reachable but DNS service reports not running"
|
||||||
|
else
|
||||||
|
if [[ -n "$VERSION" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"AdGuard Home\" - API reachable, DNS running, version ${VERSION}, port ${DNS_PORT}"
|
||||||
|
else
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"AdGuard Home\" - API reachable, DNS running, port ${DNS_PORT}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Protection state
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$PROTECTION_ENABLED" == "true" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"AdGuard Protection\" - Protection enabled"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
if [[ "$ALERT_PROTECTION_DISABLED" == "yes" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"AdGuard Protection\" - Protection disabled"
|
||||||
|
else
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"AdGuard Protection\" - Protection disabled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Statistics
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
STATS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
"${AUTH_OPTS[@]}" \
|
||||||
|
"${ADGUARD_URL%/}/control/stats" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
STATS_RC=$?
|
||||||
|
|
||||||
|
if [[ $STATS_RC -ne 0 || -z "$STATS_RESPONSE" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"AdGuard Statistics\" - Unable to retrieve DNS statistics"
|
||||||
|
|
||||||
|
elif ! printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -e 'type == "object"' >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"AdGuard Statistics\" - Statistics API returned unexpected data"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
DNS_QUERIES="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.num_dns_queries // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
BLOCKED="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.num_blocked_filtering // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
SAFE_BROWSING="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.num_replaced_safebrowsing // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
SAFE_SEARCH="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.num_replaced_safesearch // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
PARENTAL="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.num_replaced_parental // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
AVG_TIME="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.avg_processing_time // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
[[ "$DNS_QUERIES" =~ ^[0-9]+$ ]] || DNS_QUERIES=0
|
||||||
|
[[ "$BLOCKED" =~ ^[0-9]+$ ]] || BLOCKED=0
|
||||||
|
[[ "$SAFE_BROWSING" =~ ^[0-9]+$ ]] || SAFE_BROWSING=0
|
||||||
|
[[ "$SAFE_SEARCH" =~ ^[0-9]+$ ]] || SAFE_SEARCH=0
|
||||||
|
[[ "$PARENTAL" =~ ^[0-9]+$ ]] || PARENTAL=0
|
||||||
|
[[ "$AVG_TIME" =~ ^[0-9]+([.][0-9]+)?$ ]] || AVG_TIME=0
|
||||||
|
|
||||||
|
BLOCK_PERCENT="$(
|
||||||
|
awk \
|
||||||
|
-v blocked="$BLOCKED" \
|
||||||
|
-v total="$DNS_QUERIES" \
|
||||||
|
'BEGIN {
|
||||||
|
if (total > 0) {
|
||||||
|
printf "%.2f", (blocked / total) * 100
|
||||||
|
} else {
|
||||||
|
printf "0.00"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
)"
|
||||||
|
|
||||||
|
#
|
||||||
|
# avg_processing_time is reported in seconds.
|
||||||
|
#
|
||||||
|
|
||||||
|
AVG_TIME_MS="$(
|
||||||
|
awk \
|
||||||
|
-v seconds="$AVG_TIME" \
|
||||||
|
'BEGIN {
|
||||||
|
printf "%.3f", seconds * 1000
|
||||||
|
}'
|
||||||
|
)"
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"AdGuard Statistics\" queries=${DNS_QUERIES};;;0|blocked=${BLOCKED};;;0|blocked_percent=${BLOCK_PERCENT}%;;;0;100|avg_processing_time=${AVG_TIME_MS}ms;;;0 ${DNS_QUERIES} queries, ${BLOCKED} blocked (${BLOCK_PERCENT}%), average processing ${AVG_TIME_MS} ms"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AdGuard update check
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# AdGuard Home provides its own update-information endpoint.
|
||||||
|
# This is preferable to independently scraping GitHub because it follows
|
||||||
|
# AdGuard's own release/update logic.
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$CHECK_UPDATES" == "yes" ]]; then
|
||||||
|
|
||||||
|
VERSION_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
"${AUTH_OPTS[@]}" \
|
||||||
|
-X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"recheck_now":true}' \
|
||||||
|
"${ADGUARD_URL%/}/control/version.json" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
VERSION_RC=$?
|
||||||
|
|
||||||
|
if [[ $VERSION_RC -ne 0 || -z "$VERSION_RESPONSE" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"AdGuard Update\" - Installed ${VERSION:-unknown}, unable to retrieve update information"
|
||||||
|
|
||||||
|
elif ! printf '%s' "$VERSION_RESPONSE" |
|
||||||
|
jq -e 'type == "object"' >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"AdGuard Update\" - Update API returned unexpected data"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
UPDATE_DISABLED="$(
|
||||||
|
printf '%s' "$VERSION_RESPONSE" |
|
||||||
|
jq -r '.disabled // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
NEW_VERSION="$(
|
||||||
|
printf '%s' "$VERSION_RESPONSE" |
|
||||||
|
jq -r '.new_version // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
NEW_VERSION="${NEW_VERSION#v}"
|
||||||
|
INSTALLED_VERSION="${VERSION#v}"
|
||||||
|
|
||||||
|
if [[ "$UPDATE_DISABLED" == "true" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"AdGuard Update\" - Update checking disabled by AdGuard Home, installed ${INSTALLED_VERSION:-unknown}"
|
||||||
|
|
||||||
|
elif [[ -z "$NEW_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"AdGuard Update\" - Current: installed ${INSTALLED_VERSION:-unknown}"
|
||||||
|
|
||||||
|
elif [[ -z "$INSTALLED_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"AdGuard Update\" - New version ${NEW_VERSION} available, installed version unknown"
|
||||||
|
|
||||||
|
elif [[ "$NEW_VERSION" == "$INSTALLED_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"AdGuard Update\" - Current: installed ${INSTALLED_VERSION}"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"AdGuard Update\" - Update available: installed ${INSTALLED_VERSION}, latest ${NEW_VERSION}"
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,709 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# /usr/lib/check_mk_agent/local/300/check_crafty
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
CONFIG="/etc/check_mk/crafty.conf"
|
||||||
|
|
||||||
|
OK=0
|
||||||
|
WARN=1
|
||||||
|
CRIT=2
|
||||||
|
UNKNOWN=3
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ ! -r "$CONFIG" ]]; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Crafty\" - Configuration file ${CONFIG} missing or unreadable"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CONFIG"
|
||||||
|
|
||||||
|
CRAFTY_URL="${CRAFTY_URL:-https://127.0.0.1:8443}"
|
||||||
|
CRAFTY_USER="${CRAFTY_USER:-}"
|
||||||
|
CRAFTY_PASSWORD="${CRAFTY_PASSWORD:-}"
|
||||||
|
CRAFTY_INSECURE="${CRAFTY_INSECURE:-yes}"
|
||||||
|
IGNORE_HIDDEN_SERVERS="${IGNORE_HIDDEN_SERVERS:-yes}"
|
||||||
|
|
||||||
|
CHECK_UPDATES="${CHECK_UPDATES:-yes}"
|
||||||
|
CRAFTY_RELEASE_API="${CRAFTY_RELEASE_API:-https://gitlab.com/api/v4/projects/34675721/releases/permalink/latest}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Requirements
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Crafty\" - curl is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Crafty\" - jq is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$CRAFTY_USER" || -z "$CRAFTY_PASSWORD" ]]; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Crafty\" - CRAFTY_USER or CRAFTY_PASSWORD is not configured"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# curl configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
CURL_OPTS=(
|
||||||
|
--silent
|
||||||
|
--show-error
|
||||||
|
--connect-timeout 3
|
||||||
|
--max-time 10
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ "$CRAFTY_INSECURE" == "yes" ]]; then
|
||||||
|
CURL_OPTS+=(--insecure)
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Login
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
LOGIN_PAYLOAD="$(
|
||||||
|
jq -n \
|
||||||
|
--arg username "$CRAFTY_USER" \
|
||||||
|
--arg password "$CRAFTY_PASSWORD" \
|
||||||
|
'{
|
||||||
|
username: $username,
|
||||||
|
password: $password
|
||||||
|
}'
|
||||||
|
)"
|
||||||
|
|
||||||
|
LOGIN_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$LOGIN_PAYLOAD" \
|
||||||
|
"${CRAFTY_URL%/}/api/v2/auth/login" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$LOGIN_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"Crafty\" - Unable to reach Crafty login API at ${CRAFTY_URL}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$LOGIN_RESPONSE" | jq -e . >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Crafty\" - Login API returned invalid JSON"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
LOGIN_STATUS="$(
|
||||||
|
printf '%s' "$LOGIN_RESPONSE" |
|
||||||
|
jq -r '.status // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
TOKEN="$(
|
||||||
|
printf '%s' "$LOGIN_RESPONSE" |
|
||||||
|
jq -r '.data.token // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$LOGIN_STATUS" != "ok" || -z "$TOKEN" ]]; then
|
||||||
|
|
||||||
|
LOGIN_ERROR="$(
|
||||||
|
printf '%s' "$LOGIN_RESPONSE" |
|
||||||
|
jq -r '.error_data // .error // "authentication failed"'
|
||||||
|
)"
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"Crafty\" - Authentication failed: ${LOGIN_ERROR}"
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Server inventory
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
SERVERS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
"${CRAFTY_URL%/}/api/v2/servers" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"Crafty\" - Server inventory request failed, curl exit code ${CURL_RC}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$SERVERS_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"Crafty\" - Server inventory API returned an empty response"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$SERVERS_RESPONSE" | jq -e . >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Crafty\" - Server inventory returned invalid JSON"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
SERVERS_STATUS="$(
|
||||||
|
printf '%s' "$SERVERS_RESPONSE" |
|
||||||
|
jq -r '.status // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$SERVERS_STATUS" != "ok" ]]; then
|
||||||
|
|
||||||
|
SERVER_ERROR="$(
|
||||||
|
printf '%s' "$SERVERS_RESPONSE" |
|
||||||
|
jq -r '.error_data // .error // "unknown API error"'
|
||||||
|
)"
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"Crafty\" - Server inventory API error: ${SERVER_ERROR}"
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
DATA_TYPE="$(
|
||||||
|
printf '%s' "$SERVERS_RESPONSE" |
|
||||||
|
jq -r '.data | type'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$DATA_TYPE" != "array" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Crafty\" - Server inventory returned unexpected data type: ${DATA_TYPE}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Server counts
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
TOTAL_CONFIGURED="$(
|
||||||
|
printf '%s' "$SERVERS_RESPONSE" |
|
||||||
|
jq '.data | length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$IGNORE_HIDDEN_SERVERS" == "yes" ]]; then
|
||||||
|
|
||||||
|
SERVER_COUNT="$(
|
||||||
|
printf '%s' "$SERVERS_RESPONSE" |
|
||||||
|
jq '[.data[] | select(.show_status != false)] | length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
else
|
||||||
|
SERVER_COUNT="$TOTAL_CONFIGURED"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Crafty installed version via /metrics
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
METRICS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
"${CRAFTY_URL%/}/metrics" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
METRICS_RC=$?
|
||||||
|
|
||||||
|
INSTALLED_VERSION=""
|
||||||
|
|
||||||
|
if [[ $METRICS_RC -eq 0 && -n "$METRICS_RESPONSE" ]]; then
|
||||||
|
|
||||||
|
INSTALLED_VERSION="$(
|
||||||
|
printf '%s\n' "$METRICS_RESPONSE" |
|
||||||
|
grep -m1 '^Crafty_Controller_info{' |
|
||||||
|
sed -n 's/.*version="\([^"]*\)".*/\1/p' |
|
||||||
|
tr -d '[:space:]'
|
||||||
|
)"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Overall Crafty service
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ -n "$INSTALLED_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Crafty\" servers=${SERVER_COUNT};;;0 API reachable and authenticated - version ${INSTALLED_VERSION}, ${SERVER_COUNT}/${TOTAL_CONFIGURED} servers monitored"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Crafty\" servers=${SERVER_COUNT};;;0 API reachable and authenticated - ${SERVER_COUNT}/${TOTAL_CONFIGURED} servers monitored"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Crafty update check
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$CHECK_UPDATES" == "yes" ]]; then
|
||||||
|
|
||||||
|
if [[ -z "$INSTALLED_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Crafty Update\" - Unable to determine installed Crafty version from /metrics"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
RELEASE_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
--silent \
|
||||||
|
--show-error \
|
||||||
|
--location \
|
||||||
|
--fail \
|
||||||
|
--connect-timeout 3 \
|
||||||
|
--max-time 10 \
|
||||||
|
-H "User-Agent: checkmk-crafty-local-check" \
|
||||||
|
"$CRAFTY_RELEASE_API" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
RELEASE_RC=$?
|
||||||
|
|
||||||
|
if [[ $RELEASE_RC -ne 0 || -z "$RELEASE_RESPONSE" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Crafty Update\" - Installed ${INSTALLED_VERSION}, unable to query GitLab release API"
|
||||||
|
|
||||||
|
elif ! printf '%s' "$RELEASE_RESPONSE" | jq -e . >/dev/null 2>&1; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Crafty Update\" - GitLab release API returned invalid JSON"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
LATEST_VERSION="$(
|
||||||
|
printf '%s' "$RELEASE_RESPONSE" |
|
||||||
|
jq -r '.tag_name // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
LATEST_VERSION="${LATEST_VERSION#v}"
|
||||||
|
|
||||||
|
if [[ -z "$LATEST_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Crafty Update\" - Unable to determine latest Crafty release"
|
||||||
|
|
||||||
|
elif [[ ! "$INSTALLED_VERSION" =~ ^[0-9]+([.][0-9]+)*$ ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Crafty Update\" - Unable to parse installed version ${INSTALLED_VERSION}"
|
||||||
|
|
||||||
|
elif [[ ! "$LATEST_VERSION" =~ ^[0-9]+([.][0-9]+)*$ ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Crafty Update\" - Unable to parse latest version ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
NEWEST="$(
|
||||||
|
printf '%s\n%s\n' \
|
||||||
|
"$INSTALLED_VERSION" \
|
||||||
|
"$LATEST_VERSION" |
|
||||||
|
sort -V |
|
||||||
|
tail -n 1
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Crafty Update\" - Current: installed ${INSTALLED_VERSION}, latest ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
elif [[ "$NEWEST" == "$LATEST_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"Crafty Update\" - Update available: installed ${INSTALLED_VERSION}, latest ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Crafty Update\" - Installed ${INSTALLED_VERSION} is newer than latest stable ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Individual Minecraft servers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
while IFS= read -r SERVER; do
|
||||||
|
|
||||||
|
SERVER_ID="$(
|
||||||
|
printf '%s' "$SERVER" |
|
||||||
|
jq -r '.server_id // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
SERVER_NAME="$(
|
||||||
|
printf '%s' "$SERVER" |
|
||||||
|
jq -r '.server_name // "Unknown"'
|
||||||
|
)"
|
||||||
|
|
||||||
|
SERVER_PORT="$(
|
||||||
|
printf '%s' "$SERVER" |
|
||||||
|
jq -r '.server_port // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
AUTO_START="$(
|
||||||
|
printf '%s' "$SERVER" |
|
||||||
|
jq -r '.auto_start // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
CRASH_DETECTION="$(
|
||||||
|
printf '%s' "$SERVER" |
|
||||||
|
jq -r '.crash_detection // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
COUNT_PLAYERS="$(
|
||||||
|
printf '%s' "$SERVER" |
|
||||||
|
jq -r '.count_players // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
SHOW_STATUS="$(
|
||||||
|
printf '%s' "$SERVER" |
|
||||||
|
jq -r '.show_status // true'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$IGNORE_HIDDEN_SERVERS" == "yes" && "$SHOW_STATUS" == "false" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
SERVER_NAME="${SERVER_NAME//\"/\'}"
|
||||||
|
|
||||||
|
if [[ -z "$SERVER_ID" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Minecraft ${SERVER_NAME}\" - Server entry does not contain a server ID"
|
||||||
|
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Runtime statistics
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
STATS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
"${CRAFTY_URL%/}/api/v2/servers/${SERVER_ID}/stats" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
STATS_RC=$?
|
||||||
|
|
||||||
|
if [[ $STATS_RC -ne 0 || -z "$STATS_RESPONSE" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Minecraft ${SERVER_NAME}\" - Unable to retrieve Crafty server statistics"
|
||||||
|
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$STATS_RESPONSE" | jq -e . >/dev/null 2>&1; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Minecraft ${SERVER_NAME}\" - Statistics API returned invalid JSON"
|
||||||
|
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
STATS_STATUS="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.status // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$STATS_STATUS" != "ok" ]]; then
|
||||||
|
|
||||||
|
STATS_ERROR="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.error_data // .error // "unknown API error"'
|
||||||
|
)"
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Minecraft ${SERVER_NAME}\" - Crafty statistics API error: ${STATS_ERROR}"
|
||||||
|
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Runtime values
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
RUNNING="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.data.running // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
CRASHED="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.data.crashed // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
UPDATING="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.data.updating // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
WAITING_START="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.data.waiting_start // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
IMPORTING="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.data.importing // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
FIRST_RUN="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.data.first_run // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
CPU="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.data.cpu // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
MEMORY="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.data.mem // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
MEMORY_PERCENT="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.data.mem_percent // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
PLAYERS_ONLINE="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '
|
||||||
|
if (.data.online | type) == "number"
|
||||||
|
then .data.online
|
||||||
|
else 0
|
||||||
|
end
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
PLAYERS_MAX="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '
|
||||||
|
if (.data.max | type) == "number"
|
||||||
|
then .data.max
|
||||||
|
else 0
|
||||||
|
end
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
WORLD_NAME="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '
|
||||||
|
if (.data.world_name // "") == ""
|
||||||
|
then "unknown"
|
||||||
|
else .data.world_name
|
||||||
|
end
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
WORLD_SIZE="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '
|
||||||
|
if (.data.world_size // "") == ""
|
||||||
|
then "unknown"
|
||||||
|
else .data.world_size
|
||||||
|
end
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
MC_VERSION="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '
|
||||||
|
if (
|
||||||
|
.data.version == false or
|
||||||
|
.data.version == null or
|
||||||
|
.data.version == ""
|
||||||
|
)
|
||||||
|
then ""
|
||||||
|
else (.data.version | tostring)
|
||||||
|
end
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Sanitize performance values
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if ! [[ "$CPU" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
|
||||||
|
CPU=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "$MEMORY" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
|
||||||
|
MEMORY=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "$MEMORY_PERCENT" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
|
||||||
|
MEMORY_PERCENT=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "$PLAYERS_ONLINE" =~ ^[0-9]+$ ]]; then
|
||||||
|
PLAYERS_ONLINE=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "$PLAYERS_MAX" =~ ^[0-9]+$ ]]; then
|
||||||
|
PLAYERS_MAX=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Determine CheckMK state
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
STATE=$OK
|
||||||
|
STATUS_TEXT="Stopped"
|
||||||
|
|
||||||
|
if [[ "$CRASHED" == "true" ]]; then
|
||||||
|
|
||||||
|
STATE=$CRIT
|
||||||
|
STATUS_TEXT="Crashed"
|
||||||
|
|
||||||
|
elif [[ "$UPDATING" == "true" ]]; then
|
||||||
|
|
||||||
|
STATUS_TEXT="Updating"
|
||||||
|
|
||||||
|
elif [[ "$IMPORTING" == "true" ]]; then
|
||||||
|
|
||||||
|
STATUS_TEXT="Importing"
|
||||||
|
|
||||||
|
elif [[ "$WAITING_START" == "true" ]]; then
|
||||||
|
|
||||||
|
STATUS_TEXT="Waiting to start"
|
||||||
|
|
||||||
|
elif [[ "$RUNNING" == "true" ]]; then
|
||||||
|
|
||||||
|
STATUS_TEXT="Running"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Performance data
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
PERFDATA="cpu=${CPU}%;;;0;100|memory=${MEMORY};;;0|memory_percent=${MEMORY_PERCENT}%;;;0;100"
|
||||||
|
|
||||||
|
if [[ "$COUNT_PLAYERS" == "true" ]]; then
|
||||||
|
|
||||||
|
if [[ "$PLAYERS_MAX" -gt 0 ]]; then
|
||||||
|
PERFDATA="${PERFDATA}|players=${PLAYERS_ONLINE};;;0;${PLAYERS_MAX}"
|
||||||
|
else
|
||||||
|
PERFDATA="${PERFDATA}|players=${PLAYERS_ONLINE};;;0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Human-readable output
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
DETAILS="$STATUS_TEXT"
|
||||||
|
|
||||||
|
if [[ "$RUNNING" == "true" ]]; then
|
||||||
|
|
||||||
|
if [[ "$COUNT_PLAYERS" == "true" ]]; then
|
||||||
|
|
||||||
|
if [[ "$PLAYERS_MAX" -gt 0 ]]; then
|
||||||
|
DETAILS="${DETAILS} - players ${PLAYERS_ONLINE}/${PLAYERS_MAX}"
|
||||||
|
else
|
||||||
|
DETAILS="${DETAILS} - players ${PLAYERS_ONLINE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$MC_VERSION" ]]; then
|
||||||
|
DETAILS="${DETAILS}, version ${MC_VERSION}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DETAILS="${DETAILS}, CPU ${CPU}%, memory ${MEMORY_PERCENT}%"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$WORLD_NAME" != "unknown" ]]; then
|
||||||
|
DETAILS="${DETAILS}, world ${WORLD_NAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$WORLD_SIZE" != "unknown" ]]; then
|
||||||
|
DETAILS="${DETAILS} (${WORLD_SIZE})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DETAILS="${DETAILS}, port ${SERVER_PORT}"
|
||||||
|
|
||||||
|
if [[ "$AUTO_START" == "true" ]]; then
|
||||||
|
DETAILS="${DETAILS}, auto-start enabled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$CRASH_DETECTION" == "true" ]]; then
|
||||||
|
DETAILS="${DETAILS}, crash detection enabled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$FIRST_RUN" == "true" ]]; then
|
||||||
|
DETAILS="${DETAILS}, first-run flag set"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${STATE} \"Minecraft ${SERVER_NAME}\" ${PERFDATA} ${DETAILS}"
|
||||||
|
|
||||||
|
done < <(
|
||||||
|
printf '%s' "$SERVERS_RESPONSE" |
|
||||||
|
jq -c '.data[]'
|
||||||
|
)
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
CONFIG="/etc/check_mk/gitea.conf"
|
||||||
|
|
||||||
|
#
|
||||||
|
# CheckMK states
|
||||||
|
#
|
||||||
|
OK=0
|
||||||
|
WARN=1
|
||||||
|
CRIT=2
|
||||||
|
UNKNOWN=3
|
||||||
|
|
||||||
|
if [[ ! -r "$CONFIG" ]]; then
|
||||||
|
echo "${UNKNOWN} \"Gitea\" - Configuration file ${CONFIG} missing or unreadable"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CONFIG"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Defaults
|
||||||
|
#
|
||||||
|
GITEA_URL="${GITEA_URL:-http://127.0.0.1:3000}"
|
||||||
|
GITEA_TOKEN="${GITEA_TOKEN:-}"
|
||||||
|
CHECK_UPDATES="${CHECK_UPDATES:-yes}"
|
||||||
|
GITEA_RELEASE_API="${GITEA_RELEASE_API:-https://api.github.com/repos/go-gitea/gitea/releases/latest}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Requirements
|
||||||
|
#
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
echo "${UNKNOWN} \"Gitea\" - curl is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
echo "${UNKNOWN} \"Gitea\" - jq is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# curl options
|
||||||
|
#
|
||||||
|
CURL_OPTS=(
|
||||||
|
--silent
|
||||||
|
--show-error
|
||||||
|
--fail
|
||||||
|
--connect-timeout 3
|
||||||
|
--max-time 8
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Gitea authentication
|
||||||
|
#
|
||||||
|
AUTH_ARGS=()
|
||||||
|
|
||||||
|
if [[ -n "$GITEA_TOKEN" ]]; then
|
||||||
|
AUTH_ARGS=(
|
||||||
|
--header "Authorization: token ${GITEA_TOKEN}"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Gitea health / version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
VERSION_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
"${AUTH_ARGS[@]}" \
|
||||||
|
"${GITEA_URL%/}/api/v1/version" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$VERSION_RESPONSE" ]]; then
|
||||||
|
echo "${CRIT} \"Gitea\" - API unavailable at ${GITEA_URL}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
INSTALLED_VERSION="$(
|
||||||
|
printf '%s' "$VERSION_RESPONSE" |
|
||||||
|
jq -r '.version // empty' 2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$INSTALLED_VERSION" ]]; then
|
||||||
|
echo "${CRIT} \"Gitea\" - API responded but no version was returned"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${OK} \"Gitea\" - API reachable, version ${INSTALLED_VERSION}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Update check
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$CHECK_UPDATES" != "yes" ]]; then
|
||||||
|
echo "${OK} \"Gitea Update\" - Update check disabled, installed ${INSTALLED_VERSION}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
RELEASE_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
--header "Accept: application/vnd.github+json" \
|
||||||
|
--header "User-Agent: checkmk-gitea-local-check" \
|
||||||
|
"$GITEA_RELEASE_API" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$RELEASE_RESPONSE" ]]; then
|
||||||
|
echo "${UNKNOWN} \"Gitea Update\" - Installed ${INSTALLED_VERSION}, unable to query upstream release"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
LATEST_VERSION="$(
|
||||||
|
printf '%s' "$RELEASE_RESPONSE" |
|
||||||
|
jq -r '.tag_name // empty' 2>/dev/null |
|
||||||
|
sed 's/^v//'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$LATEST_VERSION" ]]; then
|
||||||
|
echo "${UNKNOWN} \"Gitea Update\" - Installed ${INSTALLED_VERSION}, upstream returned no release version"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Remove common suffixes that may appear in Gitea's version response.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# 1.27.2
|
||||||
|
# 1.27.2+gitea
|
||||||
|
# 1.27.2 built with GNU Make
|
||||||
|
#
|
||||||
|
INSTALLED_CLEAN="$(
|
||||||
|
printf '%s' "$INSTALLED_VERSION" |
|
||||||
|
grep -oE '^[0-9]+\.[0-9]+\.[0-9]+' |
|
||||||
|
head -n1
|
||||||
|
)"
|
||||||
|
|
||||||
|
LATEST_CLEAN="$(
|
||||||
|
printf '%s' "$LATEST_VERSION" |
|
||||||
|
grep -oE '^[0-9]+\.[0-9]+\.[0-9]+' |
|
||||||
|
head -n1
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$INSTALLED_CLEAN" || -z "$LATEST_CLEAN" ]]; then
|
||||||
|
echo "${UNKNOWN} \"Gitea Update\" - Unable to compare installed=${INSTALLED_VERSION} latest=${LATEST_VERSION}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# sort -V performs semantic-ish version ordering suitable for Gitea's
|
||||||
|
# major.minor.patch release numbers.
|
||||||
|
#
|
||||||
|
NEWEST="$(
|
||||||
|
printf '%s\n%s\n' "$INSTALLED_CLEAN" "$LATEST_CLEAN" |
|
||||||
|
sort -V |
|
||||||
|
tail -n1
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$INSTALLED_CLEAN" == "$LATEST_CLEAN" ]]; then
|
||||||
|
|
||||||
|
echo "${OK} \"Gitea Update\" - Current version ${INSTALLED_CLEAN}"
|
||||||
|
|
||||||
|
elif [[ "$NEWEST" == "$LATEST_CLEAN" ]]; then
|
||||||
|
|
||||||
|
echo "${WARN} \"Gitea Update\" - Update available: installed ${INSTALLED_CLEAN}, latest ${LATEST_CLEAN}"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
#
|
||||||
|
# Installed version newer than upstream latest.
|
||||||
|
# This can happen with prerelease/dev builds.
|
||||||
|
#
|
||||||
|
echo "${OK} \"Gitea Update\" - Installed ${INSTALLED_CLEAN} is newer than upstream stable ${LATEST_CLEAN}"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,466 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# /usr/lib/check_mk_agent/local/300/check_mail_archiver
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
CONFIG="/etc/check_mk/mail_archiver.conf"
|
||||||
|
|
||||||
|
OK=0
|
||||||
|
WARN=1
|
||||||
|
CRIT=2
|
||||||
|
UNKNOWN=3
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ ! -r "$CONFIG" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver\" - Configuration file ${CONFIG} missing or unreadable"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CONFIG"
|
||||||
|
|
||||||
|
MAIL_ARCHIVER_URL="${MAIL_ARCHIVER_URL:-http://127.0.0.1:5000}"
|
||||||
|
MAIL_ARCHIVER_TOKEN="${MAIL_ARCHIVER_TOKEN:-}"
|
||||||
|
|
||||||
|
WARN_SYNC_AGE="${WARN_SYNC_AGE:-3600}"
|
||||||
|
CRIT_SYNC_AGE="${CRIT_SYNC_AGE:-7200}"
|
||||||
|
|
||||||
|
CHECK_UPDATES="${CHECK_UPDATES:-yes}"
|
||||||
|
MAIL_ARCHIVER_RELEASE_API="${MAIL_ARCHIVER_RELEASE_API:-https://api.github.com/repos/s1t5/mail-archiver/releases/latest}"
|
||||||
|
|
||||||
|
VERSION_FILE="${VERSION_FILE:-/root/.mail-archiver}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Requirements
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver\" - curl is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver\" - jq is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v date >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver\" - date is not available"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$MAIL_ARCHIVER_TOKEN" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver\" - MAIL_ARCHIVER_TOKEN is not configured"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Validate thresholds
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if ! [[ "$WARN_SYNC_AGE" =~ ^[0-9]+$ ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver\" - WARN_SYNC_AGE must be an integer"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "$CRIT_SYNC_AGE" =~ ^[0-9]+$ ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver\" - CRIT_SYNC_AGE must be an integer"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$CRIT_SYNC_AGE" -le "$WARN_SYNC_AGE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver\" - CRIT_SYNC_AGE must be greater than WARN_SYNC_AGE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# curl configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
CURL_OPTS=(
|
||||||
|
--silent
|
||||||
|
--show-error
|
||||||
|
--fail
|
||||||
|
--connect-timeout 3
|
||||||
|
--max-time 10
|
||||||
|
)
|
||||||
|
|
||||||
|
AUTH_HEADER="Authorization: Bearer ${MAIL_ARCHIVER_TOKEN}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Accounts API / application health
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
ACCOUNTS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "$AUTH_HEADER" \
|
||||||
|
"${MAIL_ARCHIVER_URL%/}/api/v1/accounts" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$ACCOUNTS_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"Mail Archiver\" - API unavailable or authentication failed at ${MAIL_ARCHIVER_URL}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$ACCOUNTS_RESPONSE" |
|
||||||
|
jq -e 'type == "array"' >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver\" - Accounts API returned unexpected data"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACCOUNT_COUNT="$(
|
||||||
|
printf '%s' "$ACCOUNTS_RESPONSE" |
|
||||||
|
jq 'length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
ENABLED_COUNT="$(
|
||||||
|
printf '%s' "$ACCOUNTS_RESPONSE" |
|
||||||
|
jq '[.[] | select(.isEnabled == true)] | length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
DISABLED_COUNT=$((ACCOUNT_COUNT - ENABLED_COUNT))
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Mail Archiver\" accounts=${ACCOUNT_COUNT};;;0|enabled=${ENABLED_COUNT};;;0|disabled=${DISABLED_COUNT};;;0 API reachable and authenticated - ${ENABLED_COUNT}/${ACCOUNT_COUNT} accounts enabled"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Archive statistics
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
STATS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "$AUTH_HEADER" \
|
||||||
|
"${MAIL_ARCHIVER_URL%/}/api/v1/stats" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
STATS_RC=$?
|
||||||
|
|
||||||
|
if [[ $STATS_RC -ne 0 || -z "$STATS_RESPONSE" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archive Statistics\" - Unable to retrieve archive statistics"
|
||||||
|
|
||||||
|
elif ! printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -e 'type == "object"' >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archive Statistics\" - Statistics API returned unexpected data"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
EMAILS="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.emails // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
STATS_ACCOUNTS="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.accounts // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
ATTACHMENTS="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.attachments // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
DATABASE_MB="$(
|
||||||
|
printf '%s' "$STATS_RESPONSE" |
|
||||||
|
jq -r '.databaseSizeInMB // "0"'
|
||||||
|
)"
|
||||||
|
|
||||||
|
[[ "$EMAILS" =~ ^[0-9]+$ ]] || EMAILS=0
|
||||||
|
[[ "$STATS_ACCOUNTS" =~ ^[0-9]+$ ]] || STATS_ACCOUNTS=0
|
||||||
|
[[ "$ATTACHMENTS" =~ ^[0-9]+$ ]] || ATTACHMENTS=0
|
||||||
|
[[ "$DATABASE_MB" =~ ^[0-9]+([.][0-9]+)?$ ]] || DATABASE_MB=0
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Mail Archive Statistics\" emails=${EMAILS};;;0|accounts=${STATS_ACCOUNTS};;;0|attachments=${ATTACHMENTS};;;0|database_size=${DATABASE_MB}MB;;;0 ${EMAILS} emails, ${STATS_ACCOUNTS} accounts, ${ATTACHMENTS} attachments, database ${DATABASE_MB} MB"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Individual account sync state
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Mail-Archiver returns lastSync values such as:
|
||||||
|
#
|
||||||
|
# 2026-09-13T07:33:35.199543
|
||||||
|
#
|
||||||
|
# These represent UTC, but do not contain a timezone suffix.
|
||||||
|
#
|
||||||
|
# Therefore we explicitly parse them with TZ=UTC. Once converted to Unix
|
||||||
|
# epoch seconds, comparisons are timezone-independent and automatically safe
|
||||||
|
# across CET/CEST daylight-saving changes.
|
||||||
|
#
|
||||||
|
|
||||||
|
NOW_EPOCH="$(date +%s)"
|
||||||
|
|
||||||
|
while IFS= read -r ACCOUNT; do
|
||||||
|
|
||||||
|
ACCOUNT_NAME="$(
|
||||||
|
printf '%s' "$ACCOUNT" |
|
||||||
|
jq -r '.name // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
EMAIL_ADDRESS="$(
|
||||||
|
printf '%s' "$ACCOUNT" |
|
||||||
|
jq -r '.emailAddress // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
PROVIDER="$(
|
||||||
|
printf '%s' "$ACCOUNT" |
|
||||||
|
jq -r '.provider // "Unknown"'
|
||||||
|
)"
|
||||||
|
|
||||||
|
ENABLED="$(
|
||||||
|
printf '%s' "$ACCOUNT" |
|
||||||
|
jq -r '.isEnabled // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
LAST_SYNC="$(
|
||||||
|
printf '%s' "$ACCOUNT" |
|
||||||
|
jq -r '.lastSync // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Prefer friendly account name, then email address.
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ -n "$ACCOUNT_NAME" ]]; then
|
||||||
|
SERVICE_NAME="$ACCOUNT_NAME"
|
||||||
|
elif [[ -n "$EMAIL_ADDRESS" ]]; then
|
||||||
|
SERVICE_NAME="$EMAIL_ADDRESS"
|
||||||
|
else
|
||||||
|
SERVICE_NAME="Unknown"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SERVICE_NAME="${SERVICE_NAME//\"/\'}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Disabled accounts are treated as intentional.
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$ENABLED" != "true" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Mail Archive ${SERVICE_NAME}\" - Disabled, provider ${PROVIDER}"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Enabled account without a sync timestamp.
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ -z "$LAST_SYNC" || "$LAST_SYNC" == "null" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"Mail Archive ${SERVICE_NAME}\" - Enabled but no successful sync timestamp is available, provider ${PROVIDER}"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Mail-Archiver supplies UTC without an explicit Z/+00:00 suffix.
|
||||||
|
# Force UTC interpretation rather than allowing GNU date to interpret
|
||||||
|
# the timestamp using the host's local timezone.
|
||||||
|
#
|
||||||
|
|
||||||
|
LAST_SYNC_EPOCH="$(
|
||||||
|
TZ=UTC date -d "$LAST_SYNC" +%s 2>/dev/null || true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$LAST_SYNC_EPOCH" || ! "$LAST_SYNC_EPOCH" =~ ^[0-9]+$ ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archive ${SERVICE_NAME}\" - Unable to parse lastSync '${LAST_SYNC}'"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
SYNC_AGE=$((NOW_EPOCH - LAST_SYNC_EPOCH))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Protect against small clock differences between systems.
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$SYNC_AGE" -lt 0 ]]; then
|
||||||
|
SYNC_AGE=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
DAYS=$((SYNC_AGE / 86400))
|
||||||
|
HOURS=$(((SYNC_AGE % 86400) / 3600))
|
||||||
|
MINUTES=$(((SYNC_AGE % 3600) / 60))
|
||||||
|
|
||||||
|
if [[ "$DAYS" -gt 0 ]]; then
|
||||||
|
AGE_TEXT="${DAYS}d ${HOURS}h ${MINUTES}m"
|
||||||
|
elif [[ "$HOURS" -gt 0 ]]; then
|
||||||
|
AGE_TEXT="${HOURS}h ${MINUTES}m"
|
||||||
|
else
|
||||||
|
AGE_TEXT="${MINUTES}m"
|
||||||
|
fi
|
||||||
|
|
||||||
|
STATE=$OK
|
||||||
|
|
||||||
|
if [[ "$SYNC_AGE" -ge "$CRIT_SYNC_AGE" ]]; then
|
||||||
|
STATE=$CRIT
|
||||||
|
elif [[ "$SYNC_AGE" -ge "$WARN_SYNC_AGE" ]]; then
|
||||||
|
STATE=$WARN
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${STATE} \"Mail Archive ${SERVICE_NAME}\" sync_age=${SYNC_AGE};${WARN_SYNC_AGE};${CRIT_SYNC_AGE};0 Last sync ${AGE_TEXT} ago, provider ${PROVIDER}"
|
||||||
|
|
||||||
|
done < <(
|
||||||
|
printf '%s' "$ACCOUNTS_RESPONSE" |
|
||||||
|
jq -c '.[]'
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Installed version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Community Scripts stores the deployed Mail-Archiver release version in:
|
||||||
|
#
|
||||||
|
# /root/.mail-archiver
|
||||||
|
#
|
||||||
|
# This is maintained by the Community Scripts installer/updater and is used
|
||||||
|
# as the authoritative installed-version source here.
|
||||||
|
#
|
||||||
|
|
||||||
|
INSTALLED_VERSION=""
|
||||||
|
|
||||||
|
if [[ -r "$VERSION_FILE" ]]; then
|
||||||
|
INSTALLED_VERSION="$(
|
||||||
|
head -n 1 "$VERSION_FILE" |
|
||||||
|
tr -d '[:space:]'
|
||||||
|
)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
INSTALLED_VERSION="${INSTALLED_VERSION#v}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Mail-Archiver update check
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$CHECK_UPDATES" == "yes" ]]; then
|
||||||
|
|
||||||
|
RELEASE_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
-H "User-Agent: checkmk-mail-archiver-local-check" \
|
||||||
|
"$MAIL_ARCHIVER_RELEASE_API" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
RELEASE_RC=$?
|
||||||
|
|
||||||
|
if [[ $RELEASE_RC -ne 0 || -z "$RELEASE_RESPONSE" ]]; then
|
||||||
|
|
||||||
|
if [[ -n "$INSTALLED_VERSION" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver Update\" - Installed ${INSTALLED_VERSION}, unable to query GitHub releases"
|
||||||
|
else
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver Update\" - Unable to determine installed version or query GitHub releases"
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif ! printf '%s' "$RELEASE_RESPONSE" |
|
||||||
|
jq -e 'type == "object"' >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver Update\" - GitHub release API returned unexpected data"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
LATEST_VERSION="$(
|
||||||
|
printf '%s' "$RELEASE_RESPONSE" |
|
||||||
|
jq -r '.tag_name // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
LATEST_VERSION="${LATEST_VERSION#v}"
|
||||||
|
|
||||||
|
if [[ -z "$LATEST_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver Update\" - Unable to determine latest release"
|
||||||
|
|
||||||
|
elif [[ -z "$INSTALLED_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver Update\" - Latest ${LATEST_VERSION}, but installed version marker ${VERSION_FILE} is missing or unreadable"
|
||||||
|
|
||||||
|
elif [[ ! "$INSTALLED_VERSION" =~ ^[0-9]+([.][0-9]+)*$ ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver Update\" - Unable to parse installed version ${INSTALLED_VERSION}"
|
||||||
|
|
||||||
|
elif [[ ! "$LATEST_VERSION" =~ ^[0-9]+([.][0-9]+)*$ ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Mail Archiver Update\" - Unable to parse latest version ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
NEWEST="$(
|
||||||
|
printf '%s\n%s\n' \
|
||||||
|
"$INSTALLED_VERSION" \
|
||||||
|
"$LATEST_VERSION" |
|
||||||
|
sort -V |
|
||||||
|
tail -n 1
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Mail Archiver Update\" - Current: installed ${INSTALLED_VERSION}, latest ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
elif [[ "$NEWEST" == "$LATEST_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"Mail Archiver Update\" - Update available: installed ${INSTALLED_VERSION}, latest ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Mail Archiver Update\" - Installed ${INSTALLED_VERSION} is newer than latest ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,440 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# /usr/lib/check_mk_agent/local/300/check_npm
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
CONFIG="/etc/check_mk/npm.conf"
|
||||||
|
|
||||||
|
OK=0
|
||||||
|
WARN=1
|
||||||
|
CRIT=2
|
||||||
|
UNKNOWN=3
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ ! -r "$CONFIG" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Nginx Proxy Manager\" - Configuration file ${CONFIG} missing or unreadable"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CONFIG"
|
||||||
|
|
||||||
|
NPM_URL="${NPM_URL:-http://127.0.0.1:81}"
|
||||||
|
NPM_IDENTITY="${NPM_IDENTITY:-}"
|
||||||
|
NPM_SECRET="${NPM_SECRET:-}"
|
||||||
|
|
||||||
|
CHECK_UPDATES="${CHECK_UPDATES:-yes}"
|
||||||
|
NPM_RELEASE_API="${NPM_RELEASE_API:-https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Requirements
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Nginx Proxy Manager\" - curl is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Nginx Proxy Manager\" - jq is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$NPM_IDENTITY" || -z "$NPM_SECRET" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Nginx Proxy Manager\" - NPM_IDENTITY or NPM_SECRET is not configured"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# curl configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
CURL_OPTS=(
|
||||||
|
--silent
|
||||||
|
--show-error
|
||||||
|
--fail
|
||||||
|
--connect-timeout 3
|
||||||
|
--max-time 10
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Public API health / installed version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
STATUS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
"${NPM_URL%/}/api/" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
STATUS_RC=$?
|
||||||
|
|
||||||
|
if [[ $STATUS_RC -ne 0 || -z "$STATUS_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"Nginx Proxy Manager\" - API unavailable at ${NPM_URL}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -e 'type == "object"' >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Nginx Proxy Manager\" - Public API returned unexpected data"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
API_STATUS="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '.status // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
SETUP_COMPLETE="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '.setup // false'
|
||||||
|
)"
|
||||||
|
|
||||||
|
VERSION_MAJOR="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '.version.major // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
VERSION_MINOR="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '.version.minor // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
VERSION_REVISION="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '.version.revision // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
INSTALLED_VERSION=""
|
||||||
|
|
||||||
|
if [[ -n "$VERSION_MAJOR" && -n "$VERSION_MINOR" && -n "$VERSION_REVISION" ]]; then
|
||||||
|
INSTALLED_VERSION="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$API_STATUS" != "OK" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"Nginx Proxy Manager\" - API responded with status ${API_STATUS:-unknown}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$SETUP_COMPLETE" != "true" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"Nginx Proxy Manager\" - API reachable but initial setup is incomplete"
|
||||||
|
else
|
||||||
|
if [[ -n "$INSTALLED_VERSION" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Nginx Proxy Manager\" - API reachable, version ${INSTALLED_VERSION}"
|
||||||
|
else
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Nginx Proxy Manager\" - API reachable"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Update check
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$CHECK_UPDATES" == "yes" ]]; then
|
||||||
|
|
||||||
|
RELEASE_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
-H "User-Agent: checkmk-npm-local-check" \
|
||||||
|
"$NPM_RELEASE_API" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
RELEASE_RC=$?
|
||||||
|
|
||||||
|
if [[ $RELEASE_RC -ne 0 || -z "$RELEASE_RESPONSE" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"NPM Update\" - Installed ${INSTALLED_VERSION:-unknown}, unable to query GitHub releases"
|
||||||
|
|
||||||
|
elif ! printf '%s' "$RELEASE_RESPONSE" |
|
||||||
|
jq -e 'type == "object"' >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"NPM Update\" - GitHub release API returned unexpected data"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
LATEST_VERSION="$(
|
||||||
|
printf '%s' "$RELEASE_RESPONSE" |
|
||||||
|
jq -r '.tag_name // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
LATEST_VERSION="${LATEST_VERSION#v}"
|
||||||
|
|
||||||
|
if [[ -z "$LATEST_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"NPM Update\" - Unable to determine latest release"
|
||||||
|
|
||||||
|
elif [[ -z "$INSTALLED_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"NPM Update\" - Latest ${LATEST_VERSION}, installed version unknown"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
NEWEST="$(
|
||||||
|
printf '%s\n%s\n' \
|
||||||
|
"$INSTALLED_VERSION" \
|
||||||
|
"$LATEST_VERSION" |
|
||||||
|
sort -V |
|
||||||
|
tail -n 1
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"NPM Update\" - Current: installed ${INSTALLED_VERSION}, latest ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
elif [[ "$NEWEST" == "$LATEST_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"NPM Update\" - Update available: installed ${INSTALLED_VERSION}, latest ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"NPM Update\" - Installed ${INSTALLED_VERSION} is newer than latest ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Authentication
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
LOGIN_PAYLOAD="$(
|
||||||
|
jq -n \
|
||||||
|
--arg identity "$NPM_IDENTITY" \
|
||||||
|
--arg secret "$NPM_SECRET" \
|
||||||
|
'{
|
||||||
|
identity: $identity,
|
||||||
|
secret: $secret
|
||||||
|
}'
|
||||||
|
)"
|
||||||
|
|
||||||
|
TOKEN_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$LOGIN_PAYLOAD" \
|
||||||
|
"${NPM_URL%/}/api/tokens" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
TOKEN_RC=$?
|
||||||
|
|
||||||
|
if [[ $TOKEN_RC -ne 0 || -z "$TOKEN_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"NPM Proxy Hosts\" - API reachable but authentication failed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$TOKEN_RESPONSE" |
|
||||||
|
jq -e 'type == "object"' >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"NPM Proxy Hosts\" - Authentication API returned unexpected data"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOKEN="$(
|
||||||
|
printf '%s' "$TOKEN_RESPONSE" |
|
||||||
|
jq -r '.token // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
TOKEN_EXPIRES="$(
|
||||||
|
printf '%s' "$TOKEN_RESPONSE" |
|
||||||
|
jq -r '.expires // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$TOKEN" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"NPM Proxy Hosts\" - Authentication response did not contain a token"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Proxy host inventory
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
HOSTS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
"${NPM_URL%/}/api/nginx/proxy-hosts" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
HOSTS_RC=$?
|
||||||
|
|
||||||
|
if [[ $HOSTS_RC -ne 0 || -z "$HOSTS_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"NPM Proxy Hosts\" - Unable to retrieve proxy hosts"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$HOSTS_RESPONSE" |
|
||||||
|
jq -e 'type == "array"' >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"NPM Proxy Hosts\" - Proxy host API returned unexpected data"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOTAL_HOSTS="$(
|
||||||
|
printf '%s' "$HOSTS_RESPONSE" |
|
||||||
|
jq 'length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
ENABLED_HOSTS="$(
|
||||||
|
printf '%s' "$HOSTS_RESPONSE" |
|
||||||
|
jq '[.[] | select(.enabled == true)] | length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
DISABLED_HOSTS="$(
|
||||||
|
printf '%s' "$HOSTS_RESPONSE" |
|
||||||
|
jq '[.[] | select(.enabled != true)] | length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
ONLINE_HOSTS="$(
|
||||||
|
printf '%s' "$HOSTS_RESPONSE" |
|
||||||
|
jq '
|
||||||
|
[
|
||||||
|
.[]
|
||||||
|
| select(
|
||||||
|
.enabled == true
|
||||||
|
and
|
||||||
|
(.meta.nginx_online == true)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
| length
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
ERROR_HOSTS="$(
|
||||||
|
printf '%s' "$HOSTS_RESPONSE" |
|
||||||
|
jq '
|
||||||
|
[
|
||||||
|
.[]
|
||||||
|
| select(
|
||||||
|
.enabled == true
|
||||||
|
and
|
||||||
|
(
|
||||||
|
.meta.nginx_online != true
|
||||||
|
or
|
||||||
|
.meta.nginx_err != null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
| length
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Broken host details
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
ERROR_LIST="$(
|
||||||
|
printf '%s' "$HOSTS_RESPONSE" |
|
||||||
|
jq -r '
|
||||||
|
.[]
|
||||||
|
| select(
|
||||||
|
.enabled == true
|
||||||
|
and
|
||||||
|
(
|
||||||
|
.meta.nginx_online != true
|
||||||
|
or
|
||||||
|
.meta.nginx_err != null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
||||||
|
(
|
||||||
|
(.domain_names | join(","))
|
||||||
|
+
|
||||||
|
" -> "
|
||||||
|
+
|
||||||
|
(.forward_scheme // "http")
|
||||||
|
+
|
||||||
|
"://"
|
||||||
|
+
|
||||||
|
(.forward_host // "unknown")
|
||||||
|
+
|
||||||
|
":"
|
||||||
|
+
|
||||||
|
((.forward_port // 0) | tostring)
|
||||||
|
+
|
||||||
|
(
|
||||||
|
if .meta.nginx_err != null
|
||||||
|
then " (" + (.meta.nginx_err | tostring) + ")"
|
||||||
|
else ""
|
||||||
|
end
|
||||||
|
)
|
||||||
|
)
|
||||||
|
' |
|
||||||
|
paste -sd ';' -
|
||||||
|
)"
|
||||||
|
|
||||||
|
STATE=$OK
|
||||||
|
|
||||||
|
if [[ "$ERROR_HOSTS" -gt 0 ]]; then
|
||||||
|
STATE=$CRIT
|
||||||
|
fi
|
||||||
|
|
||||||
|
PERFDATA="total=${TOTAL_HOSTS};;;0|enabled=${ENABLED_HOSTS};;;0|disabled=${DISABLED_HOSTS};;;0|online=${ONLINE_HOSTS};;;0|errors=${ERROR_HOSTS};;;0"
|
||||||
|
|
||||||
|
DETAILS="${ENABLED_HOSTS}/${TOTAL_HOSTS} enabled, ${ONLINE_HOSTS} online"
|
||||||
|
|
||||||
|
if [[ "$DISABLED_HOSTS" -gt 0 ]]; then
|
||||||
|
DETAILS="${DETAILS}, ${DISABLED_HOSTS} disabled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ERROR_HOSTS" -gt 0 ]]; then
|
||||||
|
DETAILS="${DETAILS}, ${ERROR_HOSTS} with nginx errors"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$ERROR_LIST" ]]; then
|
||||||
|
DETAILS="${DETAILS}: ${ERROR_LIST}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${STATE} \"NPM Proxy Hosts\" ${PERFDATA} ${DETAILS}"
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,542 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# /usr/lib/check_mk_agent/local/300/check_pm2
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
CONFIG="/etc/check_mk/pm2.conf"
|
||||||
|
|
||||||
|
OK=0
|
||||||
|
WARN=1
|
||||||
|
CRIT=2
|
||||||
|
UNKNOWN=3
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ ! -r "$CONFIG" ]]; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"PM2\" - Configuration file ${CONFIG} missing or unreadable"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CONFIG"
|
||||||
|
|
||||||
|
PM2_USER="${PM2_USER:-root}"
|
||||||
|
PM2_HOME="${PM2_HOME:-}"
|
||||||
|
|
||||||
|
CHECK_PM2_UPDATES="${CHECK_PM2_UPDATES:-yes}"
|
||||||
|
CHECK_NODE_UPDATES="${CHECK_NODE_UPDATES:-yes}"
|
||||||
|
|
||||||
|
PM2_PACKAGE_URL="${PM2_PACKAGE_URL:-https://registry.npmjs.org/pm2/latest}"
|
||||||
|
NODE_RELEASES_URL="${NODE_RELEASES_URL:-https://nodejs.org/dist/index.json}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Requirements
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"PM2\" - jq is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"PM2\" - curl is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! id "$PM2_USER" >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"PM2\" - Configured PM2 user ${PM2_USER} does not exist"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# User / PM2 environment
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
USER_HOME="$(
|
||||||
|
getent passwd "$PM2_USER" |
|
||||||
|
cut -d: -f6
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$USER_HOME" ]]; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"PM2\" - Unable to determine home directory for ${PM2_USER}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$PM2_HOME" ]]; then
|
||||||
|
PM2_HOME="${USER_HOME}/.pm2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Locate PM2
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
PM2_BIN=""
|
||||||
|
|
||||||
|
for candidate in \
|
||||||
|
/usr/bin/pm2 \
|
||||||
|
/usr/local/bin/pm2 \
|
||||||
|
"${USER_HOME}/.local/bin/pm2"
|
||||||
|
do
|
||||||
|
if [[ -x "$candidate" ]]; then
|
||||||
|
PM2_BIN="$candidate"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$PM2_BIN" ]]; then
|
||||||
|
PM2_BIN="$(command -v pm2 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# NVM fallback
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ -z "$PM2_BIN" && -d "${USER_HOME}/.nvm/versions/node" ]]; then
|
||||||
|
PM2_BIN="$(
|
||||||
|
find "${USER_HOME}/.nvm/versions/node" \
|
||||||
|
-mindepth 3 \
|
||||||
|
-maxdepth 3 \
|
||||||
|
-type f \
|
||||||
|
-path '*/bin/pm2' \
|
||||||
|
-perm -u+x \
|
||||||
|
2>/dev/null |
|
||||||
|
sort -V |
|
||||||
|
tail -n 1
|
||||||
|
)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$PM2_BIN" || ! -x "$PM2_BIN" ]]; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"PM2\" - Unable to locate PM2 executable"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
PM2_BIN_DIR="$(dirname "$PM2_BIN")"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Node used by this PM2 installation.
|
||||||
|
#
|
||||||
|
|
||||||
|
NODE_BIN="${PM2_BIN_DIR}/node"
|
||||||
|
|
||||||
|
if [[ ! -x "$NODE_BIN" ]]; then
|
||||||
|
NODE_BIN="$(command -v node 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Retrieve PM2 process list
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$PM2_USER" == "root" ]]; then
|
||||||
|
|
||||||
|
PM2_JSON="$(
|
||||||
|
PM2_HOME="$PM2_HOME" \
|
||||||
|
PATH="${PM2_BIN_DIR}:${PATH}" \
|
||||||
|
"$PM2_BIN" jlist 2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
PM2_RC=$?
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
PM2_JSON="$(
|
||||||
|
runuser -u "$PM2_USER" -- \
|
||||||
|
env \
|
||||||
|
PM2_HOME="$PM2_HOME" \
|
||||||
|
PATH="${PM2_BIN_DIR}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
|
||||||
|
"$PM2_BIN" jlist 2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
PM2_RC=$?
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $PM2_RC -ne 0 || -z "$PM2_JSON" ]]; then
|
||||||
|
printf '%s\n' "${CRIT} \"PM2\" - Unable to retrieve PM2 process list for user ${PM2_USER}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$PM2_JSON" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"PM2\" - PM2 returned invalid JSON"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
PROCESS_COUNT="$(
|
||||||
|
printf '%s' "$PM2_JSON" |
|
||||||
|
jq 'length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$PROCESS_COUNT" -eq 0 ]]; then
|
||||||
|
printf '%s\n' "${WARN} \"PM2\" processes=0 No processes managed by PM2 for user ${PM2_USER}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Overall PM2 state
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
ONLINE_COUNT="$(
|
||||||
|
printf '%s' "$PM2_JSON" |
|
||||||
|
jq '[.[] | select(.pm2_env.status == "online")] | length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
OFFLINE_COUNT=$((PROCESS_COUNT - ONLINE_COUNT))
|
||||||
|
|
||||||
|
if [[ "$OFFLINE_COUNT" -eq 0 ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"PM2\" processes=${PROCESS_COUNT};;;0|online=${ONLINE_COUNT};;;0|offline=0;;;0 ${PROCESS_COUNT}/${PROCESS_COUNT} processes online"
|
||||||
|
else
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"PM2\" processes=${PROCESS_COUNT};;;0|online=${ONLINE_COUNT};;;0|offline=${OFFLINE_COUNT};;;0 ${ONLINE_COUNT}/${PROCESS_COUNT} processes online, ${OFFLINE_COUNT} not online"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PM2 version / update
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
PM2_VERSION="$(
|
||||||
|
PATH="${PM2_BIN_DIR}:${PATH}" \
|
||||||
|
"$PM2_BIN" --version 2>/dev/null |
|
||||||
|
tail -n 1 |
|
||||||
|
tr -d '[:space:]'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$CHECK_PM2_UPDATES" == "yes" ]]; then
|
||||||
|
|
||||||
|
if [[ -z "$PM2_VERSION" ]]; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"PM2 Update\" - Unable to determine installed PM2 version"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
PM2_RELEASE_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
--silent \
|
||||||
|
--show-error \
|
||||||
|
--fail \
|
||||||
|
--connect-timeout 3 \
|
||||||
|
--max-time 8 \
|
||||||
|
"$PM2_PACKAGE_URL" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$PM2_RELEASE_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"PM2 Update\" - Installed ${PM2_VERSION}, unable to query npm registry"
|
||||||
|
|
||||||
|
elif ! printf '%s' "$PM2_RELEASE_RESPONSE" | jq -e . >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"PM2 Update\" - npm registry returned invalid JSON"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
PM2_LATEST="$(
|
||||||
|
printf '%s' "$PM2_RELEASE_RESPONSE" |
|
||||||
|
jq -r '.version // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$PM2_LATEST" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"PM2 Update\" - Unable to determine latest PM2 version"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
NEWEST="$(
|
||||||
|
printf '%s\n%s\n' \
|
||||||
|
"$PM2_VERSION" \
|
||||||
|
"$PM2_LATEST" |
|
||||||
|
sort -V |
|
||||||
|
tail -n 1
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$PM2_VERSION" == "$PM2_LATEST" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"PM2 Update\" - Current: installed ${PM2_VERSION}, latest ${PM2_LATEST}"
|
||||||
|
|
||||||
|
elif [[ "$NEWEST" == "$PM2_LATEST" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"PM2 Update\" - Update available: installed ${PM2_VERSION}, latest ${PM2_LATEST}"
|
||||||
|
|
||||||
|
else
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"PM2 Update\" - Installed ${PM2_VERSION} is newer than latest ${PM2_LATEST}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Node.js / NVM version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$CHECK_NODE_UPDATES" == "yes" ]]; then
|
||||||
|
|
||||||
|
if [[ -z "$NODE_BIN" || ! -x "$NODE_BIN" ]]; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Node.js Update\" - Unable to locate Node.js executable"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
NODE_VERSION="$(
|
||||||
|
"$NODE_BIN" --version 2>/dev/null |
|
||||||
|
sed 's/^v//'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$NODE_VERSION" ]]; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Node.js Update\" - Unable to determine installed Node.js version"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
NODE_RELEASES="$(
|
||||||
|
curl \
|
||||||
|
--silent \
|
||||||
|
--show-error \
|
||||||
|
--fail \
|
||||||
|
--connect-timeout 3 \
|
||||||
|
--max-time 8 \
|
||||||
|
"$NODE_RELEASES_URL" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$NODE_RELEASES" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Node.js Update\" - Installed ${NODE_VERSION}, unable to query Node.js releases"
|
||||||
|
|
||||||
|
elif ! printf '%s' "$NODE_RELEASES" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Node.js Update\" - Node.js release API returned invalid data"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
INSTALLED_MAJOR="${NODE_VERSION%%.*}"
|
||||||
|
|
||||||
|
LATEST_CURRENT="$(
|
||||||
|
printf '%s' "$NODE_RELEASES" |
|
||||||
|
jq -r '
|
||||||
|
[
|
||||||
|
.[]
|
||||||
|
| select(.lts == false)
|
||||||
|
| .version
|
||||||
|
][0] // empty
|
||||||
|
' |
|
||||||
|
sed 's/^v//'
|
||||||
|
)"
|
||||||
|
|
||||||
|
LATEST_LTS="$(
|
||||||
|
printf '%s' "$NODE_RELEASES" |
|
||||||
|
jq -r '
|
||||||
|
[
|
||||||
|
.[]
|
||||||
|
| select(.lts != false)
|
||||||
|
| .version
|
||||||
|
][0] // empty
|
||||||
|
' |
|
||||||
|
sed 's/^v//'
|
||||||
|
)"
|
||||||
|
|
||||||
|
LATEST_SAME_MAJOR="$(
|
||||||
|
printf '%s' "$NODE_RELEASES" |
|
||||||
|
jq -r \
|
||||||
|
--arg prefix "v${INSTALLED_MAJOR}." '
|
||||||
|
[
|
||||||
|
.[]
|
||||||
|
| select(.version | startswith($prefix))
|
||||||
|
| .version
|
||||||
|
][0] // empty
|
||||||
|
' |
|
||||||
|
sed 's/^v//'
|
||||||
|
)"
|
||||||
|
|
||||||
|
SAME_MAJOR_LTS="$(
|
||||||
|
printf '%s' "$NODE_RELEASES" |
|
||||||
|
jq -r \
|
||||||
|
--arg prefix "v${INSTALLED_MAJOR}." '
|
||||||
|
[
|
||||||
|
.[]
|
||||||
|
| select(.version | startswith($prefix))
|
||||||
|
| .lts
|
||||||
|
][0] // false
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
#
|
||||||
|
# If no releases for the installed major exist in the current
|
||||||
|
# Node.js release index, consider it unsupported/EOL.
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ -z "$LATEST_SAME_MAJOR" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"Node.js Update\" - Installed ${NODE_VERSION} appears unsupported/EOL; latest LTS ${LATEST_LTS}, current ${LATEST_CURRENT}"
|
||||||
|
|
||||||
|
elif [[ "$SAME_MAJOR_LTS" == "false" && "$INSTALLED_MAJOR" -lt "${LATEST_CURRENT%%.*}" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"Node.js Update\" - Installed ${NODE_VERSION} is on a non-LTS release line; latest LTS ${LATEST_LTS}, current ${LATEST_CURRENT}"
|
||||||
|
|
||||||
|
elif [[ "$NODE_VERSION" != "$LATEST_SAME_MAJOR" ]]; then
|
||||||
|
|
||||||
|
NEWEST="$(
|
||||||
|
printf '%s\n%s\n' \
|
||||||
|
"$NODE_VERSION" \
|
||||||
|
"$LATEST_SAME_MAJOR" |
|
||||||
|
sort -V |
|
||||||
|
tail -n 1
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$NEWEST" == "$LATEST_SAME_MAJOR" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"Node.js Update\" - Update available in Node ${INSTALLED_MAJOR}.x: installed ${NODE_VERSION}, latest ${LATEST_SAME_MAJOR}; latest LTS ${LATEST_LTS}"
|
||||||
|
else
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Node.js Update\" - Installed ${NODE_VERSION}; latest LTS ${LATEST_LTS}, current ${LATEST_CURRENT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Node.js Update\" - Current on Node ${INSTALLED_MAJOR}.x: ${NODE_VERSION}; latest LTS ${LATEST_LTS}, current ${LATEST_CURRENT}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Individual PM2 processes
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
while IFS= read -r PROCESS; do
|
||||||
|
|
||||||
|
NAME="$(
|
||||||
|
printf '%s' "$PROCESS" |
|
||||||
|
jq -r '.name // "unknown"'
|
||||||
|
)"
|
||||||
|
|
||||||
|
STATUS="$(
|
||||||
|
printf '%s' "$PROCESS" |
|
||||||
|
jq -r '.pm2_env.status // "unknown"'
|
||||||
|
)"
|
||||||
|
|
||||||
|
PID="$(
|
||||||
|
printf '%s' "$PROCESS" |
|
||||||
|
jq -r '.pid // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
RESTARTS="$(
|
||||||
|
printf '%s' "$PROCESS" |
|
||||||
|
jq -r '.pm2_env.restart_time // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
CRON_RESTART="$(
|
||||||
|
printf '%s' "$PROCESS" |
|
||||||
|
jq -r '.pm2_env.cron_restart // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
AUTORESTART="$(
|
||||||
|
printf '%s' "$PROCESS" |
|
||||||
|
jq -r '.pm2_env.autorestart // true'
|
||||||
|
)"
|
||||||
|
|
||||||
|
CPU="$(
|
||||||
|
printf '%s' "$PROCESS" |
|
||||||
|
jq -r '.monit.cpu // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
MEMORY_BYTES="$(
|
||||||
|
printf '%s' "$PROCESS" |
|
||||||
|
jq -r '.monit.memory // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
START_TIME="$(
|
||||||
|
printf '%s' "$PROCESS" |
|
||||||
|
jq -r '.pm2_env.pm_uptime // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
NAME="${NAME//\"/\'}"
|
||||||
|
|
||||||
|
MEMORY_MB="$(
|
||||||
|
awk -v bytes="$MEMORY_BYTES" \
|
||||||
|
'BEGIN { printf "%.1f", bytes / 1024 / 1024 }'
|
||||||
|
)"
|
||||||
|
|
||||||
|
NOW_MS=$(( $(date +%s) * 1000 ))
|
||||||
|
|
||||||
|
if [[ "$START_TIME" =~ ^[0-9]+$ && "$START_TIME" -gt 0 ]]; then
|
||||||
|
|
||||||
|
UPTIME_SECONDS="$(
|
||||||
|
awk -v now="$NOW_MS" -v start="$START_TIME" \
|
||||||
|
'BEGIN {
|
||||||
|
diff = (now - start) / 1000
|
||||||
|
if (diff < 0) diff = 0
|
||||||
|
printf "%.0f", diff
|
||||||
|
}'
|
||||||
|
)"
|
||||||
|
|
||||||
|
else
|
||||||
|
UPTIME_SECONDS=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
DAYS=$((UPTIME_SECONDS / 86400))
|
||||||
|
HOURS=$(((UPTIME_SECONDS % 86400) / 3600))
|
||||||
|
MINUTES=$(((UPTIME_SECONDS % 3600) / 60))
|
||||||
|
|
||||||
|
if [[ "$DAYS" -gt 0 ]]; then
|
||||||
|
UPTIME_TEXT="${DAYS}d ${HOURS}h ${MINUTES}m"
|
||||||
|
elif [[ "$HOURS" -gt 0 ]]; then
|
||||||
|
UPTIME_TEXT="${HOURS}h ${MINUTES}m"
|
||||||
|
else
|
||||||
|
UPTIME_TEXT="${MINUTES}m"
|
||||||
|
fi
|
||||||
|
|
||||||
|
STATE=$OK
|
||||||
|
STATE_TEXT="Online"
|
||||||
|
|
||||||
|
if [[ "$STATUS" != "online" ]]; then
|
||||||
|
STATE=$CRIT
|
||||||
|
STATE_TEXT="Status ${STATUS}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$CRON_RESTART" ]]; then
|
||||||
|
RESTART_INFO="restarts ${RESTARTS}, scheduled restart ${CRON_RESTART}"
|
||||||
|
else
|
||||||
|
RESTART_INFO="restarts ${RESTARTS}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$AUTORESTART" == "false" ]]; then
|
||||||
|
RESTART_INFO="${RESTART_INFO}, autorestart disabled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PERFDATA="cpu=${CPU}%;;;0;100|memory=${MEMORY_BYTES}B;;;0|restarts=${RESTARTS};;;0|uptime=${UPTIME_SECONDS}s;;;0"
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${STATE} \"PM2 ${NAME}\" ${PERFDATA} ${STATE_TEXT} - PID ${PID}, uptime ${UPTIME_TEXT}, CPU ${CPU}%, memory ${MEMORY_MB} MiB, ${RESTART_INFO}"
|
||||||
|
|
||||||
|
done < <(
|
||||||
|
printf '%s' "$PM2_JSON" |
|
||||||
|
jq -c '.[]'
|
||||||
|
)
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# /usr/lib/check_mk_agent/local/300/check_portainer
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
CONFIG="/etc/check_mk/portainer.conf"
|
||||||
|
|
||||||
|
OK=0
|
||||||
|
WARN=1
|
||||||
|
CRIT=2
|
||||||
|
UNKNOWN=3
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ ! -r "$CONFIG" ]]; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Portainer\" - Configuration file ${CONFIG} missing or unreadable"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CONFIG"
|
||||||
|
|
||||||
|
PORTAINER_URL="${PORTAINER_URL:-https://127.0.0.1:9443}"
|
||||||
|
PORTAINER_TOKEN="${PORTAINER_TOKEN:-}"
|
||||||
|
PORTAINER_INSECURE="${PORTAINER_INSECURE:-yes}"
|
||||||
|
ALERT_STOPPED_CONTAINERS="${ALERT_STOPPED_CONTAINERS:-no}"
|
||||||
|
CHECK_UPDATES="${CHECK_UPDATES:-yes}"
|
||||||
|
PORTAINER_RELEASE_API="${PORTAINER_RELEASE_API:-https://api.github.com/repos/portainer/portainer/releases/latest}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Requirements
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Portainer\" - curl is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Portainer\" - jq is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v sort >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Portainer\" - sort is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$PORTAINER_TOKEN" ]]; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Portainer\" - PORTAINER_TOKEN is not configured"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# curl configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
CURL_OPTS=(
|
||||||
|
--silent
|
||||||
|
--show-error
|
||||||
|
--connect-timeout 3
|
||||||
|
--max-time 10
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ "$PORTAINER_INSECURE" == "yes" ]]; then
|
||||||
|
CURL_OPTS+=(--insecure)
|
||||||
|
fi
|
||||||
|
|
||||||
|
AUTH_HEADER="X-API-Key: ${PORTAINER_TOKEN}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Portainer health / version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
STATUS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
"${PORTAINER_URL%/}/api/status" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$STATUS_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' "${CRIT} \"Portainer\" - API unavailable at ${PORTAINER_URL}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$STATUS_RESPONSE" | jq -e . >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Portainer\" - Status API returned invalid JSON"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION="$(
|
||||||
|
printf '%s' "$STATUS_RESPONSE" |
|
||||||
|
jq -r '.Version // .version // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -n "$VERSION" ]]; then
|
||||||
|
printf '%s\n' "${OK} \"Portainer\" - API reachable, version ${VERSION}"
|
||||||
|
else
|
||||||
|
printf '%s\n' "${OK} \"Portainer\" - API reachable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Portainer update check
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$CHECK_UPDATES" == "yes" ]]; then
|
||||||
|
|
||||||
|
if [[ -z "$VERSION" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Portainer Update\" - Installed Portainer version could not be determined"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
RELEASE_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
-H "User-Agent: checkmk-portainer-local-check" \
|
||||||
|
"$PORTAINER_RELEASE_API" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$RELEASE_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Portainer Update\" - Unable to retrieve latest release information"
|
||||||
|
|
||||||
|
elif ! printf '%s' "$RELEASE_RESPONSE" | jq -e . >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Portainer Update\" - Release API returned invalid JSON"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
LATEST_VERSION="$(
|
||||||
|
printf '%s' "$RELEASE_RESPONSE" |
|
||||||
|
jq -r '.tag_name // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
INSTALLED_VERSION="${VERSION#v}"
|
||||||
|
LATEST_VERSION="${LATEST_VERSION#v}"
|
||||||
|
|
||||||
|
if [[ -z "$LATEST_VERSION" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Portainer Update\" - Latest release version could not be determined"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
#
|
||||||
|
# Strip known suffixes before numeric comparison.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# 2.36.0
|
||||||
|
# 2.36.0-sts
|
||||||
|
# 2.36.0-lts
|
||||||
|
#
|
||||||
|
|
||||||
|
INSTALLED_COMPARE="${INSTALLED_VERSION%%-*}"
|
||||||
|
LATEST_COMPARE="${LATEST_VERSION%%-*}"
|
||||||
|
|
||||||
|
if [[ ! "$INSTALLED_COMPARE" =~ ^[0-9]+([.][0-9]+)*$ ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Portainer Update\" - Unable to parse installed version ${INSTALLED_VERSION}"
|
||||||
|
|
||||||
|
elif [[ ! "$LATEST_COMPARE" =~ ^[0-9]+([.][0-9]+)*$ ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Portainer Update\" - Unable to parse latest version ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
NEWEST="$(
|
||||||
|
printf '%s\n%s\n' \
|
||||||
|
"$INSTALLED_COMPARE" \
|
||||||
|
"$LATEST_COMPARE" |
|
||||||
|
sort -V |
|
||||||
|
tail -n 1
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$INSTALLED_COMPARE" == "$LATEST_COMPARE" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Portainer Update\" - Current: installed ${INSTALLED_VERSION}, latest ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
elif [[ "$NEWEST" == "$LATEST_COMPARE" ]]; then
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${WARN} \"Portainer Update\" - Update available: installed ${INSTALLED_VERSION}, latest ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Portainer Update\" - Installed ${INSTALLED_VERSION} is newer than latest stable ${LATEST_VERSION}"
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Endpoints
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
ENDPOINTS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "$AUTH_HEADER" \
|
||||||
|
"${PORTAINER_URL%/}/api/endpoints" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$ENDPOINTS_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' "${CRIT} \"Portainer Endpoints\" - Unable to retrieve endpoints"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$ENDPOINTS_RESPONSE" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "${UNKNOWN} \"Portainer Endpoints\" - Endpoint API returned unexpected data"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENDPOINT_COUNT="$(
|
||||||
|
printf '%s' "$ENDPOINTS_RESPONSE" |
|
||||||
|
jq 'length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
UP_ENDPOINTS=0
|
||||||
|
DOWN_ENDPOINTS=0
|
||||||
|
|
||||||
|
while IFS= read -r ENDPOINT; do
|
||||||
|
|
||||||
|
ENDPOINT_ID="$(
|
||||||
|
printf '%s' "$ENDPOINT" |
|
||||||
|
jq -r '.Id // .ID // empty'
|
||||||
|
)"
|
||||||
|
|
||||||
|
ENDPOINT_NAME="$(
|
||||||
|
printf '%s' "$ENDPOINT" |
|
||||||
|
jq -r '.Name // "Unknown"'
|
||||||
|
)"
|
||||||
|
|
||||||
|
ENDPOINT_STATUS="$(
|
||||||
|
printf '%s' "$ENDPOINT" |
|
||||||
|
jq -r '.Status // 0'
|
||||||
|
)"
|
||||||
|
|
||||||
|
ENDPOINT_NAME="${ENDPOINT_NAME//\"/\'}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Portainer endpoint states:
|
||||||
|
# 1 = UP
|
||||||
|
# 2 = DOWN
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$ENDPOINT_STATUS" == "1" ]]; then
|
||||||
|
|
||||||
|
UP_ENDPOINTS=$((UP_ENDPOINTS + 1))
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${OK} \"Portainer Endpoint ${ENDPOINT_NAME}\" - Endpoint is UP"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
DOWN_ENDPOINTS=$((DOWN_ENDPOINTS + 1))
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${CRIT} \"Portainer Endpoint ${ENDPOINT_NAME}\" - Endpoint is DOWN, status ${ENDPOINT_STATUS}"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Containers for this endpoint
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ -z "$ENDPOINT_ID" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Docker Containers ${ENDPOINT_NAME}\" - Endpoint ID missing"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
CONTAINERS_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
-H "$AUTH_HEADER" \
|
||||||
|
"${PORTAINER_URL%/}/api/endpoints/${ENDPOINT_ID}/docker/containers/json?all=1" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$CONTAINERS_RESPONSE" ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Docker Containers ${ENDPOINT_NAME}\" - Unable to retrieve containers"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$CONTAINERS_RESPONSE" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' \
|
||||||
|
"${UNKNOWN} \"Docker Containers ${ENDPOINT_NAME}\" - Container API returned unexpected data"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOTAL_CONTAINERS="$(
|
||||||
|
printf '%s' "$CONTAINERS_RESPONSE" |
|
||||||
|
jq 'length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
RUNNING_CONTAINERS="$(
|
||||||
|
printf '%s' "$CONTAINERS_RESPONSE" |
|
||||||
|
jq '[.[] | select(.State == "running")] | length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
STOPPED_CONTAINERS="$(
|
||||||
|
printf '%s' "$CONTAINERS_RESPONSE" |
|
||||||
|
jq '[.[] | select(.State != "running")] | length'
|
||||||
|
)"
|
||||||
|
|
||||||
|
UNHEALTHY_CONTAINERS="$(
|
||||||
|
printf '%s' "$CONTAINERS_RESPONSE" |
|
||||||
|
jq '
|
||||||
|
[
|
||||||
|
.[]
|
||||||
|
| select(
|
||||||
|
((.Status // "") | test("\\(unhealthy\\)"))
|
||||||
|
)
|
||||||
|
]
|
||||||
|
| length
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
PROBLEM_LIST="$(
|
||||||
|
printf '%s' "$CONTAINERS_RESPONSE" |
|
||||||
|
jq -r '
|
||||||
|
.[]
|
||||||
|
| select(
|
||||||
|
(.State != "running")
|
||||||
|
or
|
||||||
|
((.Status // "") | test("\\(unhealthy\\)"))
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
(
|
||||||
|
(.Names[0] // .Id // "unknown")
|
||||||
|
| sub("^/"; "")
|
||||||
|
)
|
||||||
|
+ "="
|
||||||
|
+ (.Status // .State // "unknown")
|
||||||
|
)
|
||||||
|
' |
|
||||||
|
paste -sd ';' -
|
||||||
|
)"
|
||||||
|
|
||||||
|
STATE=$OK
|
||||||
|
|
||||||
|
if [[ "$UNHEALTHY_CONTAINERS" -gt 0 ]]; then
|
||||||
|
STATE=$CRIT
|
||||||
|
|
||||||
|
elif [[ "$ALERT_STOPPED_CONTAINERS" == "yes" && "$STOPPED_CONTAINERS" -gt 0 ]]; then
|
||||||
|
STATE=$WARN
|
||||||
|
fi
|
||||||
|
|
||||||
|
PERFDATA="total=${TOTAL_CONTAINERS};;;0|running=${RUNNING_CONTAINERS};;;0|stopped=${STOPPED_CONTAINERS};;;0|unhealthy=${UNHEALTHY_CONTAINERS};;;0"
|
||||||
|
|
||||||
|
DETAILS="${RUNNING_CONTAINERS}/${TOTAL_CONTAINERS} running"
|
||||||
|
|
||||||
|
if [[ "$UNHEALTHY_CONTAINERS" -gt 0 ]]; then
|
||||||
|
DETAILS="${DETAILS}, ${UNHEALTHY_CONTAINERS} unhealthy"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$STOPPED_CONTAINERS" -gt 0 ]]; then
|
||||||
|
DETAILS="${DETAILS}, ${STOPPED_CONTAINERS} stopped"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$PROBLEM_LIST" ]]; then
|
||||||
|
DETAILS="${DETAILS}: ${PROBLEM_LIST}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${STATE} \"Docker Containers ${ENDPOINT_NAME}\" ${PERFDATA} ${DETAILS}"
|
||||||
|
|
||||||
|
done < <(
|
||||||
|
printf '%s' "$ENDPOINTS_RESPONSE" |
|
||||||
|
jq -c '.[]'
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Endpoint summary
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
ENDPOINT_STATE=$OK
|
||||||
|
|
||||||
|
if [[ "$DOWN_ENDPOINTS" -gt 0 ]]; then
|
||||||
|
ENDPOINT_STATE=$CRIT
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
"${ENDPOINT_STATE} \"Portainer Endpoints\" total=${ENDPOINT_COUNT};;;0|up=${UP_ENDPOINTS};;;0|down=${DOWN_ENDPOINTS};;;0 ${UP_ENDPOINTS}/${ENDPOINT_COUNT} endpoints UP"
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
CONFIG="/etc/check_mk/prometheus.conf"
|
||||||
|
|
||||||
|
OK=0
|
||||||
|
WARN=1
|
||||||
|
CRIT=2
|
||||||
|
UNKNOWN=3
|
||||||
|
|
||||||
|
if [[ ! -r "$CONFIG" ]]; then
|
||||||
|
echo "${UNKNOWN} \"Prometheus\" - Configuration file ${CONFIG} missing or unreadable"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CONFIG"
|
||||||
|
|
||||||
|
PROMETHEUS_URL="${PROMETHEUS_URL:-http://127.0.0.1:9090}"
|
||||||
|
PROMETHEUS_TOKEN="${PROMETHEUS_TOKEN:-}"
|
||||||
|
TARGET_DOWN_STATE="${TARGET_DOWN_STATE:-2}"
|
||||||
|
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
echo "${UNKNOWN} \"Prometheus\" - curl is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
echo "${UNKNOWN} \"Prometheus\" - jq is not installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
CURL_OPTS=(
|
||||||
|
--silent
|
||||||
|
--show-error
|
||||||
|
--fail
|
||||||
|
--connect-timeout 3
|
||||||
|
--max-time 8
|
||||||
|
)
|
||||||
|
|
||||||
|
AUTH_ARGS=()
|
||||||
|
|
||||||
|
if [[ -n "$PROMETHEUS_TOKEN" ]]; then
|
||||||
|
AUTH_ARGS=(
|
||||||
|
--header "Authorization: Bearer ${PROMETHEUS_TOKEN}"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Prometheus health / version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
BUILD_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
"${AUTH_ARGS[@]}" \
|
||||||
|
"${PROMETHEUS_URL%/}/api/v1/status/buildinfo" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$BUILD_RESPONSE" ]]; then
|
||||||
|
echo "${CRIT} \"Prometheus\" - API unavailable at ${PROMETHEUS_URL}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
API_STATUS="$(
|
||||||
|
printf '%s' "$BUILD_RESPONSE" |
|
||||||
|
jq -r '.status // empty' 2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
VERSION="$(
|
||||||
|
printf '%s' "$BUILD_RESPONSE" |
|
||||||
|
jq -r '.data.version // empty' 2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$API_STATUS" != "success" || -z "$VERSION" ]]; then
|
||||||
|
echo "${CRIT} \"Prometheus\" - API responded but build information is invalid"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${OK} \"Prometheus\" - API reachable, version ${VERSION}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Prometheus scrape targets
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
TARGET_RESPONSE="$(
|
||||||
|
curl \
|
||||||
|
"${CURL_OPTS[@]}" \
|
||||||
|
"${AUTH_ARGS[@]}" \
|
||||||
|
"${PROMETHEUS_URL%/}/api/v1/targets?state=active" \
|
||||||
|
2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
CURL_RC=$?
|
||||||
|
|
||||||
|
if [[ $CURL_RC -ne 0 || -z "$TARGET_RESPONSE" ]]; then
|
||||||
|
echo "${UNKNOWN} \"Prometheus Targets\" - Unable to retrieve target status"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
TARGET_API_STATUS="$(
|
||||||
|
printf '%s' "$TARGET_RESPONSE" |
|
||||||
|
jq -r '.status // empty' 2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ "$TARGET_API_STATUS" != "success" ]]; then
|
||||||
|
echo "${UNKNOWN} \"Prometheus Targets\" - Prometheus returned an unsuccessful API response"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOTAL="$(
|
||||||
|
printf '%s' "$TARGET_RESPONSE" |
|
||||||
|
jq '.data.activeTargets | length' 2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
UP="$(
|
||||||
|
printf '%s' "$TARGET_RESPONSE" |
|
||||||
|
jq '[.data.activeTargets[] | select(.health == "up")] | length' 2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
DOWN="$(
|
||||||
|
printf '%s' "$TARGET_RESPONSE" |
|
||||||
|
jq '[.data.activeTargets[] | select(.health != "up")] | length' 2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$TOTAL" || -z "$UP" || -z "$DOWN" ]]; then
|
||||||
|
echo "${UNKNOWN} \"Prometheus Targets\" - Unable to parse target information"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
PERFDATA="total=${TOTAL} up=${UP} down=${DOWN}"
|
||||||
|
|
||||||
|
if [[ "$DOWN" -eq 0 ]]; then
|
||||||
|
echo "${OK} \"Prometheus Targets\" ${PERFDATA} All ${TOTAL} active targets are UP"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Build readable DOWN target list.
|
||||||
|
#
|
||||||
|
# Prefer job + instance labels. Fall back to scrape URL where needed.
|
||||||
|
#
|
||||||
|
DOWN_LIST="$(
|
||||||
|
printf '%s' "$TARGET_RESPONSE" |
|
||||||
|
jq -r '
|
||||||
|
.data.activeTargets[]
|
||||||
|
| select(.health != "up")
|
||||||
|
| (
|
||||||
|
(.labels.job // "unknown-job")
|
||||||
|
+ "/"
|
||||||
|
+ (.labels.instance // .scrapeUrl // "unknown-target")
|
||||||
|
+ (
|
||||||
|
if (.lastError // "") != ""
|
||||||
|
then " (" + .lastError + ")"
|
||||||
|
else ""
|
||||||
|
end
|
||||||
|
)
|
||||||
|
)
|
||||||
|
' 2>/dev/null |
|
||||||
|
paste -sd '; ' -
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$DOWN_LIST" ]]; then
|
||||||
|
DOWN_LIST="unable to identify failed targets"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${TARGET_DOWN_STATE} \"Prometheus Targets\" ${PERFDATA} ${UP}/${TOTAL} targets UP, ${DOWN} DOWN: ${DOWN_LIST}"
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# /etc/check_mk/crafty.conf
|
||||||
|
|
||||||
|
CRAFTY_URL="https://127.0.0.1:8443"
|
||||||
|
|
||||||
|
CRAFTY_USER="checkmk"
|
||||||
|
CRAFTY_PASSWORD="CHANGE_ME"
|
||||||
|
|
||||||
|
CRAFTY_INSECURE="yes"
|
||||||
|
IGNORE_HIDDEN_SERVERS="yes"
|
||||||
|
|
||||||
|
CHECK_UPDATES="yes"
|
||||||
|
|
||||||
|
# Community Script installation path.
|
||||||
|
CRAFTY_INSTALL_DIR="/opt/crafty-controller/crafty/crafty-4"
|
||||||
|
|
||||||
|
# Normally leave empty. Useful only if automatic version detection fails.
|
||||||
|
CRAFTY_INSTALLED_VERSION=""
|
||||||
|
|
||||||
|
CRAFTY_RELEASE_API="https://gitlab.com/api/v4/projects/34675721/releases/permalink/latest"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Gitea API base URL.
|
||||||
|
# Localhost is preferable because CheckMK runs directly on the Gitea host.
|
||||||
|
GITEA_URL="http://127.0.0.1:3000"
|
||||||
|
|
||||||
|
# Optional API token.
|
||||||
|
# /api/v1/version may be accessible without authentication depending on config,
|
||||||
|
# but using a dedicated read-only token keeps things predictable.
|
||||||
|
GITEA_TOKEN=""
|
||||||
|
|
||||||
|
# Enable/disable upstream update check.
|
||||||
|
CHECK_UPDATES="yes"
|
||||||
|
|
||||||
|
# Upstream Gitea repository API.
|
||||||
|
GITEA_RELEASE_API="https://api.github.com/repos/go-gitea/gitea/releases/latest"
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# /etc/check_mk/mail_archiver.conf
|
||||||
|
|
||||||
|
MAIL_ARCHIVER_URL="http://127.0.0.1:5000"
|
||||||
|
MAIL_ARCHIVER_TOKEN="ma_CHANGE_ME"
|
||||||
|
|
||||||
|
WARN_SYNC_AGE=3600
|
||||||
|
CRIT_SYNC_AGE=7200
|
||||||
|
|
||||||
|
CHECK_UPDATES="yes"
|
||||||
|
MAIL_ARCHIVER_RELEASE_API="https://api.github.com/repos/s1t5/mail-archiver/releases/latest"
|
||||||
|
|
||||||
|
VERSION_FILE="/root/.mail-archiver"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# /etc/check_mk/npm.conf
|
||||||
|
|
||||||
|
NPM_URL="http://127.0.0.1:81"
|
||||||
|
|
||||||
|
NPM_IDENTITY="checkmk@example.com"
|
||||||
|
NPM_SECRET="CHANGE_ME"
|
||||||
|
|
||||||
|
CHECK_UPDATES="yes"
|
||||||
|
NPM_RELEASE_API="https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# /etc/check_mk/pm2.conf
|
||||||
|
|
||||||
|
PM2_USER="root"
|
||||||
|
PM2_HOME=""
|
||||||
|
|
||||||
|
CHECK_PM2_UPDATES="yes"
|
||||||
|
CHECK_NODE_UPDATES="yes"
|
||||||
|
|
||||||
|
PM2_PACKAGE_URL="https://registry.npmjs.org/pm2/latest"
|
||||||
|
NODE_RELEASES_URL="https://nodejs.org/dist/index.json"
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# /etc/check_mk/portainer.conf
|
||||||
|
|
||||||
|
CHECK_UPDATES="yes"
|
||||||
|
PORTAINER_RELEASE_API="https://api.github.com/repos/portainer/portainer/releases/latest"
|
||||||
|
|
||||||
|
PORTAINER_URL="https://127.0.0.1:9443"
|
||||||
|
|
||||||
|
PORTAINER_TOKEN="ptr_XXXXXXXXXXXXXXXXXXXX"
|
||||||
|
|
||||||
|
# Portainer's local certificate may be self-signed.
|
||||||
|
PORTAINER_INSECURE="yes"
|
||||||
|
|
||||||
|
# Ignore containers that were intentionally stopped.
|
||||||
|
# Only unhealthy/running-state problems will alert.
|
||||||
|
ALERT_STOPPED_CONTAINERS="no"
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
PROMETHEUS_URL="http://127.0.0.1:9090"
|
||||||
|
|
||||||
|
# Optional bearer token if you ever place auth in front of Prometheus.
|
||||||
|
PROMETHEUS_TOKEN=""
|
||||||
|
|
||||||
|
# State when one or more scrape targets are DOWN.
|
||||||
|
TARGET_DOWN_STATE=2
|
||||||
Reference in New Issue
Block a user