Ansible module using libvirt to manage KVM virtual machines.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
Michaël Costa 07f53b5c00 chore: better log 9 months ago
.gitignore Initial commit 1 year ago
LICENSE Initial commit 1 year ago
README.md Add supplementary disks with virsh 1 year ago
TODO.md Fix typos: Merci Tom` 1 year ago
kvm_base.xml v0.4-alpha 1 year ago
virt_vm.py chore: better log 9 months ago

README.md

virt_vm

Ansible module using libvirt to manage QEMU/KVM virtual machines

This module allow basic QEMU/KVM virtual machines management.

This module does not intend to cover every libvirt aspects and capabilities.

The objective of this module is to offer a suitable response to MCO System internal virtual machines deployment problematics.

There is no plan to make this module part of the community Ansible modules collection and, therefore, it will not be published to the Ansible Galaxy module collection.

This Ansible module is released under the WTFPL and you are encouraged to use it if it suits you needs.

You also may consider forking this module to adapt it to your specific needs.

Requirements

QEMU guest agent must be installed and started on the guest OS.

Packages

The following packages are mandatory for this modules to be used (Centos 8 naming convention).

  • libvirt
  • libvirt-client
  • libvirt-daemon-driver-qemu
  • python3-libvirt
  • qemu-img
  • qemu-kvm
  • qemu-kvm-core
  • qemu-kvm-common
  • qemu-kvm-block-iscsi
  • qemu-kvm-block-ssh
  • qemu-kvm-block-gluster
  • qemu-kvm-block-rbd
  • qemu-kvm-block-curl
  • ipxe-roms-qemu
  • python3-libvirt
  • python3-lxml

Python3 modules

The following module is not available in basic Centos 8 packages repositories and must be installed with pip3

  • xmldiff

Features

  • Generates and define persistent libvirt domains from XML definition
  • Hot configuration modification and application whenever possible
  • VM deployment from template disk image
  • Supplementary disk creation and attachment
  • Idempotent module

Usage

Basics

The module needs to have access to a template XML file (kvm_base.xml) stored in libvirt_tmp_path.

This template XML file must be deployed by a previous task.

Created virtual machines disks will be stored in libvirt_img_path

Virtual machine base disk are copied from the image file disk defined by the src_img parameter. The src_img will be searched in libvirt_img_path and therefore must be deployed by a previous task.

The module needs to access XML domain files. These files will be searched in libvirt_qemu_conf_path.

Be aware that if force_copy is set to yes and the corresponding virtual machine is shutoff, the module will overwrite the existing image disk. All data for that domain will be lost. However force_copy will have no effect on a running system.

Also note that, on the first run, the force_copy parameter must be set to yes to fully deploy a virtual machine.

Network configuration

When guests O.S are configured to get their IPv4 addresses via DHCP, it becomes possible to use libvirt DHCP capabilities to assign specific IP addresses to specific hosts. This should allow easy access to newly created virtual machines.

To make it possible a default network must have been configured (the default network configuration shipped with libvirt will do) and an handler must be triggered at VM creation (see example below).

Limitations

This module is not a complete implementation of libvirt and some libvirt parameters are not available.

This module does not make any consistency ckeck on parameters. It's up to users to provide a consistent configuration to prevent failed module run or inconsistent virtual machine state. However, libvirt validates XML domain files before defining the conresponding domain so an invalid configuration would not be possible.

Also note that some operations such as upgrading max_memory or some CPU parameters modifcations can not be done on a running system.

These operations will be tried but, as they need a full reboot of guest system, will not be forced. Rebooting guest OS when needed must be done manually and is user responsability.

Registering a variable may be useful for debugging purpose (especially the live_settings dict that will show what has been successfully, or not, applied to the running guest O.S).

Known bugs

Most bugs are still unknown 😃

Please, send reports or patches to contact@mcos.nc.

Parameters

Due to complex combinations of parameters, all parameters are mandatory:

var object type
uuid Domain unique identifier str. Must be UUID formated
name Domain name str. Should not contain space characters
description Description for domain str
src_img Name of disk image file template virtual machine. This disk image file will be used as a base disk and must be present on host str
disks List of disks that will be added to VM. Must be set as an empty list if no disk have to added. See example below List
force_copy If set to True. This system disk image file will be replaced (or created) by coying src_img. This operation applies only on shutdown system and will have no effect on a running system. bool
machine_type Virtual machine type (see documentation) str
vcpus Max number of CPUs used by virtual machine int
cpu_set Set of CPUs used by virtual machine (see documentation) str
max_memory Maximum of RAM allocation for guest O.S. Expressed in KiB. int
memory RAM size available for guest O.S. Expressed in KiB. int
mac_addr MAC address for guest interface. This MAC address will be assigned to guest network interface and used by libvirt DHCP str
ip_addr IP address for guest interface. This IP address will be provided to guest by libvirt DHCP str
spice_password Password to connect via spice protocol to domain str
spice_interface IPv4 address used to connect to domain via spice protocol. This address must be one of the host addresses (127.0.0.1 is allowed). str
spice_port TCP port used to connect to domain via spice protocol int
active Define domain state. Set to False, domain will be immediately destroyed. Set to True, domain will be started bool
autostart if set to True, domain will auto start with libvirt daemon bool
present If set to False, existing domain will be undefined and deleted. This operation is not recoverable. bool

Exemples

The following example will create two virtual machines, named foo and bar, from the template disk image centos_8.img.

The foo's disk path will be /var/lib/libvirt/images/foo.img while the bar's disk path will be /var/lib/libvirt/images/bar.img

192.168.122.2 IP address will be assigned to foo while 192.168.122.3 IP address will be assigned to bar by libvirt DHCP server.

The bar VM will receive a supplementary 100GB disk that will be seen as /dev/vdb by the guest O.S. (see the disks parameter).

---
# vars/kvm.yml
# -- ---------------------------------------------------------------------------
libvirt_img_path: /var/lib/libvirt/images
libvirt_tmp_path: /var/lib/libvirt/tmp
libvirt_qemu_conf_path: /etc/libvirt/qemu

kvm_vms:
- uuid: '63d0c0ee-5b01-11ea-8213-37bed4a19f1f'
  name: foo
  description: The famous Foo VM
  src_img: centos_8.img
  disks: []
  force_copy: yes
  machine_type: pc-q35-rhel7.6.0
  vcpus: 2
  cpu_set: 0-1
  memory_slots: 16
  max_memory: 4194304
  memory: 2097152
  mac_addr: '00:71:37:05:00:01'
  ip_addr: 192.168.122.2
  spice_password: GcwpZqi7zwSqRWZJcgOl
  spice_interface: "{{ ansible_default_ipv4.address }}"
  spice_port: 5901
  active: yes
  autostart: yes
  present: yes
- uuid: 'cee3cbde-e2c4-4206-af76-2c25d340a765'
  name: bar
  description: The infamous Bar VM
  src_img: centos_8.img
  disks:
    - name: vdb
      size: 100G
  force_copy: yes
  machine_type: pc-q35-rhel7.6.0
  vcpus: 1
  cpu_set: 2
  max_memory: 2097152
  memory: 1048576
  mac_addr: '00:71:37:05:00:02'
  ip_addr: 192.168.122.3
  spice_password: GcwpZqi7zwSqRWZJcgOl
  spice_interface: "{{ ansible_default_ipv4.address }}"
  spice_port: 5902
  active: yes
  autostart: yes
  present: yes
---
# tasks/kvm.yml
# -- ---------------------------------------------------------------------------
- name: include kvm vars
  include_vars: kvm.yml

- name: base VM image installation
  copy:
    src: files/centos_8.img
    dest: "{{ libvirt_img_path }}/"
    owner: root
    group: root
    mode: '0644'

- name: create libvirt tmp dir
  file:
    path: "{{ libvirt_tmp_path }}"
    state: directory
    owner: root
    group: root
    mode: 0755

- name: base VM base XML file installtion
  copy:
    src: files/kvm_base.xml
    dest: "{{ libvirt_tmp_path }}/"
    owner: root
    group: root
    mode: '0644'

- name: KVM virtual machines installation and configuration
  virt_vm:
    uuid: "{{ item.uuid }}"
    name: "{{ item.name }}"
    description: "{{ item.description }}"
    src_img: "{{ item.src_img }}"
    force_copy: "{{ item.force_copy }}"
    machine_type: "{{ item.machine_type }}"
    vcpus: "{{ item.vcpus }}"
    cpu_set: "{{ item.cpu_set }}"
    max_memory: "{{ item.max_memory }}"
    memory: "{{ item.memory }}"
    mac_addr: "{{ item.mac_addr }}"
    ip_addr: "{{ item.ip_addr }}"
    spice_password: "{{ item.spice_password }}"
    spice_interface: "{{ item.spice_interface | default( ansible_default_ipv4.address ) }}"
    spice_port: "{{ item.spice_port }}"
    active: "{{ item.active }}"
    autostart: "{{ item.autostart}}"
    present: "{{ item.present }}"
    libvirt_qemu_conf_path: "{{ item.libvirt_qemu_conf_path | default(libvirt_qemu_conf_path) }}"
    libvirt_img_path: "{{ item.libvirt_img_path | default(libvirt_img_path) }}"
    libvirt_tmp_path: "{{ item.libvirt_tmp_path | default(libvirt_tmp_path) }}"
  register: vms_creation
  loop: "{{ kvm_vms }}"
  notify:
  - add dhcp hosts

- name: kVM domains debug
  debug:
    msg: "{{ item }}"
  loop: "{{ vms_creation.results }}"

---
# handlers/main.yml
# -- ---------------------------------------------------------------------------

- name: add dhcp hosts
  vars:
    xml: "<host mac='{{ item.item.mac_addr}}' name='{{ item.item.name }}' ip='{{ item.item.ip_addr }}'/>"
  shell:
    cmd: "/usr/bin/virsh net-update default add ip-dhcp-host \"{{ xml }}\" --live --config"
  when: item.changed and not item.already_exists
  loop: "{{ vms_creation.results }}"