diff --git a/CHANGELOG.md b/CHANGELOG.md index ee644a7..607d04a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ ## 2026-08-06 — le chemin nord-sud devient dérivable +### Trois nœuds de sortie, et le primaire enfin émis + +`t17` passe à `asgard,gandalf,vishnu`, primaire `asgard` — `t11` l'était déjà. La strophe FRR +est posée sur les trois nœuds, et les six VRF (deux zones × trois nœuds) portent leur défaut. + +**Un défaut du devis découvert au passage.** `proxmox_sdn.sortie_primaire` était déclaré et +**utilisé** par `devis_opnsense` pour dériver le prochain saut des routes de la frontière, +mais `devis_sdn` ne l'émettait jamais. Une zone créée depuis ce devis n'aurait pas eu de +primaire, et les deux devis se seraient contredits : la frontière aurait pointé un nœud que le +SDN n'avait pas désigné. Le devis l'émet désormais, et refuse un primaire absent de la liste +des nœuds de sortie. + +**Une mesure qui accusait à tort.** Depuis `gandalf` et `vishnu`, la sortie semblait morte : +100 % de perte là où `asgard` passait. La capture a tranché — pendant que `gandalf` pingait, +**`asgard` recevait les quatre réponses** : + +``` +IP 10.0.4.1 > 10.27.19.1: ICMP echo reply, seq 1..4 (vu sur asgard) +``` + +La source du test, `10.27.19.1`, est la passerelle **anycast** : elle existe à l'identique sur +les trois nœuds. La frontière route `10.27.0.0/16` par sa route statique unique vers +`10.0.4.41`, et `asgard` consomme les réponses. Une VM tenant a une adresse unique, et le +relais EVPN la lui rend — mécanisme déjà observé (`10.27.19.21 via 10.0.5.41`). + +Ce retour par le primaire n'est pas un défaut : c'est le sens de « primaire », et la +conséquence d'une route statique unique côté frontière. À retenir pour les mesures futures : +**une adresse anycast ne peut pas servir de source de test** — elle ne désigne pas le nœud +d'où l'on part. + + ### Les tenants sortent — et le chemin est entièrement dérivé Bout en bout, mesuré depuis `vrf_t17` puis `vrf_t11` sur `asgard` : diff --git a/scripts/devis_sdn.py b/scripts/devis_sdn.py index 3030aaf..1df194d 100644 --- a/scripts/devis_sdn.py +++ b/scripts/devis_sdn.py @@ -151,6 +151,10 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict: "controleur": sdn.get("controleur") or "", "asn": sdn.get("asn"), "noeuds_de_sortie": sdn.get("noeuds_de_sortie") or [], + # Le PRIMAIRE doit etre emis : `devis_opnsense` en derive le prochain saut des + # routes de la frontiere. Declare d'un cote et tu par l'autre, les deux devis se + # contrediraient — la frontiere pointerait un noeud que le SDN n'a pas designe. + "sortie_primaire": str(sdn.get("sortie_primaire") or "").strip(), "anciennes": list(ANCIEN_NOMMAGE), "frontiere": str(_tr.get("passerelle_sortie") or "").strip(), "zones": blocs, @@ -192,8 +196,22 @@ def rendre(devis: dict) -> str: f"pvesh create /cluster/sdn/zones --zone {b['zone']} --type evpn \\", f" --controller {devis['controleur']} --vrf-vxlan {b['vrf_vxlan']} \\", f" --mtu {b['mtu']} --ipam pve \\", - f" --exitnodes {','.join(sortie) if sortie else MARQUEUR_SORTIE}", + f" --exitnodes {','.join(sortie) if sortie else MARQUEUR_SORTIE}" + + (f" \\\n --exitnodes-primary {devis['sortie_primaire']}" + if devis.get("sortie_primaire") else ""), ] + if sortie and not devis.get("sortie_primaire"): + out += [ + "# /!\\ Aucun `proxmox_sdn.sortie_primaire` declare. La frontiere derive son", + "# prochain saut de cette valeur : sans elle, ses routes tenants restent", + "# en marqueur et aucun trafic entrant n'aboutit.", + ] + elif devis.get("sortie_primaire") and devis["sortie_primaire"] not in sortie: + out += [ + f"# /!\\ `sortie_primaire` = {devis['sortie_primaire']}, ABSENT des noeuds de", + "# sortie. Proxmox refuserait, et la frontiere pointerait un noeud sans", + "# chemin vers le tenant.", + ] if not sortie: out += [ "# /!\\ AUCUN noeud de sortie declare chez l'hebergeur.",