Files
dinlo f624fee5e0 Initial commit: Debian Manager — управление Debian-серверами
- SSH/SFTP управление без агентов
- Пакеты (apt), Docker, файлы, systemd, ufw, cron
- Плейбуки, групповые операции, планировщик
- Бэкапы, отчёты, Portainer, Proxmox VE, Gitea Deploy
- Терминал, Telegram-уведомления, тёмная/светлая тема
2026-09-05 19:46:45 +08:00

110 lines
3.3 KiB
Python

"""Docker по SSH: команды docker с JSON-выводом. Не требует docker CLI на клиенте."""
from __future__ import annotations
import json
from typing import List, Optional
from app.core.ssh import SSHClient
async def _docker(c: SSHClient, args: str, timeout: int = 120) -> str:
r = await c.sudo(f"docker {args}", timeout=timeout)
return r.stdout
async def available(c: SSHClient) -> bool:
r = await c.sudo("docker version --format '{{.Server.Version}}'", timeout=30)
return r.ok and bool(r.stdout.strip())
async def server_version(c: SSHClient) -> str:
r = await c.sudo("docker version --format '{{.Server.Version}}'", timeout=30)
return r.stdout.strip()
# ---------- контейнеры ----------
async def list_containers(c: SSHClient) -> List[dict]:
out = await _docker(c, "ps -a --format '{{json .}}'")
return [json.loads(l) for l in out.splitlines() if l.strip()]
async def container_action(c: SSHClient, cid: str, action: str) -> str:
assert action in ("start", "stop", "restart", "kill", "rm")
return await _docker(c, f"{action} {cid}")
async def container_logs(c: SSHClient, cid: str, tail: int = 200) -> str:
return await _docker(c, f"logs --tail {tail} {cid}")
async def container_inspect(c: SSHClient, cid: str) -> dict:
out = await _docker(c, f"inspect {cid}")
data = json.loads(out)
return data[0] if data else {}
async def container_stats(c: SSHClient) -> List[dict]:
out = await _docker(
c, "stats --no-stream --format '{{json .}}'", timeout=60
)
return [json.loads(l) for l in out.splitlines() if l.strip()]
# ---------- образы ----------
async def list_images(c: SSHClient) -> List[dict]:
out = await _docker(c, "images --format '{{json .}}'")
return [json.loads(l) for l in out.splitlines() if l.strip()]
async def pull(c: SSHClient, image: str):
return c.run_stream(f"docker pull {image}", use_sudo=True)
async def remove_image(c: SSHClient, image: str) -> str:
return await _docker(c, f"rmi {image}")
# ---------- тома ----------
async def list_volumes(c: SSHClient) -> List[dict]:
out = await _docker(c, "volume ls --format '{{json .}}'")
return [json.loads(l) for l in out.splitlines() if l.strip()]
async def remove_volume(c: SSHClient, name: str) -> str:
return await _docker(c, f"volume rm {name}")
# ---------- сети ----------
async def list_networks(c: SSHClient) -> List[dict]:
out = await _docker(c, "network ls --format '{{json .}}'")
return [json.loads(l) for l in out.splitlines() if l.strip()]
# ---------- система ----------
async def system_df(c: SSHClient) -> str:
return await _docker(c, "system df")
async def system_prune(c: SSHClient) -> str:
return await _docker(c, "system prune -f", timeout=600)
# ---------- compose ----------
async def compose_projects(c: SSHClient) -> List[dict]:
out = await _docker(c, "compose ls --format json")
try:
return json.loads(out)
except json.JSONDecodeError:
return []
async def compose_action(c: SSHClient, workdir: str, action: str, timeout: int = 600) -> str:
assert action in ("up -d", "down", "restart", "pull", "ps", "logs")
return await _docker(c, f"compose -f {workdir}/docker-compose.yml {action}", timeout)