""" VM provisioning on the Proxmox host. This module is a stub — the real Proxmox integration (qm create, qm importdisk, qm set) runs on the backend host (srv2). Fill in the actual Proxmox commands when you deploy the backend to the host. """ from __future__ import annotations import subprocess import uuid import time from pathlib import Path from typing import Optional from models import ( JobSubmissionRequest, JobStatusResponse, JobStatus, ) STAGING_OUT = Path("/mnt/converter/out") def submit_job(req: JobSubmissionRequest) -> JobStatusResponse: """Submit a conversion + provisioning job. Returns initial status.""" job_id = f"job_{req.vmid}_{int(time.time())}" # Create output staging directory out_dir = STAGING_OUT / str(req.vmid) out_dir.mkdir(parents=True, exist_ok=True) # In the real backend, this would: # 1. Convert the source image to QCOW2 via qemu-img convert # 2. Resize if target_disk_size_gb is set # 3. Create the VM with qm create # 4. Import the disk: qm importdisk # 5. Configure boot: qm set --boot order=scsi0 --bios ovmf # 6. Auto-start if configured: qm set --onboot 1 # Stub: simulate a job being queued return JobStatusResponse( job_id=job_id, vmid=req.vmid, status=JobStatus.QUEUED, progress_percentage=0, message="Job queued for processing", error_details=None, ) def get_job_status(job_id: str) -> JobStatusResponse: """Query the current status of a job. In the real backend, this would check the actual conversion process (qemu-img progress, qm task status, etc.). For now, returns a simulated progressing status to demonstrate the flow. """ # Stub: simulate progress advancing parts = job_id.split("_") vmid = int(parts[1]) if len(parts) >= 2 else 0 ts = int(parts[2]) if len(parts) >= 3 else 0 elapsed = int(time.time()) - ts if elapsed < 5: progress = min(int(elapsed * 10), 40) status = JobStatus.PROCESSING_CONVERSION msg = "Converting disk image..." elif elapsed < 10: progress = 40 + min(int((elapsed - 5) * 10), 50) status = JobStatus.IMPORTING_STORAGE msg = "Importing disk to Proxmox storage..." else: progress = 100 status = JobStatus.COMPLETED msg = "VM created successfully" return JobStatusResponse( job_id=job_id, vmid=vmid, status=status, progress_percentage=progress, message=msg, error_details=None, ) def cleanup_staging(vmid: int, delete: bool) -> dict: """Remove staging files for a VM ID. Returns action details.""" in_dir = Path("/mnt/converter/in") out_dir = STAGING_OUT / str(vmid) if not delete: return { "job_id": f"job_{vmid}", "action_taken": "retained", "message": f"Staging files preserved for VM {vmid}", } # Remove extracted source files in /mnt/converter/in for this vmid # (In production, you'd track which files belong to which job) cleaned_in = False cleaned_out = False if out_dir.exists(): import shutil shutil.rmtree(out_dir, ignore_errors=True) cleaned_out = True return { "job_id": f"job_{vmid}", "action_taken": "purged", "message": f"Staging files deleted for VM {vmid}", }