Create swap_cleaner.sh
This commit is contained in:
86
swap_cleaner.sh
Normal file
86
swap_cleaner.sh
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/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/1237668309856686090/xqN9G2ZtCkNKuiivng2kEMXLUY7Zx3_RK2w1Y4TCgVgPcc4M5ZYN7QStwHzyp7dEIqrb"
|
||||
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
|
||||
Reference in New Issue
Block a user