devis switch : une interface, un bloc

La partie A déclarait ses ports de bord deux fois — l'interface en section
4, son portfast en section 6 — alors que la partie B posait tout dans le
même bloc. Deux conventions pour la même chose dans un seul document.

Le portfast est posé avec son interface (sections 4 et 4b). La section 6 se
réduit au global — mode, priorité du pont racine — plus les deux
avertissements : les rayons de la 4c n'en sont volontairement pas, et
pourquoi BPDU guard n'est pas émis.

Vérifié : aucune interface déclarée deux fois dans une même partie,
3 portfast avec `stp`, 0 sans lui, 0 sur un rayon.

Preuves : 24 OK, 0 échec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-01 21:35:32 -04:00
parent 92d31c0406
commit f80b53b592
2 changed files with 30 additions and 16 deletions

View file

@ -1,5 +1,20 @@
# CHANGELOG — Set-OPS
## 2026-08-01 (suite 10) — une interface, un bloc
La partie A déclarait ses ports de bord deux fois : l'interface en section 4, son `portfast`
en section 6. La partie B, elle, posait tout dans le même bloc. Deux conventions pour la même
chose dans un seul document — un opérateur qui applique la partie A section par section
configurait la même interface à deux endroits.
Le `portfast` est désormais posé **avec son interface** (sections 4 et 4b). La section 6 se
réduit à ce qui est global — mode, priorité du pont racine — plus les deux avertissements :
que les rayons de la section 4c n'en sont volontairement pas, et pourquoi BPDU guard n'est
pas émis.
Vérifié : aucune interface n'est déclarée deux fois dans une même partie, trois `portfast`
avec `stp`, zéro sans lui, zéro sur un rayon.
## 2026-08-01 (suite 9) — rayons de l'étoile séparés des ports terminaux
L'ajout du spanning-tree venait de rendre dangereuse une imprécision qui, jusque-là, n'était

View file

@ -142,7 +142,7 @@ def _stp_lignes(dialecte: str, mode: str, priorite: int) -> list[str]:
return [f"spanning-tree mode {mode}", f"spanning-tree priority {priorite}"]
def section_stp(underlay: dict | None, dialecte: str, ports_bord: list[str]) -> list[str]:
def section_stp(underlay: dict | None, dialecte: str) -> list[str]:
"""Spanning-tree du switch routeur : il est la racine, par construction.
En etoile, tous les chemins passent deja par le centre : en faire le pont racine
@ -164,17 +164,13 @@ def section_stp(underlay: dict | None, dialecte: str, ports_bord: list[str]) ->
"! le decider — l'arbre logique suit alors le cablage physique.",
"! Aucun lien redondant en etoile : RSTP est un filet, pas une necessite."]
out += _stp_lignes(dialecte, mode, underlay_mod.STP_PRIORITE_RACINE)
if ports_bord:
out += ["!",
"! Ports terminaux (hyperviseurs, frontiere) : rien derriere eux ne",
"! participe a l'arbre. Les declarer en bord evite d'attendre la",
"! convergence a chaque montee de lien."]
for p in ports_bord:
out += [f"interface {p}",
" spanning-tree portfast trunk"]
out += ["! BPDU guard non emis volontairement : un pont Linux dont le STP serait",
"! active enverrait des BPDU et ferait tomber le port cote hyperviseur.",
"! Ne l'ajouter qu'apres avoir verifie que ces ponts n'en emettent pas."]
out += ["!",
"! Les ports de bord sont declares AVEC leur interface (sections 4 et 4b),",
"! pas ici : chaque interface se lit d'un seul tenant. Les rayons de la",
"! section 4c n'en sont volontairement pas.",
"! BPDU guard non emis volontairement : un pont Linux dont le STP serait",
"! active enverrait des BPDU et ferait tomber le port cote hyperviseur.",
"! Ne l'ajouter qu'apres avoir verifie que ces ponts n'en emettent pas."]
out.append("!")
return out
@ -263,7 +259,7 @@ def partie_acces(underlay: dict | None, tenants: list, vlans: str,
return out
def section_frontiere(transit: dict | None) -> list[str]:
def section_frontiere(transit: dict | None, bord: bool = False) -> list[str]:
"""Port du switch vers le pare-feu de bordure. Porte le VLAN de transit, et lui seul.
Distinct du trunk vers Proxmox : les hyperviseurs n'ont aucune interface sur le
@ -277,10 +273,11 @@ def section_frontiere(transit: dict | None) -> list[str]:
"! ----- 4b. Port vers la frontiere nord/sud -----",
f"! Porte le seul VLAN {transit['vlan']} ({transit['nom']}). En lien dedie,",
f"! remplacer par : switchport access vlan {transit['vlan']}.",
"! Port terminal : rien derriere lui ne participe au spanning-tree.",
"interface <PORT-VERS-FRONTIERE>",
" switchport mode trunk",
f" switchport trunk allowed vlan add {transit['vlan']}",
]
] + ([" spanning-tree portfast trunk"] if bord else [])
def section_routes(underlay: dict | None) -> list[str]:
@ -445,10 +442,12 @@ def generer(tenants: list[tuple[str, str, dict]], dialecte: str | None = None) -
out.append("interface <PORT-VERS-PROXMOX>")
out.append(" switchport mode trunk")
out.append(f" switchport trunk allowed vlan add {vlans}")
out += ["!"] + section_frontiere(transit)
if underlay_mod.stp(underlay):
out.append(" spanning-tree portfast trunk")
out += ["!"] + section_frontiere(transit, bord=bool(underlay_mod.stp(underlay)))
out += ["!"] + section_rayons(underlay, ",".join(vlans_underlay + vlans_tenants))
out += ["!"] + section_routes(underlay)
out += section_stp(underlay, dialecte, ["<PORT-VERS-PROXMOX>", "<PORT-VERS-FRONTIERE>"])
out += section_stp(underlay, dialecte)
out += ["end", "write memory"]
# Les switches d'acces : memes VLANs, aucun SVI de zone, aucune ACL, aucune route.
out += partie_acces(underlay, tenants, ",".join(vlans_underlay + vlans_tenants), dialecte)