sdn : un cluster muet n est pas un cluster vide, et t29 se retire

Le poste coupe du site, sdn-plan annoncait a creer : 33 — toutes les zones,
Chezlepro comprise. Une lecture en echec rendait un dict d erreur, que rep or []
parcourait comme une liste vide. La lecture s arrete maintenant, comme la frontiere.

t29 rejoint l ancien nommage : sortie du devis sans cela, la zone aurait ete
lue comme etrangere et laissee en place, avec ses VLAN 1291-1296.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-09-28 09:34:20 -04:00
parent c0f610be33
commit 6a7188290f
2 changed files with 26 additions and 8 deletions

View file

@ -66,13 +66,30 @@ def _vm_par_vnet(api: Cluster) -> dict[str, list[str]]:
return dedans
def _lire(api: Cluster, chemin: str) -> list:
"""Une LISTE du cluster, ou l'arret — jamais une liste vide a la place d'une erreur.
UN CLUSTER MUET N'EST PAS UN CLUSTER VIDE (2026-09-27). `Cluster` rend
`{'_erreur': ...}` au lieu de lever ; `rep or []` iterait alors sur les CLES de ce
dict, que le filtre `isinstance(dict)` ecartait en silence. Poste coupe du site, le
plan annoncait « a creer : 33 » — toutes les zones, celles qui existent comprises.
`frontiere-plan` refusait deja de conclure dans ce cas ; celui-ci le fait aussi.
"""
rep = api(chemin)
if api.rate(rep):
raise SystemExit(f"Le cluster {api.hote} ne repond pas sur `{chemin}` : "
f"{api.rate(rep)}\n (un cluster injoignable serait lu comme un "
f"cluster VIDE, et le plan proposerait de tout recreer)")
return rep or []
def plan(api: Cluster, devis: dict) -> dict:
zones_voulues = {b["zone"]: b for b in devis["zones"]}
connues = set(zones_voulues) | set(devis.get("anciennes") or [])
zones_posees = {z["zone"]: z for z in (api("/cluster/sdn/zones") or [])
zones_posees = {z["zone"]: z for z in _lire(api, "/cluster/sdn/zones")
if isinstance(z, dict)}
vnets_poses = {v["vnet"]: v for v in (api("/cluster/sdn/vnets") or [])
vnets_poses = {v["vnet"]: v for v in _lire(api, "/cluster/sdn/vnets")
if isinstance(v, dict)}
vnets_voulus = {v["vnet"]: (b["zone"], v)
@ -83,7 +100,7 @@ def plan(api: Cluster, devis: dict) -> dict:
for nom, v in vnets_poses.items():
if v.get("zone") not in connues:
continue
for s in (api(f"/cluster/sdn/vnets/{nom}/subnets") or []):
for s in _lire(api, f"/cluster/sdn/vnets/{nom}/subnets"):
if isinstance(s, dict):
sr_poses[(nom, s.get("cidr"))] = s
sr_voulus = {(v["vnet"], v["sous_reseau"]): (b["zone"], v)

View file

@ -75,7 +75,9 @@ LONGUEUR_MAX_ID = 8
MARQUEUR_SORTIE = "<NOEUDS-DE-SORTIE-A-DESIGNER>"
# Nommages precedents, a retirer : VRF00xx (cree a la main) puis CHEZ17/TECH11
# (premiere derivation, remplacee — les chiffres de zone n'etaient pas lisibles).
ANCIEN_NOMMAGE = ("VRF0011", "VRF0017", "CHEZ17", "TECH11")
# `t29` : l'index 29, libere le 2026-09-27. Sortie du devis sans passer ici, la zone
# serait lue comme ETRANGERE — laissee intacte, et ses VLAN avec elle.
ANCIEN_NOMMAGE = ("VRF0011", "VRF0017", "CHEZ17", "TECH11", "t29")
def zone_de(index: int) -> str:
@ -276,11 +278,10 @@ def rendre(devis: dict) -> str:
]
if devis.get("anciennes"):
out += [
"## 0. Retirer l'ancien nommage (fait a la main, remplace)",
"## 0. Retirer les zones qui ne servent plus (ancien nommage, index liberes)",
"#",
"# Ces zones ne portent AUCUN VNet : les supprimer ne debranche rien. C'est",
"# precisement pourquoi on renomme maintenant — avec des VM attachees, ce",
"# serait une migration. Verifier l'absence de VNet avant de supprimer :",
"# `make sdn-appliquer` les retire, VNets et sous-reseaux compris, et REFUSE",
"# un VNet ou une VM est encore branchee. A la main, verifier d'abord :",
"# pvesh get /cluster/sdn/vnets --output-format json",
]
for z in devis["anciennes"]: