Set-OPS-Public/scripts/tests/test_raser.py
Daniel Allaire a204ead8ab pre-vol M4 : raser et le devis de placement nomment le locataire designe
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>
2026-10-07 10:16:04 -04:00

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}")