Резервные копии машин были добавлены: создание, восстановление, удаление

Первый модуль-фича на общем 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
Этот коммит содержится в:
Claude
2026-09-25 14:32:05 +00:00
родитель 1e29134fb3
Коммит 2450278d93
13 изменённых файлов: 1433 добавлений и 8 удалений
+4
Просмотреть файл
@@ -2016,3 +2016,7 @@ def api_health(request: Request):
if not get_current_user(request):
return JSONResponse({"ok": False, "error": "Unauthorized"}, status_code=401)
return {"system": system_summary(), "host_profile": host_profile.load_host_profile(), "services": {"libvirtd": service_state("libvirtd.service"), "virtlogd": service_state("virtlogd.service"), "cockpit": service_state("cockpit.socket"), "dashboard": service_state("virtuality-console-dashboard.service"), "web": service_state("virtuality-web.service")}, "vms": parse_virsh_list(), "pools": parse_pool_list(), "network": network_summary(), "virtuality_nat": network_core.network_context()}
import features.backups as backups # noqa: E402
app.include_router(backups.router)