Set-OPS-Public/playbooks/proxmox/cloner_vm_debian.yml

746 lines
41 KiB
YAML
Raw Normal View History

---
- name: Cloner une VM Debian depuis le modele Proxmox
hosts: localhost
connection: local
gather_facts: false
become: false
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"
voutes : le monde physique a la sienne, les tenants n'en portent plus les cles Demande de l'exploitant : « l'underlay et son tenant doivent avoir chacun sa voute ». CE QUI ETAIT FAUX. Le jeton d'API du cluster et la cle d'API de la frontiere vivaient dans la voute de CHAQUE tenant. Patient 0 a du les recopier pour exister. Consequence : on ne pouvait plus revoquer l'acces d'un locataire sans le revoquer pour tous — la faute des neuf copies, appliquee aux secrets. CE QUI EST POSE : - `underlay.vault.yml`, chez l'hebergeur, a cote d'underlay.yml. Quatre secrets deplaces (jeton Proxmox, cle et secret d'API OPNsense), retires des deux voutes de tenants. - `proxmox_api.voute()` lit l'underlay APRES le tenant, donc l'hebergeur fait foi ; un site non encore migre continue de fonctionner sur son ancienne voute. - `appliquer_opnsense._voute()` n'a plus sa propre lecture : elle appelle celle du cluster. La reecrire aurait fait une dixieme copie le jour ou l'on refermait les neuf autres. - Le playbook de clonage charge la voute de l'underlay APRES celle du tenant, par la meme derivation que proxmox-hebergeur.yml : le symlink designe deja l'hebergeur. - `voute.py` sait que ces secrets ne sont plus attendus chez un tenant (P18). EPROUVE SUR LE REEL, apres retrait des cles chez les deux tenants : l'API du cluster repond, le devis de placement est conforme pour les deux ecosystemes, le devis de frontiere se genere (86 objets), le SDN est convergent. UNE PRECAUTION APPRISE EN CHEMIN : le premier essai a ecrit la voute EN CLAIR avant de la chiffrer, et le chiffrement a echoue — il a fallu detruire le fichier. La sequence est desormais l'inverse : chiffrer dans un dossier de travail, ne deposer que le resultat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 17:13:48 -04:00
# LA VOUTE DE L'UNDERLAY — les secrets du MONDE PHYSIQUE (jeton d'API du cluster).
# Meme derivation que le fichier ci-dessus : le symlink `underlay.yml` designe deja
# l'hebergeur, on n'a rien a redeclarer. Depuis le 2026-08-22 ces secrets ne vivent
# plus dans la voute d'un tenant : recopies chez chacun, ils ne pouvaient plus etre
# revoques isolement (docs/frontiere-physique-virtuel.md).
setops_voute_underlay: "{{ (playbook_dir ~ '/../../underlay.yml') | realpath | dirname }}/underlay.vault.yml"
tasks:
- name: Verifier les fichiers de variables Proxmox optionnels
ansible.builtin.stat:
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 }}"
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
voutes : le monde physique a la sienne, les tenants n'en portent plus les cles Demande de l'exploitant : « l'underlay et son tenant doivent avoir chacun sa voute ». CE QUI ETAIT FAUX. Le jeton d'API du cluster et la cle d'API de la frontiere vivaient dans la voute de CHAQUE tenant. Patient 0 a du les recopier pour exister. Consequence : on ne pouvait plus revoquer l'acces d'un locataire sans le revoquer pour tous — la faute des neuf copies, appliquee aux secrets. CE QUI EST POSE : - `underlay.vault.yml`, chez l'hebergeur, a cote d'underlay.yml. Quatre secrets deplaces (jeton Proxmox, cle et secret d'API OPNsense), retires des deux voutes de tenants. - `proxmox_api.voute()` lit l'underlay APRES le tenant, donc l'hebergeur fait foi ; un site non encore migre continue de fonctionner sur son ancienne voute. - `appliquer_opnsense._voute()` n'a plus sa propre lecture : elle appelle celle du cluster. La reecrire aurait fait une dixieme copie le jour ou l'on refermait les neuf autres. - Le playbook de clonage charge la voute de l'underlay APRES celle du tenant, par la meme derivation que proxmox-hebergeur.yml : le symlink designe deja l'hebergeur. - `voute.py` sait que ces secrets ne sont plus attendus chez un tenant (P18). EPROUVE SUR LE REEL, apres retrait des cles chez les deux tenants : l'API du cluster repond, le devis de placement est conforme pour les deux ecosystemes, le devis de frontiere se genere (86 objets), le SDN est convergent. UNE PRECAUTION APPRISE EN CHEMIN : le premier essai a ecrit la voute EN CLAIR avant de la chiffrer, et le chiffrement a echoue — il a fallu detruire le fichier. La sequence est desormais l'inverse : chiffrer dans un dossier de travail, ne deposer que le resultat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 17:13:48 -04:00
- name: Verifier la voute de l'underlay
ansible.builtin.stat:
path: "{{ setops_voute_underlay }}"
register: proxmox_voute_underlay
- name: Charger les secrets Proxmox Vault
block:
- name: Charger les secrets de la voute Proxmox
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
voutes : le monde physique a la sienne, les tenants n'en portent plus les cles Demande de l'exploitant : « l'underlay et son tenant doivent avoir chacun sa voute ». CE QUI ETAIT FAUX. Le jeton d'API du cluster et la cle d'API de la frontiere vivaient dans la voute de CHAQUE tenant. Patient 0 a du les recopier pour exister. Consequence : on ne pouvait plus revoquer l'acces d'un locataire sans le revoquer pour tous — la faute des neuf copies, appliquee aux secrets. CE QUI EST POSE : - `underlay.vault.yml`, chez l'hebergeur, a cote d'underlay.yml. Quatre secrets deplaces (jeton Proxmox, cle et secret d'API OPNsense), retires des deux voutes de tenants. - `proxmox_api.voute()` lit l'underlay APRES le tenant, donc l'hebergeur fait foi ; un site non encore migre continue de fonctionner sur son ancienne voute. - `appliquer_opnsense._voute()` n'a plus sa propre lecture : elle appelle celle du cluster. La reecrire aurait fait une dixieme copie le jour ou l'on refermait les neuf autres. - Le playbook de clonage charge la voute de l'underlay APRES celle du tenant, par la meme derivation que proxmox-hebergeur.yml : le symlink designe deja l'hebergeur. - `voute.py` sait que ces secrets ne sont plus attendus chez un tenant (P18). EPROUVE SUR LE REEL, apres retrait des cles chez les deux tenants : l'API du cluster repond, le devis de placement est conforme pour les deux ecosystemes, le devis de frontiere se genere (86 objets), le SDN est convergent. UNE PRECAUTION APPRISE EN CHEMIN : le premier essai a ecrit la voute EN CLAIR avant de la chiffrer, et le chiffrement a echoue — il a fallu detruire le fichier. La sequence est desormais l'inverse : chiffrer dans un dossier de travail, ne deposer que le resultat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 17:13:48 -04:00
# APRES celle du tenant, donc elle GAGNE : quand les deux portent la cle, c'est
# celle de l'hebergeur qui fait foi. C'est le sens de la frontiere entre les deux
# mondes, et ca rend la migration d'un site sans coupure.
- name: Charger les secrets du monde physique (voute de l'underlay)
ansible.builtin.include_vars:
file: "{{ setops_voute_underlay }}"
when: proxmox_voute_underlay.stat.exists
no_log: true
rescue:
- name: Expliquer l echec de chargement du Vault
ansible.builtin.fail:
msg: >-
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.
- 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 }}"
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
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."
- 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
insemination : la cle d'amorcage, bornee au meme groupe que le flux Le flux etait declare et applique ; il manquait l'IDENTITE. Le runner du SITE atteignait la porte de ops-01 sans avoir de cle. LE PIEGE COMPTE PLUS QUE LE CORRECTIF. La cle s'injecte au CLONAGE, et `creer-vm` cree TOUTES les machines d'un tenant. L'injecter a chaque clonage aurait donne a l'hebergeur un acces SSH a la flotte entiere de chaque locataire, en silence — ca aurait defait a la couche IDENTITE ce que le pare-feu venait de borner a la couche RESEAU. Le meme critere gouverne donc les deux : porter `serveur_ops_tenant`. `SETOPS_CLES_AMORCAGE` est vide partout ailleurs. Elle S'AJOUTE a celle de l'exploitant, elle ne la remplace pas : c'est l'humain qui arme. La cle vient du PLAN DU SITE, pas du disque local : materialiser depuis le poste et depuis le runner doit produire la meme VM. DEUX COUCHES MANGEAIENT LES ESPACES. Proxmox rendait `SSH public key validation error` — message muet sur la cause. Isole par un CONTROLE (rejouer sans la cle : la tache passe), puis par la mesure de ce qui arrivait au module : "sshkeys": "ssh-ed25519" <- le premier mot, rien d'autre J'ai accuse `make` d'abord ; c'etait `ansible-playbook -e cle=valeur`, qui decoupe AU SHLEX. D'ou l'environnement pour le transport et `-e '{...}'` en JSON pour l'entree. (Un scalaire YAML plie ne produit pas non plus de saut de ligne.) TROIS TESTS QUI NE GARDAIENT RIEN. `test_inventory_host` inscrit ses tests dans une liste explicite ; mes deux nouveaux n'y etaient pas — definis, jamais joues. La garde d'exhaustivite ajoutee en a trouve un TROISIEME le jour meme, `test_etiquette_vlan_repli_et_vide_explicite`, jamais inscrit depuis sa creation : inscrit, il levait un KeyError sur une fixture qu'il lisait mal. Un test non inscrit est pire qu'un test absent : on croit l'avoir. PREUVE, AVEC SON CONTROLE NEGATIF : runner du SITE -> ops-01 ops-01 10.17.19.41/24 entre runner du SITE -> 10.17.19.21 Connection timed out refuse make verifier : vert. make prouver : CONFORME, 52 OK, 0 echec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-28 13:45:44 -04:00
# LA CLE D'AMORCAGE S'AJOUTE, ELLE NE REMPLACE PAS.
#
# L'exploitant doit garder son acces a la machine qu'il vient de creer : ecraser sa
# cle par celle du runner rendrait le runner seul maitre du terrain, ce qui est
# exactement l'inverse de la doctrine (c'est l'humain qui arme, cf.
# docs/filiation-emancipation.md). Deux cles, deux lignes, et cloud-init les pose
# toutes les deux.
#
# Elle n'arrive ICI que pour le runner d'un tenant : `inventory_host.py` la rend vide
# partout ailleurs, et cette tache ne se declenche donc pas.
#
# DEUX PIEGES, MESURES LE 2026-08-28, ET LE MEME MESSAGE POUR LES DEUX :
# 500 Internal Server Error: SSH public key validation error
#
# 1. LE SEPARATEUR. Un scalaire PLIE (`>-`) ne produit pas de saut de ligne : `'\n'`
# y reste deux caracteres, une barre et un « n ». Seul le scalaire YAML entre
# GUILLEMETS le convertit avant que Jinja ne le lise (verifie en base64 sur les
# trois formes candidates).
#
# 2. LA LISTE PEUT N'AVOIR QU'UN ELEMENT — et c'est le cas courant. Sur cette flotte,
# `CLE_SSH_PUBLIQUE` est vide : la cle de l'exploitant vient du GABARIT DORE, pas
# de cloud-init. Une simple concatenation rendait donc une valeur commencant par
# un saut de ligne, que Proxmox refuse tout autant. On assemble une LISTE et on
# laisse `select` retirer les vides — la forme est juste qu'il y ait zero, une ou
# deux cles.
- name: Ajouter la cle d'amorcage du runner de SITE (insemination)
ansible.builtin.set_fact:
proxmox_clone_sshkeys: "{{ [proxmox_clone_sshkeys | default('') | trim, proxmox_clone_cles_amorcage | trim] | select | join('\n') }}"
when:
- proxmox_clone_cles_amorcage is defined
- proxmox_clone_cles_amorcage | 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 }}
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 }}
clonage : quatre defauts que la premiere VM placee hors du noeud du gabarit a reveles Toutes les VM naissaient sur le noeud du gabarit puis migraient. site-cache-01 est la premiere a etre placee ailleurs, et elle a fait tomber quatre defauts enchaines : 1. l'URL de clonage visait le noeud de DESTINATION, alors que l'API veut celui qui DETIENT le gabarit ; la destination se dit par `target`. D'ou un 500 sur tout clonage inter-noeuds. Le noeud du gabarit se decouvre dans l'inventaire du cluster. 2. ce 500 etait avale par failed_when: false + no_log: true. Une assertion le releve desormais, message de l'API compris. 3. l'attente interrogeait nodes/<destination>/tasks/<UPID> ; la tache vit sur le noeud du gabarit. 60 tentatives x 10 s pour un clonage termine en 87 s, puis la suite qui reprend sans un mot. Le noeud se lit dans l'UPID lui-meme. 4. la garde d'apres-attente retombait sur `exitstatus | default('OK')` : une attente qui n'avait rien observe passait pour un succes. Elle exige d'avoir VU la tache s'arreter. Et une taille de disque porte toujours son unite : `disque: 40` etait lu comme un retrecissement, erreur toleree a raison — la machine naissait donc avec les 16 Go du gabarit au lieu de 40, sans que rien ne le dise. Le site declare enfin ce qu'il materialise (materialisation: gabarit, stockage, clone_complet) au lieu de l'heriter du tenant actif. Chrono : clonage reel 59 s et 87 s pour 3,3 Gio alloues. 42 preuves vertes, ansible-lint profil production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 02:44:21 -04:00
# OU VIT LE GABARIT — et ce n'est pas forcement ou va la VM (2026-08-25).
#
# L'URL de clonage visait `nodes/{{ proxmox_clone_noeud }}`, c'est-a-dire le noeud de
# DESTINATION. Or l'API veut le noeud qui DETIENT le modele ; la destination se dit
# par `target`. Cloner vers gandalf un gabarit qui vit sur asgard rendait donc :
#
# 500 Configuration file 'nodes/gandalf/qemu-server/99998.conf' does not exist
#
# Ca n'etait jamais apparu parce que toutes les VM naissaient sur le noeud du gabarit
# et migraient ensuite. La premiere machine placee ailleurs l'a revele.
- name: Localiser le noeud qui detient le gabarit
ansible.builtin.set_fact:
proxmox_noeud_gabarit: >-
{{ ((proxmox_inventaire_vm.json.data | default([]))
| selectattr('vmid', 'defined')
| selectattr('vmid', 'equalto', proxmox_clone_vmid_modele | int)
| map(attribute='node') | list | first) | default('', true) }}
- name: "Un noeud de la grappe detient bien le gabarit {{ proxmox_clone_vmid_modele }}"
ansible.builtin.assert:
that: proxmox_noeud_gabarit | length > 0
fail_msg: >-
Aucun noeud de la grappe ne detient le gabarit
{{ proxmox_clone_vmid_modele }}. Sans lui, rien a cloner.
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
- 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:
clonage : quatre defauts que la premiere VM placee hors du noeud du gabarit a reveles Toutes les VM naissaient sur le noeud du gabarit puis migraient. site-cache-01 est la premiere a etre placee ailleurs, et elle a fait tomber quatre defauts enchaines : 1. l'URL de clonage visait le noeud de DESTINATION, alors que l'API veut celui qui DETIENT le gabarit ; la destination se dit par `target`. D'ou un 500 sur tout clonage inter-noeuds. Le noeud du gabarit se decouvre dans l'inventaire du cluster. 2. ce 500 etait avale par failed_when: false + no_log: true. Une assertion le releve desormais, message de l'API compris. 3. l'attente interrogeait nodes/<destination>/tasks/<UPID> ; la tache vit sur le noeud du gabarit. 60 tentatives x 10 s pour un clonage termine en 87 s, puis la suite qui reprend sans un mot. Le noeud se lit dans l'UPID lui-meme. 4. la garde d'apres-attente retombait sur `exitstatus | default('OK')` : une attente qui n'avait rien observe passait pour un succes. Elle exige d'avoir VU la tache s'arreter. Et une taille de disque porte toujours son unite : `disque: 40` etait lu comme un retrecissement, erreur toleree a raison — la machine naissait donc avec les 16 Go du gabarit au lieu de 40, sans que rien ne le dise. Le site declare enfin ce qu'il materialise (materialisation: gabarit, stockage, clone_complet) au lieu de l'heriter du tenant actif. Chrono : clonage reel 59 s et 87 s pour 3,3 Gio alloues. 42 preuves vertes, ansible-lint profil production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 02:44:21 -04:00
url: "{{ proxmox_api_racine }}/api2/json/nodes/{{ proxmox_noeud_gabarit }}/qemu/{{ proxmox_clone_vmid_modele | int }}/clone"
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
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) }}"
clonage : quatre defauts que la premiere VM placee hors du noeud du gabarit a reveles Toutes les VM naissaient sur le noeud du gabarit puis migraient. site-cache-01 est la premiere a etre placee ailleurs, et elle a fait tomber quatre defauts enchaines : 1. l'URL de clonage visait le noeud de DESTINATION, alors que l'API veut celui qui DETIENT le gabarit ; la destination se dit par `target`. D'ou un 500 sur tout clonage inter-noeuds. Le noeud du gabarit se decouvre dans l'inventaire du cluster. 2. ce 500 etait avale par failed_when: false + no_log: true. Une assertion le releve desormais, message de l'API compris. 3. l'attente interrogeait nodes/<destination>/tasks/<UPID> ; la tache vit sur le noeud du gabarit. 60 tentatives x 10 s pour un clonage termine en 87 s, puis la suite qui reprend sans un mot. Le noeud se lit dans l'UPID lui-meme. 4. la garde d'apres-attente retombait sur `exitstatus | default('OK')` : une attente qui n'avait rien observe passait pour un succes. Elle exige d'avoir VU la tache s'arreter. Et une taille de disque porte toujours son unite : `disque: 40` etait lu comme un retrecissement, erreur toleree a raison — la machine naissait donc avec les 16 Go du gabarit au lieu de 40, sans que rien ne le dise. Le site declare enfin ce qu'il materialise (materialisation: gabarit, stockage, clone_complet) au lieu de l'heriter du tenant actif. Chrono : clonage reel 59 s et 87 s pour 3,3 Gio alloues. 42 preuves vertes, ansible-lint profil production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 02:44:21 -04:00
# LA DESTINATION SE DIT ICI, pas dans l'URL. Omise quand elle coincide avec le
# noeud du gabarit : l'API refuse un `target` inutile sur stockage non partage.
target: "{{ proxmox_clone_noeud if proxmox_clone_noeud != proxmox_noeud_gabarit else omit }}"
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
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
clonage : quatre defauts que la premiere VM placee hors du noeud du gabarit a reveles Toutes les VM naissaient sur le noeud du gabarit puis migraient. site-cache-01 est la premiere a etre placee ailleurs, et elle a fait tomber quatre defauts enchaines : 1. l'URL de clonage visait le noeud de DESTINATION, alors que l'API veut celui qui DETIENT le gabarit ; la destination se dit par `target`. D'ou un 500 sur tout clonage inter-noeuds. Le noeud du gabarit se decouvre dans l'inventaire du cluster. 2. ce 500 etait avale par failed_when: false + no_log: true. Une assertion le releve desormais, message de l'API compris. 3. l'attente interrogeait nodes/<destination>/tasks/<UPID> ; la tache vit sur le noeud du gabarit. 60 tentatives x 10 s pour un clonage termine en 87 s, puis la suite qui reprend sans un mot. Le noeud se lit dans l'UPID lui-meme. 4. la garde d'apres-attente retombait sur `exitstatus | default('OK')` : une attente qui n'avait rien observe passait pour un succes. Elle exige d'avoir VU la tache s'arreter. Et une taille de disque porte toujours son unite : `disque: 40` etait lu comme un retrecissement, erreur toleree a raison — la machine naissait donc avec les 16 Go du gabarit au lieu de 40, sans que rien ne le dise. Le site declare enfin ce qu'il materialise (materialisation: gabarit, stockage, clone_complet) au lieu de l'heriter du tenant actif. Chrono : clonage reel 59 s et 87 s pour 3,3 Gio alloues. 42 preuves vertes, ansible-lint profil production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 02:44:21 -04:00
# L'ECHEC DU POST ETAIT AVALE. `failed_when: false` protege du 500 « VMID deja pris »,
# mais il avalait AUSSI un refus reel — et la tache d'attente qui suit se bloquait
# alors sur un clonage qui n'avait jamais commence. Un blocage sans message, la panne
# la plus chere a diagnostiquer. On garde le masque pour l'en-tete, et on releve
# l'echec ici, message de l'API compris.
- name: "Le clonage a bien ete accepte par l'API"
ansible.builtin.assert:
that: (proxmox_clone_lance.status | default(0)) == 200
fail_msg: >-
L'API a refuse le clonage
({{ proxmox_clone_lance.status | default('aucune reponse') }}) :
{{ proxmox_clone_lance.json.message
| default(proxmox_clone_lance.msg | default('sans message')) }}
— gabarit {{ proxmox_clone_vmid_modele }} sur « {{ proxmox_noeud_gabarit }} »,
destination « {{ proxmox_clone_noeud }} ».
when: not proxmox_clone_deja_la
clonage : attendre la fin reelle de la tache, pas seulement l'avoir demandee Mon optimisation d'hier soir a mis le cluster a genoux, et la faute est entiere. En remplacant proxmox_kvm par un appel d'API direct, j'ai perdu ce que le module faisait pour moi : attendre la fin de la tache (timeout: 600). `POST .../clone` rend un UPID et la main immediatement ; Proxmox copie le disque en tache de fond. En sequentiel ca ne se voyait pas — l'attente de SSH absorbait le delai. En PARALLELE, `make creer-vm` rendait la main pendant la copie : la limite de concurrence ne retenait plus que des PROCESSUS VIDES et les clones s'empilaient. Mesure : limite a 4, QUATORZE copies integrales simultanees. Le symptome trompait — CPU de l'hyperviseur a 2 %, RAM a 13/62, et tout ramait. Ce n'etait pas la machine, c'etait TrueNAS. Les 14 hotes ont echoue, et flotte-creer a refuse de continuer : la garde ajoutee le matin meme a fait son travail. Le clonage relit desormais l'UPID et interroge l'etat de la tache jusqu'a `stopped`, avec un message clair si la sortie n'est pas OK. La limite de concurrence retrouve un sens : quatre clones REELS, pas quatre coquilles. NOTE, PAS ENCORE CORRIGE : `raser` a le meme defaut dans l'autre sens. Il a rapporte « 6/6 VM detruites » alors que les six etaient la — l'API accepte le DELETE, rend un UPID, et la tache echoue ensuite sur « VM is locked (clone) ». raser ne lit que la reponse immediate, jamais le resultat. MESURE DES OPTIMISATIONS, reconstruction complete avec gabarit sur CephNVMe (proposition de l'exploitant) et concurrence a 3 : 54 min 02 s contre 1 h 12 min 48 s — 26 % de moins, zero echec, 2838 taches. Le cache d'artefacts est le plus rentable : Nextcloud 10m55s -> 4m14s, Forgejo 3m42s -> 1m33s, verifie par les `skipping` du journal. J'avais annonce un gain « limite a la part telechargement » — cette part etait bien plus grosse que je ne le croyais. forks=20 : les couches larges 3m09s -> 1m37s. Gabarit NVMe + 3 clones : 1m35s -> 1m17s par VM, le plus modeste, car les disques ECRIVENT toujours sur TrueNAS. Verifie : 3 clones actifs jamais plus, 7 devis CONFORME, ansible-lint production, prouver.py 35 OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 22:38:42 -04:00
# ATTENDRE QUE LE CLONE SOIT REELLEMENT FINI, pas seulement demande.
#
# `POST .../clone` rend un UPID et la main IMMEDIATEMENT : Proxmox copie le disque en
# tache de fond. `proxmox_kvm`, qu'on a remplace, attendait la fin (`timeout: 600`) — la
# reecriture par API avait perdu cette attente sans que ca se voie, parce qu'en
# sequentiel l'attente de SSH qui suit l'absorbait.
#
# En PARALLELE, c'est tout autre chose : `make creer-vm` rend la main pendant que le
# disque se copie, la limite de concurrence ne retient plus que des processus vides, et
# les clones s'empilent. Mesure du 2026-08-10 : limite a 4, DOUZE copies integrales du
# gabarit simultanees sur le meme stockage. Le CPU de l'hyperviseur a 2 %, et pourtant
# tout ramait — ce n'etait pas la machine, c'etait TrueNAS.
- name: Attendre la fin reelle du clonage
vars:
proxmox_api_racine: "https://{{ proxmox_api_host_effectif }}:{{ proxmox_api_port_effectif | default(8006, true) }}"
ansible.builtin.uri:
clonage : quatre defauts que la premiere VM placee hors du noeud du gabarit a reveles Toutes les VM naissaient sur le noeud du gabarit puis migraient. site-cache-01 est la premiere a etre placee ailleurs, et elle a fait tomber quatre defauts enchaines : 1. l'URL de clonage visait le noeud de DESTINATION, alors que l'API veut celui qui DETIENT le gabarit ; la destination se dit par `target`. D'ou un 500 sur tout clonage inter-noeuds. Le noeud du gabarit se decouvre dans l'inventaire du cluster. 2. ce 500 etait avale par failed_when: false + no_log: true. Une assertion le releve desormais, message de l'API compris. 3. l'attente interrogeait nodes/<destination>/tasks/<UPID> ; la tache vit sur le noeud du gabarit. 60 tentatives x 10 s pour un clonage termine en 87 s, puis la suite qui reprend sans un mot. Le noeud se lit dans l'UPID lui-meme. 4. la garde d'apres-attente retombait sur `exitstatus | default('OK')` : une attente qui n'avait rien observe passait pour un succes. Elle exige d'avoir VU la tache s'arreter. Et une taille de disque porte toujours son unite : `disque: 40` etait lu comme un retrecissement, erreur toleree a raison — la machine naissait donc avec les 16 Go du gabarit au lieu de 40, sans que rien ne le dise. Le site declare enfin ce qu'il materialise (materialisation: gabarit, stockage, clone_complet) au lieu de l'heriter du tenant actif. Chrono : clonage reel 59 s et 87 s pour 3,3 Gio alloues. 42 preuves vertes, ansible-lint profil production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 02:44:21 -04:00
# LE NOEUD VIENT DE L'UPID LUI-MEME, et de nulle part ailleurs (2026-08-25).
#
# Cette attente interrogeait `nodes/{{ proxmox_clone_noeud }}` — la DESTINATION.
# Or un clonage inter-noeuds s'execute sur le noeud du GABARIT : l'UPID n'existait
# pas la ou on le cherchait, l'API repondait en erreur, `failed_when: false`
# l'avalait, et `until` n'etait jamais satisfait. Resultat : soixante tentatives a
# dix secondes pour un clonage deja termine en 87 s — dix minutes d'attente
# immobile, puis la suite qui reprend sans un mot.
#
# Un UPID s'ecrit `UPID:<noeud>:<pid>:...`. Le noeud y est deja ; le redemander a
# une variable, c'est se donner une chance de se tromper.
clonage : attendre la fin reelle de la tache, pas seulement l'avoir demandee Mon optimisation d'hier soir a mis le cluster a genoux, et la faute est entiere. En remplacant proxmox_kvm par un appel d'API direct, j'ai perdu ce que le module faisait pour moi : attendre la fin de la tache (timeout: 600). `POST .../clone` rend un UPID et la main immediatement ; Proxmox copie le disque en tache de fond. En sequentiel ca ne se voyait pas — l'attente de SSH absorbait le delai. En PARALLELE, `make creer-vm` rendait la main pendant la copie : la limite de concurrence ne retenait plus que des PROCESSUS VIDES et les clones s'empilaient. Mesure : limite a 4, QUATORZE copies integrales simultanees. Le symptome trompait — CPU de l'hyperviseur a 2 %, RAM a 13/62, et tout ramait. Ce n'etait pas la machine, c'etait TrueNAS. Les 14 hotes ont echoue, et flotte-creer a refuse de continuer : la garde ajoutee le matin meme a fait son travail. Le clonage relit desormais l'UPID et interroge l'etat de la tache jusqu'a `stopped`, avec un message clair si la sortie n'est pas OK. La limite de concurrence retrouve un sens : quatre clones REELS, pas quatre coquilles. NOTE, PAS ENCORE CORRIGE : `raser` a le meme defaut dans l'autre sens. Il a rapporte « 6/6 VM detruites » alors que les six etaient la — l'API accepte le DELETE, rend un UPID, et la tache echoue ensuite sur « VM is locked (clone) ». raser ne lit que la reponse immediate, jamais le resultat. MESURE DES OPTIMISATIONS, reconstruction complete avec gabarit sur CephNVMe (proposition de l'exploitant) et concurrence a 3 : 54 min 02 s contre 1 h 12 min 48 s — 26 % de moins, zero echec, 2838 taches. Le cache d'artefacts est le plus rentable : Nextcloud 10m55s -> 4m14s, Forgejo 3m42s -> 1m33s, verifie par les `skipping` du journal. J'avais annonce un gain « limite a la part telechargement » — cette part etait bien plus grosse que je ne le croyais. forks=20 : les couches larges 3m09s -> 1m37s. Gabarit NVMe + 3 clones : 1m35s -> 1m17s par VM, le plus modeste, car les disques ECRIVENT toujours sur TrueNAS. Verifie : 3 clones actifs jamais plus, 7 devis CONFORME, ansible-lint production, prouver.py 35 OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 22:38:42 -04:00
url: >-
clonage : quatre defauts que la premiere VM placee hors du noeud du gabarit a reveles Toutes les VM naissaient sur le noeud du gabarit puis migraient. site-cache-01 est la premiere a etre placee ailleurs, et elle a fait tomber quatre defauts enchaines : 1. l'URL de clonage visait le noeud de DESTINATION, alors que l'API veut celui qui DETIENT le gabarit ; la destination se dit par `target`. D'ou un 500 sur tout clonage inter-noeuds. Le noeud du gabarit se decouvre dans l'inventaire du cluster. 2. ce 500 etait avale par failed_when: false + no_log: true. Une assertion le releve desormais, message de l'API compris. 3. l'attente interrogeait nodes/<destination>/tasks/<UPID> ; la tache vit sur le noeud du gabarit. 60 tentatives x 10 s pour un clonage termine en 87 s, puis la suite qui reprend sans un mot. Le noeud se lit dans l'UPID lui-meme. 4. la garde d'apres-attente retombait sur `exitstatus | default('OK')` : une attente qui n'avait rien observe passait pour un succes. Elle exige d'avoir VU la tache s'arreter. Et une taille de disque porte toujours son unite : `disque: 40` etait lu comme un retrecissement, erreur toleree a raison — la machine naissait donc avec les 16 Go du gabarit au lieu de 40, sans que rien ne le dise. Le site declare enfin ce qu'il materialise (materialisation: gabarit, stockage, clone_complet) au lieu de l'heriter du tenant actif. Chrono : clonage reel 59 s et 87 s pour 3,3 Gio alloues. 42 preuves vertes, ansible-lint profil production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 02:44:21 -04:00
{{ proxmox_api_racine }}/api2/json/nodes/{{
proxmox_clone_lance.json.data.split(':')[1]
clonage : attendre la fin reelle de la tache, pas seulement l'avoir demandee Mon optimisation d'hier soir a mis le cluster a genoux, et la faute est entiere. En remplacant proxmox_kvm par un appel d'API direct, j'ai perdu ce que le module faisait pour moi : attendre la fin de la tache (timeout: 600). `POST .../clone` rend un UPID et la main immediatement ; Proxmox copie le disque en tache de fond. En sequentiel ca ne se voyait pas — l'attente de SSH absorbait le delai. En PARALLELE, `make creer-vm` rendait la main pendant la copie : la limite de concurrence ne retenait plus que des PROCESSUS VIDES et les clones s'empilaient. Mesure : limite a 4, QUATORZE copies integrales simultanees. Le symptome trompait — CPU de l'hyperviseur a 2 %, RAM a 13/62, et tout ramait. Ce n'etait pas la machine, c'etait TrueNAS. Les 14 hotes ont echoue, et flotte-creer a refuse de continuer : la garde ajoutee le matin meme a fait son travail. Le clonage relit desormais l'UPID et interroge l'etat de la tache jusqu'a `stopped`, avec un message clair si la sortie n'est pas OK. La limite de concurrence retrouve un sens : quatre clones REELS, pas quatre coquilles. NOTE, PAS ENCORE CORRIGE : `raser` a le meme defaut dans l'autre sens. Il a rapporte « 6/6 VM detruites » alors que les six etaient la — l'API accepte le DELETE, rend un UPID, et la tache echoue ensuite sur « VM is locked (clone) ». raser ne lit que la reponse immediate, jamais le resultat. MESURE DES OPTIMISATIONS, reconstruction complete avec gabarit sur CephNVMe (proposition de l'exploitant) et concurrence a 3 : 54 min 02 s contre 1 h 12 min 48 s — 26 % de moins, zero echec, 2838 taches. Le cache d'artefacts est le plus rentable : Nextcloud 10m55s -> 4m14s, Forgejo 3m42s -> 1m33s, verifie par les `skipping` du journal. J'avais annonce un gain « limite a la part telechargement » — cette part etait bien plus grosse que je ne le croyais. forks=20 : les couches larges 3m09s -> 1m37s. Gabarit NVMe + 3 clones : 1m35s -> 1m17s par VM, le plus modeste, car les disques ECRIVENT toujours sur TrueNAS. Verifie : 3 clones actifs jamais plus, 7 devis CONFORME, ansible-lint production, prouver.py 35 OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 22:38:42 -04:00
}}/tasks/{{ proxmox_clone_lance.json.data | urlencode }}/status
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_tache
until: (proxmox_clone_tache.json.data.status | default('running')) == 'stopped'
retries: "{{ ((proxmox_clone_timeout | default(600) | int) / 10) | round | int }}"
delay: 10
changed_when: false
failed_when: false
no_log: true
when:
- not proxmox_clone_deja_la
- proxmox_clone_lance.json.data is defined
reconstruction : quatre defauts que seule une flotte rasee pouvait montrer Chezlepro detruite (15 VM, disques compris) et refaite depuis le gabarit minimal. 15/15 hotes, 0 echec ; make valider passe, test de restitution compris. Aucun defaut ne venait de la flotte ni du gabarit. 1. Un avertissement n est pas un echec. Proxmox rend WARNINGS: n pour une tache ABOUTIE ; la garde n acceptait que OK et declarait perdues cinq VM clonees a 100 pourcent. Le message parlait d etat stopped — celui de la TACHE, pas de la VM. 2. client_artefacts se contredisait : son commentaire disait de degrader, son code arretait. L autorite monte en premier, donc avant le cache du locataire : aucun ordre ne pouvait satisfaire la garde. 3. harden-below-nxdomain etendait le NXDOMAIN signe de la racine pour le TLD internal a toute la zone du site, sans jamais interroger l autoritatif. Declencheur : toute question sur un nom absent sous internal, y compris la zone d un autre locataire. Le cache contenait la bonne reponse ET un message negatif ; c est le negatif qui etait servi. aggressive-nsec: no avait semble marcher — c est le redemarrage qui vidait le cache, pas le reglage. 4. Un locataire doit savoir a qui demander la zone de son hebergeur, sans quoi il ne peut plus nommer son depot de sauvegarde. La derivation prenait dns_amorcage pour le resolveur du site : faux chez Technolibre, dont l amorcage est 9.9.9.9. P03 l a attrape avant tout deploiement. Au passage : instancier tentait encore le mot de passe unique d avant la separation des voutes ; comparer echouait en exit 4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 09:59:15 -04:00
# UN AVERTISSEMENT N'EST PAS UN ECHEC — ET IL NE DOIT PAS DISPARAITRE NON PLUS.
#
# Proxmox rend trois formes d'`exitstatus` : `OK`, `WARNINGS: <n>` pour une tache
# QUI A ABOUTI en signalant quelque chose, et un texte d'erreur pour un vrai echec.
# La garde n'acceptait que `OK` : elle declarait donc perdus des clonages termines.
#
# MESURE DU 2026-09-01, RECONSTRUCTION DE CHEZLEPRO. Cinq VM sur quinze refusees,
# toutes avec `WARNINGS: 1`. Le journal de la tache disait :
#
# transferred 16.0 GiB of 16.0 GiB (100.00%)
# can't deactivate LV 'vm-9006-disk-1': Logical volume in use.
# WARN: volume deactivation failed
#
# Le disque etait transfere INTEGRALEMENT. Proxmox essaie ensuite de desactiver le
# volume DU GABARIT, et n'y parvient pas tant qu'un autre clonage parallele s'en
# sert. C'est la consequence NORMALE de cloner un meme gabarit en parallele — la
# condition meme que `flotte-creer` recherche pour aller vite.
#
# Ce que ca coutait : la reconstruction s'arretait sur cinq VM parfaitement clonees,
# et le message parlait d'« etat stopped » — l'etat de la TACHE, qui signifie
# « terminee ». On lit « la VM est arretee » et on cherche un probleme de demarrage
# qui n'existe pas. Le message dit desormais de quoi il parle.
- name: Le clonage a-t-il signale quelque chose en aboutissant ?
ansible.builtin.debug:
msg: >-
Clonage de {{ proxmox_clone_nom }} abouti AVEC AVERTISSEMENT
({{ proxmox_clone_tache.json.data.exitstatus }}). Le plus courant est
« volume deactivation failed » sur le disque du gabarit : un autre clonage
parallele s'en servait encore. Le journal complet de la tache est sur
« {{ proxmox_clone_lance.json.data.split(':')[1] | default('?') }} ».
when:
- not proxmox_clone_deja_la
- (proxmox_clone_tache.json.data.status | default('')) == 'stopped'
- (proxmox_clone_tache.json.data.exitstatus | default('')) is match('WARNINGS')
clonage : attendre la fin reelle de la tache, pas seulement l'avoir demandee Mon optimisation d'hier soir a mis le cluster a genoux, et la faute est entiere. En remplacant proxmox_kvm par un appel d'API direct, j'ai perdu ce que le module faisait pour moi : attendre la fin de la tache (timeout: 600). `POST .../clone` rend un UPID et la main immediatement ; Proxmox copie le disque en tache de fond. En sequentiel ca ne se voyait pas — l'attente de SSH absorbait le delai. En PARALLELE, `make creer-vm` rendait la main pendant la copie : la limite de concurrence ne retenait plus que des PROCESSUS VIDES et les clones s'empilaient. Mesure : limite a 4, QUATORZE copies integrales simultanees. Le symptome trompait — CPU de l'hyperviseur a 2 %, RAM a 13/62, et tout ramait. Ce n'etait pas la machine, c'etait TrueNAS. Les 14 hotes ont echoue, et flotte-creer a refuse de continuer : la garde ajoutee le matin meme a fait son travail. Le clonage relit desormais l'UPID et interroge l'etat de la tache jusqu'a `stopped`, avec un message clair si la sortie n'est pas OK. La limite de concurrence retrouve un sens : quatre clones REELS, pas quatre coquilles. NOTE, PAS ENCORE CORRIGE : `raser` a le meme defaut dans l'autre sens. Il a rapporte « 6/6 VM detruites » alors que les six etaient la — l'API accepte le DELETE, rend un UPID, et la tache echoue ensuite sur « VM is locked (clone) ». raser ne lit que la reponse immediate, jamais le resultat. MESURE DES OPTIMISATIONS, reconstruction complete avec gabarit sur CephNVMe (proposition de l'exploitant) et concurrence a 3 : 54 min 02 s contre 1 h 12 min 48 s — 26 % de moins, zero echec, 2838 taches. Le cache d'artefacts est le plus rentable : Nextcloud 10m55s -> 4m14s, Forgejo 3m42s -> 1m33s, verifie par les `skipping` du journal. J'avais annonce un gain « limite a la part telechargement » — cette part etait bien plus grosse que je ne le croyais. forks=20 : les couches larges 3m09s -> 1m37s. Gabarit NVMe + 3 clones : 1m35s -> 1m17s par VM, le plus modeste, car les disques ECRIVENT toujours sur TrueNAS. Verifie : 3 clones actifs jamais plus, 7 devis CONFORME, ansible-lint production, prouver.py 35 OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 22:38:42 -04:00
- name: Dire si le clonage ne s'est pas termine correctement
ansible.builtin.fail:
msg: >-
Le clonage de {{ proxmox_clone_nom }} (VMID {{ proxmox_clone_vmid }}) ne s'est pas
reconstruction : quatre defauts que seule une flotte rasee pouvait montrer Chezlepro detruite (15 VM, disques compris) et refaite depuis le gabarit minimal. 15/15 hotes, 0 echec ; make valider passe, test de restitution compris. Aucun defaut ne venait de la flotte ni du gabarit. 1. Un avertissement n est pas un echec. Proxmox rend WARNINGS: n pour une tache ABOUTIE ; la garde n acceptait que OK et declarait perdues cinq VM clonees a 100 pourcent. Le message parlait d etat stopped — celui de la TACHE, pas de la VM. 2. client_artefacts se contredisait : son commentaire disait de degrader, son code arretait. L autorite monte en premier, donc avant le cache du locataire : aucun ordre ne pouvait satisfaire la garde. 3. harden-below-nxdomain etendait le NXDOMAIN signe de la racine pour le TLD internal a toute la zone du site, sans jamais interroger l autoritatif. Declencheur : toute question sur un nom absent sous internal, y compris la zone d un autre locataire. Le cache contenait la bonne reponse ET un message negatif ; c est le negatif qui etait servi. aggressive-nsec: no avait semble marcher — c est le redemarrage qui vidait le cache, pas le reglage. 4. Un locataire doit savoir a qui demander la zone de son hebergeur, sans quoi il ne peut plus nommer son depot de sauvegarde. La derivation prenait dns_amorcage pour le resolveur du site : faux chez Technolibre, dont l amorcage est 9.9.9.9. P03 l a attrape avant tout deploiement. Au passage : instancier tentait encore le mot de passe unique d avant la separation des voutes ; comparer echouait en exit 4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 09:59:15 -04:00
termine correctement : la TACHE est « {{ proxmox_clone_tache.json.data.status
| default('?') }} » (« stopped » = terminee, ce n'est pas l'etat de la VM) et sa
sortie est « {{ proxmox_clone_tache.json.data.exitstatus | default('?') }} ».
clonage : quatre defauts que la premiere VM placee hors du noeud du gabarit a reveles Toutes les VM naissaient sur le noeud du gabarit puis migraient. site-cache-01 est la premiere a etre placee ailleurs, et elle a fait tomber quatre defauts enchaines : 1. l'URL de clonage visait le noeud de DESTINATION, alors que l'API veut celui qui DETIENT le gabarit ; la destination se dit par `target`. D'ou un 500 sur tout clonage inter-noeuds. Le noeud du gabarit se decouvre dans l'inventaire du cluster. 2. ce 500 etait avale par failed_when: false + no_log: true. Une assertion le releve desormais, message de l'API compris. 3. l'attente interrogeait nodes/<destination>/tasks/<UPID> ; la tache vit sur le noeud du gabarit. 60 tentatives x 10 s pour un clonage termine en 87 s, puis la suite qui reprend sans un mot. Le noeud se lit dans l'UPID lui-meme. 4. la garde d'apres-attente retombait sur `exitstatus | default('OK')` : une attente qui n'avait rien observe passait pour un succes. Elle exige d'avoir VU la tache s'arreter. Et une taille de disque porte toujours son unite : `disque: 40` etait lu comme un retrecissement, erreur toleree a raison — la machine naissait donc avec les 16 Go du gabarit au lieu de 40, sans que rien ne le dise. Le site declare enfin ce qu'il materialise (materialisation: gabarit, stockage, clone_complet) au lieu de l'heriter du tenant actif. Chrono : clonage reel 59 s et 87 s pour 3,3 Gio alloues. 42 preuves vertes, ansible-lint profil production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 02:44:21 -04:00
Regarder la tache sur
« {{ proxmox_clone_lance.json.data.split(':')[1] | default('?') }} ».
clonage : attendre la fin reelle de la tache, pas seulement l'avoir demandee Mon optimisation d'hier soir a mis le cluster a genoux, et la faute est entiere. En remplacant proxmox_kvm par un appel d'API direct, j'ai perdu ce que le module faisait pour moi : attendre la fin de la tache (timeout: 600). `POST .../clone` rend un UPID et la main immediatement ; Proxmox copie le disque en tache de fond. En sequentiel ca ne se voyait pas — l'attente de SSH absorbait le delai. En PARALLELE, `make creer-vm` rendait la main pendant la copie : la limite de concurrence ne retenait plus que des PROCESSUS VIDES et les clones s'empilaient. Mesure : limite a 4, QUATORZE copies integrales simultanees. Le symptome trompait — CPU de l'hyperviseur a 2 %, RAM a 13/62, et tout ramait. Ce n'etait pas la machine, c'etait TrueNAS. Les 14 hotes ont echoue, et flotte-creer a refuse de continuer : la garde ajoutee le matin meme a fait son travail. Le clonage relit desormais l'UPID et interroge l'etat de la tache jusqu'a `stopped`, avec un message clair si la sortie n'est pas OK. La limite de concurrence retrouve un sens : quatre clones REELS, pas quatre coquilles. NOTE, PAS ENCORE CORRIGE : `raser` a le meme defaut dans l'autre sens. Il a rapporte « 6/6 VM detruites » alors que les six etaient la — l'API accepte le DELETE, rend un UPID, et la tache echoue ensuite sur « VM is locked (clone) ». raser ne lit que la reponse immediate, jamais le resultat. MESURE DES OPTIMISATIONS, reconstruction complete avec gabarit sur CephNVMe (proposition de l'exploitant) et concurrence a 3 : 54 min 02 s contre 1 h 12 min 48 s — 26 % de moins, zero echec, 2838 taches. Le cache d'artefacts est le plus rentable : Nextcloud 10m55s -> 4m14s, Forgejo 3m42s -> 1m33s, verifie par les `skipping` du journal. J'avais annonce un gain « limite a la part telechargement » — cette part etait bien plus grosse que je ne le croyais. forks=20 : les couches larges 3m09s -> 1m37s. Gabarit NVMe + 3 clones : 1m35s -> 1m17s par VM, le plus modeste, car les disques ECRIVENT toujours sur TrueNAS. Verifie : 3 clones actifs jamais plus, 7 devis CONFORME, ansible-lint production, prouver.py 35 OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 22:38:42 -04:00
when:
- not proxmox_clone_deja_la
clonage : quatre defauts que la premiere VM placee hors du noeud du gabarit a reveles Toutes les VM naissaient sur le noeud du gabarit puis migraient. site-cache-01 est la premiere a etre placee ailleurs, et elle a fait tomber quatre defauts enchaines : 1. l'URL de clonage visait le noeud de DESTINATION, alors que l'API veut celui qui DETIENT le gabarit ; la destination se dit par `target`. D'ou un 500 sur tout clonage inter-noeuds. Le noeud du gabarit se decouvre dans l'inventaire du cluster. 2. ce 500 etait avale par failed_when: false + no_log: true. Une assertion le releve desormais, message de l'API compris. 3. l'attente interrogeait nodes/<destination>/tasks/<UPID> ; la tache vit sur le noeud du gabarit. 60 tentatives x 10 s pour un clonage termine en 87 s, puis la suite qui reprend sans un mot. Le noeud se lit dans l'UPID lui-meme. 4. la garde d'apres-attente retombait sur `exitstatus | default('OK')` : une attente qui n'avait rien observe passait pour un succes. Elle exige d'avoir VU la tache s'arreter. Et une taille de disque porte toujours son unite : `disque: 40` etait lu comme un retrecissement, erreur toleree a raison — la machine naissait donc avec les 16 Go du gabarit au lieu de 40, sans que rien ne le dise. Le site declare enfin ce qu'il materialise (materialisation: gabarit, stockage, clone_complet) au lieu de l'heriter du tenant actif. Chrono : clonage reel 59 s et 87 s pour 3,3 Gio alloues. 42 preuves vertes, ansible-lint profil production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 02:44:21 -04:00
# `exitstatus | default('OK')` faisait passer pour un SUCCES une attente qui
# n'avait jamais rien observe : quand l'API repondait en erreur — le cas du
# clonage inter-noeuds interroge sur le mauvais noeud — il n'y avait aucun
# `json.data`, donc aucun `exitstatus`, donc « OK » par defaut. On exige
# desormais d'avoir VU la tache s'arreter, et bien s'arreter.
reconstruction : quatre defauts que seule une flotte rasee pouvait montrer Chezlepro detruite (15 VM, disques compris) et refaite depuis le gabarit minimal. 15/15 hotes, 0 echec ; make valider passe, test de restitution compris. Aucun defaut ne venait de la flotte ni du gabarit. 1. Un avertissement n est pas un echec. Proxmox rend WARNINGS: n pour une tache ABOUTIE ; la garde n acceptait que OK et declarait perdues cinq VM clonees a 100 pourcent. Le message parlait d etat stopped — celui de la TACHE, pas de la VM. 2. client_artefacts se contredisait : son commentaire disait de degrader, son code arretait. L autorite monte en premier, donc avant le cache du locataire : aucun ordre ne pouvait satisfaire la garde. 3. harden-below-nxdomain etendait le NXDOMAIN signe de la racine pour le TLD internal a toute la zone du site, sans jamais interroger l autoritatif. Declencheur : toute question sur un nom absent sous internal, y compris la zone d un autre locataire. Le cache contenait la bonne reponse ET un message negatif ; c est le negatif qui etait servi. aggressive-nsec: no avait semble marcher — c est le redemarrage qui vidait le cache, pas le reglage. 4. Un locataire doit savoir a qui demander la zone de son hebergeur, sans quoi il ne peut plus nommer son depot de sauvegarde. La derivation prenait dns_amorcage pour le resolveur du site : faux chez Technolibre, dont l amorcage est 9.9.9.9. P03 l a attrape avant tout deploiement. Au passage : instancier tentait encore le mot de passe unique d avant la separation des voutes ; comparer echouait en exit 4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 09:59:15 -04:00
# `OK` et `WARNINGS: <n>` disent tous deux que la tache a ABOUTI. Tout autre
# texte est un vrai echec, et l'absence de sortie signifie qu'on n'a jamais vu
# la tache s'arreter — le cas que la note ci-dessus decrit.
clonage : quatre defauts que la premiere VM placee hors du noeud du gabarit a reveles Toutes les VM naissaient sur le noeud du gabarit puis migraient. site-cache-01 est la premiere a etre placee ailleurs, et elle a fait tomber quatre defauts enchaines : 1. l'URL de clonage visait le noeud de DESTINATION, alors que l'API veut celui qui DETIENT le gabarit ; la destination se dit par `target`. D'ou un 500 sur tout clonage inter-noeuds. Le noeud du gabarit se decouvre dans l'inventaire du cluster. 2. ce 500 etait avale par failed_when: false + no_log: true. Une assertion le releve desormais, message de l'API compris. 3. l'attente interrogeait nodes/<destination>/tasks/<UPID> ; la tache vit sur le noeud du gabarit. 60 tentatives x 10 s pour un clonage termine en 87 s, puis la suite qui reprend sans un mot. Le noeud se lit dans l'UPID lui-meme. 4. la garde d'apres-attente retombait sur `exitstatus | default('OK')` : une attente qui n'avait rien observe passait pour un succes. Elle exige d'avoir VU la tache s'arreter. Et une taille de disque porte toujours son unite : `disque: 40` etait lu comme un retrecissement, erreur toleree a raison — la machine naissait donc avec les 16 Go du gabarit au lieu de 40, sans que rien ne le dise. Le site declare enfin ce qu'il materialise (materialisation: gabarit, stockage, clone_complet) au lieu de l'heriter du tenant actif. Chrono : clonage reel 59 s et 87 s pour 3,3 Gio alloues. 42 preuves vertes, ansible-lint profil production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 02:44:21 -04:00
- >-
(proxmox_clone_tache.json.data.status | default('')) != 'stopped'
reconstruction : quatre defauts que seule une flotte rasee pouvait montrer Chezlepro detruite (15 VM, disques compris) et refaite depuis le gabarit minimal. 15/15 hotes, 0 echec ; make valider passe, test de restitution compris. Aucun defaut ne venait de la flotte ni du gabarit. 1. Un avertissement n est pas un echec. Proxmox rend WARNINGS: n pour une tache ABOUTIE ; la garde n acceptait que OK et declarait perdues cinq VM clonees a 100 pourcent. Le message parlait d etat stopped — celui de la TACHE, pas de la VM. 2. client_artefacts se contredisait : son commentaire disait de degrader, son code arretait. L autorite monte en premier, donc avant le cache du locataire : aucun ordre ne pouvait satisfaire la garde. 3. harden-below-nxdomain etendait le NXDOMAIN signe de la racine pour le TLD internal a toute la zone du site, sans jamais interroger l autoritatif. Declencheur : toute question sur un nom absent sous internal, y compris la zone d un autre locataire. Le cache contenait la bonne reponse ET un message negatif ; c est le negatif qui etait servi. aggressive-nsec: no avait semble marcher — c est le redemarrage qui vidait le cache, pas le reglage. 4. Un locataire doit savoir a qui demander la zone de son hebergeur, sans quoi il ne peut plus nommer son depot de sauvegarde. La derivation prenait dns_amorcage pour le resolveur du site : faux chez Technolibre, dont l amorcage est 9.9.9.9. P03 l a attrape avant tout deploiement. Au passage : instancier tentait encore le mot de passe unique d avant la separation des voutes ; comparer echouait en exit 4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-02 09:59:15 -04:00
or not ((proxmox_clone_tache.json.data.exitstatus | default('')) == 'OK'
or (proxmox_clone_tache.json.data.exitstatus | default('')) is match('WARNINGS'))
clonage : attendre la fin reelle de la tache, pas seulement l'avoir demandee Mon optimisation d'hier soir a mis le cluster a genoux, et la faute est entiere. En remplacant proxmox_kvm par un appel d'API direct, j'ai perdu ce que le module faisait pour moi : attendre la fin de la tache (timeout: 600). `POST .../clone` rend un UPID et la main immediatement ; Proxmox copie le disque en tache de fond. En sequentiel ca ne se voyait pas — l'attente de SSH absorbait le delai. En PARALLELE, `make creer-vm` rendait la main pendant la copie : la limite de concurrence ne retenait plus que des PROCESSUS VIDES et les clones s'empilaient. Mesure : limite a 4, QUATORZE copies integrales simultanees. Le symptome trompait — CPU de l'hyperviseur a 2 %, RAM a 13/62, et tout ramait. Ce n'etait pas la machine, c'etait TrueNAS. Les 14 hotes ont echoue, et flotte-creer a refuse de continuer : la garde ajoutee le matin meme a fait son travail. Le clonage relit desormais l'UPID et interroge l'etat de la tache jusqu'a `stopped`, avec un message clair si la sortie n'est pas OK. La limite de concurrence retrouve un sens : quatre clones REELS, pas quatre coquilles. NOTE, PAS ENCORE CORRIGE : `raser` a le meme defaut dans l'autre sens. Il a rapporte « 6/6 VM detruites » alors que les six etaient la — l'API accepte le DELETE, rend un UPID, et la tache echoue ensuite sur « VM is locked (clone) ». raser ne lit que la reponse immediate, jamais le resultat. MESURE DES OPTIMISATIONS, reconstruction complete avec gabarit sur CephNVMe (proposition de l'exploitant) et concurrence a 3 : 54 min 02 s contre 1 h 12 min 48 s — 26 % de moins, zero echec, 2838 taches. Le cache d'artefacts est le plus rentable : Nextcloud 10m55s -> 4m14s, Forgejo 3m42s -> 1m33s, verifie par les `skipping` du journal. J'avais annonce un gain « limite a la part telechargement » — cette part etait bien plus grosse que je ne le croyais. forks=20 : les couches larges 3m09s -> 1m37s. Gabarit NVMe + 3 clones : 1m35s -> 1m17s par VM, le plus modeste, car les disques ECRIVENT toujours sur TrueNAS. Verifie : 3 clones actifs jamais plus, 7 devis CONFORME, ansible-lint production, prouver.py 35 OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 22:38:42 -04:00
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
- 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
# 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.
# 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
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
- 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 }}"
# LE PLANCHER DE MEMOIRE — ce que l'hote peut REPRENDRE, pas ce qu'il donne.
#
# `balloon` vaut 0 sur toute la flotte, c'est-a-dire ballooning DESACTIVE : l'hote
# ne peut jamais reprendre une page, meme jamais touchee. Mesure : 40 Go alloues
# a un ecosysteme pour 15,5 Go reellement utilises — 39 %. Les 24,5 Go restants
# sont tenus sans etre lus.
#
# CE QUE LE BALLOONING N'EST PAS : une compression. L'invite voit `memory` au
# demarrage et s'y dimensionne — `shared_buffers` de PostgreSQL, tas d'une JVM,
# series en memoire d'un collecteur. Reprendre en dessous de ce qu'il a reserve
# le fait swapper A L'INTERIEUR, ce qui coute plus que la RAM gagnee.
#
# D'ou un PLANCHER DECLARE PAR MACHINE plutot qu'un ratio global : les machines
# qui se dimensionnent au demarrage gardent un plancher haut, les autres non.
balloon: "{{ proxmox_clone_memoire_min | 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
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
- proxmox_clone_memoire_min is defined and proxmox_clone_memoire_min | string | length > 0
proxmox : le pare-feu ne s'arme que dans le SDN — et 45e preuve Le decoupage du site en quatre zones a revele un defaut qui dormait dans le moteur. `firewall=1` sur l'interface d'une VM fait passer tout le trafic ponte par conntrack. Sur un VNet SDN c'est sans consequence : en EVPN le routage inter-VNet se fait dans le VRF, SUR LE NOEUD, et le flux ne quitte jamais l'hyperviseur. Sur un pont classique route par une frontiere externe, deux VM du MEME noeud dans deux VLAN differents ne se parlent qu'en EPINGLE : la trame sort par le lien physique, la frontiere la route, elle revient sur le meme pont. La meme table conntrack voit alors les deux moities de la connexion, classe le retour INVALID, et PVEFW-FORWARD le jette. La mesure a tranche : ops(asgard) -> pki(asgard) 0/8 dns(gandalf) -> cache(gandalf) 0/8 ops(asgard) -> forge(vishnu) 6/8 dns(gandalf) -> pki(asgard) 6/8 ops(asgard) -> cache(gandalf) 8/8 dns(gandalf) -> forge(vishnu) 8/8 Toutes les paires intra-noeud echouent, toutes les paires inter-noeuds passent. Douze tentatives faisaient monter le compteur `ctstate INVALID` de +112 sur asgard et +116 sur gandalf ; `firewall=0` pose, il ne bouge plus — 0 sur les deux, pour le meme trafic. Le defaut se deguise en panne reseau : la poignee TCP ABOUTIT, et ce sont les paquets de DONNEES qui disparaissent. L'AC le disait dans son propre journal (`TLS handshake error ... i/o timeout` : elle accepte et attend un ClientHello qui n'arrive jamais). Ecartes un par un, par la mesure : regles identiques champ par champ, alias corrects, assignation des interfaces confirmee, ARP et routes saines, IPS desactive, shaper vide, NAT source limite a `wan`, MTU a 1500 de bout en bout — et la taille sans effet, 100 octets se perdant comme 1460. L'INTENTION DECLAREE NE SUFFIT PAS : un tenant declare `proxmox_clone_parefeu_interface: true` avec `proxmox_clone_pont: vmbr1` comme valeur PAR DEFAUT, chaque hote la remplacant par son VNet derive. L'hote qui retombe sur `vmbr1` naitrait arme sur un pont classique. `cloner_vm_debian.yml` croise donc l'intention avec le pont REELLEMENT utilise, et le dit quand il desarme — un desarmement muet serait le meme piege, en silence. P45 EVALUE l'expression du playbook sur quatre cas plutot que d'en lire le texte, dont un qui DOIT rendre vrai : sans lui, une expression constamment fausse passerait la preuve sans rien garantir. Controle negatif verifie — le drapeau remis a plat fait echouer la preuve. Ce commit emporte aussi le desarmement d'`unattended-upgrades` dans `common_packages`, jusqu'ici applique sur les machines mais pas versionne : le verrou dpkg tenu 48 minutes par une vague de maj automatiques a coute deux deploiements. Le site a revele ce defaut parce qu'il a ete le premier a porter plusieurs zones. Ce n'est pas une particularite du site : un tenant derive les siennes du meme principe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 19:50:53 -04:00
# Un desarmement doit se VOIR. Sans ce message, l'ecart entre ce que l'ecosysteme
# declare et ce que la VM recoit resterait muet — et c'est precisement le genre de
# silence qui a coute une soiree de diagnostic le 2026-08-25.
- name: Dire que le pare-feu Proxmox reste desarme (pont classique, pas un VNet SDN)
ansible.builtin.debug:
msg: >-
`proxmox_clone_parefeu_interface` vaut vrai, mais {{ proxmox_clone_nom }} nait
sur `{{ proxmox_clone_pont }}` — un pont Linux, pas un VNet SDN. Le pare-feu
Proxmox y jetterait le retour des flux en epingle (`ctstate INVALID`) : il reste
desarme. La frontiere continue de policer ce segment.
when:
- proxmox_clone_parefeu_interface | default(false) | bool
- proxmox_clone_pont is defined
- proxmox_clone_pont is match('^vmbr[0-9]+$')
- 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 }}"
proxmox : le pare-feu ne s'arme que dans le SDN — et 45e preuve Le decoupage du site en quatre zones a revele un defaut qui dormait dans le moteur. `firewall=1` sur l'interface d'une VM fait passer tout le trafic ponte par conntrack. Sur un VNet SDN c'est sans consequence : en EVPN le routage inter-VNet se fait dans le VRF, SUR LE NOEUD, et le flux ne quitte jamais l'hyperviseur. Sur un pont classique route par une frontiere externe, deux VM du MEME noeud dans deux VLAN differents ne se parlent qu'en EPINGLE : la trame sort par le lien physique, la frontiere la route, elle revient sur le meme pont. La meme table conntrack voit alors les deux moities de la connexion, classe le retour INVALID, et PVEFW-FORWARD le jette. La mesure a tranche : ops(asgard) -> pki(asgard) 0/8 dns(gandalf) -> cache(gandalf) 0/8 ops(asgard) -> forge(vishnu) 6/8 dns(gandalf) -> pki(asgard) 6/8 ops(asgard) -> cache(gandalf) 8/8 dns(gandalf) -> forge(vishnu) 8/8 Toutes les paires intra-noeud echouent, toutes les paires inter-noeuds passent. Douze tentatives faisaient monter le compteur `ctstate INVALID` de +112 sur asgard et +116 sur gandalf ; `firewall=0` pose, il ne bouge plus — 0 sur les deux, pour le meme trafic. Le defaut se deguise en panne reseau : la poignee TCP ABOUTIT, et ce sont les paquets de DONNEES qui disparaissent. L'AC le disait dans son propre journal (`TLS handshake error ... i/o timeout` : elle accepte et attend un ClientHello qui n'arrive jamais). Ecartes un par un, par la mesure : regles identiques champ par champ, alias corrects, assignation des interfaces confirmee, ARP et routes saines, IPS desactive, shaper vide, NAT source limite a `wan`, MTU a 1500 de bout en bout — et la taille sans effet, 100 octets se perdant comme 1460. L'INTENTION DECLAREE NE SUFFIT PAS : un tenant declare `proxmox_clone_parefeu_interface: true` avec `proxmox_clone_pont: vmbr1` comme valeur PAR DEFAUT, chaque hote la remplacant par son VNet derive. L'hote qui retombe sur `vmbr1` naitrait arme sur un pont classique. `cloner_vm_debian.yml` croise donc l'intention avec le pont REELLEMENT utilise, et le dit quand il desarme — un desarmement muet serait le meme piege, en silence. P45 EVALUE l'expression du playbook sur quatre cas plutot que d'en lire le texte, dont un qui DOIT rendre vrai : sans lui, une expression constamment fausse passerait la preuve sans rien garantir. Controle negatif verifie — le drapeau remis a plat fait echouer la preuve. Ce commit emporte aussi le desarmement d'`unattended-upgrades` dans `common_packages`, jusqu'ici applique sur les machines mais pas versionne : le verrou dpkg tenu 48 minutes par une vague de maj automatiques a coute deux deploiements. Le site a revele ce defaut parce qu'il a ete le premier a porter plusieurs zones. Ce n'est pas une particularite du site : un tenant derive les siennes du meme principe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 19:50:53 -04:00
# LE PARE-FEU PROXMOX NE S'ARME QUE SUR UN VNET SDN (2026-08-25).
#
# `firewall=1` fait passer tout le trafic ponte par conntrack. Sur un VNET SDN
# c'est sans consequence : en EVPN le routage inter-VNet se fait dans le VRF, SUR
# LE NOEUD. Le flux ne quitte jamais l'hyperviseur, conntrack en voit un chemin
# coherent, et le devis est-ouest (`devis_proxmox_fw.py`) peut le policer.
#
# Sur un PONT CLASSIQUE route par une frontiere externe, le meme reglage devient
# un piege. Deux VM du meme noeud dans deux VLAN differents ne se parlent qu'en
# EPINGLE : la trame sort par le lien physique, la frontiere la route, et elle
# revient sur LE MEME PONT. La meme table conntrack voit alors les deux moities de
# la connexion, classe le retour `INVALID`, et `PVEFW-FORWARD` le jette.
#
# MESURE DU 2026-08-25, sur le site decoupe en quatre zones : `site-ops-01` et
# `site-pki-01` (asgard), `site-dns-01` et `site-cache-01` (gandalf) ne se
# joignaient plus — 0/8 — alors que TOUTES les paires entre noeuds differents
# etaient a 8/8. Douze tentatives faisaient monter le compteur `ctstate INVALID`
# de +112 sur asgard et +116 sur gandalf ; `firewall=0` pose, il ne bouge plus.
#
# Le defaut se deguise en panne reseau : la poignee TCP aboutit (SYN/SYN-ACK
# creent l'etat), et ce sont les paquets de DONNEES qui disparaissent. On accuse
# le MTU, la frontiere, la zone. Rien de tout cela n'y est pour quelque chose.
#
# L'INTENTION DECLAREE NE SUFFIT DONC PAS : un tenant declare
# `proxmox_clone_parefeu_interface: true` avec `proxmox_clone_pont: vmbr1` comme
# valeur PAR DEFAUT, chaque hote la remplacant par son VNet derive. L'hote qui
# retombe sur `vmbr1` naitrait arme sur un pont classique. On croise donc
# l'intention avec le pont REELLEMENT utilise. Un pont Linux se nomme `vmbrN` ;
# un VNet SDN porte le nom derive de sa zone (`t29serv`).
firewall: "{{ (proxmox_clone_parefeu_interface | default(false) | bool) and (proxmox_clone_pont is not match('^vmbr[0-9]+$')) }}"
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
state: present
when:
- proxmox_clone_pont is defined
- proxmox_clone_pont | length > 0
- name: Agrandir le disque principal du clone (grow-only)
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
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(''))"
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"
clonage : l hyperviseur juge le demarrage, plus le chronometre du module Quinze VM creees trois a la fois. L une a depasse le delai du module PENDANT QUE PROXMOX GENERAIT ENCORE SON ISO CLOUD-INIT : Reached timeout while waiting for starting VM. Last line in task before timeout: generating cloud-init ISO Elle a demarre juste apres. flotte-creer a rendu « au moins une VM n a pas ete creee » alors que les quinze tournaient. CE FAUX ECHEC ARRETE UNE RECONSTRUCTION : reconstruire enchaine flotte-creer puis deployer-tout. DEUX CORRECTIONS, LA SECONDE EST LA VRAIE. Delai plus genereux, parce que la generation d ISO sur stockage partage se met en file quand trois clonages tombent ensemble. Mais un delai reste un pari : son expiration ne conclut plus rien. L HYPERVISEUR JUGE, en repondant ce qu il fait tourner — meme principe que P52, ou l agent invite juge la materialisation a la place d une reponse SSH. Logique verifiee sur quatre cas : seul running passe ; VM arretee, introuvable et reponse malformee echouent. Une reponse vide ne passe pas en silence. Rejoue sur une VM deja en marche : idempotent. CE QUE LA CREATION DE CETTE NUIT N A PAS PROUVE : les quinze VM ont ete materialisees DEPUIS LE POSTE, pas depuis le runner du SITE. Le chemin existait — make inseminer, ecrit la veille — et il n a pas servi. Le poste detient legitimement la voute du site, donc ce n est pas une faute de pouvoir ; c est une demonstration qui manque. make verifier : vert. make prouver : CONFORME, 54 OK, 0 echec, 0 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-30 08:28:13 -04:00
# LE DEMARRAGE DECLARAIT EN PANNE CE QUI FONCTIONNAIT (2026-08-29).
#
# Quinze VM creees trois a la fois : l'une d'elles a depasse le delai du module
# PENDANT QUE PROXMOX GENERAIT ENCORE SON ISO CLOUD-INIT.
#
# fatal: Reached timeout while waiting for starting VM.
# Last line in task before timeout: 'generating cloud-init ISO'
#
# Elle a demarre juste apres. Le module avait abandonne, pas l'hyperviseur — et
# `flotte-creer` a rendu « au moins une VM n'a pas ete creee » alors que les quinze
# tournaient. CE FAUX ECHEC ARRETE UNE RECONSTRUCTION : `reconstruire` enchaine
# `flotte-creer` puis `deployer-tout`, et se serait arrete la.
#
# DEUX CORRECTIONS, ET LA SECONDE EST LA VRAIE. Un delai plus genereux (le defaut du
# module est court, et la generation d'ISO sur un stockage partage se met en file
# quand trois clonages tombent ensemble). Mais un delai reste un pari : on ne fait
# donc plus de son expiration un verdict. C'EST L'HYPERVISEUR QUI JUGE, en repondant
# ce qu'il fait tourner — meme principe que P52, ou l'agent invite juge la
# materialisation a la place d'une reponse SSH.
- 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
clonage : l hyperviseur juge le demarrage, plus le chronometre du module Quinze VM creees trois a la fois. L une a depasse le delai du module PENDANT QUE PROXMOX GENERAIT ENCORE SON ISO CLOUD-INIT : Reached timeout while waiting for starting VM. Last line in task before timeout: generating cloud-init ISO Elle a demarre juste apres. flotte-creer a rendu « au moins une VM n a pas ete creee » alors que les quinze tournaient. CE FAUX ECHEC ARRETE UNE RECONSTRUCTION : reconstruire enchaine flotte-creer puis deployer-tout. DEUX CORRECTIONS, LA SECONDE EST LA VRAIE. Delai plus genereux, parce que la generation d ISO sur stockage partage se met en file quand trois clonages tombent ensemble. Mais un delai reste un pari : son expiration ne conclut plus rien. L HYPERVISEUR JUGE, en repondant ce qu il fait tourner — meme principe que P52, ou l agent invite juge la materialisation a la place d une reponse SSH. Logique verifiee sur quatre cas : seul running passe ; VM arretee, introuvable et reponse malformee echouent. Une reponse vide ne passe pas en silence. Rejoue sur une VM deja en marche : idempotent. CE QUE LA CREATION DE CETTE NUIT N A PAS PROUVE : les quinze VM ont ete materialisees DEPUIS LE POSTE, pas depuis le runner du SITE. Le chemin existait — make inseminer, ecrit la veille — et il n a pas servi. Le poste detient legitimement la voute du site, donc ce n est pas une faute de pouvoir ; c est une demonstration qui manque. make verifier : vert. make prouver : CONFORME, 54 OK, 0 echec, 0 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-30 08:28:13 -04:00
timeout: "{{ proxmox_clone_delai_demarrage | default(180) | int }}"
register: proxmox_clone_demarrage
failed_when: false
when: proxmox_clone_demarrer | default(true) | bool
# L'HYPERVISEUR EST LE JUGE. On lui demande ce qu'il fait tourner, on ne deduit rien
# du sort de la tache precedente. Les tentatives couvrent le cas ou il finissait
# encore son travail au moment ou le module a renonce.
- name: Confirmer aupres de l'hyperviseur que la VM tourne
community.general.proxmox_vm_info:
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 }}"
type: qemu
register: proxmox_clone_etat
until: (proxmox_clone_etat.proxmox_vms | default([])
| map(attribute='status') | first | default('')) == 'running'
retries: 12
delay: 10
changed_when: false
when: proxmox_clone_demarrer | default(true) | bool
- name: Refuser si la VM ne tourne toujours pas
ansible.builtin.assert:
that:
- (proxmox_clone_etat.proxmox_vms | default([])
| map(attribute='status') | first | default('')) == 'running'
fail_msg: >-
{{ proxmox_clone_nom }} (VMID {{ proxmox_clone_vmid }}) ne tourne pas apres
deux minutes d'attente. Ce n'est plus un delai trop court : l'hyperviseur lui-meme
la rapporte
« {{ proxmox_clone_etat.proxmox_vms | default([]) | map(attribute='status')
| first | default('introuvable') }} ».
success_msg: >-
{{ proxmox_clone_nom }} tourne
{{ '(le module avait renonce avant l hyperviseur)'
if proxmox_clone_demarrage.failed | default(false) else '' }}
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) }}"
- "Cloud-Init ipconfig0: {{ proxmox_clone_ipconfig0 }}"