# LongTest — tests that create real machines These are not unit tests. They create virtual machines, install systems on them, and take hours. They live here and **not** in `test/`, which the unit runner sweeps: `./script/test/run_unit_test.sh` must stay runnable in seconds on any machine, including one without virtualisation. Run them from the menu — `TODO › Execute › Test › Long tests` — or directly. ## deep_proxmox.py — how deep does Proxmox-in-Proxmox go? The practicable nesting depth cannot be deduced, only measured — and one measurement is not a measurement. A manual look at one fourth-level VM found a guest **36 times slower than real time** (583 seconds of wall clock for 16 seconds of guest time, each ACPI line taking a second) and then a frozen kernel: identical RIP across three samples two minutes apart, and **not one byte written** to disk. Running this script **refuted the conclusion drawn from it**. Its own fourth-level VM — 2 vCPU where the manual one had 12 — booted, installed, and wrote gigabytes. What looked like a nesting ceiling was a *parallelism* ceiling under nesting. That is exactly what the algorithm caps, and this is how it stopped being a guess. Which is the point of the script: a number obtained once, on one machine, in one chain, is an anecdote. ``` ./LongTest/deep_proxmox.py --depth 10 --dry-run # the plan, nothing created ./LongTest/deep_proxmox.py --depth 10 # hours ./LongTest/deep_proxmox.py --detruire # undo it ``` The descent is **uniform**. Every level, the first included, goes through the same six steps: create, wait for ssh, install Proxmox, reboot and check the kernel, bring pmxcfs back up, check the storage. Only creation differs — libvirt locally, `qm` afterwards. It sends **our** `install_proxmox.sh` over scp instead of letting the VM clone the repository: it is our code we want to exercise, and the remote is often behind the checkout — a fix absent from the remote made the same defect "come back" on three VMs in a row. ### The resource algorithm — sized from the bottom up The first version handed down whatever the parent could spare, and a real descent showed what that costs. Level 4 ended up with 44 GB of memory and 2 vCPU **on a host that had 2** — a hundred percent overcommit, at every level, with the hypervisor itself to serve on top. Its install ran past two and a half hours against thirteen minutes for level 3, and extrapolating that ratio gave five years for the tenth. So the direction is reversed for **memory and disk**. The deepest level gets what a test Proxmox actually asks for — 4 GB of memory, 25 GB of disk — and every parent above it adds its own overhead and nothing else: 2 GiB and 10 GB. A ten-level descent therefore asks its first level for 22 GB and 115 GB, where handing resources down wanted 50 GB of memory for the same depth. The processor follows a different rule; see below. Three budgets can bound the depth, and `script/proxmox/nesting.py` names the one that ran out: * **memory** — every level must run its own daemons (`pve-cluster`, `pvestatd`, `pvedaemon`, `pveproxy`) *and* hold its child; * **disk** — the child's disk lives *inside* the parent's, which must also hold its own system; * **processor** — it does *not* grow with depth. Every nested level keeps a fixed, narrow width; only the first level counts against the physical cores. Either the machine can carry that first level or it can carry nothing. That third rule is measured, and it cost two descents to get right. A nested guest at the **fourth** level freezes in early boot as soon as it is wide: twelve vCPU the first time, eight the second — same instruction pointer at three readings five minutes apart, 32 MiB read and not one byte more for 106 minutes. Two vCPU boots. The first freeze was blamed on **overcommit**: that VM had twelve vCPU on a host with two. The second measurement refuted it — eight vCPU on a parent with **nine**, load 1.47, no overcommit at all, and the same freeze. It is the nested guest's vCPU count, not its ratio to its host's. At the third level, 9 vCPU boots in 117 s. The threshold sits between the third and fourth level, so no nested level is ever made wide. An earlier version of this algorithm gave each parent one vCPU more than its child, which made level 4 eight wide — exactly the frozen case. The rule made wide what must stay narrow. Hence three fixed widths: `VCPU_METAL` for level 1 (on bare metal, no freeze risk — eleven vCPU booted there in 42 s), `VCPU_IMBRIQUE` for the deepest, and `VCPU_INTERMEDIAIRE` in between, wide enough to host its child without being as narrow as it. That middle number is a **hypothesis**: two is proven to boot at the fourth level and eight is proven to freeze, with nothing measured in between. The descent decides. Memory is not the lever. On that same manual VM, dropping it from 9 GB to 2 GB moved nothing — it stopped after reading the same 32 MiB, which is simply the size of the boot files. The plan is printed **before** anything is created, and the script never promises a depth it knows will not fit — better to announce six levels and reach six than to promise ten and die at the seventh without knowing why. # LongTest — des tests qui créent de vraies machines Ce ne sont pas des tests unitaires. Ils créent des machines virtuelles, y installent des systèmes, et durent des heures. Ils vivent ici et **non** dans `test/`, que le lanceur unitaire balaie : `./script/test/run_unit_test.sh` doit rester lançable en quelques secondes, sur n'importe quelle machine, y compris sans virtualisation. Ils se lancent depuis le menu — `TODO › Execute › Test › Tests longs` — ou directement. ## deep_proxmox.py — jusqu'à quel étage un Proxmox dans un Proxmox tient-il ? La profondeur d'imbrication praticable ne se déduit pas, elle se mesure — et une mesure n'est pas une mesure. Un examen à la main d'UNE VM du quatrième étage a trouvé un invité **36 fois plus lent que le temps réel** (583 secondes d'horloge pour 16 secondes de temps invité, chaque ligne d'ACPI prenant une seconde), puis un noyau gelé : même RIP à trois relevés deux minutes d'écart, et **pas un octet écrit** sur le disque. Lancer ce script a **réfuté la conclusion qu'on en avait tirée**. Sa propre VM du quatrième étage — 2 vCPU là où celle de la main en avait 12 — a démarré, s'est installée, et a écrit des gigaoctets. Ce qui ressemblait à un plafond d'imbrication était un plafond de *parallélisme* sous imbrication. C'est précisément ce que l'algorithme borne, et c'est ainsi qu'il a cessé d'être une supposition. D'où le script : un chiffre obtenu une fois, sur une machine, dans une chaîne, est une anecdote. ``` ./LongTest/deep_proxmox.py --depth 10 --dry-run # le plan, rien de créé ./LongTest/deep_proxmox.py --depth 10 # des heures ./LongTest/deep_proxmox.py --detruire # défaire ``` La descente est **uniforme**. Chaque étage, le premier compris, passe par les mêmes six étapes : créer, attendre le ssh, installer Proxmox, redémarrer et vérifier le noyau, remettre pmxcfs debout, contrôler le stockage. Seule la création diffère — libvirt en local, `qm` ensuite. Il envoie **notre** `install_proxmox.sh` par scp au lieu de laisser la VM cloner le dépôt : c'est notre code qu'on veut éprouver, et le dépôt distant est souvent en retard sur le checkout — un correctif absent du distant a fait « revenir » le même défaut sur trois VM de suite. ### L'algorithme de ressources — dimensionné depuis le bas La première version cédait à l'enfant ce que le parent pouvait céder, et une descente réelle a montré ce que cela coûte. L'étage 4 se retrouvait avec 44 Go de mémoire et 2 vCPU **sur un hôte qui en avait 2** — cent pour cent de surengagement, à chaque étage, avec l'hyperviseur lui-même à servir par-dessus. Son installation dépassait deux heures et demie contre treize minutes pour l'étage 3, et l'extrapolation de ce rapport donnait cinq ANS pour le dixième. Le sens est donc inversé pour la **mémoire et le disque**. Le plus profond reçoit ce qu'un Proxmox de test demande vraiment — 4 Go de mémoire, 25 Go de disque — et chaque parent au-dessus ajoute son propre surcoût, rien d'autre : 2 Gio et 10 Go. Une descente à dix étages demande ainsi 22 Go et 115 Go à son premier étage, là où la cession de haut en bas voulait 50 Go de mémoire pour la même profondeur. Le processeur, lui, suit une autre règle — voir plus bas. Trois budgets peuvent borner la profondeur, et `script/proxmox/nesting.py` nomme celui qui a manqué : * **la mémoire** — chaque étage doit faire tourner ses propres démons (`pve-cluster`, `pvestatd`, `pvedaemon`, `pveproxy`) *et* héberger son enfant ; * **le disque** — le disque de l'enfant vit *dans* celui du parent, qui doit aussi contenir son propre système ; * **le processeur** — il ne croît *pas* avec la profondeur. Tout étage imbriqué garde une largeur fixe et étroite ; seul le premier compte sur les cœurs physiques. Ou la machine peut porter ce premier étage, ou elle ne peut rien. Cette troisième règle est mesurée, et il a fallu deux descentes pour la poser juste. Un invité imbriqué au **quatrième** étage gèle en tout début de démarrage dès qu'il est large : douze vCPU la première fois, huit la seconde — même pointeur d'instruction à trois relevés espacés de cinq minutes, 32 Mio lus et plus un octet pendant 106 minutes. Deux vCPU démarrent. Le premier gel avait été imputé au **surengagement** : cette VM à douze vCPU tournait sur un hôte qui en avait deux. La seconde mesure l'a réfuté — huit vCPU sur un parent qui en avait **neuf**, charge 1,47, aucun surengagement, et le même gel. C'est le nombre de vCPU de l'invité imbriqué, et non son rapport à celui de son hôte. Au troisième étage, 9 vCPU démarrent en 117 s. Le seuil est entre le troisième et le quatrième étage : aucun étage imbriqué n'est donc rendu large. Une version précédente de cet algorithme donnait un vCPU de plus à chaque parent, ce qui rendait l'étage 4 large de huit — exactement le cas gelé. La règle rendait large ce qui doit rester étroit. D'où trois largeurs fixes : `VCPU_METAL` pour l'étage 1 (sur le métal, aucun risque de gel — onze vCPU y ont démarré en 42 s), `VCPU_IMBRIQUE` pour le plus profond, et `VCPU_INTERMEDIAIRE` entre les deux, juste assez large pour héberger son enfant sans être aussi étroit que lui. Ce nombre du milieu est une **hypothèse** : deux démarre au quatrième étage, huit gèle, et rien n'est mesuré entre les deux. C'est la descente qui tranche. La mémoire n'est pas le levier. Sur cette même VM examinée à la main, la faire passer de 9 Go à 2 Go n'a rien déplacé : elle s'arrêtait après avoir lu les mêmes 32 Mio, c'est-à-dire simplement la taille des fichiers d'amorçage. Le plan est affiché **avant** que quoi que ce soit ne soit créé, et le script ne promet jamais une profondeur qu'il sait irréalisable — mieux vaut annoncer six étages et en réussir six que d'en promettre dix et mourir au septième sans savoir pourquoi.