diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ae6bfd..e3f61af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # CHANGELOG — Set-OPS +## 2026-10-07 (102) — Le devis de placement vérifiait un gabarit que personne ne clone + +**Trouvé au pré-vol de la reconstruction de Chezlepro** (`make placement-plan +TENANT=OPS-Chezlepro`, runner du site, lecture seule) : rasage conforme (13 VM, chacune +sous son nom, aucun conflit), mais placement en écart, avec un gabarit « None » introuvable. + +**La cause.** Depuis le 2026-09-01, le gabarit et son stockage viennent du **site** +(`underlay.py --gabarit` : 9006 sur `vishnu`, stockage `CephNVMe`) : `cloner-vm` les passe en +`-e`, par-dessus le tenant. Chezlepro suit la règle et ne déclare plus de gabarit ; +Technolibre déclare encore `99998` et `TrueNAS`, valeurs que plus rien n'utilise (les 13 +clones de M4 sont nés du 9006). Le devis lisait le tenant : il **validait chez Technolibre +ce que personne ne clone**, et refusait Chezlepro. Défaut antérieur à la face (l'ancien +chemin lisait les mêmes valeurs) ; il ne touche pas le clonage. + +**Fait** : `devis_placement.ce_que_le_clonage_utilise()` remplace le gabarit et le stockage +du tenant par ceux du site, même règle que le Makefile : le site décide ; s'il ne déclare +rien, le tenant reprend la main. + +**Éprouvé** : `test_devis_placement.py`, un sixième test (valeurs périmées remplacées, tenant +sans gabarit servi, site muet = tenant tel quel). Témoin : le correctif neutralisé, le test +échoue. `make verifier` conforme, **94/94**. + +**Reste** : les lignes `proxmox_clone_vmid_modele` et `proxmox_clone_source_nom` de +Technolibre sont mortes ; les mettre en commentaire comme chez Chezlepro changera sa face +(à republier). Sans urgence : le site a la priorité partout. + ## 2026-10-07 (101) — M4 : Technolibre reconstruite par le site qui la nomme ; l'instantané d'avant rasage ne survivait pas **M4, la preuve par reconstruction** (`make reconstruire-locataire TENANT=OPS-Technolibre`, diff --git a/scripts/devis_placement.py b/scripts/devis_placement.py index 1cb0672..57bba93 100644 --- a/scripts/devis_placement.py +++ b/scripts/devis_placement.py @@ -207,6 +207,27 @@ def nom_du_tenant(source: Path | None, locataire: str | None = None) -> str: return source.parents[3].resolve().name if source else "?" +def ce_que_le_clonage_utilise(place: dict, gabarit: dict) -> dict: + """Le placement tel que `cloner-vm` le passe REELLEMENT a Proxmox. + + LE DEVIS VERIFIAIT CE QUE PERSONNE NE CLONE (2026-10-07). Depuis le 2026-09-01, le gabarit + et son stockage viennent du SITE (`underlay.py --gabarit`) : `cloner-vm` les passe en + `-e`, par-dessus ce que dit le tenant. Le devis, lui, lisait encore le tenant. Chez + Technolibre, il validait le 99998, que plus rien ne clone (M4 est nee du 9006, sur + CephNVMe) ; chez Chezlepro, qui ne declare plus de gabarit (c'est la regle), il refusait + « None » — trouve au pre-vol de sa reconstruction. + + Meme regle que le Makefile : le site decide ; s'il ne declare rien, le tenant reprend + la main. Degrader, jamais deviner. + """ + vu = dict(place) + if gabarit.get("vmid") not in (None, ""): + vu["proxmox_clone_vmid_modele"] = int(gabarit["vmid"]) + if gabarit.get("stockage"): + vu["proxmox_clone_stockage"] = gabarit["stockage"] + return vu + + def afficher(lignes: list[dict], tenant: str, depot: Path) -> bool: print(f"Devis du placement — tenant « {tenant} » sur l'hebergeur « {depot.name} »\n") print(f" {'OBJET':<12} {'DEMANDE':<34} {'VERDICT'}") @@ -247,6 +268,7 @@ def main() -> int: else: place, source = placement_du_tenant() tenant = nom_du_tenant(source, a.locataire) + place = ce_que_le_clonage_utilise(place, underlay_mod.gabarit()) if not place: print(f"Aucun placement declare par « {tenant} » — rien a confronter.") return 0 diff --git a/scripts/tests/test_devis_placement.py b/scripts/tests/test_devis_placement.py index 392b606..0cb535f 100644 --- a/scripts/tests/test_devis_placement.py +++ b/scripts/tests/test_devis_placement.py @@ -117,6 +117,22 @@ def test_le_devis_nomme_le_locataire_designe(): assert "« OPS-Exemple »" in entete and "?" not in entete, f"en-tete : {entete!r}" +def test_le_devis_verifie_le_gabarit_et_le_stockage_que_le_clonage_utilise(): + """Le site decide du gabarit et de son stockage ; `cloner-vm` les passe en `-e`. Le devis + lisait le tenant : il validait chez Technolibre un 99998 que plus rien ne clone, et + refusait chez Chezlepro un gabarit « None » (pre-vol de sa reconstruction, 2026-10-07).""" + site = {"vmid": 9006, "nom": "modeleSetOPS-minimal", "noeud": "vishnu", "stockage": "CephNVMe"} + perime = {**PLACEMENT, "proxmox_clone_vmid_modele": 99998, "proxmox_clone_stockage": "TrueNAS"} + vu = dp.ce_que_le_clonage_utilise(perime, site) + assert vu["proxmox_clone_vmid_modele"] == 9006 and vu["proxmox_clone_stockage"] == "CephNVMe", vu + sans = {k: v for k, v in perime.items() if k != "proxmox_clone_vmid_modele"} + assert dp.ce_que_le_clonage_utilise(sans, site)["proxmox_clone_vmid_modele"] == 9006, \ + "un tenant qui suit la regle (aucun gabarit declare) recoit celui du site" + assert dp.ce_que_le_clonage_utilise(perime, {}) == perime, \ + "sans gabarit declare par le site, le tenant reprend la main, tel quel" + assert perime["proxmox_clone_vmid_modele"] == 99998, "le placement du tenant n'est pas modifie en place" + + if __name__ == "__main__": tests = [v for k, v in sorted(globals().items()) if k.startswith("test_")] for t in tests: