Files
swap_cleaner/swap_cleaner.sh
2025-11-03 16:44:49 +01:00

87 lines
3.3 KiB
Bash

#!/bin/bash
# swap_cleaner.sh — purge le swap si > 90% pendant 2h et envoie une notif Discord
# Exécuter en root (PVE), sans sudo.
set -euo pipefail
# --- Config -------------------------------------------------------------------
WEBHOOK="https://discord.com/api/webhooks/12345678900987865432/sahogfuhwsaoghowpishagnopwisghjpiwrjgwpiqjgknvdsoag
THRESHOLD=${THRESHOLD:-90} # % de swap utilisé (override possible via env)
CHECK_EVERY_MIN=${CHECK_EVERY_MIN:-10} # fréquence du cron en minutes
DURATION_MIN=${DURATION_MIN:-120} # durée continue requise au-dessus du seuil
STATE_FILE="/var/tmp/swap_usage_count.txt"
LOG="/var/log/swap_cleaner.log"
HOST="$(hostname)"
# --- Pré-requis ---------------------------------------------------------------
if ! command -v jq >/dev/null 2>&1; then
apt update -y && apt install -y jq
fi
mkdir -p "$(dirname "$STATE_FILE")"
touch "$STATE_FILE" "$LOG"
# --- Fonctions ----------------------------------------------------------------
limit_checks() {
python3 - "$CHECK_EVERY_MIN" "$DURATION_MIN" <<'PY'
import sys, math
every = int(sys.argv[1]); duration = int(sys.argv[2])
print(max(1, math.ceil(duration / every)))
PY
}
discord_notify() {
# $1 = message (multi-ligne), $2 = fichier joint (optionnel)
local msg="$1" file="${2:-}"
# marge stricte < 2000 caractères
if [ "${#msg}" -gt 1900 ]; then msg="${msg:0:1900}\n(truncated)"; fi
printf "%s" "$msg" | jq -Rs '{content: .}' > /tmp/payload.json
if [ -n "${file}" ] && [ -f "${file}" ]; then
curl -sS -f \
-F "payload_json=@/tmp/payload.json;type=application/json" \
-F "file=@${file};type=text/plain" \
"$WEBHOOK" >/dev/null || true
else
curl -sS -f -H "Content-Type: application/json" \
-d @/tmp/payload.json "$WEBHOOK" >/dev/null || true
fi
rm -f /tmp/payload.json
}
swap_usage_percent() {
local total used
read -r total used _ < <(free -m | awk '/^Swap:/ {print $2, $3, $4}')
if [ "${total:-0}" -gt 0 ]; then echo $(( used * 100 / total )); else echo 0; fi
}
log() { echo "$(date '+%F %T') $*" | tee -a "$LOG" >/dev/null; }
# --- Single instance (évite chevauchements) -----------------------------------
exec 9>/var/tmp/.swap_cleaner.lock
flock -n 9 || exit 0
# --- Logic --------------------------------------------------------------------
COUNT="$(cat "$STATE_FILE" 2>/dev/null || echo 0)"
USAGE="$(swap_usage_percent)"
LIMIT="$(limit_checks)"
if [ "$USAGE" -ge "$THRESHOLD" ]; then
COUNT=$((COUNT + 1)); echo "$COUNT" > "$STATE_FILE"
log "[INFO] Swap ${USAGE}% (>=${THRESHOLD}%). Compteur ${COUNT}/${LIMIT}."
if [ "$COUNT" -ge "$LIMIT" ]; then
log "[ACTION] ${DURATION_MIN} min >= ${THRESHOLD}% — swapoff -a && swapon -a"
BEFORE="$(free -h)"
if swapoff -a && swapon -a; then
AFTER="$(free -h)"; echo "0" > "$STATE_FILE"
MSG="🧹 **Swap cleaner (PVE: ${HOST})**\nSeuil: ${THRESHOLD}% maintenu ${DURATION_MIN} min.\nAction: \`swapoff -a && swapon -a\`\n\nAvant:\n${BEFORE}\n\nAprès:\n${AFTER}\n\nLog: ${LOG}"
discord_notify "$MSG" "$LOG"
else
log "[ERROR] Échec swapoff/swapon."
discord_notify "⚠️ **Swap cleaner (PVE: ${HOST})**\nÉchec de \`swapoff -a && swapon -a\` (swap=${USAGE}%)." "$LOG"
fi
fi
else
if [ "$COUNT" -ne 0 ]; then log "[INFO] Swap à ${USAGE}%, reset compteur."; fi
echo "0" > "$STATE_FILE"
fi