Set-OPS-Public/playbooks/proxmox/cloner_vm_debian.yml
Daniel Allaire 776f087f70 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

467 lines
25 KiB
YAML

---
- name: Cloner une VM Debian depuis le modele Proxmox
hosts: localhost
connection: local
gather_facts: false
become: false
vars:
# Config Proxmox de l'instance : on cherche dans l'inventaire de l'instance,
# quel que soit son nom (lab > principal > production). Compatible « par instance ».
setops_inventaires: [lab, principal, production]
# 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]
# 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"
tasks:
- name: Verifier les fichiers de variables Proxmox optionnels
ansible.builtin.stat:
path: "{{ playbook_dir }}/../../instance/inventories/{{ item.0 }}/group_vars/{{ item.1 }}"
loop: "{{ setops_inventaires | product(setops_gv_proxmox) | list }}"
loop_control:
label: "{{ item.0 }}/{{ item.1 }}"
register: proxmox_fichiers_variables
- name: Charger les variables Proxmox non sensibles disponibles
ansible.builtin.include_vars:
file: "{{ item.stat.path }}"
loop: "{{ proxmox_fichiers_variables.results }}"
when:
- item.stat.exists
- item.stat.path is not search('vault')
- 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
- name: Charger les secrets Proxmox Vault
block:
- name: Charger les secrets de la voute Proxmox
ansible.builtin.include_vars:
file: "{{ item.stat.path }}"
loop: "{{ proxmox_fichiers_variables.results }}"
when:
- item.stat.exists
- item.stat.path is search('vault')
no_log: true
rescue:
- name: Expliquer l echec de chargement du Vault
ansible.builtin.fail:
msg: >-
Impossible de charger la voute Proxmox (group_vars/all/vault.yml ou
proxmox.vault.yml). Si elle est chiffree avec Ansible Vault, fournir son
mot de passe ou definir ANSIBLE_VAULT_PASSWORD_FILE avant de relancer make creer-vm.
- name: Appliquer les valeurs Proxmox avec repli environnement
ansible.builtin.set_fact:
proxmox_api_host_effectif: "{{ proxmox_api_host | default(lookup('env', 'PROXMOX_API_HOST'), true) }}"
proxmox_api_user_effectif: "{{ proxmox_api_user | default(lookup('env', 'PROXMOX_API_USER'), true) }}"
# `split('!')[-1]` : proxmoxer recompose lui-meme `utilisateur!nom` a partir de
# `api_user` et `api_token_id`. Lui passer la forme COMPLETE produit
# `ansible@pve!ansible@pve!nom` et un 401 muet — le meme jeton fonctionne en
# HTTP direct, ce qui rend le diagnostic trompeur. Verifie contre le cluster le
# 2026-08-03 : forme complete = 401, forme courte = OK. Sans effet si la voute
# stocke deja le nom seul, donc les deux ecritures restent acceptees.
proxmox_api_token_id_effectif: "{{ (proxmox_api_token_id | default(lookup('env', 'PROXMOX_API_TOKEN_ID'), true)) | string | split('!') | last }}"
proxmox_api_token_secret_effectif: "{{ proxmox_api_token_secret | default(lookup('env', 'PROXMOX_API_TOKEN_SECRET'), true) }}"
proxmox_api_port_effectif: "{{ proxmox_api_port | default(lookup('env', 'PROXMOX_API_PORT'), true) }}"
no_log: true
- name: Valider les parametres obligatoires Proxmox
ansible.builtin.assert:
that:
- proxmox_api_host_effectif | length > 0
- proxmox_api_user_effectif | length > 0
- proxmox_api_token_id_effectif | length > 0
- proxmox_api_token_secret_effectif | length > 0
- proxmox_clone_nom is defined
- proxmox_clone_nom | length > 0
- proxmox_clone_vmid_modele is defined
- proxmox_clone_vmid is defined
- proxmox_clone_noeud is defined
- proxmox_clone_noeud | length > 0
- proxmox_clone_ipconfig0 is defined
- proxmox_clone_ipconfig0 | length > 0
fail_msg: "Parametres Proxmox incomplets. Verifier proxmox.yml, la voute (all/vault.yml ou proxmox.vault.yml) et les variables make propres a la VM."
- name: Lire la cle publique SSH depuis le fichier fourni
ansible.builtin.set_fact:
proxmox_clone_sshkeys: "{{ lookup('ansible.builtin.file', proxmox_clone_cle_publique_fichier) }}"
when:
- proxmox_clone_cle_publique is not defined
- proxmox_clone_cle_publique_fichier is defined
- proxmox_clone_cle_publique_fichier | length > 0
- name: Utiliser la cle publique SSH fournie directement
ansible.builtin.set_fact:
proxmox_clone_sshkeys: "{{ proxmox_clone_cle_publique }}"
when:
- proxmox_clone_cle_publique is defined
- proxmox_clone_cle_publique | length > 0
- 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
- 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
# LE CLONE SE CIBLE PAR VMID, JAMAIS PAR NOM — et c'est tout l'objet de ces trois
# taches. `community.general.proxmox_kvm` cherche d'abord une VM portant le `name`
# demande ; s'il en trouve une, il conclut « elle existe deja », rend `ok` et ne
# clone RIEN. Aucune tache n'apparait meme cote cluster.
#
# Or les noms courts sont VOLONTAIREMENT identiques d'un tenant a l'autre (meme
# fonction, meme nom) : c'est le pool qui restitue l'appartenance. Le premier clone
# de Technolibre — `backup-01` — est donc tombe sur le `backup-01` de Chezlepro et
# n'a rien fait, puis l'attente a expire sur une configuration qui n'existerait
# jamais. Mesure du 2026-08-10 : zero VM creee, zero tache `qmclone` au cluster.
#
# Le defaut etait invisible tant qu'un seul tenant existait. C'est exactement ce
# qu'une epreuve de portabilite doit trouver.
- name: Recenser les VM du cluster (pour cibler par VMID)
vars:
proxmox_api_racine: "https://{{ proxmox_api_host_effectif }}:{{ proxmox_api_port_effectif | default(8006, true) }}"
ansible.builtin.uri:
url: "{{ proxmox_api_racine }}/api2/json/cluster/resources?type=vm"
method: GET
headers:
Authorization: >-
PVEAPIToken={{ proxmox_api_user_effectif }}!{{ proxmox_api_token_id_effectif }}={{ proxmox_api_token_secret_effectif }}
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
status_code: 200
register: proxmox_inventaire_vm
changed_when: false
no_log: true
- name: Retenir si le VMID cible est deja pris
ansible.builtin.set_fact:
proxmox_clone_deja_la: >-
{{ (proxmox_inventaire_vm.json.data | default([]))
| selectattr('vmid', 'defined')
| selectattr('vmid', 'equalto', proxmox_clone_vmid | int)
| list | length > 0 }}
- name: Cloner la VM depuis le modele (API directe, cible par VMID)
vars:
proxmox_api_racine: "https://{{ proxmox_api_host_effectif }}:{{ proxmox_api_port_effectif | default(8006, true) }}"
ansible.builtin.uri:
url: "{{ proxmox_api_racine }}/api2/json/nodes/{{ proxmox_clone_noeud }}/qemu/{{ proxmox_clone_vmid_modele | int }}/clone"
method: POST
headers:
Authorization: >-
PVEAPIToken={{ proxmox_api_user_effectif }}!{{ proxmox_api_token_id_effectif }}={{ proxmox_api_token_secret_effectif }}
body_format: form-urlencoded
# Corps ecrit en MAPPING YAML, pas assemble en Jinja : une expression `>-` rend
# une CHAINE, et le premier jet a ainsi perdu le `pool` en route — la VM est
# nee hors de son pool, sans un mot. `omit` retire proprement les cles vides.
body:
newid: "{{ proxmox_clone_vmid | int }}"
name: "{{ proxmox_clone_nom }}"
full: "{{ 1 if (proxmox_clone_complet | default(true) | bool) else 0 }}"
storage: "{{ proxmox_clone_stockage | default(omit, true) }}"
format: "{{ proxmox_clone_format | default(omit, true) }}"
# A la CREATION seulement : l'API ne sait pas changer le pool d'une VM
# existante par cet appel (il faut passer par le membre du pool).
pool: "{{ proxmox_clone_pool | default(omit, true) }}"
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
status_code: 200
register: proxmox_clone_lance
# Le corps ne porte aucun secret, mais l'en-tete si : `no_log` masquerait alors
# l'erreur de l'API, qui est justement ce qu'on veut lire. On ne journalise donc
# que l'ECHEC, et sans les en-tetes.
failed_when: false
no_log: true
when: not proxmox_clone_deja_la
# 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:
url: >-
{{ proxmox_api_racine }}/api2/json/nodes/{{ proxmox_clone_noeud
}}/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
- 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
termine correctement : etat « {{ proxmox_clone_tache.json.data.status | default('?')
}} », sortie « {{ proxmox_clone_tache.json.data.exitstatus | default('?') }} ».
Regarder la tache sur {{ proxmox_clone_noeud }}.
when:
- not proxmox_clone_deja_la
- proxmox_clone_tache.json.data.exitstatus | default('OK') != 'OK'
- name: Dire pourquoi le clonage a echoue, s'il a echoue
ansible.builtin.fail:
msg: >-
Le clonage de {{ proxmox_clone_nom }} (VMID {{ proxmox_clone_vmid }}) depuis le
gabarit {{ proxmox_clone_vmid_modele }} a ete refuse par l'API Proxmox
(HTTP {{ proxmox_clone_lance.status | default('?') }}).
Verifier que le gabarit existe sur {{ proxmox_clone_noeud }}, que le pool
{{ proxmox_clone_pool | default('(aucun)', true) }} existe, et que le VMID
cible est libre.
when:
- not proxmox_clone_deja_la
- proxmox_clone_lance.status | default(0) != 200
# L'API de CLONAGE ignore `cores` et `memory` : le clone herite du gabarit, et le
# plan est contredit sans un mot. Mesure du 2026-08-06 : plan 1 coeur / 1024 Mo,
# VM creee avec 2 / 2048 — ceux du gabarit. Il faut donc les reposer APRES le clone.
# Le clone COMPLET rend la main avant que sa configuration ne soit ecrite : sur
# un stockage lent, `qm clone` retourne, et le `.conf` n'existe pas encore. La
# tache suivante echouait alors sur « Configuration file ... does not exist ».
# Observe deux fois sur quinze clones (2026-08-07) — une course, pas un hasard.
- name: Attendre que la configuration du clone existe
vars:
# Assemblee dans une variable : un `>-` replie les retours en ESPACES, ce qui
# inserait une espace au milieu de l'URL. Le lint passait, la requete non.
proxmox_api_racine: "https://{{ proxmox_api_host_effectif }}:{{ proxmox_api_port_effectif | default(8006, true) }}"
ansible.builtin.uri:
url: "{{ proxmox_api_racine }}/api2/json/nodes/{{ proxmox_clone_noeud }}/qemu/{{ proxmox_clone_vmid | int }}/config"
method: GET
headers:
Authorization: >-
PVEAPIToken={{ proxmox_api_user_effectif }}!{{ proxmox_api_token_id_effectif }}={{ proxmox_api_token_secret_effectif }}
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
status_code: 200
register: proxmox_clone_conf
until: proxmox_clone_conf.status == 200
retries: 30
delay: 5
changed_when: false
failed_when: false
no_log: true
# `no_log` masque la cause au moment ou on en a besoin : le 2026-08-10, l'echec de
# cette attente s'est lu « the output has been hidden », et il a fallu interroger le
# cluster a la main pour comprendre que le clone n'avait jamais eu lieu. On rend
# donc le verdict lisible ici, sans reveler l'en-tete d'autorisation.
- name: Dire ce que l'attente du clone a constate
ansible.builtin.fail:
msg: >-
La configuration de {{ proxmox_clone_nom }} (VMID {{ proxmox_clone_vmid }})
n'existe toujours pas apres {{ 30 * 5 }} s sur {{ proxmox_clone_noeud }}.
Le clonage n'a donc pas abouti — verifier les taches du cluster
(`/cluster/tasks`) : si aucune tache de clonage n'apparait, c'est que rien
n'a ete demande a l'API.
when: proxmox_clone_conf.status | default(0) != 200
- name: Appliquer le gabarit de calcul du plan (le clonage ne le fait pas)
community.general.proxmox_kvm:
api_host: "{{ proxmox_api_host_effectif }}"
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
api_user: "{{ proxmox_api_user_effectif }}"
api_token_id: "{{ proxmox_api_token_id_effectif }}"
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
node: "{{ proxmox_clone_noeud }}"
vmid: "{{ proxmox_clone_vmid | int }}"
cores: "{{ proxmox_clone_coeurs | int }}"
memory: "{{ proxmox_clone_memoire | int }}"
update: true
# Le noeud vient de finir un clone COMPLET : son API repond encore lentement, et
# la lecture expire a 5 s. Mesure du 2026-08-10 : « Read timed out » sur le tout
# premier clone, VM creee mais laissee aux valeurs du gabarit — 2 coeurs / 2 Go au
# lieu du plan. Une VM sous-dimensionnee en silence est pire qu'un echec franc.
register: proxmox_gabarit_calcul
until: proxmox_gabarit_calcul is succeeded
retries: 6
delay: 10
when:
- proxmox_clone_coeurs is defined and proxmox_clone_coeurs | string | length > 0
- proxmox_clone_memoire is defined and proxmox_clone_memoire | string | length > 0
- name: Ajuster le reseau de la VM clonee
community.general.proxmox_nic:
api_host: "{{ proxmox_api_host_effectif }}"
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
api_user: "{{ proxmox_api_user_effectif }}"
api_token_id: "{{ proxmox_api_token_id_effectif }}"
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
vmid: "{{ proxmox_clone_vmid | int }}"
interface: "{{ proxmox_clone_interface | default('net0') }}"
model: virtio
bridge: "{{ proxmox_clone_pont }}"
tag: "{{ proxmox_clone_vlan | int if proxmox_clone_vlan is defined and proxmox_clone_vlan | string | length > 0 else omit }}"
firewall: "{{ proxmox_clone_parefeu_interface | default(false) | bool }}"
# `mtu: 1` est la valeur Proxmox pour « HERITE DU PONT » (virtio uniquement).
#
# Sans elle, l'invite nait a 1500 quel que soit le pont. En SDN EVPN la zone est a
# 1450 (VXLAN coute 50 octets) : la VM emet donc des trames que son propre chemin
# ne peut pas encapsuler. Ca ne casse pas franchement — la connexion s'etablit,
# les petites requetes passent, les grosses reponses restent suspendues. C'est
# exactement la panne que le registre des flux decrit comme « la plus couteuse a
# diagnostiquer », et pourquoi il declare l'ICMP « fragmentation necessaire ».
#
# Mesure du 2026-08-10 : zones `t11` et `t17` a 1450 sur le cluster, les 14 invites
# a 1500. Invisible tant que toutes les VM vivent sur le MEME hyperviseur — elles
# communiquent alors par le pont local, sans encapsulation. La panne apparaitrait
# au premier eclatement de la flotte sur plusieurs noeuds.
#
# HERITER plutot qu'ecrire 1450 : hors SDN le pont est a 1500 et la VM suit. Une
# seule source de verite — celle du pont auquel elle est reellement attachee — et
# la valeur reste juste le jour ou la fabric passera aux trames jumbo.
#
# Le GABARIT le porte aussi (`net0 ... mtu=1`), ce qui couvre les clonages qui ne
# passent pas par ce playbook — un clone fait a la main dans l'interface, par
# exemple. On garde neanmoins la ligne ici : un gabarit se RECAPTURE (fait le
# 2026-08-09), et ce qui n'est pas versionne se perd en silence. Le playbook est
# la garantie qui survit a la recapture ; `make mtu-mesurer` verifie le resultat.
#
# Cette tache s'execute AVANT le demarrage du clone (voir « Demarrer le clone »
# plus bas) : aucun risque de rebranchement a chaud. Applique a une VM EN MARCHE,
# le meme changement detache et rebranche la carte sans que l'invite reconfigure
# son interface — 14 machines coupees d'un coup le 2026-08-10, et il a fallu les
# redemarrer. Sur une VM deja en service : poser la config, puis redemarrer.
mtu: 1
state: present
when:
- proxmox_clone_pont is defined
- proxmox_clone_pont | length > 0
- name: Agrandir le disque principal du clone (grow-only)
community.general.proxmox_disk:
api_host: "{{ proxmox_api_host_effectif }}"
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
api_user: "{{ proxmox_api_user_effectif }}"
api_token_id: "{{ proxmox_api_token_id_effectif }}"
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
vmid: "{{ proxmox_clone_vmid | int }}"
disk: "{{ proxmox_clone_disque | default('scsi0') }}"
size: "{{ proxmox_clone_taille_disque }}"
state: resized
register: proxmox_redim_disque
# Le disque derive du plan est un MINIMUM. Si le template est deja plus grand,
# Proxmox refuse de retrecir : on tolere ce cas (grow-only), la VM garde le
# disque du template. Toute autre erreur reste bloquante.
failed_when:
- proxmox_redim_disque is failed
- "'shrinking disks is not supported' not in (proxmox_redim_disque.msg | default(''))"
when:
- proxmox_clone_taille_disque is defined
- proxmox_clone_taille_disque | length > 0
- name: Configurer Cloud-Init sur le clone
community.general.proxmox_kvm:
api_host: "{{ proxmox_api_host_effectif }}"
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
api_user: "{{ proxmox_api_user_effectif }}"
api_token_id: "{{ proxmox_api_token_id_effectif }}"
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
node: "{{ proxmox_clone_noeud }}"
vmid: "{{ proxmox_clone_vmid | int }}"
name: "{{ proxmox_clone_nom }}"
update: true
ciuser: "{{ proxmox_clone_ciuser | default(omit, true) }}"
sshkeys: "{{ proxmox_clone_sshkeys | default(omit) }}"
ipconfig:
ipconfig0: "{{ proxmox_clone_ipconfig0 }}"
nameservers: "{{ proxmox_clone_nameservers if proxmox_clone_nameservers | length > 0 else omit }}"
searchdomains: "{{ proxmox_clone_domaines_recherche | default(omit, true) }}"
agent: "enabled=1"
- name: Demarrer le clone
community.general.proxmox_kvm:
api_host: "{{ proxmox_api_host_effectif }}"
api_port: "{{ proxmox_api_port_effectif | int if proxmox_api_port_effectif | length > 0 else omit }}"
api_user: "{{ proxmox_api_user_effectif }}"
api_token_id: "{{ proxmox_api_token_id_effectif }}"
api_token_secret: "{{ proxmox_api_token_secret_effectif }}"
validate_certs: "{{ proxmox_validate_certs | default(false) | bool }}"
node: "{{ proxmox_clone_noeud }}"
vmid: "{{ proxmox_clone_vmid | int }}"
name: "{{ proxmox_clone_nom }}"
state: started
when: proxmox_clone_demarrer | default(true) | bool
- name: Afficher le resume du clone
ansible.builtin.debug:
msg:
- "VM creee: {{ proxmox_clone_nom }} (VMID {{ proxmox_clone_vmid }})"
- "Pool: {{ proxmox_clone_pool | default('(aucun)', true) }}"
- "Cloud-Init ipconfig0: {{ proxmox_clone_ipconfig0 }}"