# 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 (B1–B4, B6–B8) 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; B4–B8 sono > sequenziali e iniziano solo dopo "A done". > **Tempo stimato totale**: 1–2 giorni di lavoro distribuiti su una > finestra di 1–2 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 ``): ```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 \ --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 1–5 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). > **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 > ``` - [ ] **Avviato in Linux** — burn-in 4 job concorrenti × 10 round su `WinBuild2025`: ```bash 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' ``` - [ ] **Avviato in Linux** — burn-in 4 × 10 su `LinuxBuild2404`: ```bash pwsh /opt/ci/local-ci-cd-system/scripts/Test-CapacityBurnIn.ps1 \ -Parallelism 4 -Rounds 10 \ -TemplatePath /var/lib/ci/templates/LinuxBuild2404/LinuxBuild2404.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 \ -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/`: ```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"). - [ ] Se delta > 20% vs baseline Windows: aprire issue in `TODO.md` con dettagli per profiling (probabile candidato: filesystem ext4 vs NTFS — valutare XFS/BTRFS). --- ## Passo 8 — Stabilità ≥1 settimana **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): ```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 ``` - [ ] 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. - [ ] Aggiornare `docs/RUNBOOK.md` con la procedura dual-boot: come passare da Linux a Windows e viceversa, e cosa verificare al boot (runner online, act-runner.service attivo). - [ ] (Facoltativo) Verificare che GRUB abbia i timeout e i default corretti per l'uso quotidiano: ```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) | [ ] | | 7 | B7 | Capacity burn-in 4 × 10 (Win + Linux, sequenziale) | [ ] | | 8 | — | ≥1 settimana di stabilità (boot Linux abituale) | [ ] | | 9 | — | Gestione dual-boot + RUNBOOK aggiornato | [ ] | > **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.