Paillasse/docs/runbooks/labs.md
Adam Belkacemi 80b6c0722d ajoute le stock chaud, la correction automatique et les nouveaux essais de création de stack
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>
2026-10-08 21:52:16 +02:00

5.9 KiB

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, rotation : compte-service-openstack.md)
Stock chaud portail/app/Services/Labs/StockChaud.php, job CreerStackStock, commande labs:remplir-stock chaque minute — ADR 0003
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.
  • 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.
  • 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.