- защита от удаления проверяет все незавершённые восстановления этой копии (под любым именем машины)
- наблюдатель, который запускает машину после позднего выключения, снимается при «Выключить»/«Удалить»
в панели и при новой копии, и не мешает другим задачам с этой машиной
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- Проверка «уже скачивается» идёт по всем операциям каталога (glob), а не по последним 50;
скачивания без живого потока закрываются при старте (reap_stale_downloads), их .part удаляются,
а отмена такой операции завершает её сразу, не дожидаясь потока.
- Два запуска одного файла больше не пишут в один .part: проверки и создание операции под замком,
файл создаётся монопольно (O_EXCL), вторая копия завершается ошибкой, не трогая чужой файл;
осиротевший .part без владельца убирается перед новым запуском.
- Отмена во время проверки контрольной суммы и распаковки .img.xz останавливает работу:
операция завершается как отменённая, недокачанный файл, архив и недораспакованный образ удаляются.
- Битый .img.xz после неудачной распаковки удаляется с понятным сообщением, повторное скачивание разрешено;
проверка «файл уже есть» учитывает распакованный .img и qcow2 после конвертации.
- Ссылки с кириллицей (домен IDNA, путь и параметры в процентах) открываются, некорректный порт
и непонятные символы отклоняются сразу; ошибки сети описаны словами, исходный текст — в журнале.
- Прогресс раз в секунду перечитывает JSON операции, чтобы не затирать cancel_requested и замечать
отмену из другого процесса; операции читаются один раз на страницу; отмена в карточке — не ошибка.
- Формулировки карточек и сообщения о недоступной архитектуре упрощены; добавлены тесты на каждое поведение.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- Папка копии занимается атомарно ещё в create_backup; вторая копия в ту же
минуту получает суффикс -2, поэтому неудачная задача удаляет только свою папку.
- qemu-img info вызывается с -U (диск работающей машины занят QEMU), а ошибка
теперь поднимается пользователю вместо тихих нулей в расчёте места.
- Проверки «с машиной уже идёт задача» переведены на core.active_operations_for;
удаление копии, которую читает восстановление под другим именем, блокирует
метка .restoring с id задачи (устаревшая метка после перезапуска игнорируется).
- Зависший qemu-img убивается по общему таймауту stream_cmd.
- Восстановление с заменой сначала распаковывает все диски во временные файлы
и только потом удаляет старую машину; при ошибке она остаётся на месте.
Освобождаемое место старой машины больше не учитывается в проверке.
- Отклонённая команда выключения сообщается как есть, машина на паузе
сохраняется через managedsave и продолжает с того же места, а если машина
выключилась уже после отказа задачи — панель запускает её снова.
- rewrite_xml при замене сохраняет uuid и mac, убирает <backingStore> и
вынимает из привода образ, которого больше нет на сервере.
- Имена дисков из meta.json проверяются, процент занятого места считается от
всего диска, убран двойной отклик при удалении, сообщения об ошибках
копирования без жаргона, путь запуска из расписания указан через venv панели.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- stable (по умолчанию): обновление до последнего тега vX.Y.Z; пока релизов нет — ветка main
- main: ранний доступ, все изменения; канал берётся из web.env (Настройки → Обновления)
- scripts/update_target.sh общий для автообновления и ручного обновления, ZIP-запасной путь для тегов
- страница «Обновления» показывает канал и целевую версию
- клонирование и увеличение диска недоступны, пока машина в спящем режиме
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- все команды запускаются с LC_ALL=C.UTF-8: русский перевод virsh ломал разбор состояния машин
- имя машины не может быть числом или UUID (virsh принял бы его за ID другой машины)
- IP без MAC машины не угадывается по чужой аренде DHCP; у выключенных машин нет устаревшего IP
- вставленный ISO определяется по устройству CD-ROM, а не по «.iso» в пути
- ISO, вставленный в привод машины, нельзя удалить — машина перестала бы запускаться
- на ARM-платах нет ложного предупреждения про флаги vmx/svm
- проверка машины в консоли не блокирует остальные запросы
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- готовые образы: пользователь, пароль (хеш SHA-512) и SSH-ключ через virt-install --cloud-init,
диск увеличивается до выбранного размера, osinfo из каталога
- установка с ISO: выбор Linux / Windows 11 (UEFI + TPM) / Windows 10 / другая;
Windows получает SATA-диск и сетевую карту e1000e
- каждая машина в NAT получает фиксированный MAC и резервацию DHCP — адрес не меняется,
пробросы портов не ломаются; при удалении резервация снимается
- xorriso в пакетах ноды (для seed-образа cloud-init)
- tests/test_quickvm.py; тест каталога скачиваний сделан детерминированным
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- core.interrupt_orphaned_operations() при запуске панели, active_operations_for() по всем задачам
- снимки: имена с пробелами, снимки вне панели без кнопок, --force только на «revert requires force»,
память на паузе учитывается, защита от двойного нажатия, надпись «Создаём снимок…»
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- формат образа диска определяется через qemu-img info (облачные .img — это qcow2)
- ISO подключается/извлекается через virsh change-media, без угадывания устройств
- действия start/shutdown/delete сообщают об ошибке virsh; удаление UEFI-машин (--nvram)
- правила iptables/ufw для удалённых пробросов портов теперь удаляются
- VNC машин слушает только 127.0.0.1; virtuality-ctl reconfigure чинит старые машины
- зелёное уведомление не показывается, если действие вернуло ошибку
- убраны смена архитектуры машины, эмуляция ARM64 на x86 и загрузка по PXE
- tests/test_audit_fixes.py
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
Модуль web/features/vmops.py (router подключён двумя строками в app.py):
- Увеличение диска на вкладке «Диски и загрузка»: размер из qemu-img info,
только рост, проверка свободного места; выключенная машина — qemu-img resize,
работающая — virsh blockresize. Диски вне /var/lib/virtuality не трогаются.
- Клонирование: страница /vm/{name}/clone с проверками (машина выключена,
хватает места, имя свободно), фоновая операция virt-clone с явными --file
в хранилище образов, прогресс по росту файлов копии, pool-refresh после.
- Пауза, продолжение, спящий режим (managedsave как операция) и удаление
сохранённого состояния — /vm/{name}/ops/{action}; пункты меню на обзоре
и странице машины, кнопка «Продолжить» для приостановленной машины.
- Заметки: название и описание через virsh desc (--new-desc=ТЕКСТ одним
аргументом, чтобы текст вида «--edit» не стал опцией); название видно
под именем машины на обзоре (virsh list --all --title, парсер в presenters).
- Живая нагрузка: /live/stats по virsh domstats для всех активных машин
одним вызовом, CPU % по разнице cpu.time между опросами; шкалы в строках
обзора и карточка «Нагрузка сейчас» на странице машины (panel.js, 5 с).
Тесты для всех парсеров и маршрутов (tests/test_vmops.py), демо-данные
в dev_server.py (три машины в разных состояниях, живые шкалы), значки
notebook-pen и expand в спрайте.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- web/features/settings.py + settings.html: всё, что спрашивал мастер, меняется в одном месте
- перезагрузка/выключение с ожиданием возврата панели, архив настроек и отчёт для поддержки
- сообщения после редиректов показываются на любой странице (?message=, ?error=)
- virtuality-ctl set: настройки обновлений применяются без перезапуска панели
- tests/test_settings.py
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- web/features/downloads.py: фоновая операция типа «download» — urllib по 1 МиБ,
таймаут 30 с, User-Agent Virtuality/<версия>, запись в .<имя>.part и переименование
в конце; в сообщении скорость и остаток («245 МБ из 3,1 ГБ · 12 МБ/с · осталось 4 мин»);
отмена между кусками (POST /api/operations/{id}/cancel и /downloads/{id}/cancel);
проверка схемы, имени файла, занятости имени и свободного места (Content-Length + 512 МиБ);
для каталога — проверка контрольной суммы (SHA256SUMS/SHA512SUMS/одиночный хеш),
при несовпадении файл удаляется; после ISO — virsh pool-refresh virtuality-iso,
после .img.xz — распаковка и запуск конвертации в qcow2 как при загрузке с компьютера.
- /iso и /disk-images: карточка «Скачать по ссылке», на /disk-images — «Каталог готовых
систем» для процессора сервера с пометками «Рекомендуем», «Загружен», «Скачивается»;
идущие скачивания вверху страниц с живым прогрессом (опрос раз в 2 с) и «Отменить».
- Тесты: парсер контрольных сумм, проверка ссылок и имён, загрузка с локального
http-сервера, редирект, отмена, несовпадение суммы, нехватка места, .img.xz, маршруты.
- В ветку скопированы web/core.py, web/catalog.py, presenters.format_bytes и tests/fixtures
из общей рабочей копии (без изменений), conftest подменяет run_cmd и каталоги и в core.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- web/nodectl.py: факты об узле (сеть, диски, часовые пояса, KVM) и действия через virtuality-ctl
- web/features/setup.py + шаблоны setup*: мастер из 8 шагов (установка, часовой пояс, пароль, HTTPS, сеть, хранилище, обновления, итог)
- безопасное переключение на мост br0 с авто-откатом и подтверждением из браузера
- virtuality-ctl storage-use: перенос данных на отдельный диск
- панель перенаправляет на мастер до его завершения; демо-режим VIRTUALITY_DEV_SETUP
- tests/test_setup.py: 8 тестов на маршруты мастера
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
Первый модуль-фича на общем core.py — web/features/backups.py,
подключён в app.py одной строкой include_router.
- копия = папка /var/lib/virtuality/backups/<машина>/<ГГГГММДД-ЧЧММ>/
с vm.xml (virsh dumpxml --migratable), <диск>.qcow2 на каждый диск
и meta.json (машина, дата, диски, размер, версия, заметка);
- создание — фоновая операция: свободное место проверяется заранее по
qemu-img info, работающая машина мягко выключается (ждём до 3 минут,
без принудительного выключения), диски копируются qemu-img convert -c
с прогрессом из строк «(NN.NN/100%)», после чего машина запускается;
- восстановление — операция: диски распаковываются в images/<имя>[-<диск>]
.qcow2, в XML меняется имя, убираются uuid, MAC и nvram, затем
virsh define; восстановление под занятым именем требует галочку
«заменить существующую» — старая машина удаляется с дисками, а ISO
перед этим извлекается из привода, чтобы --remove-all-storage его не
стёр;
- страницы: «Резервные копии» в меню (все копии по машинам, занятое и
свободное место), вкладка «Резервные копии» на странице машины с
формой «Создать копию» и заметкой, страница восстановления с
понятным списком шагов;
- create_backup(vm, note, wait=True) можно вызывать без HTTP —
задел под расписание (python3 web/features/backups.py web01 «заметка»);
- tests/conftest.py: каталог backups, патчи core.*, вывод virsh
domblklist --details, dumpxml --migratable и qemu-img info из fixtures;
tests/test_backups.py — 33 теста парсеров, воркеров и маршрутов;
tests/dev_server.py показывает демо-копии.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
Новая страница /vm/{name}/snapshots и вкладка «Снимки» на странице машины:
список внутренних снимков libvirt с описанием, датой, состоянием и меткой
«Текущий», создание снимка с автоматическим именем snap-ГГГГММДД-ЧЧММ,
возврат к снимку и удаление. Создание и возврат идут как фоновые задачи
с журналом; страница сама обновляется, когда задача завершится.
Ограничения и ошибки объясняются по-русски: не больше 8 снимков на машину,
только диски qcow2, предупреждение для машин с UEFI; известные ошибки libvirt
переводятся, а исходный текст показывается рядом. Возврат повторяется
с --force, если libvirt этого требует.
Модуль web/features/snapshots.py, парсеры с тестами на образцах вывода virsh,
fake_run_cmd в тестах расширен командами snapshot-*, demo-сервер показывает
снимки и идущую задачу. В спрайт добавлены иконки camera и history.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- графическая тема GRUB (фон, подсветка выбранного пункта, полоса
автозапуска) в image/grub, подключается в build-iso.sh; при отсутствии
модулей GRUB меню остаётся текстовым;
- пункты меню переведены: «Установить Virtuality X.Y.Z», «Загрузить
с другого диска», «Настройки UEFI», «Проверить память»;
- tests/render_html.mjs рендерит HTML в PNG (для фона темы).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- web/core.py: настройки, авторизация, run_cmd, хранилище фоновых операций
и шаблоны вынесены в общий модуль, чтобы новые функции жили в отдельных
модулях; app.py пока не переключён;
- web/catalog.py: официальные облачные образы Ubuntu 26.04/24.04,
Debian 13, Alpine 3.23 (amd64/arm64) и типы ОС для установки с ISO;
- tests/fixtures: реалистичный вывод virsh для тестов будущих парсеров;
- presenters.format_bytes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
Новый дизайн в стиле коммерческих продуктов, рассчитанный на обычного
пользователя:
- светлая и тёмная темы (следует системной), шрифт Inter и иконки
Lucide вшиты в панель — работает без интернета;
- общий каркас base.html и компоненты _ui.html вместо 14 независимых
страниц; адаптивная вёрстка для телефона;
- простой язык: «Работает / Выключена», «Автозапуск», «Доступ из
интернета» вместо running, autostart, DNAT; технические данные
убраны во вкладку «Для специалистов»;
- главная: загрузка процессора, памяти и хранилища, карточки машин с
главным действием и меню остальных, приветственный сценарий для
первой машины;
- мастер создания машины из трёх шагов с готовыми конфигурациями;
- страница машины с вкладками, переключателем автозапуска и порядком
загрузки кнопками;
- «Сеть и доступ»: открытие SSH, сайта, RDP и т. д. из списка,
ручные IP машин, понятная диагностика;
- загрузка образов перетаскиванием с прогрессом и оставшимся временем;
- подтверждения опасных действий в диалоге и уведомления;
- раздел «Помощь»;
- web/presenters.py переводит вывод virsh и /proc в данные для
страниц; tests/dev_server.py запускает панель с имитацией virsh.
Также: исправлено определение модели CPU на x86 («207» вместо
названия процессора), история версии 0.10.0 и документация обновлены
под Ubuntu 26.04 LTS, в README добавлены скриншоты.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
- tests/: 55 тестов web-панели, авторизации, сети и обновлений,
все страницы рендерятся на замоканных virsh/systemctl;
- GitHub Actions: pyflakes + pytest на Python 3.11–3.13, shellcheck,
сборка ISO amd64/arm64 по тегу v* или вручную;
- Makefile: make check / make test / make iso;
- docs/IMAGE.md и README: установка с ISO, продакшен-настройки,
разработка;
- updates/versions.json: описание версии 0.10.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg