[UPD] LongTest : trois étages par défaut, la mesure le dit

Le défaut promettait dix étages qu'aucune machine ne tient. Une descente
complète par ligne, sur 28 cœurs :

    étage 1 :   0 s d'amorçage,    200 s d'installation, 280 s en tout
    étage 2 :  37 s,               344 s,                495 s
    étage 3 :  93 s,               777 s,              1 064 s
    étage 4 : 15 608 s,         26 306 s,      n'a pas abouti

Trois étages coûtent une demi-heure. Le quatrième a coûté 4 h 20 d'amorçage et
7 h 18 d'installation sur la même machine : tout y est 15 à 30 fois plus lent,
pas une seule étape. C'est aussi là que les fabricants s'arrêtent — le
quatrième étage est le troisième hyperviseur imbriqué, AMD en documente deux.

La profondeur reste le seul paramètre, et le README dit désormais ce qu'on
achète en la montant.

--- EN ---

The default promised ten levels no machine can hold. One full descent per row,
on 28 cores:

    level 1:      0 s boot,    200 s install,   280 s total
    level 2:     37 s,         344 s,           495 s
    level 3:     93 s,         777 s,         1 064 s
    level 4: 15 608 s,      26 306 s,      did not finish

Three levels cost half an hour. The fourth cost 4 h 20 of boot and 7 h 18 of
install on the same machine: everything there is 15 to 30 times slower, not one
step. It is also where the vendors stop — level 4 is the third nested
hypervisor, and AMD documents two.

Depth remains the only parameter, and the README now says what raising it buys.

Assisted-by: claude-opus-5
(cherry picked from commit 226d6ffa3c664ba9e1a6dfdf48d52027b52eda23)
This commit is contained in:
Mathieu Benoit 2026-08-28 00:21:51 -04:00
parent 30399b4559
commit 8e5f5b9097
7 changed files with 187 additions and 16 deletions

View file

@ -33,11 +33,36 @@ Which is the point of the script: a number obtained once, on one machine, in
one chain, is an anecdote. one chain, is an anecdote.
``` ```
./LongTest/deep_proxmox.py --depth 10 --dry-run # the plan, nothing created ./LongTest/deep_proxmox.py # three levels, ~30 minutes
./LongTest/deep_proxmox.py --depth 10 # hours ./LongTest/deep_proxmox.py --dry-run # the plan, nothing created
./LongTest/deep_proxmox.py --depth 5 # ask for more, knowingly
./LongTest/deep_proxmox.py --detruire # undo it ./LongTest/deep_proxmox.py --detruire # undo it
``` ```
### How deep is worth asking for
The depth is the only setting, and **three** is the default because three
works. Measured on a 28-core machine, one full descent per row:
| level | boot (ssh) | install | total |
|------:|-----------:|--------:|------:|
| 1 | 0 s | 200 s | 280 s |
| 2 | 37 s | 344 s | 495 s |
| 3 | 93 s | 777 s | 1 064 s |
| 4 | **15 608 s** | **26 306 s** | did not finish |
Three levels cost half an hour. The **fourth** cost 4 h 20 of boot and 7 h 18
of install on the same machine — everything there is 15 to 30 times slower, not
just one step. And it lands exactly where the hardware vendors stop: level 4 is
the *third* nested hypervisor, and AMD documents two.
A wider guest makes it worse, sharply: at level 4, one extra vCPU multiplied
the boot by 9.4 (1 664 s at two vCPU, 15 608 s at three), and at eight vCPU the
guest read 32 MiB in 106 minutes with a static instruction pointer. At levels 2
and 3 that same vCPU costs nothing.
So: three by default, five if you want to know, ten only to watch the wall.
The descent is **uniform**. Every level, the first included, goes through the 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 same six steps: create, wait for ssh, install Proxmox, reboot and check the
kernel, bring pmxcfs back up, check the storage. Only creation differs — kernel, bring pmxcfs back up, check the storage. Only creation differs —
@ -141,11 +166,37 @@ D'où le script : un chiffre obtenu une fois, sur une machine, dans une chaîne,
est une anecdote. est une anecdote.
``` ```
./LongTest/deep_proxmox.py --depth 10 --dry-run # le plan, rien de créé ./LongTest/deep_proxmox.py # trois étages, ~30 minutes
./LongTest/deep_proxmox.py --depth 10 # des heures ./LongTest/deep_proxmox.py --dry-run # le plan, rien de créé
./LongTest/deep_proxmox.py --depth 5 # en demander plus, sciemment
./LongTest/deep_proxmox.py --detruire # défaire ./LongTest/deep_proxmox.py --detruire # défaire
``` ```
### Quelle profondeur vaut la peine d'être demandée
La profondeur est le seul réglage, et **trois** est le défaut parce que trois
marche. Mesuré sur une machine à 28 cœurs, une descente complète par ligne :
| étage | amorçage (ssh) | installation | total |
|------:|---------------:|-------------:|------:|
| 1 | 0 s | 200 s | 280 s |
| 2 | 37 s | 344 s | 495 s |
| 3 | 93 s | 777 s | 1 064 s |
| 4 | **15 608 s** | **26 306 s** | n'a pas abouti |
Trois étages coûtent une demi-heure. Le **quatrième** a coûté 4 h 20
d'amorçage et 7 h 18 d'installation sur la même machine — tout y est 15 à 30
fois plus lent, pas une seule étape. Et cela tombe précisément là où les
fabricants s'arrêtent : le quatrième étage est le *troisième* hyperviseur
imbriqué, et AMD en documente deux.
Un invité plus large aggrave brutalement : au quatrième étage, un vCPU de plus
a multiplié l'amorçage par 9,4 (1 664 s à deux vCPU, 15 608 s à trois), et à
huit vCPU l'invité a lu 32 Mio en 106 minutes, pointeur d'instruction
immobile. Aux étages 2 et 3, ce même vCPU ne coûte rien.
Donc : trois par défaut, cinq pour savoir, dix seulement pour voir le mur.
La descente est **uniforme**. Chaque étage, le premier compris, passe par les 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 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 vérifier le noyau, remettre pmxcfs debout, contrôler le stockage. Seule la

View file

@ -32,11 +32,37 @@ D'où le script : un chiffre obtenu une fois, sur une machine, dans une chaîne,
est une anecdote. est une anecdote.
``` ```
./LongTest/deep_proxmox.py --depth 10 --dry-run # le plan, rien de créé ./LongTest/deep_proxmox.py # trois étages, ~30 minutes
./LongTest/deep_proxmox.py --depth 10 # des heures ./LongTest/deep_proxmox.py --dry-run # le plan, rien de créé
./LongTest/deep_proxmox.py --depth 5 # en demander plus, sciemment
./LongTest/deep_proxmox.py --detruire # défaire ./LongTest/deep_proxmox.py --detruire # défaire
``` ```
### Quelle profondeur vaut la peine d'être demandée
La profondeur est le seul réglage, et **trois** est le défaut parce que trois
marche. Mesuré sur une machine à 28 cœurs, une descente complète par ligne :
| étage | amorçage (ssh) | installation | total |
|------:|---------------:|-------------:|------:|
| 1 | 0 s | 200 s | 280 s |
| 2 | 37 s | 344 s | 495 s |
| 3 | 93 s | 777 s | 1 064 s |
| 4 | **15 608 s** | **26 306 s** | n'a pas abouti |
Trois étages coûtent une demi-heure. Le **quatrième** a coûté 4 h 20
d'amorçage et 7 h 18 d'installation sur la même machine — tout y est 15 à 30
fois plus lent, pas une seule étape. Et cela tombe précisément là où les
fabricants s'arrêtent : le quatrième étage est le *troisième* hyperviseur
imbriqué, et AMD en documente deux.
Un invité plus large aggrave brutalement : au quatrième étage, un vCPU de plus
a multiplié l'amorçage par 9,4 (1 664 s à deux vCPU, 15 608 s à trois), et à
huit vCPU l'invité a lu 32 Mio en 106 minutes, pointeur d'instruction
immobile. Aux étages 2 et 3, ce même vCPU ne coûte rien.
Donc : trois par défaut, cinq pour savoir, dix seulement pour voir le mur.
La descente est **uniforme**. Chaque étage, le premier compris, passe par les 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 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 vérifier le noyau, remettre pmxcfs debout, contrôler le stockage. Seule la

View file

@ -28,11 +28,36 @@ Which is the point of the script: a number obtained once, on one machine, in
one chain, is an anecdote. one chain, is an anecdote.
``` ```
./LongTest/deep_proxmox.py --depth 10 --dry-run # the plan, nothing created ./LongTest/deep_proxmox.py # three levels, ~30 minutes
./LongTest/deep_proxmox.py --depth 10 # hours ./LongTest/deep_proxmox.py --dry-run # the plan, nothing created
./LongTest/deep_proxmox.py --depth 5 # ask for more, knowingly
./LongTest/deep_proxmox.py --detruire # undo it ./LongTest/deep_proxmox.py --detruire # undo it
``` ```
### How deep is worth asking for
The depth is the only setting, and **three** is the default because three
works. Measured on a 28-core machine, one full descent per row:
| level | boot (ssh) | install | total |
|------:|-----------:|--------:|------:|
| 1 | 0 s | 200 s | 280 s |
| 2 | 37 s | 344 s | 495 s |
| 3 | 93 s | 777 s | 1 064 s |
| 4 | **15 608 s** | **26 306 s** | did not finish |
Three levels cost half an hour. The **fourth** cost 4 h 20 of boot and 7 h 18
of install on the same machine — everything there is 15 to 30 times slower, not
just one step. And it lands exactly where the hardware vendors stop: level 4 is
the *third* nested hypervisor, and AMD documents two.
A wider guest makes it worse, sharply: at level 4, one extra vCPU multiplied
the boot by 9.4 (1 664 s at two vCPU, 15 608 s at three), and at eight vCPU the
guest read 32 MiB in 106 minutes with a static instruction pointer. At levels 2
and 3 that same vCPU costs nothing.
So: three by default, five if you want to know, ten only to watch the wall.
The descent is **uniform**. Every level, the first included, goes through the 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 same six steps: create, wait for ssh, install Proxmox, reboot and check the
kernel, bring pmxcfs back up, check the storage. Only creation differs — kernel, bring pmxcfs back up, check the storage. Only creation differs —

View file

@ -1174,7 +1174,13 @@ def principal(argv=None):
parseur = argparse.ArgumentParser( parseur = argparse.ArgumentParser(
description="Jusqu'à quel étage un Proxmox dans un Proxmox tient-il ?" description="Jusqu'à quel étage un Proxmox dans un Proxmox tient-il ?"
) )
parseur.add_argument("--depth", type=int, default=10) # Trois par défaut, et c'est une MESURE, pas une prudence : les trois
# premiers étages coûtent 280, 495 et 1 064 secondes — une demi-heure en
# tout. Le quatrième en a coûté 7 h 18 d'installation et 4 h 20 d'amorçage
# sur la même machine. Un défaut à dix promettait ce qu'aucune machine ne
# peut tenir ; la profondeur reste un paramètre, et c'est à qui la demande
# de savoir ce qu'il demande.
parseur.add_argument("--depth", type=int, default=3)
parseur.add_argument("--dry-run", action="store_true") parseur.add_argument("--dry-run", action="store_true")
parseur.add_argument("--detruire", action="store_true") parseur.add_argument("--detruire", action="store_true")
args = parseur.parse_args(argv) args = parseur.parse_args(argv)

View file

@ -87,6 +87,12 @@ class LongTestMenuMixin:
print(t("Command not found !")) print(t("Command not found !"))
def _longtest_depth(self): def _longtest_depth(self):
"""Profondeur demandée. Dix par défaut : c'est ce qu'on veut mesurer.""" """Profondeur demandée. Trois par défaut, parce que trois marche.
brut = input(f"{t('Depth (default 10): ')}").strip()
return int(brut) if brut.isdigit() and int(brut) > 0 else 10 Mesuré sur cette machine : les trois premiers étages prennent 280, 495
et 1 064 secondes — une demi-heure. Le quatrième a demandé 7 h 18
d'installation et 4 h 20 d'amorçage, et les suivants se comptent en
jours. Dix par défaut promettait ce qu'aucune machine ne tient.
"""
brut = input(f"{t('Depth (default 3): ')}").strip()
return int(brut) if brut.isdigit() and int(brut) > 0 else 3

View file

@ -3454,9 +3454,9 @@ TRANSLATIONS = {
"fr": "Script introuvable :", "fr": "Script introuvable :",
"en": "Script not found:", "en": "Script not found:",
}, },
"Depth (default 10): ": { "Depth (default 3): ": {
"fr": "Profondeur (défaut 10) : ", "fr": "Profondeur (défaut 3) : ",
"en": "Depth (default 10): ", "en": "Depth (default 3): ",
}, },
"Nesting level": { "Nesting level": {
"fr": "Étage d'imbrication", "fr": "Étage d'imbrication",

View file

@ -208,15 +208,72 @@ class TestLEssaiABlanc(unittest.TestCase):
(int(n), int(v), int(r), int(d)) for n, v, r, d in plan (int(n), int(v), int(r), int(d)) for n, v, r, d in plan
) )
for parent, enfant in zip(etages, etages[1:]): for parent, enfant in zip(etages, etages[1:]):
for i, quoi in ((1, "vCPU"), (2, "RAM"), (3, "disque")): # Mémoire et disque : STRICTEMENT décroissants, chaque parent
# portant son enfant en plus de lui-même.
for i, quoi in ((2, "RAM"), (3, "disque")):
self.assertGreater( self.assertGreater(
parent[i], enfant[i], f"étage {parent[0]} : {quoi}" parent[i], enfant[i], f"étage {parent[0]} : {quoi}"
) )
# Le processeur : jamais plus étroit, mais pas toujours plus
# large. Deux étages imbriqués peu profonds ont la même largeur —
# au quatrième étage, un vCPU de plus multiplie l'amorçage par
# 9,4, alors qu'aux étages 2 et 3 il ne coûte rien.
self.assertGreaterEqual(
parent[1], enfant[1], f"étage {parent[0]} : vCPU"
)
# Et le plus profond reçoit ce qu'un Proxmox de test demande, pas ce # Et le plus profond reçoit ce qu'un Proxmox de test demande, pas ce
# qui reste. # qui reste.
self.assertEqual(etages[-1][1], 2, "vCPU du plus profond") self.assertEqual(etages[-1][1], 2, "vCPU du plus profond")
class TestLaProfondeurParDefaut(unittest.TestCase):
"""Trois, et c'est une MESURE, pas une prudence.
Sur la machine où ce test a été écrit, les trois premiers étages coûtent
280, 495 et 1 064 secondes — une demi-heure en tout. Le quatrième a demandé
7 h 18 d'installation et 4 h 20 d'amorçage, et les suivants se comptent en
jours. Un défaut à dix promettait ce qu'aucune machine ne peut tenir : la
profondeur reste un paramètre, mais le défaut doit marcher."""
def test_the_script_defaults_to_three(self):
import inspect
import sys as _sys
_sys.path.insert(0, os.path.join(RACINE, "LongTest"))
import deep_proxmox
src = inspect.getsource(deep_proxmox.principal)
self.assertIn('"--depth", type=int, default=3', src)
def test_the_menu_defaults_to_three(self):
import inspect
from script.todo.todo import TODO
src = inspect.getsource(TODO._longtest_depth)
self.assertIn("else 3", src)
# Et l'invite le DIT : un défaut caché se subit, il ne se choisit pas.
self.assertIn("Depth (default 3): ", src)
def test_the_prompt_is_translated(self):
from script.todo.todo_i18n import TRANSLATIONS
entree = TRANSLATIONS.get("Depth (default 3): ")
self.assertIsNotNone(entree, "invite non traduite")
self.assertIn("3", entree["fr"])
def test_the_default_depth_fits_a_modest_machine(self):
"""Le défaut doit tenir là où le test sera lancé, pas seulement sur la
machine de celui qui l'a écrit."""
from script.proxmox import nesting
plan = nesting.nesting_plan(
3, cpu_hote=8, ram_dispo_mo=24000, disque_libre_go=120
)
self.assertEqual(plan["atteignable"], 3)
self.assertEqual(plan["arret"], "")
class TestDefaireSansEffacerAutreChose(unittest.TestCase): class TestDefaireSansEffacerAutreChose(unittest.TestCase):
"""« --detruire » effaçait par SOUS-CHAÎNE de nom, dans le mauvais ordre, """« --detruire » effaçait par SOUS-CHAÎNE de nom, dans le mauvais ordre,
sans confirmation et sans honorer --dry-run. sans confirmation et sans honorer --dry-run.