Aller au contenu
Infrastructure as Code medium

Inventaire dynamique KVM avec community.libvirt.libvirt

10 min de lecture

Logo Ansible

Pour un homelab KVM ou un parc Proxmox bare-metal, le plugin community.libvirt.libvirt transforme votre hyperviseur en source de vérité pour Ansible. Vous créez une VM via virt-install, Terraform ou un autre outil, et Ansible la voit immédiatement sans aucune mise à jour d'inventaire.

C'est la solution la plus accessible pour découvrir les inventaires dynamiques : elle fonctionne sur votre poste, sans cloud, sans clé API, juste avec libvirt local.

  • Installer la collection community.libvirt et ses dépendances Python.
  • Configurer le plugin avec qemu:///system.
  • Créer des groupes logiques via groups: Jinja (webservers, dbservers, lab_vms).
  • Combiner le plugin avec un fichier statique pour les paramètres SSH.
  • Démontrer le côté dynamique : créer une nouvelle VM, la voir apparaître.
  • Un host Linux avec libvirt/KVM opérationnel (virsh list fonctionne).
  • Ansible installé via pipx, venv ou paquet distribution.
  • Pratiqué les concepts de plugins d'inventaire.

Le plugin vit dans la collection community.libvirt :

Fenêtre de terminal
ansible-galaxy collection install community.libvirt

Le plugin nécessite les bindings Python libvirt côté control node. Selon votre environnement :

Fenêtre de terminal
# Si Ansible installé via pipx :
pipx inject ansible libvirt-python
# Si Ansible dans un venv :
source ~/.venv/ansible/bin/activate
pip install libvirt-python
# Si paquet distribution :
sudo dnf install python3-libvirt # RHEL/Alma
sudo apt install python3-libvirt # Debian/Ubuntu

Vérifier :

Fenêtre de terminal
python3 -c "import libvirt; print('OK')"

Sans ces bindings, le plugin échoue avec libvirt python bindings must be installed.

Créer inventory/01-libvirt.yml :

---
plugin: community.libvirt.libvirt
uri: qemu:///system
inventory_hostname: name

Trois options essentielles :

  • plugin: au top-level dit à Ansible que ce fichier est une config plugin.
  • uri: indique le socket libvirt :
    • qemu:///system = libvirt root (toutes les VMs du système).
    • qemu:///session = libvirt user-mode (VMs de l'utilisateur courant).
  • inventory_hostname: name dit au plugin d'utiliser le nom de la VM comme hostname Ansible. L'autre option est uuid (plus stable mais illisible).

Tester :

Fenêtre de terminal
ansible-inventory -i inventory/01-libvirt.yml --graph 2>/dev/null | head -20

Toutes vos VMs apparaissent. Le suffixe 2>/dev/null masque les warnings libvirt sur les VMs arrêtées.

Sans filtre, le plugin liste TOUTES les VMs de l'host, y compris vos VMs personnelles non gérées par Ansible. Pour ne garder que celles du lab :

plugin: community.libvirt.libvirt
uri: qemu:///system
inventory_hostname: name
groups:
lab_vms: inventory_hostname in ['control-node', 'web1', 'web2', 'db1']
webservers: inventory_hostname in ['web1', 'web2']
dbservers: inventory_hostname == 'db1'
control: inventory_hostname == 'control-node'

groups: est un dict nom_du_groupe: condition_jinja. La condition est évaluée pour chaque VM ; si vraie, la VM rejoint le groupe.

Maintenant ansible -i inventory/ lab_vms -m ping cible uniquement les VMs du lab, les autres VMs libvirt sont ignorées.

Limite majeure du plugin : libvirt ne connaît pas les IPs des VMs (sauf si l'agent QEMU est installé et que vous utilisez compose: complexe). Pour la connexion SSH classique, on complète avec un fichier statique ou des host_vars/ :

inventory/
├── 01-libvirt.yml ← plugin (liste des VMs)
└── host_vars/ ← paramètres SSH par host
├── web1.yml
├── web2.yml
├── db1.yml
└── control-node.yml

Exemple host_vars/web1.yml :

---
ansible_host: 10.10.20.21
ansible_connection: ssh
ansible_user: ansible
ansible_ssh_private_key_file: ~/.ssh/lab_ed25519

Tester la connexion :

Fenêtre de terminal
ansible web1 -i inventory/ -m ansible.builtin.ping

Sortie attendue : pong.

Pour que le plugin soit automatiquement détecté sur n'importe quel inventaire :

[inventory]
enable_plugins = host_list, script, auto, yaml, ini, community.libvirt.libvirt

Sans cette activation, Ansible peut afficher un warning Failed to parse inventory with 'auto' plugin: libvirt plugin not enabled.

C'est ici que la magie opère. Sans toucher à votre fichier inventaire :

Fenêtre de terminal
# 1. Créer une nouvelle VM via virsh ou virt-install
virt-install \
--name new-web3 \
--memory 1024 \
--vcpus 1 \
--disk size=10 \
--import \
--os-variant almalinux10 \
--network network=lab-ansible \
--noautoconsole
# 2. Vérifier qu'Ansible la voit
ansible-inventory -i inventory/01-libvirt.yml --graph 2>/dev/null | grep new-web3

La VM apparaît immédiatement. Pas de modification d'inventaire, pas de ansible-inventory --refresh-cache (pas de cache configuré ici).

Pour qu'elle rejoigne le groupe webservers, ajouter une condition :

groups:
webservers: inventory_hostname in ['web1', 'web2'] or inventory_hostname.startswith('new-web')

Maintenant new-web3 est dans webservers automatiquement. Provisionner devient un simple virt-install sans toucher à Ansible.

Cette page a un lab d'accompagnement complet : labs/inventaires/dynamique-kvm/ dans stephrobert/ansible-training.

Il pose tous les fichiers (plugin config + host_vars), démontre :

  • La détection des 4 VMs du lab via libvirt.
  • Le pattern hybride libvirt + host_vars.
  • L'override ansible_connection: ssh.
  • Le côté dynamique (démarrage/arrêt d'une VM, Ansible voit immédiatement le changement).
Fenêtre de terminal
cd ~/Projets/ansible-training/labs/inventaires/dynamique-kvm/
cat README.md
ansible-inventory -i inventory/ --graph 2>/dev/null | head -10
ansible lab_vms -i inventory/ -m ping
pytest -v challenge/tests/

Activer le cache pour éviter les API calls répétés

Section intitulée « Activer le cache pour éviter les API calls répétés »

Sur un parc avec beaucoup de VMs, l'appel à libvirt à chaque commande devient lent. Cache :

plugin: community.libvirt.libvirt
uri: qemu:///system
inventory_hostname: name
cache: true
cache_plugin: jsonfile
cache_timeout: 300 # 5 minutes
cache_connection: /tmp/ansible_libvirt_cache

Première commande : appel libvirt, sauvegarde JSON. Commandes suivantes (5 min) : lecture du cache.

Pour rafraîchir manuellement après création d'une VM :

Fenêtre de terminal
ansible-inventory -i inventory/ --list --refresh-cache

Si l'agent QEMU est installé sur les VMs (paquet qemu-guest-agent), le plugin peut récupérer les IPs via compose: :

plugin: community.libvirt.libvirt
uri: qemu:///system
inventory_hostname: name
compose:
ansible_host: >
(guest_info.network.0.ip-addresses
| selectattr('ip-address-type', 'equalto', 'ipv4')
| first).ip_address

Avantage : plus besoin de fichier statique pour les IPs. Limite : nécessite qemu-guest-agent installé et démarré sur toutes les VMs cibles. Si l'agent est absent, le compose: plante.

SymptômeCauseFix
libvirt python bindings must be installedModule Python libvirt absentpipx inject ansible libvirt-python ou pip install libvirt-python
Plugin retourne 0 VMuri: pointe sur le mauvais socketTester virsh -c qemu:///system list directement
ansible_connection non overrideansible_connection: ssh mis dans all.vars au lieu de host_vars/Mettre dans chaque host_vars/<host>.yml
groups: ne match pas comme attenduCondition Jinja incorrecteTester en debug avec ansible-inventory -i ... --list -vvv
VM créée non visibleCache non rafraîchi--refresh-cache ou désactiver le cache
Trop de bruit (toutes les VMs personnelles)Pas de filtre groups:Ajouter groups: lab_vms: inventory_hostname in [...]
CritèrelibvirtProxmoxAWS EC2
SetupSimple, localToken APIIAM credentials
CoûtZéroZéro (homelab)Cloud bill
Retourne IPs ?Non (sans QEMU agent)Oui (avec agent)Oui
GroupageManuel via groups:Tags ProxmoxTags AWS
Cas d'usageHomelab KVMHomelab/PME ProxmoxCloud public
  • community.libvirt.libvirt = plugin idéal pour homelab KVM, fonctionne 100 % en local.
  • Bindings Python libvirt mandatory côté control node.
  • groups: Jinja pour filtrer les VMs et créer des groupes logiques.
  • Pattern hybride : plugin pour la liste, host_vars/ pour les IPs et SSH.
  • Override ansible_connection: ssh au niveau host_vars (pas all.vars).
  • Cache mandatory dès que le parc grossit.
  • Lab 57 disponible pour pratique complète.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn