From 5a408f10612dafa24437ff9a1342a85864250143 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Tue, 28 Jul 2026 09:39:59 +0000 Subject: [PATCH] =?UTF-8?q?[IMP]=20script=20todo:=20infra=20=E2=80=94=20mu?= =?UTF-8?q?ltiplicateur=20de=20ressources=20par=20VM=20(x1..x4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avant le déploiement infra, on demande un multiplicateur de ressources : x1 (minimum catalogue) .. x4. Il multiplie la RAM (base = minimum de la version) et les vCPU (base 2), en bornant les vCPU au nombre de cœurs de l'hôte et en signalant si la RAM totale dépasse la RAM libre. - Le prompt affiche les ressources de l'hôte (vCPU, RAM libre) et, pour chaque multiplicateur, le total RAM + vCPU/VM avec un ⚠ si ça dépasse. - Le plan reflète les ressources effectives (vCPU + RAM×mult par VM). - Les jobs passent --memory et --vcpus effectifs à deploy_qemu (avant, l'infra laissait le minimum catalogue + 2 vCPU par défaut). Co-Authored-By: Claude Opus 4.8 (1M context) --- script/todo/todo.py | 69 ++++++++++++++++++++++++++++++++++++---- script/todo/todo_i18n.py | 28 ++++++++++++++++ 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/script/todo/todo.py b/script/todo/todo.py index 20e1f0a..b3f6f50 100755 --- a/script/todo/todo.py +++ b/script/todo/todo.py @@ -2068,6 +2068,45 @@ class TODO: print(f" {t('Will execute:')} {cmd}") self.execute.exec_command_live(cmd, source_erplibre=False) + # vCPU de base (x1) par VM. Le multiplicateur monte de là. + _QEMU_BASE_VCPUS = 2 + + def _qemu_prompt_resource_mult(self, selected, host_cpu, free_ram): + """Multiplicateur de ressources par VM : x1 (minimum catalogue) .. x4. + Multiplie la RAM (base = minimum de la version) et les vCPU (base + _QEMU_BASE_VCPUS), en bornant les vCPU au nombre de cœurs de l'hôte et + en signalant si la RAM totale dépasse la RAM libre. Renvoie (mult, + base_vcpus).""" + base_ram = sum(s[2] for s in selected) # RAM min totale (x1) + base_vcpus = self._QEMU_BASE_VCPUS + print(f"\n{t('Resources per VM (x1 = catalog minimum):')}") + cpu_txt = f"{host_cpu} vCPU" + ram_txt = ( + f"~{free_ram} Mo {t('free')}" + if free_ram + else t("free RAM unknown") + ) + print(f" {t('Host:')} {cpu_txt}, {ram_txt}") + for n in (1, 2, 3, 4): + vcpus = min(base_vcpus * n, host_cpu) + total = base_ram * n + star = " *" if n == 1 else "" + warn = "" + if free_ram and total > free_ram: + warn = f" ⚠ {t('> host free RAM')}" + print( + f" [{n}] x{n}{star} {vcpus} vCPU/VM, " + f"{t('total RAM')} ~{total} Mo{warn}" + ) + sel = input(f"{t('Choice (1-4, default 1):')} ").strip() + try: + n = int(sel) + if 1 <= n <= 4: + return n, base_vcpus + except ValueError: + pass + return 1, base_vcpus + def _qemu_deploy_infra(self): print(f"🏗 {t('Deploy ERPLibre infra: one minimal VM per image')}") try: @@ -2208,16 +2247,28 @@ class TODO: print(t("Nothing selected.")) return - # 3) Plan + estimation des ressources. - total_ram = sum(s[2] for s in selected) - total_disk = sum(self._parse_disk_gb(s[3]) for s in selected) + # 2b) Multiplicateur de ressources par VM (x1 = minimum .. x4) : monte + # la RAM et les vCPU selon ce que l'hôte peut fournir. + host_cpu = os.cpu_count() or 2 free_ram = self._host_free_ram_mb() - print(f"\n{t('Deployment plan')} ({len(selected)} VM) :") + res_mult, base_vcpus = self._qemu_prompt_resource_mult( + selected, host_cpu, free_ram + ) + + def eff_res(ram): + """(RAM effective, vCPU) pour une VM selon le multiplicateur.""" + return ram * res_mult, min(base_vcpus * res_mult, host_cpu) + + # 3) Plan + estimation des ressources (avec le multiplicateur appliqué). + total_ram = sum(s[2] * res_mult for s in selected) + total_disk = sum(self._parse_disk_gb(s[3]) for s in selected) + print(f"\n{t('Deployment plan')} ({len(selected)} VM, x{res_mult}) :") for d, v, ram, disk, a in selected: name = self._qemu_infra_name(d, v, a) + eram, evcpus = eff_res(ram) print( f" - {name:<30} {d} {v:<7} [{a:<5}] " - f"RAM {ram}Mo {t('disk')} {disk}" + f"{evcpus} vCPU RAM {eram}Mo {t('disk')} {disk}" ) print(f"\n {t('Total RAM (all running):')} {total_ram} Mo") print(f" {t('Total virtual disk (thin qcow2):')} ~{total_disk} G") @@ -2275,12 +2326,13 @@ class TODO: script_path = self._qemu_script_path() deployed = [] jobs = [] # (name, parts) des VM à créer - for d, v, _ram, disk, a in selected: + for d, v, ram, disk, a in selected: name = self._qemu_infra_name(d, v, a) if self._qemu_domain_exists(name): print(f"⏭ {name}: {t('already exists, skipped.')}") deployed.append(name) continue + eram, evcpus = eff_res(ram) parts = [ "sudo", script_path, @@ -2290,6 +2342,11 @@ class TODO: v, "--name", name, + # Ressources selon le multiplicateur choisi (x1 = minimum). + "--memory", + str(eram), + "--vcpus", + str(evcpus), # Mot de passe console « erplibre » (+ clé SSH). --no-wait-ip : # ne bloque pas 90s par VM, l'IP est collectée après coup. "--password", diff --git a/script/todo/todo_i18n.py b/script/todo/todo_i18n.py index 06cd6fe..c3b9d85 100644 --- a/script/todo/todo_i18n.py +++ b/script/todo/todo_i18n.py @@ -1205,6 +1205,34 @@ TRANSLATIONS = { "fr": "installation", "en": "installation", }, + "Resources per VM (x1 = catalog minimum):": { + "fr": "Ressources par VM (x1 = minimum catalogue) :", + "en": "Resources per VM (x1 = catalog minimum):", + }, + "Host:": { + "fr": "Hôte :", + "en": "Host:", + }, + "free": { + "fr": "libres", + "en": "free", + }, + "free RAM unknown": { + "fr": "RAM libre inconnue", + "en": "free RAM unknown", + }, + "> host free RAM": { + "fr": "> RAM libre de l'hôte", + "en": "> host free RAM", + }, + "total RAM": { + "fr": "RAM totale", + "en": "total RAM", + }, + "Choice (1-4, default 1):": { + "fr": "Choix (1-4, défaut 1) :", + "en": "Choice (1-4, default 1):", + }, "(includes emulated architectures — some VMs are slow)": { "fr": "(inclut des architectures émulées — certaines VM sont lentes)", "en": "(includes emulated architectures — some VMs are slow)",