- New /session/configure/{analysis_id} page with VM config + disk table
- Each disk has radio (boot selection) + shrink checkbox + size input
- Per-disk shrink control — check to enable, set custom size
- Disks sorted smallest first, boot disk pre-selected as largest
- Submit via /session/configure/submit, then /session/status/{job_id} polling
- Landpage download/SCP flow redirects to configure after analysis completes
97 lines
No EOL
3.5 KiB
HTML
97 lines
No EOL
3.5 KiB
HTML
{% extends "base.html" %}
|
|
{% block content %}
|
|
|
|
<div class="pve-panel pve-center">
|
|
<div class="pve-panel-header">
|
|
<span class="pve-panel-title">Converting VM {{ vmid or '...' }}</span>
|
|
</div>
|
|
<div class="pve-panel-body">
|
|
<p class="pve-dim">Job: <span class="pve-code">{{ job_id }}</span> | {{ vm_name }}</p>
|
|
|
|
<div id="status-badge" class="pve-badge pve-badge-queued">
|
|
<span class="pve-spinner"></span> Queued
|
|
</div>
|
|
|
|
<div class="pve-progress">
|
|
<div class="pve-progress-bar" id="pve-progress-bar" style="width:0%">0%</div>
|
|
</div>
|
|
|
|
<p id="status-message" class="pve-dim">Waiting for backend...</p>
|
|
<p id="elapsed" class="pve-dim">Elapsed: 0s</p>
|
|
<div id="error-block" class="pve-alert pve-alert-error pve-hidden"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="reuse-section" class="pve-panel pve-hidden">
|
|
<div class="pve-panel-header">
|
|
<span class="pve-panel-title">Conversion Complete — VM {{ vmid or '...' }} Ready</span>
|
|
</div>
|
|
<div class="pve-panel-body">
|
|
<p style="margin-bottom:1rem;">Create another VM from this disk?</p>
|
|
<button id="btn-cleanup" class="pve-btn" onclick="finishCleanup()">Clean Up & Finish</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
var jobId = '{{ job_id }}';
|
|
var startTime = Date.now();
|
|
var completed = false;
|
|
|
|
function finishCleanup() {
|
|
fetch('/session/cleanup/' + jobId, { method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ delete_staging_files: true, session_id: '{{ session_id }}' }) });
|
|
window.location.href = '/';
|
|
}
|
|
|
|
async function poll() {
|
|
try {
|
|
var resp = await fetch('/session/status/' + jobId);
|
|
var data = await resp.json();
|
|
var pct = data.progress_percentage || 0;
|
|
var fill = document.getElementById('pve-progress-bar');
|
|
if (fill) { fill.style.width = pct + '%'; fill.textContent = pct + '%'; }
|
|
|
|
var elMsg = document.getElementById('status-message');
|
|
if (elMsg) elMsg.textContent = data.message || '';
|
|
|
|
document.getElementById('elapsed').textContent = 'Elapsed: ' + Math.round((Date.now() - startTime) / 1000) + 's';
|
|
|
|
var badge = document.getElementById('status-badge');
|
|
var map = {
|
|
'queued': ['pve-badge pve-badge-queued', 'Queued'],
|
|
'processing_conversion': ['pve-badge pve-badge-running', 'Converting...'],
|
|
'importing_storage': ['pve-badge pve-badge-running', 'Importing...'],
|
|
'completed': ['pve-badge pve-badge-completed', 'Completed'],
|
|
'failed': ['pve-badge pve-badge-failed', 'Failed']
|
|
};
|
|
var clsText = map[data.status] || ['pve-badge pve-badge-queued', data.status];
|
|
if (badge) {
|
|
badge.className = 'badge ' + clsText[0];
|
|
badge.innerHTML = (data.status === 'processing_conversion' || data.status === 'importing_storage')
|
|
? '<span class="pve-spinner"></span> ' + clsText[1] : clsText[1];
|
|
}
|
|
|
|
if (data.error_details) {
|
|
var eb = document.getElementById('error-block');
|
|
if (eb) { eb.classList.remove('pve-hidden'); eb.textContent = data.error_details; }
|
|
}
|
|
|
|
if (data.status === 'completed' || data.status === 'failed') {
|
|
completed = true;
|
|
if (data.status === 'completed') {
|
|
var rs = document.getElementById('reuse-section');
|
|
if (rs) { rs.classList.remove('pve-hidden'); rs.scrollIntoView({ behavior: 'smooth' }); }
|
|
}
|
|
return;
|
|
}
|
|
} catch (err) {
|
|
var eb = document.getElementById('error-block');
|
|
if (eb) { eb.classList.remove('pve-hidden'); eb.textContent = 'Polling error: ' + err.message; }
|
|
}
|
|
if (!completed) setTimeout(poll, 2000);
|
|
}
|
|
|
|
setTimeout(poll, 3000);
|
|
</script>
|
|
|
|
{% endblock %} |