Set-OPS-Public/scripts/tests/test_raser_resultat.py

71 lines
2.8 KiB
Python
Raw Normal View History

raser : lire le RESULTAT de la destruction, pas son accuse de reception Le defaut note hier est corrige. `raser` concluait au succes sur la reponse immediate de l'API : le DELETE rend un UPID et la main tout de suite, la destruction se fait en tache de fond, et elle peut echouer APRES. Le 2026-08-10, six VM ont ete rapportees « detruites » alors qu'elles etaient toujours la — la tache sortait sur « VM is locked (clone) », verrou laisse par des clonages interrompus. Confondre « demande acceptee » et « travail fait » est le pire mensonge possible pour la SEULE commande destructive du moteur : on croit la place libre, on relance la construction, et rien ne se cree sans qu'on comprenne pourquoi. _attendre_tache() relit l'UPID, interroge l'etat jusqu'a `stopped` et rend l'exitstatus reel. Chaque VM est annoncee detruite OU en echec, avec la cause telle que le cluster l'a donnee ; le compte final ne ment plus. test_raser_resultat.py fabrique la situation exacte : un faux cluster qui accepte tout puis rend une tache terminee en erreur. EPROUVE DANS LES DEUX SENS — avec l'ancien comportement retabli temporairement il echoue en designant le defaut, avec le correctif il passe. Raccorde a `make test`, donc rejoue par P02. Meme motif que le clonage corrige une heure plus tot, dans l'autre sens : une operation asynchrone dont on ne verifie pas l'issue. Les deux venaient du passage a des appels d'API directs, ou plus rien n'attend a notre place. Verifie : make test vert, prouver.py 35 OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 22:47:28 -04:00
"""`raser` : une destruction qui ECHOUE ne doit pas etre annoncee comme reussie.
Le 2026-08-10, `raser` a rapporte « 6/6 VM detruites » alors que les six etaient toujours
la. L'API Proxmox accepte le `DELETE`, rend un UPID et la main IMMEDIATEMENT ; la
destruction se fait en tache de fond, et elle avait echoue APRES sur
« VM is locked (clone) » un verrou laisse par des clonages interrompus.
`raser` ne lisait que l'accuse de reception. Confondre « demande acceptee » et « travail
fait » est le pire mensonge possible pour la SEULE commande destructive du moteur : on
croit la place libre, on relance la construction, et rien ne se cree sans qu'on comprenne
pourquoi.
Ce test fabrique exactement cette situation : le faux cluster accepte tout, puis rend une
tache terminee en erreur. `raser` doit sortir en 1 et n'annoncer aucune destruction.
"""
import io
import pathlib
import sys
from contextlib import redirect_stdout
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.")
NOM_1, VMID_1 = COUPLES[0]
MOTIF = "VM is locked (clone)"
class ClusterQuiEchoueApresCoup:
"""Accepte tout, puis rend une tache TERMINEE EN ERREUR — le cas reel du 2026-08-10."""
@classmethod
def depuis_hebergeur(cls):
return cls(), None
def __call__(self, chemin, methode="GET", corps=None):
if chemin.startswith("/cluster/resources"):
# Le plan et le cluster s'accordent : le verrou de nom ne doit pas se
# declencher, sinon on n'atteindrait jamais la destruction.
return [{"type": "qemu", "vmid": VMID_1, "name": NOM_1, "node": "asgard"}]
if chemin.endswith("/status/current"):
return {"status": "stopped"}
if chemin.endswith("/status/stop"):
return "UPID:asgard:0:0:0:qmstop:0:test:"
if "/tasks/" in chemin and chemin.endswith("/status"):
return {"status": "stopped", "exitstatus": MOTIF}
if methode == "DELETE":
return "UPID:asgard:0:0:0:qmdestroy:0:test:"
return {}
def rate(self, rep):
return None
raser.Cluster = ClusterQuiEchoueApresCoup
sortie = io.StringIO()
with redirect_stdout(sortie):
code = raser.main(["--instance", NOM, "--confirmer"])
texte = sortie.getvalue()
print(f"\n>>> code de sortie : {code} (1 = echec attendu)")
assert code == 1, "raser a rendu 0 alors que la destruction a ECHOUE"
assert "ECHEC" in texte, "raser n'a pas signale l'echec"
assert MOTIF in texte, "raser n'a pas rapporte la cause rendue par le cluster"
assert "1/1 VM detruites" not in texte, "raser annonce une destruction qui n'a pas eu lieu"
print(">>> l'echec est vu, nomme, et compte : aucune destruction fictive annoncee")