Merge branch 'feature/vmops' into claude/production-system-image-smcxyr

# Conflicts:
#	tests/conftest.py
#	tests/dev_server.py
#	web/app.py
#	web/static/app.css
#	web/static/icons.svg
Этот коммит содержится в:
Claude
2026-09-25 15:03:48 +00:00
родитель a43f96883e 34684634af
Коммит 2b7223f311
19 изменённых файлов: 1400 добавлений и 22 удалений
+25
Просмотреть файл
@@ -249,3 +249,28 @@ def format_bytes(value: int | float) -> str:
return f"{value:.0f} {unit}" if value >= 10 or unit == "Б" else f"{value:.1f} {unit}"
value /= 1024
return f"{value:.0f} ТБ"
def parse_virsh_list_titles(text: str) -> dict[str, str]:
"""`virsh list --all --title` → {имя: название}. Колонки virsh выровнены
пробелами, поэтому название берём по смещению колонки Title из заголовка:
так не ломаются состояния с пробелом («shut off») и названия из нескольких слов."""
lines = (text or "").splitlines()
if not lines:
return {}
header = lines[0]
name_col, title_col = re.search(r"\bName\b", header), re.search(r"\bTitle\b", header)
if not name_col or not title_col:
return {}
titles: dict[str, str] = {}
for line in lines[1:]:
stripped = line.strip()
if not stripped or set(stripped) <= {"-"}:
continue
name_part = line[name_col.start():].split()
if not name_part:
continue
title = line[title_col.start():].strip()
if title:
titles[name_part[0]] = title
return titles