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
This commit is contained in:
Daniel Allaire 2026-08-30 08:28:13 -04:00
parent 5c59f19530
commit f2f6cccd3e
3 changed files with 195 additions and 0 deletions

View file

@ -1,5 +1,49 @@
# CHANGELOG — Set-OPS
## 2026-08-30 — Le demarrage declarait en panne ce qui fonctionnait
**54 preuves.** Quinze VM de Chezlepro 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 renonce, 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.** `make reconstruire` enchaine `flotte-creer`
puis `deployer-tout` : la sequence se serait arretee la, sur une flotte complete et saine.
### Un delai reste un pari — on n'en fait plus un verdict
Deux corrections, et la seconde est la vraie. Le delai devient genereux, parce que la
generation d'ISO sur un stockage partage se met en file quand trois clonages tombent
ensemble. Mais son expiration ne conclut plus rien : **c'est l'hyperviseur qui juge**, en
repondant ce qu'il fait tourner.
C'est exactement le principe de P52, applique un cran plus tot — la, l'agent invite jugeait
la materialisation a la place d'une reponse SSH ; ici, l'API juge le demarrage a la place
d'un chronometre.
*Logique verifiee sur quatre cas : seul `running` passe ; VM arretee, VM introuvable et
reponse malformee echouent toutes. 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` etait ecrit la veille — et il n'a pas servi. Rien de
cette nuit ne demontre donc que le runner du site sait materialiser une flotte : il a cree
une VM l'avant-veille, pas celles-ci.
*Ce n'est pas une faute de pouvoir : le poste detient legitimement la voute du site. C'est
une demonstration qui manque, et il faut le dire plutot que de laisser croire le contraire.*
make verifier : vert. make prouver : CONFORME, 54 OK, 0 echec, 0 saute.
## 2026-08-28 — Le puits avalait le plan d'administration
**54 preuves.** Le poste de l'exploitant ne joignait aucune machine de tenant. La chaîne,

View file

@ -0,0 +1,90 @@
# Preuve de conformite — Set-OPS — 2026-08-30
> Genere par `make prouver` (`scripts/prouver.py`). **Rejouable** : relancer
> reproduit ce rapport. Chaque preuve rejoue l'outillage existant du depot ;
> aucune validation n'est reimplementee ici. Voir le mode d'emploi :
> [`docs/audit/README.md`](README.md), et le registre trace :
> [`docs/audit/affirmations.md`](affirmations.md).
- **Instance** : `instance` — inventaire `instance/inventories/principal/hosts.yml`
- **Verdict** : ✅ CONFORME (54 OK · 0 echec · 0 saute)
## Preuves
| # | Preuve | Affirmations | Statut | Detail |
|---|---|---|---|---|
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK |  |
| P02 | Tests unitaires (inventory_host) | — | ✅ OK | >>> le verrou tient : aucune VM n'aurait ete touchee |
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 4 instance(s) verifiee(s) — OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, OPS-Patient0 : plan et inventaire applique coincident. |
| P04 | Groupes <-> playbooks homonymes | AFF-008 | ✅ OK | |
| P05 | Dependances causales de groupes | AFF-009, AFF-084 | ✅ OK | |
| P06 | Validateurs de registres (serveurs/apps/bases/domaines) | AFF-003 | ✅ OK | Registre des domaines valide. |
| P07 | GUI (node --check) | AFF-033 | ✅ OK | JS du GUI : syntaxe valide (node --check). |
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 38 groupes classes, aucun cycle, aucune arete en arriere. |
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 36 rôles, 95 flux, schéma + matrice OK. |
| P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). |
| P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | playbook: playbooks/proxmox/cloner_vm_debian.yml |
| P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. |
| P13 | Invariants structurels/doctrinaux | AFF-015, AFF-022, AFF-037, AFF-038, AFF-062 | ✅ OK | LICENSE, socle dossier, pas de couches paralleles, SSH clef-only, nftables off : OK. |
| P14 | Pas de chemin lab/ code en dur | AFF-097 | ✅ OK | Aucun chemin instance/inventories/lab/group_vars code en dur. |
| P15 | Modele public socle valide | AFF-022, AFF-099 | ✅ OK | Modele public socle : domaines/serveurs/applications/bases valides. |
| P16 | Inventaire Ansible complet (--list) | AFF-030 | ✅ OK | 15 hotes, 36 groupes (inventaire dechiffre et parse). |
| P17 | Tous les modeles valident (registres + underlay) | AFF-022, AFF-099 | ✅ OK | Les 1 modele(s) decouvert(s) valident. |
| P18 | Gabarit de voute complet | AFF-026 | ✅ OK | Gabarit de voute complet : 23 secret(s) exige(s), tous presents. (Voute reelle non lisible ici : verification sautee.) |
| P19 | Le GUI couvre le schema du plan | AFF-002, AFF-095 | ✅ OK | GUI : les 28 champ(s) des plans reels sont editables (2 plan(s) inspecte(s)), registres toleres : nomenclature. |
| P20 | Adressage 100% derive du seed (aucun stocke) | AFF-001, AFF-003 | ✅ OK | 2 nomenclature(s) : adressage 100% derive du seed index. |
| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 3 instance(s) federee(s), aucun index en collision. |
| P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (22 sections). |
| P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 11 reseau(x), aucune collision avec la plage tenant. |
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | note : serveur_powerdns declare un port `derive` que le plan du site ne resout pas — aucune regle emise. |
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 3 tenant(s), 51 groupe(s), 81 regle(s). |
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 15 hote(s) x 5 integration(s) universelle(s) : aucune lacune, aucune recopie (0 exemption(s) derivee(s) du service rendu). |
| P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 8 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. |
| P28 | Pools Proxmox : un par tenant, sans collision | AFF-110 | ✅ OK | CONFORME : 3 pool(s) Proxmox, 35 VM placee(s), aucun nom ni VMID en collision. |
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 30 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 19, socle-identite 2, web-sso 5) ; 2 lacune(s) nommee(s) : serveur_loki, serv |
| P30 | SDN EVPN : zones, VNets et sous-reseaux derives | AFF-112 | ✅ OK | CONFORME : SDN EVPN, 3 zone(s), 15 VNet(s), 15 sous-reseau(x), aucune collision. |
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 53 scripts expliques et atteignables, 106 cibles make documentees, 62 roles avec README. |
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 41 exigence(s) de role, toutes satisfaites (130 cle(s) declaree(s) par l'instance). |
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 33 revendication(s) de port, aucune collision entre roles co-localises (38 groupes). |
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (26 genere(s) exempte(s)). |
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 5 application(s) exigeant une base l'ont toutes (4 entree(s) au registre). |
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 9 hote(s) detiennent de l'etat, tous porteurs de `client_backup` (9 groupe(s) au catalogue). |
| P37 | Le placement du tenant existe chez son hebergeur | — | ✅ OK | placement confronte a l'hebergeur monte (SITE-Chezlepro) : noeud, stockage, pont — tous offerts. |
| P38 | Catalogue des services : la carte dit ce que le moteur fait | — | ✅ OK | Catalogue a jour : 37 role(s) serveur/client tous nommes, 38 groupe(s) cite(s) en table existent tous. |
| P39 | Glossaire : tout mot employe est enseigne | — | ✅ OK | Glossaire complet : 81 terme(s) du jargon expliques, 15 lien(s) valides, 27 page(s) de wiki toutes atteignables. |
| P40 | Parente : l'ecosysteme sait de quoi il descend | — | ✅ OK | Parente coherente : 4 depot(s), tous retrouves, tous porteurs d'un remote. |
| P41 | Resolution d'instance : une seule, partagee | — | ✅ OK | Resolution unique : 49 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 4 edge(s) emettent un certificat portant les noms publies (OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre/principal, OPS-Patient0/product |
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 5 machine(s) du plan retrouvees, 60 regle(s) du site. |
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 4 integration(s) appliquent leur serveur avant leurs clients. |
| P45 | Pare-feu Proxmox : arme sur les VNet SDN, jamais ailleurs | — | ✅ OK | Le pare-feu Proxmox ne s'arme que sur un VNet SDN (4 cas evalues, dont un qui doit rendre VRAI). |
| P46 | Plancher /etc/hosts : un seul role en decide | — | ✅ OK | Un seul maitre du plancher — roles/hosts_statiques/tasks/main.yml : manage_etc_hosts: false ; et le gabarit maitre est pose (roles/hosts_statiques/templates/hos |
| P47 | Zones inverses : couvrir l'occupe, et rien de plus | — | ✅ OK | Les zones inverses couvrent l'occupe et rien de plus (5 cas evalues, dont un site a quatre zones et un tenant a une). |
| P48 | La carte d'orientation designe ce qui existe, et compte juste | — | ✅ OK | La carte designe 84 chemin(s) qui existent, et ses 7 chiffres correspondent a la mesure. |
| P49 | Registre des flux : la matrice d'audit est a jour | — | ✅ OK | Le registre des flux reproduit exactement ce que les `meta/flux.yml` declarent (113 lignes). |
| P50 | Silences : un refus muet est declare, place en dernier, et motive | — | ✅ OK | 2 silence(s) declare(s), tous en sequence > 1 (la plus haute des 125 regles `pass`), tous non consignes et tous motives. |
| P51 | Collections : toutes declarees, toutes epinglees | — | ✅ OK | 3 collection(s) et 2 bibliotheque(s) Python declarees et epinglees : ansible.posix==1.6.2, community.general==10.3.0, community.postgresql==3.10.2 |
| P52 | Materialiser n'exige pas d'entrer dans le tenant | — | ✅ OK | `creer-vm` confirme par l'agent invite (API des hyperviseurs, deja utilisee pour creer), sans exiger d'entrer dans le tenant. |
| P53 | L'interne refuse a voix haute, la bordure se tait | — | ✅ OK | L'interne parle, la bordure se tait — 15 ruleset(s) nftables refusent a voix haute ; pare-feu est-ouest en REJECT, source unique ; frontiere muette (actions : b |
| P54 | L'insemination ne reclame aucun secret du tenant | — | ✅ OK | 2 couche(s) d'insemination (serveur_debian, serveur_ops), 10 role(s) applique(s), aucun secret de tenant reclame. |
## Couverture des affirmations ✅ du registre
Chaque affirmation ✅ automatisable est couverte par la preuve indiquee ci-dessus.
Les ✅ **structurelles/doctrinales** non rejouables par une commande (ex. AFF-005
`make`=aide, AFF-014 ciblage groupe, AFF-024 `instancier-appliquer`, AFF-051 autorite
d'AGENTS.md, AFF-073/075 gardes `make`, AFF-090 wiki) ont ete verifiees a l'audit ;
elles restent hors du harnais recurrent (rien d'executable a rejouer).
## Declarations d'intention (⚪ invérifiables localement — assumees)
Ces affirmations ne sont pas rejouables hors production ; elles sont **assumees**
comme declarations d'intention, non comme preuves :
- **AFF-036** — « testables avec `--check` autant que possible » : verifiable seulement
contre une flotte vivante.
- **AFF-091** — contenu pedagogique du wiki : affirmations conceptuelles.
- **AFF-096** — « GUI 100 % francais » : revue exhaustive des libelles rendus, non automatisee.
- **AFF-007** — hote d'exemple `web-frontal-01` : placeholder assume.
_Rapport genere le 2026-08-30._

View file

@ -606,6 +606,25 @@
searchdomains: "{{ proxmox_clone_domaines_recherche | default(omit, true) }}"
agent: "enabled=1"
# LE DEMARRAGE DECLARAIT EN PANNE CE QUI FONCTIONNAIT (2026-08-29).
#
# Quinze VM creees trois a la fois : l'une d'elles a depasse le delai du module
# PENDANT QUE PROXMOX GENERAIT ENCORE SON ISO CLOUD-INIT.
#
# fatal: Reached timeout while waiting for starting VM.
# Last line in task before timeout: 'generating cloud-init ISO'
#
# Elle a demarre juste apres. Le module avait abandonne, pas l'hyperviseur — et
# `flotte-creer` a rendu « au moins une VM n'a pas ete creee » alors que les quinze
# tournaient. CE FAUX ECHEC ARRETE UNE RECONSTRUCTION : `reconstruire` enchaine
# `flotte-creer` puis `deployer-tout`, et se serait arrete la.
#
# DEUX CORRECTIONS, ET LA SECONDE EST LA VRAIE. Un delai plus genereux (le defaut du
# module est court, et la generation d'ISO sur un stockage partage se met en file
# quand trois clonages tombent ensemble). Mais un delai reste un pari : on ne fait
# donc plus de son expiration un verdict. C'EST L'HYPERVISEUR QUI JUGE, en repondant
# ce qu'il fait tourner — meme principe que P52, ou l'agent invite juge la
# materialisation a la place d'une reponse SSH.
- name: Demarrer le clone
community.general.proxmox_kvm:
api_host: "{{ proxmox_api_host_effectif }}"
@ -618,6 +637,48 @@
vmid: "{{ proxmox_clone_vmid | int }}"
name: "{{ proxmox_clone_nom }}"
state: started
timeout: "{{ proxmox_clone_delai_demarrage | default(180) | int }}"
register: proxmox_clone_demarrage
failed_when: false
when: proxmox_clone_demarrer | default(true) | bool
# L'HYPERVISEUR EST LE JUGE. On lui demande ce qu'il fait tourner, on ne deduit rien
# du sort de la tache precedente. Les tentatives couvrent le cas ou il finissait
# encore son travail au moment ou le module a renonce.
- name: Confirmer aupres de l'hyperviseur que la VM tourne
community.general.proxmox_vm_info:
api_host: "{{ proxmox_api_host_effectif }}"
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
api_user: "{{ proxmox_api_user_effectif }}"
api_token_id: "{{ proxmox_api_token_id_effectif }}"
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
node: "{{ proxmox_clone_noeud }}"
vmid: "{{ proxmox_clone_vmid | int }}"
type: qemu
register: proxmox_clone_etat
until: (proxmox_clone_etat.proxmox_vms | default([])
| map(attribute='status') | first | default('')) == 'running'
retries: 12
delay: 10
changed_when: false
when: proxmox_clone_demarrer | default(true) | bool
- name: Refuser si la VM ne tourne toujours pas
ansible.builtin.assert:
that:
- (proxmox_clone_etat.proxmox_vms | default([])
| map(attribute='status') | first | default('')) == 'running'
fail_msg: >-
{{ proxmox_clone_nom }} (VMID {{ proxmox_clone_vmid }}) ne tourne pas apres
deux minutes d'attente. Ce n'est plus un delai trop court : l'hyperviseur lui-meme
la rapporte
« {{ proxmox_clone_etat.proxmox_vms | default([]) | map(attribute='status')
| first | default('introuvable') }} ».
success_msg: >-
{{ proxmox_clone_nom }} tourne
{{ '(le module avait renonce avant l hyperviseur)'
if proxmox_clone_demarrage.failed | default(false) else '' }}
when: proxmox_clone_demarrer | default(true) | bool
- name: Afficher le resume du clone