main.py aktualisiert
This commit is contained in:
110
main.py
110
main.py
@@ -15,6 +15,7 @@ from fastapi import FastAPI, WebSocket, BackgroundTasks, Request, Form, WebSocke
|
||||
from fastapi.responses import RedirectResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from contextlib import asynccontextmanager
|
||||
from dotenv import load_dotenv, set_key
|
||||
|
||||
# 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.")
|
||||
|
||||
# --- AUTO-REFRESH (Alle 60 Sekunden) ---
|
||||
@app.on_event("startup")
|
||||
async def start_auto_refresh():
|
||||
asyncio.create_task(auto_refresh_loop())
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# 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():
|
||||
print("🚀 Auto-Refresh Task gestartet...")
|
||||
while True:
|
||||
await asyncio.sleep(60) # Alle 60 Sekunden
|
||||
conn = get_db()
|
||||
nodes = conn.execute('SELECT id, ip FROM nodes').fetchall()
|
||||
conn.close()
|
||||
try:
|
||||
conn = get_db()
|
||||
nodes = conn.execute('SELECT id, ip FROM nodes').fetchall()
|
||||
conn.close()
|
||||
|
||||
for n in nodes:
|
||||
# 1. Schneller Ping-Check, um lange SSH-Timeouts bei Offline-Nodes zu vermeiden
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
"ping", "-c", "1", "-W", "1", n['ip'],
|
||||
stdout=asyncio.subprocess.DEVNULL, stderr=asyncio.subprocess.DEVNULL
|
||||
)
|
||||
await proc.wait()
|
||||
for n in nodes:
|
||||
# Schneller Ping
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
"ping", "-c", "1", "-W", "1", n['ip'],
|
||||
stdout=asyncio.subprocess.DEVNULL,
|
||||
stderr=asyncio.subprocess.DEVNULL
|
||||
)
|
||||
await proc.wait()
|
||||
|
||||
if proc.returncode == 0:
|
||||
# Node ist online, führe SSH Info-Check durch
|
||||
await check_and_update_node(n['id'])
|
||||
else:
|
||||
# Node ist offline
|
||||
conn = get_db()
|
||||
conn.execute("UPDATE nodes SET status='Offline' WHERE id=?", (n['id'],))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
if proc.returncode == 0:
|
||||
await check_and_update_node(n['id'])
|
||||
else:
|
||||
conn = get_db()
|
||||
conn.execute("UPDATE nodes SET status='Offline' WHERE id=?", (n['id'],))
|
||||
conn.commit()
|
||||
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
|
||||
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()
|
||||
if not node:
|
||||
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 = (
|
||||
'arch=$(uname -m); '
|
||||
'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"'
|
||||
)
|
||||
|
||||
# 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:
|
||||
# Kein shell=True mehr nötig
|
||||
output = subprocess.check_output(ssh_args, stderr=subprocess.STDOUT).decode().strip()
|
||||
# Hier nutzen wir jetzt den asynchronen Subprocess-Aufruf von asyncio
|
||||
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 "|" not in output:
|
||||
raise ValueError(f"Unerwarteter Output: {output}")
|
||||
if proc.returncode == 0:
|
||||
output = stdout.decode().strip()
|
||||
arch, dock, os_name, vnc = output.split('|')
|
||||
status = "Docker Aktiv" if dock == "1" else "Bereit (Kein Docker)"
|
||||
|
||||
arch, dock, os_name, vnc = output.split('|')
|
||||
status = "Docker Aktiv" if dock == "1" else "Bereit (Kein Docker)"
|
||||
|
||||
conn.execute('''
|
||||
UPDATE nodes
|
||||
SET status=?, arch=?, docker=?, os=?, vnc=?
|
||||
WHERE id=?
|
||||
''', (status, arch, int(dock), os_name, int(vnc), node_id))
|
||||
conn.commit()
|
||||
return status
|
||||
conn.execute('''
|
||||
UPDATE nodes SET status=?, arch=?, docker=?, os=?, vnc=? WHERE id=?
|
||||
''', (status, arch, int(dock), os_name, int(vnc), node_id))
|
||||
conn.commit()
|
||||
print(f"✅ Auto-Refresh: Node {node['ip']} ist online ({status})")
|
||||
else:
|
||||
raise Exception(stderr.decode())
|
||||
|
||||
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.commit()
|
||||
return "Offline/Fehler"
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user