Sur le chemin TENANT=, raser annoncait un ecosysteme monte (rien n'est monte sur le runner du site) et le devis de placement un tenant « ? ». Les deux en-tetes disent desormais ce qui est vise et d'ou vient la liste. Le pre-vol lui-meme est consigne : 13 VM, aucun conflit, placement conforme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
87 lines
4 KiB
Python
87 lines
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")
|
|
|
|
# L'EN-TETE DIT D'OU VIENT LA LISTE (2026-10-07). Sur le runner du site, `--locataire`
|
|
# annoncait « Ecosysteme monte » — rien n'y est monte, le locataire est NOMME et sa face fait
|
|
# foi. Lu au pre-vol de M4, avant de raser : un devis qui se trompe de provenance fait douter
|
|
# de la liste. Le cluster rend ici exactement les machines publiees : rien n'est refuse, et
|
|
# sans `--confirmer` rien n'est fait — seul l'en-tete est juge, sur la sortie reelle.
|
|
import contextlib # noqa: E402
|
|
import io # noqa: E402
|
|
|
|
try:
|
|
_, COUPLES_FACE = raser.couples_publies(BASE.name)
|
|
except (SystemExit, Exception) as e: # noqa: BLE001 — une instance sans face publiee
|
|
COUPLES_FACE = []
|
|
print(f">>> SAUTE l'en-tete du chemin nomme : {e}")
|
|
if COUPLES_FACE:
|
|
class ClusterConforme(FauxCluster):
|
|
def __call__(self, chemin, methode="GET", corps=None):
|
|
return [{"type": "qemu", "vmid": v, "name": n, "node": "asgard"}
|
|
for n, v in COUPLES_FACE]
|
|
|
|
raser.Cluster = ClusterConforme
|
|
for args, attendu, banni in ((["--locataire", BASE.name], "Locataire nommé", "monté"),
|
|
([], "Écosystème monté", "nommé")):
|
|
sortie = io.StringIO()
|
|
with contextlib.redirect_stdout(sortie):
|
|
code = raser.main(args)
|
|
entete = sortie.getvalue().splitlines()[0]
|
|
assert code == 0, f"code {code} sur un cluster conforme ({' '.join(args) or 'instance'})"
|
|
assert attendu in entete and banni not in entete, f"EN-TETE TROMPEUR : {entete!r}"
|
|
print(f">>> {' '.join(args) or 'instance montee'} : {entete}")
|