Les deux reconstructions de la semaine rebatissaient Chezlepro sur son propre materiel : une preuve de reproductibilite, pas de portabilite. La vraie epreuve est un second tenant — Technolibre, index 11, plan distinct, voute separee, meme cluster. 1. LE CLONAGE RESOLVAIT PAR NOM. proxmox_kvm cherche une VM portant le `name` demande ; s'il en trouve une il rend `ok` et ne clone RIEN — aucune tache cote cluster. Or les noms courts sont VOLONTAIREMENT identiques d'un tenant a l'autre. `backup-01` de Technolibre tombait sur celui de Chezlepro. Mesure : id-ldap-01 et sup-01 (noms absents chez Chezlepro) passaient du premier coup, backup-01 echouait toujours. Le clonage cible desormais par VMID, via l'API. Deux defauts de ce correctif, trouves en le mesurant : le corps assemble en Jinja avec `>-` rendait une CHAINE et perdait le `pool` (VM nee hors de son pool, sans un mot) ; et l'application du gabarit de calcul expirait a 5 s, laissant la VM aux valeurs du gabarit — 2 coeurs/2 Go au lieu du plan, en silence. Corps en mapping YAML avec omit ; six tentatives espacees. Et `no_log` a masque la cause au moment ou elle servait. Les deux attentes disent maintenant ce qu'elles ont constate. 2. LE GUI DETRUISAIT DES INTRANTS. Dans ecrire_intrants, `identite` etait la seule branche sur quatre a ecrire par-dessus le disque au lieu de fusionner. Un enregistrement a supprime dns_amorcage et amorcage_acces_courriel. Le meme geste sur Chezlepro aurait mange les memes cles. 3. LE VERROU DE `raser` N'ETAIT PROUVE QUE POUR UN TENANT. Le faux cluster codait en dur les VMID de Chezlepro ; monte ailleurs, le test rendait 0 au lieu de 2. Il fabrique desormais la collision sur le plan courant. Verifie : prouver.py 34 OK sur Technolibre, test_raser vert sur les deux tenants, ansible-lint production sur le playbook de clonage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
57 lines
2.4 KiB
Python
57 lines
2.4 KiB
Python
"""`raser` : un VMID du plan portant un AUTRE nom doit faire REFUSER l'operation entiere.
|
|
|
|
C'est le verrou le plus important de la seule commande destructive du moteur, et le seul
|
|
qu'on ne peut pas eprouver sur le vrai cluster sans y fabriquer une collision. On isole
|
|
donc la logique derriere un faux cluster.
|
|
|
|
Le danger n'est pas theorique : le 2026-08-07, une VM heritee portait un VMID du plan sous
|
|
le nom `web-frontal-01`, et `proxmox_kvm` avait rapporte `ok` sans rien faire. Rase sans ce
|
|
controle, on detruisait la machine de quelqu'un d'autre.
|
|
|
|
LE FAUX CLUSTER SE DERIVE DE L'INSTANCE MONTEE, il ne code aucun VMID en dur. La premiere
|
|
version en portait deux, ceux de Chezlepro (index 17) : le test passait pour ce tenant et
|
|
rendait 0 — « aucune VM du plan n'est presente, rien a faire » — des qu'on montait un tenant
|
|
dont le plan derive d'autres VMID. Trouve le 2026-08-10 en basculant sur Technolibre, au
|
|
premier pas de l'epreuve de portabilite. Un verrou prouve pour un seul tenant n'est pas
|
|
prouve : c'est exactement ce que le multi-instance est cense empecher.
|
|
"""
|
|
import pathlib
|
|
import sys
|
|
|
|
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent))
|
|
import raser # noqa: E402
|
|
|
|
BASE, NOM = raser.instance_active()
|
|
COUPLES = raser.plan_derive(BASE)
|
|
if not COUPLES:
|
|
raise SystemExit("Plan sans serveur : rien a eprouver.")
|
|
|
|
# La collision est FABRIQUEE sur le premier hote du plan courant, quel qu'il soit ; un
|
|
# second hote, correctement nomme, garantit que le refus vient bien de la collision et
|
|
# non d'un cluster vide.
|
|
(NOM_1, VMID_1), *reste = COUPLES
|
|
NOM_2, VMID_2 = reste[0] if reste else (NOM_1 + "-bis", VMID_1 + 1)
|
|
|
|
|
|
class FauxCluster:
|
|
"""Le cluster repond que `VMID_1` porte le nom d'une VM etrangere au plan."""
|
|
|
|
@classmethod
|
|
def depuis_hebergeur(cls):
|
|
return cls(), None
|
|
|
|
def __call__(self, chemin, methode="GET", corps=None):
|
|
return [{"type": "qemu", "vmid": VMID_1, "name": "vm-heritee-de-2019",
|
|
"node": "asgard"},
|
|
{"type": "qemu", "vmid": VMID_2, "name": NOM_2, "node": "asgard"}]
|
|
|
|
def rate(self, rep):
|
|
return None
|
|
|
|
|
|
raser.Cluster = FauxCluster
|
|
code = raser.main(["--instance", NOM, "--confirmer"])
|
|
print(f"\n>>> instance montee : {NOM} | collision fabriquee sur {VMID_1} ({NOM_1})")
|
|
print(f">>> code de sortie : {code} (2 = refus attendu)")
|
|
assert code == 2, "LE VERROU N'A PAS TENU"
|
|
print(">>> le verrou tient : aucune VM n'aurait ete touchee")
|