diff --git a/LongTest/README.base.md b/LongTest/README.base.md index d280936..2e9c8ef 100644 --- a/LongTest/README.base.md +++ b/LongTest/README.base.md @@ -57,12 +57,12 @@ 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. The deepest level gets what a test Proxmox -actually asks for — 4 GB of memory, 25 GB of disk, 2 vCPU — and every parent -above it adds its own overhead and nothing else: one vCPU, 2 GiB, 10 GB. A -ten-level descent therefore asks its first level for 11 vCPU, 22 GB and -115 GB, where handing resources down wanted 50 GB of memory for the same -depth. +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: @@ -71,15 +71,33 @@ one that ran out: `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** — each level wants one vCPU more than its child, so ten levels - ask eleven of the first. Half the physical cores is the ceiling: the - orchestrator runs on that machine too. +* **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 budget is measured, not assumed. Twelve vCPU at the fourth level -froze the guest kernel in early boot — same instruction pointer at three -readings two minutes apart — while two progressed. The number was not the -culprit: that VM had twelve vCPU on a host with two, six times wider than its -own machine. Overcommit freezes, not the twelve. +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 @@ -147,12 +165,12 @@ surengagement, à chaque étage, avec l'hyperviseur lui-même à servir par-dess 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é. Le plus profond reçoit ce qu'un Proxmox de test -demande vraiment — 4 Go de mémoire, 25 Go de disque, 2 vCPU — et chaque parent -au-dessus ajoute son propre surcoût, rien d'autre : un vCPU, 2 Gio, 10 Go. Une -descente à dix étages demande ainsi 11 vCPU, 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 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é : @@ -162,16 +180,35 @@ nomme celui qui a manqué : enfant ; * **le disque** — le disque de l'enfant vit *dans* celui du parent, qui doit aussi contenir son propre système ; -* **le processeur** — chaque étage en veut un de plus que son enfant, donc dix - étages en demandent onze au premier. La moitié des cœurs physiques est le - plafond : l'orchestrateur tourne sur cette machine lui aussi. +* **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. -Ce troisième budget est mesuré, pas supposé. Douze vCPU au quatrième étage ont -gelé le noyau invité en tout début de démarrage — même pointeur d'instruction à -trois relevés, deux minutes d'écart — quand deux avançaient. Le nombre n'était -pas le fautif : cette VM avait douze vCPU sur un hôte qui en avait deux, six -fois plus large que sa propre machine. C'est le surengagement qui gèle, pas le -douze. +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 diff --git a/LongTest/README.fr.md b/LongTest/README.fr.md index 9c1b051..67e05c2 100644 --- a/LongTest/README.fr.md +++ b/LongTest/README.fr.md @@ -56,12 +56,12 @@ surengagement, à chaque étage, avec l'hyperviseur lui-même à servir par-dess 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é. Le plus profond reçoit ce qu'un Proxmox de test -demande vraiment — 4 Go de mémoire, 25 Go de disque, 2 vCPU — et chaque parent -au-dessus ajoute son propre surcoût, rien d'autre : un vCPU, 2 Gio, 10 Go. Une -descente à dix étages demande ainsi 11 vCPU, 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 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é : @@ -71,16 +71,35 @@ nomme celui qui a manqué : enfant ; * **le disque** — le disque de l'enfant vit *dans* celui du parent, qui doit aussi contenir son propre système ; -* **le processeur** — chaque étage en veut un de plus que son enfant, donc dix - étages en demandent onze au premier. La moitié des cœurs physiques est le - plafond : l'orchestrateur tourne sur cette machine lui aussi. +* **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. -Ce troisième budget est mesuré, pas supposé. Douze vCPU au quatrième étage ont -gelé le noyau invité en tout début de démarrage — même pointeur d'instruction à -trois relevés, deux minutes d'écart — quand deux avançaient. Le nombre n'était -pas le fautif : cette VM avait douze vCPU sur un hôte qui en avait deux, six -fois plus large que sa propre machine. C'est le surengagement qui gèle, pas le -douze. +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 diff --git a/LongTest/README.md b/LongTest/README.md index ef5fb45..41cc307 100644 --- a/LongTest/README.md +++ b/LongTest/README.md @@ -52,12 +52,12 @@ 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. The deepest level gets what a test Proxmox -actually asks for — 4 GB of memory, 25 GB of disk, 2 vCPU — and every parent -above it adds its own overhead and nothing else: one vCPU, 2 GiB, 10 GB. A -ten-level descent therefore asks its first level for 11 vCPU, 22 GB and -115 GB, where handing resources down wanted 50 GB of memory for the same -depth. +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: @@ -66,15 +66,33 @@ one that ran out: `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** — each level wants one vCPU more than its child, so ten levels - ask eleven of the first. Half the physical cores is the ceiling: the - orchestrator runs on that machine too. +* **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 budget is measured, not assumed. Twelve vCPU at the fourth level -froze the guest kernel in early boot — same instruction pointer at three -readings two minutes apart — while two progressed. The number was not the -culprit: that VM had twelve vCPU on a host with two, six times wider than its -own machine. Overcommit freezes, not the twelve. +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 diff --git a/script/proxmox/nesting.py b/script/proxmox/nesting.py index fa3714f..f73296c 100644 --- a/script/proxmox/nesting.py +++ b/script/proxmox/nesting.py @@ -22,13 +22,19 @@ documente l'imbrication qu'à DEUX niveaux. Deux nombres viennent de la même mesure, et méritent d'être dits : -* 12 vCPU au quatrième étage ont GELÉ le noyau invité en tout début de - démarrage — même RIP à trois relevés, deux minutes d'écart, pas un octet lu - de plus. Les mêmes 2 vCPU avançaient. Le nombre n'était pas le fautif : cette - VM avait douze vCPU sur un hôte qui en avait DEUX, six fois plus large que sa - propre machine. C'est le surengagement qui gèle, pas le douze — d'où le - dimensionnement par étage plus bas, qui donne à chaque parent un vCPU de plus - qu'à son enfant ; +* au QUATRIÈME étage, un invité large GÈLE en tout début de démarrage. Mesuré + deux fois, à douze vCPU puis à huit : même RIP à trois relevés espacés de + cinq minutes, 32 Mio lus et plus un octet — 106 minutes durant, pour le + second. Les mêmes 2 vCPU démarrent. + + On avait d'abord imputé le premier gel 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 bien 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 donc entre le + troisième et le quatrième étage ; tout étage imbriqué reste étroit ; * cette VM-là s'arrêtait après avoir lu 33 682 432 octets — 32 Mio, soit simplement la taille de ses fichiers d'amorçage — et le chiffre ne bougeait pas quand on lui retirait de la mémoire. La mémoire n'était donc pas le @@ -74,22 +80,40 @@ PVE_DISQUE_CIBLE_GO = 25 # En dessous, un Proxmox ne démarre pas ses démons ou n'a plus la place # d'importer une image cloud. +# Une profondeur qu'aucun budget ne borne. Grand, mais fini : « inf » se +# propagerait dans min() et rendrait un float là où tout le reste compte des +# étages entiers. +PLAFOND_LIBRE = 10**6 RAM_MIN_MO = 2048 DISQUE_MIN_GO = 15 -# Le processeur se dimensionne DEPUIS LE BAS lui aussi, et pour la même -# raison que la mémoire — mais celle-là s'est vue à l'usage. +# Le processeur NE se dimensionne PAS depuis le bas, contrairement à la +# mémoire et au disque. Trois nombres fixes, et la mesure les impose. # -# Avec deux vCPU à chaque étage imbriqué, l'étage 3 avait deux vCPU pour -# héberger un invité qui en demandait deux : cent pour cent de surengagement, -# et l'hyperviseur lui-même à servir par-dessus. À chaque étage. Mesuré sur une -# descente réelle : une seconde VM démarrée au quatrième étage a lu DEUX -# KILO-OCTETS en onze minutes, affamée par l'installation qui tournait à côté. +# Une première version donnait à chaque parent un vCPU de plus qu'à son enfant, +# pour supprimer le surengagement : le plus profond deux, son parent trois, et +# ainsi de suite jusqu'à onze au premier. Elle rendait donc LARGES les étages +# du milieu — huit au quatrième, sept au cinquième. Or c'est exactement là que +# l'invité gèle : mesuré, l'étage 4 à huit vCPU n'a pas passé son amorçage en +# 106 minutes, sur un parent à neuf vCPU parfaitement sain. La règle rendait +# large ce qui doit rester étroit. # -# Chaque étage reçoit donc UN vCPU de plus que son enfant : le plus profond en -# a deux, son parent trois, et ainsi de suite. Le premier étage d'une descente -# à dix en demande onze — sur vingt-huit cœurs réels, cela passe. +# Le plus profond : deux, le seul chiffre dont on ait la preuve qu'il démarre +# au quatrième étage. VCPU_IMBRIQUE = 2 +# Les étages imbriqués intermédiaires : un de plus, pour héberger leur enfant +# sans être aussi étroits que lui. Deux hébergeant deux, c'est cent pour cent +# de surengagement — et l'installation de l'étage 4 dépassait alors 2 h 50 +# contre 793 s pour l'étage 3. +# +# Trois est une HYPOTHÈSE : on a la preuve que deux démarre au quatrième étage +# et que huit gèle, rien entre les deux. C'est la descente qui tranchera. +VCPU_INTERMEDIAIRE = 3 +# Le premier étage tourne sur le MÉTAL : aucun risque de gel, et son amorçage +# est rapide — onze vCPU y ont démarré en 42 s. Il n'a pourtant qu'un enfant à +# trois vCPU à servir ; quatre suffisent, et laissent la machine physique aux +# autres. +VCPU_METAL = 4 # Ce qu'on LAISSE à la machine physique. L'orchestrateur tourne dessus, son # ssh vers chaque étage aussi, et la suite de tests avec. # @@ -153,14 +177,14 @@ def nesting_plan( """Ce que le PREMIER étage doit avoir pour qu'une descente de `d` étages tienne : la cible du bas, plus un surcoût par étage au-dessus. - Le processeur en fait partie : chaque étage en veut un de plus que son - enfant, donc le premier en veut VCPU_IMBRIQUE + d - 1. Sans cette - condition, on annonçait dix étages sur une machine à quatre cœurs. + Le processeur n'en fait PAS partie de la même façon : il ne croît + pas avec la profondeur, puisque tout étage imbriqué reste étroit. Le + premier étage demande VCPU_METAL, quelle que soit la profondeur. """ return ( PVE_RAM_CIBLE_MO + (d - 1) * PVE_RAM_MO, PVE_DISQUE_CIBLE_GO + (d - 1) * PVE_DISQUE_GO, - VCPU_IMBRIQUE + d - 1, + VCPU_METAL if d > 1 else VCPU_IMBRIQUE, ) budget_vcpu = int(cpu_hote) - HOTE_RESERVE_VCPU @@ -171,7 +195,10 @@ def nesting_plan( plafonds = { "ram": (budget_ram - PVE_RAM_CIBLE_MO) // PVE_RAM_MO + 1, "disque": (budget_disque - PVE_DISQUE_CIBLE_GO) // PVE_DISQUE_GO + 1, - "vcpu": budget_vcpu - VCPU_IMBRIQUE + 1, + # Le processeur ne borne plus la profondeur : les étages imbriqués + # gardent une largeur fixe, seul le premier compte sur le métal. Il + # borne encore à ZÉRO une machine trop petite pour ce premier étage. + "vcpu": PLAFOND_LIBRE if budget_vcpu >= VCPU_METAL else 0, } plafonds = {nom: max(0, valeur) for nom, valeur in plafonds.items()} demandee = max(0, int(profondeur)) @@ -185,10 +212,18 @@ def nesting_plan( niveaux = [ { "niveau": niveau, - # UN de plus que son enfant. Un parent aussi étroit que son - # enfant, c'est cent pour cent de surengagement — et l'hyperviseur - # à servir en plus. - "vcpu": VCPU_IMBRIQUE + (atteignable - niveau), + # Trois largeurs, et aucune ne dépend de la profondeur : le + # métal en premier, le fond au plus étroit, les intermédiaires + # juste assez larges pour héberger leur enfant. + "vcpu": ( + VCPU_METAL + if niveau == 1 + else ( + VCPU_IMBRIQUE + if niveau == atteignable + else VCPU_INTERMEDIAIRE + ) + ), # Les planchers ne sont pas décoratifs : ils tiennent même si # quelqu'un baisse une CIBLE un jour. Sans eux, ils n'étaient plus # lus par personne et les tests qui les vérifiaient passaient diff --git a/test/test_proxmox_nesting.py b/test/test_proxmox_nesting.py index 8a20fd0..36db064 100644 --- a/test/test_proxmox_nesting.py +++ b/test/test_proxmox_nesting.py @@ -50,21 +50,64 @@ class TestLePlanDesEtages(unittest.TestCase): def test_each_parent_adds_exactly_its_own_overhead(self): # Ni plus ni moins : un parent plus large que nécessaire ralentit tout - # ce qu'il héberge, un parent trop juste ne le fait pas tourner. + # ce qu'il héberge, un parent trop juste ne le fait pas tourner. Vaut + # pour la mémoire et le disque — le processeur, lui, ne croît pas avec + # la profondeur, voir test_no_nested_level_is_ever_wide. niveaux = nesting.nesting_plan(8, **self.HOTE)["niveaux"] for parent, enfant in zip(niveaux, niveaux[1:]): self.assertEqual(parent["ram"] - enfant["ram"], nesting.PVE_RAM_MO) self.assertEqual( parent["disque"] - enfant["disque"], nesting.PVE_DISQUE_GO ) - self.assertEqual(parent["vcpu"] - enfant["vcpu"], 1) + + def test_no_nested_level_is_ever_wide(self): + """MESURÉ, deux fois : au quatrième étage un invité large GÈLE. + + Douze vCPU d'abord, sur un parent qui en avait deux : on avait imputé + le gel au surengagement. Puis huit vCPU sur un parent qui en avait + NEUF, charge 1,47, aucun surengagement — 32 Mio lus en 106 minutes, + même RIP à trois relevés espacés de cinq minutes. C'est le nombre de + vCPU de l'invité imbriqué, et rien d'autre. + + Une version de ce module donnait un vCPU de plus à chaque parent, ce + qui rendait l'étage 4 large de huit : exactement le cas gelé. Aucun + étage imbriqué ne doit dépasser VCPU_INTERMEDIAIRE, à AUCUNE + profondeur demandée.""" + for profondeur in range(1, 13): + niveaux = nesting.nesting_plan(profondeur, **self.HOTE)["niveaux"] + for n in niveaux[1:]: + self.assertLessEqual( + n["vcpu"], + nesting.VCPU_INTERMEDIAIRE, + f"profondeur {profondeur}, étage {n['niveau']}", + ) + + def test_the_three_widths_are_where_they_belong(self): + niveaux = nesting.nesting_plan(6, **self.HOTE)["niveaux"] + # Le métal : aucun risque de gel, onze vCPU y ont démarré en 42 s. + self.assertEqual(niveaux[0]["vcpu"], nesting.VCPU_METAL) + # Le fond : le seul chiffre dont on ait la preuve qu'il démarre au + # quatrième étage. + self.assertEqual(niveaux[-1]["vcpu"], nesting.VCPU_IMBRIQUE) + for n in niveaux[1:-1]: + self.assertEqual(n["vcpu"], nesting.VCPU_INTERMEDIAIRE) + + def test_a_single_level_descent_runs_on_metal(self): + niveaux = nesting.nesting_plan(1, **self.HOTE)["niveaux"] + self.assertEqual(len(niveaux), 1) + self.assertEqual(niveaux[0]["vcpu"], nesting.VCPU_METAL) def test_a_parent_is_never_narrower_than_its_child(self): """Deux vCPU hébergeant deux vCPU, c'est cent pour cent de surengagement — et l'hyperviseur à servir en plus. Mesuré : une VM démarrée au quatrième étage a lu DEUX KILO-OCTETS en onze minutes, - affamée par l'installation qui tournait à côté.""" - for coeurs in (4, 8, 12, 28): + affamée par l'installation qui tournait à côté. L'installation de + l'étage 4 dépassait alors 2 h 50 contre 793 s pour l'étage 3. + + « Jamais plus étroit », et non « toujours plus large » : deux étages + imbriqués voisins ont la même largeur, ce que le gel du quatrième + étage impose. C'est le PLUS PROFOND qui descend à deux.""" + for coeurs in (6, 8, 12, 28): with self.subTest(coeurs=coeurs): niveaux = nesting.nesting_plan( 6, @@ -73,22 +116,38 @@ class TestLePlanDesEtages(unittest.TestCase): disque_libre_go=400, )["niveaux"] for parent, enfant in zip(niveaux, niveaux[1:]): - self.assertGreater(parent["vcpu"], enfant["vcpu"]) + self.assertGreaterEqual(parent["vcpu"], enfant["vcpu"]) self.assertGreater(parent["ram"], enfant["ram"]) self.assertGreater(parent["disque"], enfant["disque"]) - def test_the_cpu_budget_can_bound_the_depth(self): - """Sur une petite machine, c'est le PROCESSEUR qui borne, pas la - mémoire : chaque étage en veut un de plus que son enfant, donc dix - étages demandent onze vCPU au premier.""" - plan = nesting.nesting_plan( + def test_the_cpu_only_ever_refuses_outright(self): + """Le processeur ne borne plus une profondeur INTERMÉDIAIRE : les + étages imbriqués gardent une largeur fixe, seul le premier compte sur + le métal. Ou la machine peut le porter, ou elle ne peut rien. + + Une version d'avant faisait croître la demande avec la profondeur — + onze vCPU pour dix étages — et bornait donc à trois étages sur huit + cœurs. Elle rendait aussi le quatrième étage large de huit, ce qui le + gelait : la borne cachait un défaut.""" + large = nesting.nesting_plan( 10, cpu_hote=8, ram_dispo_mo=64000, disque_libre_go=400 ) - self.assertEqual(plan["arret"], "vcpu") - self.assertLess(plan["atteignable"], 10) + self.assertEqual(large["atteignable"], 10) + self.assertEqual(large["arret"], "") + # Trop petite pour le premier étage : zéro, et le dire. + for coeurs in (1, 2, 4): + with self.subTest(coeurs=coeurs): + petite = nesting.nesting_plan( + 10, + cpu_hote=coeurs, + ram_dispo_mo=64000, + disque_libre_go=400, + ) + self.assertEqual(petite["atteignable"], 0) + self.assertEqual(petite["arret"], "vcpu") # Et le premier étage laisse à l'hôte ce qui lui est réservé. self.assertLessEqual( - plan["niveaux"][0]["vcpu"], 8 - nesting.HOTE_RESERVE_VCPU + large["niveaux"][0]["vcpu"], 8 - nesting.HOTE_RESERVE_VCPU ) def test_the_named_resource_is_the_one_that_really_binds(self):