D-77 disait ou l'underlay doit vivre ; rien ne le verifiait. Une convention qu'on n'outille pas pourrit en silence (D-70) — c'est ce qui a laisse la sauvegarde vide pendant un mois. Le controle disait l'INVERSE de la decision : underlay.py refusait tout chevauchement avec un supernet tenant. Rendu plus FIN, pas plus strict : son propre supernet, bande basse -> conforme (la regle) son propre supernet, bande haute -> REFUS (collision avec ses zones) supernet d'un AUTRE site -> REFUS (jamais reliables) hors de tout supernet -> conforme (heritage 10.0.x, stockage 192.168.x) La frontiere derive d'OCTET_ZONE, jamais ecrite en dur. Le site declare son `index` dans underlay.yml ; sans lui on retombe sur la regle stricte d'avant D-77, sure — Chezlepro reste donc conforme en 10.0.x tant qu'il n'a pas migre. LE PIEGE, attrape par le test : un prefixe peut COMMENCER dans la bande basse et deborder — 10.21.0.0/19 couvre les octets 0 a 31. La borne est evaluee sur toute l'etendue du prefixe, pas sur son premier octet. Deux de mes propres cas d'epreuve etaient mal choisis (10.21.14.0/23 et 10.21.12.0/21 se normalisent dans la bande basse) : il a fallu un prefixe qui franchit reellement la frontiere pour eprouver la garde. test_underlay_bande_basse.py : 7 cas, cable dans make test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
3.4 KiB
Python
82 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
"""D-77 — l'underlay tient dans la BANDE BASSE du supernet de son propre site.
|
|
|
|
Ce que ce test garde, et pourquoi il existe. La regle « underlay dans la bande basse »
|
|
n'est sure que parce que les zones d'un tenant commencent a `OCTET_ZONE + 1`. C'est une
|
|
propriete de `inventory_rules`, pas une place qu'on aurait constatee libre un jour : si
|
|
`OCTET_ZONE` bougeait sans que la borne suive, la validation continuerait a dire « oui »
|
|
en laissant l'underlay recouvrir les zones — et personne ne le verrait avant que deux
|
|
machines ne se disputent une adresse.
|
|
|
|
Le test derive donc sa frontiere de la MEME constante que le code, et verifie les trois
|
|
cas qui comptent, y compris ceux ou un prefixe FRANCHIT la frontiere (un /19 couvre les
|
|
octets 0 a 31 : il deborde, meme s'il commence dans la bande basse).
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
RACINE = Path(__file__).resolve().parent.parent.parent
|
|
sys.path.insert(0, str(RACINE / "scripts"))
|
|
|
|
import underlay as u # noqa: E402
|
|
from inventory_rules import OCTET_ZONE # noqa: E402
|
|
|
|
SUPERNETS = ["10.21.0.0/16", "10.27.0.0/16"] # deux sites : index 11 et 17
|
|
MIEN = 11
|
|
|
|
|
|
def _erreurs(sous: str, index: int | None = MIEN) -> list[str]:
|
|
site = {"reseaux": [{"nom": "g", "vlan": 10, "sous_reseau": sous}]}
|
|
if index is not None:
|
|
site["index"] = index
|
|
return [e for e in u.valider(site, tenants_vlans=set(), tenants_supernets=SUPERNETS)
|
|
if "deborde" in e or "chevauche le supernet" in e]
|
|
|
|
|
|
def test_la_borne_derive_de_la_regle_des_zones():
|
|
"""La frontiere n'est pas 16 en dur : elle suit `OCTET_ZONE`."""
|
|
assert u.PREMIER_OCTET_ZONE == OCTET_ZONE + 1
|
|
|
|
|
|
def test_bande_basse_de_son_propre_supernet_acceptee():
|
|
for sous in ("10.21.0.0/24", "10.21.4.0/24", "10.21.5.0/24", "10.21.15.0/24"):
|
|
assert _erreurs(sous) == [], f"{sous} devrait etre conforme (bande basse)"
|
|
|
|
|
|
def test_bande_des_zones_refusee():
|
|
for sous in (f"10.21.{OCTET_ZONE + 1}.0/24", "10.21.18.0/24", "10.21.16.0/20"):
|
|
assert _erreurs(sous), f"{sous} devrait etre REFUSE (bande des zones)"
|
|
|
|
|
|
def test_un_prefixe_qui_franchit_la_frontiere_est_refuse():
|
|
"""Le piege : il COMMENCE dans la bande basse et deborde. Un test qui ne
|
|
regarderait que le premier octet le laisserait passer."""
|
|
for sous in ("10.21.0.0/19", "10.21.0.0/16"):
|
|
assert _erreurs(sous), f"{sous} couvre au-dela de {OCTET_ZONE} : a refuser"
|
|
|
|
|
|
def test_supernet_d_un_autre_site_refuse():
|
|
"""C'est ce qui empeche deux sites d'etre relies — l'erreur la plus couteuse."""
|
|
assert _erreurs("10.27.0.0/24"), "le supernet d'un autre site doit etre refuse"
|
|
|
|
|
|
def test_hors_de_tout_supernet_accepte():
|
|
"""Le stockage (192.168.x, identique partout) et l'heritage 10.0.x avant migration."""
|
|
for sous in ("192.168.20.0/24", "192.168.31.0/24", "10.0.0.0/24"):
|
|
assert _erreurs(sous) == [], f"{sous} est hors des supernets : conforme"
|
|
|
|
|
|
def test_sans_index_on_retombe_sur_la_regle_stricte():
|
|
"""Un underlay qui n'a pas encore migre ne declare pas d'index : aucun chevauchement
|
|
n'est alors tolere, ce qui reste le comportement sur."""
|
|
assert _erreurs("10.21.0.0/24", index=None), \
|
|
"sans index, tout chevauchement doit etre refuse"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
n = 0
|
|
for nom, fn in sorted(globals().items()):
|
|
if nom.startswith("test_") and callable(fn):
|
|
fn()
|
|
n += 1
|
|
print(f"OK — {n} test(s) D-77 (bande basse, frontiere derivee d'OCTET_ZONE={OCTET_ZONE}).")
|