V1.0 de GameurBot (Metrics surveillance des perfs)
This commit is contained in:
commit
cd99ed12eb
14 changed files with 1248 additions and 0 deletions
138
agent.py
Normal file
138
agent.py
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
"""
|
||||
agent.py — Moniteur système à déployer sur le serveur de jeux.
|
||||
Collecte CPU, RAM, swap + usage par service, et push vers le bot Discord.
|
||||
|
||||
Lancement : python agent.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
import aiohttp
|
||||
import psutil
|
||||
from datetime import datetime
|
||||
|
||||
# ─── Configuration ────────────────────────────────────────────────────────────
|
||||
|
||||
BOT_API_URL = "http://localhost:8765/metrics" # URL du bot (même machine ou réseau local)
|
||||
API_SECRET = "Mon-cul-est-tres-doux" # Doit correspondre à config.json
|
||||
PUSH_INTERVAL = 5 # secondes entre chaque envoi
|
||||
|
||||
# Mots-clés pour identifier chaque service dans la liste des processus
|
||||
# Clé = identifiant interne, valeurs = sous-chaînes à chercher dans le nom / cmdline
|
||||
SERVICE_KEYWORDS: dict[str, list[str]] = {
|
||||
"minecraft": ["minecraft", "paper", "spigot", "purpur", "fabric", "forge"],
|
||||
"hytale": ["hytale"],
|
||||
"satisfactory": ["factoryserver", "satisfactory"],
|
||||
"valheim": ["valheim_server", "valheim"],
|
||||
"terraria": ["terraria"],
|
||||
"ark": ["shootergame", "ark"],
|
||||
"ollama": ["ollama"],
|
||||
"discord_bots": ["discord_bot", "bot.py", "main.py"], # adapter si besoin
|
||||
}
|
||||
|
||||
# ─── Collecte ─────────────────────────────────────────────────────────────────
|
||||
|
||||
def scan_services() -> dict:
|
||||
"""Parcourt les processus et agrège l'usage par service."""
|
||||
buckets: dict[str, dict] = {k: {"running": False, "cpu": 0.0, "ram_mb": 0.0, "pids": []}
|
||||
for k in SERVICE_KEYWORDS}
|
||||
|
||||
for proc in psutil.process_iter(["pid", "name", "cmdline", "cpu_percent", "memory_info", "status"]):
|
||||
try:
|
||||
name = (proc.info["name"] or "").lower()
|
||||
cmdline = " ".join(proc.info["cmdline"] or []).lower()
|
||||
text = name + " " + cmdline
|
||||
|
||||
for svc, keywords in SERVICE_KEYWORDS.items():
|
||||
if any(kw in text for kw in keywords):
|
||||
b = buckets[svc]
|
||||
b["running"] = True
|
||||
b["cpu"] += proc.info["cpu_percent"] or 0.0
|
||||
b["ram_mb"] += (proc.info["memory_info"].rss / 1024 / 1024) if proc.info["memory_info"] else 0.0
|
||||
b["pids"].append(proc.info["pid"])
|
||||
break # un process ne compte que pour un service
|
||||
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||
continue
|
||||
|
||||
# Nettoyage : on retire les pids de la réponse (inutiles pour le bot)
|
||||
for svc in buckets:
|
||||
buckets[svc].pop("pids", None)
|
||||
|
||||
return buckets
|
||||
|
||||
|
||||
async def collect() -> dict:
|
||||
"""Construit le payload complet à envoyer au bot."""
|
||||
cpu = psutil.cpu_percent(interval=1) # bloquant 1s — thread OK en async
|
||||
mem = psutil.virtual_memory()
|
||||
swap = psutil.swap_memory()
|
||||
load = psutil.getloadavg() # (1m, 5m, 15m)
|
||||
temps = {}
|
||||
|
||||
try:
|
||||
sensor_data = psutil.sensors_temperatures()
|
||||
if sensor_data:
|
||||
# On prend la temp CPU principale si dispo
|
||||
for key in ("coretemp", "k10temp", "cpu_thermal"):
|
||||
if key in sensor_data:
|
||||
temps["cpu_c"] = round(sensor_data[key][0].current, 1)
|
||||
break
|
||||
except AttributeError:
|
||||
pass # Windows / pas de capteurs
|
||||
|
||||
services = scan_services()
|
||||
|
||||
return {
|
||||
"timestamp": time.time(),
|
||||
"api_key": API_SECRET,
|
||||
"system": {
|
||||
"cpu_percent": round(cpu, 1),
|
||||
"cpu_count": psutil.cpu_count(logical=True),
|
||||
"load_avg_1m": round(load[0], 2),
|
||||
"load_avg_5m": round(load[1], 2),
|
||||
"load_avg_15m": round(load[2], 2),
|
||||
"ram_total_gb": round(mem.total / 1024**3, 2),
|
||||
"ram_used_gb": round(mem.used / 1024**3, 2),
|
||||
"ram_percent": mem.percent,
|
||||
"swap_total_gb": round(swap.total / 1024**3, 2),
|
||||
"swap_used_gb": round(swap.used / 1024**3, 2),
|
||||
"swap_percent": swap.percent,
|
||||
**temps,
|
||||
},
|
||||
"services": services,
|
||||
}
|
||||
|
||||
# ─── Boucle principale ────────────────────────────────────────────────────────
|
||||
|
||||
async def run():
|
||||
# Premier appel cpu_percent sans intervalle pour initialiser le compteur
|
||||
psutil.cpu_percent(interval=None)
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
print(f"[Agent] Démarrage — push vers {BOT_API_URL} toutes les {PUSH_INTERVAL}s")
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
while True:
|
||||
try:
|
||||
payload = await collect()
|
||||
async with session.post(
|
||||
BOT_API_URL,
|
||||
json=payload,
|
||||
timeout=aiohttp.ClientTimeout(total=8)
|
||||
) as resp:
|
||||
status_str = "✓" if resp.status == 200 else f"✗ HTTP {resp.status}"
|
||||
ts = datetime.now().strftime("%H:%M:%S")
|
||||
print(f"[{ts}] CPU={payload['system']['cpu_percent']}% "
|
||||
f"RAM={payload['system']['ram_percent']}% → {status_str}")
|
||||
|
||||
except aiohttp.ClientConnectorError:
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] ✗ Impossible de joindre le bot — il est lancé ?")
|
||||
except Exception as exc:
|
||||
print(f"[{datetime.now().strftime('%H:%M:%S')}] ✗ Erreur inattendue : {exc}")
|
||||
|
||||
await asyncio.sleep(PUSH_INTERVAL)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run())
|
||||
Loading…
Add table
Add a link
Reference in a new issue