init
This commit is contained in:
parent
32864d5687
commit
e148b2fa21
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM python:3.10-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY watcher.py .
|
||||
COPY config.yaml .
|
||||
|
||||
CMD ["python", "-u", "watcher.py"]
|
||||
21
config.yaml
Normal file
21
config.yaml
Normal file
@ -0,0 +1,21 @@
|
||||
folders:
|
||||
/mnt/tenants/ferrari: 20GB
|
||||
/mnt/tenants/discovery: 20GB
|
||||
/mnt/tenants/marrocco: 20GB
|
||||
/mnt/tenants/unilab: 20GB
|
||||
/mnt/tenants/lbdue: 20GB
|
||||
|
||||
email:
|
||||
smtp_server: relay.poloinformatico.it
|
||||
smtp_port: 587
|
||||
username: brass@relay.poloinformatico.it
|
||||
password: DMKqP9vUYn8s
|
||||
to: paciotti@poloinformatico.it
|
||||
from: brass@poloinformatico.it
|
||||
|
||||
webui:
|
||||
enabled: true
|
||||
port: 8080
|
||||
|
||||
schedule:
|
||||
seconds: 300
|
||||
9
docker-compose.yml
Normal file
9
docker-compose.yml
Normal file
@ -0,0 +1,9 @@
|
||||
services:
|
||||
folder-watcher:
|
||||
build: .
|
||||
ports:
|
||||
- "48080:8080"
|
||||
volumes:
|
||||
- ./config.yaml:/app/config.yml
|
||||
- /zucchetti/infinity:/mnt/tenants:ro
|
||||
restart: unless-stopped
|
||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
flask
|
||||
psutil
|
||||
pyyaml
|
||||
119
watcher.py
Normal file
119
watcher.py
Normal file
@ -0,0 +1,119 @@
|
||||
import os
|
||||
import smtplib
|
||||
import yaml
|
||||
import psutil
|
||||
import threading
|
||||
import time
|
||||
from flask import Flask, render_template_string
|
||||
from email.mime.text import MIMEText
|
||||
import subprocess
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Load configuration
|
||||
try:
|
||||
with open("config.yaml") as f:
|
||||
config = yaml.safe_load(f)
|
||||
print("[DEBUG] Loaded config.yaml successfully.")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to load config.yaml: {e}")
|
||||
exit(1)
|
||||
|
||||
results = {}
|
||||
|
||||
def get_folder_size(path):
|
||||
print(f"[DEBUG] Calculating size for: {path}")
|
||||
try:
|
||||
out = subprocess.check_output(["du", "-sb", path], timeout=60)
|
||||
size_bytes = int(out.decode().split()[0])
|
||||
print(f"[DEBUG] Total size for {path}: {size_bytes / 1e9:.2f} GB")
|
||||
return size_bytes
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to get size for {path}: {e}")
|
||||
return 0
|
||||
|
||||
def send_alert(folder, used, limit):
|
||||
print(f"[ALERT] Sending email for {folder}: used={used}, limit={limit}")
|
||||
try:
|
||||
msg = MIMEText(f"Folder {folder} is over limit: {used / 1e9:.2f} GB used of {limit / 1e9:.2f} GB")
|
||||
msg['Subject'] = f"[ALERT] Disk usage for {folder}"
|
||||
msg['From'] = config['email']['from']
|
||||
msg['To'] = config['email']['to']
|
||||
|
||||
with smtplib.SMTP(config['email']['smtp_server'], config['email']['smtp_port']) as s:
|
||||
s.starttls()
|
||||
s.login(config['email']['username'], config['email']['password'])
|
||||
s.send_message(msg)
|
||||
print(f"[INFO] Email alert sent for {folder}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to send email alert: {e}")
|
||||
|
||||
def check_folders():
|
||||
print("[INFO] Running folder usage check...")
|
||||
new_results = {}
|
||||
for folder, limit_str in config['folders'].items():
|
||||
if not os.path.exists(folder):
|
||||
print(f"[WARN] Folder does not exist: {folder}")
|
||||
continue
|
||||
try:
|
||||
limit = int(limit_str.replace("GB", "").strip()) * 1024 ** 3
|
||||
used = get_folder_size(folder)
|
||||
exceeded = used > limit
|
||||
new_results[folder] = {
|
||||
"used_bytes": used,
|
||||
"limit_bytes": limit,
|
||||
"used_gb": used / (1024 ** 3),
|
||||
"limit_gb": limit / (1024 ** 3),
|
||||
"exceeded": exceeded
|
||||
}
|
||||
if exceeded:
|
||||
send_alert(folder, used, limit)
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to check folder {folder}: {e}")
|
||||
results.clear()
|
||||
results.update(new_results)
|
||||
|
||||
def schedule_check(interval_sec):
|
||||
def loop():
|
||||
while True:
|
||||
check_folders()
|
||||
time.sleep(interval_sec)
|
||||
t = threading.Thread(target=loop, daemon=True)
|
||||
t.start()
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
html = """
|
||||
<html>
|
||||
<head><title>Tenant Disk Usage</title></head>
|
||||
<body>
|
||||
<h2>Tenant Folder Disk Usage (GB)</h2>
|
||||
<table border="1" cellpadding="6" cellspacing="0">
|
||||
<tr><th>Tenant</th><th>Used (GB)</th><th>Limit (GB)</th><th>Status</th></tr>
|
||||
{% for folder, data in results.items() %}
|
||||
<tr>
|
||||
<td>{{ folder }}</td>
|
||||
<td>{{ "%.2f"|format(data.used_gb) }}</td>
|
||||
<td>{{ "%.2f"|format(data.limit_gb) }}</td>
|
||||
<td style="color: {{ 'red' if data.exceeded else 'green' }}">{{ 'Exceeded' if data.exceeded else 'OK' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<p>Last updated: {{ now }}</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
import datetime
|
||||
return render_template_string(html, results=results, now=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
|
||||
|
||||
if __name__ == "__main__":
|
||||
interval = config.get("schedule", {}).get("seconds", 300) # default 5 minutes
|
||||
print(f"[INFO] Starting scheduled checks every {interval} seconds.")
|
||||
schedule_check(interval)
|
||||
|
||||
if config.get("webui", {}).get("enabled", False):
|
||||
port = config["webui"].get("port", 8080)
|
||||
print(f"[INFO] Starting Flask Web UI on port {port}")
|
||||
app.run(host="0.0.0.0", port=port)
|
||||
else:
|
||||
print("[INFO] Web UI disabled or missing from config.")
|
||||
Loading…
Reference in New Issue
Block a user