feat(ollama): add Ollama local LLM (.27) card & remote SSH control to service-controller
This commit is contained in:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user