Using Dynamic KVM Inventories with Ansible for VM Lifecycle and Configuration Management

Ansible is a powerful automation tool that simplifies the management of IT infrastructure. One of its strengths is the ability to work with dynamic inventories, which allow Ansible to discover and manage systems on-the-fly. When working with a KVM (Kernel-based Virtual Machine) environment, leveraging a dynamic inventory can significantly streamline the management of virtual machines (VMs) throughout their lifecycle.

Understanding Dynamic Inventory in Ansible

In Ansible, an inventory is a list of systems that Ansible manages. By default, Ansible uses a static inventory, which is a simple text file containing the hostnames or IP addresses of the systems. However, in dynamic environments where systems are frequently created and destroyed, a static inventory can quickly become outdated and unmanageable.

Dynamic inventories solve this problem by allowing Ansible to query a source (such as a cloud provider, virtualization platform, or database) to generate the list of systems in real-time. This ensures that Ansible always has an up-to-date view of the infrastructure it needs to manage.

Setting Up a Dynamic KVM Inventory

To manage VMs in a KVM environment, we can create a dynamic inventory script that queries the KVM hypervisor for the current list of VMs. This script can be written in Python, Bash, or any other language that can output JSON, as Ansible expects the inventory in JSON format.

Example: A Simple Python Script for KVM Inventory

Here’s an example of a simple Python script (kvm_inventory.py) that uses the libvirt library to query the KVM hypervisor for a list of running VMs:

#!/usr/bin/env python3

import libvirt
import json

def get_vm_info():
    conn = libvirt.open('qemu:///system')
    if conn is None:
        print('Failed to open connection to qemu:///system')
        return {}

    inventory = {'_meta': {'hostvars': {}}}

    for domain_id in conn.listDomainsID():
        domain = conn.lookupByID(domain_id)
        name = domain.name()
        ip_address = None

        if domain.hasCurrentSnapshot():
            ifaces = domain.interfaceAddresses(libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT, 0)
            for (iface_name, iface_info) in ifaces.items():
                if iface_info['addrs']:
                    for addr in iface_info['addrs']:
                        if addr['type'] == libvirt.VIR_IP_ADDR_TYPE_IPV4:
                            ip_address = addr['addr']
                            break

        if ip_address:
            inventory['g_' + name] = {'hosts': [name]}
            inventory['_meta']['hostvars'][name] = {'ansible_host': ip_address}

    conn.close()
    return inventory

if __name__ == "__main__":
    inventory = get_vm_info()
    print(json.dumps(inventory, indent=2))

This script connects to the KVM hypervisor, retrieves the list of running VMs, and generates a JSON inventory that Ansible can use.

Using the Dynamic Inventory in Ansible

To use the dynamic inventory with Ansible, you need to specify the script as the inventory source in your Ansible command or playbook:

ansible-playbook -i kvm_inventory.py playbook.yml

Ansible will execute the kvm_inventory.py script, which will return the inventory in JSON format. Ansible will then use this dynamic inventory to determine which hosts to target in the playbook.

Using the Libvirt Inventory Plugin

The Libvirt inventory plugin queries the libvirt daemon to obtain a list of VMs, including their IP addresses, status, and other metadata. Here’s how you can use it:

1. Install the Necessary Dependencies

sudo apt install python3-dev libvirt-dev

Ensure that the libvirt-python package is installed, as it allows Python to interact with the libvirt API.

pip install libvirt-python

You also need the libvirt daemon and KVM installed on your host system.

2. Configure the inventory plugin

Create an inventory configuration file that uses the libvirt inventory plugin. This configuration is usually written in YAML and saved with a .yml or .yaml extension.

Here’s an example configuration (libvirt_inventory.yml):

plugin: community.libvirt.libvirt
uri: qemu:///system
  • plugin: community.libvirt.libvirt: Specifies the use of the libvirt inventory plugin.
  • uri: qemu:///system: Defines the URI to connect to the KVM hypervisor. The URI qemu:///system connects to the system-wide instance of KVM.

This simple configuration will tell Ansible to connect to the KVM hypervisor and gather information about all VMs.

3. Using the Libvirt Inventory Plugin in Ansible

With the configuration in place, you can now use this dynamic inventory in your Ansible commands or playbooks:

ansible-inventory -i libvirt_inventory.yml --list

This command will display the current list of VMs managed by KVM in JSON format.

To run a playbook using this inventory:

ansible-playbook -i libvirt_inventory.yml playbook.yml

Ansible will automatically query the KVM hypervisor for the list of VMs and use it as the inventory for the playbook.

Example Playbook Using Libvirt Inventory Plugin

Here’s a sample playbook that uses the Libvirt inventory to configure VMs:

---
- name: Configure all KVM VMs
  hosts: all
  tasks:
    - name: Install Apache on all VMs
      ansible.builtin.yum:
        name: httpd
        state: present
      when: ansible_facts['os_family'] == "RedHat"

    - name: Ensure Apache is running
      ansible.builtin.service:
        name: httpd
        state: started
      when: ansible_facts['os_family'] == "RedHat"

In this example, the playbook installs and starts Apache on all VMs managed by the KVM hypervisor.

Advantages of Using the Libvirt Inventory Plugin

  • Simplicity: No need to write custom scripts to generate inventories.
  • Real-time inventory: Always up-to-date with the current state of VMs.
  • Integrated with Ansible: Works seamlessly with Ansible’s inventory system.

Managing VM Lifecycle with Ansible

Ansible can also manage the entire lifecycle of VMs, including creation, configuration, and deletion. For example, you can write a playbook to create a new VM, configure its operating system, and install necessary software. Here’s a basic example:

Example: Playbook to Create and Configure a New VM

---
- name: Create and configure a new VM
  hosts: localhost
  tasks:
    - name: Create a new VM
      virt:
        name: new_vm
        state: running
        memory: 1024
        vcpus: 2
        disk:
          - size: 10
        network:
          - bridge: virbr0
        os_type: linux
        boot:
          devices:
            - cdrom

    - name: Wait for VM to start and obtain an IP
      pause:
        minutes: 1

    - name: Add the new VM to the inventory
      add_host:
        name: "{{ hostvars['localhost']['new_vm_ip'] }}"
        groups: new_vms

    - name: Install Nginx on the new VM
      ansible.builtin.yum:
        name: nginx
        state: present
      when: ansible_facts['os_family'] == "RedHat"
      delegate_to: "{{ item }}"
      with_items: "{{ groups['new_vms'] }}"

This playbook creates a new VM using the virt module, waits for it to obtain an IP address, adds the new VM to the inventory, and then installs Nginx on it. The delegate_to and add_host modules are used to dynamically manage the new VM.

Conclusion

Using a dynamic KVM inventory with Ansible is an efficient way to manage the lifecycle and configuration of VMs. By automating the discovery and management of VMs, you can ensure that your infrastructure remains up-to-date and consistent, even in highly dynamic environments. The combination of Ansible’s flexibility and the power of KVM provides a robust solution for managing virtualized infrastructure.

Whether you are spinning up new VMs, configuring existing ones, or tearing down old instances, a dynamic KVM inventory simplifies these tasks, enabling you to focus on higher-level management and automation.

Leave a comment