From b9c8d0eff3ecf5f2dbaec93cae4f28afca2592e7 Mon Sep 17 00:00:00 2001 From: "info@pi-farm.de" Date: Tue, 3 Mar 2026 21:12:32 +0000 Subject: [PATCH] pi_admin/main.py aktualisiert --- pi_admin/main.py | 55 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/pi_admin/main.py b/pi_admin/main.py index 5b875e1..8744d6e 100644 --- a/pi_admin/main.py +++ b/pi_admin/main.py @@ -1,14 +1,20 @@ -import json import os -from fastapi import FastAPI, Request, WebSocket +import subprocess +import pty +import select +import threading +import json +import paramiko +from fastapi import FastAPI, WebSocket, BackgroundTasks, Request +from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates from fastapi.staticfiles import StaticFiles -import paramiko from python_on_whales import DockerClient app = FastAPI() templates = Jinja2Templates(directory="templates") - +# Pfad zum SSH-Key +SSH_KEY = os.path.expanduser("~/.ssh/id_rsa") # Speicher für Nodes (einfache JSON-Datei) NODES_FILE = "nodes.json" @@ -112,6 +118,47 @@ async def chat_endpoint(websocket: WebSocket): else: await websocket.send_text("🤖 Auf welchem Pi soll ich Ollama installieren? (Nenne Name oder IP)") +def ensure_ssh_key(): + if not os.path.exists(SSH_KEY): + subprocess.run(["ssh-keygen", "-t", "rsa", "-N", "", "-f", SSH_KEY]) + +async def deploy_key_and_install(ip, user, password, ws: WebSocket): + ensure_ssh_key() + await ws.send_text(f"📦 Initialisiere Node {ip}...") + + # 1. SSH-Key kopieren (automatisiert mit sshpass) + cmd_copy = f"sshpass -p '{password}' ssh-copy-id -o StrictHostKeyChecking=no {user}@{ip}" + process = subprocess.Popen(cmd_copy, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + + for line in process.stdout: + await ws.send_text(f"🔑 SSH-Key: {line.strip()}") + + # 2. Docker & Ollama Installation (Beispiel-Streaming) + await ws.send_text(f"🚀 Starte Setup auf {ip}...") + cmd_install = f"ssh {user}@{ip} 'curl -sSL https://get.docker.com | sh && curl -fsSL https://ollama.com/install.sh | sh'" + + process = subprocess.Popen(cmd_install, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + for line in process.stdout: + await ws.send_text(f"🛠️ Install: {line.strip()}") + +# --- WEB TERMINAL LOGIK (Vereinfacht) --- +@app.websocket("/ws/terminal/{ip}") +async def terminal_websocket(websocket: WebSocket, ip: str): + await websocket.accept() + + # Öffne eine echte Shell-Sitzung zum Ziel-Node + (master_fd, slave_fd) = pty.openpty() + p = subprocess.Popen(["ssh", f"pi@{ip}"], stdin=slave_fd, stdout=slave_fd, stderr=slave_fd, text=True) + + async def pty_to_ws(): + while True: + await websocket.receive_text() # Input vom Browser empfangen (vereinfacht) + # Hier müsste xterm.js Input an master_fd geschrieben werden + + # Hinweis: Ein volles Terminal braucht bidirektionales Streaming von Bytes. + await websocket.send_text(f"Verbunden mit {ip}. Terminal-Session gestartet.") + + if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file