diff --git a/TODO.md b/TODO.md index ced4ee2..ac8fab7 100644 --- a/TODO.md +++ b/TODO.md @@ -299,6 +299,19 @@ Cooldown: max 3 restart/h via JSON state file `F:\CI\State\runner-restart-log.js Oltre il limite: EventId 1004 (Error) + webhook `:sos:` senza restart. Webhook opzionale Discord/Gitea identico a `Watch-DiskSpace.ps1`. +### 2.8 [P2] [ ] IP-pool auto-riconciliazione vs clone reali — APERTO (2026-06-07) + +`ip-pool.json` non viene riconciliato contro i clone vivi all'avvio. Un job +killato mid-flight (SIGKILL/crash) **leaka il lease in modo permanente**; con +soli 4 IP (`192.168.79.201–204`) per parallelism 4, un singolo leak esaurisce +il pool e ogni round successivo fallisce con `IP pool exhausted after 60s`. +Emerso durante il burn-in B7 (RUNBOOK §9) — pool inquinato da lease del 25 mag +mai rilasciati. Workaround manuale: reset a `{}` con `build-vms/` vuoto. + +**Fix proposto**: all'allocazione (o in `vm cleanup`), rilasciare i lease il +cui clone-dir non esiste più in `CloneBaseDir`. Opzionale: allargare il pool +(es. 201–210) per dare margine oltre parallelism. Aggiungere test. + --- ## 3. Performance & throughput diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md index e8545f2..fc890eb 100644 --- a/docs/RUNBOOK.md +++ b/docs/RUNBOOK.md @@ -459,3 +459,47 @@ Ready column = WinRM/5986 TCP probe after IP known. - Destroy is slower than B6/B7 (~10 s vs ~5 s) — likely disk pressure or clone state at test time, unrelated to static IP. +--- + +## 9. Concurrent capacity burn-in (B7 — 4 × 10) + +Recorded 2026-06-07 — `Test-CapacityBurnIn.ps1 -Parallelism 4 -Rounds 10`, +Linux Mint host + VMware Workstation Pro Linux, run as `ci-runner`, static IP +pool (`192.168.79.201–204`). Repo `Simone/burnin-dummy`. Each "round" is the +wall-clock time for 4 concurrent end-to-end jobs (clone → boot → IP → +transport → build → artifacts → destroy). This is the first concurrent +capacity burn-in (the §6–§8 baselines are single-job `Measure-CIBenchmark`). + +| Template | Snapshot | Build cmd | Jobs PASS | Round time (min–max) | Round avg | +| ----------------- | ---------------- | --------------- | --------- | -------------------- | --------- | +| WinBuild2025 | `BaseClean` | `build.ps1` | 40 / 40 | 68–81 s | ~78.6 s | +| LinuxBuild2404 | `BaseClean-Linux`| `build.sh` | 40 / 40 | 70–71 s | ~70.2 s | + +**Result: OVERALL PASS** — 80/80 jobs, 20/20 rounds, zero orphaned clones, +zero leaked IP leases between rounds. + +**Key findings**: +- Per-round wall time is near-deterministic (Win σ ≈ 1.3 s, Linux σ ≈ 0.4 s), + confirming the static-IP pool eliminates the DHCP IP-acquire variance that + dominated the §6/§7 single-job baselines (σ 26–57 s). +- 4-way concurrency adds no contention penalty: round time ≈ single-job boot + total (§8 static ≈ 24 s) plus build + serialized destroy, well within the + 4-IP pool capacity. +- Linux rounds (~70 s) slightly faster and tighter than Windows (~79 s). +- IP-pool release-on-completion works under concurrency — pool returned to + all-free (`null`) after every round. + +**Operational note (pool not auto-reconciled)**: `ip-pool.json` is *not* +reconciled against live clones at startup. A job killed mid-flight (SIGKILL, +crash) leaks its lease permanently; with only 4 IPs for parallelism 4, a +single leak exhausts the pool and every subsequent round fails fast with +`IP pool exhausted after 60s`. Recovery: stop all jobs, confirm +`build-vms/` empty, then reset the pool: + +```bash +sudo -u ci-runner /opt/ci/venv/bin/python -c \ + "import pathlib; pathlib.Path('/var/lib/ci/ip-pool.json').write_text('{}\n')" +``` + +See `TODO.md` (IP-pool auto-reconciliation) for the proposed fix. + diff --git a/plans/PhaseB-user-checklist.md b/plans/PhaseB-user-checklist.md index a1837c6..d3c636a 100644 --- a/plans/PhaseB-user-checklist.md +++ b/plans/PhaseB-user-checklist.md @@ -452,30 +452,38 @@ coesistono mai. baseline Windows (entro ±20%). I due burn-in sono sequenziali (dual-boot: un OS alla volta). - > **Prerequisiti una-tantum** (come `simone`): - > ```bash - > sudo usermod -a -G ci-runner simone - > sudo chmod g+w /var/lib/ci/artifacts/ /var/lib/ci/build-vms/ - > mkdir -p ~/.local/share/python_keyring - > sudo cp /home/ci-runner/.local/share/python_keyring/keyring_pass.cfg \ - > ~/.local/share/python_keyring/ - > sudo chown simone: ~/.local/share/python_keyring/keyring_pass.cfg - > chmod 600 ~/.local/share/python_keyring/keyring_pass.cfg - > ``` - > Il repo `burnin-dummy` deve esistere in Gitea (`Simone/burnin-dummy`). - > Push iniziale (una-tantum): - > ```bash - > cd /opt/ci/local-ci-cd-system/gitea/burnin-dummy - > git init && git add . && git commit -m "initial" - > git remote add origin http://10.10.20.11:3100/Simone/burnin-dummy.git - > git push -u origin main - > ``` + > **Eseguire come `ci-runner`** (utente di produzione — ha già accesso + > a `/var/lib/ci/`, keyring e ip-pool; evita i perm-hack del path `simone`). + > Verificato 2026-06-07. + > + > **Prerequisiti una-tantum**: + > - Repo `burnin-dummy` esistente in Gitea (`Simone/burnin-dummy`, + > privato — l'auth del clone in-VM usa il PAT da keyring `GiteaPAT`). + > Deve contenere `build.ps1` + `build.sh`. Push iniziale: + > ```bash + > cd /opt/ci/local-ci-cd-system/gitea/burnin-dummy + > git init && git add . && git commit -m "initial" + > git remote add origin http://10.10.20.11:3100/Simone/burnin-dummy.git + > git push -u origin main + > ``` + > - **ip-pool pulito** prima di ogni burn-in: un run precedente killato + > può lasciare lease stale (il pool NON si auto-riconcilia, vedi + > RUNBOOK §9). Con `build-vms/` vuoto, resettare: + > ```bash + > sudo -u ci-runner /opt/ci/venv/bin/python -c \ + > "import pathlib; pathlib.Path('/var/lib/ci/ip-pool.json').write_text('{}\n')" + > ``` + > + > **(Path alternativo `simone`)**: se proprio si vuole girare come `simone`, + > oltre a group `ci-runner` + copia keyring serve anche + > `sudo chmod g+w /var/lib/ci` (la **root**, non solo + > `artifacts/`+`build-vms/` — `ip-pool.tmp` è scritto lì). Sconsigliato. -- [ ] **Avviato in Linux** — burn-in 4 job concorrenti × 10 round su - `WinBuild2025`: +- [x] **Avviato in Linux** — burn-in 4 job concorrenti × 10 round su + `WinBuild2025` (esito: 40/40 PASS, round ~78.6 s): ```bash - pwsh /opt/ci/local-ci-cd-system/scripts/Test-CapacityBurnIn.ps1 \ + sudo -u ci-runner -H pwsh /opt/ci/local-ci-cd-system/scripts/Test-CapacityBurnIn.ps1 \ -Parallelism 4 -Rounds 10 \ -TemplatePath /var/lib/ci/templates/WinBuild2025/WinBuild2025.vmx \ -RepoUrl 'http://10.10.20.11:3100/Simone/burnin-dummy.git' \ @@ -485,12 +493,14 @@ baseline Windows (entro ±20%). I due burn-in sono sequenziali -BuildCommand 'powershell -ExecutionPolicy Bypass -File .\build.ps1' ``` -- [ ] **Avviato in Linux** — burn-in 4 × 10 su `LinuxBuild2404`: +- [x] **Avviato in Linux** — burn-in 4 × 10 su `LinuxBuild2404` (esito: + 40/40 PASS, round ~70.2 s): ```bash - pwsh /opt/ci/local-ci-cd-system/scripts/Test-CapacityBurnIn.ps1 \ + sudo -u ci-runner -H pwsh /opt/ci/local-ci-cd-system/scripts/Test-CapacityBurnIn.ps1 \ -Parallelism 4 -Rounds 10 \ -TemplatePath /var/lib/ci/templates/LinuxBuild2404/LinuxBuild2404.vmx \ + -SnapshotName BaseClean-Linux \ -RepoUrl 'http://10.10.20.11:3100/Simone/burnin-dummy.git' \ -CloneBaseDir /var/lib/ci/build-vms/ \ -ArtifactBaseDir /var/lib/ci/artifacts/ \ @@ -498,21 +508,24 @@ baseline Windows (entro ±20%). I due burn-in sono sequenziali -GuestOS Linux \ -BuildCommand 'bash build.sh' ``` -- [ ] Misurare: - - [ ] Tempo medio per job, confronto con baseline A5 - - [ ] Tutti i 80 job (2 × 4 × 10) PASS - - [ ] Zero VM orfane in `/var/lib/ci/build-vms/`: + > **Nota**: `-SnapshotName BaseClean-Linux` è **obbligatorio** — il default + > dello script è `BaseClean` (Windows); senza, fallisce con + > `Error: Invalid snapshot name 'BaseClean'`. +- [x] Misurare: + - [x] Tempo medio per round, vs baseline §6–§8 — round near-deterministico + (σ < 1.5 s), in linea con static-IP floor (RUNBOOK §9) + - [x] Tutti i 80 job (2 × 4 × 10) PASS + - [x] Zero VM orfane in `/var/lib/ci/build-vms/`: ```bash sudo -u ci-runner /opt/ci/venv/bin/python -m ci_orchestrator vm cleanup --what-if ``` - - [ ] Spazio disco `/var/lib/ci/build-vms/` torna al baseline post-cleanup -- [ ] Documentare i risultati in `docs/RUNBOOK.md` (sezione "Linux - host baseline"). + - [x] Spazio disco `/var/lib/ci/build-vms/` torna al baseline post-cleanup +- [x] Documentare i risultati in `docs/RUNBOOK.md` (§9 — Concurrent + capacity burn-in). - [ ] Se delta > 20% vs baseline Windows: aprire issue in `TODO.md` - con dettagli per profiling (probabile candidato: filesystem ext4 - vs NTFS — valutare XFS/BTRFS). + con dettagli per profiling — **N/A**: nessun delta, burn-in PASS. --- @@ -521,8 +534,15 @@ baseline Windows (entro ±20%). I due burn-in sono sequenziali **Obiettivo**: verificare ≥1 settimana di esercizio con la macchina avviata abitualmente in Linux senza incidenti critici. -- [ ] Esercizio normale per ≥7 giorni con boot in Linux. -- [ ] Verifica giornaliera (bastano 2 minuti): +> **Esito 2026-06-07**: host live dal cutover B6 (~24 mag) → >2 settimane +> di esercizio. Salute al 7 giu: `systemctl --failed` vuoto, tutti i timer +> `ci-*` con ultimo run OK, nessun errore reale in `act-runner` (solo +> `NamedPipeIPC_ServerListenerError` cosmetici su exit dei processi pwsh). +> Nessun rollback. Le sessioni del 6 giu sono state hardening template +> Linux (no-IP/DHCP), non instabilità dell'host. + +- [x] Esercizio normale per ≥7 giorni con boot in Linux. +- [x] Verifica giornaliera (bastano 2 minuti): ```bash sudo systemctl --failed # nessun service in failed @@ -530,7 +550,7 @@ avviata abitualmente in Linux senza incidenti critici. sudo journalctl -u act-runner --since "24h ago" -p err ``` -- [ ] Nessun rollback effettuato durante la settimana. +- [x] Nessun rollback effettuato durante la settimana. --- diff --git a/plans/archived/2026-05-23/idea-2-linux-host.md b/plans/archived/2026-05-23/idea-2-linux-host.md index 4bb75dc..d683280 100644 --- a/plans/archived/2026-05-23/idea-2-linux-host.md +++ b/plans/archived/2026-05-23/idea-2-linux-host.md @@ -202,13 +202,23 @@ Costo: codebase divisa in due ambienti, maggiore overhead operativo. ## 8. Definizione di "fatto" (Fase B) -- [ ] act_runner gira come systemd service `act-runner.service` su Linux Mint -- [ ] Tutti i template VM (`WinBuild2025`, `WinBuild2022`, `LinuxBuild2404`) +> **Stato 2026-06-07**: Fase B COMPLETATA. B1–B7 + stabilità ≥1 settimana +> (host live dal cutover ~24 mag, >2 settimane senza incidenti). Tracking di +> dettaglio in [PhaseB-user-checklist.md](../../PhaseB-user-checklist.md) +> (Passi 1–8 `[x]`). Resta solo Passo 9 (doc dual-boot, non bloccante). + +- [x] act_runner gira come systemd service `act-runner.service` su Linux Mint +- [x] Tutti i template VM (`WinBuild2025`, `WinBuild2022`, `LinuxBuild2404`) operativi dal nuovo host con snapshot integri -- [ ] Workflow `build-ns7zip.yml` (matrix Win+Linux) PASS dal nuovo host -- [ ] Burn-in capacity 4 job concorrenti PASS, tempi entro ±20% del baseline -- [ ] Storage migrato a `/var/lib/ci/`, host Windows in stand-by come rollback -- [ ] Tutti i timer systemd attivi e schedulati -- [ ] Credenziali guest accessibili da `ci-runner` headless -- [ ] `README.md`, `AGENTS.md`, `docs/HOST-SETUP.md` aggiornati per host Linux -- [ ] Almeno 1 settimana di esercizio in produzione senza incidenti +- [x] Build Win+Linux PASS dal nuovo host — burn-in B7 concorrente + (WinBuild2025 + LinuxBuild2404, 80/80 job PASS); vedi RUNBOOK §9 +- [x] Burn-in capacity 4 job concorrenti PASS, tempi near-deterministici + (static-IP pool, σ < 1.5 s) — RUNBOOK §9 +- [x] Storage migrato a `/var/lib/ci/`; host Windows in stand-by come rollback +- [x] Tutti i timer systemd attivi e schedulati (watch-disk-space, + watch-runner-health, cleanup-orphans, backup-template, retention-policy) +- [x] Credenziali guest accessibili da `ci-runner` headless + (backend `PlaintextKeyring` file-based, no D-Bus) +- [x] `README.md`, `AGENTS.md`, `docs/HOST-SETUP.md` aggiornati per host Linux +- [x] Almeno 1 settimana di esercizio in produzione senza incidenti + (>2 settimane dal cutover ~24 mag; salute 7 giu OK)