chore(plans): archive completed Phase B docs

Phase B is closed (checklist Passi 1–9 [x], benchmark matrix §6–§14
recorded). Move the two finished working docs into the dated archive:

  plans/PhaseB-user-checklist.md   → plans/archived/2026-06-07/
  plans/benchmark-windows-host.md  → plans/archived/2026-06-07/

plans/ now holds only the forward-looking plans (idea-3 Phase C pwsh
removal, idea-4 Phase D ESXi, ideas-overview). Fixed the live links that
pointed at the moved/relocated files: idea-3 §4, idea-2 §8 (relative path),
and ideas-overview (idea-1/idea-2 now under archived/, Phase A+B marked ).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 19:41:24 +02:00
parent 4366ba6b04
commit 8f1b3b7eb1
5 changed files with 17 additions and 16 deletions
@@ -204,8 +204,8 @@ Costo: codebase divisa in due ambienti, maggiore overhead operativo.
> **Stato 2026-06-07**: Fase B COMPLETATA. B1B7 + 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 18 `[x]`). Resta solo Passo 9 (doc dual-boot, non bloccante).
> dettaglio in [PhaseB-user-checklist.md](../2026-06-07/PhaseB-user-checklist.md)
> (Passi 19 `[x]`, archiviato). Dual-boot in RUNBOOK §15.
- [x] act_runner gira come systemd service `act-runner.service` su Linux Mint
- [x] Tutti i template VM (`WinBuild2025`, `WinBuild2022`, `LinuxBuild2404`)
@@ -0,0 +1,602 @@
# Fase B — Checklist finale per l'utente
La Fase B è la migrazione del runner CI dall'host Windows attuale a un
nuovo host **Linux Mint LTS** con VMware Workstation Pro Linux.
> **Stato repo**: solo lo step **B5** (unit `systemd` per i task
> periodici) è già committato in `deploy/systemd/` sul branch
> `feature/python-rewrite-and-linux-migration` (commit `50d37b5`).
> Tutti gli altri step (B1B4, B6B8) sono **operazioni hardware**
> sull'host Linux nuovo e devono essere eseguiti dall'utente.
>
> **Avanzamento**: B1 ✅ B2 ✅ B3 ✅ B4 ✅ — in corso: **B5** (timer systemd).
> **Pre-requisito**: la Fase A deve essere `done` (vedi
> `plans/PhaseA-user-checklist.md` Passo 9, merge + tag).
> B1, B2, B3 possono partire in parallelo a A3/A4/A5; B4B8 sono
> sequenziali e iniziano solo dopo "A done".
> **Tempo stimato totale**: 12 giorni di lavoro distribuiti su una
> finestra di 12 settimane (per consentire il burn-in tra B6 e B8).
---
## Passo 1 — Setup host Linux Mint (B1)
**Obiettivo**: hardware target con Linux Mint LTS, VMware Workstation
Pro Linux, layout storage e venv Python pronti.
- [x] Installare Linux Mint LTS sull'hardware target.
- [x] `sudo apt update && sudo apt full-upgrade -y && sudo reboot`
- [x] Scaricare e installare VMware Workstation Pro Linux (bundle
`.bundle` da Broadcow):
```bash
sudo bash VMware-Workstation-Full-*.bundle
vmrun -T ws --version # deve ritornare versione coerente
```
- [x] Smoke test Workstation (UI): creare VM trivia, clone, start,
stop, delete.
- [x] Configurare `vmnet8` NAT range `192.168.79.0/24` (allineato a
Windows) editando `/etc/vmware/vmnet8/dhcpd/dhcpd.conf` o via
`vmware-netcfg`.
- [x] Creare utente di servizio `ci-runner`:
```bash
sudo useradd -r -m -s /bin/bash ci-runner
```
- [x] Eseguire `setup-host-linux.sh` (copre mount disco, layout, Python venv,
clone repo, PowerShell Core):
```bash
# Clonare il repo prima di eseguire lo script
git clone https://gitea.emulab.it/Simone/local-ci-cd-system.git /tmp/local-ci-cd-system
sudo bash /tmp/local-ci-cd-system/setup-host-linux.sh -d /dev/sdf1
```
Lo script:
- Monta `/dev/sdf1` su `/var/lib/ci` via fstab (boot automatico).
Se la partizione ha una sottocartella `ci/`, ne sposta i contenuti
alla root della partizione prima del mount definitivo.
- Crea il layout `build-vms/`, `artifacts/`, `templates/`, `keys/`,
`logs/`, `runner/` con permessi e ACL corretti.
- Installa Python 3.11, crea `/opt/ci/venv`, clona il repo in
`/opt/ci/local-ci-cd-system` e installa il package.
- Installa PowerShell Core.
Flag utili se alcune dipendenze sono già presenti:
```bash
sudo bash setup-host-linux.sh -d /dev/sdf1 --skip-pwsh --skip-clone
```
Atteso a fine script: `ci_orchestrator --help` lista gli 11 sub-comandi.
---
## Passo 2 — Trasferimento template VM (B2)
**Obiettivo**: copiare i template VMware da `F:\CI\Templates\` su
`/var/lib/ci/templates/` mantenendo gli snapshot.
> ⚠️ AGENTS.md errore #9: i template devono essere **fully
> powered-off** prima della copia. Verificare assenza di `*.vmem` /
> `*.vmsn` di runtime.
- [x] Sull'host Windows:
```powershell
Get-ChildItem 'F:\CI\Templates' -Recurse -Include *.vmem,*.vmsn
# atteso: nessun risultato
```
- [x] Sull'host Linux, eseguire rsync via SSH dall'host Windows
(richiede OpenSSH Server attivo su Windows o, in alternativa,
`scp -r` lanciato da Windows verso Linux):
```bash
sudo -u ci-runner rsync -av --progress \
user@windows-host:/cygdrive/f/CI/Templates/ \
/var/lib/ci/templates/
```
- [x] Validare integrità snapshot:
```bash
find /var/lib/ci/templates -name '*.vmx' -exec vmrun -T ws listSnapshots {} \;
```
Atteso: `BaseClean` su `WinBuild2025.vmx` e `WinBuild2022.vmx`,
`BaseClean-Linux` su `LinuxBuild2404.vmx`.
- [x] Aprire ciascun `.vmx` su Workstation Linux per registrarli;
al prompt rispondere "**I copied it**".
- [x] **Prerequisito smoke test Windows**: il keyring file-based deve essere
configurato e la credenziale `BuildVMGuest` deve essere presente
**prima** di eseguire `wait-ready` (che prova WinRM). Questo è
formalmente il Passo 3 (B3), ma il minimo indispensabile per lo
smoke test è:
```bash
# Configurare il backend file keyring per ci-runner (una sola volta)
sudo -u ci-runner mkdir -p ~/.local/share/python_keyring
sudo -u ci-runner bash -c "cat > ~/.local/share/python_keyring/keyringrc.cfg << 'EOF'
[backend]
default-keyring=keyrings.alt.file.PlaintextKeyring
EOF"
sudo -u ci-runner /opt/ci/venv/bin/pip install keyrings.alt -q
# Salvare la credenziale guest Windows
sudo -u ci-runner /opt/ci/venv/bin/python /opt/ci/local-ci-cd-system/tools/ci-cred-set.py BuildVMGuest "WINBUILD-2025\\ci_build"
# Verificare
sudo -u ci-runner /opt/ci/venv/bin/python /opt/ci/local-ci-cd-system/tools/ci-cred-check.py BuildVMGuest
# atteso: OK (file) username='WINBUILD-2025\ci_build' password=****
```
> Il setup completo delle credenziali (chiavi SSH, GiteaPAT,
> PoC headless systemd) è nel Passo 3.
- [x] Smoke test pipeline VM:
```bash
# Windows smoke
CLONE_VMX=$(sudo -u ci-runner /opt/ci/venv/bin/python -m ci_orchestrator vm new \
--template /var/lib/ci/templates/WinBuild2025/WinBuild2025.vmx \
--snapshot BaseClean \
--clone-base-dir /var/lib/ci/build-vms \
--job-id smoke-b2-win \
--start)
echo "Clone VMX: $CLONE_VMX"
sudo -u ci-runner /opt/ci/venv/bin/python -m ci_orchestrator wait-ready \
--vmx "$CLONE_VMX" --guest-os windows --timeout 300
sudo -u ci-runner /opt/ci/venv/bin/python -m ci_orchestrator vm remove \
--vmx "$CLONE_VMX" --force
```
Ripetere per `LinuxBuild2404` (snapshot `BaseClean-Linux`,
`--guest-os linux`, `--job-id smoke-b2-linux`).
---
## Passo 3 — Trasferimento credenziali e chiavi (B3)
**Obiettivo**: chiavi SSH e credenziali guest disponibili al user
`ci-runner` headless.
- [x] Chiavi SSH guest Linux già presenti in `/var/lib/ci/keys/` (copiate
insieme ai template). Correggere solo i permessi:
```bash
sudo chown ci-runner:ci-runner /var/lib/ci/keys/ci_linux /var/lib/ci/keys/ci_linux.pub
sudo chmod 600 /var/lib/ci/keys/ci_linux
sudo chmod 644 /var/lib/ci/keys/ci_linux.pub
# verifica
ls -la /var/lib/ci/keys/
```
- [x] Credenziale guest Windows `BuildVMGuest` già salvata nel
prerequisito del Passo 2 con `keyrings.alt.file.PlaintextKeyring`
(schema a due entry — `secret-tool` scartato perché non funziona
headless senza D-Bus session).
Per riscrivere la credenziale se necessario:
```bash
sudo -u ci-runner /opt/ci/venv/bin/python /opt/ci/local-ci-cd-system/tools/ci-cred-set.py BuildVMGuest "WINBUILD-2025\\ci_build"
sudo -u ci-runner /opt/ci/venv/bin/python /opt/ci/local-ci-cd-system/tools/ci-cred-check.py BuildVMGuest
```
- [x] Re-store Gitea PAT con lo stesso schema:
```bash
sudo -u ci-runner /opt/ci/venv/bin/python /opt/ci/local-ci-cd-system/tools/ci-cred-set.py GiteaPAT ci-runner-linux
sudo -u ci-runner /opt/ci/venv/bin/python /opt/ci/local-ci-cd-system/tools/ci-cred-check.py GiteaPAT
```
- [x] **PoC headless**: il backend `PlaintextKeyring` non usa D-Bus —
funziona già da `sudo -u ci-runner` e funzionerà da
`act-runner.service`. Verificato durante smoke test Passo 2.
---
## Passo 4 — Setup act_runner come systemd service (B4)
**Obiettivo**: act_runner Linux registrato verso Gitea, gestito da
systemd.
- [x] Scaricare il binario act_runner Linux ≥ v1.0.4:
```bash
sudo mkdir -p /opt/ci/act_runner
sudo wget -O /opt/ci/act_runner/act_runner \
https://gitea.com/gitea/runner/releases/download/v1.0.4/gitea-runner-1.0.4-linux-amd64
sudo chmod +x /opt/ci/act_runner/act_runner
sudo chown -R ci-runner:ci-runner /opt/ci/act_runner
```
- [x] Generare token registrazione su Gitea (Admin → Runners → Create new runner).
- [x] Registrare il runner (sostituire `<TOKEN>`):
```bash
sudo -u ci-runner mkdir -p /var/lib/ci/runner
cd /var/lib/ci/runner
sudo -u ci-runner /opt/ci/act_runner/act_runner register \
--no-interactive \
--instance https://gitea.emulab.it \
--token <TOKEN> \
--name ci-linux \
--labels windows-build:host,linux-build:host
```
> ⚠️ Lasciare il runner in stato **paused** lato Gitea UI fino al
> cutover (Passo 6), per non intercettare job di produzione.
- [x] Creare `/etc/systemd/system/act-runner.service`:
```ini
[Unit]
Description=Gitea Act Runner
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=ci-runner
WorkingDirectory=/var/lib/ci/runner
Environment="PYTHONIOENCODING=utf-8"
Environment="CI_ROOT=/var/lib/ci"
Environment="CI_TEMPLATES=/var/lib/ci/templates"
Environment="CI_BUILD_VMS=/var/lib/ci/build-vms"
Environment="CI_ARTIFACTS=/var/lib/ci/artifacts"
Environment="CI_KEYS=/etc/ci/keys"
ExecStart=/opt/ci/act_runner/act_runner daemon --config /var/lib/ci/runner/config.yaml
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
```
- [x] Creare `/etc/ci/environment` (letto dalle unit dei task periodici
di B5; vedi `deploy/systemd/README.md`):
```bash
sudo tee /etc/ci/environment <<'EOF'
PYTHONIOENCODING=utf-8
CI_ROOT=/var/lib/ci
CI_TEMPLATES=/var/lib/ci/templates
CI_BUILD_VMS=/var/lib/ci/build-vms
CI_ARTIFACTS=/var/lib/ci/artifacts
CI_KEYS=/etc/ci/keys
EOF
sudo chmod 644 /etc/ci/environment
```
- [x] Abilitare e avviare:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now act-runner.service
sudo systemctl status act-runner
sudo journalctl -u act-runner -f # Ctrl+C dopo conferma OK
```
- [x] Generare e configurare `config.yaml` del runner:
```bash
# 1. Generare il file di default (conserva la registrazione in .runner)
sudo -u ci-runner /opt/ci/act_runner/act_runner generate-config \
| sudo -u ci-runner tee /var/lib/ci/runner/config.yaml > /dev/null
# 2. Sostituire i label Docker generati di default con quelli host corretti
# (generate-config scrive label ubuntu-*:docker:// che fanno cercare Docker)
sudo -u ci-runner sed -i \
-e 's| - "ubuntu-latest:docker://.*"| - "windows-build:host"|' \
-e '/ubuntu-24.04:docker:\/\//d' \
-e '/ubuntu-22.04:docker:\/\//d' \
/var/lib/ci/runner/config.yaml
sudo -u ci-runner sed -i \
'/ - "windows-build:host"/a\ - "linux-build:host"' \
/var/lib/ci/runner/config.yaml
# 3. Sostituire le variabili di esempio nella sezione runner.envs con le variabili CI
# (generate-config scrive due righe A_TEST_ENV_NAME_* come placeholder)
sudo -u ci-runner sed -i \
-e 's/ A_TEST_ENV_NAME_1: a_test_env_value_1/ CI_PYTHON_LAUNCHER: \/opt\/ci\/venv\/bin\/python/' \
-e 's/ A_TEST_ENV_NAME_2: a_test_env_value_2/ GITEA_CI_TEMPLATE_PATH: \/var\/lib\/ci\/templates\/WinBuild2025\/WinBuild2025.vmx\n GITEA_CI_LINUX_TEMPLATE_PATH: \/var\/lib\/ci\/templates\/LinuxBuild2404\/LinuxBuild2404.vmx/' \
/var/lib/ci/runner/config.yaml
# 4. Verificare
grep -A3 'labels:' /var/lib/ci/runner/config.yaml | head -5
grep -A4 'envs:' /var/lib/ci/runner/config.yaml
```
Atteso:
```yaml
labels:
- "windows-build:host"
- "linux-build:host"
envs:
CI_PYTHON_LAUNCHER: /opt/ci/venv/bin/python
GITEA_CI_TEMPLATE_PATH: /var/lib/ci/templates/WinBuild2025/WinBuild2025.vmx
GITEA_CI_LINUX_TEMPLATE_PATH: /var/lib/ci/templates/LinuxBuild2404/LinuxBuild2404.vmx
```
> Le variabili di sistema (`CI_ROOT`, `PYTHONIOENCODING`, ecc.) restano
> nel service unit. `runner.envs` è per la configurazione specifica
> del runner CI (path template, launcher Python).
```bash
# 5. Ricaricare e riavviare
sudo systemctl daemon-reload
sudo systemctl restart act-runner.service
sudo journalctl -u act-runner -n 20 # confermare avvio OK — nessun errore Docker
```
- [x] Smoke job: triggerare manualmente `self-test.yml` selezionando
il runner Linux (mettere temporaneamente in pausa quello Windows
o usare label distinte). Atteso: PASS.
> ⚠️ Prerequisito: le variabili `GITEA_CI_TEMPLATE_PATH` e
> `GITEA_CI_LINUX_TEMPLATE_PATH` devono essere presenti nel service
> unit **prima** di avviare lo smoke test, altrimenti il job fallisce
> con "Got unexpected extra argument".
---
## Passo 5 — Installare i timer systemd (B5)
**Obiettivo**: attivare le coppie `.service`+`.timer` già committate
in `deploy/systemd/` per replicare i task periodici di
`Register-CIScheduledTasks.ps1`.
> **Nota**: `ci-retention-policy` e `ci-backup-template` sono stati portati
> in Python puro (`retention run` / `template backup`) — **pwsh non è
> richiesto**. I backup template vengono compressi con `7z -mx=1` in
> `/var/lib/ci/backups/`. `ci-watch-runner-health` usa `--service-name
> act-runner` (nome Linux, non Windows).
> **Prerequisiti creati sull'host**:
> ```bash
> sudo mkdir -p /var/log/ci /var/lib/ci/backups
> sudo chown ci-runner:ci-runner /var/log/ci
> ```
- [x] Copiare le unit:
```bash
cd /opt/ci/local-ci-cd-system
sudo cp deploy/systemd/ci-*.service deploy/systemd/ci-*.timer /etc/systemd/system/
sudo systemctl daemon-reload
```
- [x] Abilitare e avviare tutti i timer:
```bash
for t in ci-cleanup-orphans ci-retention-policy \
ci-watch-disk-space ci-watch-runner-health \
ci-backup-template; do
sudo systemctl enable --now "${t}.timer"
done
```
- [x] Verificare lo schedule:
```bash
systemctl list-timers --all 'ci-*'
```
Atteso: 5 timer in stato `active`. ✅ PASS
- [x] Trigger manuale di ciascun service per validare l'esecuzione
one-shot:
```bash
for s in ci-cleanup-orphans ci-retention-policy \
ci-watch-disk-space ci-watch-runner-health \
ci-backup-template; do
sudo systemctl start "${s}.service"
sudo systemctl status "${s}.service" --no-pager
done
```
✅ Tutti e 5 PASS.
- [x] Per maggiori dettagli (mapping Windows→Linux, troubleshooting,
rollback) vedi `deploy/systemd/README.md`.
---
## Passo 6 — Cutover (B6)
**Obiettivo**: passare all'uso normale di Linux come OS primario di
produzione CI. Il cutover è un semplice riavvio — i due sistemi non
coesistono mai.
> ⚠️ Eseguire solo dopo che i Passi 15 sono tutti `[x]` PASS.
- [x] Verificare nessun job in coda lato Gitea (Admin → Actions → Tasks).
- [x] Attendere la fine di eventuali job in esecuzione su Windows.
- [x] Sbloccare il runner Linux su Gitea UI (rimuovere `paused`).
- [x] Riavviare la macchina in Linux.
- [x] Verificare runner Linux online su Gitea UI e act-runner attivo:
```bash
sudo systemctl status act-runner
sudo journalctl -u act-runner -n 20
```
- [x] Smoke trigger:
- [x] `self-test.yml` → PASS dal runner Linux
- [x] `build-ns7zip.yml` matrix Win+Linux → PASS dal runner Linux
- [x] Monitorare per ≥30 min:
```bash
sudo journalctl -u act-runner -f
```
> **Rollback**: riavviare in Windows — il runner Windows riprende
> automaticamente. Mettere in pausa il runner Linux su Gitea UI,
> documentare l'incidente in `TODO.md`.
---
## Passo 7 — Capacity burn-in (B7)
**Obiettivo**: validare il carico target con tempi paragonabili al
baseline Windows (entro ±20%). I due burn-in sono sequenziali
(dual-boot: un OS alla volta).
> **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.
- [x] **Avviato in Linux** — burn-in 4 job concorrenti × 10 round su
`WinBuild2025` (esito: 40/40 PASS, round ~78.6 s):
```bash
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' \
-CloneBaseDir /var/lib/ci/build-vms/ \
-ArtifactBaseDir /var/lib/ci/artifacts/ \
-VmrunPath /usr/bin/vmrun \
-BuildCommand 'powershell -ExecutionPolicy Bypass -File .\build.ps1'
```
- [x] **Avviato in Linux** — burn-in 4 × 10 su `LinuxBuild2404` (esito:
40/40 PASS, round ~70.2 s):
```bash
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/ \
-VmrunPath /usr/bin/vmrun \
-GuestOS Linux \
-BuildCommand 'bash build.sh'
```
> **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
```
- [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 — **N/A**: nessun delta, burn-in PASS.
---
## Passo 8 — Stabilità ≥1 settimana
**Obiettivo**: verificare ≥1 settimana di esercizio con la macchina
avviata abitualmente in Linux senza incidenti critici.
> **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
systemctl list-timers --all 'ci-*' # tutti gli ultimi run OK
sudo journalctl -u act-runner --since "24h ago" -p err
```
- [x] Nessun rollback effettuato durante la settimana.
---
## Passo 9 — Gestione dual-boot
> La macchina è **dual-boot**: Windows e Linux coesistono sullo stesso
> hardware, non girano mai contemporaneamente. Non c'è dismissione —
> Windows resta pienamente funzionale e si usa riavviando nel boot
> entry corrispondente.
- [x] Aggiornare `docs/RUNBOOK.md` con la procedura dual-boot: vedi
**RUNBOOK §15** (switch Linux⇄Windows, verifica post-boot per
entrambi gli OS, tweak GRUB opzionale).
- [x] (Facoltativo) GRUB verificato: `GRUB_DEFAULT=0`, `GRUB_TIMEOUT=0`,
`GRUB_TIMEOUT_STYLE=hidden` → boot diretto in Linux (intenzionale:
la macchina torna host Linux dopo ogni reboot unattended). Per
switch a Windows: tasto Esc al boot, oppure `grub-reboot` con
`GRUB_DEFAULT=saved`. Tweak documentato in RUNBOOK §15, **non
applicato** (default Linux voluto).
```bash
sudo nano /etc/default/grub # GRUB_DEFAULT, GRUB_TIMEOUT
sudo update-grub
```
---
## Tracciamento globale
| Passo | Step | Descrizione | Stato |
| ----- | ---- | -------------------------------------------------- | ----- |
| 1 | B1 | Setup host Linux Mint (OS, Workstation, venv, ACL) | [x] |
| 2 | B2 | Trasferimento template VM + smoke `vm new` | [x] |
| 3 | B3 | Chiavi SSH + keyring (PoC headless) | [x] |
| 4 | B4 | act_runner systemd service + `self-test.yml` PASS | [x] |
| 5 | B5 | Timer systemd installati e attivi | [x] |
| 6 | B6 | Cutover (riavvio in Linux, runner Linux attivo) | [x] |
| 7 | B7 | Capacity burn-in 4 × 10 (Win + Linux, sequenziale) | [x] |
| 8 | — | ≥1 settimana di stabilità (boot Linux abituale) | [x] |
| 9 | — | Gestione dual-boot + RUNBOOK aggiornato (§15) | [x] |
> **Architettura**: la macchina è **dual-boot** (stesso hardware).
> Avviata in Linux → runner Linux attivo. Avviata in Windows → runner
> Windows attivo. I due sistemi non girano mai contemporaneamente.
Quando i passi 1-8 sono `[x]`, la **Fase B è chiusa** (B8 annullato per
vincolo) e si può valutare l'apertura della Fase C (eliminazione `pwsh`
dall'host Linux, vedi `plans/idea-3-powershell-removal.md`). La Fase D
(backend ESXi, `plans/idea-3-esxi-support.md`) viene dopo.
@@ -0,0 +1,151 @@
# Plan — Windows-host benchmarks (comparable to Linux-host §6–§10)
> **Scopo**: la macchina è dual-boot (Windows ⇄ Linux, stesso hardware).
> Le baseline RUNBOOK §6–§10 sono quasi tutte sul **Linux host**. Questo
> plan raccoglie i numeri mancanti sul **Windows host** per chiudere la
> matrice host × guest × IP-mode e rendere il confronto Linux-vs-Windows
> completo e simmetrico.
>
> **Prerequisito**: riavviare la macchina nel boot entry **Windows**.
> Tutto gira su Windows PowerShell 5.1 o pwsh 7; usare la stessa
> `Measure-CIBenchmark.ps1` / `Test-CapacityBurnIn.ps1` del repo (sono
> cross-platform, i default path passano a `F:\CI\...` quando `$IsWindows`).
---
## 1. Cosa esiste già e cosa manca
| RUNBOOK § | Host | Guest | IP mode | Stato |
| --------- | ----- | -------------- | --------------------- | ------------ |
| §6 | Win | WinBuild2025 | DHCP | ✓ 2026-05-17 |
| §7 | Linux | WinBuild2025 | DHCP | ✓ |
| §8 | Linux | WinBuild2025 | static | ✓ |
| §9 | Linux | Win + Linux | burn-in (static pool) | ✓ |
| §10 | Linux | LinuxBuild2404 | DHCP | ✓ |
**Mancanti sul Windows host** (target di questo plan):
| Nuovo § | Host | Guest | IP mode | Pair-with |
| ------- | ---- | -------------- | ------------ | --------- |
| §11 | Win | WinBuild2025 | static | §8 |
| §12 | Win | LinuxBuild2404 | DHCP | §10 |
| §13 | Win | Win + Linux | burn-in 4×10 | §9 |
§6 copre già Win×Win×DHCP. Opzionale: ri-eseguirlo nella stessa sessione per
coerenza (stesso disco/fragmentazione del momento).
---
## 2. Prerequisiti sul Windows host
- [x] Boot in Windows; verificare VMware Workstation + `vmrun.exe` in
`C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe`.
- [x] Template fully powered-off con snapshot integri:
```powershell
& 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe' -T ws listSnapshots 'F:\CI\Templates\WinBuild2025\WinBuild2025.vmx'
& 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe' -T ws listSnapshots 'F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx'
```
Attesi: `BaseClean` e `BaseClean-Linux`. ✓ (anche `PostInstall` su Win)
- [x] Credenziali in **Windows Credential Manager** (non keyring file):
target `BuildVMGuest` (guest WinRM, user `ci_build`) e `GiteaPAT`
(user `Simone`). Verificato.
- [x] Chiave SSH guest Linux presente: `F:\CI\keys\ci_linux` (per §12/§13
lato LinuxBuild2404).
- [x] Gitea raggiungibile (`http://10.10.20.11:3100`, HTTP 200) e repo
`Simone/burnin-dummy` con `build.ps1` + `build.sh` (verificato via API).
- [x] `F:\CI\BuildVMs\` privo di cloni orfani e `F:\CI\ip-pool.json` assente
(pulito). Reset se serve:
```powershell
Set-Content -Path 'F:\CI\ip-pool.json' -Value '{}' -NoNewline
```
---
## 3. §11 — WinBuild2025 static IP (pair §8)
```powershell
.\scripts\Measure-CIBenchmark.ps1 `
-TemplatePath 'F:\CI\Templates\WinBuild2025\WinBuild2025.vmx' `
-SnapshotName BaseClean `
-StaticIP 192.168.79.200 -Netmask 255.255.255.0 `
-Iterations 10
```
- [x] 10 iter completate, tabella per-fase stampata. **Boot total avg 29.75 s.**
- [x] Confronto vs §8 (Linux host static): IP-floor confermato host-independent
(22.81 s vs 21.77 s, +5 %). Penalità host: clone +58 % (NTFS), Ready +4.7 s
(WinRM re-bind dopo NIC reconfig), destroy +82 % (bimodale), boot +24 %.
Vedi RUNBOOK §11. **Nota**: primo tentativo fallito (timeout 300 s/iter) —
template Windows privo dell'agent `ci-static-ip`; risolto copiando i
template dal Linux host (parità template).
## 4. §12 — LinuxBuild2404 DHCP (pair §10)
```powershell
.\scripts\Measure-CIBenchmark.ps1 `
-TemplatePath 'F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx' `
-SnapshotName BaseClean-Linux `
-GuestOS Linux `
-Iterations 10
```
- [x] 10 iter completate. **Boot total avg 13.51 s.**
- [x] Confronto vs §10 (Linux host, 8.82 s): **+53 %** host-side — clone +191 %
(NTFS), IP-acquire +52 % (NAT vmnet8 Windows + guestinfo). Guest Linux resta
deterministico (IP σ ≈ 0.05 s). Vedi RUNBOOK §12.
## 5. §13 — Concurrent burn-in 4×10 (pair §9)
```powershell
# WinBuild2025
.\scripts\Test-CapacityBurnIn.ps1 `
-Parallelism 4 -Rounds 10 `
-TemplatePath 'F:\CI\Templates\WinBuild2025\WinBuild2025.vmx' `
-RepoUrl 'http://10.10.20.11:3100/Simone/burnin-dummy.git' `
-CloneBaseDir 'F:\CI\BuildVMs\' `
-ArtifactBaseDir 'F:\CI\Artifacts\' `
-VmrunPath 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe' `
-BuildCommand 'powershell -ExecutionPolicy Bypass -File .\build.ps1'
# LinuxBuild2404 (snapshot OBBLIGATORIO, default è BaseClean=Windows)
.\scripts\Test-CapacityBurnIn.ps1 `
-Parallelism 4 -Rounds 10 `
-TemplatePath 'F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx' `
-SnapshotName BaseClean-Linux `
-RepoUrl 'http://10.10.20.11:3100/Simone/burnin-dummy.git' `
-CloneBaseDir 'F:\CI\BuildVMs\' `
-ArtifactBaseDir 'F:\CI\Artifacts\' `
-VmrunPath 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe' `
-GuestOS Linux `
-BuildCommand 'bash build.sh'
```
- [~] **Linux 40/40 PASS (10/10 round). Windows 36/40 (7/10 round).** I 4 fail
Windows = fault WinRM transitori (round 68: connect-timeout 5986 +
WSManFault "shell not found") sotto 4× concorrenza; auto-recuperati (slot IP
rilasciati, 0 cloni orfani, round 910 di nuovo puliti). Linux/SSH 0 fault.
- [x] Round avg vs §9 (Win ~78.6 s, Lin ~70.2 s): Windows host Win ~91.6 s
(round PASS) **+16 %** + instabilità WinRM; Lin 96.4 s **+37 %**.
Vedi RUNBOOK §13. **Pre-req scoperti**: (1) parità template, (2) prod venv
F:\CI\python\venv stale (pre-`ip_pool`) → reinstallato `pip install .`,
(3) `CI_VENV_PYTHON` da settare (bug shim `$IsWindows` su PS 5.1+StrictMode).
---
## 6. Output e documentazione
- Tutti i run appendono a `F:\CI\Logs\benchmark.jsonl` (single-job) e
stampano summary (burn-in).
- [x] Risultati aggiunti a `docs/RUNBOOK.md` §11–§13 (+ §14 matrice completa),
con confronto host Linux-vs-Windows per ciascuna coppia.
- [x] Pulizia post-run: `F:\CI\BuildVMs\` vuoto, artifact `burnin-*`
rimossi, `F:\CI\ip-pool.json` reset.
---
## 7. Matrice finale attesa (dopo questo plan)
| Host \ (Guest, mode) | Win/DHCP | Win/static | Lin/DHCP | burn-in 4×10 |
| -------------------- | -------- | ---------- | -------- | ------------ |
| **Linux** | §7 | §8 | §10 | §9 |
| **Windows** | §6 | §11 | §12 | §13 |
Confronto colonna-per-colonna = **effetto host** a guest+mode costanti.
Confronto riga-per-riga = **effetto guest/IP-mode** a host costante.