# Postmortem — Template Linux: deploy/prepare "no IP" (2026-06-06) ## Sintesi Durante l'aggiunta di dipendenze GUI al toolchain del template Linux (`python3-gi gir1.2-gtk-4.0 xvfb`), una serie di fallimenti ha aperto una lunga sessione di debug. Sono emersi **due problemi distinti**, più una causa ambientale che ha confuso la diagnosi: 1. **Origine reale della sessione** — `prepare-linux` falliva allo Step 4a con `Unable to locate package gir1.2-gtk-4.0`. Causa: `--skip-update` saltava anche `apt-get update`, lasciando l'indice apt vecchio. **Non c'entrava la rete.** Fix banale, applicato. 2. **Bug `deploy-linux --force`** — il resize allo Step 4 falliva con *"disk is part of a snapshot chain"* perché `--force` riusava la directory senza rimuovere la snapshot chain precedente. Bug deterministico, colpito due volte. Fix applicato. 3. **Saga "no IP" (ambientale)** — il rilevamento IP via open-vm-tools andava in timeout in modo **intermittente**. Causa finale: il servizio DHCP/NAT di vmnet8 sull'host si era **inceppato** a causa della churn di debug (decine di `vmrun start/stop`, `tcpdump` promiscuo, DISCOVER ripetuti del guest). Risolto con un **restart di `vmware-networks`**. Non è un bug del codice né del CI normale. Stato finale: template `BaseClean-Linux` ricostruito da powered-off, con gtk/xvfb e cloud-init sigillato. Tutti i test passano (coverage 94.4%). --- ## Cronologia | Ora (host) | Evento | |-----------|--------| | ~10:50 | `prepare-linux --skip-update` → fallisce allo Step 4a (`gir1.2-gtk-4.0` non trovato) per indice apt stale | | ~11:00 | Dopo fix apt + revert snapshot: `prepare-linux` riesce, guest ottiene IP `.153`, snapshot ripreso | | ~11:03+ | I boot successivi dello snapshot non ottengono IP utile → inizio debug rete | | 11:20 | Cattura sul filo: DHCP OFFER/ACK presenti, ma il guest non applica l'IPv4 | | 12:06 | Deploy fresco: il guest **ottiene** `.143` (cloud-init), ma deploy Step 7 va in timeout sui tools | | 12:26 | Reboot: stesso guest **non** ottiene IP (intermittenza) | | ~12:30 | `vmware-networks --stop/--start` → l'IP torna stabile. Sblocco confermato | | dopo | Implementati e testati i fix; template rigenerato | --- ## Analisi cause ### 2.1 Step 4a apt (origine della sessione) `Install-CIToolchain-Linux2404.sh` faceva `apt-get update` **solo** se non era passato `--skip-update`. Ma `--skip-update` dovrebbe saltare il solo *upgrade* (lento), non il refresh dell'indice. Con indice vecchio, i pacchetti nuovi (`gir1.2-gtk-4.0`, `xvfb`) non venivano trovati. A riprova che a quel punto la rete funzionava: quel `prepare-linux` era entrato in SSH nel guest senza problemi. ### 2.2 deploy-linux --force e la snapshot chain `deploy-linux --force` riusa la directory della VM ma non rimuoveva gli artefatti di un'eventuale snapshot precedente (`-NNNNNN.vmdk`, `.vmsn`, `.vmsd`). Con la chain presente, `vmware-vdiskmanager` rifiuta il resize dello Step 4: ``` This disk is part of a snapshot chain in '.../LinuxBuild2404.vmx'. The selected operation can only be executed on a disk with no snapshots. ``` ### 2.3 La saga "no IP" — vmnet8 DHCP inceppato Sintomo: `vmrun getGuestIPAddress` → *"VMware Tools are not running"* / *"Unable to get the IP address"*, **in modo intermittente**. Il guest in realtà: - bootava correttamente (login prompt), - aveva open-vm-tools installato e attivo, - ma `systemd-networkd` a volte **non applicava** un lease DHCPv4 (link up, solo IPv6LL, `networkd-wait-online` in timeout). Sul filo il DHCP a volte completava (`.143` applicato e raggiungibile), a volte no. Il server rispondeva (altre VM ottenevano lease). La causa finale: il demone `vmnet-dhcpd`/`vmnet-natd` di vmnet8 era andato in uno stato degradato dopo la churn di debug. Il **restart** `vmware-networks --stop/--start` lo ha sbloccato e l'IP è tornato stabile e immediato. #### Ipotesi scartate (vicoli ciechi) - **Controller pvscsi vs lsilogic** — il boot funzionava con pvscsi; irrilevante. - **Naming NIC `ens192` vs `ens160`** — cloud-init si adattava; l'IP arrivava lo stesso quando la rete funzionava. - **Saturazione del pool DHCP** — il pool era di fatto libero (0 lease attivi); i MAC multipli per IP nel lease DB sono storia normale di ISC dhcpd. - **Seal cloud-init / machine-id** — difensivi ma non erano la causa del no-IP. --- ## Fix applicati (branch `fix/linux-template-toolchain-deploy`) ### Commit 1 — toolchain `template/Install-CIToolchain-Linux2404.sh` - **Step 4a**: installa `python3-gi gir1.2-gtk-4.0 xvfb` con assert su `import gi` e `Xvfb` (richiesta originale). - **Step 1**: `apt-get update` ora gira **sempre**; `--skip-update` salta solo l'upgrade. Risolve il fallimento Step 4a. ### Commit 2 — robustezza deploy/prepare `src/ci_orchestrator/commands/template.py` (+ test) - **`_clean_force_artifacts`**: con `--force` rimuove snapshot delta, `.vmsn`, `.vmsd` e `.lck` prima del resize → niente più errore snapshot-chain. Coperto da 3 unit test. - **prepare-linux Step 7d (seal cloud-init)**: rimuove la `50-cloud-init.yaml` stale, rigenera netplan (resta solo il `99-ci-dhcp-allnics` MAC-agnostic), disabilita cloud-init (`/etc/cloud/cloud-init.disabled`) così non rigira sui cloni. - **deploy user-data**: `package_update/upgrade: false` per non tenere il lock dpkg al primo boot e non ritardare open-vm-tools oltre il poll dello Step 7. - **Step 7 timeout**: `300s → 600s` come rete di sicurezza per primi boot lenti. Verifica: `ruff` ✅, `mypy --strict` (23 file) ✅, suite completa ✅, coverage **94.4%** (gate 90%). Prod venv reinstallato (non-editable). --- ## Procedura di recovery: deploy/prepare non ottiene IP Quando `deploy-linux` (Step 7) o `prepare-linux` non riescono a rilevare l'IP **ma il guest sembra sano** (booting, tools attivi), prima di sospettare immagine o codice, riavvia il networking VMware sull'host: ```bash sudo /usr/bin/vmware-networks --stop sudo /usr/bin/vmware-networks --start ``` Flush opzionale dei lease (backup prima), **solo se nessuna VM è accesa**: ```bash sudo cp /etc/vmware/vmnet8/dhcpd/dhcpd.leases /tmp/dhcpd.leases.bak sudo sh -c ': > /etc/vmware/vmnet8/dhcpd/dhcpd.leases' ``` Distinto da: - **Tools-not-running sul template** → revert a `BaseClean-Linux` (vedi `template-live-state-recovery`). - **vmmon/vmnet non compilati dopo bump kernel** → il guard systemd ricostruisce i moduli al boot (vedi `vmware-modules-kernel-upgrade`). --- ## Raccomandazioni (NON applicate di proposito) ### Cosa NON fare - **Restart/flush automatico ad ogni clone/deploy/prepare.** `vmware-networks` è globale (bridge + hostonly + NAT insieme): un restart sotto un job in corso rompe la rete di **tutte** le VM in esecuzione → job concorrenti falliti. Il flush dei lease con VM attive causa conflitti IP al rinnovo. Sproporzionato rispetto a un problema non dimostrato ricorrente. ### Cosa fare - **Monitorare.** L'inceppamento è nato dalla churn anomala di debug, non dal CI normale. Tenere la recovery manuale e agire solo se ricapita con dati reali. ### Se servisse hardening preventivo (a freddo, con cautela) - Abbassare `max-lease-time` (i cloni vivono minuti → IP liberati in fretta; i job lunghi rinnovano da soli via networkd). - Allargare il range DHCP per più margine. - **Caveat**: `/etc/vmware/vmnet8/dhcpd/dhcpd.conf` è **auto-generato da VMware** ("DO NOT MODIFY", validato con `CFG_HASH` in `/etc/vmware/networking`). Edit a mano fragili: rischio rigenerazione al prossimo `--configure`/update. Da fare solo se il problema si dimostra ricorrente. --- ## Lezioni - L'attività di debug intensa sulla rete host (start/stop ripetuti, sniffing promiscuo) può degradare `vmnet-dhcpd`: in futuro, minimizzare la churn e preferire osservazione mirata. - Tre cause distinte si sono sovrapposte; separarle prima ha richiesto tempo. Il guest era fondamentalmente sano per quasi tutta la sessione — l'errore di rilevamento IP non implicava un guest rotto. - Non sovrastimare: i fix committati coprono i bug **veri e provati**; il DHCP è stato lasciato invariato per evitare over-engineering.