From af64ad468fe92615e6167407249ec5c37d18e630 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80?=
<78488229+viktor138irk@users.noreply.github.com>
Date: Thu, 7 May 2026 02:03:43 +0900
Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BA=D0=B0=D0=B7=D0=B0=D1=82?=
=?UTF-8?q?=D1=8C=20=D0=B2=D1=82=D0=BE=D1=80=D0=BE=D0=B9=20=D0=BF=D1=80?=
=?UTF-8?q?=D0=BE=D0=B3=D1=80=D0=B5=D1=81=D1=81=20=D0=BA=D0=BE=D0=BD=D0=B2?=
=?UTF-8?q?=D0=B5=D1=80=D1=82=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BF=D0=BE=D1=81?=
=?UTF-8?q?=D0=BB=D0=B5=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8?=
=?UTF-8?q?=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B7=D0=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
web/templates/disk_images.html | 63 ++++++++++++++++++++++++++++------
1 file changed, 52 insertions(+), 11 deletions(-)
diff --git a/web/templates/disk_images.html b/web/templates/disk_images.html
index 35c4ab6..8f78f5b 100644
--- a/web/templates/disk_images.html
+++ b/web/templates/disk_images.html
@@ -51,7 +51,7 @@
Загрузить образ диска
-
Файл попадёт в /var/lib/virtuality/disk-images . При создании VM образ будет скопирован/сконвертирован в отдельный qcow2 -диск VM.
+ После загрузки .img/.raw прогресс начнётся заново и покажет конвертацию в qcow2. Уже готовый .qcow2 сохраняется без конвертации.
@@ -62,15 +62,14 @@
Raspberry / Orange Pi images
- Загрузи .img, затем в “Создать VM” выбери режим “Готовый диск”.
+ Загрузи .img, дождись конвертации, затем в “Создать VM” выбери режим “Готовый диск”.
Оригинал не трогаем
- Virtuality делает отдельный диск VM, чтобы загруженный образ оставался шаблоном.
+ Virtuality сохраняет исходник и создаёт рядом готовый qcow2-шаблон.
- qemu-img convert -O qcow2 source.img VM.qcow2
-virt-install --import --disk path=VM.qcow2
+ qemu-img convert -p -f raw -O qcow2 source.img source.qcow2
@@ -126,6 +125,44 @@ virt-install --import --disk path=VM.qcow2
return value.toFixed(value >= 10 || i === 0 ? 0 : 1) + ' ' + units[i];
}
+ function resetProgress(title, detail) {
+ bar.style.width = '0%';
+ percent.textContent = '0%';
+ statusLine.textContent = title;
+ loadedLine.textContent = detail || '0%';
+ speedLine.textContent = 'ожидание…';
+ }
+
+ async function pollConvert(operationId) {
+ resetProgress('Конвертация образа в qcow2…', '0%');
+ while (true) {
+ const response = await fetch('/api/operations/' + operationId, {cache: 'no-store'});
+ const payload = await response.json();
+ if (!payload.ok) throw new Error(payload.error || 'operation error');
+ const op = payload.operation;
+ const progress = Math.max(0, Math.min(100, Number(op.progress || 0)));
+ bar.style.width = progress + '%';
+ percent.textContent = progress + '%';
+ loadedLine.textContent = 'Конвертация: ' + progress + '%';
+ speedLine.textContent = op.status;
+ statusLine.textContent = op.message || 'Конвертация образа…';
+ if (op.status === 'success') {
+ bar.style.width = '100%';
+ percent.textContent = '100%';
+ statusLine.textContent = 'Конвертация завершена. Обновляем список образов…';
+ setTimeout(() => { window.location.href = '/disk-images'; }, 650);
+ return;
+ }
+ if (op.status === 'error') {
+ statusLine.textContent = 'Ошибка конвертации: ' + (op.message || 'см. журнал операций');
+ button.disabled = false;
+ fileInput.disabled = false;
+ return;
+ }
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ }
+ }
+
fileInput.addEventListener('change', function () {
const file = fileInput.files[0];
hint.textContent = file ? file.name + ' · ' + bytesText(file.size) : 'Файл ещё не выбран.';
@@ -143,11 +180,7 @@ virt-install --import --disk path=VM.qcow2
box.hidden = false;
button.disabled = true;
fileInput.disabled = true;
- statusLine.textContent = 'Передача образа на сервер…';
- bar.style.width = '0%';
- percent.textContent = '0%';
- loadedLine.textContent = '0 MB из ' + bytesText(file.size);
- speedLine.textContent = 'скорость считается…';
+ resetProgress('Передача образа на сервер…', '0 MB из ' + bytesText(file.size));
xhr.upload.onprogress = function (event) {
if (!event.lengthComputable) {
@@ -164,8 +197,14 @@ virt-install --import --disk path=VM.qcow2
if (pct >= 100) statusLine.textContent = 'Файл передан. Сервер сохраняет образ…';
};
- xhr.onload = function () {
+ xhr.onload = async function () {
if (xhr.status >= 200 && xhr.status < 400) {
+ let payload = null;
+ try { payload = JSON.parse(xhr.responseText); } catch (error) {}
+ if (payload && payload.mode === 'converting' && payload.operation_id) {
+ await pollConvert(payload.operation_id);
+ return;
+ }
bar.style.width = '100%';
percent.textContent = '100%';
statusLine.textContent = 'Готово. Обновляем список образов…';
@@ -184,6 +223,8 @@ virt-install --import --disk path=VM.qcow2
};
xhr.open('POST', form.action);
+ xhr.setRequestHeader('Accept', 'application/json');
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.send(data);
});