Commit graph

29 commits

Author SHA1 Message Date
837e674195 feat: SCP Pull page for large files from remote servers
New /scp page with form for host, user, password, remote path.
Runs scp via sshpass in background with progress polling.
Files go to /mnt/converter/in/{session_id}/ for multi-user isolation.

- Added sshpass + openssh-client to frontend installer
- scp.html template with full form + progress bar
- POST /scp/start — launches background SCP via sshpass -e
- GET /scp/progress/{sid}/{file} — polls file size + speed
- 'SCP Pull (+10 GB)' button on start page
2026-07-22 05:52:01 +00:00
1183059d8b fix: add no-cache headers + remove debug console.logs
- Cache-Control/Pragma/Expires headers prevent browser caching
  of JS-heavy pages (fixes stale code after updates)
- Removed debug console.log statements from upload handlers
2026-07-22 05:24:21 +00:00
6519fc1cf7 fix: use permanent DOM elements for progress instead of innerHTML reset
innerHTML reset in startSession was destroying #phase-label and
#progress-fill elements, then recreating them with same IDs. XHR
progress events fired asynchronously but getElementById might
return stale references or fail during the DOM update window.

Now #phase-label, #progress-fill, and #session-error are permanent
elements in the static HTML. startSession clears them via
textContent/style instead of innerHTML — no DOM destruction.
showError appends to #session-error instead of #session-status.
2026-07-22 05:19:13 +00:00
d8d301a13f debug: add request-logging middleware + make upload handler sync
- HTTP middleware logs every incoming request (method, path, body size)
  BEFORE handler runs — catches silent failures at the network/parser layer
- Changed upload handler from async to sync so FastAPI runs it
  in a thread pool, preventing event loop blockage during large
  file reads
2026-07-22 05:12:21 +00:00
f032c546e1 debug: add console.log to upload handlers to diagnose missing progress 2026-07-21 19:46:08 +00:00
5ff0635aa5 fix: show upload phase immediately + fallback for non-computable progress
Added loadstart handler to show 'Uploading...' spinner as soon as
upload begins, plus fallback label when lengthComputable is false
(shows bytes uploaded even without percentage).
2026-07-21 19:43:04 +00:00
5174939939 fix: add download progress/completion logging to frontend
Uploads already logged start, every-1-GiB progress, and completion.
Downloads only logged start — now also log:
- Completion with file size (Download complete: file.7z (1.9 GiB))
- Failure with wget exit code
- Periodic progress every ~1 GiB with speed and ETA
2026-07-21 19:40:03 +00:00
6ba7944bb2 fix: add unrar-free, use 7z for .zip extraction (more reliable)
- Added unrar-free package to backend installer
- Added .rar to archive extension detection
- Changed .zip extraction from unzip → 7z (x -y)
  7z handles .zip, .7z, and other formats with one tool
- Added unrar handler for .rar files
2026-07-21 19:37:27 +00:00
5e786533b1 fix: add /usr/sbin to PATH in service files (qm, virt-resize, etc.)
Proxmox tools (qm, pct) and libguestfs tools (virt-resize,
guestfish) live in /usr/sbin which wasn't in the service PATH.
2026-07-21 19:24:13 +00:00
9a6d2df78b 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
2026-07-21 19:14:32 +00:00
affcde7afb fix: provisioner now extracts archives and discovers disks before conversion
The job submission receives the original filename (e.g. Debian_13_VMG.7z)
but qemu-img convert needs the actual VMDK path inside the archive.
Now reuse extract_if_needed() + discover_disk() from converter module
to locate the real disk image before conversion.
2026-07-21 19:11:19 +00:00
56131d6346 feat: real Proxmox VM provisioning — qemu-img, virt-resize, qm commands
Replace stub provisioner with real Proxmox integration:

- qemu-img convert: source → QCOW2 in /mnt/converter/out/{vmid}/
- virt-resize --shrink --resize-force: auto-shrink >30 GiB disks
- qm create: VM with cores, RAM, network, SCSI controller
- qm importdisk: import each QCOW2 into Proxmox storage
- qm set: attach disks, configure boot (OVMF/SeaBIOS), EFI disk, serial
- Auto-destroy existing VM with same ID before recreating
- Background thread execution with in-memory status tracking
- Full logging throughout the conversion pipeline
2026-07-21 19:05:07 +00:00
7808b16946 fix: prevent double-nested directory on archive extraction
Archives like 64bit.7z that already contain a 64bit/ top-level dir
were extracted into STAGING_IN/64bit/, creating 64bit/64bit/ nesting.

Now extract into STAGING_IN root, then:
- If the archive produced a single dir matching the stem, use it
- Otherwise, collect scattered files into a stem-named subdirectory

Removes the need for nested path: 64bit/Debian.vmdk instead of
64bit/64bit/Debian.vmdk
2026-07-21 18:58:13 +00:00
0e7f8c81f3 feat: shared file-based logging to /mnt/converter/logs/
Both frontend and backend now write rotating log files to
/mnt/converter/logs/ (shared between host and LXC):

  /mnt/converter/logs/vm-bench.log          (frontend)
  /mnt/converter/logs/vm-bench-backend.log   (backend)

- RotatingFileHandler: 10 MB per file, 5 backups
- Console handler still writes to systemd journal
- Logs/ directory is gitignored and auto-created on startup
- Install script creates logs/ directory
2026-07-21 18:52:58 +00:00
42d3cdf1fe fix: add logging to backend — analyze, job submit, status, cleanup
Backend had zero logging. Frontend logs were fine (logging.basicConfig
in app.py). Now both services write structured logs to systemd journal:

  journalctl -u vm-bench -f            # frontend (LXC)
  journalctl -u vm-bench-backend -f    # backend (srv2)
2026-07-21 18:46:12 +00:00
989807f29e fix: innerHTML script execution bug — confirm form and polling now work
Root cause: innerHTML does not execute <script> tags in modern browsers.
The _analysis.html and polling.html fragments had inline <script> blocks
that were silently dropped, causing:
- Confirm form's submitJob() undefined → form submitted as normal GET
  back to '/' (user saw 'New Conversion Session' instead of polling)
- Polling page's poll() never started → stuck on 'Waiting for backend...'

Fix:
- Stripped all <script> tags from _analysis.html and polling.html
- Moved all JS logic into index.html (executed on page load)
- initConfirmForm() attaches submit/change listeners after analysis HTML
  is injected via innerHTML
- initPolling() attaches poll timer + reuse button handlers after
  polling fragment is injected
- Polling fragment replaces step1/analysis content while keeping
  base.html header/footer intact
- Reuse buttons now use onclick attached via JS (not inline)
2026-07-21 18:43:16 +00:00
267b64ec1b docs: explain disk sizing, virtual vs actual size, and auto-shrink behavior 2026-07-21 18:35:15 +00:00
ebe175afcf fix: bump analyze timeout to 300s for 87 GB+ disk images 2026-07-21 18:26:57 +00:00
71792756ea fix: increase analyze API timeout to 120s for large disk probing
guestfish needs more than 30s to probe a 7.5 GiB VMDK image
(mount filesystem, read /etc/os-release, check EFI partition).
Other endpoints keep the 30s default.
2026-07-21 18:24:26 +00:00
10199f896b fix: escape f-string braces in backend route decorators
{job_id} in f-strings was interpreted as Python variable instead
of FastAPI path parameter. Double braces {{job_id}} produce
literal {job_id} in the route string.
2026-07-21 18:17:41 +00:00
af26da7b64 feat: two-phase upload/download with real-time progress + smart speed timeout
Split monolithic /session/start into two endpoints:
- POST /session/upload  — phase 1: file acquisition only, returns JSON
- POST /session/analyze — phase 2: backend analysis, returns HTML
- GET  /session/progress/{filename} — poll download progress

Uploads:
- Browser-native progress bar via XMLHttpRequest (real % + GiB)

Downloads:
- wget runs in background (Popen), frontend polls /session/progress
- HEAD request gets Content-Length before download starts
- Real-time speed (MB/s) and ETA displayed in the UI
- Smart timeout: after 30s, if ETA > 1 hour, kills download and
  suggests manual download to laptop + File Upload instead
- Absolute safety net at 4 hours

UI: clear phase transitions — 'Downloading... 2.3 GiB (4.5 MB/s) ~12 min'
→ 'Step 2/2: Analysing source image...' → result
2026-07-21 18:10:52 +00:00
509eb97b57 fix: large-file support + user-defined VM name
Large-file handling:
- Set TMPDIR=/mnt/converter/in in service to spool uploads to
  shared storage instead of 24 GB LXC rootfs (critical for >24GB)
- Chunked upload streaming (8 MiB) with progress logging every 1 GiB
- Pre-flight disk space check via Content-Length header
- Clean up partial files on upload/download failure
- Download timeout extended to 7200s (2 hours) for 88 GB images
- Switched wget from --show-progress to --progress=dot:giga
  (compact output, won't fill memory on large transfers)
- uvicorn --timeout-keep-alive 300 on both frontend and backend

VM name:
- Added vm_name field to initial session form (step 1)
- Falls back to auto-generated 'os_type-vmid' if left blank
- Pre-filled & editable in confirm form (step 2)
2026-07-21 17:59:31 +00:00
4b50b185fa feat: full deployment scripts — backend + LXC creation, frontend installer
backend/install.sh (run on srv2):
- Creates /mnt/converter directory tree, clones/updates git repo
- Installs 10 system packages (qemu-img, guestfish, archive tools)
- Installs Python deps + vm-bench-backend systemd service
- Interactive LXC creation: prompts for container ID, IP, gateway,
  bridge, MAC (all with defaults)
- Creates vm-bench LXC: 3 cores, 6GB RAM, 24GB rootfs, nesting,
  AppArmor unconfined, cgroup access, bind mount /mnt/converter

frontend/install.sh (run inside LXC):
- Installs python3, pip3, wget, curl
- Installs Python deps (fastapi, uvicorn, jinja2, etc.)
- Verifies backend connectivity, installs vm-bench systemd service

frontend/vm-bench.service: systemd unit bundled in repo

README: replaced Prerequisites/Installation with full Deployment
section covering both steps, container config table, and re-deploy
2026-07-21 17:51:53 +00:00
c887ccd5a3 fix: uniform /api/v1/ prefix, real EFI detection, spec alignment
Backend:
- All routes now consistently under /api/v1/ (health, analyze, jobs)
- Replace dummy EFI heuristic with actual guestfish-based detect_efi()
- Add path traversal validation on analyze source_filename
- Use typed response models (HealthResponse, CleanupResponse) everywhere
- Clean up unused imports (Path, ErrorResponse)

Models:
- Align AnalyzeResponse with open-api.yaml (add vmid, remove bootable/error,
  make efi_detectable nullable, deduplicate CleanupResponse)

Converter:
- Add detect_efi(disk_path) using guestfish list-filesystems

Frontend:
- Update api_client paths to /api/v1/health and /api/v1/analyze
- Remove unsupported 'network' field from job payload

Spec:
- Promote inline HealthResponse/CleanupResponse to named schemas
- Move /analyze and /health under /api/v1/ prefix

Docs:
- Add README.md with architecture, install, API reference, usage flow
2026-07-21 17:40:08 +00:00
ab61878fbc chore: restore open-api.yaml 2026-07-21 16:50:49 +00:00
6bcb68c552 chore: VM ID range 21000-21100, network default vmbr0, target_storage default local-lvm 2026-07-21 16:50:40 +00:00
be756e33bf chore: add install.sh for backend systemd setup 2026-07-21 15:01:02 +00:00
439b0c51a1 chore: add systemd service file for backend deployment 2026-07-21 14:56:02 +00:00
a8099f504c chore: initial commit — vm-bench frontend + backend 2026-07-21 14:53:29 +00:00