Files

95 lines
3.4 KiB
Python
Raw Permalink Normal View History

"""Мониторинг: сбор метрик по SSH, снапшоты в БД."""
from __future__ import annotations
from typing import Optional
from sqlmodel import select
from app.core.ssh import SSHClient
from app.db.database import get_session
from app.db.models import MetricSnapshot
# Каждая команда выводит одну строку "KEY value..."; парсер ниже.
# CPU считаем по дельте двух замеров /proc/stat за 1 секунду.
_METRICS_CMD = (
# CPU: первая строка cpu в /proc/stat (user nice system idle iowait irq softirq steal)
"read cpu u n s idle iow irq sirq st rest < /proc/stat; "
"t1=$((u+n+s+idle+iow+irq+sirq+st)); i1=$((idle+iow)); "
"sleep 1; "
"read cpu u n s idle iow irq sirq st rest < /proc/stat; "
"t2=$((u+n+s+idle+iow+irq+sirq+st)); i2=$((idle+iow)); "
"dt=$((t2-t1)); di=$((i2-i1)); "
"if [ $dt -gt 0 ]; then awk -v a=$dt -v b=$di 'BEGIN{printf \"CPU %.1f\\n\", (a-b)*100/a}'; else echo 'CPU 0.0'; fi; "
# RAM: used/total в МБ
"free -m | awk 'NR==2{printf \"MEM %d %d\\n\", $3, $2}'; "
# Диск: процент занятого на /
"df -m / | awk 'NR==2{printf \"DISK %.1f\\n\", $3*100/$2}'; "
# Load average 1min
"awk '{printf \"LOAD %s\\n\", $1}' /proc/loadavg; "
# Сеть: суммарные байты rx/tx всех интерфейсов
"awk '/: /{rx+=$2; tx+=$10} END{printf \"NET %d %d\\n\", rx, tx}' /proc/net/dev"
)
async def collect(c: SSHClient, server_id: int, prev_net: Optional[tuple] = None) -> MetricSnapshot:
r = await c.run(_METRICS_CMD, timeout=30)
cpu = mem_used = mem_total = disk = load = 0.0
rx = tx = 0
for line in r.stdout.splitlines():
p = line.split()
if len(p) < 2:
continue
if p[0] == "CPU":
cpu = float(p[1])
elif p[0] == "MEM":
mem_used, mem_total = int(p[1]), int(p[2])
elif p[0] == "DISK":
disk = float(p[1])
elif p[0] == "LOAD":
load = float(p[1])
elif p[0] == "NET":
rx, tx = int(p[1]), int(p[2])
rx_kbs = tx_kbs = 0.0
if prev_net:
dt = 60.0 # интервал опроса
rx_kbs = max(0.0, (rx - prev_net[0]) / 1024 / dt)
tx_kbs = max(0.0, (tx - prev_net[1]) / 1024 / dt)
snap = MetricSnapshot(
server_id=server_id,
cpu_percent=round(cpu, 1),
mem_percent=round(mem_used * 100 / mem_total, 1) if mem_total else 0,
mem_used_mb=mem_used,
mem_total_mb=mem_total,
disk_percent=disk,
load1=load,
net_rx_kbs=round(rx_kbs, 1),
net_tx_kbs=round(tx_kbs, 1),
)
with get_session() as s:
s.add(snap)
s.commit()
return snap
def latest(server_id: int) -> Optional[MetricSnapshot]:
with get_session() as s:
return s.exec(
select(MetricSnapshot)
.where(MetricSnapshot.server_id == server_id)
.order_by(MetricSnapshot.id.desc())
.limit(1)
).first()
def history(server_id: int, limit: int = 60) -> list[MetricSnapshot]:
with get_session() as s:
rows = s.exec(
select(MetricSnapshot)
.where(MetricSnapshot.server_id == server_id)
.order_by(MetricSnapshot.id.desc())
.limit(limit)
).all()
return list(reversed(rows))