Underlay : fabric physique cluster-global (mgmt/iSCSI/Ceph) + preuve P23
Le modele derive l'adressage par tenant (VLAN 1000+index*10+zone), mais la fabric physique qui porte la flotte (mgmt switches/Proxmox/OOB, iSCSI, Ceph public+cluster) n'appartient a aucun tenant. Elle est desormais codifiee. - scripts/underlay.py + make underlay : charge/affiche/valide underlay.yml (VLAN < 1000, sous-reseaux hors des supernets tenant 10.(10+index).0.0/16). - underlay.yml gitignore (comme le vault) ; gabarit public underlay.yml.example ; surchargeable par SETOPS_UNDERLAY. - devis_reseau : section 0. Underlay + VLAN underlay sur le trunk, selon dialecte. - P23 : underlay.py --verifier ; sautee si underlay.yml absent (comme P16 sans vault). 23/23 preuves. Doc : docs/audit/README.md, wiki page reseau, CHANGELOG. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
02f3122b3d
commit
272f6369d8
9 changed files with 322 additions and 3 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -7,6 +7,8 @@ facts_cache/
|
|||
*.vault
|
||||
*.vault.yml
|
||||
**/vault.yml
|
||||
# Underlay = fabric physique de l'operateur (cluster-global) ; gabarit public seul.
|
||||
/underlay.yml
|
||||
*.secret
|
||||
*.pem
|
||||
*.key
|
||||
|
|
|
|||
23
CHANGELOG.md
23
CHANGELOG.md
|
|
@ -1,5 +1,28 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-07-24 — underlay (fabric physique, cluster-global)
|
||||
|
||||
### Ajouté — l'underlay comme concept de premier plan
|
||||
Le modèle dérive l'adressage **par tenant** (VLAN `1000+index×10+zone`), mais la **fabric
|
||||
physique** qui porte la flotte — mgmt des switches, mgmt Proxmox/OOB, iSCSI, Ceph — n'appartient
|
||||
à aucun tenant et ne dérive d'aucun `index`. Elle vit dans le *sous-sol* du modèle. Jusqu'ici
|
||||
elle n'était pas codifiée. Elle l'est.
|
||||
|
||||
- **`scripts/underlay.py`** + **`make underlay`** — charge/affiche/**valide** `underlay.yml` :
|
||||
réseaux (nom, VLAN, sous-réseau, passerelle optionnelle → SVI, MTU/jumbo) et hôtes fixes
|
||||
documentés (les switches). La validation refuse toute **collision avec la plage tenant** :
|
||||
VLAN < 1000, sous-réseaux hors des supernets `10.(10+index).0.0/16`.
|
||||
- **`underlay.yml`** (racine du moteur, **gitignore** comme le vault ; gabarit public
|
||||
`underlay.yml.example`), surchargeable par `SETOPS_UNDERLAY`. Absent → tout reste inchangé.
|
||||
- **`make devis-reseau`** émet désormais une **section 0. Underlay** (VLANs, SVI, hints jumbo,
|
||||
IP des switches en commentaire) et ajoute les VLAN underlay au **trunk** Proxmox, avant les
|
||||
tenants. Respecte le dialecte (`cisco`/`binardat`).
|
||||
- **Preuve P23** — `underlay.py --verifier` : la fabric n'empiète pas sur la plage tenant.
|
||||
**Sautée** (⚪) si `underlay.yml` est absent (dépôt public), comme P16 sans vault.
|
||||
|
||||
Le plafond tenant (245) est inchangé : l'underlay occupe `10.0.0.0/16 .. 10.10.0.0/16`, laissé
|
||||
libre par la dérivation (index ≥ 1 → `10.11+`).
|
||||
|
||||
## 2026-07-23 (suite 7)
|
||||
|
||||
### Ajouté — dialecte de CLI du commutateur (`devis-reseau`)
|
||||
|
|
|
|||
4
Makefile
4
Makefile
|
|
@ -344,6 +344,10 @@ flux: ansible-runtime
|
|||
devis-reseau: ansible-runtime ## Devis switch (VLANs/SVIs/ACLs) du reseau converge, derive des nomenclatures. DIALECTE=cisco|binardat
|
||||
python3 scripts/devis_reseau.py $(if $(DIALECTE),--dialecte $(DIALECTE),)
|
||||
|
||||
.PHONY: underlay
|
||||
underlay: ## Underlay (fabric physique cluster-global : mgmt/iSCSI/Ceph) : affiche + valide (P23)
|
||||
python3 scripts/underlay.py
|
||||
|
||||
flux-verifier:
|
||||
python3 scripts/resoudre_flux.py verifier
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ make prouver
|
|||
| P20 | Adressage dérivé du seed | aucune nomenclature ne **stocke** d'adressage (supernet, sous-réseau, passerelle, VLAN) : tout se dérive du seul `index`. Attrape toute rechute vers l'écriture manuelle. |
|
||||
| P21 | Fédération sans collision | aucune paire d'instances **fédérées** ne partage un `index` (mêmes VLAN/VMID sur le trunk). Le garde-fou du multi-instances ; vue avec `make instances`. |
|
||||
| P22 | Plan de recette à jour | `docs/audit/plan-de-recette.md` (les 78 gestes manuels, générés des exercices du wiki) est **à jour** — il ne peut pas dériver du wiki. Régénérer : `make plan-recette`. |
|
||||
| P23 | Underlay sans collision | la **fabric physique** (`underlay.yml` : mgmt/iSCSI/Ceph, cluster-global) n'empiète pas sur la plage tenant — VLAN < 1000 et sous-réseaux hors des supernets `10.(10+index).0.0/16`. Sautée si `underlay.yml` absent. Vue : `make underlay`. |
|
||||
|
||||
> **P17, P18, P19 ferment les angles morts du harnais** : il ne vérifiait qu'*une* instance
|
||||
> et le seul modèle `socle`. P17 aurait attrapé l'hôte fantôme d'`integral` ; P18, les neuf
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ DOSSIER_INSTANCES = RACINE.parent
|
|||
# Adressage et VLAN : derives du seul index, source unique dans inventory_rules.
|
||||
sys.path.insert(0, str(RACINE / "scripts"))
|
||||
from inventory_rules import passerelle_de, supernet_de, vlan_de # noqa: E402
|
||||
import underlay as underlay_mod # noqa: E402 (fabric physique, cluster-global, hors tenant)
|
||||
|
||||
|
||||
def masque(cidr: int) -> str:
|
||||
|
|
@ -54,6 +55,30 @@ def remarque(dialecte: str, texte: str) -> list[str]:
|
|||
return [] if dialecte == "binardat" else [f" remark {texte}"]
|
||||
|
||||
|
||||
def section_underlay(underlay: dict | None) -> list[str]:
|
||||
"""VLANs + SVIs (si passerelle) de la fabric physique. Vide si aucun underlay."""
|
||||
reseaux = underlay_mod.reseaux(underlay)
|
||||
if not reseaux:
|
||||
return []
|
||||
out = ["! ----- 0. Underlay (fabric physique, cluster-global, hors tenant) -----"]
|
||||
for r in reseaux:
|
||||
out.append(f"vlan {r['vlan']}")
|
||||
out.append(f" name {r['nom']}")
|
||||
if r.get("mtu") and int(r["mtu"]) > 1500:
|
||||
out.append(f" ! jumbo MTU {r['mtu']} — a appliquer sur les ports + bridges Proxmox")
|
||||
for r in reseaux:
|
||||
if r.get("passerelle"):
|
||||
net = ipaddress.ip_network(r["sous_reseau"], strict=False)
|
||||
out.append(f"interface Vlan{r['vlan']}")
|
||||
out.append(f" description underlay {r['nom']}")
|
||||
out.append(f" ip address {r['passerelle']} {net.netmask}")
|
||||
out.append(" no shutdown")
|
||||
for h in underlay_mod.hotes(underlay):
|
||||
out.append(f"! {h.get('nom','?')} = {h.get('ip','?')} ({h.get('reseau','?')})")
|
||||
out.append("!")
|
||||
return out
|
||||
|
||||
|
||||
def prefixe(nom_dossier: str) -> str:
|
||||
base = re.sub(r"^OPS-", "", nom_dossier)
|
||||
base = re.sub(r"-lab$", "", base)
|
||||
|
|
@ -76,16 +101,19 @@ def decouvrir() -> list[tuple[str, str, dict]]:
|
|||
|
||||
def generer(tenants: list[tuple[str, str, dict]], dialecte: str | None = None) -> str:
|
||||
dialecte = dialecte or DIALECTE_DEFAUT
|
||||
underlay = underlay_mod.charger()
|
||||
out = ["configure terminal", f"! dialecte CLI : {dialecte}", "!"]
|
||||
out += [
|
||||
"! ============================================================",
|
||||
"! DEVIS SWITCH — reseau converge multi-tenant (Set-OPS)",
|
||||
"! Genere par scripts/devis_reseau.py depuis les nomenclatures.",
|
||||
"! VLAN = 1000 + index*10 + zone (unique globalement sur le trunk).",
|
||||
"! Underlay (VLAN < 1000) = fabric physique, cluster-global.",
|
||||
"! ============================================================",
|
||||
"!",
|
||||
"! ----- 1. VLANs -----",
|
||||
]
|
||||
out += section_underlay(underlay)
|
||||
out += ["! ----- 1. VLANs (tenants) -----"]
|
||||
for nom, pfx, n in tenants:
|
||||
out.append(f"! {nom} (index {n['index']})")
|
||||
for zone in sorted(n["categories"]):
|
||||
|
|
@ -117,11 +145,13 @@ def generer(tenants: list[tuple[str, str, dict]], dialecte: str | None = None) -
|
|||
out += remarque(dialecte, "Reste (Internet / inter-tenant controle) -> passerelle OPNsense")
|
||||
out.append(f" permit ip {reseau} {m} any")
|
||||
out += ["!", "! ----- 4. Trunk vers Proxmox + inter-switch (a adapter) -----"]
|
||||
vlans = ",".join(
|
||||
vlans_underlay = [str(r["vlan"]) for r in underlay_mod.reseaux(underlay)]
|
||||
vlans_tenants = [
|
||||
str(vlan_de(n["index"], zone))
|
||||
for _, _, n in tenants
|
||||
for zone in sorted(n["categories"])
|
||||
)
|
||||
]
|
||||
vlans = ",".join(vlans_underlay + vlans_tenants)
|
||||
out.append("interface <PORT-VERS-PROXMOX>")
|
||||
out.append(" switchport mode trunk")
|
||||
out.append(f" switchport trunk allowed vlan add {vlans}")
|
||||
|
|
|
|||
|
|
@ -290,6 +290,8 @@ PREUVES: list[dict] = [
|
|||
"cmds": [[sys.executable, "scripts/instances.py", "--verifier"]]},
|
||||
{"id": "P22", "titre": "Plan de recette a jour (genere du wiki)", "refs": ["AFF-002"],
|
||||
"cmds": [[sys.executable, "scripts/plan_recette.py", "--verifier"]]},
|
||||
{"id": "P23", "titre": "Underlay sans collision avec la plage tenant", "refs": ["AFF-001"],
|
||||
"cmds": [[sys.executable, "scripts/underlay.py", "--verifier"]]},
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -310,6 +312,12 @@ def _vault_requis_absent() -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def _underlay_absent() -> bool:
|
||||
"""Vrai si aucun underlay.yml n'est defini (fabric non decrite : rien a prouver)."""
|
||||
import underlay
|
||||
return underlay.chemin() is None
|
||||
|
||||
|
||||
def _executer(preuve: dict) -> tuple[str, str]:
|
||||
"""Renvoie (statut, detail). statut in {OK, ECHEC, SAUTE}."""
|
||||
if "func" in preuve:
|
||||
|
|
@ -344,6 +352,13 @@ def main(argv: list[str] | None = None) -> int:
|
|||
else:
|
||||
liste.append(preuve_inv)
|
||||
|
||||
# Preuve conditionnelle : underlay (fabric physique). Sautee si non definie.
|
||||
if _underlay_absent():
|
||||
p23 = next((p for p in liste if p["id"] == "P23"), None)
|
||||
if p23:
|
||||
liste.remove(p23)
|
||||
resultats.append((p23, "SAUTE", "Aucun underlay.yml (fabric non definie) — rien a prouver."))
|
||||
|
||||
for preuve in liste:
|
||||
statut, detail = _executer(preuve)
|
||||
resultats.append((preuve, statut, detail))
|
||||
|
|
|
|||
181
scripts/underlay.py
Normal file
181
scripts/underlay.py
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Underlay = la fabric physique partagee par les instances federees.
|
||||
|
||||
Cluster-global, HORS de la derivation par-tenant (aucun index) : management des
|
||||
switches et du mgmt Proxmox/OOB, stockage iSCSI, reseaux Ceph (public + cluster).
|
||||
Ce sont des reseaux du *sous-sol* : ils portent la flotte, ils n'appartiennent a
|
||||
aucun tenant.
|
||||
|
||||
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).
|
||||
|
||||
Fichier : `underlay.yml` a la racine du moteur (gitignore ; voir `underlay.yml.example`),
|
||||
surchargeable par `SETOPS_UNDERLAY`. Absent => le devis omet simplement la section underlay.
|
||||
|
||||
CLI : `python3 scripts/underlay.py` (affiche + valide), `--verifier` (P23, code de sortie).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import argparse
|
||||
import ipaddress
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
RACINE = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(RACINE / "scripts"))
|
||||
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.
|
||||
SEUIL_VLAN_TENANT = 1000
|
||||
|
||||
|
||||
def chemin() -> Path | None:
|
||||
"""Emplacement du underlay.yml : SETOPS_UNDERLAY, sinon racine du moteur. None si absent."""
|
||||
env = os.environ.get("SETOPS_UNDERLAY")
|
||||
if env:
|
||||
p = Path(env).expanduser()
|
||||
return p if p.exists() else None
|
||||
p = RACINE / "underlay.yml"
|
||||
return p if p.exists() else None
|
||||
|
||||
|
||||
def charger() -> dict | None:
|
||||
"""Le bloc `underlay` du fichier, ou None si absent/vide."""
|
||||
c = chemin()
|
||||
if not c:
|
||||
return None
|
||||
data = yaml.safe_load(c.read_text(encoding="utf-8")) or {}
|
||||
return data.get("underlay") or None
|
||||
|
||||
|
||||
def reseaux(underlay: dict | None) -> list[dict]:
|
||||
return (underlay or {}).get("reseaux", []) or []
|
||||
|
||||
|
||||
def hotes(underlay: dict | None) -> list[dict]:
|
||||
return (underlay or {}).get("hotes", []) or []
|
||||
|
||||
|
||||
def _tenants_reperes() -> tuple[set[int], list[str]]:
|
||||
"""VLAN et supernets des tenants federes (import paresseux : evite le cycle avec devis)."""
|
||||
import devis_reseau
|
||||
tenants = devis_reseau.decouvrir()
|
||||
vlans = {vlan_de(n["index"], z) for _, _, n in tenants for z in n["categories"]}
|
||||
supernets = [supernet_de(n["index"]) for _, _, n in tenants]
|
||||
return vlans, supernets
|
||||
|
||||
|
||||
def valider(underlay: dict | None,
|
||||
tenants_vlans: set[int] | None = None,
|
||||
tenants_supernets: list[str] | None = None) -> list[str]:
|
||||
"""Liste des erreurs (vide = conforme). Verifie champs requis, unicite, et
|
||||
surtout l'absence de collision avec la plage tenant (VLAN et sous-reseaux)."""
|
||||
if underlay is None:
|
||||
return [] # absent = rien a valider (le devis omettra la section)
|
||||
if tenants_vlans is None or tenants_supernets is None:
|
||||
tenants_vlans, tenants_supernets = _tenants_reperes()
|
||||
t_supernets = [ipaddress.ip_network(s, strict=False) for s in tenants_supernets]
|
||||
|
||||
erreurs: list[str] = []
|
||||
vus_vlan: dict[int, str] = {}
|
||||
vus_sous: list[tuple[ipaddress.IPv4Network, str]] = []
|
||||
noms_reseaux: set[str] = set()
|
||||
|
||||
for r in reseaux(underlay):
|
||||
nom = r.get("nom", "?")
|
||||
noms_reseaux.add(nom)
|
||||
vlan, sous = r.get("vlan"), r.get("sous_reseau")
|
||||
if vlan is None or sous is None:
|
||||
erreurs.append(f"reseau '{nom}': 'vlan' et 'sous_reseau' sont requis")
|
||||
continue
|
||||
if not isinstance(vlan, int) or vlan >= SEUIL_VLAN_TENANT:
|
||||
erreurs.append(f"reseau '{nom}': VLAN {vlan} doit etre un entier < {SEUIL_VLAN_TENANT} "
|
||||
f"(sinon empiete sur la plage tenant)")
|
||||
if vlan in tenants_vlans:
|
||||
erreurs.append(f"reseau '{nom}': VLAN {vlan} deja porte par un tenant federe")
|
||||
if vlan in vus_vlan:
|
||||
erreurs.append(f"reseau '{nom}': VLAN {vlan} en double avec '{vus_vlan[vlan]}'")
|
||||
vus_vlan[vlan] = nom
|
||||
try:
|
||||
net = ipaddress.ip_network(sous, strict=False)
|
||||
except ValueError:
|
||||
erreurs.append(f"reseau '{nom}': sous_reseau '{sous}' invalide")
|
||||
continue
|
||||
for ts in t_supernets:
|
||||
if net.overlaps(ts):
|
||||
erreurs.append(f"reseau '{nom}': {sous} chevauche le supernet tenant {ts}")
|
||||
for autre, autre_nom in vus_sous:
|
||||
if net.overlaps(autre):
|
||||
erreurs.append(f"reseau '{nom}': {sous} chevauche le reseau underlay '{autre_nom}'")
|
||||
if r.get("passerelle"):
|
||||
try:
|
||||
if ipaddress.ip_address(r["passerelle"]) not in net:
|
||||
erreurs.append(f"reseau '{nom}': passerelle {r['passerelle']} hors de {sous}")
|
||||
except ValueError:
|
||||
erreurs.append(f"reseau '{nom}': passerelle '{r['passerelle']}' invalide")
|
||||
vus_sous.append((net, nom))
|
||||
|
||||
par_nom = {r.get("nom"): r for r in reseaux(underlay)}
|
||||
for h in hotes(underlay):
|
||||
hn = h.get("nom", "?")
|
||||
r = par_nom.get(h.get("reseau"))
|
||||
if not r:
|
||||
erreurs.append(f"hote '{hn}': reseau '{h.get('reseau')}' inconnu")
|
||||
continue
|
||||
try:
|
||||
if ipaddress.ip_address(h["ip"]) not in ipaddress.ip_network(r["sous_reseau"], strict=False):
|
||||
erreurs.append(f"hote '{hn}': IP {h.get('ip')} hors de {r['sous_reseau']}")
|
||||
except (ValueError, KeyError):
|
||||
erreurs.append(f"hote '{hn}': IP absente ou invalide")
|
||||
return erreurs
|
||||
|
||||
|
||||
def verifier() -> int:
|
||||
underlay = charger()
|
||||
if underlay is None:
|
||||
print("Underlay : aucun fichier (underlay.yml absent) — section omise, rien a prouver.")
|
||||
return 0
|
||||
erreurs = valider(underlay)
|
||||
if erreurs:
|
||||
print("Underlay NON conforme :")
|
||||
for e in erreurs:
|
||||
print(f" - {e}")
|
||||
return 1
|
||||
n = len(reseaux(underlay))
|
||||
print(f"Underlay conforme : {n} reseau(x), aucune collision avec la plage tenant.")
|
||||
return 0
|
||||
|
||||
|
||||
def afficher() -> None:
|
||||
underlay = charger()
|
||||
if underlay is None:
|
||||
print("Aucun underlay defini (underlay.yml absent). Copier underlay.yml.example pour commencer.")
|
||||
return
|
||||
print(f"Underlay (fabric physique, cluster-global) — source : {chemin()}")
|
||||
print(f"{'RESEAU':<16}{'VLAN':>6} {'SOUS-RESEAU':<18}{'PASSERELLE':<15}{'MTU':>6} DESCRIPTION")
|
||||
for r in reseaux(underlay):
|
||||
print(f"{r.get('nom','?'):<16}{str(r.get('vlan','?')):>6} "
|
||||
f"{r.get('sous_reseau','?'):<18}{str(r.get('passerelle') or '-'):<15}"
|
||||
f"{str(r.get('mtu') or '-'):>6} {r.get('description','')}")
|
||||
if hotes(underlay):
|
||||
print("\nHotes fixes documentes :")
|
||||
for h in hotes(underlay):
|
||||
print(f" {h.get('nom','?'):<16} {h.get('ip','?'):<15} ({h.get('reseau','?')})")
|
||||
erreurs = valider(underlay)
|
||||
print("\n" + ("Conforme (aucune collision tenant)." if not erreurs
|
||||
else "ERREURS :\n - " + "\n - ".join(erreurs)))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser(description=__doc__)
|
||||
ap.add_argument("--verifier", action="store_true", help="valide l'underlay (P23), code de sortie")
|
||||
args = ap.parse_args()
|
||||
if args.verifier:
|
||||
sys.exit(verifier())
|
||||
afficher()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
45
underlay.yml.example
Normal file
45
underlay.yml.example
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Underlay — la fabric physique partagee par les instances federees.
|
||||
#
|
||||
# Cluster-global : ces reseaux portent TOUTE la flotte, ils n'appartiennent a aucun
|
||||
# tenant et ne derivent d'aucun `index`. Ils vivent dans le *sous-sol* du modele.
|
||||
#
|
||||
# Copier vers `underlay.yml` (a la racine du moteur ; gitignore) et adapter a ta fabric.
|
||||
# Surchargeable par SETOPS_UNDERLAY=/chemin/underlay.yml. Absent => le devis switch
|
||||
# omet simplement la section underlay (retro-compatible).
|
||||
#
|
||||
# Regles (prouvees par P23 / `make underlay`) :
|
||||
# - VLAN < 1000 : franchement SOUS la plage tenant (VLAN tenant = 1000+index*10+zone).
|
||||
# - sous_reseau : ne chevauche aucun supernet tenant (10.(10+index).0.0/16, index >= 1).
|
||||
# => la plage 10.0.0.0/16 .. 10.10.0.0/16 est libre pour l'underlay.
|
||||
# - passerelle : OPTIONNELLE. Presente => un SVI est genere (routage inter-VLAN).
|
||||
# Storage/Ceph restent en general L2 pur (pas de passerelle).
|
||||
---
|
||||
underlay:
|
||||
reseaux:
|
||||
- nom: management
|
||||
description: Switches, mgmt Proxmox, OOB/IPMI
|
||||
vlan: 10
|
||||
sous_reseau: 10.0.0.0/24
|
||||
passerelle: 10.0.0.1 # SVI (retirer pour du L2 pur)
|
||||
mtu: 1500
|
||||
- nom: stockage-iscsi
|
||||
description: iSCSI MPIO
|
||||
vlan: 20
|
||||
sous_reseau: 10.0.1.0/24
|
||||
mtu: 9000 # jumbo
|
||||
- nom: ceph-public
|
||||
description: Clients <-> MON/OSD
|
||||
vlan: 30
|
||||
sous_reseau: 10.0.2.0/24
|
||||
mtu: 9000
|
||||
- nom: ceph-cluster
|
||||
description: OSD <-> OSD (replication, backfill, recovery)
|
||||
vlan: 31
|
||||
sous_reseau: 10.0.3.0/24
|
||||
mtu: 9000
|
||||
|
||||
# Hotes fixes documentes (optionnel) : IP hors DHCP, verifiees dans leur reseau.
|
||||
hotes:
|
||||
- { nom: switch-01, reseau: management, ip: 10.0.0.2 }
|
||||
- { nom: switch-02, reseau: management, ip: 10.0.0.3 }
|
||||
- { nom: switch-03, reseau: management, ip: 10.0.0.4 }
|
||||
|
|
@ -60,6 +60,24 @@ Choisir le dialecte : `make devis-reseau DIALECTE=binardat`, la variable d'envir
|
|||
`SETOPS_DIALECTE=binardat` (défaut permanent), ou l'option `--dialecte`. La GUI (vue Réseau,
|
||||
lecture seule) suit l'environnement. **Le dépôt public reste générique** (`cisco`).
|
||||
|
||||
**L'underlay — le sous-sol.** Les VLAN tenant (`1000+index×10+zone`) sont les *overlays*. En
|
||||
dessous vit la **fabric physique** partagée par toute la flotte : management des switches et de
|
||||
Proxmox/OOB, iSCSI, Ceph (public + cluster). Elle **n'appartient à aucun tenant** et ne dérive
|
||||
d'aucun `index`. On la décrit dans `underlay.yml` (racine du moteur, gitignore comme le vault ;
|
||||
gabarit `underlay.yml.example`) :
|
||||
|
||||
| Underlay | VLAN | Sous-réseau |
|
||||
|---|---|---|
|
||||
| management (switches, Proxmox, OOB) | 10 | `10.0.0.0/24` |
|
||||
| stockage / iSCSI | 20 | `10.0.1.0/24` |
|
||||
| ceph-public | 30 | `10.0.2.0/24` |
|
||||
| ceph-cluster | 31 | `10.0.3.0/24` |
|
||||
|
||||
`make underlay` l'affiche et le **valide** : VLAN < 1000 et sous-réseaux hors des supernets
|
||||
tenant (`10.(10+index).0.0/16`) — **aucune collision possible** avec les overlays. `make
|
||||
devis-reseau` en émet la config (section 0) et l'ajoute au trunk. La **preuve P23** garde la
|
||||
règle ; elle est *sautée* si aucun `underlay.yml` n'est défini.
|
||||
|
||||
---
|
||||
|
||||
## ③ Pourquoi c'est transférable
|
||||
|
|
|
|||
Loading…
Reference in a new issue