diff --git a/backend/converter.py b/backend/converter.py index 670989e..788f300 100644 --- a/backend/converter.py +++ b/backend/converter.py @@ -162,8 +162,14 @@ def _detect_archive_ext(name: str) -> Optional[str]: def discover_disk(source: Path) -> DiskInfo: """Walk the source directory and find the primary disk image.""" + return _pick_largest(discover_all_disks(source)) + + +def discover_all_disks(source: Path) -> list: + """Walk the source directory and return all disk images found, + sorted by size descending.""" if source.is_file(): - return _probe(source) + return [_probe(source)] candidates = [] for dirpath, _, filenames in os.walk(source): @@ -175,9 +181,14 @@ def discover_disk(source: Path) -> DiskInfo: if not candidates: raise FileNotFoundError(f"No disk image found in {source}") - # Pick largest as primary candidates.sort(key=lambda p: p.stat().st_size, reverse=True) - return _probe(candidates[0]) + return [_probe(p) for p in candidates] + + +def _pick_largest(disks: list) -> DiskInfo: + if not disks: + raise FileNotFoundError("No disks") + return disks[0] def _probe(disk_path: Path) -> DiskInfo: @@ -392,8 +403,10 @@ def _process_analysis(analysis_id: str, vmid: int, source_filename: str): source = _extract_nested(source) _update_analysis(analysis_id, "processing", "Discovering disk image...") - disk = discover_disk(source) + disks = discover_all_disks(source) + disk = disks[0] logger.info("Disk found: %s (format=%s, size=%.1f GiB)", disk.path, disk.format, disk.size_gb) + logger.info("Total disks discovered: %d", len(disks)) _update_analysis(analysis_id, "processing", "Detecting OS and boot type...") extract_dir = source if source.is_dir() else source.parent @@ -402,6 +415,13 @@ def _process_analysis(analysis_id: str, vmid: int, source_filename: str): logger.info("Analysis done: os=%s efi=%s", os_type, efi) from models import AnalyzeResponse + all_disks_data = [{ + "filename": d.path.name, + "format": d.format, + "size_gb": d.size_gb, + "size_bytes": d.size_bytes, + } for d in disks] + result = AnalyzeResponse( vmid=vmid, filename=disk.path.name, @@ -409,6 +429,7 @@ def _process_analysis(analysis_id: str, vmid: int, source_filename: str): disk_size_gb=disk.size_gb, os_type=os_type, efi_detectable=efi, + all_disks=all_disks_data, ) _update_analysis(analysis_id, "completed", "Analysis complete", result=result.model_dump()) diff --git a/backend/models.py b/backend/models.py index 603ee13..809eaec 100644 --- a/backend/models.py +++ b/backend/models.py @@ -106,6 +106,7 @@ class AnalyzeResponse(BaseModel): disk_size_gb: float os_type: Optional[str] = None efi_detectable: Optional[bool] = None + all_disks: list = [] # --------------------------------------------------------------------------- diff --git a/frontend/app.py b/frontend/app.py index 30ae6af..4eeb72f 100644 --- a/frontend/app.py +++ b/frontend/app.py @@ -398,13 +398,36 @@ async def confirm_session( auto_detect_boot: str = Form("true"), boot_type: str = Form("uefi"), session_id: str = Form(""), + has_multiple_disks: str = Form("0"), + boot_disk_index: str = Form("0"), ): """Build the job payload and submit to the backend.""" - # Validate VM ID range err = _validate_vmid(vmid) if err: return render("_analysis.html", request=request, error=err) + # Determine boot disk from multi-disk selection + boot_file = source_filename + boot_fmt = disk_format + + if has_multiple_disks == "1": + idx = int(boot_disk_index) if boot_disk_index.isdigit() else 0 + form_data = await request.form() + boot_file = form_data.get(f"disk_{idx}_file", source_filename) + boot_fmt = form_data.get(f"disk_{idx}_fmt", disk_format) + + additional_disks = [] + for i in range(10): + dfile = form_data.get(f"disk_{i}_file") + if dfile and i != idx: + additional_disks.append({ + "disk_type": "image_file", + "source_filename": dfile, + "format": form_data.get(f"disk_{i}_fmt", "vmdk"), + }) + else: + additional_disks = [] + payload = { "vmid": vmid, "vm_name": vm_name, @@ -416,9 +439,10 @@ async def confirm_session( "session_id": session_id, "boot_disk": { "disk_type": "image_file", - "source_filename": source_filename, - "format": disk_format, + "source_filename": boot_file, + "format": boot_fmt, }, + "additional_disks": additional_disks, } if target_disk_size_gb is not None: payload["target_disk_size_gb"] = target_disk_size_gb diff --git a/frontend/templates/_analysis.html b/frontend/templates/_analysis.html index 83896aa..4646fe5 100644 --- a/frontend/templates/_analysis.html +++ b/frontend/templates/_analysis.html @@ -25,6 +25,24 @@ EFI Bootable{{ 'Yes' if analysis.efi_detectable else 'Unknown' }} + {% if analysis.all_disks and analysis.all_disks|length > 1 %} +
+ + + {% for d in analysis.all_disks %} + + + + + + + {% endfor %} +
+ + {{ d.filename }}{{ d.format }}{{ d.size_gb }} GB
+
+ {% endif %} +

Is this correct? Configure the VM below and submit the conversion job.

@@ -32,6 +50,13 @@ + {% if analysis.all_disks and analysis.all_disks|length > 1 %} + + {% for d in analysis.all_disks %} + + + {% endfor %} + {% endif %}