fix: real-time progress during qemu-img convert, smoother percentages
qemu-img convert is the longest step but previously had no progress updates — bar stuck at 10% until conversion completed. Now _convert_with_progress() runs qemu-img in background and polls output file size every 2s, updating progress with GiB done/total. Progress flow for single disk: 5% queued → 10% starting → 10-55% converting (real-time) → 60% shrinking → 70% creating VM → 75-85% importing → 95% configuring → 100% done
This commit is contained in:
parent
affcde7afb
commit
9a6d2df78b
1 changed files with 44 additions and 8 deletions
|
|
@ -145,6 +145,43 @@ def _run(cmd: list[str], check: bool = True, timeout: int = 3600) -> subprocess.
|
|||
return result
|
||||
|
||||
|
||||
def _convert_with_progress(
|
||||
job_id: str, source_path: Path, output_path: Path, format: str,
|
||||
start_pct: int, end_pct: int,
|
||||
):
|
||||
"""Run qemu-img convert in background and update job progress
|
||||
by polling the output file size against the source virtual size."""
|
||||
# Get source virtual size for percentage estimation
|
||||
info = _run(["qemu-img", "info", "--output=json", str(source_path)])
|
||||
source_size = json.loads(info.stdout).get("virtual-size", 0)
|
||||
if source_size == 0:
|
||||
source_size = source_path.stat().st_size
|
||||
|
||||
proc = subprocess.Popen(
|
||||
["qemu-img", "convert", "-f", format, "-O", "qcow2",
|
||||
str(source_path), str(output_path)],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
# Poll output file size and update progress
|
||||
while proc.poll() is None:
|
||||
time.sleep(2)
|
||||
if output_path.exists():
|
||||
current = output_path.stat().st_size
|
||||
if source_size > 0:
|
||||
ratio = min(current / source_size, 1.0)
|
||||
pct = start_pct + int(ratio * (end_pct - start_pct))
|
||||
gb_done = current / (1024**3)
|
||||
gb_total = source_size / (1024**3)
|
||||
_update_job(job_id, progress=pct,
|
||||
message=f"Converting disk... {gb_done:.1f} / {gb_total:.1f} GiB")
|
||||
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(f"qemu-img convert failed (rc={proc.returncode})")
|
||||
|
||||
_update_job(job_id, progress=end_pct, message="Disk converted.")
|
||||
|
||||
|
||||
def _process_job(job_id: str, req: JobSubmissionRequest):
|
||||
try:
|
||||
vmid = req.vmid
|
||||
|
|
@ -171,13 +208,11 @@ def _process_job(job_id: str, req: JobSubmissionRequest):
|
|||
logger.info("Disk %d: source=%s format=%s size=%.1f GiB",
|
||||
idx, source_path, disk.format, disk.size_gb)
|
||||
|
||||
_run(["qemu-img", "convert",
|
||||
"-f", disk.format,
|
||||
"-O", "qcow2",
|
||||
str(source_path), str(output_path)],
|
||||
timeout=7200)
|
||||
_update_job(job_id, progress=20 + (idx + 1) * 15,
|
||||
message=f"Converted disk {idx + 1}/{len(disk_specs)}")
|
||||
# Convert with real-time progress
|
||||
_convert_with_progress(
|
||||
job_id, source_path, output_path, disk.format,
|
||||
start_pct=10 + idx * 10, end_pct=55 + idx * 10,
|
||||
)
|
||||
else:
|
||||
size_gb = spec.size_gb or 32
|
||||
logger.info("Creating empty disk %d: %d GiB", idx, size_gb)
|
||||
|
|
@ -204,7 +239,7 @@ def _process_job(job_id: str, req: JobSubmissionRequest):
|
|||
target_gb = int(round(current_gb)) + 1
|
||||
|
||||
if target_gb < current_gb:
|
||||
_update_job(job_id, progress=65,
|
||||
_update_job(job_id, progress=60,
|
||||
message=f"Shrinking disk to {target_gb} GiB...")
|
||||
logger.info("Shrinking disk: %.1f GiB → %d GiB via virt-resize", current_gb, target_gb)
|
||||
try:
|
||||
|
|
@ -276,6 +311,7 @@ def _process_job(job_id: str, req: JobSubmissionRequest):
|
|||
f"{req.target_storage}:0,format=raw,size=4M"])
|
||||
|
||||
# ── Finalise ───────────────────────────────────────────────
|
||||
_update_job(job_id, progress=95, message="Configuring VM...")
|
||||
_run(["qm", "set", str(vmid), "--boot", "order=scsi0"])
|
||||
_run(["qm", "set", str(vmid), "--serial0", "socket"])
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue