Add package data for GPU rent and update CLI documentation
- Added package data configuration for the 'gpu_rent' package in pyproject.toml. - Updated README.md to include usage instructions for Windows and Unix launchers. - Enhanced CLI documentation in cli.md to reflect new commands and their functionalities. - Revised setup.md to clarify installation steps and environment setup. - Improved error handling and command descriptions in the CLI implementation. - Added new functions for model version handling and flavor resolution in the codebase. - Updated state management to include additional properties for better tracking.
This commit is contained in:
+142
-59
@@ -18,7 +18,12 @@ from gpu_rent.config import load_config
|
||||
from gpu_rent.doctor import blocking_failed, dry_run_plan, run_doctor
|
||||
from gpu_rent.errors import GpuRentError, NotReadyError
|
||||
from gpu_rent.os_client import connect, find_snapshot_by_name, find_tagged_servers
|
||||
from gpu_rent.session import cmd_stop, cmd_up
|
||||
from gpu_rent.ssh_ops import interactive_ssh, run_ssh
|
||||
from gpu_rent.state import load_state, preempt_window_end
|
||||
from gpu_rent.provision import ensure_swarmui_running, seed_civitai, seed_extensions
|
||||
from gpu_rent.sync_files import pull_tree, push_tree
|
||||
from gpu_rent.tunnel import run_tunnel
|
||||
|
||||
if sys.platform == "win32":
|
||||
for _stream in (sys.stdout, sys.stderr):
|
||||
@@ -55,10 +60,8 @@ def _die(exc: BaseException) -> None:
|
||||
|
||||
def _nyi(name: str) -> None:
|
||||
raise NotReadyError(
|
||||
f"`{name}` ещё не создаёт/не гасит GPU. Сейчас работают: doctor, dry-run, status, open.\n"
|
||||
"1) Заполни ~/.gpu-rent/.env по docs/setup.md\n"
|
||||
"2) gpu-rent doctor\n"
|
||||
"3) Когда doctor зелёный и квота GPU > 0 — можно писать up."
|
||||
f"`{name}` ещё не готов. Уже работают: doctor, dry-run, status, open, up, stop, destroy, ssh, logs, tunnel, seed-*, push, pull-output.\n"
|
||||
"Ключи: docs/setup.md"
|
||||
)
|
||||
|
||||
|
||||
@@ -77,10 +80,18 @@ def _print_checks(checks) -> int:
|
||||
if failed:
|
||||
console.print("\n[red]Сессию начинать нельзя.[/red] См. docs/setup.md")
|
||||
return 1
|
||||
console.print("\n[green]Можно идти дальше.[/green] mutating up пока не подключён.")
|
||||
console.print("\n[green]Можно идти дальше.[/green] Дальше: gpu-rent up --yes")
|
||||
return 0
|
||||
|
||||
|
||||
def _live():
|
||||
cfg = load_config(require_auth=True)
|
||||
state = load_state()
|
||||
if not state.floating_ip:
|
||||
raise GpuRentError("нет floating IP — сначала gpu-rent up")
|
||||
return cfg, state.floating_ip
|
||||
|
||||
|
||||
@app.command()
|
||||
def version() -> None:
|
||||
"""Версия пакета."""
|
||||
@@ -185,14 +196,86 @@ def up(
|
||||
yes: bool = typer.Option(False, "--yes", help="Без вопросов"),
|
||||
adopt: bool = typer.Option(False, "--adopt", help="Подхватить тег gpu-rent без state"),
|
||||
) -> None:
|
||||
"""Create/unshelve GPU. Пока: doctor, затем стоп — mutating ещё не подключён."""
|
||||
del no_spot, flavor, yes, adopt
|
||||
"""Create/unshelve GPU и bootstrap SwarmUI (без Docker)."""
|
||||
try:
|
||||
checks = run_doctor()
|
||||
code = _print_checks(checks)
|
||||
if code != 0:
|
||||
raise typer.Exit(1)
|
||||
_nyi("up")
|
||||
cfg = load_config(require_auth=True)
|
||||
|
||||
def confirm(msg: str) -> bool:
|
||||
return typer.confirm(msg)
|
||||
|
||||
cmd_up(
|
||||
cfg,
|
||||
no_spot=no_spot,
|
||||
flavor=flavor,
|
||||
yes=yes,
|
||||
adopt=adopt,
|
||||
confirm=confirm,
|
||||
log=lambda m: console.print(m),
|
||||
)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command()
|
||||
def stop(
|
||||
no_pull: bool = typer.Option(False, "--no-pull"),
|
||||
) -> None:
|
||||
"""Удалить compute и FIP, диски оставить."""
|
||||
try:
|
||||
cfg = load_config(require_auth=True)
|
||||
cmd_stop(cfg, no_pull=no_pull, log=lambda m: console.print(m))
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command()
|
||||
def destroy(
|
||||
i_understand_data_loss: bool = typer.Option(False, "--i-understand-data-loss"),
|
||||
) -> None:
|
||||
"""stop + диски."""
|
||||
if not i_understand_data_loss:
|
||||
console.print("Нужен флаг --i-understand-data-loss")
|
||||
raise typer.Exit(1)
|
||||
try:
|
||||
cfg = load_config(require_auth=True)
|
||||
cmd_stop(cfg, destroy_disks=True, log=lambda m: console.print(m))
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command()
|
||||
def ssh() -> None:
|
||||
"""Оболочка на VM (нужен живой compute и FIP)."""
|
||||
try:
|
||||
cfg = load_config(require_auth=True)
|
||||
state = load_state()
|
||||
if not state.floating_ip:
|
||||
raise GpuRentError("нет floating IP в state — сначала gpu-rent up")
|
||||
raise typer.Exit(interactive_ssh(cfg, state.floating_ip))
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command()
|
||||
def logs() -> None:
|
||||
"""cloud-init / journalctl на VM."""
|
||||
try:
|
||||
cfg = load_config(require_auth=True)
|
||||
state = load_state()
|
||||
if not state.floating_ip:
|
||||
raise GpuRentError("нет IP — VM не поднята")
|
||||
out = run_ssh(
|
||||
cfg,
|
||||
state.floating_ip,
|
||||
"sudo -n tail -n 80 /var/log/cloud-init-output.log 2>/dev/null; "
|
||||
"systemctl is-active swarmui 2>/dev/null || true",
|
||||
check=False,
|
||||
)
|
||||
console.print(out)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
@@ -202,9 +285,17 @@ def tunnel(
|
||||
open_browser: bool = typer.Option(False, "--open", help="Открыть браузер на 17801"),
|
||||
) -> None:
|
||||
"""SSH localhost:17801 -> VM :7801. Ctrl+C закрывает туннель, GPU оставляет."""
|
||||
del open_browser
|
||||
try:
|
||||
_nyi("tunnel")
|
||||
cfg = load_config(require_auth=True)
|
||||
state = load_state()
|
||||
if not state.floating_ip:
|
||||
raise GpuRentError("нет floating IP — сначала gpu-rent up")
|
||||
run_tunnel(
|
||||
cfg,
|
||||
state.floating_ip,
|
||||
open_browser=open_browser,
|
||||
log=lambda m: console.print(m),
|
||||
)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
@@ -223,84 +314,76 @@ def hold(
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command()
|
||||
def stop(
|
||||
no_pull: bool = typer.Option(False, "--no-pull"),
|
||||
) -> None:
|
||||
"""Удалить compute и FIP, диски оставить."""
|
||||
del no_pull
|
||||
try:
|
||||
_nyi("stop")
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command()
|
||||
def destroy(
|
||||
i_understand_data_loss: bool = typer.Option(False, "--i-understand-data-loss"),
|
||||
) -> None:
|
||||
"""stop + диски."""
|
||||
if not i_understand_data_loss:
|
||||
console.print("Нужен флаг --i-understand-data-loss")
|
||||
raise typer.Exit(1)
|
||||
try:
|
||||
_nyi("destroy")
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command()
|
||||
def logs() -> None:
|
||||
try:
|
||||
_nyi("logs")
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command()
|
||||
def ssh() -> None:
|
||||
try:
|
||||
_nyi("ssh")
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command("seed-models")
|
||||
def seed_models() -> None:
|
||||
"""Докачать Civitai-манифест на живой диск."""
|
||||
try:
|
||||
_nyi("seed-models")
|
||||
cfg, host = _live()
|
||||
seed_civitai(cfg, host, log=lambda m: console.print(m))
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command("push")
|
||||
def push_all() -> None:
|
||||
"""SFTP Models + Wildcards + CustomWorkflows."""
|
||||
try:
|
||||
_nyi("push")
|
||||
cfg, host = _live()
|
||||
|
||||
def log(msg: str) -> None:
|
||||
console.print(msg)
|
||||
|
||||
push_tree(cfg, host, cfg.local_models_dir, "/mnt/swarm_data/Models", log, models=True)
|
||||
push_tree(cfg, host, cfg.local_wildcards_dir, "/mnt/swarm_data/Data/Wildcards", log, models=False)
|
||||
push_tree(cfg, host, cfg.local_workflows_dir, "/mnt/swarm_data/CustomWorkflows", log, models=False)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command("push-models")
|
||||
def push_models() -> None:
|
||||
"""SFTP только ./Models."""
|
||||
try:
|
||||
_nyi("push-models")
|
||||
cfg, host = _live()
|
||||
push_tree(
|
||||
cfg,
|
||||
host,
|
||||
cfg.local_models_dir,
|
||||
"/mnt/swarm_data/Models",
|
||||
lambda m: console.print(m),
|
||||
models=True,
|
||||
)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command("pull-output")
|
||||
def pull_output() -> None:
|
||||
def pull_output_cmd() -> None:
|
||||
"""Забрать новые файлы Output/ с VM."""
|
||||
try:
|
||||
_nyi("pull-output")
|
||||
cfg, host = _live()
|
||||
pull_tree(
|
||||
cfg,
|
||||
host,
|
||||
"/mnt/swarm_data/Output",
|
||||
cfg.local_output_dir,
|
||||
lambda m: console.print(m),
|
||||
)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
@app.command("seed-extensions")
|
||||
def seed_extensions() -> None:
|
||||
def seed_extensions_cmd() -> None:
|
||||
"""Clone/fetch extensions.yaml, затем restart swarmui."""
|
||||
try:
|
||||
_nyi("seed-extensions")
|
||||
cfg, host = _live()
|
||||
|
||||
def log(msg: str) -> None:
|
||||
console.print(msg)
|
||||
|
||||
seed_extensions(cfg, host, log)
|
||||
ensure_swarmui_running(cfg, host, log, restart=True)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user