P23 : la bande basse de D-77 devient une regle outillee
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>
This commit is contained in:
parent
0c93569dd7
commit
b7239149bb
5 changed files with 180 additions and 8 deletions
34
CHANGELOG.md
34
CHANGELOG.md
|
|
@ -1,5 +1,39 @@
|
||||||
# CHANGELOG — Set-OPS
|
# CHANGELOG — Set-OPS
|
||||||
|
|
||||||
|
## 2026-08-12 — P23 outille D-77 : la bande basse devient une règle, pas une convention
|
||||||
|
|
||||||
|
D-77 disait où l'underlay doit vivre. Rien ne le vérifiait — et une convention qu'on
|
||||||
|
n'outille pas pourrit en silence (D-70). C'est exactement ce qui a laissé la sauvegarde
|
||||||
|
vide pendant un mois.
|
||||||
|
|
||||||
|
**Le contrôle disait l'inverse de la décision.** `underlay.py` refusait *tout*
|
||||||
|
chevauchement avec un supernet tenant. Il fallait le rendre plus **fin**, pas plus strict :
|
||||||
|
|
||||||
|
| Situation | Verdict |
|
||||||
|
|---|---|
|
||||||
|
| dans **son propre** supernet, bande basse | conforme — c'est la règle |
|
||||||
|
| dans son propre supernet, bande **haute** | **refusé** — collision avec ses propres zones |
|
||||||
|
| dans le supernet d'un **autre** site | **refusé** — les deux ne pourront jamais être reliés |
|
||||||
|
| hors de tout supernet (`10.0.x`, `192.168.x`) | conforme — héritage, et stockage |
|
||||||
|
|
||||||
|
La frontière est **dérivée** de `OCTET_ZONE`, jamais écrite en dur : déplacer la règle des
|
||||||
|
zones déplace la borne avec elle. Le site déclare son `index` dans `underlay.yml` ; sans
|
||||||
|
lui, on retombe sur la règle stricte d'avant D-77 — le comportement sûr pour un underlay
|
||||||
|
qui n'a pas encore migré. **Chezlepro reste donc conforme aujourd'hui**, en `10.0.x`.
|
||||||
|
|
||||||
|
### Le piège que le test attrape
|
||||||
|
|
||||||
|
Un préfixe peut **commencer** dans la bande basse et déborder : `10.21.0.0/19` couvre les
|
||||||
|
octets 0 à 31. Une vérification qui ne regarderait que le premier octet le laisserait
|
||||||
|
passer. La borne est donc évaluée sur **toute l'étendue** du préfixe.
|
||||||
|
|
||||||
|
Deux de mes propres cas d'épreuve étaient mal choisis — `10.21.14.0/23` et `10.21.12.0/21`
|
||||||
|
se normalisent entièrement dans la bande basse, et « conforme » y était la bonne réponse.
|
||||||
|
Il a fallu construire un préfixe qui franchit réellement la frontière pour éprouver la
|
||||||
|
garde.
|
||||||
|
|
||||||
|
`scripts/tests/test_underlay_bande_basse.py` — 7 cas, câblé dans `make test`.
|
||||||
|
|
||||||
## 2026-08-12 — `modeleSetOPS`, et une porte pour l'hébergeur
|
## 2026-08-12 — `modeleSetOPS`, et une porte pour l'hébergeur
|
||||||
|
|
||||||
### Le gabarit portait le nom du mauvais propriétaire
|
### Le gabarit portait le nom du mauvais propriétaire
|
||||||
|
|
|
||||||
1
Makefile
1
Makefile
|
|
@ -211,6 +211,7 @@ test: ## Lance les tests unitaires (derivation de nomenclature et d'inventaire)
|
||||||
python3 scripts/tests/test_inventory_host.py
|
python3 scripts/tests/test_inventory_host.py
|
||||||
python3 scripts/tests/test_raser.py
|
python3 scripts/tests/test_raser.py
|
||||||
python3 scripts/tests/test_raser_resultat.py
|
python3 scripts/tests/test_raser_resultat.py
|
||||||
|
python3 scripts/tests/test_underlay_bande_basse.py
|
||||||
|
|
||||||
.PHONY: verifier
|
.PHONY: verifier
|
||||||
verifier: lint test inventaire-verifier site-verifier flux-verifier syntaxe ## Rejoue les preuves SANS reecrire le rapport (verification rapide)
|
verifier: lint test inventaire-verifier site-verifier flux-verifier syntaxe ## Rejoue les preuves SANS reecrire le rapport (verification rapide)
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ sont les seules vérifiables.
|
||||||
| **D-71** | **Une PKI et un DNS fonctionnels avant toute chose** ; puis, par VM : socle → enrôlement PKI → enregistrement DNS (A **et** PTR) | `deployer-tout` déroule par COUCHES — correct, mais chaque VM réclame alors un certificat à une autorité pas encore debout, et l'échec se lit comme un défaut du rôle et non d'ordre. Les deux hôtes d'amorçage se **dérivent** de `applications.<step_ca\|powerdns>.hote` : déplacer l'autorité déplace l'amorçage. **Deux exceptions structurelles assumées** — l'AC s'auto-signe, le DNS pose son propre enregistrement | `Makefile` `_amorcer-socle`, `scripts/socle_amorcage.py` | — |
|
| **D-71** | **Une PKI et un DNS fonctionnels avant toute chose** ; puis, par VM : socle → enrôlement PKI → enregistrement DNS (A **et** PTR) | `deployer-tout` déroule par COUCHES — correct, mais chaque VM réclame alors un certificat à une autorité pas encore debout, et l'échec se lit comme un défaut du rôle et non d'ordre. Les deux hôtes d'amorçage se **dérivent** de `applications.<step_ca\|powerdns>.hote` : déplacer l'autorité déplace l'amorçage. **Deux exceptions structurelles assumées** — l'AC s'auto-signe, le DNS pose son propre enregistrement | `Makefile` `_amorcer-socle`, `scripts/socle_amorcage.py` | — |
|
||||||
| **D-76** | Le **gabarit doit se FABRIQUER par le dépôt**, pas se façonner à la main — décidée le 2026-08-12, **volontairement différée** après Technolibre | tout dérive d'un plan et se prouve ; le gabarit est la **seule pièce faite à la main** — 853 lignes de procédure manuelle — et il est en amont des quatorze VM. Le 2026-08-09 l'a démontré : l'ancien portait une **clé privée d'hôte SSH** et un `/etc/resolv.conf` figé, recopiés dans chaque clone, et il a fallu le recapturer. La preuve de reconstruction s'arrête donc un cran trop tôt : on reconstruit la flotte, pas ce dont elle est clonée. **Cible** : image cloud Debian officielle → signature vérifiée contre une empreinte épinglée (`verifier_signature.py`, déjà écrit) → import, réglages, conversion. **Coût honnête** : la recette doit être COMPARÉE au gabarit courant avant bascule — un réglage oublié serait hérité par les quatorze VM, et découvert loin de sa cause. **En attendant**, un gabarit se transporte d'un cluster à l'autre par `vzdump` / `qmrestore` | `docs/procedure-template-debian13-proxmox.md`, `scripts/verifier_signature.py` | — *(à construire)* |
|
| **D-76** | Le **gabarit doit se FABRIQUER par le dépôt**, pas se façonner à la main — décidée le 2026-08-12, **volontairement différée** après Technolibre | tout dérive d'un plan et se prouve ; le gabarit est la **seule pièce faite à la main** — 853 lignes de procédure manuelle — et il est en amont des quatorze VM. Le 2026-08-09 l'a démontré : l'ancien portait une **clé privée d'hôte SSH** et un `/etc/resolv.conf` figé, recopiés dans chaque clone, et il a fallu le recapturer. La preuve de reconstruction s'arrête donc un cran trop tôt : on reconstruit la flotte, pas ce dont elle est clonée. **Cible** : image cloud Debian officielle → signature vérifiée contre une empreinte épinglée (`verifier_signature.py`, déjà écrit) → import, réglages, conversion. **Coût honnête** : la recette doit être COMPARÉE au gabarit courant avant bascule — un réglage oublié serait hérité par les quatorze VM, et découvert loin de sa cause. **En attendant**, un gabarit se transporte d'un cluster à l'autre par `vzdump` / `qmrestore` | `docs/procedure-template-debian13-proxmox.md`, `scripts/verifier_signature.py` | — *(à construire)* |
|
||||||
|
|
||||||
| **D-77** | **L'underlay d'un site dérive du même `index` que son tenant**, dans la **bande basse** `10.(10+index).0–15.x` ; les réseaux de **stockage** en sortent et sont **identiques partout**, en `192.168.<vlan>.0/24` | tant qu'il n'existait qu'un site, `10.0.x.x` suffisait. Deux sites qui doivent se joindre — reprise mutuelle, sauvegardes croisées, exploitation à distance — **ne peuvent pas** porter les mêmes plages : chaque routeur croit que le réseau est chez lui. Un « numéro de site » a d'abord été proposé : **rejeté**, c'était un SECOND SEED à tenir et à synchroniser, alors que tout dérive déjà d'`index` (D-17). La bande basse est sûre **par la règle, pas par chance** : les zones valent `10.(10+index).(15+categorie).0/24`, donc troisième octet ≥ 16 — les octets 0 à 15 ne sont jamais alloués. Les troisièmes et derniers octets de l'underlay actuel sont ainsi **préservés** : seul le deuxième change, et l'invariant `.1` (D-04) survit intact. Le **stockage** en est sorti parce qu'il est jumbo, non routé, et ne quitte jamais son site : il n'a aucun besoin d'être unique, et l'aligner sur le VLAN (`192.168.20.x` ↔ VLAN 20) fait dire son VLAN à l'adresse. Contrepartie assumée : ces réseaux ne pourront jamais traverser un lien inter-sites — sans conséquence, puisque ce qui voyage entre deux sites est l'**état**, par le dépôt de sauvegarde, pas le stockage bloc | `underlay.yml`, `docs/preparer-un-site-hebergeur.md`, `scripts/inventory_rules.py` (`OCTET_ZONE`) | **P23** *(à étendre : borner le 3ᵉ octet sous `OCTET_ZONE + 1`)* |
|
| **D-77** | **L'underlay d'un site dérive du même `index` que son tenant**, dans la **bande basse** `10.(10+index).0–15.x` ; les réseaux de **stockage** en sortent et sont **identiques partout**, en `192.168.<vlan>.0/24` | tant qu'il n'existait qu'un site, `10.0.x.x` suffisait. Deux sites qui doivent se joindre — reprise mutuelle, sauvegardes croisées, exploitation à distance — **ne peuvent pas** porter les mêmes plages : chaque routeur croit que le réseau est chez lui. Un « numéro de site » a d'abord été proposé : **rejeté**, c'était un SECOND SEED à tenir et à synchroniser, alors que tout dérive déjà d'`index` (D-17). La bande basse est sûre **par la règle, pas par chance** : les zones valent `10.(10+index).(15+categorie).0/24`, donc troisième octet ≥ 16 — les octets 0 à 15 ne sont jamais alloués. Les troisièmes et derniers octets de l'underlay actuel sont ainsi **préservés** : seul le deuxième change, et l'invariant `.1` (D-04) survit intact. Le **stockage** en est sorti parce qu'il est jumbo, non routé, et ne quitte jamais son site : il n'a aucun besoin d'être unique, et l'aligner sur le VLAN (`192.168.20.x` ↔ VLAN 20) fait dire son VLAN à l'adresse. Contrepartie assumée : ces réseaux ne pourront jamais traverser un lien inter-sites — sans conséquence, puisque ce qui voyage entre deux sites est l'**état**, par le dépôt de sauvegarde, pas le stockage bloc | `underlay.yml` (`index`), `scripts/underlay.py`, `docs/preparer-un-site-hebergeur.md` | **P23** |
|
||||||
|
|
||||||
> **Ce que D-77 rend possible, et ce qu'elle coûte.** Elle est la condition de la reprise
|
> **Ce que D-77 rend possible, et ce qu'elle coûte.** Elle est la condition de la reprise
|
||||||
> mutuelle : sans plages distinctes, aucun lien entre deux sites ne peut router. Elle est
|
> mutuelle : sans plages distinctes, aucun lien entre deux sites ne peut router. Elle est
|
||||||
|
|
|
||||||
82
scripts/tests/test_underlay_bande_basse.py
Normal file
82
scripts/tests/test_underlay_bande_basse.py
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
#!/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}).")
|
||||||
|
|
@ -1,13 +1,33 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Underlay = la fabric physique partagee par les instances federees.
|
"""Underlay = la fabric physique partagee par les instances federees.
|
||||||
|
|
||||||
Cluster-global, HORS de la derivation par-tenant (aucun index) : management des
|
Cluster-global : management des switches et du mgmt Proxmox/OOB, stockage iSCSI,
|
||||||
switches et du mgmt Proxmox/OOB, stockage iSCSI, reseaux Ceph (public + cluster).
|
reseaux Ceph (public + cluster). Ce sont des reseaux du *sous-sol* : ils portent la
|
||||||
Ce sont des reseaux du *sous-sol* : ils portent la flotte, ils n'appartiennent a
|
flotte, ils n'appartiennent a aucun tenant.
|
||||||
aucun tenant.
|
|
||||||
|
|
||||||
Convention : VLAN < 1000, franchement SOUS la plage tenant (VLAN = 1000+index*10+zone).
|
Convention : VLAN < 1000, franchement SOUS la plage tenant (VLAN = 1000+index*10+zone).
|
||||||
Les sous-reseaux ne doivent chevaucher aucun supernet tenant (10.(10+index).0.0/16).
|
|
||||||
|
ADRESSAGE (D-77, 2026-08-12). Un site derive du meme `index` que son tenant, et son
|
||||||
|
underlay occupe la BANDE BASSE de ce supernet : `10.(10+index).0-15.x`. La bande est
|
||||||
|
sure PAR LA REGLE et non par chance — les zones d'un tenant valent
|
||||||
|
`10.(10+index).(15+categorie).0/24`, donc troisieme octet >= 16 ; les octets 0 a 15
|
||||||
|
ne sont jamais alloues. Deux sites ne peuvent alors pas se chevaucher, sans second
|
||||||
|
registre a tenir.
|
||||||
|
|
||||||
|
Trois cas, et un seul est une erreur :
|
||||||
|
|
||||||
|
- dans SON PROPRE supernet, bande basse -> conforme (c'est la regle)
|
||||||
|
- dans son propre supernet, bande HAUTE -> ERREUR : collision avec ses zones
|
||||||
|
- dans le supernet d'un AUTRE tenant -> ERREUR : collision entre sites
|
||||||
|
- hors de tout supernet (10.0.x, 192.168.x) -> conforme (heritage, et stockage)
|
||||||
|
|
||||||
|
Le site declare son index par `underlay.index`. **Sans lui**, on retombe sur la regle
|
||||||
|
stricte d'avant D-77 (aucun chevauchement) : c'est le comportement sur pour un
|
||||||
|
underlay qui n'a pas encore migre.
|
||||||
|
|
||||||
|
Les reseaux de STOCKAGE sortent de cet espace (192.168.<vlan>.0/24, identiques a tous
|
||||||
|
les sites) : jumbo, non routes, ils ne quittent jamais leur site, donc ils n'ont aucun
|
||||||
|
besoin d'etre uniques.
|
||||||
|
|
||||||
EMPLACEMENT : l'underlay appartient a l'HEBERGEUR — ses switches, ses cables. Il vit
|
EMPLACEMENT : l'underlay appartient a l'HEBERGEUR — ses switches, ses cables. Il vit
|
||||||
donc dans SON depot, et le moteur le monte par symlink :
|
donc dans SON depot, et le moteur le monte par symlink :
|
||||||
|
|
@ -36,6 +56,12 @@ from inventory_rules import supernet_de, vlan_de # noqa: E402
|
||||||
# Les VLAN tenant commencent a 1000+index*10+zone (>= 1011). L'underlay reste en dessous.
|
# Les VLAN tenant commencent a 1000+index*10+zone (>= 1011). L'underlay reste en dessous.
|
||||||
SEUIL_VLAN_TENANT = 1000
|
SEUIL_VLAN_TENANT = 1000
|
||||||
|
|
||||||
|
# Premier troisieme-octet alloue aux ZONES d'un tenant. DERIVE de la regle
|
||||||
|
# (`base3_de` = 10.(10+index).(15+categorie)) et jamais ecrit en dur : deplacer
|
||||||
|
# OCTET_ZONE deplacerait la frontiere de la bande basse avec lui.
|
||||||
|
from inventory_rules import OCTET_ZONE # noqa: E402
|
||||||
|
PREMIER_OCTET_ZONE = OCTET_ZONE + 1
|
||||||
|
|
||||||
|
|
||||||
def chemin() -> Path | None:
|
def chemin() -> Path | None:
|
||||||
"""Emplacement du underlay.yml : SETOPS_UNDERLAY, sinon le symlink a la racine.
|
"""Emplacement du underlay.yml : SETOPS_UNDERLAY, sinon le symlink a la racine.
|
||||||
|
|
@ -265,6 +291,12 @@ def octet_passerelle(plan_nomenclature: Path | None = None) -> int | None:
|
||||||
return int(v) if isinstance(v, int) else None
|
return int(v) if isinstance(v, int) else None
|
||||||
|
|
||||||
|
|
||||||
|
def index_site(underlay: dict | None) -> int | None:
|
||||||
|
"""`index` du site — le seed dont derive tout son adressage (D-77). None si absent."""
|
||||||
|
v = (underlay or {}).get("underlay", underlay or {}).get("index")
|
||||||
|
return int(v) if isinstance(v, int) else None
|
||||||
|
|
||||||
|
|
||||||
def _tenants_reperes() -> tuple[set[int], list[str]]:
|
def _tenants_reperes() -> tuple[set[int], list[str]]:
|
||||||
"""VLAN et supernets des tenants federes (import paresseux : evite le cycle avec devis)."""
|
"""VLAN et supernets des tenants federes (import paresseux : evite le cycle avec devis)."""
|
||||||
import devis_reseau
|
import devis_reseau
|
||||||
|
|
@ -285,6 +317,9 @@ def valider(underlay: dict | None,
|
||||||
if tenants_vlans is None or tenants_supernets is None:
|
if tenants_vlans is None or tenants_supernets is None:
|
||||||
tenants_vlans, tenants_supernets = _tenants_reperes()
|
tenants_vlans, tenants_supernets = _tenants_reperes()
|
||||||
t_supernets = [ipaddress.ip_network(s, strict=False) for s in tenants_supernets]
|
t_supernets = [ipaddress.ip_network(s, strict=False) for s in tenants_supernets]
|
||||||
|
# Sans `index` declare, on ignore quel supernet est le SIEN : on retombe alors sur la
|
||||||
|
# regle stricte d'avant D-77 (aucun chevauchement), qui reste sure.
|
||||||
|
idx_site = index_site(underlay)
|
||||||
|
|
||||||
erreurs: list[str] = []
|
erreurs: list[str] = []
|
||||||
vus_vlan: dict[int, str] = {}
|
vus_vlan: dict[int, str] = {}
|
||||||
|
|
@ -311,9 +346,29 @@ def valider(underlay: dict | None,
|
||||||
except ValueError:
|
except ValueError:
|
||||||
erreurs.append(f"reseau '{nom}': sous_reseau '{sous}' invalide")
|
erreurs.append(f"reseau '{nom}': sous_reseau '{sous}' invalide")
|
||||||
continue
|
continue
|
||||||
|
# D-77 : l'underlay d'un site vit dans la BANDE BASSE de SON propre supernet.
|
||||||
|
# Chevaucher celui d'un AUTRE tenant reste une erreur — c'est ce qui empeche deux
|
||||||
|
# sites de se joindre. Et deborder sur la bande haute de son propre supernet en
|
||||||
|
# est une aussi : on entrerait en collision avec ses propres zones.
|
||||||
|
mien = ipaddress.ip_network(supernet_de(idx_site), strict=False) \
|
||||||
|
if idx_site is not None else None
|
||||||
for ts in t_supernets:
|
for ts in t_supernets:
|
||||||
if net.overlaps(ts):
|
if not net.overlaps(ts):
|
||||||
erreurs.append(f"reseau '{nom}': {sous} chevauche le supernet tenant {ts}")
|
continue
|
||||||
|
if mien is not None and ts == mien:
|
||||||
|
# Le sien : seule la bande basse est permise.
|
||||||
|
hauts = [o for o in range(net.network_address.packed[2],
|
||||||
|
net.broadcast_address.packed[2] + 1)
|
||||||
|
if o >= PREMIER_OCTET_ZONE]
|
||||||
|
if hauts:
|
||||||
|
erreurs.append(
|
||||||
|
f"reseau '{nom}': {sous} deborde sur la bande des ZONES de son "
|
||||||
|
f"propre tenant (3e octet {hauts[0]} >= {PREMIER_OCTET_ZONE}). "
|
||||||
|
f"L'underlay tient dans 10.x.0-{PREMIER_OCTET_ZONE - 1}.x (D-77).")
|
||||||
|
else:
|
||||||
|
erreurs.append(f"reseau '{nom}': {sous} chevauche le supernet tenant {ts} "
|
||||||
|
f"— c'est celui d'un AUTRE site, les deux ne pourront "
|
||||||
|
f"jamais etre relies")
|
||||||
for autre, autre_nom in vus_sous:
|
for autre, autre_nom in vus_sous:
|
||||||
if net.overlaps(autre):
|
if net.overlaps(autre):
|
||||||
erreurs.append(f"reseau '{nom}': {sous} chevauche le reseau underlay '{autre_nom}'")
|
erreurs.append(f"reseau '{nom}': {sous} chevauche le reseau underlay '{autre_nom}'")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue