Показать второй прогресс конвертации после загрузки образа
Этот коммит содержится в:
@@ -51,7 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button id="disk-upload-button" type="submit" class="primary wide">Загрузить образ диска</button>
|
<button id="disk-upload-button" type="submit" class="primary wide">Загрузить образ диска</button>
|
||||||
</form>
|
</form>
|
||||||
<p class="muted small-note">Файл попадёт в <b>/var/lib/virtuality/disk-images</b>. При создании VM образ будет скопирован/сконвертирован в отдельный <b>qcow2</b>-диск VM.</p>
|
<p class="muted small-note">После загрузки .img/.raw прогресс начнётся заново и покажет конвертацию в qcow2. Уже готовый .qcow2 сохраняется без конвертации.</p>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article class="card">
|
<article class="card">
|
||||||
@@ -62,15 +62,14 @@
|
|||||||
<div class="quick-actions">
|
<div class="quick-actions">
|
||||||
<div class="big-action static">
|
<div class="big-action static">
|
||||||
<strong>Raspberry / Orange Pi images</strong>
|
<strong>Raspberry / Orange Pi images</strong>
|
||||||
<span>Загрузи .img, затем в “Создать VM” выбери режим “Готовый диск”.</span>
|
<span>Загрузи .img, дождись конвертации, затем в “Создать VM” выбери режим “Готовый диск”.</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="big-action static">
|
<div class="big-action static">
|
||||||
<strong>Оригинал не трогаем</strong>
|
<strong>Оригинал не трогаем</strong>
|
||||||
<span>Virtuality делает отдельный диск VM, чтобы загруженный образ оставался шаблоном.</span>
|
<span>Virtuality сохраняет исходник и создаёт рядом готовый qcow2-шаблон.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<pre>qemu-img convert -O qcow2 source.img VM.qcow2
|
<pre>qemu-img convert -p -f raw -O qcow2 source.img source.qcow2</pre>
|
||||||
virt-install --import --disk path=VM.qcow2</pre>
|
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -126,6 +125,44 @@ virt-install --import --disk path=VM.qcow2</pre>
|
|||||||
return value.toFixed(value >= 10 || i === 0 ? 0 : 1) + ' ' + units[i];
|
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 () {
|
fileInput.addEventListener('change', function () {
|
||||||
const file = fileInput.files[0];
|
const file = fileInput.files[0];
|
||||||
hint.textContent = file ? file.name + ' · ' + bytesText(file.size) : 'Файл ещё не выбран.';
|
hint.textContent = file ? file.name + ' · ' + bytesText(file.size) : 'Файл ещё не выбран.';
|
||||||
@@ -143,11 +180,7 @@ virt-install --import --disk path=VM.qcow2</pre>
|
|||||||
box.hidden = false;
|
box.hidden = false;
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
fileInput.disabled = true;
|
fileInput.disabled = true;
|
||||||
statusLine.textContent = 'Передача образа на сервер…';
|
resetProgress('Передача образа на сервер…', '0 MB из ' + bytesText(file.size));
|
||||||
bar.style.width = '0%';
|
|
||||||
percent.textContent = '0%';
|
|
||||||
loadedLine.textContent = '0 MB из ' + bytesText(file.size);
|
|
||||||
speedLine.textContent = 'скорость считается…';
|
|
||||||
|
|
||||||
xhr.upload.onprogress = function (event) {
|
xhr.upload.onprogress = function (event) {
|
||||||
if (!event.lengthComputable) {
|
if (!event.lengthComputable) {
|
||||||
@@ -164,8 +197,14 @@ virt-install --import --disk path=VM.qcow2</pre>
|
|||||||
if (pct >= 100) statusLine.textContent = 'Файл передан. Сервер сохраняет образ…';
|
if (pct >= 100) statusLine.textContent = 'Файл передан. Сервер сохраняет образ…';
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.onload = function () {
|
xhr.onload = async function () {
|
||||||
if (xhr.status >= 200 && xhr.status < 400) {
|
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%';
|
bar.style.width = '100%';
|
||||||
percent.textContent = '100%';
|
percent.textContent = '100%';
|
||||||
statusLine.textContent = 'Готово. Обновляем список образов…';
|
statusLine.textContent = 'Готово. Обновляем список образов…';
|
||||||
@@ -184,6 +223,8 @@ virt-install --import --disk path=VM.qcow2</pre>
|
|||||||
};
|
};
|
||||||
|
|
||||||
xhr.open('POST', form.action);
|
xhr.open('POST', form.action);
|
||||||
|
xhr.setRequestHeader('Accept', 'application/json');
|
||||||
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||||
xhr.send(data);
|
xhr.send(data);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user