feat: detailed control center status and history #2
1 changed files with 74 additions and 3 deletions
|
|
@ -33,15 +33,60 @@ def rate_map():
|
||||||
found[target] = value
|
found[target] = value
|
||||||
return found, rc
|
return found, rc
|
||||||
|
|
||||||
|
|
||||||
|
def tc_metrics():
|
||||||
|
rc, out, _ = run(["sudo", "tc", "-s", "class", "show", "dev", DEV])
|
||||||
|
result = {}
|
||||||
|
current = None
|
||||||
|
for line in out.splitlines():
|
||||||
|
m = re.search(r"^class htb (1:\w+).*?\brate\s+([0-9.]+)([KMG])bit", line, re.I)
|
||||||
|
if m:
|
||||||
|
current = next((key for key, value in CLASSES.items() if value == m.group(1)), m.group(1))
|
||||||
|
rate = float(m.group(2)) * {"K": .001, "M": 1, "G": 1000}[m.group(3).upper()]
|
||||||
|
result[current] = {"rate_mbit": rate, "bytes": 0, "packets": 0, "drops": 0}
|
||||||
|
continue
|
||||||
|
if current:
|
||||||
|
sent = re.search(r"Sent\s+(\d+)\s+bytes\s+(\d+)\s+pkt.*?dropped\s+(\d+)", line)
|
||||||
|
if sent:
|
||||||
|
result[current].update(bytes=int(sent.group(1)), packets=int(sent.group(2)), drops=int(sent.group(3)))
|
||||||
|
current = None
|
||||||
|
return result, rc
|
||||||
|
|
||||||
|
|
||||||
|
def host_metrics():
|
||||||
|
up = int(float(open("/proc/uptime").read().split()[0]))
|
||||||
|
load = [round(value, 2) for value in os.getloadavg()]
|
||||||
|
mem = {}
|
||||||
|
for line in open("/proc/meminfo"):
|
||||||
|
key, value = line.split(":", 1)
|
||||||
|
mem[key] = int(value.strip().split()[0])
|
||||||
|
disk = os.statvfs("/")
|
||||||
|
return {
|
||||||
|
"uptime_seconds": up,
|
||||||
|
"load": load,
|
||||||
|
"memory_total": mem.get("MemTotal", 0) * 1024,
|
||||||
|
"memory_used": (mem.get("MemTotal", 0) - mem.get("MemAvailable", 0)) * 1024,
|
||||||
|
"disk_total": disk.f_blocks * disk.f_frsize,
|
||||||
|
"disk_used": (disk.f_blocks - disk.f_bavail) * disk.f_frsize,
|
||||||
|
}
|
||||||
|
|
||||||
def state():
|
def state():
|
||||||
c = db()
|
c = db()
|
||||||
limits = {r["target"]: r["limit_mbit"] for r in c.execute("SELECT target,limit_mbit FROM global_limits")}
|
limits = {r["target"]: r["limit_mbit"] for r in c.execute("SELECT target,limit_mbit FROM global_limits")}
|
||||||
settings = {r["key"]: r["value"] for r in c.execute("SELECT key,value FROM settings")}
|
settings = {r["key"]: r["value"] for r in c.execute("SELECT key,value FROM settings")}
|
||||||
active_sessions = c.execute("SELECT count(*) FROM session_history WHERE active=1").fetchone()[0]
|
active_sessions = c.execute("SELECT count(*) FROM session_history WHERE active=1").fetchone()[0]
|
||||||
|
session_rows = [dict(r) for r in c.execute("SELECT user_name,device,client,ip,title,series,started_at,last_seen,country,city,asn,bytes FROM session_history WHERE active=1 ORDER BY last_seen DESC LIMIT 20")]
|
||||||
recent = [dict(r) for r in c.execute("SELECT ts,user_name,kind,detail FROM multisession_events ORDER BY id DESC LIMIT 8")]
|
recent = [dict(r) for r in c.execute("SELECT ts,user_name,kind,detail FROM multisession_events ORDER BY id DESC LIMIT 8")]
|
||||||
active_blocks = c.execute("SELECT count(*) FROM multisession_blocks WHERE active=1 AND expires_at>strftime('%s','now')").fetchone()[0]
|
active_blocks = c.execute("SELECT count(*) FROM multisession_blocks WHERE active=1 AND expires_at>strftime('%s','now')").fetchone()[0]
|
||||||
|
totals = {
|
||||||
|
"sessions": c.execute("SELECT count(*) FROM session_history").fetchone()[0],
|
||||||
|
"known_ips": c.execute("SELECT count(*) FROM ip_history").fetchone()[0],
|
||||||
|
"policy_events": c.execute("SELECT count(*) FROM multisession_events").fetchone()[0],
|
||||||
|
"blocks": c.execute("SELECT count(*) FROM multisession_blocks").fetchone()[0],
|
||||||
|
}
|
||||||
c.close()
|
c.close()
|
||||||
tc, tc_rc = rate_map()
|
tc_detail, tc_rc = tc_metrics()
|
||||||
|
tc = {key: value["rate_mbit"] for key, value in tc_detail.items()}
|
||||||
ipsets = {}
|
ipsets = {}
|
||||||
for name in ("geo-a-v4", "geo-a-v6", "cloud-v4", "cloud-v6", "persist-v4", "persist-v6"):
|
for name in ("geo-a-v4", "geo-a-v6", "cloud-v4", "cloud-v6", "persist-v4", "persist-v6"):
|
||||||
rc, out, _ = run(["sudo", "ipset", "list", name, "-t"])
|
rc, out, _ = run(["sudo", "ipset", "list", name, "-t"])
|
||||||
|
|
@ -54,8 +99,9 @@ def state():
|
||||||
rc, rules, _ = run(["sudo", "iptables", "-t", "mangle", "-S"])
|
rc, rules, _ = run(["sudo", "iptables", "-t", "mangle", "-S"])
|
||||||
return {
|
return {
|
||||||
"ok": tc_rc == 0 and all(v == "active" for k, v in services.items() if k != "netqos"),
|
"ok": tc_rc == 0 and all(v == "active" for k, v in services.items() if k != "netqos"),
|
||||||
"ts": int(time.time()), "limits": limits, "tc": tc, "ipsets": ipsets,
|
"ts": int(time.time()), "host": host_metrics(), "limits": limits, "tc": tc, "tc_detail": tc_detail, "ipsets": ipsets,
|
||||||
"services": services, "active_sessions": active_sessions, "active_blocks": active_blocks,
|
"services": services, "active_sessions": active_sessions, "sessions": session_rows,
|
||||||
|
"active_blocks": active_blocks, "totals": totals,
|
||||||
"policy": {
|
"policy": {
|
||||||
"enabled": settings.get("multisession_enabled", "0") == "1",
|
"enabled": settings.get("multisession_enabled", "0") == "1",
|
||||||
"mode": settings.get("multisession_mode", "monitor"),
|
"mode": settings.get("multisession_mode", "monitor"),
|
||||||
|
|
@ -67,6 +113,29 @@ def state():
|
||||||
"recent_events": recent,
|
"recent_events": recent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def history():
|
||||||
|
c = db()
|
||||||
|
data = {
|
||||||
|
"sessions": [dict(r) for r in c.execute(
|
||||||
|
"SELECT user_name,device,client,ip,title,series,started_at,ended_at,last_seen,bytes,country,city,asn,active "
|
||||||
|
"FROM session_history ORDER BY last_seen DESC LIMIT 150"
|
||||||
|
)],
|
||||||
|
"ips": [dict(r) for r in c.execute(
|
||||||
|
"SELECT user_name,ip,device,client,first_seen,last_seen,sightings,limited_sightings,last_classification,country,city,asn "
|
||||||
|
"FROM ip_history ORDER BY last_seen DESC LIMIT 150"
|
||||||
|
)],
|
||||||
|
"events": [dict(r) for r in c.execute(
|
||||||
|
"SELECT ts,user_name,kind,detail FROM multisession_events ORDER BY id DESC LIMIT 150"
|
||||||
|
)],
|
||||||
|
"blocks": [dict(r) for r in c.execute(
|
||||||
|
"SELECT ip,user_name,reason,blocked_at,expires_at,active,manual_allow_until FROM multisession_blocks ORDER BY blocked_at DESC LIMIT 150"
|
||||||
|
)],
|
||||||
|
}
|
||||||
|
c.close()
|
||||||
|
return {"ok": True, "history": data, "generated_at": int(time.time())}
|
||||||
|
|
||||||
|
|
||||||
def set_limits(ch, vpn):
|
def set_limits(ch, vpn):
|
||||||
values = {"ch": float(ch), "vpn": float(vpn)}
|
values = {"ch": float(ch), "vpn": float(vpn)}
|
||||||
if any(not 0.1 <= value <= 100 for value in values.values()):
|
if any(not 0.1 <= value <= 100 for value in values.values()):
|
||||||
|
|
@ -133,6 +202,8 @@ def main():
|
||||||
if not argv: argv = ["state"]
|
if not argv: argv = ["state"]
|
||||||
if argv == ["state"]:
|
if argv == ["state"]:
|
||||||
result = state()
|
result = state()
|
||||||
|
elif argv == ["history"]:
|
||||||
|
result = history()
|
||||||
elif len(argv) == 3 and argv[0] == "set-limits":
|
elif len(argv) == 3 and argv[0] == "set-limits":
|
||||||
result = set_limits(argv[1], argv[2])
|
result = set_limits(argv[1], argv[2])
|
||||||
elif argv[0] == "set-policy" and 2 <= len(argv) <= 4:
|
elif argv[0] == "set-policy" and 2 <= len(argv) <= 4:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue