billing frontend for mariadb. setup as otb_billing for outsidethebox.top accounting. also involved with outsidethedb
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

137 lines
4.0 KiB

import os
import time
import shutil
import platform
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
from flask import render_template, jsonify
APP_START_TS = time.time()
def _read_meminfo():
data = {}
try:
with open("/proc/meminfo", "r", encoding="utf-8") as f:
for line in f:
if ":" not in line:
continue
key, val = line.split(":", 1)
data[key.strip()] = val.strip()
except Exception:
pass
return data
def _kb_to_mb(kb_value):
try:
return round(int(kb_value) / 1024, 2)
except Exception:
return None
def _server_uptime_seconds():
try:
with open("/proc/uptime", "r", encoding="utf-8") as f:
return int(float(f.read().split()[0]))
except Exception:
return None
def _format_duration(seconds):
if seconds is None:
return None
seconds = int(seconds)
days, rem = divmod(seconds, 86400)
hours, rem = divmod(rem, 3600)
minutes, secs = divmod(rem, 60)
return f"{days}d {hours}h {minutes}m {secs}s"
def _health_payload(app):
now_utc = datetime.now(timezone.utc)
now_toronto = now_utc.astimezone(ZoneInfo("America/Toronto"))
load1 = load5 = load15 = None
try:
load1, load5, load15 = os.getloadavg()
except Exception:
pass
meminfo = _read_meminfo()
mem_total_kb = None
mem_available_kb = None
mem_used_kb = None
mem_used_percent = None
try:
mem_total_kb = int(meminfo.get("MemTotal", "0 kB").split()[0])
mem_available_kb = int(meminfo.get("MemAvailable", "0 kB").split()[0])
mem_used_kb = mem_total_kb - mem_available_kb
if mem_total_kb > 0:
mem_used_percent = round((mem_used_kb / mem_total_kb) * 100, 2)
except Exception:
pass
disk = shutil.disk_usage("/")
db_ok = False
db_error = None
try:
connector = app.config.get("OTB_HEALTH_DB_CONNECTOR")
if callable(connector):
conn = connector()
cur = conn.cursor()
cur.execute("SELECT 1")
cur.fetchone()
cur.close()
conn.close()
db_ok = True
else:
db_error = "DB connector not registered"
except Exception as e:
db_error = str(e)
app_uptime = int(time.time() - APP_START_TS)
server_uptime = _server_uptime_seconds()
payload = {
"status": "ok" if db_ok else "degraded",
"app_name": "otb_billing",
"hostname": platform.node(),
"server_time_utc": now_utc.isoformat(),
"server_time_toronto": now_toronto.isoformat(),
"app_uptime_seconds": app_uptime,
"app_uptime_human": _format_duration(app_uptime),
"server_uptime_seconds": server_uptime,
"server_uptime_human": _format_duration(server_uptime),
"load_average": {"1m": load1, "5m": load5, "15m": load15},
"memory": {
"total_mb": _kb_to_mb(mem_total_kb) if mem_total_kb is not None else None,
"available_mb": _kb_to_mb(mem_available_kb) if mem_available_kb is not None else None,
"used_mb": _kb_to_mb(mem_used_kb) if mem_used_kb is not None else None,
"used_percent": mem_used_percent,
},
"disk_root": {
"total_gb": round(disk.total / (1024**3), 2),
"used_gb": round(disk.used / (1024**3), 2),
"free_gb": round(disk.free / (1024**3), 2),
"used_percent": round((disk.used / disk.total) * 100, 2) if disk.total else None,
},
"database": {"ok": db_ok, "error": db_error},
}
http_code = 200 if db_ok else 503
return payload, http_code
def register_health_routes(app):
@app.route("/health", methods=["GET"])
def health_page():
health, http_code = _health_payload(app)
return render_template("health.html", health=health), http_code
@app.route("/health.json", methods=["GET"])
def health_json():
health, http_code = _health_payload(app)
return jsonify(health), http_code