feat(ollama): add Ollama local LLM (.27) card & remote SSH control to service-controller

This commit is contained in:
2026-10-12 14:12:30 +11:00
parent c9d09ac859
commit a661fb3b2e
3 changed files with 33 additions and 9 deletions

View File

@@ -84,21 +84,29 @@ def _container_state(target: str) -> str:
return out or ("unknown" if r.returncode != 0 else "unknown")
def _systemd_user_state(target: str) -> str:
r = subprocess.run(["systemctl", "--user", "is-active", target],
capture_output=True, text=True)
return (r.stdout.strip() or "inactive").lower()
def _systemd_user_state(target: str, remote: str | None = None) -> str:
cmd = ["systemctl", "--user", "is-active", target]
if remote:
cmd = ["ssh", "-o", "StrictHostKeyChecking=no", "-o", "BatchMode=yes", remote] + cmd
r = subprocess.run(cmd, capture_output=True, text=True)
out = (r.stdout.strip() or "inactive").lower()
if out == "active":
return "running"
return out
def _do(kind: str, action: str, target: str) -> str:
def _do(kind: str, action: str, target: str, remote: str | None = None) -> str:
"""Run start/stop. Returns the state after a short settle."""
if kind == "container":
subprocess.run(["docker", action, target], capture_output=True, text=True)
time.sleep(1.5)
return _container_state(target)
subprocess.run(["systemctl", "--user", action, target], capture_output=True, text=True)
cmd = ["systemctl", "--user", action, target]
if remote:
cmd = ["ssh", "-o", "StrictHostKeyChecking=no", "-o", "BatchMode=yes", remote] + cmd
subprocess.run(cmd, capture_output=True, text=True)
time.sleep(1.5)
return _systemd_user_state(target)
return _systemd_user_state(target, remote=remote)
def _targets(s: dict[str, Any]) -> list[str]:
@@ -114,12 +122,13 @@ def _probe(s: dict[str, Any]) -> tuple[str, list[str]]:
"""Collect the state of every target. Returns (aggregate, per-target list).
Aggregate: running if ANY is running; stopped if ALL stopped; else partial."""
kind = s["kind"]
remote = s.get("remote")
states = []
for tgt in _targets(s):
if kind == "container":
states.append(_container_state(tgt))
else:
states.append(_systemd_user_state(tgt))
states.append(_systemd_user_state(tgt, remote=remote))
n_ok = sum(1 for st in states if st in ("running", "active", "started"))
n_none = sum(1 for st in states if st in ("unknown",)) or 0
if n_ok == len(states):
@@ -184,9 +193,10 @@ async def service_action(sid: str, action: str,
if s is None:
raise HTTPException(status_code=404, detail="unknown service")
actor = (who or "unknown-user").strip()[:40]
remote = s.get("remote")
results = {}
for tgt in _targets(s):
results[tgt] = _do(s["kind"], action, tgt)
results[tgt] = _do(s["kind"], action, tgt, remote=remote)
agg, _ = _probe(s)
audit(f"{actor} {action} {sid} -> {agg} ({len(_targets(s))} targets)")
return {"ok": True, "id": sid, "state": agg, "targets": results}