main.py aktualisiert

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

110
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: conn = get_db()
# Node ist offline conn.execute("UPDATE nodes SET status='Offline' WHERE id=?", (n['id'],))
conn = get_db() conn.commit()
conn.execute("UPDATE nodes SET status='Offline' WHERE id=?", (n['id'],)) conn.close()
conn.commit() print(f"⚠️ Auto-Refresh: Node {n['ip']} ist per Ping nicht erreichbar.")
conn.close()
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",
f"{node['user']}@{node['ip']}", check_cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await proc.communicate()
# Debugging: Falls doch was schief geht, sehen wir es im Log if proc.returncode == 0:
if "|" not in output: output = stdout.decode().strip()
raise ValueError(f"Unerwarteter Output: {output}") arch, dock, os_name, vnc = output.split('|')
status = "Docker Aktiv" if dock == "1" else "Bereit (Kein Docker)"
arch, dock, os_name, vnc = output.split('|') conn.execute('''
status = "Docker Aktiv" if dock == "1" else "Bereit (Kein Docker)" UPDATE nodes SET status=?, arch=?, docker=?, os=?, vnc=? WHERE id=?
''', (status, arch, int(dock), os_name, int(vnc), node_id))
conn.execute(''' conn.commit()
UPDATE nodes print(f"✅ Auto-Refresh: Node {node['ip']} ist online ({status})")
SET status=?, arch=?, docker=?, os=?, vnc=? else:
WHERE id=? raise Exception(stderr.decode())
''', (status, arch, int(dock), os_name, int(vnc), node_id))
conn.commit()
return status
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()