pare-feu Proxmox : un seul schéma de nommage

Les IPSets portaient l'étiquette longue (`chez17_serveur_postgresql`), les
groupes l'index court (`t17-srv-postgresql`) : deux conventions dans un même
document. Tout porte maintenant `t<index>-` et la même forme abrégée.

La troncature reste propre à chaque objet — Proxmox est large sur les IPSets,
étroit sur les groupes. Un nom peut être entier d'un côté et abrégé de
l'autre ; chacun respecte sa contrainte, le préfixe reste commun.

Vérifié : aucune collision d'IPSet, et tout renvoi `+X` d'une règle pointe
vers un IPSet existant — 58 IPSets, 34 groupes, aucun orphelin.

Preuves : 25 OK, 0 échec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-03 11:50:50 -04:00
parent 6de44736ff
commit ece250ae49
2 changed files with 32 additions and 9 deletions

View file

@ -24,6 +24,18 @@ Le préfixe porte maintenant l'**index** (`t17-`) plutôt que l'étiquette (`che
rend la troncature bien plus rare, et une garde **échoue** sur toute collision plutôt que
d'émettre un devis pareil. Exercée.
### Unifié — un seul schéma de nommage dans le devis
Les IPSets portaient l'étiquette longue (`chez17_serveur_postgresql`), les groupes l'index
court (`t17-srv-postgresql`) : deux conventions à lire dans un même document. Tout porte
désormais le préfixe `t<index>-` et la même forme abrégée de rôle.
La troncature reste **propre à chaque objet** : Proxmox est large sur les IPSets, étroit
(18 caractères) sur les groupes. Un nom peut donc être entier d'un côté et abrégé de l'autre —
chacun respecte sa contrainte, et le préfixe reste commun.
Vérifié : aucune collision d'IPSet, et **tout renvoi `+X` d'une règle pointe vers un IPSet qui
existe** — 58 IPSets, 34 groupes, aucun orphelin.
### Corrigé — l'affectation variait selon l'état du tenant
Elle partait de `hotes_actifs`, avec un repli sur « tous » quand il n'y en avait aucun. Deux
tenants donnaient donc deux comportements : Technolibre listait ses 14 VM (zéro actif → repli),

View file

@ -44,13 +44,25 @@ from resoudre_flux import ( # noqa: E402
from devis_reseau import admin_de, decouvrir, inventaire_de, prefixe # noqa: E402
def nom_ipset(role: str, etiquette: str) -> str:
return f"{etiquette}_{role}".lower()
LONGUEUR_MAX_GROUPE = 18 # limite de Proxmox pour un nom de groupe de securite
def _role_court(role: str) -> str:
"""Forme abregee d'un role, commune aux IPSets et aux groupes."""
return role.replace("serveur_", "srv-").replace("client_", "cli-").replace("_", "-")
def nom_ipset(role: str, index: int) -> str:
"""Nom d'IPSet. MEME prefixe que les groupes — `t17-` — pour qu'un seul schema de
nommage se lise dans tout le devis.
Pas de troncature ici : Proxmox est large sur les IPSets, etroit sur les groupes.
Un nom peut donc etre entier cote IPSet et abrege cote groupe ; chacun respecte sa
contrainte, et le prefixe reste commun.
"""
return f"t{index}-{_role_court(role)}".lower()
def nom_groupe(role: str, index: int) -> str:
"""Nom de groupe de securite, sous la limite de 18 caracteres de Proxmox.
@ -58,8 +70,7 @@ def nom_groupe(role: str, index: int) -> str:
`chez17-`, ce qui laisse trois caracteres de plus au role et rend la troncature
beaucoup plus rare. Les groupes sont a l'echelle du datacenter, d'ou le prefixe.
"""
court = role.replace("serveur_", "srv-").replace("client_", "cli-").replace("_", "-")
return f"t{index}-{court}".lower()[:LONGUEUR_MAX_GROUPE]
return f"t{index}-{_role_court(role)}".lower()[:LONGUEUR_MAX_GROUPE]
def _verifier_unicite(noms: list[str], tenant: str) -> None:
@ -106,7 +117,7 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict:
continue
membres = sorted({ips[h] for h in _hotes_du_groupe(data, groupe) if h in ips})
if membres:
ipsets[nom_ipset(groupe, etiquette)] = {
ipsets[nom_ipset(groupe, n["index"])] = {
"role": groupe, "membres": membres,
}
@ -131,8 +142,8 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict:
sans_source.append(f"{groupe}:{','.join(_ports(fl))}")
continue
# Source par IPSet quand le pair est UN role : une seule liste a tenir.
source = (f"+{nom_ipset(pairs[0], etiquette)}"
if len(pairs) == 1 and nom_ipset(pairs[0], etiquette) in ipsets
source = (f"+{nom_ipset(pairs[0], n['index'])}"
if len(pairs) == 1 and nom_ipset(pairs[0], n["index"]) in ipsets
else ",".join(srcs))
for port in _ports(fl):
regles.append({