Enhance logging functionality in CLI and tunnel operations. Updated gpu-rent logs to provide a complete journal output, including cloud-init logs by default. Added log digest printing for better visibility during VM operations, especially on failure scenarios. Improved error handling and added tests for new log digest features.

This commit is contained in:
Leonid Pershin
2026-08-23 05:08:05 +03:00
parent d06b4f4fdc
commit ac0797530d
7 changed files with 326 additions and 47 deletions
+1
View File
@@ -145,6 +145,7 @@ def _stub_tunnel(monkeypatch) -> None:
monkeypatch.setattr("gpu_rent.tunnel.load_state", lambda: st)
monkeypatch.setattr("gpu_rent.tunnel.save_state", lambda s: None)
monkeypatch.setattr("gpu_rent.access_card.print_access_card", lambda *a, **k: None)
monkeypatch.setattr("gpu_rent.vm_logs.print_log_digest", lambda *a, **k: None)
monkeypatch.setattr("gpu_rent.local_watchdog.watchdog_installed", lambda: False)
monkeypatch.setattr("gpu_rent.local_watchdog.clear_lease", lambda: None)
+31
View File
@@ -15,11 +15,42 @@ def test_stop_after_failed_up_calls_cmd_stop(monkeypatch):
monkeypatch.setattr("gpu_rent.cli.ok", lambda *_a, **_k: None)
monkeypatch.setattr("gpu_rent.cli.err", lambda *_a, **_k: None)
monkeypatch.setattr("gpu_rent.cli.log", lambda *_a, **_k: None)
monkeypatch.setattr(
"gpu_rent.state.load_state",
lambda: SimpleNamespace(floating_ip=None),
)
_stop_after_failed_up(SimpleNamespace(), RuntimeError("boom"))
assert calls == [{"no_pull": True, "destroy_disks": False}]
def test_stop_after_failed_up_prints_digest_when_fip(monkeypatch):
calls: list[dict] = []
digests: list[str] = []
def fake_stop(cfg, *, no_pull=False, log=None, destroy_disks=False):
calls.append({"no_pull": no_pull})
return SimpleNamespace()
monkeypatch.setattr("gpu_rent.cli.cmd_stop", fake_stop)
monkeypatch.setattr("gpu_rent.cli.warn", lambda *_a, **_k: None)
monkeypatch.setattr("gpu_rent.cli.ok", lambda *_a, **_k: None)
monkeypatch.setattr("gpu_rent.cli.err", lambda *_a, **_k: None)
monkeypatch.setattr("gpu_rent.cli.log", lambda *_a, **_k: None)
monkeypatch.setattr(
"gpu_rent.state.load_state",
lambda: SimpleNamespace(floating_ip="10.0.0.1"),
)
monkeypatch.setattr(
"gpu_rent.vm_logs.print_log_digest",
lambda cfg, host, **kw: digests.append(host),
)
_stop_after_failed_up(SimpleNamespace(enable_swarmui=True, llm_runtime="none"), RuntimeError("boom"))
assert digests == ["10.0.0.1"]
assert calls == [{"no_pull": True}]
def test_up_stop_on_fail_default_true(monkeypatch, tmp_path):
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("USERPROFILE", str(tmp_path))
+93
View File
@@ -0,0 +1,93 @@
"""Unit tests for vm_logs digest / CLI log command builders."""
from __future__ import annotations
import pytest
from gpu_rent.vm_logs import (
DIGEST_LINES,
KILLER_UNIT,
build_logs_remote_cmd,
fetch_unit_logs,
resolve_unit_alias,
units_for,
)
class _Cfg:
def __init__(self, *, enable_swarmui: bool = True, llm_runtime: str = "none"):
self.enable_swarmui = enable_swarmui
self.llm_runtime = llm_runtime
def test_units_for_swarm_and_ollama():
assert units_for(_Cfg(enable_swarmui=True, llm_runtime="ollama")) == [
"swarmui",
"ollama",
KILLER_UNIT,
]
def test_units_for_swarm_only():
assert units_for(_Cfg(enable_swarmui=True, llm_runtime="none")) == [
"swarmui",
KILLER_UNIT,
]
def test_units_for_llm_only():
assert units_for(_Cfg(enable_swarmui=False, llm_runtime="ollama")) == [
"ollama",
KILLER_UNIT,
]
def test_digest_cmd_has_journal_no_cloud_init():
units = units_for(_Cfg(enable_swarmui=True, llm_runtime="ollama"))
cmd = build_logs_remote_cmd(
lines=DIGEST_LINES,
include_cloud_init=False,
journal_units=units,
)
assert "cloud-init" not in cmd
assert "journalctl -u swarmui" in cmd
assert "journalctl -u ollama" in cmd
assert f"journalctl -u {KILLER_UNIT}" in cmd
assert f"-n {DIGEST_LINES}" in cmd
assert "systemctl is-active swarmui" in cmd
def test_full_logs_cmd_includes_cloud_init():
cmd = build_logs_remote_cmd(
lines=80,
include_cloud_init=True,
journal_units=["swarmui", "ollama", KILLER_UNIT],
)
assert "cloud-init-output.log" in cmd
assert "journalctl -u swarmui" in cmd
def test_resolve_unit_alias():
assert resolve_unit_alias(None) == "all"
assert resolve_unit_alias("swarm") == "swarmui"
assert resolve_unit_alias("killer") == KILLER_UNIT
with pytest.raises(ValueError, match="неизвестный"):
resolve_unit_alias("bogus")
def test_fetch_unit_logs_uses_ssh(monkeypatch):
calls: list[tuple] = []
def fake_ssh(cfg, host, command, check=False, timeout=60):
calls.append((host, command, check, timeout))
return "active\nok"
monkeypatch.setattr("gpu_rent.vm_logs.run_ssh", fake_ssh)
cfg = _Cfg(enable_swarmui=True, llm_runtime="none")
out = fetch_unit_logs(cfg, "1.2.3.4", units_for(cfg), lines=20)
assert out == "active\nok"
assert calls[0][0] == "1.2.3.4"
assert "cloud-init" not in calls[0][1]
assert "journalctl -u swarmui" in calls[0][1]
assert f"journalctl -u {KILLER_UNIT}" in calls[0][1]
assert "journalctl -u ollama" not in calls[0][1]