feat(admin): Backend-Wechsel + Gateway-Neustart aus dem Admin-Panel — Plan-Schritt 3
- admin_llm: switch_backend() mit strikter Allowlist (backend ∈ {ollama,llamacpp},
Modell gegen 'ollama list' + Format-Regex), detached (Self-Restart-sicher),
niemals shell=True. restart_gateway_detached() für systemd-User-Dienst.
- switch-llm.sh: flock-Lock gegen parallele Backend-Wechsel (Exit 75).
- Endpunkte POST /api/admin/llm/backend (422 bei ungültig) und
POST /api/admin/gateway/restart (require_admin).
- Status-Tab: Steuerung (Backend-Dropdown + Modell, Wechseln/Neustart) mit
Poll bis das Gateway wieder antwortet; Hinweis auf systemd-Voraussetzung.
- Tests: Auth + Allowlist (Shell-Metazeichen/unbekanntes Modell -> 422). 165 grün.
- Doku §7.5.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
97ae0a5d34
commit
5b6f0f8ef2
7 changed files with 219 additions and 6 deletions
|
|
@ -39,3 +39,39 @@ def test_status_schema():
|
|||
assert isinstance(data["gpus"], list)
|
||||
# backend wird aus der base_url abgeleitet.
|
||||
assert data["backend"] in ("ollama", "llamacpp", "unknown")
|
||||
|
||||
|
||||
# ── Schreibende Steuerung: Validierung & Auth (ohne echten Switch) ───────────
|
||||
|
||||
def test_backend_switch_requires_admin():
|
||||
assert client.post("/api/admin/llm/backend", json={"backend": "ollama"}).status_code == 401
|
||||
|
||||
|
||||
def test_backend_switch_rejects_unknown_backend():
|
||||
# Ungültiges Backend -> 422, KEIN Prozess wird gestartet.
|
||||
r = client.post("/api/admin/llm/backend", json={"backend": "boese; rm -rf /"}, headers=ADM_HDR)
|
||||
assert r.status_code == 422
|
||||
|
||||
|
||||
def test_backend_switch_rejects_bad_model(monkeypatch):
|
||||
# Modell, das nicht in 'ollama list' ist -> 422 (sofern eine Liste vorhanden ist).
|
||||
import app.admin_llm as al
|
||||
|
||||
async def fake_models():
|
||||
return ["gemma3:latest", "qwen2.5:latest"]
|
||||
|
||||
monkeypatch.setattr(al, "available_ollama_models", fake_models)
|
||||
r = client.post("/api/admin/llm/backend",
|
||||
json={"backend": "ollama", "model": "gibtsnicht:99b"}, headers=ADM_HDR)
|
||||
assert r.status_code == 422
|
||||
|
||||
|
||||
def test_backend_switch_rejects_malformed_model(monkeypatch):
|
||||
# Modellname mit Shell-Metazeichen -> 422 (Format-Regex), kein Subprozess.
|
||||
r = client.post("/api/admin/llm/backend",
|
||||
json={"backend": "ollama", "model": "a; rm -rf /"}, headers=ADM_HDR)
|
||||
assert r.status_code == 422
|
||||
|
||||
|
||||
def test_gateway_restart_requires_admin():
|
||||
assert client.post("/api/admin/gateway/restart").status_code == 401
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue