2026-08-13 10:11:25 -04:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""Devis du PLACEMENT — les quatre objets d'hebergeur que le tenant monte designe.
|
|
|
|
|
|
|
|
|
|
D-80 : un tenant est agnostique de son underlay ; tout son adressage derive du seed.
|
|
|
|
|
Ce qui le lie a une fabric tient en QUATRE valeurs, et elles nomment des objets qui
|
|
|
|
|
appartiennent a l'hebergeur :
|
|
|
|
|
|
|
|
|
|
proxmox_clone_noeud sur quel noeud cloner
|
|
|
|
|
proxmox_clone_stockage ou poser le disque
|
|
|
|
|
proxmox_clone_pont ou brancher la carte
|
|
|
|
|
proxmox_clone_vmid_modele quel gabarit cloner
|
|
|
|
|
|
|
|
|
|
**P37 en verifie trois**, statiquement, contre les listes que l'hebergeur DECLARE
|
|
|
|
|
(`proxmox-hebergeur.yml`). C'est utile et insuffisant : une liste declaree peut elle-meme
|
|
|
|
|
etre perimee, et le GABARIT n'y figure pas — c'est un objet du cluster, pas une
|
|
|
|
|
declaration.
|
|
|
|
|
|
|
|
|
|
Ce devis interroge donc le CLUSTER. Il ne verifie pas ce qui est ecrit : il verifie ce qui
|
|
|
|
|
existe. Sur un site inconnu, c'est la premiere chose a savoir — sans quoi l'ecart se
|
|
|
|
|
decouvre au premier clone, apres quarante minutes de deploiement.
|
|
|
|
|
|
|
|
|
|
N'ECRIT RIEN. Lecture seule, sans effet de bord.
|
|
|
|
|
|
|
|
|
|
python3 scripts/devis_placement.py # tableau lisible
|
|
|
|
|
python3 scripts/devis_placement.py --verifier # code de sortie (0 = tout existe)
|
|
|
|
|
"""
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import argparse
|
devis de placement : mourir n'est pas un diagnostic
Soir de reconstruction, VPN pas encore monte. `make placement-plan` — le devis qu'on lance
AVANT quarante minutes de deploiement — rendait `AttributeError: 'str' object has no
attribute 'get'` : dix lignes de trace Python pour dire « le nom `asgard` ne se resout pas
d'ici ».
En panne, `Cluster.__call__` rend {"_erreur": "..."} — un DICT. Le devis l'iterait comme
une liste, et un dict itere rend ses CLEFS. `Cluster.rate()` existait pour ca et n'etait
appele nulle part ici. Les quatre appels passent desormais par une garde qui nomme la
cause, l'hote interroge et le geste a tenter (resolution, VPN, jeton).
ET LE DEVIS MESURAIT LE MAUVAIS TENANT. `placement_du_tenant()` lisait `instance/` en dur :
viser patient 0 avec SETOPS_INSTANCE mesurait en silence le placement de l'instance
montee. Le verdict etait juste — pour l'autre tenant. Les deux portaient les memes quatre
valeurs, ce qui est exactement la circonstance ou l'erreur ne se voit pas. C'est la
HUITIEME resolution d'instance ou d'inventaire codee en dur trouvee en trois jours ; a ce
compte ce n'est plus une serie de bogues, c'est une piece manquante.
L'en-tete annoncait aussi « tenant instance » — le nom du lien, pas celui du tenant.
TROIS TESTS, aucun reseau touche (Cluster simule) : une panne devient un refus lisible ;
une reponse qui n'est pas une liste est refusee — c'est le cas silencieux, celui qui
franchirait la premiere garde ; le cas nominal traverse sans gene. Branches sur `make test`.
Verifie ensuite contre le cluster reel : le devis nomme « OPS-Patient0 » et confirme ses
quatre objets (asgard, TrueNAS, vmbr1, gabarit 99998).
make test OK ; make verifier 38/38 ; make ci 38/38.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:26:32 -04:00
|
|
|
import os
|
2026-08-13 10:11:25 -04:00
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
|
|
RACINE = Path(__file__).resolve().parents[1]
|
|
|
|
|
sys.path.insert(0, str(RACINE / "scripts"))
|
|
|
|
|
|
placement : le devis validait un pont que la flotte n'utilise pas
Remarque de l'exploitant en preparant patient 0 : « le pont ne me semble pas approprie du
tout, depuis qu'on cree des VNets pour des tenants ». Juste, et plus grave que cosmetique.
`make placement-plan` confrontait `proxmox_clone_pont` (vmbr1) au cluster. Ce n'est PAS la
que les VM de la flotte atterrissent : `instancier` pose dans chaque hote le pont DERIVE
de sa zone (le VNet du tenant), et `make creer-vm` le passe au clone en ecrasant ce
defaut. vmbr1 n'est que le repli des clones MANUELS, hors plan. Le devis mesurait donc un
objet qui ne sert pas, et ignorait celui qui sert.
SUR PATIENT 0 : avant, « pont vmbr1 existe -> CONFORME ». Apres, « reseaux VM : t29appl,
t29donn, t29fron, t29serv INTROUVABLE — passer `make sdn-appliquer` AVANT de creer les
VM ». Aucun de ses quatre VNets n'existe sur le cluster : le devis d'avant-vol declarait
conforme un tenant dont les VM n'auraient eu nulle part ou naitre.
D-80 avait pourtant ete corrigee le 2026-08-13 — la liaison de placement est noeud,
stockage et gabarit, le pont se derive. Le devis continuait de compter quatre objets et de
nommer le mauvais : une doctrine corrigee dans un document ne se propage pas toute seule
dans le code qui l'applique.
MESURE MAINTENANT : en `sdn`, les VNets derives confrontes a /cluster/sdn/vnets ; en
`switch`, les ponts du noeud retenu. Avec le geste correctif quand il en manque.
NON-REGRESSION sur l'ecosysteme de reference : ses six VNets existent, conforme, code 0.
Quatre tests (Cluster simule, aucun reseau touche), harnais 38/38.
Au passage, dans patient 0 : le commentaire annoncait « les QUATRE valeurs qui rattachent
un tenant a une fabric » — trois, et le pont n'en est pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:39:26 -04:00
|
|
|
import underlay as underlay_mod # noqa: E402
|
resolution d'instance : une seule, partagee — au lieu de neuf copies
Cinq jours, cinq defauts, tous de la meme famille : « quelle instance, quel inventaire ? »
Neuf modules portaient chacun leur reponse.
- 18 aout : P03 comparait chaque instance a l'inventaire d'une AUTRE ;
- 19 aout : verifier_ports codait `principal/` en dur ; verifier_intrants et
_frontiere_absente lisaient le symlink au lieu de la variable ;
- 20 aout : devis_placement rendait un verdict juste sur le mauvais tenant ;
- 22 aout : P35, puis P36 — la dixieme, trouvee par la preuve elle-meme.
Aucune n'etait une faute d'inattention : chacune avait ete ecrite de bonne foi, a un
moment ou le besoin semblait local. C'est le mode de panne de la duplication — pas
l'erreur, mais la DERIVE, invisible depuis l'interieur d'un fichier.
LA RESOLUTION UNIQUE. `inventory_rules` porte instance_courante(), inventaire_de(),
dossier_inventaire() et plan_de(). Trois niveaux de repli, dont le TROISIEME manquait a la
moitie des copies : un hosts.yml existant, puis un REPERTOIRE existant (instance neuve —
c'est ce qui faisait echouer `make instancier` sur le modele public), puis le defaut.
Vingt-huit modules y sont branches.
CE QUI REND CE REFACTOR SUR : avant de toucher quoi que ce soit, chaque module a ete
interroge sur ce qu'il resolvait, pour les DEUX ecosystemes. Apres refactor, meme mesure :
17 modules x 2 instances, diff VIDE. Aucune resolution n'a change — prouve, pas suppose.
P41 echoue des qu'un module reintroduit une copie. Eprouvee en negatif : une copie
replacee dans genome.py est signalee avec son numero de ligne. Trois exemptions nommees :
instances.py et inventory_gui.py manipulent le SYMLINK lui-meme (bascule d'instance), et
devis_opnsense lit deliberement quelle instance est ACTIVE. Elles parlent du lien, pas de
la resolution.
make verifier 41 OK, 0 echec, 0 saute ; make ci idem ; lint vert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 14:19:55 -04:00
|
|
|
from inventory_rules import instance_courante # noqa: E402
|
2026-08-13 10:11:25 -04:00
|
|
|
from proxmox_api import Cluster # noqa: E402
|
|
|
|
|
|
|
|
|
|
INVENTAIRES = ("principal", "production", "lab")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def placement_du_tenant() -> tuple[dict, Path | None]:
|
devis de placement : mourir n'est pas un diagnostic
Soir de reconstruction, VPN pas encore monte. `make placement-plan` — le devis qu'on lance
AVANT quarante minutes de deploiement — rendait `AttributeError: 'str' object has no
attribute 'get'` : dix lignes de trace Python pour dire « le nom `asgard` ne se resout pas
d'ici ».
En panne, `Cluster.__call__` rend {"_erreur": "..."} — un DICT. Le devis l'iterait comme
une liste, et un dict itere rend ses CLEFS. `Cluster.rate()` existait pour ca et n'etait
appele nulle part ici. Les quatre appels passent desormais par une garde qui nomme la
cause, l'hote interroge et le geste a tenter (resolution, VPN, jeton).
ET LE DEVIS MESURAIT LE MAUVAIS TENANT. `placement_du_tenant()` lisait `instance/` en dur :
viser patient 0 avec SETOPS_INSTANCE mesurait en silence le placement de l'instance
montee. Le verdict etait juste — pour l'autre tenant. Les deux portaient les memes quatre
valeurs, ce qui est exactement la circonstance ou l'erreur ne se voit pas. C'est la
HUITIEME resolution d'instance ou d'inventaire codee en dur trouvee en trois jours ; a ce
compte ce n'est plus une serie de bogues, c'est une piece manquante.
L'en-tete annoncait aussi « tenant instance » — le nom du lien, pas celui du tenant.
TROIS TESTS, aucun reseau touche (Cluster simule) : une panne devient un refus lisible ;
une reponse qui n'est pas une liste est refusee — c'est le cas silencieux, celui qui
franchirait la premiere garde ; le cas nominal traverse sans gene. Branches sur `make test`.
Verifie ensuite contre le cluster reel : le devis nomme « OPS-Patient0 » et confirme ses
quatre objets (asgard, TrueNAS, vmbr1, gabarit 99998).
make test OK ; make verifier 38/38 ; make ci 38/38.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:26:32 -04:00
|
|
|
"""Les valeurs de placement declarees par le tenant VISE.
|
|
|
|
|
|
|
|
|
|
SETOPS_INSTANCE d'abord ; le symlink `instance/` n'en est que le cas courant. Code en
|
2026-09-27 21:53:36 -04:00
|
|
|
dur, il rendait ce devis incapable de regarder un AUTRE tenant : viser un autre tenant
|
devis de placement : mourir n'est pas un diagnostic
Soir de reconstruction, VPN pas encore monte. `make placement-plan` — le devis qu'on lance
AVANT quarante minutes de deploiement — rendait `AttributeError: 'str' object has no
attribute 'get'` : dix lignes de trace Python pour dire « le nom `asgard` ne se resout pas
d'ici ».
En panne, `Cluster.__call__` rend {"_erreur": "..."} — un DICT. Le devis l'iterait comme
une liste, et un dict itere rend ses CLEFS. `Cluster.rate()` existait pour ca et n'etait
appele nulle part ici. Les quatre appels passent desormais par une garde qui nomme la
cause, l'hote interroge et le geste a tenter (resolution, VPN, jeton).
ET LE DEVIS MESURAIT LE MAUVAIS TENANT. `placement_du_tenant()` lisait `instance/` en dur :
viser patient 0 avec SETOPS_INSTANCE mesurait en silence le placement de l'instance
montee. Le verdict etait juste — pour l'autre tenant. Les deux portaient les memes quatre
valeurs, ce qui est exactement la circonstance ou l'erreur ne se voit pas. C'est la
HUITIEME resolution d'instance ou d'inventaire codee en dur trouvee en trois jours ; a ce
compte ce n'est plus une serie de bogues, c'est une piece manquante.
L'en-tete annoncait aussi « tenant instance » — le nom du lien, pas celui du tenant.
TROIS TESTS, aucun reseau touche (Cluster simule) : une panne devient un refus lisible ;
une reponse qui n'est pas une liste est refusee — c'est le cas silencieux, celui qui
franchirait la premiere garde ; le cas nominal traverse sans gene. Branches sur `make test`.
Verifie ensuite contre le cluster reel : le devis nomme « OPS-Patient0 » et confirme ses
quatre objets (asgard, TrueNAS, vmbr1, gabarit 99998).
make test OK ; make verifier 38/38 ; make ci 38/38.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:26:32 -04:00
|
|
|
mesurait en silence le placement de l'instance montee, et rendait un verdict juste
|
|
|
|
|
pour le mauvais tenant (mesure du 2026-08-20 — les deux avaient les memes quatre
|
|
|
|
|
valeurs, ce qui est exactement la circonstance ou l'erreur ne se voit pas).
|
|
|
|
|
"""
|
resolution d'instance : une seule, partagee — au lieu de neuf copies
Cinq jours, cinq defauts, tous de la meme famille : « quelle instance, quel inventaire ? »
Neuf modules portaient chacun leur reponse.
- 18 aout : P03 comparait chaque instance a l'inventaire d'une AUTRE ;
- 19 aout : verifier_ports codait `principal/` en dur ; verifier_intrants et
_frontiere_absente lisaient le symlink au lieu de la variable ;
- 20 aout : devis_placement rendait un verdict juste sur le mauvais tenant ;
- 22 aout : P35, puis P36 — la dixieme, trouvee par la preuve elle-meme.
Aucune n'etait une faute d'inattention : chacune avait ete ecrite de bonne foi, a un
moment ou le besoin semblait local. C'est le mode de panne de la duplication — pas
l'erreur, mais la DERIVE, invisible depuis l'interieur d'un fichier.
LA RESOLUTION UNIQUE. `inventory_rules` porte instance_courante(), inventaire_de(),
dossier_inventaire() et plan_de(). Trois niveaux de repli, dont le TROISIEME manquait a la
moitie des copies : un hosts.yml existant, puis un REPERTOIRE existant (instance neuve —
c'est ce qui faisait echouer `make instancier` sur le modele public), puis le defaut.
Vingt-huit modules y sont branches.
CE QUI REND CE REFACTOR SUR : avant de toucher quoi que ce soit, chaque module a ete
interroge sur ce qu'il resolvait, pour les DEUX ecosystemes. Apres refactor, meme mesure :
17 modules x 2 instances, diff VIDE. Aucune resolution n'a change — prouve, pas suppose.
P41 echoue des qu'un module reintroduit une copie. Eprouvee en negatif : une copie
replacee dans genome.py est signalee avec son numero de ligne. Trois exemptions nommees :
instances.py et inventory_gui.py manipulent le SYMLINK lui-meme (bascule d'instance), et
devis_opnsense lit deliberement quelle instance est ACTIVE. Elles parlent du lien, pas de
la resolution.
make verifier 41 OK, 0 echec, 0 saute ; make ci idem ; lint vert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 14:19:55 -04:00
|
|
|
base = instance_courante()
|
2026-08-13 10:11:25 -04:00
|
|
|
for nom in INVENTAIRES:
|
devis de placement : mourir n'est pas un diagnostic
Soir de reconstruction, VPN pas encore monte. `make placement-plan` — le devis qu'on lance
AVANT quarante minutes de deploiement — rendait `AttributeError: 'str' object has no
attribute 'get'` : dix lignes de trace Python pour dire « le nom `asgard` ne se resout pas
d'ici ».
En panne, `Cluster.__call__` rend {"_erreur": "..."} — un DICT. Le devis l'iterait comme
une liste, et un dict itere rend ses CLEFS. `Cluster.rate()` existait pour ca et n'etait
appele nulle part ici. Les quatre appels passent desormais par une garde qui nomme la
cause, l'hote interroge et le geste a tenter (resolution, VPN, jeton).
ET LE DEVIS MESURAIT LE MAUVAIS TENANT. `placement_du_tenant()` lisait `instance/` en dur :
viser patient 0 avec SETOPS_INSTANCE mesurait en silence le placement de l'instance
montee. Le verdict etait juste — pour l'autre tenant. Les deux portaient les memes quatre
valeurs, ce qui est exactement la circonstance ou l'erreur ne se voit pas. C'est la
HUITIEME resolution d'instance ou d'inventaire codee en dur trouvee en trois jours ; a ce
compte ce n'est plus une serie de bogues, c'est une piece manquante.
L'en-tete annoncait aussi « tenant instance » — le nom du lien, pas celui du tenant.
TROIS TESTS, aucun reseau touche (Cluster simule) : une panne devient un refus lisible ;
une reponse qui n'est pas une liste est refusee — c'est le cas silencieux, celui qui
franchirait la premiere garde ; le cas nominal traverse sans gene. Branches sur `make test`.
Verifie ensuite contre le cluster reel : le devis nomme « OPS-Patient0 » et confirme ses
quatre objets (asgard, TrueNAS, vmbr1, gabarit 99998).
make test OK ; make verifier 38/38 ; make ci 38/38.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:26:32 -04:00
|
|
|
f = base / "inventories" / nom / "group_vars" / "proxmox.yml"
|
2026-08-13 10:11:25 -04:00
|
|
|
if f.is_file():
|
|
|
|
|
return (yaml.safe_load(f.read_text(encoding="utf-8")) or {}), f
|
|
|
|
|
return {}, None
|
|
|
|
|
|
|
|
|
|
|
devis de placement : mourir n'est pas un diagnostic
Soir de reconstruction, VPN pas encore monte. `make placement-plan` — le devis qu'on lance
AVANT quarante minutes de deploiement — rendait `AttributeError: 'str' object has no
attribute 'get'` : dix lignes de trace Python pour dire « le nom `asgard` ne se resout pas
d'ici ».
En panne, `Cluster.__call__` rend {"_erreur": "..."} — un DICT. Le devis l'iterait comme
une liste, et un dict itere rend ses CLEFS. `Cluster.rate()` existait pour ca et n'etait
appele nulle part ici. Les quatre appels passent desormais par une garde qui nomme la
cause, l'hote interroge et le geste a tenter (resolution, VPN, jeton).
ET LE DEVIS MESURAIT LE MAUVAIS TENANT. `placement_du_tenant()` lisait `instance/` en dur :
viser patient 0 avec SETOPS_INSTANCE mesurait en silence le placement de l'instance
montee. Le verdict etait juste — pour l'autre tenant. Les deux portaient les memes quatre
valeurs, ce qui est exactement la circonstance ou l'erreur ne se voit pas. C'est la
HUITIEME resolution d'instance ou d'inventaire codee en dur trouvee en trois jours ; a ce
compte ce n'est plus une serie de bogues, c'est une piece manquante.
L'en-tete annoncait aussi « tenant instance » — le nom du lien, pas celui du tenant.
TROIS TESTS, aucun reseau touche (Cluster simule) : une panne devient un refus lisible ;
une reponse qui n'est pas une liste est refusee — c'est le cas silencieux, celui qui
franchirait la premiere garde ; le cas nominal traverse sans gene. Branches sur `make test`.
Verifie ensuite contre le cluster reel : le devis nomme « OPS-Patient0 » et confirme ses
quatre objets (asgard, TrueNAS, vmbr1, gabarit 99998).
make test OK ; make verifier 38/38 ; make ci 38/38.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:26:32 -04:00
|
|
|
def _liste(api: Cluster, chemin: str) -> list:
|
|
|
|
|
"""Une reponse de l'API garantie ITERABLE — ou un refus lisible.
|
|
|
|
|
|
|
|
|
|
POURQUOI (mesure du 2026-08-20, un soir de reconstruction). Sur erreur,
|
|
|
|
|
`Cluster.__call__` rend `{"_erreur": "..."}` — un DICT. Itere comme une liste, un
|
|
|
|
|
dict rend ses CLEFS : le devis mourait sur
|
|
|
|
|
`AttributeError: 'str' object has no attribute 'get'`, dix lignes de trace Python
|
|
|
|
|
pour dire « le nom `asgard` ne se resout pas ».
|
|
|
|
|
|
|
|
|
|
C'est le pire endroit pour un message illisible : ce devis est precisement ce qu'on
|
|
|
|
|
lance AVANT quarante minutes de deploiement, quand on cherche a savoir si le terrain
|
|
|
|
|
est bon. `Cluster.rate()` existait pour ca ; il n'etait pas appele ici.
|
|
|
|
|
"""
|
|
|
|
|
rep = api(chemin)
|
|
|
|
|
faute = api.rate(rep)
|
|
|
|
|
if faute:
|
|
|
|
|
raise SystemExit(
|
|
|
|
|
f"Le cluster ne repond pas sur `{chemin}` : {faute}\n"
|
|
|
|
|
f" hote interroge : {api.hote}\n"
|
|
|
|
|
f" a verifier : le nom se resout-il d'ici, le VPN est-il monte, "
|
|
|
|
|
f"le jeton d'API est-il encore valide ?")
|
|
|
|
|
if not isinstance(rep, list):
|
|
|
|
|
raise SystemExit(
|
|
|
|
|
f"Reponse inattendue du cluster sur `{chemin}` : "
|
|
|
|
|
f"{type(rep).__name__} au lieu d'une liste.\n"
|
|
|
|
|
f" hote interroge : {api.hote}")
|
|
|
|
|
return rep
|
|
|
|
|
|
|
|
|
|
|
placement : le devis validait un pont que la flotte n'utilise pas
Remarque de l'exploitant en preparant patient 0 : « le pont ne me semble pas approprie du
tout, depuis qu'on cree des VNets pour des tenants ». Juste, et plus grave que cosmetique.
`make placement-plan` confrontait `proxmox_clone_pont` (vmbr1) au cluster. Ce n'est PAS la
que les VM de la flotte atterrissent : `instancier` pose dans chaque hote le pont DERIVE
de sa zone (le VNet du tenant), et `make creer-vm` le passe au clone en ecrasant ce
defaut. vmbr1 n'est que le repli des clones MANUELS, hors plan. Le devis mesurait donc un
objet qui ne sert pas, et ignorait celui qui sert.
SUR PATIENT 0 : avant, « pont vmbr1 existe -> CONFORME ». Apres, « reseaux VM : t29appl,
t29donn, t29fron, t29serv INTROUVABLE — passer `make sdn-appliquer` AVANT de creer les
VM ». Aucun de ses quatre VNets n'existe sur le cluster : le devis d'avant-vol declarait
conforme un tenant dont les VM n'auraient eu nulle part ou naitre.
D-80 avait pourtant ete corrigee le 2026-08-13 — la liaison de placement est noeud,
stockage et gabarit, le pont se derive. Le devis continuait de compter quatre objets et de
nommer le mauvais : une doctrine corrigee dans un document ne se propage pas toute seule
dans le code qui l'applique.
MESURE MAINTENANT : en `sdn`, les VNets derives confrontes a /cluster/sdn/vnets ; en
`switch`, les ponts du noeud retenu. Avec le geste correctif quand il en manque.
NON-REGRESSION sur l'ecosysteme de reference : ses six VNets existent, conforme, code 0.
Quatre tests (Cluster simule, aucun reseau touche), harnais 38/38.
Au passage, dans patient 0 : le commentaire annoncait « les QUATRE valeurs qui rattachent
un tenant a une fabric » — trois, et le pont n'en est pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:39:26 -04:00
|
|
|
def _ponts_derives(base: Path | None) -> set[str]:
|
|
|
|
|
"""Les reseaux ou les VM de CE tenant atterriront reellement.
|
|
|
|
|
|
|
|
|
|
Ce ne sont pas les valeurs du plan : `instancier` pose dans chaque hote le pont
|
|
|
|
|
DERIVE de sa zone (`proxmox_pont` = le VNet du tenant), et `make creer-vm` le passe au
|
|
|
|
|
clone en ecrasant le defaut. C'est donc la seule chose a confronter au cluster.
|
|
|
|
|
"""
|
|
|
|
|
ponts: set[str] = set()
|
|
|
|
|
if base is None:
|
|
|
|
|
return ponts
|
|
|
|
|
for f in sorted(base.glob("inventories/*/hosts.yml")):
|
|
|
|
|
data = yaml.safe_load(f.read_text(encoding="utf-8")) or {}
|
|
|
|
|
|
|
|
|
|
def marcher(noeud):
|
|
|
|
|
if not isinstance(noeud, dict):
|
|
|
|
|
return
|
|
|
|
|
for hote in (noeud.get("hosts") or {}).values():
|
|
|
|
|
if isinstance(hote, dict) and hote.get("proxmox_pont"):
|
|
|
|
|
ponts.add(str(hote["proxmox_pont"]))
|
|
|
|
|
for enfant in (noeud.get("children") or {}).values():
|
|
|
|
|
marcher(enfant or {})
|
|
|
|
|
marcher((data.get("all") or {}))
|
|
|
|
|
return ponts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def constater(api: Cluster, place: dict, base: Path | None = None,
|
|
|
|
|
routage: str = "sdn") -> list[dict]:
|
2026-08-13 10:11:25 -04:00
|
|
|
"""Confronte chaque valeur au cluster. Renvoie une ligne par objet."""
|
|
|
|
|
lignes: list[dict] = []
|
placement : le devis validait un pont que la flotte n'utilise pas
Remarque de l'exploitant en preparant patient 0 : « le pont ne me semble pas approprie du
tout, depuis qu'on cree des VNets pour des tenants ». Juste, et plus grave que cosmetique.
`make placement-plan` confrontait `proxmox_clone_pont` (vmbr1) au cluster. Ce n'est PAS la
que les VM de la flotte atterrissent : `instancier` pose dans chaque hote le pont DERIVE
de sa zone (le VNet du tenant), et `make creer-vm` le passe au clone en ecrasant ce
defaut. vmbr1 n'est que le repli des clones MANUELS, hors plan. Le devis mesurait donc un
objet qui ne sert pas, et ignorait celui qui sert.
SUR PATIENT 0 : avant, « pont vmbr1 existe -> CONFORME ». Apres, « reseaux VM : t29appl,
t29donn, t29fron, t29serv INTROUVABLE — passer `make sdn-appliquer` AVANT de creer les
VM ». Aucun de ses quatre VNets n'existe sur le cluster : le devis d'avant-vol declarait
conforme un tenant dont les VM n'auraient eu nulle part ou naitre.
D-80 avait pourtant ete corrigee le 2026-08-13 — la liaison de placement est noeud,
stockage et gabarit, le pont se derive. Le devis continuait de compter quatre objets et de
nommer le mauvais : une doctrine corrigee dans un document ne se propage pas toute seule
dans le code qui l'applique.
MESURE MAINTENANT : en `sdn`, les VNets derives confrontes a /cluster/sdn/vnets ; en
`switch`, les ponts du noeud retenu. Avec le geste correctif quand il en manque.
NON-REGRESSION sur l'ecosysteme de reference : ses six VNets existent, conforme, code 0.
Quatre tests (Cluster simule, aucun reseau touche), harnais 38/38.
Au passage, dans patient 0 : le commentaire annoncait « les QUATRE valeurs qui rattachent
un tenant a une fabric » — trois, et le pont n'en est pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:39:26 -04:00
|
|
|
place = {**place, "_instance": base}
|
2026-08-13 10:11:25 -04:00
|
|
|
|
devis de placement : mourir n'est pas un diagnostic
Soir de reconstruction, VPN pas encore monte. `make placement-plan` — le devis qu'on lance
AVANT quarante minutes de deploiement — rendait `AttributeError: 'str' object has no
attribute 'get'` : dix lignes de trace Python pour dire « le nom `asgard` ne se resout pas
d'ici ».
En panne, `Cluster.__call__` rend {"_erreur": "..."} — un DICT. Le devis l'iterait comme
une liste, et un dict itere rend ses CLEFS. `Cluster.rate()` existait pour ca et n'etait
appele nulle part ici. Les quatre appels passent desormais par une garde qui nomme la
cause, l'hote interroge et le geste a tenter (resolution, VPN, jeton).
ET LE DEVIS MESURAIT LE MAUVAIS TENANT. `placement_du_tenant()` lisait `instance/` en dur :
viser patient 0 avec SETOPS_INSTANCE mesurait en silence le placement de l'instance
montee. Le verdict etait juste — pour l'autre tenant. Les deux portaient les memes quatre
valeurs, ce qui est exactement la circonstance ou l'erreur ne se voit pas. C'est la
HUITIEME resolution d'instance ou d'inventaire codee en dur trouvee en trois jours ; a ce
compte ce n'est plus une serie de bogues, c'est une piece manquante.
L'en-tete annoncait aussi « tenant instance » — le nom du lien, pas celui du tenant.
TROIS TESTS, aucun reseau touche (Cluster simule) : une panne devient un refus lisible ;
une reponse qui n'est pas une liste est refusee — c'est le cas silencieux, celui qui
franchirait la premiere garde ; le cas nominal traverse sans gene. Branches sur `make test`.
Verifie ensuite contre le cluster reel : le devis nomme « OPS-Patient0 » et confirme ses
quatre objets (asgard, TrueNAS, vmbr1, gabarit 99998).
make test OK ; make verifier 38/38 ; make ci 38/38.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:26:32 -04:00
|
|
|
noeuds = [n.get("node") for n in _liste(api, "/nodes")]
|
2026-08-13 10:11:25 -04:00
|
|
|
voulu = place.get("proxmox_clone_noeud")
|
|
|
|
|
lignes.append({"objet": "noeud", "voulu": voulu,
|
|
|
|
|
"existe": voulu in noeuds, "offre": noeuds})
|
|
|
|
|
|
|
|
|
|
# Un stockage ne suffit pas : il doit accepter le contenu `images`, sinon on ne peut
|
|
|
|
|
# pas y poser un disque de VM — et l'erreur ne se lit qu'au clone.
|
|
|
|
|
stockages = {}
|
devis de placement : mourir n'est pas un diagnostic
Soir de reconstruction, VPN pas encore monte. `make placement-plan` — le devis qu'on lance
AVANT quarante minutes de deploiement — rendait `AttributeError: 'str' object has no
attribute 'get'` : dix lignes de trace Python pour dire « le nom `asgard` ne se resout pas
d'ici ».
En panne, `Cluster.__call__` rend {"_erreur": "..."} — un DICT. Le devis l'iterait comme
une liste, et un dict itere rend ses CLEFS. `Cluster.rate()` existait pour ca et n'etait
appele nulle part ici. Les quatre appels passent desormais par une garde qui nomme la
cause, l'hote interroge et le geste a tenter (resolution, VPN, jeton).
ET LE DEVIS MESURAIT LE MAUVAIS TENANT. `placement_du_tenant()` lisait `instance/` en dur :
viser patient 0 avec SETOPS_INSTANCE mesurait en silence le placement de l'instance
montee. Le verdict etait juste — pour l'autre tenant. Les deux portaient les memes quatre
valeurs, ce qui est exactement la circonstance ou l'erreur ne se voit pas. C'est la
HUITIEME resolution d'instance ou d'inventaire codee en dur trouvee en trois jours ; a ce
compte ce n'est plus une serie de bogues, c'est une piece manquante.
L'en-tete annoncait aussi « tenant instance » — le nom du lien, pas celui du tenant.
TROIS TESTS, aucun reseau touche (Cluster simule) : une panne devient un refus lisible ;
une reponse qui n'est pas une liste est refusee — c'est le cas silencieux, celui qui
franchirait la premiere garde ; le cas nominal traverse sans gene. Branches sur `make test`.
Verifie ensuite contre le cluster reel : le devis nomme « OPS-Patient0 » et confirme ses
quatre objets (asgard, TrueNAS, vmbr1, gabarit 99998).
make test OK ; make verifier 38/38 ; make ci 38/38.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:26:32 -04:00
|
|
|
for s in _liste(api, "/storage"):
|
2026-08-13 10:11:25 -04:00
|
|
|
stockages[s.get("storage")] = str(s.get("content") or "")
|
|
|
|
|
voulu = place.get("proxmox_clone_stockage")
|
|
|
|
|
lignes.append({"objet": "stockage", "voulu": voulu,
|
|
|
|
|
"existe": voulu in stockages and "images" in stockages[voulu],
|
|
|
|
|
"offre": [k for k, v in stockages.items() if "images" in v],
|
|
|
|
|
"note": ("" if voulu not in stockages or "images" in stockages[voulu]
|
|
|
|
|
else f"present mais n'accepte pas `images` ({stockages[voulu]})")})
|
|
|
|
|
|
placement : le devis validait un pont que la flotte n'utilise pas
Remarque de l'exploitant en preparant patient 0 : « le pont ne me semble pas approprie du
tout, depuis qu'on cree des VNets pour des tenants ». Juste, et plus grave que cosmetique.
`make placement-plan` confrontait `proxmox_clone_pont` (vmbr1) au cluster. Ce n'est PAS la
que les VM de la flotte atterrissent : `instancier` pose dans chaque hote le pont DERIVE
de sa zone (le VNet du tenant), et `make creer-vm` le passe au clone en ecrasant ce
defaut. vmbr1 n'est que le repli des clones MANUELS, hors plan. Le devis mesurait donc un
objet qui ne sert pas, et ignorait celui qui sert.
SUR PATIENT 0 : avant, « pont vmbr1 existe -> CONFORME ». Apres, « reseaux VM : t29appl,
t29donn, t29fron, t29serv INTROUVABLE — passer `make sdn-appliquer` AVANT de creer les
VM ». Aucun de ses quatre VNets n'existe sur le cluster : le devis d'avant-vol declarait
conforme un tenant dont les VM n'auraient eu nulle part ou naitre.
D-80 avait pourtant ete corrigee le 2026-08-13 — la liaison de placement est noeud,
stockage et gabarit, le pont se derive. Le devis continuait de compter quatre objets et de
nommer le mauvais : une doctrine corrigee dans un document ne se propage pas toute seule
dans le code qui l'applique.
MESURE MAINTENANT : en `sdn`, les VNets derives confrontes a /cluster/sdn/vnets ; en
`switch`, les ponts du noeud retenu. Avec le geste correctif quand il en manque.
NON-REGRESSION sur l'ecosysteme de reference : ses six VNets existent, conforme, code 0.
Quatre tests (Cluster simule, aucun reseau touche), harnais 38/38.
Au passage, dans patient 0 : le commentaire annoncait « les QUATRE valeurs qui rattachent
un tenant a une fabric » — trois, et le pont n'en est pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:39:26 -04:00
|
|
|
# LE RESEAU OU LES VM ATTERRISSENT — pas `proxmox_clone_pont`.
|
|
|
|
|
#
|
|
|
|
|
# Mesure du 2026-08-20, remarque de l'exploitant : « le pont ne me semble pas approprie
|
|
|
|
|
# depuis qu'on cree des VNets par tenant ». Il avait raison, et le defaut etait pire
|
|
|
|
|
# que cosmetique. Ce devis validait `vmbr1` — le REPLI des clones manuels — pendant que
|
|
|
|
|
# le point d'attache reel des VM de la flotte n'etait controle par personne :
|
|
|
|
|
# `creer-vm` lit le pont DERIVE de chaque hote (`SETOPS_PONT`, le VNet de sa zone) et
|
|
|
|
|
# ecrase le defaut. Resultat : « CONFORME » sur un tenant dont aucun VNet n'existait.
|
|
|
|
|
#
|
|
|
|
|
# D-80 corrigee le 2026-08-13 dit deja que la liaison de placement est nœud, stockage
|
|
|
|
|
# et gabarit — le pont se derive. Le devis, lui, continuait de compter quatre objets.
|
|
|
|
|
ponts_derives = sorted(_ponts_derives(place.get("_instance")))
|
|
|
|
|
if routage == "sdn":
|
|
|
|
|
vnets = {v.get("vnet") for v in _liste(api, "/cluster/sdn/vnets")}
|
|
|
|
|
manquants = [p for p in ponts_derives if p not in vnets]
|
|
|
|
|
lignes.append({
|
|
|
|
|
"objet": "reseaux VM", "voulu": ", ".join(ponts_derives) or "(aucun)",
|
|
|
|
|
"existe": bool(ponts_derives) and not manquants,
|
|
|
|
|
"offre": sorted(vnets),
|
|
|
|
|
"note": ("" if not manquants else
|
|
|
|
|
f"VNet(s) absent(s) du cluster : {', '.join(manquants)} — "
|
|
|
|
|
f"passer `make sdn-appliquer` AVANT de creer les VM")})
|
|
|
|
|
else:
|
|
|
|
|
n = place.get("proxmox_clone_noeud")
|
|
|
|
|
ponts: list[str] = []
|
|
|
|
|
if n in noeuds:
|
|
|
|
|
ponts = [i.get("iface") for i in _liste(api, f"/nodes/{n}/network")
|
|
|
|
|
if str(i.get("type")) in ("bridge", "OVSBridge")]
|
|
|
|
|
manquants = [p for p in ponts_derives if p not in ponts]
|
|
|
|
|
lignes.append({
|
|
|
|
|
"objet": "reseaux VM", "voulu": ", ".join(ponts_derives) or "(aucun)",
|
|
|
|
|
"existe": bool(ponts_derives) and not manquants,
|
|
|
|
|
"offre": ponts,
|
|
|
|
|
"note": ("noeud introuvable : ponts non verifiables" if n not in noeuds else
|
|
|
|
|
"" if not manquants else
|
|
|
|
|
f"pont(s) absent(s) sur {n} : {', '.join(manquants)}")})
|
2026-08-13 10:11:25 -04:00
|
|
|
|
|
|
|
|
# Le gabarit : celui que P37 ne peut PAS voir. Presence, et surtout `template=1` —
|
|
|
|
|
# cloner une VM ordinaire marcherait, et produirait quatorze copies d'une machine
|
|
|
|
|
# vivante.
|
|
|
|
|
vmid = place.get("proxmox_clone_vmid_modele")
|
devis de placement : mourir n'est pas un diagnostic
Soir de reconstruction, VPN pas encore monte. `make placement-plan` — le devis qu'on lance
AVANT quarante minutes de deploiement — rendait `AttributeError: 'str' object has no
attribute 'get'` : dix lignes de trace Python pour dire « le nom `asgard` ne se resout pas
d'ici ».
En panne, `Cluster.__call__` rend {"_erreur": "..."} — un DICT. Le devis l'iterait comme
une liste, et un dict itere rend ses CLEFS. `Cluster.rate()` existait pour ca et n'etait
appele nulle part ici. Les quatre appels passent desormais par une garde qui nomme la
cause, l'hote interroge et le geste a tenter (resolution, VPN, jeton).
ET LE DEVIS MESURAIT LE MAUVAIS TENANT. `placement_du_tenant()` lisait `instance/` en dur :
viser patient 0 avec SETOPS_INSTANCE mesurait en silence le placement de l'instance
montee. Le verdict etait juste — pour l'autre tenant. Les deux portaient les memes quatre
valeurs, ce qui est exactement la circonstance ou l'erreur ne se voit pas. C'est la
HUITIEME resolution d'instance ou d'inventaire codee en dur trouvee en trois jours ; a ce
compte ce n'est plus une serie de bogues, c'est une piece manquante.
L'en-tete annoncait aussi « tenant instance » — le nom du lien, pas celui du tenant.
TROIS TESTS, aucun reseau touche (Cluster simule) : une panne devient un refus lisible ;
une reponse qui n'est pas une liste est refusee — c'est le cas silencieux, celui qui
franchirait la premiere garde ; le cas nominal traverse sans gene. Branches sur `make test`.
Verifie ensuite contre le cluster reel : le devis nomme « OPS-Patient0 » et confirme ses
quatre objets (asgard, TrueNAS, vmbr1, gabarit 99998).
make test OK ; make verifier 38/38 ; make ci 38/38.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:26:32 -04:00
|
|
|
vms = {v.get("vmid"): v for v in _liste(api, "/cluster/resources?type=vm")}
|
2026-08-13 10:11:25 -04:00
|
|
|
trouve = vms.get(int(vmid)) if vmid is not None else None
|
|
|
|
|
est_gabarit = bool(trouve and str(trouve.get("template", 0)) == "1")
|
|
|
|
|
lignes.append({"objet": "gabarit", "voulu": vmid,
|
|
|
|
|
"existe": est_gabarit,
|
|
|
|
|
"offre": [f"{v.get('vmid')} ({v.get('name')})" for v in vms.values()
|
|
|
|
|
if str(v.get("template", 0)) == "1"],
|
|
|
|
|
"note": ("" if est_gabarit else
|
|
|
|
|
(f"VMID {vmid} existe (« {trouve.get('name')} ») mais n'est PAS "
|
|
|
|
|
f"un gabarit" if trouve else f"aucune VM au VMID {vmid}"))})
|
|
|
|
|
return lignes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def afficher(lignes: list[dict], source: Path | None, depot: Path) -> bool:
|
devis de placement : mourir n'est pas un diagnostic
Soir de reconstruction, VPN pas encore monte. `make placement-plan` — le devis qu'on lance
AVANT quarante minutes de deploiement — rendait `AttributeError: 'str' object has no
attribute 'get'` : dix lignes de trace Python pour dire « le nom `asgard` ne se resout pas
d'ici ».
En panne, `Cluster.__call__` rend {"_erreur": "..."} — un DICT. Le devis l'iterait comme
une liste, et un dict itere rend ses CLEFS. `Cluster.rate()` existait pour ca et n'etait
appele nulle part ici. Les quatre appels passent desormais par une garde qui nomme la
cause, l'hote interroge et le geste a tenter (resolution, VPN, jeton).
ET LE DEVIS MESURAIT LE MAUVAIS TENANT. `placement_du_tenant()` lisait `instance/` en dur :
viser patient 0 avec SETOPS_INSTANCE mesurait en silence le placement de l'instance
montee. Le verdict etait juste — pour l'autre tenant. Les deux portaient les memes quatre
valeurs, ce qui est exactement la circonstance ou l'erreur ne se voit pas. C'est la
HUITIEME resolution d'instance ou d'inventaire codee en dur trouvee en trois jours ; a ce
compte ce n'est plus une serie de bogues, c'est une piece manquante.
L'en-tete annoncait aussi « tenant instance » — le nom du lien, pas celui du tenant.
TROIS TESTS, aucun reseau touche (Cluster simule) : une panne devient un refus lisible ;
une reponse qui n'est pas une liste est refusee — c'est le cas silencieux, celui qui
franchirait la premiere garde ; le cas nominal traverse sans gene. Branches sur `make test`.
Verifie ensuite contre le cluster reel : le devis nomme « OPS-Patient0 » et confirme ses
quatre objets (asgard, TrueNAS, vmbr1, gabarit 99998).
make test OK ; make verifier 38/38 ; make ci 38/38.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:26:32 -04:00
|
|
|
# `.resolve()` : sans lui, un tenant monte par symlink s'annonce « instance » — le nom
|
|
|
|
|
# du lien, pas celui du tenant. Un devis doit nommer ce qu'il a mesure.
|
|
|
|
|
tenant = source.parents[3].resolve().name if source else "?"
|
|
|
|
|
print(f"Devis du placement — tenant « {tenant} » sur l'hebergeur « {depot.name} »\n")
|
placement : le devis validait un pont que la flotte n'utilise pas
Remarque de l'exploitant en preparant patient 0 : « le pont ne me semble pas approprie du
tout, depuis qu'on cree des VNets pour des tenants ». Juste, et plus grave que cosmetique.
`make placement-plan` confrontait `proxmox_clone_pont` (vmbr1) au cluster. Ce n'est PAS la
que les VM de la flotte atterrissent : `instancier` pose dans chaque hote le pont DERIVE
de sa zone (le VNet du tenant), et `make creer-vm` le passe au clone en ecrasant ce
defaut. vmbr1 n'est que le repli des clones MANUELS, hors plan. Le devis mesurait donc un
objet qui ne sert pas, et ignorait celui qui sert.
SUR PATIENT 0 : avant, « pont vmbr1 existe -> CONFORME ». Apres, « reseaux VM : t29appl,
t29donn, t29fron, t29serv INTROUVABLE — passer `make sdn-appliquer` AVANT de creer les
VM ». Aucun de ses quatre VNets n'existe sur le cluster : le devis d'avant-vol declarait
conforme un tenant dont les VM n'auraient eu nulle part ou naitre.
D-80 avait pourtant ete corrigee le 2026-08-13 — la liaison de placement est noeud,
stockage et gabarit, le pont se derive. Le devis continuait de compter quatre objets et de
nommer le mauvais : une doctrine corrigee dans un document ne se propage pas toute seule
dans le code qui l'applique.
MESURE MAINTENANT : en `sdn`, les VNets derives confrontes a /cluster/sdn/vnets ; en
`switch`, les ponts du noeud retenu. Avec le geste correctif quand il en manque.
NON-REGRESSION sur l'ecosysteme de reference : ses six VNets existent, conforme, code 0.
Quatre tests (Cluster simule, aucun reseau touche), harnais 38/38.
Au passage, dans patient 0 : le commentaire annoncait « les QUATRE valeurs qui rattachent
un tenant a une fabric » — trois, et le pont n'en est pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:39:26 -04:00
|
|
|
print(f" {'OBJET':<12} {'DEMANDE':<34} {'VERDICT'}")
|
2026-08-13 10:11:25 -04:00
|
|
|
ok = True
|
|
|
|
|
for l in lignes:
|
|
|
|
|
verdict = "existe" if l["existe"] else "INTROUVABLE"
|
|
|
|
|
ok = ok and l["existe"]
|
placement : le devis validait un pont que la flotte n'utilise pas
Remarque de l'exploitant en preparant patient 0 : « le pont ne me semble pas approprie du
tout, depuis qu'on cree des VNets pour des tenants ». Juste, et plus grave que cosmetique.
`make placement-plan` confrontait `proxmox_clone_pont` (vmbr1) au cluster. Ce n'est PAS la
que les VM de la flotte atterrissent : `instancier` pose dans chaque hote le pont DERIVE
de sa zone (le VNet du tenant), et `make creer-vm` le passe au clone en ecrasant ce
defaut. vmbr1 n'est que le repli des clones MANUELS, hors plan. Le devis mesurait donc un
objet qui ne sert pas, et ignorait celui qui sert.
SUR PATIENT 0 : avant, « pont vmbr1 existe -> CONFORME ». Apres, « reseaux VM : t29appl,
t29donn, t29fron, t29serv INTROUVABLE — passer `make sdn-appliquer` AVANT de creer les
VM ». Aucun de ses quatre VNets n'existe sur le cluster : le devis d'avant-vol declarait
conforme un tenant dont les VM n'auraient eu nulle part ou naitre.
D-80 avait pourtant ete corrigee le 2026-08-13 — la liaison de placement est noeud,
stockage et gabarit, le pont se derive. Le devis continuait de compter quatre objets et de
nommer le mauvais : une doctrine corrigee dans un document ne se propage pas toute seule
dans le code qui l'applique.
MESURE MAINTENANT : en `sdn`, les VNets derives confrontes a /cluster/sdn/vnets ; en
`switch`, les ponts du noeud retenu. Avec le geste correctif quand il en manque.
NON-REGRESSION sur l'ecosysteme de reference : ses six VNets existent, conforme, code 0.
Quatre tests (Cluster simule, aucun reseau touche), harnais 38/38.
Au passage, dans patient 0 : le commentaire annoncait « les QUATRE valeurs qui rattachent
un tenant a une fabric » — trois, et le pont n'en est pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:39:26 -04:00
|
|
|
print(f" {l['objet']:<12} {str(l['voulu']):<34} {verdict}")
|
2026-08-13 10:11:25 -04:00
|
|
|
if l.get("note"):
|
placement : le devis validait un pont que la flotte n'utilise pas
Remarque de l'exploitant en preparant patient 0 : « le pont ne me semble pas approprie du
tout, depuis qu'on cree des VNets pour des tenants ». Juste, et plus grave que cosmetique.
`make placement-plan` confrontait `proxmox_clone_pont` (vmbr1) au cluster. Ce n'est PAS la
que les VM de la flotte atterrissent : `instancier` pose dans chaque hote le pont DERIVE
de sa zone (le VNet du tenant), et `make creer-vm` le passe au clone en ecrasant ce
defaut. vmbr1 n'est que le repli des clones MANUELS, hors plan. Le devis mesurait donc un
objet qui ne sert pas, et ignorait celui qui sert.
SUR PATIENT 0 : avant, « pont vmbr1 existe -> CONFORME ». Apres, « reseaux VM : t29appl,
t29donn, t29fron, t29serv INTROUVABLE — passer `make sdn-appliquer` AVANT de creer les
VM ». Aucun de ses quatre VNets n'existe sur le cluster : le devis d'avant-vol declarait
conforme un tenant dont les VM n'auraient eu nulle part ou naitre.
D-80 avait pourtant ete corrigee le 2026-08-13 — la liaison de placement est noeud,
stockage et gabarit, le pont se derive. Le devis continuait de compter quatre objets et de
nommer le mauvais : une doctrine corrigee dans un document ne se propage pas toute seule
dans le code qui l'applique.
MESURE MAINTENANT : en `sdn`, les VNets derives confrontes a /cluster/sdn/vnets ; en
`switch`, les ponts du noeud retenu. Avec le geste correctif quand il en manque.
NON-REGRESSION sur l'ecosysteme de reference : ses six VNets existent, conforme, code 0.
Quatre tests (Cluster simule, aucun reseau touche), harnais 38/38.
Au passage, dans patient 0 : le commentaire annoncait « les QUATRE valeurs qui rattachent
un tenant a une fabric » — trois, et le pont n'en est pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:39:26 -04:00
|
|
|
print(f" {'':<12} {'':<34} -> {l['note']}")
|
2026-08-13 10:11:25 -04:00
|
|
|
if not l["existe"] and l["offre"]:
|
|
|
|
|
offert = ", ".join(str(x) for x in l["offre"][:6])
|
placement : le devis validait un pont que la flotte n'utilise pas
Remarque de l'exploitant en preparant patient 0 : « le pont ne me semble pas approprie du
tout, depuis qu'on cree des VNets pour des tenants ». Juste, et plus grave que cosmetique.
`make placement-plan` confrontait `proxmox_clone_pont` (vmbr1) au cluster. Ce n'est PAS la
que les VM de la flotte atterrissent : `instancier` pose dans chaque hote le pont DERIVE
de sa zone (le VNet du tenant), et `make creer-vm` le passe au clone en ecrasant ce
defaut. vmbr1 n'est que le repli des clones MANUELS, hors plan. Le devis mesurait donc un
objet qui ne sert pas, et ignorait celui qui sert.
SUR PATIENT 0 : avant, « pont vmbr1 existe -> CONFORME ». Apres, « reseaux VM : t29appl,
t29donn, t29fron, t29serv INTROUVABLE — passer `make sdn-appliquer` AVANT de creer les
VM ». Aucun de ses quatre VNets n'existe sur le cluster : le devis d'avant-vol declarait
conforme un tenant dont les VM n'auraient eu nulle part ou naitre.
D-80 avait pourtant ete corrigee le 2026-08-13 — la liaison de placement est noeud,
stockage et gabarit, le pont se derive. Le devis continuait de compter quatre objets et de
nommer le mauvais : une doctrine corrigee dans un document ne se propage pas toute seule
dans le code qui l'applique.
MESURE MAINTENANT : en `sdn`, les VNets derives confrontes a /cluster/sdn/vnets ; en
`switch`, les ponts du noeud retenu. Avec le geste correctif quand il en manque.
NON-REGRESSION sur l'ecosysteme de reference : ses six VNets existent, conforme, code 0.
Quatre tests (Cluster simule, aucun reseau touche), harnais 38/38.
Au passage, dans patient 0 : le commentaire annoncait « les QUATRE valeurs qui rattachent
un tenant a une fabric » — trois, et le pont n'en est pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:39:26 -04:00
|
|
|
print(f" {'':<12} {'':<34} offert : {offert}")
|
|
|
|
|
print("\n" + ("CONFORME : le noeud, le stockage, le gabarit et les reseaux des VM "
|
|
|
|
|
"existent sur ce cluster." if ok else
|
|
|
|
|
"ECART : le tenant a besoin d'objets que ce cluster n'offre pas."))
|
2026-08-13 10:11:25 -04:00
|
|
|
return ok
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> int:
|
|
|
|
|
ap = argparse.ArgumentParser(description=__doc__)
|
|
|
|
|
ap.add_argument("--verifier", action="store_true", help="code de sortie seulement")
|
|
|
|
|
a = ap.parse_args()
|
|
|
|
|
|
|
|
|
|
place, source = placement_du_tenant()
|
|
|
|
|
if not place:
|
|
|
|
|
print("Aucun placement declare par le tenant monte — rien a confronter.")
|
|
|
|
|
return 0
|
|
|
|
|
try:
|
|
|
|
|
api, depot = Cluster.depuis_hebergeur()
|
|
|
|
|
except Exception as e: # noqa: BLE001 — l'API est un tiers : on rend la cause lisible
|
|
|
|
|
print(f"Cluster injoignable : {e}", file=sys.stderr)
|
|
|
|
|
return 2
|
|
|
|
|
|
placement : le devis validait un pont que la flotte n'utilise pas
Remarque de l'exploitant en preparant patient 0 : « le pont ne me semble pas approprie du
tout, depuis qu'on cree des VNets pour des tenants ». Juste, et plus grave que cosmetique.
`make placement-plan` confrontait `proxmox_clone_pont` (vmbr1) au cluster. Ce n'est PAS la
que les VM de la flotte atterrissent : `instancier` pose dans chaque hote le pont DERIVE
de sa zone (le VNet du tenant), et `make creer-vm` le passe au clone en ecrasant ce
defaut. vmbr1 n'est que le repli des clones MANUELS, hors plan. Le devis mesurait donc un
objet qui ne sert pas, et ignorait celui qui sert.
SUR PATIENT 0 : avant, « pont vmbr1 existe -> CONFORME ». Apres, « reseaux VM : t29appl,
t29donn, t29fron, t29serv INTROUVABLE — passer `make sdn-appliquer` AVANT de creer les
VM ». Aucun de ses quatre VNets n'existe sur le cluster : le devis d'avant-vol declarait
conforme un tenant dont les VM n'auraient eu nulle part ou naitre.
D-80 avait pourtant ete corrigee le 2026-08-13 — la liaison de placement est noeud,
stockage et gabarit, le pont se derive. Le devis continuait de compter quatre objets et de
nommer le mauvais : une doctrine corrigee dans un document ne se propage pas toute seule
dans le code qui l'applique.
MESURE MAINTENANT : en `sdn`, les VNets derives confrontes a /cluster/sdn/vnets ; en
`switch`, les ponts du noeud retenu. Avec le geste correctif quand il en manque.
NON-REGRESSION sur l'ecosysteme de reference : ses six VNets existent, conforme, code 0.
Quatre tests (Cluster simule, aucun reseau touche), harnais 38/38.
Au passage, dans patient 0 : le commentaire annoncait « les QUATRE valeurs qui rattachent
un tenant a une fabric » — trois, et le pont n'en est pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:39:26 -04:00
|
|
|
base = source.parents[3] if source else None
|
|
|
|
|
routage = underlay_mod.routage_tenants(underlay_mod.charger())
|
|
|
|
|
lignes = constater(api, place, base, routage)
|
2026-08-13 10:11:25 -04:00
|
|
|
if a.verifier:
|
|
|
|
|
mauvais = [l for l in lignes if not l["existe"]]
|
|
|
|
|
if mauvais:
|
|
|
|
|
for l in mauvais:
|
|
|
|
|
print(f"placement : {l['objet']} « {l['voulu']} » introuvable"
|
|
|
|
|
+ (f" — {l['note']}" if l.get("note") else ""), file=sys.stderr)
|
|
|
|
|
return 1
|
placement : le devis validait un pont que la flotte n'utilise pas
Remarque de l'exploitant en preparant patient 0 : « le pont ne me semble pas approprie du
tout, depuis qu'on cree des VNets pour des tenants ». Juste, et plus grave que cosmetique.
`make placement-plan` confrontait `proxmox_clone_pont` (vmbr1) au cluster. Ce n'est PAS la
que les VM de la flotte atterrissent : `instancier` pose dans chaque hote le pont DERIVE
de sa zone (le VNet du tenant), et `make creer-vm` le passe au clone en ecrasant ce
defaut. vmbr1 n'est que le repli des clones MANUELS, hors plan. Le devis mesurait donc un
objet qui ne sert pas, et ignorait celui qui sert.
SUR PATIENT 0 : avant, « pont vmbr1 existe -> CONFORME ». Apres, « reseaux VM : t29appl,
t29donn, t29fron, t29serv INTROUVABLE — passer `make sdn-appliquer` AVANT de creer les
VM ». Aucun de ses quatre VNets n'existe sur le cluster : le devis d'avant-vol declarait
conforme un tenant dont les VM n'auraient eu nulle part ou naitre.
D-80 avait pourtant ete corrigee le 2026-08-13 — la liaison de placement est noeud,
stockage et gabarit, le pont se derive. Le devis continuait de compter quatre objets et de
nommer le mauvais : une doctrine corrigee dans un document ne se propage pas toute seule
dans le code qui l'applique.
MESURE MAINTENANT : en `sdn`, les VNets derives confrontes a /cluster/sdn/vnets ; en
`switch`, les ponts du noeud retenu. Avec le geste correctif quand il en manque.
NON-REGRESSION sur l'ecosysteme de reference : ses six VNets existent, conforme, code 0.
Quatre tests (Cluster simule, aucun reseau touche), harnais 38/38.
Au passage, dans patient 0 : le commentaire annoncait « les QUATRE valeurs qui rattachent
un tenant a une fabric » — trois, et le pont n'en est pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:39:26 -04:00
|
|
|
print(f"Placement conforme : noeud, stockage, gabarit et reseaux des VM "
|
|
|
|
|
f"existent sur « {depot.name} ».")
|
2026-08-13 10:11:25 -04:00
|
|
|
return 0
|
|
|
|
|
return 0 if afficher(lignes, source, depot) else 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
sys.exit(main())
|