Heartbeat und Überwachung eingeführt
This commit is contained in:
22
.env.example
Normal file
22
.env.example
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# E-Mail Empfang (IMAP) - für Videos von der Kamera
|
||||||
|
IMAP_SERVER=secureimap.t-online.de
|
||||||
|
IMAP_PORT=993
|
||||||
|
EMAIL_USER=deine-email@t-online.de
|
||||||
|
EMAIL_PASS=dein-passwort
|
||||||
|
|
||||||
|
# Speicherort für Videos
|
||||||
|
SAVE_DIR=/app/videospeicher
|
||||||
|
|
||||||
|
# E-Mail Versand (SMTP) - für Monitoring-Benachrichtigungen
|
||||||
|
SMTP_SERVER=securesmtp.t-online.de
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_USER=deine-email@t-online.de
|
||||||
|
SMTP_PASS=dein-passwort
|
||||||
|
|
||||||
|
# Alert-E-Mail (an wen sollen Benachrichtigungen gehen?)
|
||||||
|
ALERT_EMAIL=deine-email@t-online.de
|
||||||
|
|
||||||
|
# Monitoring-Einstellungen (optional)
|
||||||
|
# CHECK_INTERVAL=600 # Monitoring-Prüfung alle 10 Minuten
|
||||||
|
# MAX_AGE=900 # Alarm wenn kein Heartbeat seit 15 Minuten
|
||||||
|
# ALERT_COOLDOWN=3600 # Mindestens 1 Stunde zwischen Alarm-E-Mails
|
||||||
@@ -10,6 +10,7 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
|
|
||||||
# Kopiere den Anwendungscode
|
# Kopiere den Anwendungscode
|
||||||
COPY main.py .
|
COPY main.py .
|
||||||
|
COPY monitor.py .
|
||||||
|
|
||||||
# Erstelle Verzeichnis für Videospeicher
|
# Erstelle Verzeichnis für Videospeicher
|
||||||
RUN mkdir -p /app/videospeicher
|
RUN mkdir -p /app/videospeicher
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ./videospeicher:/app/videospeicher
|
- ./videospeicher:/app/videospeicher
|
||||||
|
- heartbeat:/app
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
@@ -14,8 +15,32 @@ services:
|
|||||||
- EMAIL_USER=${EMAIL_USER}
|
- EMAIL_USER=${EMAIL_USER}
|
||||||
- EMAIL_PASS=${EMAIL_PASS}
|
- EMAIL_PASS=${EMAIL_PASS}
|
||||||
- SAVE_DIR=${SAVE_DIR}
|
- SAVE_DIR=${SAVE_DIR}
|
||||||
|
- HEARTBEAT_FILE=/app/heartbeat.txt
|
||||||
# Führe das Skript alle 5 Minuten aus
|
# Führe das Skript alle 5 Minuten aus
|
||||||
command: >
|
command: >
|
||||||
sh -c "while true; do
|
sh -c "while true; do
|
||||||
python main.py && echo \"CameraSave ausgeführt um \$$(date)\" && sleep 300;
|
python main.py && echo \"CameraSave ausgeführt um \$$(date)\" && sleep 300;
|
||||||
done"
|
done"
|
||||||
|
|
||||||
|
monitor:
|
||||||
|
build: .
|
||||||
|
container_name: camerasave-monitor
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- heartbeat:/app
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- HEARTBEAT_FILE=/app/heartbeat.txt
|
||||||
|
- CHECK_INTERVAL=600 # Prüfung alle 10 Minuten
|
||||||
|
- MAX_AGE=900 # Alarm nach 15 Minuten ohne Heartbeat
|
||||||
|
- ALERT_COOLDOWN=3600 # 1 Stunde zwischen Alarm-E-Mails
|
||||||
|
- SMTP_SERVER=${SMTP_SERVER:-securesmtp.t-online.de}
|
||||||
|
- SMTP_PORT=${SMTP_PORT:-587}
|
||||||
|
- SMTP_USER=${SMTP_USER:-${EMAIL_USER}}
|
||||||
|
- SMTP_PASS=${SMTP_PASS:-${EMAIL_PASS}}
|
||||||
|
- ALERT_EMAIL=${ALERT_EMAIL:-${EMAIL_USER}}
|
||||||
|
command: python monitor.py
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
heartbeat:
|
||||||
13
main.py
13
main.py
@@ -10,6 +10,17 @@ IMAP_PORT = int(os.getenv("IMAP_PORT", "993"))
|
|||||||
EMAIL_USER = os.getenv("EMAIL_USER", "dk2ge@t-online.de")
|
EMAIL_USER = os.getenv("EMAIL_USER", "dk2ge@t-online.de")
|
||||||
EMAIL_PASS = os.getenv("EMAIL_PASS", "ETBjw65tf2")
|
EMAIL_PASS = os.getenv("EMAIL_PASS", "ETBjw65tf2")
|
||||||
SAVE_DIR = os.getenv("SAVE_DIR", "./videospeicher")
|
SAVE_DIR = os.getenv("SAVE_DIR", "./videospeicher")
|
||||||
|
HEARTBEAT_FILE = os.getenv("HEARTBEAT_FILE", "/app/heartbeat.txt")
|
||||||
|
|
||||||
|
# === HEARTBEAT ===
|
||||||
|
def update_heartbeat():
|
||||||
|
"""Schreibt einen Heartbeat für das Monitoring."""
|
||||||
|
try:
|
||||||
|
with open(HEARTBEAT_FILE, "w") as f:
|
||||||
|
f.write(datetime.now().isoformat())
|
||||||
|
print(f"💓 Heartbeat aktualisiert: {HEARTBEAT_FILE}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Fehler beim Heartbeat-Update: {e}")
|
||||||
|
|
||||||
# === ALTE DATEIEN LÖSCHEN (älter als 1 Jahr) ===
|
# === ALTE DATEIEN LÖSCHEN (älter als 1 Jahr) ===
|
||||||
def cleanup_old_files(base_dir, days=365):
|
def cleanup_old_files(base_dir, days=365):
|
||||||
@@ -84,5 +95,7 @@ def process_mails():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
update_heartbeat() # Heartbeat zu Beginn
|
||||||
cleanup_old_files(SAVE_DIR, days=365)
|
cleanup_old_files(SAVE_DIR, days=365)
|
||||||
process_mails()
|
process_mails()
|
||||||
|
update_heartbeat() # Heartbeat nach erfolgreicher Ausführung
|
||||||
|
|||||||
142
monitor.py
Normal file
142
monitor.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Monitoring-Script für CameraSave
|
||||||
|
Prüft, ob das Programm regelmäßig läuft und sendet E-Mail-Benachrichtigungen bei Problemen.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import smtplib
|
||||||
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
|
||||||
|
# === KONFIGURATION ===
|
||||||
|
HEARTBEAT_FILE = os.getenv("HEARTBEAT_FILE", "/app/heartbeat.txt")
|
||||||
|
CHECK_INTERVAL = int(os.getenv("CHECK_INTERVAL", "600")) # 10 Minuten
|
||||||
|
MAX_AGE = int(os.getenv("MAX_AGE", "900")) # 15 Minuten - danach Alarm
|
||||||
|
|
||||||
|
# E-Mail Konfiguration für Benachrichtigungen
|
||||||
|
SMTP_SERVER = os.getenv("SMTP_SERVER", "securesmtp.t-online.de")
|
||||||
|
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
|
||||||
|
SMTP_USER = os.getenv("SMTP_USER", os.getenv("EMAIL_USER", ""))
|
||||||
|
SMTP_PASS = os.getenv("SMTP_PASS", os.getenv("EMAIL_PASS", ""))
|
||||||
|
ALERT_EMAIL = os.getenv("ALERT_EMAIL", SMTP_USER) # Standard: an sich selbst
|
||||||
|
|
||||||
|
last_alert_time = None
|
||||||
|
ALERT_COOLDOWN = int(os.getenv("ALERT_COOLDOWN", "3600")) # 1 Stunde zwischen Alarmen
|
||||||
|
|
||||||
|
|
||||||
|
def send_alert_email(subject, message):
|
||||||
|
"""Sendet eine E-Mail-Benachrichtigung."""
|
||||||
|
global last_alert_time
|
||||||
|
|
||||||
|
# Cooldown prüfen - nicht zu oft E-Mails senden
|
||||||
|
if last_alert_time:
|
||||||
|
time_since_last = (datetime.now() - last_alert_time).total_seconds()
|
||||||
|
if time_since_last < ALERT_COOLDOWN:
|
||||||
|
print(f"⏳ Cooldown aktiv. Nächste E-Mail möglich in {int(ALERT_COOLDOWN - time_since_last)}s")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
msg = MIMEMultipart()
|
||||||
|
msg['From'] = SMTP_USER
|
||||||
|
msg['To'] = ALERT_EMAIL
|
||||||
|
msg['Subject'] = subject
|
||||||
|
|
||||||
|
body = f"""
|
||||||
|
CameraSave Monitoring Alert
|
||||||
|
==========================
|
||||||
|
|
||||||
|
{message}
|
||||||
|
|
||||||
|
Zeit: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||||
|
Server: {os.uname().nodename if hasattr(os, 'uname') else 'Unknown'}
|
||||||
|
|
||||||
|
Diese Nachricht wurde automatisch vom Monitoring-System generiert.
|
||||||
|
"""
|
||||||
|
msg.attach(MIMEText(body, 'plain'))
|
||||||
|
|
||||||
|
server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
|
||||||
|
server.starttls()
|
||||||
|
server.login(SMTP_USER, SMTP_PASS)
|
||||||
|
server.send_message(msg)
|
||||||
|
server.quit()
|
||||||
|
|
||||||
|
last_alert_time = datetime.now()
|
||||||
|
print(f"✅ Alert-E-Mail gesendet an {ALERT_EMAIL}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Fehler beim E-Mail-Versand: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def check_heartbeat():
|
||||||
|
"""Prüft, ob die Heartbeat-Datei aktuell ist."""
|
||||||
|
if not os.path.exists(HEARTBEAT_FILE):
|
||||||
|
return False, "Heartbeat-Datei existiert nicht"
|
||||||
|
|
||||||
|
try:
|
||||||
|
mtime = os.path.getmtime(HEARTBEAT_FILE)
|
||||||
|
age = time.time() - mtime
|
||||||
|
|
||||||
|
if age > MAX_AGE:
|
||||||
|
return False, f"Heartbeat zu alt: {int(age)}s (max {MAX_AGE}s)"
|
||||||
|
|
||||||
|
return True, f"OK - Heartbeat vor {int(age)}s"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return False, f"Fehler beim Lesen der Heartbeat-Datei: {e}"
|
||||||
|
|
||||||
|
|
||||||
|
def send_recovery_email():
|
||||||
|
"""Sendet eine E-Mail, wenn das System sich erholt hat."""
|
||||||
|
send_alert_email(
|
||||||
|
"✅ CameraSave: System wiederhergestellt",
|
||||||
|
"Das CameraSave-Programm läuft wieder normal."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Hauptschleife des Monitoring."""
|
||||||
|
print("🔍 CameraSave Monitor gestartet")
|
||||||
|
print(f" Heartbeat-Datei: {HEARTBEAT_FILE}")
|
||||||
|
print(f" Prüfintervall: {CHECK_INTERVAL}s")
|
||||||
|
print(f" Max Alter: {MAX_AGE}s")
|
||||||
|
print(f" Alert E-Mail: {ALERT_EMAIL}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
was_down = False
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
is_healthy, status = check_heartbeat()
|
||||||
|
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
if is_healthy:
|
||||||
|
print(f"[{timestamp}] ✅ {status}")
|
||||||
|
if was_down:
|
||||||
|
# System ist wieder online
|
||||||
|
send_recovery_email()
|
||||||
|
was_down = False
|
||||||
|
else:
|
||||||
|
print(f"[{timestamp}] ❌ {status}")
|
||||||
|
if not was_down:
|
||||||
|
# Erster Fehler - Alarm senden
|
||||||
|
send_alert_email(
|
||||||
|
"⚠️ CameraSave: Programm läuft nicht mehr!",
|
||||||
|
f"Das CameraSave-Programm antwortet nicht mehr.\n\nStatus: {status}"
|
||||||
|
)
|
||||||
|
was_down = True
|
||||||
|
|
||||||
|
time.sleep(CHECK_INTERVAL)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n🛑 Monitor gestoppt")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Unerwarteter Fehler: {e}")
|
||||||
|
time.sleep(CHECK_INTERVAL)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user