Paillasse/docs/runbooks/labs.md

94 lines
5.9 KiB
Markdown
Raw Normal View History

# Runbook — Labs, stock chaud et correction (étapes 10 et 11)
## Où sont les choses
| Élément | Emplacement |
|---|---|
| Source éditoriale des labs | `paillasse-contenus/` (dépôt de contenu, pas de code) |
| Copie de travail lue par le portail et le correcteur | `labs/` à la racine de ce dépôt → synchronisée vers `/opt/labs` sur le serveur |
| Catalogue (table `labs`) | `php artisan labs:synchroniser`, lancé par Ansible après chaque synchronisation de `labs/` |
| Lecture d'un `lab.yaml` | `portail/app/Services/Labs/DefinitionLab.php` |
| Traduction lab → gabarit Heat | `portail/app/Services/OpenStack/GenerateurTemplateHeat.php` |
| Construction d'une stack (3 essais) | `portail/app/Services/OpenStack/FabriqueStack.php` |
| Authentification OpenStack | `portail/app/Services/OpenStack/KeystoneAuth.php` (mot de passe — [ADR 0002](../adr/0002-mot-de-passe-pour-heat.md), rotation : [compte-service-openstack.md](compte-service-openstack.md)) |
| Stock chaud | `portail/app/Services/Labs/StockChaud.php`, job `CreerStackStock`, commande `labs:remplir-stock` chaque minute — [ADR 0003](../adr/0003-stock-chaud-et-horizon.md) |
| Admission (stock ou construction) | `SeanceController::admettre` → `CreerStackLab` si le stock est vide |
| Suppression et expiration | `SupprimerStackLab`, commande `labs:expirer-sessions` chaque minute |
| Files de jobs | Laravel Horizon, service systemd `portail-horizon` : superviseurs `sessions` (4 processus) et `stock` (2), `config/horizon.php` |
| Correcteur | service systemd `correcteur` (Go, `correcteur/`, binaire `/usr/local/sbin/correcteur`), socket `/run/correcteur/correcteur.sock` (`0660 root:www-data`) |
| Correction côté portail | `ClientCorrecteur`, `CorrigerSession`, table `corrections` |
| Tests du parcours réel | `portail/tests/Reels/` (préparation + parcours prof/élève) |
## États d'une session de lab
`en_attente` → (prof : Admettre) → `actif` tout de suite si le stock a une
stack, sinon `admis` puis `actif` quand `CreerStackLab` a fini → `expire` ou
`termine`. Après 3 essais ratés : `echec`, la raison est affichée au prof
qui peut cliquer « Réessayer » (retour à `admis`).
## Ajouter un nouveau lab
1. Le lab existe dans `paillasse-contenus/{id}/lab.yaml`. L'ordre des réseaux d'une machine décide de l'ordre des interfaces (`ens3`, `ens4`…). Les vérifications dont l'id commence par `bonus-` ne comptent pas pour « réussi ».
2. Le copier dans `labs/{id}/lab.yaml` de ce dépôt.
3. Vérifier que les images et flavors existent (`openstack image list`, `openstack flavor list`) et que l'image a `hw_qemu_guest_agent=yes` (sinon le correcteur répond `agent_injoignable`).
4. Déployer : `cd ansible && script -q /dev/null ansible-playbook correcteur.yml -i inventory.ini` (portail + correcteur). Le lab entre au catalogue, et son stock se remplit dans la minute.
5. Tester le parcours : `portail/tests/Reels/` (voir l'en-tête des deux scripts), puis supprimer les données de test.
## Stock chaud
```
ssh paillasse "cd /var/www/portail && sudo -u www-data HOME=/tmp php8.3 artisan labs:remplir-stock"
```
Affiche, par lab : cible, prêtes, en construction, commandées, retirées,
disjoncteur. Réglages (`.env` du portail, sinon valeurs de `config/labs.php`) :
- `LABS_CAPACITE_VM` (15) : plafond de VM `eleve.petit`, sessions et stock compris ;
- `LABS_STOCK_TAILLE` (1) : stacks prêtes par lab hors séance.
Disjoncteur ouvert (« oui ») : 3 échecs définitifs de suite sur ce lab. Lire
`storage/logs/laravel.log` (`CreerStackStock`), corriger la cause (image,
flavor, quota…), puis le réarmer sans attendre ses 30 min :
```
sudo -u www-data HOME=/tmp php8.3 artisan tinker --execute='Illuminate\Support\Facades\Redis::del("stock:echecs:{lab}");'
```
Vider le stock d'un lab (les stacks seront supprimées par le ménage) : passer
`LABS_STOCK_TAILLE=0` puis attendre une minute, ou supprimer la clé
`stock:pretes:{lab}` (le ménage supprime alors les stacks orphelines après 10 min).
## Correcteur
```
systemctl status correcteur
journalctl -u correcteur -f # une ligne par vérification : instance, lab, id, résultat, durée
```
Il n'accepte que `{"instance": uuid, "lab": id, "verification": id}`, refuse
tout champ en plus, et n'exécute que la commande du `lab.yaml`, en root dans
la VM, par `guest-exec` (10 s maximum). Avant d'exécuter, il vérifie dans les
métadonnées libvirt que la VM appartient au projet `paillasse` et porte le
nom de la machine visée. Codes d'erreur : `requete_invalide`, `lab_inconnu`,
`verification_inconnue`, `machine_non_conforme`, `agent_injoignable`,
`delai_depasse`.
## Inspecter les stacks sur le serveur
```
sudo -i
export PATH=/opt/kolla-venv/bin:$PATH
export OS_CLIENT_CONFIG_FILE=/etc/kolla/clouds-paillasse.yaml OS_CLOUD=paillasse-service
openstack stack list # lab-session-{id}-e{n} : sessions ; stock-{lab}-{xxxxxx}-e{n} : stock
openstack server list
```
## Pièges rencontrés
- **Heat refuse une application credential** (`Delegated tokens cannot manage trusts`), même `--unrestricted` : voir [ADR 0002](../adr/0002-mot-de-passe-pour-heat.md).
- **Horizon garde le code et la configuration en mémoire** : le rôle Ansible le redémarre après chaque synchronisation de code ou changement de `.env`.
- **Timeouts emboîtés** : job (280 s) < superviseur Horizon (300 s) < `REDIS_QUEUE_RETRY_AFTER` (330 s). Sinon Redis redonne un job encore en cours à un autre processus, et deux stacks sont construites.
- **Console muette après la première touche** : `nova-serialproxy` ferme la connexion à la première trame WebSocket texte. Le proxy Go envoie tout en binaire — voir [post-mortem du 2026-10-08](../postmortems/2026-10-08-console-trames-texte.md).
- `sudo -u www-data php artisan ...` exige `HOME=/tmp` (pas de `$HOME` inscriptible pour `www-data`).
- Une stack appartient à une session (un élève, une tentative) ; une stack du stock n'y retourne jamais après avoir été donnée.