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 tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn