Stock de stacks prêtes par lab dans Redis (prise atomique en Lua, ménage par comparaison avec Heat, disjoncteur), files gérées par Horizon, séances planifiables. Correcteur Go sur l'hyperviseur : socket Unix, commandes lues dans le lab.yaml uniquement, exécution par l'agent invité QEMU. Une création de stack ratée est supprimée et réessayée deux fois, puis la session passe en échec, visible du prof. Corrige aussi trois défauts des étapes précédentes : la console se coupait à la première frappe d'un navigateur (trames texte), l'élève ne pouvait pas rejoindre une séance, la déconnexion n'avait pas de jeton CSRF. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
93 lines
5.9 KiB
Markdown
93 lines
5.9 KiB
Markdown
# 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.
|