main.py aktualisiert

This commit is contained in:
2026-03-06 10:33:59 +00:00
parent 89d41e0343
commit 099609fa29

70
main.py
View File

@@ -15,6 +15,7 @@ from fastapi import FastAPI, WebSocket, BackgroundTasks, Request, Form, WebSocke
from fastapi.responses import RedirectResponse from fastapi.responses import RedirectResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from contextlib import asynccontextmanager
from dotenv import load_dotenv, set_key from dotenv import load_dotenv, set_key
# Lade Umgebungsvariablen aus der .env Datei # Lade Umgebungsvariablen aus der .env Datei
@@ -176,34 +177,47 @@ async def bootstrap_ssh_only(ip, user, password, sudo_pass=""):
await manager.broadcast(f"✅ Node {ip} verbunden und analysiert.") await manager.broadcast(f"✅ Node {ip} verbunden und analysiert.")
# --- AUTO-REFRESH (Alle 60 Sekunden) --- # --- AUTO-REFRESH (Alle 60 Sekunden) ---
@app.on_event("startup") @asynccontextmanager
async def start_auto_refresh(): async def lifespan(app: FastAPI):
asyncio.create_task(auto_refresh_loop()) # Alles hier drin läuft beim Starten
refresh_task = asyncio.create_task(auto_refresh_loop())
yield
# Alles hier drin läuft beim Beenden
refresh_task.cancel()
app = FastAPI(lifespan=lifespan)
async def auto_refresh_loop(): async def auto_refresh_loop():
print("🚀 Auto-Refresh Task gestartet...")
while True: while True:
await asyncio.sleep(60) # Alle 60 Sekunden try:
conn = get_db() conn = get_db()
nodes = conn.execute('SELECT id, ip FROM nodes').fetchall() nodes = conn.execute('SELECT id, ip FROM nodes').fetchall()
conn.close() conn.close()
for n in nodes: for n in nodes:
# 1. Schneller Ping-Check, um lange SSH-Timeouts bei Offline-Nodes zu vermeiden # Schneller Ping
proc = await asyncio.create_subprocess_exec( proc = await asyncio.create_subprocess_exec(
"ping", "-c", "1", "-W", "1", n['ip'], "ping", "-c", "1", "-W", "1", n['ip'],
stdout=asyncio.subprocess.DEVNULL, stderr=asyncio.subprocess.DEVNULL stdout=asyncio.subprocess.DEVNULL,
stderr=asyncio.subprocess.DEVNULL
) )
await proc.wait() await proc.wait()
if proc.returncode == 0: if proc.returncode == 0:
# Node ist online, führe SSH Info-Check durch
await check_and_update_node(n['id']) await check_and_update_node(n['id'])
else: else:
# Node ist offline
conn = get_db() conn = get_db()
conn.execute("UPDATE nodes SET status='Offline' WHERE id=?", (n['id'],)) conn.execute("UPDATE nodes SET status='Offline' WHERE id=?", (n['id'],))
conn.commit() conn.commit()
conn.close() conn.close()
print(f"⚠️ Auto-Refresh: Node {n['ip']} ist per Ping nicht erreichbar.")
except Exception as e:
print(f"🚨 Schwerer Fehler im Auto-Refresh Loop: {e}")
# Erst am Ende der Runde warten
await asyncio.sleep(60)
# Hilfsfunktion für den Info-Check via SSH # Hilfsfunktion für den Info-Check via SSH
async def check_and_update_node(node_id: int): async def check_and_update_node(node_id: int):
@@ -211,10 +225,8 @@ async def check_and_update_node(node_id: int):
node = conn.execute('SELECT * FROM nodes WHERE id = ?', (node_id,)).fetchone() node = conn.execute('SELECT * FROM nodes WHERE id = ?', (node_id,)).fetchone()
if not node: if not node:
conn.close() conn.close()
return "Node nicht gefunden" return
# Wir bauen den Befehl kompakt zusammen
# Nutze im Grep nun doppelte Anführungszeichen, um Konflikte zu vermeiden
check_cmd = ( check_cmd = (
'arch=$(uname -m); ' 'arch=$(uname -m); '
'dock=$(command -v docker >/dev/null 2>&1 && echo 1 || echo 0); ' 'dock=$(command -v docker >/dev/null 2>&1 && echo 1 || echo 0); '
@@ -224,39 +236,33 @@ async def check_and_update_node(node_id: int):
'echo "$arch|$dock|$os|$vnc"' 'echo "$arch|$dock|$os|$vnc"'
) )
# WICHTIG: Als Liste übergeben, damit kein f-String Quoting nötig ist!
ssh_args = [
"ssh",
"-o", "StrictHostKeyChecking=no",
"-o", "ConnectTimeout=3",
f"{node['user']}@{node['ip']}",
check_cmd
]
try: try:
# Kein shell=True mehr nötig # Hier nutzen wir jetzt den asynchronen Subprocess-Aufruf von asyncio
output = subprocess.check_output(ssh_args, stderr=subprocess.STDOUT).decode().strip() proc = await asyncio.create_subprocess_exec(
"ssh", "-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=3",
# Debugging: Falls doch was schief geht, sehen wir es im Log f"{node['user']}@{node['ip']}", check_cmd,
if "|" not in output: stdout=asyncio.subprocess.PIPE,
raise ValueError(f"Unerwarteter Output: {output}") stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await proc.communicate()
if proc.returncode == 0:
output = stdout.decode().strip()
arch, dock, os_name, vnc = output.split('|') arch, dock, os_name, vnc = output.split('|')
status = "Docker Aktiv" if dock == "1" else "Bereit (Kein Docker)" status = "Docker Aktiv" if dock == "1" else "Bereit (Kein Docker)"
conn.execute(''' conn.execute('''
UPDATE nodes UPDATE nodes SET status=?, arch=?, docker=?, os=?, vnc=? WHERE id=?
SET status=?, arch=?, docker=?, os=?, vnc=?
WHERE id=?
''', (status, arch, int(dock), os_name, int(vnc), node_id)) ''', (status, arch, int(dock), os_name, int(vnc), node_id))
conn.commit() conn.commit()
return status print(f"✅ Auto-Refresh: Node {node['ip']} ist online ({status})")
else:
raise Exception(stderr.decode())
except Exception as e: except Exception as e:
print(f"Fehler bei Node {node_id} ({node['ip']}): {e}") print(f"❌ Auto-Refresh Fehler bei {node['ip']}: {e}")
conn.execute("UPDATE nodes SET status='Offline/Fehler' WHERE id=?", (node_id,)) conn.execute("UPDATE nodes SET status='Offline/Fehler' WHERE id=?", (node_id,))
conn.commit() conn.commit()
return "Offline/Fehler"
finally: finally:
conn.close() conn.close()