- 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
- Backend: None = keep current size (no auto-shrink); only resize when value explicitly provided
- Frontend: Target Disk Size field shows detected size, disabled (not submitted)
- Checkbox 'Enable disk resize' unlocks the field for editing
- Storage pool discovery now uses pvesm status (reliable) with Custom... option
- discover_all_disks returns all found disk images sorted by size
- Analysis shows all disks with radio button for boot disk selection
- Confirm form passes boot disk + additional disks to job payload
- Hero header at top describing the app
- Two side-by-side cards: Download (URL) and SCP Pull
- Resume sessions section appears above cards when sessions exist
- Download card expands to inline form
- Resume asks for VM Name/ID only, skips URL field
- Clean separation of entry points
- GET /api/v1/sessions scans tmp/ for session dirs with staged files
- Index page shows resumable sessions with filenames and sizes
- Resume button skips download and starts analysis with stored session_id
- session_id carried through analysis → confirm → job for correct file paths
- New GET /api/v1/storage/pools returns available lvmthin/zfspool/rbd/dir pools
- Analysis result template now shows dropdown when >1 pool available
- Falls back to text input with single pool or on discovery failure
- Backend /api/v1/analyze now returns 202 with analysis_id immediately
- New GET /api/v1/analyze/{id} for polling analysis status
- Background thread handles extraction, disk discovery, OS/EFI detection
- Nested archive extraction: handles chained zips and split zips (.z01-.zNN)
- Frontend polls /session/analyze/status/{id} every 2s until complete
- SCP page now has complete confirm form flow with job polling
1. Removed qemu-utils from required packages — Proxmox ships its own
qemu-img and installing Debian's version breaks VMs. Added preflight
check: if qemu-img missing, exits with clear error message.
2. Installer now prompts for host storage path (default:
/mnt/pve/hosted-thin). Uses it for the LXC bind mount mp0.
The LXC always sees /mnt/converter internally — no symlinks needed.
Deletes intermediate files as conversion progresses:
- Archive (.7z/.zip) after extraction
- Source VMDK after qemu-img convert
- Local QCOW2 after qm disk import
Reduces peak storage from zip+VMDK+QCOW2 to just the largest
single file — critical for the 94 GB rootfs with 87 GB images.
Runs 'ssh stat -c%s' or 'ls -l' to get total bytes before starting
SCP transfer. Progress bar now shows real percentage and ETA
instead of indeterminate spinner.
Replaces flat 30 GB default with proportional sizing:
- 500 GB → 50 GB, 200 GB → 20 GB, 80 GB → 20 GB
- Less aggressive for large disks, safer for GRUB boot
- Update UI hint and README to reflect new rule
Replaces wget with aria2c -x8 -s8 for significantly faster
downloads, especially on high-latency connections. Falls back to
single-connection if the server doesn't support range requests.
- Backend cleanup_staging accepts session_id, deletes whole
STAGING_ROOT/{guid}/ dir when provided
- CleanupRequest model gets session_id field
- Frontend cleanup proxy passes session_id from body
- api_client passes session_id to backend
- reuseImage JS includes session_id in cleanup payload
1. submit_job() was creating empty tmp/out/{vmid}/ dirs via the old
flat STAGING_OUT path. Removed — _process_job handles this via
_staging_out() in the session-aware path.
2. Copy (re-convert) flow was hardcoding boot_type=uefi. Now uses
auto_detect_boot=true with boot_type=legacy as fallback, matching
the behavior of the original analyze→convert flow.
extract_if_needed now extracts relative to the source file's parent
directory instead of /mnt/converter/tmp/in/. This correctly handles
files in tmp/{guid}/in/ paths.
Archives like Debian_13_VMG.7z contain nested single-child dirs:
Debian_13_VMG_LinuxVMImages.COM/Debian_13_VMG_LinuxVMImages.COM/
_flatten_nested() collapses these chains until files sit directly
in the parent, eliminating the double-nesting issue.
When files are stored in /mnt/converter/in/{guid}/, the backend
needs the full relative path. The analyze endpoint now constructs
'{guid}/{filename}' before passing to the backend API.
Each browser gets a UUID stored in localStorage, set as a cookie,
and attached to every API call. Files go to session-specific dirs:
/mnt/converter/in/{guid}/ — uploads, downloads, SCP pulls
/mnt/converter/out/{guid}/ — converted QCOW2s
Changes:
- base.html: generates UUID via crypto.randomUUID(), stores in
localStorage + cookie, exposes as window.VM_BENCH_SID
- Frontend: all endpoints accept session_id, _staging() helper
creates session-aware paths on demand
- JavaScript: session_id appended to all FormData, set as
X-Session-ID header on raw uploads
- Backend models: JobSubmissionRequest.session_id field added
- Provisioner: _staging_in/_staging_out helpers, source path
resolution uses session-aware directory
- Converter: extract_if_needed skips re-extraction if dir exists
When reusing a source image (Copy flow), the archive was already
extracted in /mnt/converter/in/. The extraction step failed with
'Destination path already exists' from 7z.
Now extract_if_needed checks if the expected output directory already
contains disk images, and returns it directly without re-extracting.