From 5d30a3a60848c538b0be4c4ea80499b5fe3e0a7d Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Tue, 30 Jun 2026 10:07:04 -0400 Subject: [PATCH] =?UTF-8?q?Dimensionner=20les=20ressources=20VM=20depuis?= =?UTF-8?q?=20les=20logiciels=20h=C3=A9berg=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Les cœurs/RAM/disque d'une VM sont estimés depuis l'empreinte des rôles hébergés (roles//meta/empreinte.yml) sommée au socle SE, au lieu d'hériter des specs du golden template. Le générateur écrit proxmox_coeurs/memoire/disque_taille ; le clonage les passe à Proxmox (omit si absent → aucune régression). Override par hôte dans le plan. Co-Authored-By: Claude Opus 4.8 --- Makefile | 4 + docs/dimensionnement-ressources.md | 102 ++++++++++++++++++++ playbooks/proxmox/cloner_vm_debian.yml | 2 + roles/serveur_forgejo/meta/empreinte.yml | 8 ++ roles/serveur_grafana/meta/empreinte.yml | 8 ++ roles/serveur_icinga/meta/empreinte.yml | 8 ++ roles/serveur_keycloak/meta/empreinte.yml | 8 ++ roles/serveur_loki/meta/empreinte.yml | 8 ++ roles/serveur_nginx/meta/empreinte.yml | 8 ++ roles/serveur_openldap/meta/empreinte.yml | 8 ++ roles/serveur_postgresql/meta/empreinte.yml | 8 ++ roles/serveur_powerdns/meta/empreinte.yml | 8 ++ roles/serveur_prometheus/meta/empreinte.yml | 8 ++ roles/serveur_redis/meta/empreinte.yml | 8 ++ roles/serveur_sendmail/meta/empreinte.yml | 8 ++ roles/serveur_step_ca/meta/empreinte.yml | 8 ++ scripts/instancier.py | 8 ++ scripts/inventory_host.py | 2 + scripts/inventory_rules.py | 69 +++++++++++++ scripts/tests/test_inventory_host.py | 2 + 20 files changed, 293 insertions(+) create mode 100644 docs/dimensionnement-ressources.md create mode 100644 roles/serveur_forgejo/meta/empreinte.yml create mode 100644 roles/serveur_grafana/meta/empreinte.yml create mode 100644 roles/serveur_icinga/meta/empreinte.yml create mode 100644 roles/serveur_keycloak/meta/empreinte.yml create mode 100644 roles/serveur_loki/meta/empreinte.yml create mode 100644 roles/serveur_nginx/meta/empreinte.yml create mode 100644 roles/serveur_openldap/meta/empreinte.yml create mode 100644 roles/serveur_postgresql/meta/empreinte.yml create mode 100644 roles/serveur_powerdns/meta/empreinte.yml create mode 100644 roles/serveur_prometheus/meta/empreinte.yml create mode 100644 roles/serveur_redis/meta/empreinte.yml create mode 100644 roles/serveur_sendmail/meta/empreinte.yml create mode 100644 roles/serveur_step_ca/meta/empreinte.yml diff --git a/Makefile b/Makefile index b62d1b9..bc64aaf 100644 --- a/Makefile +++ b/Makefile @@ -325,6 +325,8 @@ cloner-vm: ansible-runtime [[ -n "$(FORMAT_DISQUE)" ]] && extra_vars+=( -e proxmox_clone_format="$(FORMAT_DISQUE)" ); \ [[ -n "$(CLONE_COMPLET)" ]] && extra_vars+=( -e proxmox_clone_complet="$(CLONE_COMPLET)" ); \ [[ -n "$(TAILLE_DISQUE)" ]] && extra_vars+=( -e proxmox_clone_taille_disque="$(TAILLE_DISQUE)" ); \ + [[ -n "$(COEURS)" ]] && extra_vars+=( -e proxmox_clone_coeurs="$(COEURS)" ); \ + [[ -n "$(MEMOIRE)" ]] && extra_vars+=( -e proxmox_clone_memoire="$(MEMOIRE)" ); \ [[ -n "$(DISQUE_PROXMOX)" ]] && extra_vars+=( -e proxmox_clone_disque="$(DISQUE_PROXMOX)" ); \ [[ -n "$(DNS)" ]] && extra_vars+=( -e proxmox_clone_dns="$(DNS)" ); \ [[ -n "$(CIUSER)" ]] && extra_vars+=( -e proxmox_clone_ciuser="$(CIUSER)" ); \ @@ -363,6 +365,8 @@ creer-vm: _instance-requise VLAN="$$SETOPS_VLAN" \ STOCKAGE_PROXMOX="$${SETOPS_STOCKAGE:-$(STOCKAGE_PROXMOX)}" \ TAILLE_DISQUE="$${SETOPS_DISQUE:-$(TAILLE_DISQUE)}" \ + COEURS="$${SETOPS_COEURS:-$(COEURS)}" \ + MEMOIRE="$${SETOPS_MEMOIRE:-$(MEMOIRE)}" \ NOEUD_PROXMOX="$${SETOPS_NOEUD:-$(NOEUD_PROXMOX)}" \ VMID_MODELE="$(VMID_MODELE)" \ FORMAT_DISQUE="$(FORMAT_DISQUE)" \ diff --git a/docs/dimensionnement-ressources.md b/docs/dimensionnement-ressources.md new file mode 100644 index 0000000..a3c7e42 --- /dev/null +++ b/docs/dimensionnement-ressources.md @@ -0,0 +1,102 @@ +# Dimensionnement dérivé des ressources VM + +Ce document décrit comment Set-OPS **estime les ressources d'une VM** (cœurs, RAM, +disque) à partir des **propriétés des logiciels** qu'elle héberge et du **socle SE**, +plutôt que de laisser chaque clone hériter aveuglément des specs du golden template. + +> Décision actée le 2026-06-25 : extension assumée de la *modélisation* du plan de +> contrôle (cf. `positionnement.md`). C'est le prolongement naturel de la dérivation +> réseau (`nomenclature.yml`) — du dimensionnement, pas une fonction de type NetBox. + +## Principe + +``` +ressources(VM) = socle_SE + Σ empreinte(logiciel hébergé) → marge → arrondi +``` + +Précédence (du plus fort au plus faible) : +1. **Valeur explicite par hôte** dans `serveurs.yml` (`coeurs` / `memoire` / `disque`) — gagne toujours. +2. **Valeur dérivée** (socle + empreintes + marge + arrondi) sinon. + +Comme Ansible/Proxmox sont idempotents, ajuster une empreinte puis régénérer ne fait +que recalculer la cible ; aucune dérive silencieuse. + +## Où vit chaque donnée + +| Donnée | Emplacement | Remarque | +| --- | --- | --- | +| Empreinte d'un logiciel | `roles//meta/empreinte.yml` | Propriété du logiciel, voyage avec le rôle. Fichier *pur données* (parsable sans Jinja). | +| Groupes sans rôle dédié | `EMPREINTES_SANS_ROLE` dans `scripts/inventory_rules.py` | p. ex. `serveur_web_frontal`, `serveur_web_dorsal`. | +| Repli ultime | `EMPREINTE_DEFAUT` | groupe inconnu : empreinte minimale. | +| Socle SE | `SOCLE_SE` dans `inventory_rules.py` | coût de base Debian durci. | +| Override par hôte | `serveurs.yml` (`coeurs`/`memoire`/`disque`) | déjà supporté par le générateur (`PLACEMENT`). | +| Politique (marges, paliers, plafonds) | constantes en tête de `inventory_rules.py` | | + +Format d'une empreinte (`roles//meta/empreinte.yml`) : +```yaml +setops_empreinte: { coeurs: 2, memoire_mo: 2048, disque_go: 20 } +``` + +## Règles d'agrégation (valeurs en vigueur) + +- **Socle SE** : 1 cœur / 512 Mo / 8 Go. +- **RAM** : `(socle + Σ rôles) × 1.20`, arrondie au palier **512 Mo**, **min 1024 Mo**. +- **Disque** : `(socle + Σ rôles) × 1.25`, arrondi au palier **5 Go**, **min 12 Go**, + émis sous la forme `"G"`. +- **Cœurs** : `max(socle, Σ rôles)`, entier, **min 1**, **plafond 8** (CPU *burstable* : + ni additif strict, ni marge). + +Seuls les **groupes de service** de l'hôte (issus de `applications.yml`) entrent dans +la somme. Le socle (`serveur_debian`/`serveur_durci`) et les groupes d'état sont exclus. + +## Chaîne technique + +1. `scripts/inventory_rules.py` — `deriver_ressources(groupes_service, racine_roles)` + lit les empreintes et calcule. +2. `scripts/instancier.py` — pour chaque hôte, écrit `proxmox_coeurs`, + `proxmox_memoire`, `proxmox_disque_taille` dans l'inventaire généré + (via `setdefault` : un override `serveurs.yml` prime). +3. `scripts/inventory_host.py` — `parametres-proxmox` émet `SETOPS_COEURS` / + `SETOPS_MEMOIRE` / `SETOPS_DISQUE` lus par `make creer-vm`. +4. `Makefile` (`creer-vm` → `cloner-vm`) relaie vers `proxmox_clone_coeurs` / + `proxmox_clone_memoire`. +5. `playbooks/proxmox/cloner_vm_debian.yml` — passe `cores`/`memory` à `proxmox_kvm` + (avec `omit` si absent : aucune régression, on garde alors les specs du template). + +## Empreintes actuelles + +| Rôle | cœurs | RAM (Mo) | disque (Go) | +| --- | --- | --- | --- | +| serveur_step_ca | 1 | 256 | 2 | +| serveur_powerdns | 1 | 512 | 2 | +| serveur_sendmail | 1 | 512 | 5 | +| serveur_nginx | 1 | 512 | 3 | +| serveur_openldap | 1 | 512 | 3 | +| serveur_keycloak | 2 | 1536 | 5 | +| serveur_postgresql | 2 | 2048 | 20 | +| serveur_redis | 1 | 512 | 2 | +| serveur_forgejo | 1 | 1024 | 20 | +| serveur_prometheus | 1 | 1024 | 20 | +| serveur_loki | 1 | 1024 | 20 | +| serveur_grafana | 1 | 512 | 2 | +| serveur_icinga | 2 | 1024 | 10 | +| serveur_web_frontal (repli) | 1 | 512 | 5 | +| serveur_web_dorsal (repli) | 1 | 1024 | 10 | + +## Ajuster + +- **Une empreinte de logiciel** : éditer `roles//meta/empreinte.yml`. +- **Un hôte précis** : poser `coeurs`/`memoire`/`disque` sur le serveur dans + `serveurs.yml` (l'override prime sur la dérivation). +- **La politique** (marges, paliers, socle, plafond) : constantes en tête de + `inventory_rules.py`. + +Puis : `make instancier` (voir le diff), `make instancier-appliquer` (FORCE=1 si +changement intentionnel). + +## Limite connue + +Les ressources s'appliquent **à la création de la VM** (clonage). Modifier une +empreinte n'**ajuste pas** automatiquement une VM déjà existante : il faut un +re-clonage ou un redimensionnement Proxmox manuel. Le **plan**, lui, reste la source +de vérité et se régénère à volonté. diff --git a/playbooks/proxmox/cloner_vm_debian.yml b/playbooks/proxmox/cloner_vm_debian.yml index a3133b8..b3e50fc 100644 --- a/playbooks/proxmox/cloner_vm_debian.yml +++ b/playbooks/proxmox/cloner_vm_debian.yml @@ -121,6 +121,8 @@ vmid: "{{ proxmox_clone_vmid_modele | int }}" newid: "{{ proxmox_clone_vmid | int }}" name: "{{ proxmox_clone_nom }}" + cores: "{{ proxmox_clone_coeurs | int if proxmox_clone_coeurs is defined and proxmox_clone_coeurs | string | length > 0 else omit }}" + memory: "{{ proxmox_clone_memoire | int if proxmox_clone_memoire is defined and proxmox_clone_memoire | string | length > 0 else omit }}" storage: "{{ proxmox_clone_stockage | default(omit, true) }}" format: "{{ proxmox_clone_format | default(omit, true) }}" full: "{{ proxmox_clone_complet | default(true) | bool }}" diff --git a/roles/serveur_forgejo/meta/empreinte.yml b/roles/serveur_forgejo/meta/empreinte.yml new file mode 100644 index 0000000..2df1265 --- /dev/null +++ b/roles/serveur_forgejo/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 1 + memoire_mo: 1024 + disque_go: 20 diff --git a/roles/serveur_grafana/meta/empreinte.yml b/roles/serveur_grafana/meta/empreinte.yml new file mode 100644 index 0000000..e1fa56e --- /dev/null +++ b/roles/serveur_grafana/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 1 + memoire_mo: 512 + disque_go: 2 diff --git a/roles/serveur_icinga/meta/empreinte.yml b/roles/serveur_icinga/meta/empreinte.yml new file mode 100644 index 0000000..f26eaa9 --- /dev/null +++ b/roles/serveur_icinga/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 2 + memoire_mo: 1024 + disque_go: 10 diff --git a/roles/serveur_keycloak/meta/empreinte.yml b/roles/serveur_keycloak/meta/empreinte.yml new file mode 100644 index 0000000..bab99a1 --- /dev/null +++ b/roles/serveur_keycloak/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 2 + memoire_mo: 1536 + disque_go: 5 diff --git a/roles/serveur_loki/meta/empreinte.yml b/roles/serveur_loki/meta/empreinte.yml new file mode 100644 index 0000000..2df1265 --- /dev/null +++ b/roles/serveur_loki/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 1 + memoire_mo: 1024 + disque_go: 20 diff --git a/roles/serveur_nginx/meta/empreinte.yml b/roles/serveur_nginx/meta/empreinte.yml new file mode 100644 index 0000000..e717d0f --- /dev/null +++ b/roles/serveur_nginx/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 1 + memoire_mo: 512 + disque_go: 3 diff --git a/roles/serveur_openldap/meta/empreinte.yml b/roles/serveur_openldap/meta/empreinte.yml new file mode 100644 index 0000000..e717d0f --- /dev/null +++ b/roles/serveur_openldap/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 1 + memoire_mo: 512 + disque_go: 3 diff --git a/roles/serveur_postgresql/meta/empreinte.yml b/roles/serveur_postgresql/meta/empreinte.yml new file mode 100644 index 0000000..dbc5371 --- /dev/null +++ b/roles/serveur_postgresql/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 2 + memoire_mo: 2048 + disque_go: 20 diff --git a/roles/serveur_powerdns/meta/empreinte.yml b/roles/serveur_powerdns/meta/empreinte.yml new file mode 100644 index 0000000..e1fa56e --- /dev/null +++ b/roles/serveur_powerdns/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 1 + memoire_mo: 512 + disque_go: 2 diff --git a/roles/serveur_prometheus/meta/empreinte.yml b/roles/serveur_prometheus/meta/empreinte.yml new file mode 100644 index 0000000..2df1265 --- /dev/null +++ b/roles/serveur_prometheus/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 1 + memoire_mo: 1024 + disque_go: 20 diff --git a/roles/serveur_redis/meta/empreinte.yml b/roles/serveur_redis/meta/empreinte.yml new file mode 100644 index 0000000..e1fa56e --- /dev/null +++ b/roles/serveur_redis/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 1 + memoire_mo: 512 + disque_go: 2 diff --git a/roles/serveur_sendmail/meta/empreinte.yml b/roles/serveur_sendmail/meta/empreinte.yml new file mode 100644 index 0000000..d749186 --- /dev/null +++ b/roles/serveur_sendmail/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 1 + memoire_mo: 512 + disque_go: 5 diff --git a/roles/serveur_step_ca/meta/empreinte.yml b/roles/serveur_step_ca/meta/empreinte.yml new file mode 100644 index 0000000..2d476bc --- /dev/null +++ b/roles/serveur_step_ca/meta/empreinte.yml @@ -0,0 +1,8 @@ +--- +# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py). +# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote. +# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque). +setops_empreinte: + coeurs: 1 + memoire_mo: 256 + disque_go: 2 diff --git a/scripts/instancier.py b/scripts/instancier.py index 05a16db..155a2a8 100644 --- a/scripts/instancier.py +++ b/scripts/instancier.py @@ -30,10 +30,12 @@ from inventory_rules import ( charger_nomenclature, charger_serveurs, deriver_nomenclature, + deriver_ressources, fonction_seq, ) RACINE = Path(__file__).resolve().parents[1] +ROLES = RACINE / "roles" INSTANCE = Path(os.environ.get("SETOPS_INSTANCE") or (RACINE / "instance")) INVENTAIRE = INSTANCE / "inventories/production/hosts.yml" GENERE = INSTANCE / "inventories/production/hosts.genere.yml" @@ -75,6 +77,12 @@ def generer() -> dict: for cle_srv, cle_var in PLACEMENT: if str(srv.get(cle_srv, "")).strip(): hostvars[cle_var] = srv[cle_srv] + # Ressources derivees des logiciels (groupes de service) + socle SE. + # setdefault : une valeur explicite du plan (PLACEMENT ci-dessus) gagne. + ressources = deriver_ressources(sorted(services_par_hote.get(nom, set())), ROLES) + hostvars.setdefault("proxmox_coeurs", ressources["coeurs"]) + hostvars.setdefault("proxmox_memoire", ressources["memoire"]) + hostvars.setdefault("proxmox_disque_taille", ressources["disque"]) etat = "hotes_actifs" if srv.get("etat") == "actif" else "hotes_planifies" children[etat]["hosts"][nom] = hostvars groupes = set(GROUPES_SOCLE) | services_par_hote.get(nom, set()) | set(srv.get("integrations") or []) diff --git a/scripts/inventory_host.py b/scripts/inventory_host.py index ec3cbb1..87d1260 100644 --- a/scripts/inventory_host.py +++ b/scripts/inventory_host.py @@ -157,6 +157,8 @@ CHAMPS_PROXMOX: tuple[tuple[str, str, bool], ...] = ( ("SETOPS_STOCKAGE", "proxmox_stockage", False), ("SETOPS_DISQUE", "proxmox_disque_taille", False), ("SETOPS_NOEUD", "proxmox_noeud", False), + ("SETOPS_COEURS", "proxmox_coeurs", False), + ("SETOPS_MEMOIRE", "proxmox_memoire", False), ) diff --git a/scripts/inventory_rules.py b/scripts/inventory_rules.py index 2771223..a9cad2b 100644 --- a/scripts/inventory_rules.py +++ b/scripts/inventory_rules.py @@ -385,6 +385,75 @@ def reconcilier_serveur(nom: str, srv: dict, hote_inventaire: dict | None, nomen "absent_inventaire": hote_inventaire is None} +# --- Dimensionnement derive des ressources VM ------------------------------- +# ressources(VM) = socle_SE + somme(empreinte des logiciels) -> marge -> arrondi. +# Les empreintes vivent dans roles//meta/empreinte.yml (propriete du +# logiciel). Les groupes sans role dedie ont un repli ci-dessous. Le plan peut +# tout surcharger par hote (serveurs.yml: coeurs/memoire/disque). + +SOCLE_SE = {"coeurs": 1, "memoire_mo": 512, "disque_go": 8} +EMPREINTE_DEFAUT = {"coeurs": 1, "memoire_mo": 512, "disque_go": 5} +EMPREINTES_SANS_ROLE = { + "serveur_web_frontal": {"coeurs": 1, "memoire_mo": 512, "disque_go": 5}, + "serveur_web_dorsal": {"coeurs": 1, "memoire_mo": 1024, "disque_go": 10}, +} +MARGE_MEMOIRE = 1.20 +MARGE_DISQUE = 1.25 +PALIER_MEMOIRE_MO = 512 +MIN_MEMOIRE_MO = 1024 +PALIER_DISQUE_GO = 5 +MIN_DISQUE_GO = 12 +MIN_COEURS = 1 +MAX_COEURS = 8 +CHAMPS_EMPREINTE = ("coeurs", "memoire_mo", "disque_go") + + +def charger_empreinte_role(racine_roles: Path, groupe: str) -> dict | None: + """Lit roles//meta/empreinte.yml -> empreinte normalisee, ou None.""" + fichier = racine_roles / groupe / "meta" / "empreinte.yml" + if not fichier.exists(): + return None + with fichier.open("r", encoding="utf-8") as fh: + data = yaml.safe_load(fh) or {} + emp = (data or {}).get("setops_empreinte") or {} + if not isinstance(emp, dict): + raise ValueError(f"{fichier}: 'setops_empreinte' doit etre une table.") + return {cle: int(emp.get(cle, 0)) for cle in CHAMPS_EMPREINTE} + + +def empreinte_groupe(racine_roles: Path, groupe: str) -> dict: + """Empreinte d'un groupe de service : role > repli connu > defaut.""" + return (charger_empreinte_role(racine_roles, groupe) + or EMPREINTES_SANS_ROLE.get(groupe) + or EMPREINTE_DEFAUT) + + +def _arrondi_superieur(valeur: float, palier: int) -> int: + import math + return int(math.ceil(valeur / palier) * palier) + + +def deriver_ressources(groupes_service, racine_roles: Path, socle: dict | None = None) -> dict: + """Estime coeurs/memoire(Mo)/disque(Go) d'une VM depuis ses logiciels + le SE. + + `groupes_service` : groupes applicatifs portes par l'hote (hors socle/etat). + RAM et disque additifs (contraintes dures) ; coeurs en max (burstable). + """ + socle = socle or SOCLE_SE + coeurs_somme = 0 + memoire = socle["memoire_mo"] + disque = socle["disque_go"] + for groupe in groupes_service: + emp = empreinte_groupe(racine_roles, groupe) + coeurs_somme += emp["coeurs"] + memoire += emp["memoire_mo"] + disque += emp["disque_go"] + coeurs = max(MIN_COEURS, min(MAX_COEURS, max(socle["coeurs"], coeurs_somme))) + memoire = max(MIN_MEMOIRE_MO, _arrondi_superieur(memoire * MARGE_MEMOIRE, PALIER_MEMOIRE_MO)) + disque = max(MIN_DISQUE_GO, _arrondi_superieur(disque * MARGE_DISQUE, PALIER_DISQUE_GO)) + return {"coeurs": coeurs, "memoire": memoire, "disque": f"{disque}G"} + + def charger_dependances(path: Path | None) -> dict: if not path: return {} diff --git a/scripts/tests/test_inventory_host.py b/scripts/tests/test_inventory_host.py index d2f7440..203e77c 100644 --- a/scripts/tests/test_inventory_host.py +++ b/scripts/tests/test_inventory_host.py @@ -69,6 +69,8 @@ def test_hote_complet_avec_noeud() -> None: "SETOPS_STOCKAGE='stockage-a'", "SETOPS_DISQUE='160G'", "SETOPS_NOEUD='noeud-a'", + "SETOPS_COEURS='4'", + "SETOPS_MEMOIRE='2048'", } assert set(lignes) == attendu, lignes