2026-06-24 20:17:46 -04:00
|
|
|
---
|
|
|
|
|
- name: Cloner une VM Debian depuis le modele Proxmox
|
|
|
|
|
hosts: localhost
|
|
|
|
|
connection: local
|
|
|
|
|
gather_facts: false
|
|
|
|
|
become: false
|
|
|
|
|
|
2026-07-01 15:38:42 -04:00
|
|
|
vars:
|
|
|
|
|
# Config Proxmox de l'instance : on cherche dans l'inventaire de l'instance,
|
|
|
|
|
# quel que soit son nom (lab > principal > production). Compatible « par instance ».
|
|
|
|
|
setops_inventaires: [lab, principal, production]
|
proxmox : pools créés, jeton normalisé, reliquat de voûte supprimé
Reconnaissance en lecture seule de l'API du cluster, avec le jeton de la voûte.
Trois valeurs devinées étaient fausses, et deux défauts bloquants sont apparus.
Corrigé d'après le cluster
- stockages : truenas-dbsql manquait ; le catalogue ne garde que ceux qui
portent `images` (PBS, cephFS, local et truenas iSCSI n'accueillent pas de
disque de VM) ;
- ponts : vmbr0 avait été omis, et l'uniformité sur les trois nœuds n'avait pas
été vérifiée — un pont partiel empêche la VM de démarrer sur certains nœuds.
Pools
Chezlepro-17 et Technolibre-11 créés, dérivés comme le reste. Les pools
Env.Tenant antérieurs (Prod.Chezlepro, Lab.KBR...) sont l'ancien monde : on n'y
touche pas et on n'y verse pas la flotte générée. Diff réel : 2 pools ajoutés,
0 retiré, 1 VM sur 38 déplacée — infra-pki-01, qui n'avait aucun pool.
Défaut bloquant : make creer-vm aurait échoué en 401
proxmoxer recompose `utilisateur!nom` à partir d'api_user et d'api_token_id. La
voûte stocke la forme complète, que les playbooks passaient telle quelle, d'où
un 401 muet — alors que le même jeton fonctionne en curl. Mesuré des deux côtés
avec un module en lecture seule : forme complète = 401, forme courte = OK.
Normalisation par split('!') | last, qui accepte les deux écritures.
Reliquat proxmox.vault.yml supprimé
Toléré « en compatibilité », il restait le seul porteur du jeton chez
Technolibre — et comme *.vault.yml est gitignoré, ce jeton ne voyageait avec
aucun dépôt : une voûte unique (D-19) qui ne l'était pas. Migration faite en
mémoire, avec relecture et aller-retour de chiffrement vérifiés avant écriture ;
fichier supprimé, listes de chargement des playbooks nettoyées, validé par un
appel API réel ne chargeant que all/vault.yml.
La garde qui manquait
voute.py verifier ne comparait que le gabarit — il disait « complet » pendant
qu'un secret vivait ailleurs. Il contrôle maintenant aussi la voûte réelle quand
ANSIBLE_VAULT_PASSWORD_FILE la rend déchiffrable : noms de clés seulement,
jamais de valeur, et vérification sautée sans mot de passe.
Elle a trouvé un second trou dès son premier passage : la voûte réelle de
Technolibre n'a ni vault_nextcloud_admin ni vault_nextcloud_oidc, que le plan
exige. Non corrigé — générer ces secrets est une décision, et celui d'OIDC doit
correspondre à ce que Keycloak connaîtra.
27 preuves OK. --syntax-check et ansible-lint (production) sur les playbooks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:52:44 -04:00
|
|
|
# Voute UNIQUE de l'instance : all/vault.yml porte TOUS les secrets, jeton
|
|
|
|
|
# d'hyperviseur compris (D-19). `proxmox.vault.yml` a ete retire le 2026-08-03 :
|
|
|
|
|
# tolere "en compatibilite", il restait le SEUL porteur du jeton chez un tenant,
|
|
|
|
|
# et il etait gitignore — une voute unique qui ne l'etait pas, dans un fichier
|
|
|
|
|
# qui ne voyageait avec aucun depot. P18 ne pouvait pas le voir : il verifie le
|
|
|
|
|
# gabarit, pas le contenu reel.
|
|
|
|
|
# `proxmox.local.yml` reste une surcharge locale volontaire, non versionnee.
|
|
|
|
|
setops_gv_proxmox: [proxmox.yml, proxmox.local.yml, all/vault.yml]
|
intégrations : le rôle déclare sa politique ; le cluster passe à l'hébergeur
Deux corrections de propriété, l'une dans le plan, l'autre dans les intrants.
1. Intégrations universelles (D-33/D-34, P26)
Le plan portait 57 lignes d'intégration écrites à la main, dont 28 disaient oui à
quelque chose de vrai pour tous les hôtes. Elles n'existaient que pour être
oubliées — et elles l'avaient été : dans Chezlepro, backup-01 et infra-pki-01
n'étaient ni supervisés, ni journalisés, ni certifiés.
Le rôle déclare désormais sa politique une fois, dans meta/integration.yml ; le
plan ne garde que les vrais choix et refuse la recopie. Les exemptions se
dérivent du service rendu (sauf_role), jamais d'un nom d'hôte : l'AC ne s'enrôle
pas auprès d'elle-même, et l'exemption suit step-ca si on le déplace.
Une seule fonction de résolution — integrations_de() — lue par l'inventaire, la
voûte et le panneau. Sans le passage par la voûte, les secrets des intégrations
universelles auraient cessé d'être exigés et P18 serait passé au vert sur une
voûte incomplète.
Vérifié : diff vide sur Technolibre (la politique reproduit exactement les 41
lignes retirées) ; sur Chezlepro, exactement les groupes manquants, et pas
client_pki sur infra-pki-01.
2. Vue Intégrations : la matrice
La fiche montrait les intégrations d'UN serveur ; le trou de Chezlepro n'a pas
été trouvé par le panneau mais par le devis de pare-feu. Matrice serveurs x
intégrations : colonnes de politique en lecture seule, facultatives cochables sur
place, ligne de couverture n/N qui rend le motif visible sans le juger.
3. Propriété des intrants (D-35/D-36, P27)
Le cluster Proxmox appartient à l'hébergeur, comme sa fabric et sa frontière.
Recopié chez chaque tenant, son inventaire avait déjà divergé : deux listes de
stockages contradictoires pour le même matériel. API/nœuds/stockages/ponts vont
dans proxmox-hebergeur.yml, à côté d'underlay.yml, dont le chemin se dérive —
l'hébergeur reste non déclaré (D-17). Restent au tenant son golden template et
ses défauts de placement.
Le panneau nomme désormais le propriétaire de chaque section : éditer une section
« hébergeur » vaut pour tous ses tenants, et l'écran ne le disait pas.
26 preuves OK, 0 échec. --syntax-check des deux playbooks Proxmox.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 14:09:22 -04:00
|
|
|
# Le CLUSTER appartient a l'HEBERGEUR (API, noeuds, stockages, ponts). Son fichier
|
|
|
|
|
# vit dans le depot de l'hebergeur, que le symlink `underlay.yml` designe deja
|
|
|
|
|
# (D-14/D-17) : on en DERIVE le chemin plutot que de redeclarer l'hebergeur. Absent
|
|
|
|
|
# (depot sans underlay monte), le `stat` echoue simplement et rien n'est charge.
|
|
|
|
|
setops_proxmox_hebergeur: "{{ (playbook_dir ~ '/../../underlay.yml') | realpath | dirname }}/proxmox-hebergeur.yml"
|
2026-07-01 15:38:42 -04:00
|
|
|
|
2026-06-24 20:17:46 -04:00
|
|
|
tasks:
|
|
|
|
|
- name: Verifier les fichiers de variables Proxmox optionnels
|
|
|
|
|
ansible.builtin.stat:
|
2026-07-01 15:38:42 -04:00
|
|
|
path: "{{ playbook_dir }}/../../instance/inventories/{{ item.0 }}/group_vars/{{ item.1 }}"
|
|
|
|
|
loop: "{{ setops_inventaires | product(setops_gv_proxmox) | list }}"
|
|
|
|
|
loop_control:
|
|
|
|
|
label: "{{ item.0 }}/{{ item.1 }}"
|
2026-06-24 20:17:46 -04:00
|
|
|
register: proxmox_fichiers_variables
|
|
|
|
|
|
|
|
|
|
- name: Charger les variables Proxmox non sensibles disponibles
|
|
|
|
|
ansible.builtin.include_vars:
|
|
|
|
|
file: "{{ item.stat.path }}"
|
|
|
|
|
loop: "{{ proxmox_fichiers_variables.results }}"
|
|
|
|
|
when:
|
|
|
|
|
- item.stat.exists
|
|
|
|
|
- item.stat.path is not search('vault')
|
|
|
|
|
|
intégrations : le rôle déclare sa politique ; le cluster passe à l'hébergeur
Deux corrections de propriété, l'une dans le plan, l'autre dans les intrants.
1. Intégrations universelles (D-33/D-34, P26)
Le plan portait 57 lignes d'intégration écrites à la main, dont 28 disaient oui à
quelque chose de vrai pour tous les hôtes. Elles n'existaient que pour être
oubliées — et elles l'avaient été : dans Chezlepro, backup-01 et infra-pki-01
n'étaient ni supervisés, ni journalisés, ni certifiés.
Le rôle déclare désormais sa politique une fois, dans meta/integration.yml ; le
plan ne garde que les vrais choix et refuse la recopie. Les exemptions se
dérivent du service rendu (sauf_role), jamais d'un nom d'hôte : l'AC ne s'enrôle
pas auprès d'elle-même, et l'exemption suit step-ca si on le déplace.
Une seule fonction de résolution — integrations_de() — lue par l'inventaire, la
voûte et le panneau. Sans le passage par la voûte, les secrets des intégrations
universelles auraient cessé d'être exigés et P18 serait passé au vert sur une
voûte incomplète.
Vérifié : diff vide sur Technolibre (la politique reproduit exactement les 41
lignes retirées) ; sur Chezlepro, exactement les groupes manquants, et pas
client_pki sur infra-pki-01.
2. Vue Intégrations : la matrice
La fiche montrait les intégrations d'UN serveur ; le trou de Chezlepro n'a pas
été trouvé par le panneau mais par le devis de pare-feu. Matrice serveurs x
intégrations : colonnes de politique en lecture seule, facultatives cochables sur
place, ligne de couverture n/N qui rend le motif visible sans le juger.
3. Propriété des intrants (D-35/D-36, P27)
Le cluster Proxmox appartient à l'hébergeur, comme sa fabric et sa frontière.
Recopié chez chaque tenant, son inventaire avait déjà divergé : deux listes de
stockages contradictoires pour le même matériel. API/nœuds/stockages/ponts vont
dans proxmox-hebergeur.yml, à côté d'underlay.yml, dont le chemin se dérive —
l'hébergeur reste non déclaré (D-17). Restent au tenant son golden template et
ses défauts de placement.
Le panneau nomme désormais le propriétaire de chaque section : éditer une section
« hébergeur » vaut pour tous ses tenants, et l'écran ne le disait pas.
26 preuves OK, 0 échec. --syntax-check des deux playbooks Proxmox.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 14:09:22 -04:00
|
|
|
- name: Localiser le fichier Proxmox de l'hebergeur
|
|
|
|
|
ansible.builtin.stat:
|
|
|
|
|
path: "{{ setops_proxmox_hebergeur }}"
|
|
|
|
|
register: proxmox_fichier_hebergeur
|
|
|
|
|
|
|
|
|
|
- name: Charger le cluster de l'hebergeur (autoritaire sur ses propres cles)
|
|
|
|
|
ansible.builtin.include_vars:
|
|
|
|
|
file: "{{ setops_proxmox_hebergeur }}"
|
|
|
|
|
when: proxmox_fichier_hebergeur.stat.exists
|
|
|
|
|
|
2026-06-24 20:17:46 -04:00
|
|
|
- name: Charger les secrets Proxmox Vault
|
|
|
|
|
block:
|
Mise en conformité prouvable : registre d'affirmations + make prouver
Le dépôt fait / explique / prouve ce qu'il affirme, vérifiable en une commande.
- Phase 1 : docs/audit/affirmations.md — 54 affirmations publiques tracées vers
une commande de preuve et un statut (✅/🟡/❌/⚪).
- Phase 2 : CLAUDE.md réduit à un pointeur mince ; contradiction SSH levée (le
code applique déjà PasswordAuthentication no + AuthenticationMethods publickey,
conforme à AGENTS.md) ; section AGENTS « Codex » → « agents IA ».
- Phase 3 : parcours démarrage réparé (QUICKSTART renvoyait à un modèle absent,
chemins de voûte faux, commandes make périmées) ; make verifier vert
(ansible-lint 33 → 0 : site.yml généré nommé, pipefail, name[template]) ;
voûte Proxmox unifiée lue par le clonage (all/vault.yml).
- Phase 4 : make prouver → docs/audit/preuve-<date>.md, harnais rejouable qui
rappelle l'outillage existant (aucune validation réimplémentée).
- Phase 5 : parcours QUICKSTART prouvé hors-ligne sur le socle ; modèle socle
rendu valide (autorite interne → auto-heberge) ; split-brain d'inventaire
corrigé (repli sur le répertoire existant, pas principal/).
make prouver : 15 OK, 0 échec, 1 sautée (voûte). ansible-lint : 0 failure.
Écarts découverts en cours de traitement (AFF-097..100) : tous résolus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:53:18 -04:00
|
|
|
- name: Charger les secrets de la voute Proxmox
|
2026-06-24 20:17:46 -04:00
|
|
|
ansible.builtin.include_vars:
|
|
|
|
|
file: "{{ item.stat.path }}"
|
|
|
|
|
loop: "{{ proxmox_fichiers_variables.results }}"
|
|
|
|
|
when:
|
|
|
|
|
- item.stat.exists
|
|
|
|
|
- item.stat.path is search('vault')
|
|
|
|
|
no_log: true
|
|
|
|
|
rescue:
|
|
|
|
|
- name: Expliquer l echec de chargement du Vault
|
|
|
|
|
ansible.builtin.fail:
|
|
|
|
|
msg: >-
|
Mise en conformité prouvable : registre d'affirmations + make prouver
Le dépôt fait / explique / prouve ce qu'il affirme, vérifiable en une commande.
- Phase 1 : docs/audit/affirmations.md — 54 affirmations publiques tracées vers
une commande de preuve et un statut (✅/🟡/❌/⚪).
- Phase 2 : CLAUDE.md réduit à un pointeur mince ; contradiction SSH levée (le
code applique déjà PasswordAuthentication no + AuthenticationMethods publickey,
conforme à AGENTS.md) ; section AGENTS « Codex » → « agents IA ».
- Phase 3 : parcours démarrage réparé (QUICKSTART renvoyait à un modèle absent,
chemins de voûte faux, commandes make périmées) ; make verifier vert
(ansible-lint 33 → 0 : site.yml généré nommé, pipefail, name[template]) ;
voûte Proxmox unifiée lue par le clonage (all/vault.yml).
- Phase 4 : make prouver → docs/audit/preuve-<date>.md, harnais rejouable qui
rappelle l'outillage existant (aucune validation réimplémentée).
- Phase 5 : parcours QUICKSTART prouvé hors-ligne sur le socle ; modèle socle
rendu valide (autorite interne → auto-heberge) ; split-brain d'inventaire
corrigé (repli sur le répertoire existant, pas principal/).
make prouver : 15 OK, 0 échec, 1 sautée (voûte). ansible-lint : 0 failure.
Écarts découverts en cours de traitement (AFF-097..100) : tous résolus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:53:18 -04:00
|
|
|
Impossible de charger la voute Proxmox (group_vars/all/vault.yml ou
|
|
|
|
|
proxmox.vault.yml). Si elle est chiffree avec Ansible Vault, fournir son
|
|
|
|
|
mot de passe ou definir ANSIBLE_VAULT_PASSWORD_FILE avant de relancer make creer-vm.
|
2026-06-24 20:17:46 -04:00
|
|
|
|
|
|
|
|
- name: Appliquer les valeurs Proxmox avec repli environnement
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
proxmox_api_host_effectif: "{{ proxmox_api_host | default(lookup('env', 'PROXMOX_API_HOST'), true) }}"
|
|
|
|
|
proxmox_api_user_effectif: "{{ proxmox_api_user | default(lookup('env', 'PROXMOX_API_USER'), true) }}"
|
proxmox : pools créés, jeton normalisé, reliquat de voûte supprimé
Reconnaissance en lecture seule de l'API du cluster, avec le jeton de la voûte.
Trois valeurs devinées étaient fausses, et deux défauts bloquants sont apparus.
Corrigé d'après le cluster
- stockages : truenas-dbsql manquait ; le catalogue ne garde que ceux qui
portent `images` (PBS, cephFS, local et truenas iSCSI n'accueillent pas de
disque de VM) ;
- ponts : vmbr0 avait été omis, et l'uniformité sur les trois nœuds n'avait pas
été vérifiée — un pont partiel empêche la VM de démarrer sur certains nœuds.
Pools
Chezlepro-17 et Technolibre-11 créés, dérivés comme le reste. Les pools
Env.Tenant antérieurs (Prod.Chezlepro, Lab.KBR...) sont l'ancien monde : on n'y
touche pas et on n'y verse pas la flotte générée. Diff réel : 2 pools ajoutés,
0 retiré, 1 VM sur 38 déplacée — infra-pki-01, qui n'avait aucun pool.
Défaut bloquant : make creer-vm aurait échoué en 401
proxmoxer recompose `utilisateur!nom` à partir d'api_user et d'api_token_id. La
voûte stocke la forme complète, que les playbooks passaient telle quelle, d'où
un 401 muet — alors que le même jeton fonctionne en curl. Mesuré des deux côtés
avec un module en lecture seule : forme complète = 401, forme courte = OK.
Normalisation par split('!') | last, qui accepte les deux écritures.
Reliquat proxmox.vault.yml supprimé
Toléré « en compatibilité », il restait le seul porteur du jeton chez
Technolibre — et comme *.vault.yml est gitignoré, ce jeton ne voyageait avec
aucun dépôt : une voûte unique (D-19) qui ne l'était pas. Migration faite en
mémoire, avec relecture et aller-retour de chiffrement vérifiés avant écriture ;
fichier supprimé, listes de chargement des playbooks nettoyées, validé par un
appel API réel ne chargeant que all/vault.yml.
La garde qui manquait
voute.py verifier ne comparait que le gabarit — il disait « complet » pendant
qu'un secret vivait ailleurs. Il contrôle maintenant aussi la voûte réelle quand
ANSIBLE_VAULT_PASSWORD_FILE la rend déchiffrable : noms de clés seulement,
jamais de valeur, et vérification sautée sans mot de passe.
Elle a trouvé un second trou dès son premier passage : la voûte réelle de
Technolibre n'a ni vault_nextcloud_admin ni vault_nextcloud_oidc, que le plan
exige. Non corrigé — générer ces secrets est une décision, et celui d'OIDC doit
correspondre à ce que Keycloak connaîtra.
27 preuves OK. --syntax-check et ansible-lint (production) sur les playbooks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:52:44 -04:00
|
|
|
# `split('!')[-1]` : proxmoxer recompose lui-meme `utilisateur!nom` a partir de
|
|
|
|
|
# `api_user` et `api_token_id`. Lui passer la forme COMPLETE produit
|
|
|
|
|
# `ansible@pve!ansible@pve!nom` et un 401 muet — le meme jeton fonctionne en
|
|
|
|
|
# HTTP direct, ce qui rend le diagnostic trompeur. Verifie contre le cluster le
|
|
|
|
|
# 2026-08-03 : forme complete = 401, forme courte = OK. Sans effet si la voute
|
|
|
|
|
# stocke deja le nom seul, donc les deux ecritures restent acceptees.
|
|
|
|
|
proxmox_api_token_id_effectif: "{{ (proxmox_api_token_id | default(lookup('env', 'PROXMOX_API_TOKEN_ID'), true)) | string | split('!') | last }}"
|
2026-06-24 20:17:46 -04:00
|
|
|
proxmox_api_token_secret_effectif: "{{ proxmox_api_token_secret | default(lookup('env', 'PROXMOX_API_TOKEN_SECRET'), true) }}"
|
|
|
|
|
proxmox_api_port_effectif: "{{ proxmox_api_port | default(lookup('env', 'PROXMOX_API_PORT'), true) }}"
|
|
|
|
|
no_log: true
|
|
|
|
|
|
|
|
|
|
- name: Valider les parametres obligatoires Proxmox
|
|
|
|
|
ansible.builtin.assert:
|
|
|
|
|
that:
|
|
|
|
|
- proxmox_api_host_effectif | length > 0
|
|
|
|
|
- proxmox_api_user_effectif | length > 0
|
|
|
|
|
- proxmox_api_token_id_effectif | length > 0
|
|
|
|
|
- proxmox_api_token_secret_effectif | length > 0
|
|
|
|
|
- proxmox_clone_nom is defined
|
|
|
|
|
- proxmox_clone_nom | length > 0
|
|
|
|
|
- proxmox_clone_vmid_modele is defined
|
|
|
|
|
- proxmox_clone_vmid is defined
|
|
|
|
|
- proxmox_clone_noeud is defined
|
|
|
|
|
- proxmox_clone_noeud | length > 0
|
|
|
|
|
- proxmox_clone_ipconfig0 is defined
|
|
|
|
|
- proxmox_clone_ipconfig0 | length > 0
|
Mise en conformité prouvable : registre d'affirmations + make prouver
Le dépôt fait / explique / prouve ce qu'il affirme, vérifiable en une commande.
- Phase 1 : docs/audit/affirmations.md — 54 affirmations publiques tracées vers
une commande de preuve et un statut (✅/🟡/❌/⚪).
- Phase 2 : CLAUDE.md réduit à un pointeur mince ; contradiction SSH levée (le
code applique déjà PasswordAuthentication no + AuthenticationMethods publickey,
conforme à AGENTS.md) ; section AGENTS « Codex » → « agents IA ».
- Phase 3 : parcours démarrage réparé (QUICKSTART renvoyait à un modèle absent,
chemins de voûte faux, commandes make périmées) ; make verifier vert
(ansible-lint 33 → 0 : site.yml généré nommé, pipefail, name[template]) ;
voûte Proxmox unifiée lue par le clonage (all/vault.yml).
- Phase 4 : make prouver → docs/audit/preuve-<date>.md, harnais rejouable qui
rappelle l'outillage existant (aucune validation réimplémentée).
- Phase 5 : parcours QUICKSTART prouvé hors-ligne sur le socle ; modèle socle
rendu valide (autorite interne → auto-heberge) ; split-brain d'inventaire
corrigé (repli sur le répertoire existant, pas principal/).
make prouver : 15 OK, 0 échec, 1 sautée (voûte). ansible-lint : 0 failure.
Écarts découverts en cours de traitement (AFF-097..100) : tous résolus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:53:18 -04:00
|
|
|
fail_msg: "Parametres Proxmox incomplets. Verifier proxmox.yml, la voute (all/vault.yml ou proxmox.vault.yml) et les variables make propres a la VM."
|
2026-06-24 20:17:46 -04:00
|
|
|
|
|
|
|
|
- name: Lire la cle publique SSH depuis le fichier fourni
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
proxmox_clone_sshkeys: "{{ lookup('ansible.builtin.file', proxmox_clone_cle_publique_fichier) }}"
|
|
|
|
|
when:
|
|
|
|
|
- proxmox_clone_cle_publique is not defined
|
|
|
|
|
- proxmox_clone_cle_publique_fichier is defined
|
|
|
|
|
- proxmox_clone_cle_publique_fichier | length > 0
|
|
|
|
|
|
|
|
|
|
- name: Utiliser la cle publique SSH fournie directement
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
proxmox_clone_sshkeys: "{{ proxmox_clone_cle_publique }}"
|
|
|
|
|
when:
|
|
|
|
|
- proxmox_clone_cle_publique is defined
|
|
|
|
|
- proxmox_clone_cle_publique | length > 0
|
|
|
|
|
|
|
|
|
|
- name: Construire les serveurs DNS Cloud-Init
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
proxmox_clone_nameservers: >-
|
|
|
|
|
{{
|
|
|
|
|
(proxmox_clone_dns | default('') | split(','))
|
|
|
|
|
| map('trim')
|
|
|
|
|
| reject('equalto', '')
|
|
|
|
|
| list
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
- name: Verifier la bibliotheque Python proxmoxer locale
|
|
|
|
|
ansible.builtin.command:
|
|
|
|
|
cmd: "{{ ansible_playbook_python }} -c 'import proxmoxer'"
|
|
|
|
|
changed_when: false
|
|
|
|
|
failed_when: false
|
|
|
|
|
register: proxmoxer_verification
|
|
|
|
|
|
|
|
|
|
- name: Refuser si proxmoxer est absent
|
|
|
|
|
ansible.builtin.fail:
|
|
|
|
|
msg: >-
|
|
|
|
|
La bibliotheque Python proxmoxer est absente pour {{ ansible_playbook_python }}.
|
|
|
|
|
Installer le paquet systeme approprie, par exemple
|
|
|
|
|
sudo apt install python3-proxmoxer, ou installer proxmoxer dans
|
|
|
|
|
l'environnement Python utilise par Ansible, puis relancer make creer-vm.
|
|
|
|
|
when: proxmoxer_verification.rc != 0
|
|
|
|
|
|
pools Proxmox : un par tenant, dérivé de l'index (D-37, P28)
Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez
Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le
reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101
contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est
indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les
certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}.
Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y
sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le
codage.
Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par
P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11.
make devis-proxmox-pools rattrape la flotte existante (création du pool, puis
affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes :
make creer-vm dérive le pool par la même fonction et le passe à la création. Le
playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et
l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le
rattrapage passe par les membres.
P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID
— une machine appartenant à deux tenants serait pire qu'une homonymie.
Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai
gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart.
27 preuves OK, 0 échec. --syntax-check du playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
|
|
|
- name: S'assurer que le pool du tenant existe
|
|
|
|
|
# Un pool par tenant : la console Proxmox affiche le NOM, et onze serveurs
|
|
|
|
|
# portent le meme d'un tenant a l'autre. Le pool restitue l'appartenance sans
|
|
|
|
|
# rien renommer. Il doit exister AVANT le clone — `proxmox_kvm` echoue sur un
|
|
|
|
|
# pool inconnu, et l'API ne sait pas changer le pool d'une VM deja creee.
|
|
|
|
|
community.general.proxmox_pool:
|
|
|
|
|
api_host: "{{ proxmox_api_host_effectif }}"
|
|
|
|
|
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
|
|
|
|
api_user: "{{ proxmox_api_user_effectif }}"
|
|
|
|
|
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
|
|
|
|
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
|
|
|
|
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
|
|
|
|
poolid: "{{ proxmox_clone_pool }}"
|
|
|
|
|
comment: "Tenant Set-OPS — genere, ne pas renommer a la main"
|
|
|
|
|
state: present
|
|
|
|
|
when: proxmox_clone_pool | default('', true) | length > 0
|
|
|
|
|
|
portabilite : monter un SECOND tenant revele trois defauts invisibles
Les deux reconstructions de la semaine rebatissaient Chezlepro sur son propre
materiel : une preuve de reproductibilite, pas de portabilite. La vraie
epreuve est un second tenant — Technolibre, index 11, plan distinct, voute
separee, meme cluster.
1. LE CLONAGE RESOLVAIT PAR NOM. proxmox_kvm cherche une VM portant le `name`
demande ; s'il en trouve une il rend `ok` et ne clone RIEN — aucune tache
cote cluster. Or les noms courts sont VOLONTAIREMENT identiques d'un tenant a
l'autre. `backup-01` de Technolibre tombait sur celui de Chezlepro. Mesure :
id-ldap-01 et sup-01 (noms absents chez Chezlepro) passaient du premier coup,
backup-01 echouait toujours. Le clonage cible desormais par VMID, via l'API.
Deux defauts de ce correctif, trouves en le mesurant : le corps assemble en
Jinja avec `>-` rendait une CHAINE et perdait le `pool` (VM nee hors de son
pool, sans un mot) ; et l'application du gabarit de calcul expirait a 5 s,
laissant la VM aux valeurs du gabarit — 2 coeurs/2 Go au lieu du plan, en
silence. Corps en mapping YAML avec omit ; six tentatives espacees.
Et `no_log` a masque la cause au moment ou elle servait. Les deux attentes
disent maintenant ce qu'elles ont constate.
2. LE GUI DETRUISAIT DES INTRANTS. Dans ecrire_intrants, `identite` etait la
seule branche sur quatre a ecrire par-dessus le disque au lieu de fusionner.
Un enregistrement a supprime dns_amorcage et amorcage_acces_courriel. Le meme
geste sur Chezlepro aurait mange les memes cles.
3. LE VERROU DE `raser` N'ETAIT PROUVE QUE POUR UN TENANT. Le faux cluster
codait en dur les VMID de Chezlepro ; monte ailleurs, le test rendait 0 au
lieu de 2. Il fabrique desormais la collision sur le plan courant.
Verifie : prouver.py 34 OK sur Technolibre, test_raser vert sur les deux
tenants, ansible-lint production sur le playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 11:18:14 -04:00
|
|
|
# LE CLONE SE CIBLE PAR VMID, JAMAIS PAR NOM — et c'est tout l'objet de ces trois
|
|
|
|
|
# taches. `community.general.proxmox_kvm` cherche d'abord une VM portant le `name`
|
|
|
|
|
# demande ; s'il en trouve une, il conclut « elle existe deja », rend `ok` et ne
|
|
|
|
|
# clone RIEN. Aucune tache n'apparait meme cote cluster.
|
|
|
|
|
#
|
|
|
|
|
# Or les noms courts sont VOLONTAIREMENT identiques d'un tenant a l'autre (meme
|
|
|
|
|
# fonction, meme nom) : c'est le pool qui restitue l'appartenance. Le premier clone
|
|
|
|
|
# de Technolibre — `backup-01` — est donc tombe sur le `backup-01` de Chezlepro et
|
|
|
|
|
# n'a rien fait, puis l'attente a expire sur une configuration qui n'existerait
|
|
|
|
|
# jamais. Mesure du 2026-08-10 : zero VM creee, zero tache `qmclone` au cluster.
|
|
|
|
|
#
|
|
|
|
|
# Le defaut etait invisible tant qu'un seul tenant existait. C'est exactement ce
|
|
|
|
|
# qu'une epreuve de portabilite doit trouver.
|
|
|
|
|
- name: Recenser les VM du cluster (pour cibler par VMID)
|
|
|
|
|
vars:
|
|
|
|
|
proxmox_api_racine: "https://{{ proxmox_api_host_effectif }}:{{ proxmox_api_port_effectif | default(8006, true) }}"
|
|
|
|
|
ansible.builtin.uri:
|
|
|
|
|
url: "{{ proxmox_api_racine }}/api2/json/cluster/resources?type=vm"
|
|
|
|
|
method: GET
|
|
|
|
|
headers:
|
|
|
|
|
Authorization: >-
|
|
|
|
|
PVEAPIToken={{ proxmox_api_user_effectif }}!{{ proxmox_api_token_id_effectif }}={{ proxmox_api_token_secret_effectif }}
|
2026-06-24 20:17:46 -04:00
|
|
|
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
portabilite : monter un SECOND tenant revele trois defauts invisibles
Les deux reconstructions de la semaine rebatissaient Chezlepro sur son propre
materiel : une preuve de reproductibilite, pas de portabilite. La vraie
epreuve est un second tenant — Technolibre, index 11, plan distinct, voute
separee, meme cluster.
1. LE CLONAGE RESOLVAIT PAR NOM. proxmox_kvm cherche une VM portant le `name`
demande ; s'il en trouve une il rend `ok` et ne clone RIEN — aucune tache
cote cluster. Or les noms courts sont VOLONTAIREMENT identiques d'un tenant a
l'autre. `backup-01` de Technolibre tombait sur celui de Chezlepro. Mesure :
id-ldap-01 et sup-01 (noms absents chez Chezlepro) passaient du premier coup,
backup-01 echouait toujours. Le clonage cible desormais par VMID, via l'API.
Deux defauts de ce correctif, trouves en le mesurant : le corps assemble en
Jinja avec `>-` rendait une CHAINE et perdait le `pool` (VM nee hors de son
pool, sans un mot) ; et l'application du gabarit de calcul expirait a 5 s,
laissant la VM aux valeurs du gabarit — 2 coeurs/2 Go au lieu du plan, en
silence. Corps en mapping YAML avec omit ; six tentatives espacees.
Et `no_log` a masque la cause au moment ou elle servait. Les deux attentes
disent maintenant ce qu'elles ont constate.
2. LE GUI DETRUISAIT DES INTRANTS. Dans ecrire_intrants, `identite` etait la
seule branche sur quatre a ecrire par-dessus le disque au lieu de fusionner.
Un enregistrement a supprime dns_amorcage et amorcage_acces_courriel. Le meme
geste sur Chezlepro aurait mange les memes cles.
3. LE VERROU DE `raser` N'ETAIT PROUVE QUE POUR UN TENANT. Le faux cluster
codait en dur les VMID de Chezlepro ; monte ailleurs, le test rendait 0 au
lieu de 2. Il fabrique desormais la collision sur le plan courant.
Verifie : prouver.py 34 OK sur Technolibre, test_raser vert sur les deux
tenants, ansible-lint production sur le playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 11:18:14 -04:00
|
|
|
status_code: 200
|
|
|
|
|
register: proxmox_inventaire_vm
|
|
|
|
|
changed_when: false
|
|
|
|
|
no_log: true
|
|
|
|
|
|
|
|
|
|
- name: Retenir si le VMID cible est deja pris
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
proxmox_clone_deja_la: >-
|
|
|
|
|
{{ (proxmox_inventaire_vm.json.data | default([]))
|
|
|
|
|
| selectattr('vmid', 'defined')
|
|
|
|
|
| selectattr('vmid', 'equalto', proxmox_clone_vmid | int)
|
|
|
|
|
| list | length > 0 }}
|
|
|
|
|
|
|
|
|
|
- name: Cloner la VM depuis le modele (API directe, cible par VMID)
|
|
|
|
|
vars:
|
|
|
|
|
proxmox_api_racine: "https://{{ proxmox_api_host_effectif }}:{{ proxmox_api_port_effectif | default(8006, true) }}"
|
|
|
|
|
ansible.builtin.uri:
|
|
|
|
|
url: "{{ proxmox_api_racine }}/api2/json/nodes/{{ proxmox_clone_noeud }}/qemu/{{ proxmox_clone_vmid_modele | int }}/clone"
|
|
|
|
|
method: POST
|
|
|
|
|
headers:
|
|
|
|
|
Authorization: >-
|
|
|
|
|
PVEAPIToken={{ proxmox_api_user_effectif }}!{{ proxmox_api_token_id_effectif }}={{ proxmox_api_token_secret_effectif }}
|
|
|
|
|
body_format: form-urlencoded
|
|
|
|
|
# Corps ecrit en MAPPING YAML, pas assemble en Jinja : une expression `>-` rend
|
|
|
|
|
# une CHAINE, et le premier jet a ainsi perdu le `pool` en route — la VM est
|
|
|
|
|
# nee hors de son pool, sans un mot. `omit` retire proprement les cles vides.
|
|
|
|
|
body:
|
|
|
|
|
newid: "{{ proxmox_clone_vmid | int }}"
|
|
|
|
|
name: "{{ proxmox_clone_nom }}"
|
|
|
|
|
full: "{{ 1 if (proxmox_clone_complet | default(true) | bool) else 0 }}"
|
|
|
|
|
storage: "{{ proxmox_clone_stockage | default(omit, true) }}"
|
|
|
|
|
format: "{{ proxmox_clone_format | default(omit, true) }}"
|
|
|
|
|
# A la CREATION seulement : l'API ne sait pas changer le pool d'une VM
|
|
|
|
|
# existante par cet appel (il faut passer par le membre du pool).
|
|
|
|
|
pool: "{{ proxmox_clone_pool | default(omit, true) }}"
|
|
|
|
|
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
|
|
|
|
status_code: 200
|
|
|
|
|
register: proxmox_clone_lance
|
|
|
|
|
# Le corps ne porte aucun secret, mais l'en-tete si : `no_log` masquerait alors
|
|
|
|
|
# l'erreur de l'API, qui est justement ce qu'on veut lire. On ne journalise donc
|
|
|
|
|
# que l'ECHEC, et sans les en-tetes.
|
|
|
|
|
failed_when: false
|
|
|
|
|
no_log: true
|
|
|
|
|
when: not proxmox_clone_deja_la
|
|
|
|
|
|
|
|
|
|
- name: Dire pourquoi le clonage a echoue, s'il a echoue
|
|
|
|
|
ansible.builtin.fail:
|
|
|
|
|
msg: >-
|
|
|
|
|
Le clonage de {{ proxmox_clone_nom }} (VMID {{ proxmox_clone_vmid }}) depuis le
|
|
|
|
|
gabarit {{ proxmox_clone_vmid_modele }} a ete refuse par l'API Proxmox
|
|
|
|
|
(HTTP {{ proxmox_clone_lance.status | default('?') }}).
|
|
|
|
|
Verifier que le gabarit existe sur {{ proxmox_clone_noeud }}, que le pool
|
|
|
|
|
{{ proxmox_clone_pool | default('(aucun)', true) }} existe, et que le VMID
|
|
|
|
|
cible est libre.
|
|
|
|
|
when:
|
|
|
|
|
- not proxmox_clone_deja_la
|
|
|
|
|
- proxmox_clone_lance.status | default(0) != 200
|
2026-06-24 20:17:46 -04:00
|
|
|
|
premiere VM tenant : quatre defauts leves sur le chemin
`infra-pki-01` recreee pour eprouver la chaine complete. Tout ce qui derive du
seed est exact : VMID, pont t17serv sans etiquette, adresse, passerelle, pool,
et desormais le gabarit de calcul.
1. Le pare-feu est-ouest aurait enferme Ansible : `t<idx>-srv-debian`
n'autorisait SSH que depuis la flotte du tenant, et `admin_de(nom)` etait
collecte puis jamais utilise. IPSet `t<idx>-admin` dedie + regle sourcee
dessus. Pas d'ajout a `flotte`, qui sert aussi LDAP, SQL et les metriques.
2. L'applicateur ne convergeait pas : Proxmox range `x/32` en `x`, et la
comparaison litterale laissait un ecart perpetuel. `_norm()` le ferme.
3. `cloner-vm` refusait toute VM de tenant : son garde exigeait un VLAN, vide
par construction en SDN. Accepte desormais si un pont est fourni.
4. Le clonage ignore `cores`/`memory` : la VM heritait du gabarit (2/2048) au
lieu du plan (1/1024). Une tache les repose apres le clone.
Et le troisieme verrou de D-64 : `proxmox_clone_parefeu_interface: true`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 20:33:12 -04:00
|
|
|
# L'API de CLONAGE ignore `cores` et `memory` : le clone herite du gabarit, et le
|
|
|
|
|
# plan est contredit sans un mot. Mesure du 2026-08-06 : plan 1 coeur / 1024 Mo,
|
|
|
|
|
# VM creee avec 2 / 2048 — ceux du gabarit. Il faut donc les reposer APRES le clone.
|
empreintes muettes, courses de premier demarrage, collision de noms
Quatre meta/empreinte.yml declaraient leurs valeurs A LA RACINE, sans la cle
`setops_empreinte:` : collabora, nextcloud, web_dorsal, web_frontal. Le lecteur
les voyait vides et rendait {0,0,0} — en silence. collab-01 s'est retrouvee avec
1 coeur / 1 Go pour porter Nextcloud ET Collabora, et a cesse de repondre en SSH
faute de memoire. Corrigee : 4c/5632Mo. Une garde refuse desormais cette forme.
Un fichier qui existe mais ne dit rien est pire qu'un fichier absent : le repli
aurait donne des valeurs sensees.
Deux courses de premier demarrage :
- le clone rend la main avant que son .conf existe -> attente active sur l'API ;
- le verrou dpkg frappait hors de `common_packages` -> `lock_timeout` pose en
module_defaults sur les 30 playbooks de groupe, une declaration au lieu de 30.
Au passage, j'ai failli livrer pire que le defaut : une URL coupee avec `>-`
inserait une ESPACE en son milieu. Le lint passait, la requete non.
Enfin : `proxmox_kvm` identifie une VM par son NOM. Une VM heritee homonyme lui
a fait rapporter `ok` sans rien cloner — un deploiement peut donc PARAITRE
reussi alors qu'aucune VM n'existe. Touche D-37 directement.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 16:22:38 -04:00
|
|
|
# Le clone COMPLET rend la main avant que sa configuration ne soit ecrite : sur
|
|
|
|
|
# un stockage lent, `qm clone` retourne, et le `.conf` n'existe pas encore. La
|
|
|
|
|
# tache suivante echouait alors sur « Configuration file ... does not exist ».
|
|
|
|
|
# Observe deux fois sur quinze clones (2026-08-07) — une course, pas un hasard.
|
|
|
|
|
- name: Attendre que la configuration du clone existe
|
|
|
|
|
vars:
|
|
|
|
|
# Assemblee dans une variable : un `>-` replie les retours en ESPACES, ce qui
|
|
|
|
|
# inserait une espace au milieu de l'URL. Le lint passait, la requete non.
|
|
|
|
|
proxmox_api_racine: "https://{{ proxmox_api_host_effectif }}:{{ proxmox_api_port_effectif | default(8006, true) }}"
|
|
|
|
|
ansible.builtin.uri:
|
|
|
|
|
url: "{{ proxmox_api_racine }}/api2/json/nodes/{{ proxmox_clone_noeud }}/qemu/{{ proxmox_clone_vmid | int }}/config"
|
|
|
|
|
method: GET
|
|
|
|
|
headers:
|
|
|
|
|
Authorization: >-
|
|
|
|
|
PVEAPIToken={{ proxmox_api_user_effectif }}!{{ proxmox_api_token_id_effectif }}={{ proxmox_api_token_secret_effectif }}
|
|
|
|
|
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
|
|
|
|
status_code: 200
|
|
|
|
|
register: proxmox_clone_conf
|
|
|
|
|
until: proxmox_clone_conf.status == 200
|
|
|
|
|
retries: 30
|
|
|
|
|
delay: 5
|
|
|
|
|
changed_when: false
|
portabilite : monter un SECOND tenant revele trois defauts invisibles
Les deux reconstructions de la semaine rebatissaient Chezlepro sur son propre
materiel : une preuve de reproductibilite, pas de portabilite. La vraie
epreuve est un second tenant — Technolibre, index 11, plan distinct, voute
separee, meme cluster.
1. LE CLONAGE RESOLVAIT PAR NOM. proxmox_kvm cherche une VM portant le `name`
demande ; s'il en trouve une il rend `ok` et ne clone RIEN — aucune tache
cote cluster. Or les noms courts sont VOLONTAIREMENT identiques d'un tenant a
l'autre. `backup-01` de Technolibre tombait sur celui de Chezlepro. Mesure :
id-ldap-01 et sup-01 (noms absents chez Chezlepro) passaient du premier coup,
backup-01 echouait toujours. Le clonage cible desormais par VMID, via l'API.
Deux defauts de ce correctif, trouves en le mesurant : le corps assemble en
Jinja avec `>-` rendait une CHAINE et perdait le `pool` (VM nee hors de son
pool, sans un mot) ; et l'application du gabarit de calcul expirait a 5 s,
laissant la VM aux valeurs du gabarit — 2 coeurs/2 Go au lieu du plan, en
silence. Corps en mapping YAML avec omit ; six tentatives espacees.
Et `no_log` a masque la cause au moment ou elle servait. Les deux attentes
disent maintenant ce qu'elles ont constate.
2. LE GUI DETRUISAIT DES INTRANTS. Dans ecrire_intrants, `identite` etait la
seule branche sur quatre a ecrire par-dessus le disque au lieu de fusionner.
Un enregistrement a supprime dns_amorcage et amorcage_acces_courriel. Le meme
geste sur Chezlepro aurait mange les memes cles.
3. LE VERROU DE `raser` N'ETAIT PROUVE QUE POUR UN TENANT. Le faux cluster
codait en dur les VMID de Chezlepro ; monte ailleurs, le test rendait 0 au
lieu de 2. Il fabrique desormais la collision sur le plan courant.
Verifie : prouver.py 34 OK sur Technolibre, test_raser vert sur les deux
tenants, ansible-lint production sur le playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 11:18:14 -04:00
|
|
|
failed_when: false
|
empreintes muettes, courses de premier demarrage, collision de noms
Quatre meta/empreinte.yml declaraient leurs valeurs A LA RACINE, sans la cle
`setops_empreinte:` : collabora, nextcloud, web_dorsal, web_frontal. Le lecteur
les voyait vides et rendait {0,0,0} — en silence. collab-01 s'est retrouvee avec
1 coeur / 1 Go pour porter Nextcloud ET Collabora, et a cesse de repondre en SSH
faute de memoire. Corrigee : 4c/5632Mo. Une garde refuse desormais cette forme.
Un fichier qui existe mais ne dit rien est pire qu'un fichier absent : le repli
aurait donne des valeurs sensees.
Deux courses de premier demarrage :
- le clone rend la main avant que son .conf existe -> attente active sur l'API ;
- le verrou dpkg frappait hors de `common_packages` -> `lock_timeout` pose en
module_defaults sur les 30 playbooks de groupe, une declaration au lieu de 30.
Au passage, j'ai failli livrer pire que le defaut : une URL coupee avec `>-`
inserait une ESPACE en son milieu. Le lint passait, la requete non.
Enfin : `proxmox_kvm` identifie une VM par son NOM. Une VM heritee homonyme lui
a fait rapporter `ok` sans rien cloner — un deploiement peut donc PARAITRE
reussi alors qu'aucune VM n'existe. Touche D-37 directement.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 16:22:38 -04:00
|
|
|
no_log: true
|
|
|
|
|
|
portabilite : monter un SECOND tenant revele trois defauts invisibles
Les deux reconstructions de la semaine rebatissaient Chezlepro sur son propre
materiel : une preuve de reproductibilite, pas de portabilite. La vraie
epreuve est un second tenant — Technolibre, index 11, plan distinct, voute
separee, meme cluster.
1. LE CLONAGE RESOLVAIT PAR NOM. proxmox_kvm cherche une VM portant le `name`
demande ; s'il en trouve une il rend `ok` et ne clone RIEN — aucune tache
cote cluster. Or les noms courts sont VOLONTAIREMENT identiques d'un tenant a
l'autre. `backup-01` de Technolibre tombait sur celui de Chezlepro. Mesure :
id-ldap-01 et sup-01 (noms absents chez Chezlepro) passaient du premier coup,
backup-01 echouait toujours. Le clonage cible desormais par VMID, via l'API.
Deux defauts de ce correctif, trouves en le mesurant : le corps assemble en
Jinja avec `>-` rendait une CHAINE et perdait le `pool` (VM nee hors de son
pool, sans un mot) ; et l'application du gabarit de calcul expirait a 5 s,
laissant la VM aux valeurs du gabarit — 2 coeurs/2 Go au lieu du plan, en
silence. Corps en mapping YAML avec omit ; six tentatives espacees.
Et `no_log` a masque la cause au moment ou elle servait. Les deux attentes
disent maintenant ce qu'elles ont constate.
2. LE GUI DETRUISAIT DES INTRANTS. Dans ecrire_intrants, `identite` etait la
seule branche sur quatre a ecrire par-dessus le disque au lieu de fusionner.
Un enregistrement a supprime dns_amorcage et amorcage_acces_courriel. Le meme
geste sur Chezlepro aurait mange les memes cles.
3. LE VERROU DE `raser` N'ETAIT PROUVE QUE POUR UN TENANT. Le faux cluster
codait en dur les VMID de Chezlepro ; monte ailleurs, le test rendait 0 au
lieu de 2. Il fabrique desormais la collision sur le plan courant.
Verifie : prouver.py 34 OK sur Technolibre, test_raser vert sur les deux
tenants, ansible-lint production sur le playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 11:18:14 -04:00
|
|
|
# `no_log` masque la cause au moment ou on en a besoin : le 2026-08-10, l'echec de
|
|
|
|
|
# cette attente s'est lu « the output has been hidden », et il a fallu interroger le
|
|
|
|
|
# cluster a la main pour comprendre que le clone n'avait jamais eu lieu. On rend
|
|
|
|
|
# donc le verdict lisible ici, sans reveler l'en-tete d'autorisation.
|
|
|
|
|
- name: Dire ce que l'attente du clone a constate
|
|
|
|
|
ansible.builtin.fail:
|
|
|
|
|
msg: >-
|
|
|
|
|
La configuration de {{ proxmox_clone_nom }} (VMID {{ proxmox_clone_vmid }})
|
|
|
|
|
n'existe toujours pas apres {{ 30 * 5 }} s sur {{ proxmox_clone_noeud }}.
|
|
|
|
|
Le clonage n'a donc pas abouti — verifier les taches du cluster
|
|
|
|
|
(`/cluster/tasks`) : si aucune tache de clonage n'apparait, c'est que rien
|
|
|
|
|
n'a ete demande a l'API.
|
|
|
|
|
when: proxmox_clone_conf.status | default(0) != 200
|
|
|
|
|
|
premiere VM tenant : quatre defauts leves sur le chemin
`infra-pki-01` recreee pour eprouver la chaine complete. Tout ce qui derive du
seed est exact : VMID, pont t17serv sans etiquette, adresse, passerelle, pool,
et desormais le gabarit de calcul.
1. Le pare-feu est-ouest aurait enferme Ansible : `t<idx>-srv-debian`
n'autorisait SSH que depuis la flotte du tenant, et `admin_de(nom)` etait
collecte puis jamais utilise. IPSet `t<idx>-admin` dedie + regle sourcee
dessus. Pas d'ajout a `flotte`, qui sert aussi LDAP, SQL et les metriques.
2. L'applicateur ne convergeait pas : Proxmox range `x/32` en `x`, et la
comparaison litterale laissait un ecart perpetuel. `_norm()` le ferme.
3. `cloner-vm` refusait toute VM de tenant : son garde exigeait un VLAN, vide
par construction en SDN. Accepte desormais si un pont est fourni.
4. Le clonage ignore `cores`/`memory` : la VM heritait du gabarit (2/2048) au
lieu du plan (1/1024). Une tache les repose apres le clone.
Et le troisieme verrou de D-64 : `proxmox_clone_parefeu_interface: true`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 20:33:12 -04:00
|
|
|
- name: Appliquer le gabarit de calcul du plan (le clonage ne le fait pas)
|
|
|
|
|
community.general.proxmox_kvm:
|
|
|
|
|
api_host: "{{ proxmox_api_host_effectif }}"
|
|
|
|
|
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
|
|
|
|
api_user: "{{ proxmox_api_user_effectif }}"
|
|
|
|
|
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
|
|
|
|
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
|
|
|
|
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
|
|
|
|
node: "{{ proxmox_clone_noeud }}"
|
|
|
|
|
vmid: "{{ proxmox_clone_vmid | int }}"
|
|
|
|
|
cores: "{{ proxmox_clone_coeurs | int }}"
|
|
|
|
|
memory: "{{ proxmox_clone_memoire | int }}"
|
|
|
|
|
update: true
|
portabilite : monter un SECOND tenant revele trois defauts invisibles
Les deux reconstructions de la semaine rebatissaient Chezlepro sur son propre
materiel : une preuve de reproductibilite, pas de portabilite. La vraie
epreuve est un second tenant — Technolibre, index 11, plan distinct, voute
separee, meme cluster.
1. LE CLONAGE RESOLVAIT PAR NOM. proxmox_kvm cherche une VM portant le `name`
demande ; s'il en trouve une il rend `ok` et ne clone RIEN — aucune tache
cote cluster. Or les noms courts sont VOLONTAIREMENT identiques d'un tenant a
l'autre. `backup-01` de Technolibre tombait sur celui de Chezlepro. Mesure :
id-ldap-01 et sup-01 (noms absents chez Chezlepro) passaient du premier coup,
backup-01 echouait toujours. Le clonage cible desormais par VMID, via l'API.
Deux defauts de ce correctif, trouves en le mesurant : le corps assemble en
Jinja avec `>-` rendait une CHAINE et perdait le `pool` (VM nee hors de son
pool, sans un mot) ; et l'application du gabarit de calcul expirait a 5 s,
laissant la VM aux valeurs du gabarit — 2 coeurs/2 Go au lieu du plan, en
silence. Corps en mapping YAML avec omit ; six tentatives espacees.
Et `no_log` a masque la cause au moment ou elle servait. Les deux attentes
disent maintenant ce qu'elles ont constate.
2. LE GUI DETRUISAIT DES INTRANTS. Dans ecrire_intrants, `identite` etait la
seule branche sur quatre a ecrire par-dessus le disque au lieu de fusionner.
Un enregistrement a supprime dns_amorcage et amorcage_acces_courriel. Le meme
geste sur Chezlepro aurait mange les memes cles.
3. LE VERROU DE `raser` N'ETAIT PROUVE QUE POUR UN TENANT. Le faux cluster
codait en dur les VMID de Chezlepro ; monte ailleurs, le test rendait 0 au
lieu de 2. Il fabrique desormais la collision sur le plan courant.
Verifie : prouver.py 34 OK sur Technolibre, test_raser vert sur les deux
tenants, ansible-lint production sur le playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 11:18:14 -04:00
|
|
|
# Le noeud vient de finir un clone COMPLET : son API repond encore lentement, et
|
|
|
|
|
# la lecture expire a 5 s. Mesure du 2026-08-10 : « Read timed out » sur le tout
|
|
|
|
|
# premier clone, VM creee mais laissee aux valeurs du gabarit — 2 coeurs / 2 Go au
|
|
|
|
|
# lieu du plan. Une VM sous-dimensionnee en silence est pire qu'un echec franc.
|
|
|
|
|
register: proxmox_gabarit_calcul
|
|
|
|
|
until: proxmox_gabarit_calcul is succeeded
|
|
|
|
|
retries: 6
|
|
|
|
|
delay: 10
|
premiere VM tenant : quatre defauts leves sur le chemin
`infra-pki-01` recreee pour eprouver la chaine complete. Tout ce qui derive du
seed est exact : VMID, pont t17serv sans etiquette, adresse, passerelle, pool,
et desormais le gabarit de calcul.
1. Le pare-feu est-ouest aurait enferme Ansible : `t<idx>-srv-debian`
n'autorisait SSH que depuis la flotte du tenant, et `admin_de(nom)` etait
collecte puis jamais utilise. IPSet `t<idx>-admin` dedie + regle sourcee
dessus. Pas d'ajout a `flotte`, qui sert aussi LDAP, SQL et les metriques.
2. L'applicateur ne convergeait pas : Proxmox range `x/32` en `x`, et la
comparaison litterale laissait un ecart perpetuel. `_norm()` le ferme.
3. `cloner-vm` refusait toute VM de tenant : son garde exigeait un VLAN, vide
par construction en SDN. Accepte desormais si un pont est fourni.
4. Le clonage ignore `cores`/`memory` : la VM heritait du gabarit (2/2048) au
lieu du plan (1/1024). Une tache les repose apres le clone.
Et le troisieme verrou de D-64 : `proxmox_clone_parefeu_interface: true`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 20:33:12 -04:00
|
|
|
when:
|
|
|
|
|
- proxmox_clone_coeurs is defined and proxmox_clone_coeurs | string | length > 0
|
|
|
|
|
- proxmox_clone_memoire is defined and proxmox_clone_memoire | string | length > 0
|
|
|
|
|
|
2026-06-24 20:17:46 -04:00
|
|
|
- name: Ajuster le reseau de la VM clonee
|
|
|
|
|
community.general.proxmox_nic:
|
|
|
|
|
api_host: "{{ proxmox_api_host_effectif }}"
|
|
|
|
|
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
|
|
|
|
api_user: "{{ proxmox_api_user_effectif }}"
|
|
|
|
|
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
|
|
|
|
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
|
|
|
|
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
|
|
|
|
vmid: "{{ proxmox_clone_vmid | int }}"
|
|
|
|
|
interface: "{{ proxmox_clone_interface | default('net0') }}"
|
|
|
|
|
model: virtio
|
|
|
|
|
bridge: "{{ proxmox_clone_pont }}"
|
|
|
|
|
tag: "{{ proxmox_clone_vlan | int if proxmox_clone_vlan is defined and proxmox_clone_vlan | string | length > 0 else omit }}"
|
|
|
|
|
firewall: "{{ proxmox_clone_parefeu_interface | default(false) | bool }}"
|
mtu : le 1450 de la zone n'atteignait pas les invites
Question de l'exploitant : « je ne vois nulle part un MTU a 1450 ». Fondee.
Le 1450 existait bien — MTU_OVERLAY_DEFAUT dans underlay.py, dont P23 derive
sa garde (transport >= overlay + 50), pose par le devis SDN sur chaque zone,
et verifie sur le cluster. Mais il ne se propage pas a la carte de l'invite :
les 14 VM tournaient a 1500, et cloner_vm_debian.yml n'avait aucune occurrence
de `mtu`. La VM emettait donc des trames que son propre chemin ne pouvait pas
encapsuler — la panne que le registre des flux appelle « la plus couteuse a
diagnostiquer ».
Invisible parce que les 14 VM vivent sur asgard : deux VM du meme hyperviseur
communiquent par le pont local, sans encapsulation. Le defaut serait apparu au
premier eclatement de la flotte — c'est-a-dire quand il faudra heberger les
deux tenants ensemble.
Corrige en DEUX endroits, avec `mtu=1` (« herite du pont ») plutot que 1450 en
dur : juste en SDN comme hors SDN, et encore juste le jour des trames jumbo.
Le GABARIT le porte — proposition de l'exploitant, meilleure que la mienne :
elle couvre les clonages qui ne passent pas par le playbook. Et le playbook le
repose a chaque clone, parce qu'un gabarit se RECAPTURE et que ce qui n'est pas
versionne se perd en silence.
make mtu-mesurer (7e devis) rattache chaque hote a sa zone par son pont derive
et lit le MTU attendu dans devis_sdn.py. Ecart trouve sur 14 hotes du premier
coup, puis confirme corrige.
ET J'AI CASSE LA FLOTTE EN CORRIGEANT : applique aux cartes de VM EN MARCHE,
le changement detache et rebranche la carte sans que l'invite reconfigure son
interface. 0/14 joignables pendant trois minutes. L'agent qemu, independant du
reseau invite, a permis de constater et de redemarrer par l'API. Dans le
playbook la meme tache s'execute AVANT le demarrage du clone : aucun risque.
Sur une VM en service : poser la config, puis redemarrer — une seule d'abord.
Verifie : mtu-mesurer CONFORME 14/14 a 1450, prouver.py 35 OK, ansible-lint
production.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 17:54:03 -04:00
|
|
|
# `mtu: 1` est la valeur Proxmox pour « HERITE DU PONT » (virtio uniquement).
|
|
|
|
|
#
|
|
|
|
|
# Sans elle, l'invite nait a 1500 quel que soit le pont. En SDN EVPN la zone est a
|
|
|
|
|
# 1450 (VXLAN coute 50 octets) : la VM emet donc des trames que son propre chemin
|
|
|
|
|
# ne peut pas encapsuler. Ca ne casse pas franchement — la connexion s'etablit,
|
|
|
|
|
# les petites requetes passent, les grosses reponses restent suspendues. C'est
|
|
|
|
|
# exactement la panne que le registre des flux decrit comme « la plus couteuse a
|
|
|
|
|
# diagnostiquer », et pourquoi il declare l'ICMP « fragmentation necessaire ».
|
|
|
|
|
#
|
|
|
|
|
# Mesure du 2026-08-10 : zones `t11` et `t17` a 1450 sur le cluster, les 14 invites
|
|
|
|
|
# a 1500. Invisible tant que toutes les VM vivent sur le MEME hyperviseur — elles
|
|
|
|
|
# communiquent alors par le pont local, sans encapsulation. La panne apparaitrait
|
|
|
|
|
# au premier eclatement de la flotte sur plusieurs noeuds.
|
|
|
|
|
#
|
|
|
|
|
# HERITER plutot qu'ecrire 1450 : hors SDN le pont est a 1500 et la VM suit. Une
|
|
|
|
|
# seule source de verite — celle du pont auquel elle est reellement attachee — et
|
|
|
|
|
# la valeur reste juste le jour ou la fabric passera aux trames jumbo.
|
|
|
|
|
#
|
|
|
|
|
# Le GABARIT le porte aussi (`net0 ... mtu=1`), ce qui couvre les clonages qui ne
|
|
|
|
|
# passent pas par ce playbook — un clone fait a la main dans l'interface, par
|
|
|
|
|
# exemple. On garde neanmoins la ligne ici : un gabarit se RECAPTURE (fait le
|
|
|
|
|
# 2026-08-09), et ce qui n'est pas versionne se perd en silence. Le playbook est
|
|
|
|
|
# la garantie qui survit a la recapture ; `make mtu-mesurer` verifie le resultat.
|
|
|
|
|
#
|
|
|
|
|
# Cette tache s'execute AVANT le demarrage du clone (voir « Demarrer le clone »
|
|
|
|
|
# plus bas) : aucun risque de rebranchement a chaud. Applique a une VM EN MARCHE,
|
|
|
|
|
# le meme changement detache et rebranche la carte sans que l'invite reconfigure
|
|
|
|
|
# son interface — 14 machines coupees d'un coup le 2026-08-10, et il a fallu les
|
|
|
|
|
# redemarrer. Sur une VM deja en service : poser la config, puis redemarrer.
|
|
|
|
|
mtu: 1
|
2026-06-24 20:17:46 -04:00
|
|
|
state: present
|
|
|
|
|
when:
|
|
|
|
|
- proxmox_clone_pont is defined
|
|
|
|
|
- proxmox_clone_pont | length > 0
|
|
|
|
|
|
2026-07-01 15:38:42 -04:00
|
|
|
- name: Agrandir le disque principal du clone (grow-only)
|
2026-06-24 20:17:46 -04:00
|
|
|
community.general.proxmox_disk:
|
|
|
|
|
api_host: "{{ proxmox_api_host_effectif }}"
|
|
|
|
|
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
|
|
|
|
api_user: "{{ proxmox_api_user_effectif }}"
|
|
|
|
|
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
|
|
|
|
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
|
|
|
|
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
|
|
|
|
vmid: "{{ proxmox_clone_vmid | int }}"
|
|
|
|
|
disk: "{{ proxmox_clone_disque | default('scsi0') }}"
|
|
|
|
|
size: "{{ proxmox_clone_taille_disque }}"
|
|
|
|
|
state: resized
|
2026-07-01 15:38:42 -04:00
|
|
|
register: proxmox_redim_disque
|
|
|
|
|
# Le disque derive du plan est un MINIMUM. Si le template est deja plus grand,
|
|
|
|
|
# Proxmox refuse de retrecir : on tolere ce cas (grow-only), la VM garde le
|
|
|
|
|
# disque du template. Toute autre erreur reste bloquante.
|
|
|
|
|
failed_when:
|
|
|
|
|
- proxmox_redim_disque is failed
|
|
|
|
|
- "'shrinking disks is not supported' not in (proxmox_redim_disque.msg | default(''))"
|
2026-06-24 20:17:46 -04:00
|
|
|
when:
|
|
|
|
|
- proxmox_clone_taille_disque is defined
|
|
|
|
|
- proxmox_clone_taille_disque | length > 0
|
|
|
|
|
|
|
|
|
|
- name: Configurer Cloud-Init sur le clone
|
|
|
|
|
community.general.proxmox_kvm:
|
|
|
|
|
api_host: "{{ proxmox_api_host_effectif }}"
|
|
|
|
|
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
|
|
|
|
api_user: "{{ proxmox_api_user_effectif }}"
|
|
|
|
|
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
|
|
|
|
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
|
|
|
|
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
|
|
|
|
node: "{{ proxmox_clone_noeud }}"
|
|
|
|
|
vmid: "{{ proxmox_clone_vmid | int }}"
|
|
|
|
|
name: "{{ proxmox_clone_nom }}"
|
|
|
|
|
update: true
|
|
|
|
|
ciuser: "{{ proxmox_clone_ciuser | default(omit, true) }}"
|
|
|
|
|
sshkeys: "{{ proxmox_clone_sshkeys | default(omit) }}"
|
|
|
|
|
ipconfig:
|
|
|
|
|
ipconfig0: "{{ proxmox_clone_ipconfig0 }}"
|
|
|
|
|
nameservers: "{{ proxmox_clone_nameservers if proxmox_clone_nameservers | length > 0 else omit }}"
|
|
|
|
|
searchdomains: "{{ proxmox_clone_domaines_recherche | default(omit, true) }}"
|
|
|
|
|
agent: "enabled=1"
|
|
|
|
|
|
|
|
|
|
- name: Demarrer le clone
|
|
|
|
|
community.general.proxmox_kvm:
|
|
|
|
|
api_host: "{{ proxmox_api_host_effectif }}"
|
|
|
|
|
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
|
|
|
|
|
api_user: "{{ proxmox_api_user_effectif }}"
|
|
|
|
|
api_token_id: "{{ proxmox_api_token_id_effectif }}"
|
|
|
|
|
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
|
|
|
|
|
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
|
|
|
|
|
node: "{{ proxmox_clone_noeud }}"
|
|
|
|
|
vmid: "{{ proxmox_clone_vmid | int }}"
|
|
|
|
|
name: "{{ proxmox_clone_nom }}"
|
|
|
|
|
state: started
|
|
|
|
|
when: proxmox_clone_demarrer | default(true) | bool
|
|
|
|
|
|
|
|
|
|
- name: Afficher le resume du clone
|
|
|
|
|
ansible.builtin.debug:
|
|
|
|
|
msg:
|
|
|
|
|
- "VM creee: {{ proxmox_clone_nom }} (VMID {{ proxmox_clone_vmid }})"
|
pools Proxmox : un par tenant, dérivé de l'index (D-37, P28)
Onze des quatorze serveurs portent le même nom court chez Chezlepro et chez
Technolibre. Vérifié un par un, ce n'est pas un problème technique : tout le
reste dérive du seed et diverge (10.27.19.21 contre 10.21.19.21, VMID 117402101
contre 111402101, VLAN 1174 contre 1114, deux domaines internes), et rien n'est
indexé sur le nom court — les opérations Proxmox portent toutes un vmid, les
certificats un FQDN, et client_backup_repo vise backup-01.{{ domaine_interne }}.
Le coût est humain : la console Proxmox affiche le nom, et deux infra-pki-01 y
sont indiscernables à l'œil. Le VMID porte le tenant, encore faut-il connaître le
codage.
Un pool par tenant, dérivé du dossier d'instance et de l'index — déjà unique par
P21, donc aucun registre de plus : Chezlepro-17, Technolibre-11.
make devis-proxmox-pools rattrape la flotte existante (création du pool, puis
affectation des VM actives). Les VM créées ensuite entrent d'elles-mêmes :
make creer-vm dérive le pool par la même fonction et le passe à la création. Le
playbook crée le pool au préalable — proxmox_kvm échoue sur un pool inconnu, et
l'API ne sait pas changer le pool d'une VM existante ; c'est aussi pourquoi le
rattrapage passe par les membres.
P28 garde deux collisions : même nom de pool entre tenants, et surtout même VMID
— une machine appartenant à deux tenants serait pire qu'une homonymie.
Rien n'est renommé : les homonymes sont la preuve que la nomenclature est un vrai
gabarit. Le devis ne lit pas le cluster, il dit l'état cible et non l'écart.
27 preuves OK, 0 échec. --syntax-check du playbook de clonage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:15:14 -04:00
|
|
|
- "Pool: {{ proxmox_clone_pool | default('(aucun)', true) }}"
|
2026-06-24 20:17:46 -04:00
|
|
|
- "Cloud-Init ipconfig0: {{ proxmox_clone_ipconfig0 }}"
|