contexte : corriger le devis des pools vide (face sans fonctions)

La face publie aussi fonctions ; les pools retrouvent leurs VM. La
comparaison a l'ancien code est refaite dans un git worktree complet :
les sept sorties du site sont identiques. Garde de regression ajoutee.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-10-05 14:49:24 -04:00
parent d0a6002b87
commit 2e9b31bef7
3 changed files with 58 additions and 3 deletions

View file

@ -1,5 +1,39 @@
# CHANGELOG — Set-OPS # CHANGELOG — Set-OPS
## 2026-10-05 (95) — Une régression publiée : le devis des pools vide, et une comparaison qui ne testait rien
**Ce qui s'est passé.** (94) a fait passer la découverte des locataires par la face réseau, qui
ne publiait que cinq champs de leur nomenclature. Le devis des pools dérive les VMID par
`deriver_nomenclature`, qui lit aussi `fonctions` : sans ce champ, **chaque pool de locataire
s'est retrouvé vide** (0 membre, toutes les VM « sans VMID »), sans erreur. Publié dans
`d0a6002`.
**Pourquoi la vérification ne l'a pas vu.** Pour prouver « identique à HEAD », j'ai lancé
l'**ancien script** (`git show HEAD:scripts/X.py`) à côté du nouveau. Mais il importait le
**nouveau** module de découverte : pour tous les consommateurs de la découverte, la
comparaison comparait le nouveau code à lui-même. Sept « IDENTIQUE » qui ne testaient rien.
**Ce que ça a coûté** : un devis publié faux. Rien n'applique les pools automatiquement, et le
clonage nomme son pool par une lecture directe de la nomenclature, non touchée.
**Corrigé** :
- la face publie aussi `fonctions` ; faces republiées. Les pools retrouvent leurs 13 VM par
locataire ;
- **la comparaison refaite correctement** : un `git worktree` à `d6591cb` (avant toute
l'étape 3), arbre complet, même environnement. Les sept sorties du site (commutateur,
frontière, Proxmox, SDN ×2, pools, inventaire du site) sont **identiques octet pour octet**.
Une seule différence apparente — `instance_active` à la frontière — venait du worktree, sans
lien `instance` : la frontière lit le lien et non `SETOPS_INSTANCE`, une des devinettes de
contexte que la conception doit supprimer. Avec le même lien : identique ;
- garde de régression dans `test_contexte.py` (105 contrôles) : chaque pool porte autant de
VM que le locataire en publie, aucune sans VMID.
**La leçon, retenue** : comparer à l'ancien code se fait dans un **arbre complet**, jamais en
lançant l'ancien script seul ; vérifier `git diff --stat` après une édition scriptée avant de
comparer ; accompagner chaque « identique » d'un témoin qui doit changer.
**Validation** : `make verifier` conforme, 92/92.
## 2026-10-05 (94) — Étape 3 : le site découvre ses locataires par leur face réseau ## 2026-10-05 (94) — Étape 3 : le site découvre ses locataires par leur face réseau
**Le chemin** (`docs/conception-contextes.md` §6), étape 3. La découverte des locataires **Le chemin** (`docs/conception-contextes.md` §6), étape 3. La découverte des locataires

View file

@ -540,10 +540,11 @@ class Locataire(Ecosysteme):
"sources": sources, "sources": sources,
"index": self.index, "index": self.index,
"zones": zones, "zones": zones,
# Ce que le site lit de sa nomenclature (decouverte, frontiere, SDN, commutateur) : # Ce que le site lit de sa nomenclature (decouverte, frontiere, SDN, commutateur, pools).
# ces cinq champs, et rien d'autre (releve du 2026-10-05). # `fonctions` manquait au premier releve : les pools en derivent les VMID — le devis
# des pools s'est retrouve vide, publie ainsi avant d'etre vu (2026-10-05).
"nomenclature": {k: self.nomenclature()[k] for k in "nomenclature": {k: self.nomenclature()[k] for k in
("index", "categories", "reservations", "cidr_hote", "federe") ("index", "categories", "reservations", "cidr_hote", "federe", "fonctions")
if k in self.nomenclature()}, if k in self.nomenclature()},
"machines": machines, "machines": machines,
"groupes": groupes, "groupes": groupes,

View file

@ -579,6 +579,25 @@ def decouverte_lit_la_face() -> None:
f"la decouverte du site lit l'index publie : {avant}, puis 99 une fois la face alteree") f"la decouverte du site lit l'index publie : {avant}, puis 99 une fois la face alteree")
def pools_complets() -> None:
"""Chaque pool porte toutes les VM que le locataire publie (regression du 2026-10-05 : la
face ne publiait pas `fonctions`, et le devis des pools s'est retrouve vide, sans erreur)."""
import devis_proxmox_pools
import devis_reseau
couples = C.couples_hebergeur()
if not couples:
print("SAUTE aucun site frere : aucun pool a confronter")
return
site = couples[0][0]
with C._monter(site):
pools = {b["tenant"]: b for b in devis_proxmox_pools.construire(devis_reseau.decouvrir_du_site())["pools"]}
for _s, loc in couples:
b = pools.get(loc.nom) or {}
attendu = len((loc.face_publiee() or {}).get("machines") or {})
verifier(len(b.get("membres") or []) == attendu and not b.get("sans_vmid"),
f"pool {loc.nom} : {len(b.get('membres') or [])} VM sur {attendu} publiees, aucune sans VMID")
def main() -> int: def main() -> int:
with tempfile.TemporaryDirectory() as tmp: with tempfile.TemporaryDirectory() as tmp:
d = Path(tmp) d = Path(tmp)
@ -599,6 +618,7 @@ def main() -> int:
proxmox_lit_la_face() proxmox_lit_la_face()
frontiere_lit_la_face() frontiere_lit_la_face()
decouverte_lit_la_face() decouverte_lit_la_face()
pools_complets()
if ECHECS: if ECHECS:
print(f"\n{len(ECHECS)} echec(s).") print(f"\n{len(ECHECS)} echec(s).")
return 1 return 1