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