Question de l'exploitant : « je ne vois nulle part un MTU a 1450 ». Fondee. Le 1450 existait bien — MTU_OVERLAY_DEFAUT dans underlay.py, dont P23 derive sa garde (transport >= overlay + 50), pose par le devis SDN sur chaque zone, et verifie sur le cluster. Mais il ne se propage pas a la carte de l'invite : les 14 VM tournaient a 1500, et cloner_vm_debian.yml n'avait aucune occurrence de `mtu`. La VM emettait donc des trames que son propre chemin ne pouvait pas encapsuler — la panne que le registre des flux appelle « la plus couteuse a diagnostiquer ». Invisible parce que les 14 VM vivent sur asgard : deux VM du meme hyperviseur communiquent par le pont local, sans encapsulation. Le defaut serait apparu au premier eclatement de la flotte — c'est-a-dire quand il faudra heberger les deux tenants ensemble. Corrige en DEUX endroits, avec `mtu=1` (« herite du pont ») plutot que 1450 en dur : juste en SDN comme hors SDN, et encore juste le jour des trames jumbo. Le GABARIT le porte — proposition de l'exploitant, meilleure que la mienne : elle couvre les clonages qui ne passent pas par le playbook. Et le playbook le repose a chaque clone, parce qu'un gabarit se RECAPTURE et que ce qui n'est pas versionne se perd en silence. make mtu-mesurer (7e devis) rattache chaque hote a sa zone par son pont derive et lit le MTU attendu dans devis_sdn.py. Ecart trouve sur 14 hotes du premier coup, puis confirme corrige. ET J'AI CASSE LA FLOTTE EN CORRIGEANT : applique aux cartes de VM EN MARCHE, le changement detache et rebranche la carte sans que l'invite reconfigure son interface. 0/14 joignables pendant trois minutes. L'agent qemu, independant du reseau invite, a permis de constater et de redemarrer par l'API. Dans le playbook la meme tache s'execute AVANT le demarrage du clone : aucun risque. Sur une VM en service : poser la config, puis redemarrer — une seule d'abord. Verifie : mtu-mesurer CONFORME 14/14 a 1450, prouver.py 35 OK, ansible-lint production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
94 lines
3.9 KiB
Python
94 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Devis du MTU : l'invite porte-t-il le MTU de la zone a laquelle il est attache ?
|
|
|
|
Le MTU de la ZONE est pose sur l'hyperviseur par `make sdn-appliquer`. Rien ne garantit
|
|
que l'INVITE le porte : sa carte doit l'heriter du pont (`mtu=1` chez Proxmox), sans quoi
|
|
il nait a 1500 quelle que soit la zone.
|
|
|
|
Quand les deux divergent, la panne est sournoise : la connexion s'etablit, les petites
|
|
requetes passent, les grosses reponses restent suspendues. Elle depend alors entierement de
|
|
la decouverte de MTU de chemin — donc de messages ICMP que n'importe quel pare-feu peut
|
|
avaler en silence.
|
|
|
|
ET ELLE EST INVISIBLE TANT QUE LA FLOTTE TIENT SUR UN SEUL HYPERVISEUR : deux VM du meme
|
|
noeud communiquent par le pont local, sans encapsulation. Mesure du 2026-08-10 : zones
|
|
`t11`/`t17` a 1450, les 14 invites a 1500, et aucun symptome — les 14 etaient sur `asgard`.
|
|
Le jour ou on repartit la flotte, tout casse d'un coup et rien ne dit pourquoi.
|
|
|
|
Le MTU attendu n'est pas saisi : il est lu dans `devis_sdn.py`, la source qui configure les
|
|
zones. Le rattachement hote -> zone se derive du pont porte par l'inventaire (`t17serv` est
|
|
un VNet de la zone `t17`).
|
|
|
|
Aucun acces reseau ici — c'est le playbook qui releve (D-23).
|
|
|
|
make mtu-mesurer
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
RACINE = Path(__file__).resolve().parent.parent
|
|
|
|
|
|
def zones_attendues() -> dict[str, int]:
|
|
"""MTU attendu par VNet, lu a la source qui configure les zones."""
|
|
r = subprocess.run([sys.executable, str(RACINE / "scripts" / "devis_sdn.py"), "--json"],
|
|
cwd=RACINE, capture_output=True, text=True)
|
|
if r.returncode != 0:
|
|
raise SystemExit("Le devis SDN ne se genere pas :\n" + r.stderr.strip()[:300])
|
|
par_vnet: dict[str, int] = {}
|
|
for z in (json.loads(r.stdout).get("zones") or []):
|
|
for v in (z.get("vnets") or []):
|
|
par_vnet[str(v.get("vnet"))] = int(z.get("mtu") or 0)
|
|
return par_vnet
|
|
|
|
|
|
def analyser(releve: dict, attendus: dict[str, int]) -> tuple[list[str], list[str]]:
|
|
ecarts: list[str] = []
|
|
lignes: list[str] = []
|
|
invites = releve.get("invites") or {}
|
|
ponts = releve.get("ponts") or {}
|
|
for hote in sorted(invites):
|
|
vu = int(invites[hote] or 0)
|
|
pont = str(ponts.get(hote) or "")
|
|
attendu = attendus.get(pont)
|
|
if attendu is None:
|
|
# Hors SDN (pont classique) : le devis n'a pas d'attendu derive, il se tait
|
|
# plutot que d'inventer un seuil.
|
|
lignes.append(f" -- {hote:<16} {vu:>5} pont {pont or '(inconnu)'} — hors zone SDN")
|
|
continue
|
|
ok = vu == attendu
|
|
lignes.append(f" {'ok ' if ok else 'ECART'} {hote:<16} {vu:>5} attendu {attendu} "
|
|
f"(zone du pont {pont})")
|
|
if not ok:
|
|
ecarts.append(f"{hote} : carte a {vu}, zone {pont} a {attendu}")
|
|
return ecarts, lignes
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
ap = argparse.ArgumentParser(description=__doc__)
|
|
ap.add_argument("--releve", default=None)
|
|
a = ap.parse_args(argv)
|
|
chemin = Path(a.releve) if a.releve else RACINE / "instance" / "devis-mtu.json"
|
|
if not chemin.is_file():
|
|
raise SystemExit(f"Aucun releve : {chemin}\nLancer d'abord `make mtu-mesurer`.")
|
|
ecarts, lignes = analyser(json.loads(chemin.read_text()), zones_attendues())
|
|
print("Devis du MTU — carte de l'invite contre MTU de sa zone\n")
|
|
print("\n".join(lignes))
|
|
if ecarts:
|
|
print(f"\nECART : {len(ecarts)} invite(s) ne portent pas le MTU de leur zone.")
|
|
for e in ecarts:
|
|
print(" -", e)
|
|
print("\n Corriger : `mtu: 1` sur la carte (herite du pont) — deja pose par\n"
|
|
" `cloner_vm_debian.yml`. Une VM existante doit redemarrer pour le prendre.")
|
|
return 1
|
|
print("\nCONFORME : chaque invite porte le MTU de sa zone.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|