pools Proxmox : un par tenant, dérivé de l'index (D-37, P28)
Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez
Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le
reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101
contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est
indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les
certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}.
Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y
sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le
codage.
Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par
P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11.
make devis-proxmox-pools rattrape la flotte existante (création du pool, puis
affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes :
make creer-vm dérive le pool par la même fonction et le passe à la création. Le
playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et
l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le
rattrapage passe par les membres.
P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID
— une machine appartenant à deux tenants serait pire qu'une homonymie.
Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai
gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart.
27 preuves OK, 0 échec. --syntax-check du playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""Devis des pools Proxmox — un pool par tenant.
|
|
|
|
|
|
|
|
|
|
POURQUOI. Onze des quatorze serveurs portent le MEME nom court chez deux tenants
|
|
|
|
|
(`infra-pki-01`, `backup-01`, `obs-01`...). Ce n'est pas un defaut : c'est la preuve
|
|
|
|
|
que la nomenclature est un vrai gabarit — meme fonction, meme nom, partout. Tout le
|
|
|
|
|
reste differe et derive du seed (IP, VMID, VLAN/VNI, FQDN), et rien dans Set-OPS
|
|
|
|
|
n'est indexe sur le nom court : les operations Proxmox portent toutes un `vmid`, les
|
|
|
|
|
certificats un FQDN, les depots de sauvegarde vivent chez le serveur du tenant.
|
|
|
|
|
|
|
|
|
|
Le seul endroit ou l'homonymie se paie est HUMAIN : la console Proxmox affiche le
|
|
|
|
|
NOM. Deux `infra-pki-01` y sont indiscernables a l'oeil, et c'est ainsi qu'on eteint
|
|
|
|
|
la mauvaise machine. Le VMID porte pourtant le tenant (117... contre 111...), mais
|
|
|
|
|
il faut connaitre le codage pour le lire.
|
|
|
|
|
|
|
|
|
|
Un pool par tenant restitue l'appartenance dans l'arbre du cluster, sans renommer
|
|
|
|
|
quoi que ce soit. Effet secondaire utile : un pool est aussi une PORTEE DE
|
|
|
|
|
PERMISSION — c'est l'objet auquel on attachera plus tard un acces par tenant.
|
|
|
|
|
|
|
|
|
|
NON destructif : ce script n'ecrit rien sur le cluster. Il derive du plan ce que
|
|
|
|
|
l'etat cible devrait etre, et le rend a relire. `--json` sert l'API.
|
|
|
|
|
|
|
|
|
|
LIMITE ASSUMEE. Le devis ne LIT PAS le cluster : il ne peut donc pas dire ce qui est
|
|
|
|
|
deja en place, seulement ce que le plan implique. Les commandes emises sont
|
|
|
|
|
idempotentes — reappliquer un membre deja present ne fait rien.
|
|
|
|
|
|
|
|
|
|
Usage :
|
|
|
|
|
python3 scripts/devis_proxmox_pools.py # devis lisible
|
|
|
|
|
python3 scripts/devis_proxmox_pools.py --json # meme contenu, pour l'API
|
|
|
|
|
python3 scripts/devis_proxmox_pools.py --verifier # garde : un pool unique par tenant
|
|
|
|
|
"""
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
|
import json
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
|
|
RACINE = Path(__file__).resolve().parents[1]
|
|
|
|
|
sys.path.insert(0, str(RACINE / "scripts"))
|
|
|
|
|
|
pools nommes comme les depots, et les cles sortent du poste
LE NOM D UN POOL EST CELUI DE SON DEPOT. Chezlepro-17 devient OPS-Chezlepro :
le seed se lisait dans le nom, ce qui obligeait a connaitre le codage — et
surtout le nom CHANGEAIT si l index changeait, ce que la renumerotation du
site a montre le jour meme.
Site-OPS ne derive de rien, et c est le point : les machines du genome ne
dependent d aucun index, elles sont l infrastructure SUR laquelle les index
vivent. Sans ce bloc elles restaient hors de tout pool.
P69 — l amorcage d un tenant designe-t-il le site REEL ? dns_amorcage et
artefacts_amorcage sont ecrits a la main, volontairement : au moment ou ils
servent la machine ne resout aucun nom. Mais ils designent des machines DU
SITE, et n ont pas suivi son renumerotage. La reconstruction du locataire
s est arretee sur Failed to update apt cache, a quinze couches de sa cause.
La preuve ne juge que les valeurs qui PRETENDENT designer le site : viser
9.9.9.9 est un choix, pas un oubli.
LES CLES SORTENT DU POSTE, EN CLAIR, ET C EST RAISONNE. Support perdu : LUKS
s en charge. Poste compromis : la seconde couche n aide pas, les originaux
sont dans ~/.config sur ce meme poste. Elle coutait une phrase de passe
stockee nulle part — le seul point que la procedure ne couvre pas. Option
--support-chiffre explicite ; le defaut reste GPG, parce qu un support non
chiffre est le cas le plus frequent.
Et ma note qui disait les cles sorties depuis le 5 septembre etait fausse :
le support ne portait que le depot hors site du 1er.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-12 21:37:28 -04:00
|
|
|
from inventory_rules import (POOL_SITE,
|
resolution d'instance : une seule, partagee — au lieu de neuf copies
Cinq jours, cinq defauts, tous de la meme famille : « quelle instance, quel inventaire ? »
Neuf modules portaient chacun leur reponse.
- 18 aout : P03 comparait chaque instance a l'inventaire d'une AUTRE ;
- 19 aout : verifier_ports codait `principal/` en dur ; verifier_intrants et
_frontiere_absente lisaient le symlink au lieu de la variable ;
- 20 aout : devis_placement rendait un verdict juste sur le mauvais tenant ;
- 22 aout : P35, puis P36 — la dixieme, trouvee par la preuve elle-meme.
Aucune n'etait une faute d'inattention : chacune avait ete ecrite de bonne foi, a un
moment ou le besoin semblait local. C'est le mode de panne de la duplication — pas
l'erreur, mais la DERIVE, invisible depuis l'interieur d'un fichier.
LA RESOLUTION UNIQUE. `inventory_rules` porte instance_courante(), inventaire_de(),
dossier_inventaire() et plan_de(). Trois niveaux de repli, dont le TROISIEME manquait a la
moitie des copies : un hosts.yml existant, puis un REPERTOIRE existant (instance neuve —
c'est ce qui faisait echouer `make instancier` sur le modele public), puis le defaut.
Vingt-huit modules y sont branches.
CE QUI REND CE REFACTOR SUR : avant de toucher quoi que ce soit, chaque module a ete
interroge sur ce qu'il resolvait, pour les DEUX ecosystemes. Apres refactor, meme mesure :
17 modules x 2 instances, diff VIDE. Aucune resolution n'a change — prouve, pas suppose.
P41 echoue des qu'un module reintroduit une copie. Eprouvee en negatif : une copie
replacee dans genome.py est signalee avec son numero de ligne. Trois exemptions nommees :
instances.py et inventory_gui.py manipulent le SYMLINK lui-meme (bascule d'instance), et
devis_opnsense lit deliberement quelle instance est ACTIVE. Elles parlent du lien, pas de
la resolution.
make verifier 41 OK, 0 echec, 0 saute ; make ci idem ; lint vert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 14:19:55 -04:00
|
|
|
instance_courante, # noqa: E402
|
pools Proxmox : un par tenant, dérivé de l'index (D-37, P28)
Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez
Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le
reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101
contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est
indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les
certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}.
Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y
sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le
codage.
Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par
P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11.
make devis-proxmox-pools rattrape la flotte existante (création du pool, puis
affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes :
make creer-vm dérive le pool par la même fonction et le passe à la création. Le
playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et
l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le
rattrapage passe par les membres.
P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID
— une machine appartenant à deux tenants serait pire qu'une homonymie.
Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai
gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart.
27 preuves OK, 0 échec. --syntax-check du playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
|
|
|
charger_serveurs,
|
|
|
|
|
deriver_nomenclature,
|
|
|
|
|
fonction_seq,
|
|
|
|
|
pool_de,
|
|
|
|
|
)
|
2026-09-28 09:57:06 -04:00
|
|
|
from devis_reseau import DOSSIER_INSTANCES, decouvrir_du_site # noqa: E402
|
pools Proxmox : un par tenant, dérivé de l'index (D-37, P28)
Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez
Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le
reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101
contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est
indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les
certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}.
Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y
sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le
codage.
Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par
P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11.
make devis-proxmox-pools rattrape la flotte existante (création du pool, puis
affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes :
make creer-vm dérive le pool par la même fonction et le passe à la création. Le
playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et
l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le
rattrapage passe par les membres.
P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID
— une machine appartenant à deux tenants serait pire qu'une homonymie.
Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai
gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart.
27 preuves OK, 0 échec. --syntax-check du playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def _plan_de(nom_instance: str) -> dict:
|
|
|
|
|
p = DOSSIER_INSTANCES / nom_instance / "plan" / "serveurs.yml"
|
|
|
|
|
if not p.is_file():
|
|
|
|
|
return {}
|
|
|
|
|
return charger_serveurs(p).get("serveurs") or {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def pool_actif() -> str:
|
|
|
|
|
"""Pool du tenant ACTIF — ce que le playbook de clonage passe a la creation.
|
|
|
|
|
|
|
|
|
|
Meme derivation que le devis : `make creer-vm` et `make devis-proxmox-pools` ne
|
|
|
|
|
peuvent pas nommer le pool differemment. Chaine vide si aucune instance n'est
|
|
|
|
|
liee — l'appelant omet alors le parametre plutot que d'inventer un nom.
|
|
|
|
|
"""
|
resolution d'instance : une seule, partagee — au lieu de neuf copies
Cinq jours, cinq defauts, tous de la meme famille : « quelle instance, quel inventaire ? »
Neuf modules portaient chacun leur reponse.
- 18 aout : P03 comparait chaque instance a l'inventaire d'une AUTRE ;
- 19 aout : verifier_ports codait `principal/` en dur ; verifier_intrants et
_frontiere_absente lisaient le symlink au lieu de la variable ;
- 20 aout : devis_placement rendait un verdict juste sur le mauvais tenant ;
- 22 aout : P35, puis P36 — la dixieme, trouvee par la preuve elle-meme.
Aucune n'etait une faute d'inattention : chacune avait ete ecrite de bonne foi, a un
moment ou le besoin semblait local. C'est le mode de panne de la duplication — pas
l'erreur, mais la DERIVE, invisible depuis l'interieur d'un fichier.
LA RESOLUTION UNIQUE. `inventory_rules` porte instance_courante(), inventaire_de(),
dossier_inventaire() et plan_de(). Trois niveaux de repli, dont le TROISIEME manquait a la
moitie des copies : un hosts.yml existant, puis un REPERTOIRE existant (instance neuve —
c'est ce qui faisait echouer `make instancier` sur le modele public), puis le defaut.
Vingt-huit modules y sont branches.
CE QUI REND CE REFACTOR SUR : avant de toucher quoi que ce soit, chaque module a ete
interroge sur ce qu'il resolvait, pour les DEUX ecosystemes. Apres refactor, meme mesure :
17 modules x 2 instances, diff VIDE. Aucune resolution n'a change — prouve, pas suppose.
P41 echoue des qu'un module reintroduit une copie. Eprouvee en negatif : une copie
replacee dans genome.py est signalee avec son numero de ligne. Trois exemptions nommees :
instances.py et inventory_gui.py manipulent le SYMLINK lui-meme (bascule d'instance), et
devis_opnsense lit deliberement quelle instance est ACTIVE. Elles parlent du lien, pas de
la resolution.
make verifier 41 OK, 0 echec, 0 saute ; make ci idem ; lint vert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 14:19:55 -04:00
|
|
|
instance = instance_courante()
|
pools Proxmox : un par tenant, dérivé de l'index (D-37, P28)
Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez
Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le
reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101
contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est
indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les
certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}.
Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y
sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le
codage.
Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par
P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11.
make devis-proxmox-pools rattrape la flotte existante (création du pool, puis
affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes :
make creer-vm dérive le pool par la même fonction et le passe à la création. Le
playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et
l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le
rattrapage passe par les membres.
P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID
— une machine appartenant à deux tenants serait pire qu'une homonymie.
Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai
gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart.
27 preuves OK, 0 échec. --syntax-check du playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
|
|
|
nomenclature = instance / "plan" / "nomenclature.yml"
|
|
|
|
|
if not nomenclature.is_file():
|
|
|
|
|
return ""
|
|
|
|
|
n = yaml.safe_load(nomenclature.read_text(encoding="utf-8")) or {}
|
|
|
|
|
if n.get("index") is None:
|
|
|
|
|
return ""
|
|
|
|
|
return pool_de(instance.resolve().name, int(n["index"]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def construire(tenants: list[tuple[str, str, dict]]) -> dict:
|
|
|
|
|
"""{pools: [{pool, tenant, index, membres: [{nom, vmid, etat}], sans_vmid: []}]}"""
|
|
|
|
|
blocs = []
|
|
|
|
|
for nom_instance, _prefixe, nomenclature in tenants:
|
|
|
|
|
index = int(nomenclature["index"])
|
|
|
|
|
membres, sans_vmid = [], []
|
|
|
|
|
for nom, srv in sorted(_plan_de(nom_instance).items()):
|
|
|
|
|
_, seq = fonction_seq(nom)
|
|
|
|
|
derive = deriver_nomenclature(str(srv.get("fonction", "")), seq, nomenclature) or {}
|
|
|
|
|
vmid = derive.get("vmid")
|
|
|
|
|
if vmid is None:
|
|
|
|
|
# Une fonction absente de la nomenclature ne derive pas de VMID : on ne
|
|
|
|
|
# peut pas la placer. La taire ferait croire le pool complet.
|
|
|
|
|
sans_vmid.append(nom)
|
|
|
|
|
continue
|
|
|
|
|
membres.append({"nom": nom, "vmid": int(vmid),
|
|
|
|
|
"etat": str(srv.get("etat", "planifie"))})
|
|
|
|
|
blocs.append({
|
|
|
|
|
"pool": pool_de(nom_instance, index),
|
|
|
|
|
"tenant": nom_instance,
|
|
|
|
|
"index": index,
|
|
|
|
|
"membres": membres,
|
|
|
|
|
"sans_vmid": sans_vmid,
|
|
|
|
|
})
|
|
|
|
|
return {"pools": blocs}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def rendre(devis: dict) -> str:
|
|
|
|
|
out = [
|
|
|
|
|
"# Devis des pools Proxmox — un pool par tenant",
|
|
|
|
|
"#",
|
|
|
|
|
"# Ne renomme RIEN : les noms courts identiques d'un tenant a l'autre sont",
|
|
|
|
|
"# voulus (meme fonction, meme nom). Le pool restitue l'appartenance dans la",
|
|
|
|
|
"# console, la ou l'oeil ne voyait que deux `infra-pki-01`.",
|
|
|
|
|
"#",
|
|
|
|
|
"# Ce devis derive du PLAN et ne lit pas le cluster : il dit l'etat cible, pas",
|
|
|
|
|
"# l'ecart. Les commandes sont idempotentes.",
|
|
|
|
|
"",
|
|
|
|
|
]
|
|
|
|
|
for b in devis["pools"]:
|
|
|
|
|
actifs = [m for m in b["membres"] if m["etat"] == "actif"]
|
|
|
|
|
out += [
|
|
|
|
|
f"## {b['tenant']} — pool `{b['pool']}` (index {b['index']})",
|
|
|
|
|
f"# {len(b['membres'])} VM au plan, dont {len(actifs)} active(s).",
|
proxmox : pools créés, jeton normalisé, reliquat de voûte supprimé
Reconnaissance en lecture seule de l'API du cluster, avec le jeton de la voûte.
Trois valeurs devinées étaient fausses, et deux défauts bloquants sont apparus.
Corrigé d'après le cluster
- stockages : truenas-dbsql manquait ; le catalogue ne garde que ceux qui
portent `images` (PBS, cephFS, local et truenas iSCSI n'accueillent pas de
disque de VM) ;
- ponts : vmbr0 avait été omis, et l'uniformité sur les trois nœuds n'avait pas
été vérifiée — un pont partiel empêche la VM de démarrer sur certains nœuds.
Pools
Chezlepro-17 et Technolibre-11 créés, dérivés comme le reste. Les pools
Env.Tenant antérieurs (Prod.Chezlepro, Lab.KBR...) sont l'ancien monde : on n'y
touche pas et on n'y verse pas la flotte générée. Diff réel : 2 pools ajoutés,
0 retiré, 1 VM sur 38 déplacée — infra-pki-01, qui n'avait aucun pool.
Défaut bloquant : make creer-vm aurait échoué en 401
proxmoxer recompose `utilisateur!nom` à partir d'api_user et d'api_token_id. La
voûte stocke la forme complète, que les playbooks passaient telle quelle, d'où
un 401 muet — alors que le même jeton fonctionne en curl. Mesuré des deux côtés
avec un module en lecture seule : forme complète = 401, forme courte = OK.
Normalisation par split('!') | last, qui accepte les deux écritures.
Reliquat proxmox.vault.yml supprimé
Toléré « en compatibilité », il restait le seul porteur du jeton chez
Technolibre — et comme *.vault.yml est gitignoré, ce jeton ne voyageait avec
aucun dépôt : une voûte unique (D-19) qui ne l'était pas. Migration faite en
mémoire, avec relecture et aller-retour de chiffrement vérifiés avant écriture ;
fichier supprimé, listes de chargement des playbooks nettoyées, validé par un
appel API réel ne chargeant que all/vault.yml.
La garde qui manquait
voute.py verifier ne comparait que le gabarit — il disait « complet » pendant
qu'un secret vivait ailleurs. Il contrôle maintenant aussi la voûte réelle quand
ANSIBLE_VAULT_PASSWORD_FILE la rend déchiffrable : noms de clés seulement,
jamais de valeur, et vérification sautée sans mot de passe.
Elle a trouvé un second trou dès son premier passage : la voûte réelle de
Technolibre n'a ni vault_nextcloud_admin ni vault_nextcloud_oidc, que le plan
exige. Non corrigé — générer ces secrets est une décision, et celui d'OIDC doit
correspondre à ce que Keycloak connaîtra.
27 preuves OK. --syntax-check et ansible-lint (production) sur les playbooks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:52:44 -04:00
|
|
|
"# Nom derive du seed, comme tout le reste. Distinct des pools "
|
|
|
|
|
"`Env.Tenant` anterieurs a Set-OPS, qu'on ne touche pas.",
|
pools Proxmox : un par tenant, dérivé de l'index (D-37, P28)
Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez
Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le
reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101
contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est
indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les
certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}.
Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y
sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le
codage.
Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par
P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11.
make devis-proxmox-pools rattrape la flotte existante (création du pool, puis
affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes :
make creer-vm dérive le pool par la même fonction et le passe à la création. Le
playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et
l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le
rattrapage passe par les membres.
P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID
— une machine appartenant à deux tenants serait pire qu'une homonymie.
Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai
gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart.
27 preuves OK, 0 échec. --syntax-check du playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
|
|
|
"",
|
|
|
|
|
"### 1. Creer le pool (sans effet s'il existe)",
|
|
|
|
|
f"pvesh create /pools --poolid {b['pool']} \\",
|
|
|
|
|
f" --comment 'Tenant {b['tenant']} (index {b['index']}) — genere par Set-OPS'",
|
|
|
|
|
"",
|
|
|
|
|
"### 2. Y placer les VM",
|
|
|
|
|
"# Un membre deja present est ignore par Proxmox.",
|
|
|
|
|
]
|
|
|
|
|
if actifs:
|
|
|
|
|
out.append(f"pvesh set /pools/{b['pool']} --vms "
|
|
|
|
|
+ ",".join(str(m['vmid']) for m in actifs))
|
|
|
|
|
else:
|
|
|
|
|
out.append("# (aucune VM active : rien a placer pour l'instant)")
|
|
|
|
|
out += ["", "# VMID serveur etat"]
|
|
|
|
|
for m in b["membres"]:
|
|
|
|
|
marque = "" if m["etat"] == "actif" else " (pas encore creee)"
|
|
|
|
|
out.append(f"# {m['vmid']:<11} {m['nom']:<22} {m['etat']}{marque}")
|
|
|
|
|
if b["sans_vmid"]:
|
|
|
|
|
out += ["",
|
|
|
|
|
"# /!\\ Sans VMID derivable (fonction absente de la nomenclature) :",
|
|
|
|
|
"# " + ", ".join(b["sans_vmid"]),
|
|
|
|
|
"# Ces serveurs ne peuvent pas etre places tant que leur fonction",
|
|
|
|
|
"# n'est pas declaree — les placer a la main recreerait un ecart."]
|
|
|
|
|
out.append("")
|
|
|
|
|
out += [
|
|
|
|
|
"## Ensuite",
|
|
|
|
|
"# Les VM CREEES PAR LA SUITE entrent d'elles-memes dans le pool : le playbook",
|
|
|
|
|
"# de clonage le derive et le passe a la creation. Ce devis ne sert donc qu'a",
|
|
|
|
|
"# rattraper la flotte deja en place — une fois.",
|
|
|
|
|
"#",
|
|
|
|
|
"# Note : l'API Proxmox ne permet pas de CHANGER le pool d'une VM existante par",
|
|
|
|
|
"# le meme appel que la creation. Deplacer une VM d'un pool a l'autre se fait",
|
|
|
|
|
"# par le membre, pas par la VM — ce qui compte le jour d'une migration.",
|
|
|
|
|
]
|
|
|
|
|
return "\n".join(out)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(argv: list[str]) -> int:
|
|
|
|
|
ap = argparse.ArgumentParser(description=__doc__)
|
|
|
|
|
ap.add_argument("--json", action="store_true")
|
|
|
|
|
ap.add_argument("--verifier", action="store_true")
|
|
|
|
|
ap.add_argument("--pool-actif", action="store_true",
|
|
|
|
|
help="nom du pool du tenant actif (consomme par make creer-vm)")
|
pools : le genome ne nait plus chez un locataire
`Chezlepro-17` contenait VINGT ET UNE VM : les quatorze du locataire ET les sept du
genome. `OPS-Chezlepro` et `OPS-Technolibre`, crees a la main, etaient vides.
La cause : `site-creer` appelle `cloner-vm`, qui derive son pool par `--pool-actif`,
c est-a-dire le pool du TENANT lie. Les machines du site heritaient du locataire
courant. Range a la main, ca se serait defait au prochain `site-creer` — sans un mot,
parce que la VM est bien creee, bien nommee, bien adressee. Seule son appartenance
est fausse, et rien ne la regarde.
- `--pool-site` rend le nom invariable du pool du genome. Option DISTINCTE, pas un
drapeau sur la premiere : une fonction qui repond aux deux questions finit par se
tromper d appelant.
- `cloner-vm` accepte une surcharge POOL= ; `site-creer` la nomme. Substitution au
niveau MAKE, pas shell : POOL arrive du sur-make comme variable make, et $${POOL}
ne l aurait jamais vue.
- P71 exige que `site-creer` nomme son pool. Eprouvee dans les deux sens : passe sur
le Makefile sain, tire des qu on retire l argument.
Applique au cluster : Site-OPS 9 VM (7 du site + 2 gabarits), OPS-Chezlepro 14,
OPS-Patient0 5. Chezlepro-17, Patient0-29 et Set-OPS supprimes une fois vides. Les
pools anterieurs a Set-OPS et les quinze VM hors pool n ont pas ete touches.
make prouver : 70 OK, 0 echec, 1 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-13 04:12:27 -04:00
|
|
|
ap.add_argument("--pool-site", action="store_true",
|
|
|
|
|
help="nom invariable du pool du genome (consomme par make site-creer)")
|
pools Proxmox : un par tenant, dérivé de l'index (D-37, P28)
Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez
Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le
reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101
contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est
indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les
certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}.
Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y
sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le
codage.
Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par
P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11.
make devis-proxmox-pools rattrape la flotte existante (création du pool, puis
affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes :
make creer-vm dérive le pool par la même fonction et le passe à la création. Le
playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et
l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le
rattrapage passe par les membres.
P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID
— une machine appartenant à deux tenants serait pire qu'une homonymie.
Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai
gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart.
27 preuves OK, 0 échec. --syntax-check du playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
|
|
|
args = ap.parse_args(argv)
|
|
|
|
|
|
|
|
|
|
if args.pool_actif:
|
|
|
|
|
print(pool_actif())
|
|
|
|
|
return 0
|
|
|
|
|
|
pools : le genome ne nait plus chez un locataire
`Chezlepro-17` contenait VINGT ET UNE VM : les quatorze du locataire ET les sept du
genome. `OPS-Chezlepro` et `OPS-Technolibre`, crees a la main, etaient vides.
La cause : `site-creer` appelle `cloner-vm`, qui derive son pool par `--pool-actif`,
c est-a-dire le pool du TENANT lie. Les machines du site heritaient du locataire
courant. Range a la main, ca se serait defait au prochain `site-creer` — sans un mot,
parce que la VM est bien creee, bien nommee, bien adressee. Seule son appartenance
est fausse, et rien ne la regarde.
- `--pool-site` rend le nom invariable du pool du genome. Option DISTINCTE, pas un
drapeau sur la premiere : une fonction qui repond aux deux questions finit par se
tromper d appelant.
- `cloner-vm` accepte une surcharge POOL= ; `site-creer` la nomme. Substitution au
niveau MAKE, pas shell : POOL arrive du sur-make comme variable make, et $${POOL}
ne l aurait jamais vue.
- P71 exige que `site-creer` nomme son pool. Eprouvee dans les deux sens : passe sur
le Makefile sain, tire des qu on retire l argument.
Applique au cluster : Site-OPS 9 VM (7 du site + 2 gabarits), OPS-Chezlepro 14,
OPS-Patient0 5. Chezlepro-17, Patient0-29 et Set-OPS supprimes une fois vides. Les
pools anterieurs a Set-OPS et les quinze VM hors pool n ont pas ete touches.
make prouver : 70 OK, 0 echec, 1 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-13 04:12:27 -04:00
|
|
|
# LE SITE NE DERIVE D'AUCUN INDEX, DONC SON POOL NE SE CALCULE PAS (2026-09-13).
|
|
|
|
|
#
|
|
|
|
|
# `--pool-actif` rend le pool du TENANT lie. `make site-creer` passait par le meme
|
|
|
|
|
# chemin : les sept machines du genome sont donc nees dans `OPS-Chezlepro`, aux cotes
|
|
|
|
|
# des quatorze du locataire — vingt et une VM dans un pool cense n'en contenir que
|
|
|
|
|
# quatorze. Range a la main, ca se serait defait au prochain `site-creer`.
|
|
|
|
|
#
|
|
|
|
|
# Une option distincte, et non un drapeau sur la premiere : le site et un tenant ne
|
|
|
|
|
# repondent pas a la meme question, et une fonction qui repond aux deux finit par
|
|
|
|
|
# rendre la mauvaise reponse a l'un des deux.
|
|
|
|
|
if args.pool_site:
|
|
|
|
|
print(POOL_SITE)
|
|
|
|
|
return 0
|
|
|
|
|
|
2026-09-28 09:57:06 -04:00
|
|
|
# Les tenants de CE SITE (voir `devis_proxmox_fw.main`, 2026-09-28).
|
|
|
|
|
devis = construire(decouvrir_du_site())
|
pools nommes comme les depots, et les cles sortent du poste
LE NOM D UN POOL EST CELUI DE SON DEPOT. Chezlepro-17 devient OPS-Chezlepro :
le seed se lisait dans le nom, ce qui obligeait a connaitre le codage — et
surtout le nom CHANGEAIT si l index changeait, ce que la renumerotation du
site a montre le jour meme.
Site-OPS ne derive de rien, et c est le point : les machines du genome ne
dependent d aucun index, elles sont l infrastructure SUR laquelle les index
vivent. Sans ce bloc elles restaient hors de tout pool.
P69 — l amorcage d un tenant designe-t-il le site REEL ? dns_amorcage et
artefacts_amorcage sont ecrits a la main, volontairement : au moment ou ils
servent la machine ne resout aucun nom. Mais ils designent des machines DU
SITE, et n ont pas suivi son renumerotage. La reconstruction du locataire
s est arretee sur Failed to update apt cache, a quinze couches de sa cause.
La preuve ne juge que les valeurs qui PRETENDENT designer le site : viser
9.9.9.9 est un choix, pas un oubli.
LES CLES SORTENT DU POSTE, EN CLAIR, ET C EST RAISONNE. Support perdu : LUKS
s en charge. Poste compromis : la seconde couche n aide pas, les originaux
sont dans ~/.config sur ce meme poste. Elle coutait une phrase de passe
stockee nulle part — le seul point que la procedure ne couvre pas. Option
--support-chiffre explicite ; le defaut reste GPG, parce qu un support non
chiffre est le cas le plus frequent.
Et ma note qui disait les cles sorties depuis le 5 septembre etait fausse :
le support ne portait que le depot hors site du 1er.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-12 21:37:28 -04:00
|
|
|
# LE SITE EST UN POOL AUSSI (2026-09-12). Les machines du genome — cache, forge, AC,
|
|
|
|
|
# noms, depot, supervision — ne derivent d'aucun index : elles sont l'infrastructure
|
|
|
|
|
# SUR laquelle les index vivent. D'ou un nom invariable, `Site-OPS`, identique d'un
|
|
|
|
|
# hebergeur a l'autre.
|
|
|
|
|
#
|
|
|
|
|
# Sans ce bloc, elles restaient hors de tout pool : dans la console, sept machines
|
|
|
|
|
# sans appartenance a cote de trois flottes rangees — exactement le desordre que le
|
|
|
|
|
# reste du devis supprime.
|
|
|
|
|
try:
|
|
|
|
|
import underlay as _u
|
|
|
|
|
_plan = (_u.lire_plan_site("serveurs.yml") or {}).get("serveurs") or {}
|
|
|
|
|
_membres = sorted(
|
|
|
|
|
({"nom": n, "vmid": int(v["vmid"]), "etat": str(v.get("etat", "actif"))}
|
|
|
|
|
for n, v in _plan.items() if str(v.get("vmid", "")).strip().isdigit()),
|
|
|
|
|
key=lambda m: m["vmid"])
|
|
|
|
|
if _membres:
|
|
|
|
|
devis["pools"].insert(0, {
|
|
|
|
|
"pool": POOL_SITE, "tenant": "(site)", "index": None,
|
|
|
|
|
"membres": _membres, "sans_vmid": [],
|
|
|
|
|
})
|
|
|
|
|
except Exception:
|
|
|
|
|
pass # aucun underlay monte : le site n'existe pas de ce point de vue
|
|
|
|
|
|
pools Proxmox : un par tenant, dérivé de l'index (D-37, P28)
Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez
Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le
reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101
contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est
indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les
certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}.
Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y
sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le
codage.
Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par
P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11.
make devis-proxmox-pools rattrape la flotte existante (création du pool, puis
affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes :
make creer-vm dérive le pool par la même fonction et le passe à la création. Le
playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et
l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le
rattrapage passe par les membres.
P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID
— une machine appartenant à deux tenants serait pire qu'une homonymie.
Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai
gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart.
27 preuves OK, 0 échec. --syntax-check du playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
|
|
|
if args.verifier:
|
|
|
|
|
blocs = devis["pools"]
|
|
|
|
|
if not blocs:
|
|
|
|
|
print("erreur: aucun tenant federe decouvert.", file=sys.stderr)
|
|
|
|
|
return 2
|
|
|
|
|
noms = [b["pool"] for b in blocs]
|
|
|
|
|
if len(set(noms)) != len(noms):
|
|
|
|
|
doublons = sorted({n for n in noms if noms.count(n) > 1})
|
|
|
|
|
print(f"erreur: pool(s) en collision entre tenants : {', '.join(doublons)}",
|
|
|
|
|
file=sys.stderr)
|
|
|
|
|
return 2
|
|
|
|
|
# Un VMID dans deux pools serait pire qu'une homonymie : la machine
|
|
|
|
|
# appartiendrait a deux tenants. L'index les separe, mais on le prouve.
|
|
|
|
|
vus: dict[int, str] = {}
|
|
|
|
|
for b in blocs:
|
|
|
|
|
for m in b["membres"]:
|
|
|
|
|
if m["vmid"] in vus:
|
|
|
|
|
print(f"erreur: VMID {m['vmid']} revendique par {vus[m['vmid']]} "
|
|
|
|
|
f"et {b['pool']}.", file=sys.stderr)
|
|
|
|
|
return 2
|
|
|
|
|
vus[m["vmid"]] = b["pool"]
|
|
|
|
|
total = sum(len(b["membres"]) for b in blocs)
|
|
|
|
|
print(f"CONFORME : {len(blocs)} pool(s) Proxmox, {total} VM placee(s), "
|
|
|
|
|
f"aucun nom ni VMID en collision.")
|
|
|
|
|
return 0
|
|
|
|
|
print(json.dumps(devis, indent=2, ensure_ascii=False) if args.json else rendre(devis))
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
raise SystemExit(main(sys.argv[1:]))
|