sdn : emet le noeud de sortie PRIMAIRE, et trois noeuds pour t17

`proxmox_sdn.sortie_primaire` etait declare et utilise par `devis_opnsense`
pour deriver le prochain saut de la frontiere, mais `devis_sdn` ne l'emettait
jamais : une zone creee depuis ce devis n'aurait pas eu de primaire, et les deux
devis se seraient contredits. Le devis l'emet, et refuse un primaire absent de
la liste des noeuds de sortie.

Cluster aligne : t17 passe a asgard,gandalf,vishnu (primaire asgard), strophe
FRR posee sur les trois noeuds, six VRF portant leur defaut.

Note de methode : une adresse ANYCAST ne peut pas servir de source de test. Le
ping depuis gandalf semblait mort a 100 % ; la capture a montre asgard recevant
les quatre reponses — la frontiere route le supernet vers le primaire, qui porte
la meme passerelle localement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-06 16:12:38 -04:00
parent fa69ec7c0f
commit 917b22ccc8
2 changed files with 50 additions and 1 deletions

View file

@ -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` :

View file

@ -151,6 +151,10 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict:
"controleur": sdn.get("controleur") or "<CONTROLEUR-EVPN-A-DECLARER>",
"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.",