# Fase D — Supporto ESXi per le Build VM (futuro) > **Stato**: **piano differito**, da rivalutare dopo che Fase A > ([rewrite Python](idea-1-python-rewrite.md)), Fase B > ([host Linux](idea-2-linux-host.md)) e Fase C > ([eliminazione PowerShell](idea-3-powershell-removal.md)) sono stabili > in produzione. Vedi [overview](ideas-overview.md). > > Questo documento esiste per: > 1. Garantire che il design di Fase A abbia gli hook corretti > (astrazione `VmBackend`). > 2. Avere un piano pronto se/quando si decide di scalare oltre i limiti > dell'host singolo Workstation Pro. --- ## 1. Quando attivare Fase C Fase C ha senso **solo se** si verifica almeno uno di: - Saturazione della concorrenza locale (>4 job paralleli regolarmente) - Necessità di disaccoppiare hardware CI control plane (ufficio) dal hardware build (rack/datacenter) - Disponibilità di hardware ESXi + licenza vSphere Essentials o superiore - Richiesta di QoS / resource pool per i job CI vs altri carichi Se nessuno è vero a 6-12 mesi dal completamento Fase B, **non aprire Fase C**: Workstation Pro su Linux copre il caso d'uso e l'overhead operativo di un secondo backend non è giustificato. --- ## 2. Architettura prevista ``` ┌──────────────┐ ┌─────────────────────────────┐ │ Gitea │ │ ESXi host(s) │ │ act_runner │ │ ├─ Template Windows (snap BaseClean) │ │ (Linux Mint) │ │ ├─ Template Linux (snap BaseClean-Linux) │ │ │ │ └─ Datastore: ci-builds │ └──────┬───────┘ └──────────▲──────────────────┘ │ │ vSphere API (443) ▼ │ ┌──────────────────┐ │ │ ci_orchestrator │───────────┘ │ (Python, Linux) │ │ ↳ EsxiBackend │ └─────┬────────────┘ │ WinRM/SSH ▼ Build VM efimera su ESXi ``` L'host CI Linux (Fase B) continua a esistere come control plane. Le build VM girano su ESXi remoto. Il transport WinRM/SSH al guest non cambia. --- ## 3. Implementazione: nuovo backend pyVmomi Sfruttando l'astrazione `VmBackend` introdotta in Fase A: ```python # src/ci_orchestrator/backends/esxi.py class EsxiBackend(VmBackend): def __init__(self, host: str, user: str, password: str, datastore: str, folder: str): ... def clone_linked(self, template: str, snapshot: str, name: str) -> VmHandle: ... def start(self, vm: VmHandle) -> None: ... def stop(self, vm: VmHandle, hard: bool = True) -> None: ... def delete(self, vm: VmHandle) -> None: ... def get_ip(self, vm: VmHandle, timeout: float) -> str: ... ``` Selezione backend via env / config: ```toml [backend] type = "esxi" # oppure "workstation" [backend.esxi] host = "esxi.lab.local" user = "ci-runner@vsphere.local" datastore = "ci-builds" folder = "ci" ``` --- ## 4. Mappa operazioni Workstation → ESXi | Operazione | Workstation (oggi) | ESXi (Fase C, pyVmomi) | | ----------------------------- | --------------------------------------------- | ----------------------------------------------------------------------- | | Linked clone | `vmrun clone ... linked -snapshot=BaseClean` | `vim.VirtualMachineCloneSpec(linkedClone=True, snapshot=ref)` | | Start VM | `vmrun start ` | `VirtualMachine.PowerOn()` (Task) | | Stop VM | `vmrun stop hard` | `VirtualMachine.PowerOff()` (Task) | | Delete VM | `vmrun deleteVM ` | `VirtualMachine.Destroy_Task()` | | List snapshots | `vmrun listSnapshots` | `VirtualMachine.snapshot.rootSnapshotList` | | Get guest IP | `vmrun getGuestIPAddress` | `VirtualMachine.guest.ipAddress` (richiede VMware Tools nei guest) | | Test running | `vmrun list` | `VirtualMachine.runtime.powerState == 'poweredOn'` | --- ## 5. Setup ESXi richiesto (Step C0) Prerequisiti hardware/licenza da verificare PRIMA di pianificare: - **Licenza**: ESXi free **non basta** (le API write sono bloccate dal 2020). Serve almeno **vSphere Essentials** o licenza valutativa rinnovabile. - **Hardware**: server con CPU che supporti la versione ESXi target, RAM ≥64 GB, NVMe locale per datastore. - **Network**: portgroup dedicato per le build VM, DHCP raggiungibile. - **Account**: user dedicato con ruolo custom (datastore, folder, snapshot, power, network — minimo set richiesto da `clone_linked`/`destroy`). --- ## 6. Sequenza implementativa ### C1 — Validazione ambiente - Setup hardware ESXi + licenza - Account vSphere dedicato - Test manuale clone+start+stop via UI vSphere - Misurazione tempi di clone (baseline) ### C2 — Implementazione `EsxiBackend` - `pip install pyvmomi` aggiunto al `pyproject.toml` - `src/ci_orchestrator/backends/esxi.py` implementato - Test pytest con `pyvmomi` mock + un test di integrazione opzionale che gira solo se env `CI_TEST_ESXI_HOST` è settata ### C3 — Migrazione template - Export OVF dei template Workstation: `ovftool ... template.vmx template.ovf` - Import su datastore ESXi: `ovftool template.ovf vi://...` - Verifica WinRM/SSH raggiungibili dal control plane verso le VM ESXi - Snapshot `BaseClean` / `BaseClean-Linux` ricreati lato ESXi ### C4 — Canary deploy - Workflow di test dedicato con label `windows-build:esxi` o config override `backend=esxi` - Eseguito quotidianamente per ≥1 settimana - Confronto metriche (tempo totale, success rate) vs Workstation backend ### C5 — Rollout selettivo - Workflow di produzione spostati uno a uno su backend ESXi - Monitoring tempi via `Measure-CIBenchmark` equivalente Python - Workstation backend resta disponibile come fallback (basta cambiare config) ### C6 — Adattamenti tooling secondari - `Cleanup-OrphanedBuildVMs` esteso per query ESXi folder-scoped - `Watch-DiskSpace` esteso a query datastore via API - `Backup-CITemplate` sostituito da snapshot ESXi nativi o `ovftool` schedulato --- ## 7. Rischi specifici Fase C | Rischio | Severità | Mitigazione | | -------------------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------- | | Licenza ESXi free blocca API write (`CloneVM_Task` non autorizzata) | **Critica** | Verificare licenza in C1 prima di qualsiasi sviluppo. Procurare vSphere Essentials. | | Linked clone su ESXi standalone (no vCenter) ha gap funzionali | Alta | PoC manuale in C1; se gap rilevante, valutare full clone vs linked. | | Tempo clone+poweron+IP > Workstation locale per latenza rete + overhead vSphere | Media | Misurare in C1; possibile mitigazione: pool di VM "warm" pre-clonate. | | VMware Tools nel guest non sempre rapide a riportare IP via API | Bassa | Stesso problema oggi (`AGENTS.md` errore #10). Fallback: parse DHCP lease su ESXi via API. | | Concorrenza: race condition su naming VM o folder | Media | Lock per JobId lato orchestrator; suffisso UUID nel nome VM. | | Cleanup VM orfane più costoso (API call per ogni VM in folder) | Bassa | Riscrivere cleanup come query folder-scoped, non scan globale. | | Single point of failure: ESXi giù = tutta la CI giù | Media | Documentare procedura fallback su backend Workstation locale (basta cambio config). | | Errore #11 `AGENTS.md` (machine-id identico clone Linux) si ripropone | Bassa | Stessa fix lato template, già documentata. | --- ## 8. Definizione di "fatto" (Fase C) - [ ] Hardware ESXi + licenza Essentials operativi - [ ] `EsxiBackend` implementato, testato (unit + integration test) - [ ] Template Windows e Linux importati e funzionanti su ESXi con snapshot - [ ] Almeno 1 workflow di produzione in canary su backend ESXi per ≥1 settimana senza regressioni - [ ] Documentazione (`docs/HOST-SETUP.md`, `docs/ARCHITECTURE.md`, `AGENTS.md`) aggiornata con istruzioni multi-backend - [ ] Procedura di rollback su backend Workstation testata e documentata