diff --git a/CHANGELOG.md b/CHANGELOG.md index fff6e88..51ba3d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ This project does not follow semantic versioning (it is a script collection, not ## [Unreleased] +### Changed + +- `monitor-cpu-load.sh`: the `[RESOLVED]` email now reports when the incident was alerted and resolved, the peak load and its time, and the cause, CPU per subscription, offenders and top processes from the peak. The peak snapshot is kept in `STATE_DIR/incident_peak` while an incident is open. + ## v2026.10.10 ### Fixed diff --git a/monitor-cpu-load/README.md b/monitor-cpu-load/README.md index 3d36aea..f0edc50 100644 --- a/monitor-cpu-load/README.md +++ b/monitor-cpu-load/README.md @@ -41,7 +41,8 @@ Linux only (`monitor-cpu-load.sh`), AlmaLinux Plesk servers, root required. No W - `[ALERT]`: sustained load that fail2ban is not handling; the email lists CPU per subscription, the busiest offenders (with the jails holding them) and the likely cause - `[NOTICE]`: fail2ban is banning the attack but load stayed high for another full window -- `[REMINDER]` / `[RESOLVED]`: the incident is still ongoing (every `REMINDER_HOURS`), or load is back to normal +- `[REMINDER]`: the incident is still ongoing (every `REMINDER_HOURS`); it repeats the full analysis +- `[RESOLVED]`: load is back to normal; the email gives when it alerted and resolved, the peak load and its time, and the cause, CPU per subscription, offenders and processes captured at the peak ## Configuration diff --git a/monitor-cpu-load/monitor-cpu-load.sh b/monitor-cpu-load/monitor-cpu-load.sh index 3cb294c..1d46a52 100755 --- a/monitor-cpu-load/monitor-cpu-load.sh +++ b/monitor-cpu-load/monitor-cpu-load.sh @@ -295,6 +295,7 @@ SMTP_FROM="${SMTP_FROM:-}" SAMPLES_FILE="${STATE_DIR}/samples" STATE_FILE="${STATE_DIR}/state" +PEAK_DIR="${STATE_DIR}/incident_peak" PIDFILE="${STATE_DIR}/monitor.pid" HOSTNAME_SHORT="$(hostname -s 2>/dev/null || hostname)" WORK="" @@ -409,6 +410,8 @@ load_state() { INCIDENT_START=0 LAST_ALERT_TS=0 LAST_REMINDER_TS=0 + INCIDENT_PEAK_LOAD=0 + INCIDENT_PEAK_TS=0 ALERT_SENT="false" [ -f "${STATE_FILE}" ] || return 0 @@ -425,11 +428,16 @@ load_state() { ALERT_SENT="true" fi ;; - MITIGATED_SINCE|INCIDENT_START|LAST_ALERT_TS|LAST_REMINDER_TS) + MITIGATED_SINCE|INCIDENT_START|LAST_ALERT_TS|LAST_REMINDER_TS|INCIDENT_PEAK_TS) if [[ "${value}" =~ ^[0-9]+$ ]]; then printf -v "${key}" '%s' "${value}" fi ;; + INCIDENT_PEAK_LOAD) + if [[ "${value}" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then + INCIDENT_PEAK_LOAD="${value}" + fi + ;; esac done < "${STATE_FILE}" return 0 @@ -442,6 +450,8 @@ save_state() { echo "INCIDENT_START=${INCIDENT_START}" echo "LAST_ALERT_TS=${LAST_ALERT_TS}" echo "LAST_REMINDER_TS=${LAST_REMINDER_TS}" + echo "INCIDENT_PEAK_LOAD=${INCIDENT_PEAK_LOAD}" + echo "INCIDENT_PEAK_TS=${INCIDENT_PEAK_TS}" echo "ALERT_SENT=${ALERT_SENT}" } > "${STATE_FILE}.tmp" mv -f "${STATE_FILE}.tmp" "${STATE_FILE}" @@ -635,23 +645,71 @@ build_html_report() { printf '
fail2ban: banned offenders account for %s%% of attack traffic (%s of %s requests); handling threshold %s%%.
\n' \ "${HANDLED_PCT}" "${BANNED_TOTAL}" "${ATTACK_TOTAL}" "${HANDLED_PERCENT}" + print_analysis_tables "${WORK}" + printf '