added new scripts
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user