Skip to content

Allow specification of multiple VMs #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Aug 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 82 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Libvirt VM
==========

This role configures an creates (or destroys) a VM on a KVM hypervisor.
This role configures and creates (or destroys) VMs on a KVM hypervisor.

Requirements
------------
Expand All @@ -12,45 +12,70 @@ be preconfigured with libvirt/KVM.
Role Variables
--------------

`libvirt_vm_state`: set to `present` to create or `absent` to destroy the VM.
Defaults to `present`.
- `libvirt_vm_default_console_log_dir`: The default directory in which to store
VM console logs, if a VM-specific log file path is not given. Default is
"/var/log/libvirt/qemu/".

`libvirt_vm_name`: the name to assign to the VM.
- `libvirt_image_cache_path`: The directory in which to cache downloaded
images. Default is "/tmp/".

`libvirt_vm_memory_mb`: the memory to assign to the VM, in megabytes.
- `libvirt_vm_engine`: virtualisation engine. If not set, the role will attempt
to auto-detect the optimal engine to use.

`libvirt_vm_vcpus`: the number of VCPU cores to assign to the VM.
- `libvirt_vm_emulator`: path to emulator binary. If not set, the role will
attempt to auto-detect the correct emulator to use.

`libvirt_vm_engine`: virtualisation engine. If not set, the role will attempt
to auto-detect the optimal engine to use.
- `libvirt_vm_arch`: CPU architecture, default is `x86_64`.

`libvirt_vm_emulator`: path to emulator binary. If not set, the role will
attempt to auto-detect the correct emulator to use.
- `libvirt_vms`: list of VMs to be created/destroyed. Each one may have the
following attributes:

`libvirt_vm_arch`: CPU architecture, default is `x86_64`.
- `state`: set to `present` to create or `absent` to destroy the VM.
Defaults to `present`.

`libvirt_vm_machine`: Virtual machine type. Default is `None` if
`libvirt_vm_engine` is `kvm`, otherwise `pc-1.0`.
- `name`: the name to assign to the VM.

`libvirt_vm_cpu_mode`: Virtual machine CPU mode. Default is `host-passthrough`
if `libvirt_vm_engine` is `kvm`, otherwise `host-model`.
- `memory_mb`: the memory to assign to the VM, in megabytes.

`libvirt_vm_volumes`: a list of volumes to attach to the VM. Each volume is
defined with the following dict:
- `name`: Name to associate with the volume being created.
- `device`: `disk`
- `format`: options include `raw`, `qcow2`, `vmdk`. See `man virsh` for the
full range. Default is `qcow2`
- `capacity`: volume capacity (can be suffixed with M,G,T or MB,GB,TB, etc)
- `image`: (optional) a URL to an image with which the volume is initalised.
- `pool`: Name or UUID of the storage pool from which the volume should be
allocated.
- `vcpus`: the number of VCPU cores to assign to the VM.

`libvirt_vm_interfaces`: a list of network interfaces to attach to the VM.
Each network interface is defined with the following dict:
- `network`: Name of the network to which an interface should be attached.
- `machine`: Virtual machine type. Default is `None` if
`libvirt_vm_engine` is `kvm`, otherwise `pc-1.0`.

`libvirt_vm_image_cache_path`: path to cache downloaded images.
- `cpu_mode`: Virtual machine CPU mode. Default is `host-passthrough` if
`libvirt_vm_engine` is `kvm`, otherwise `host-model`.

- `volumes`: a list of volumes to attach to the VM. Each volume is
defined with the following dict:
- `name`: Name to associate with the volume being created.
- `device`: `disk`
- `format`: options include `raw`, `qcow2`, `vmdk`. See `man virsh` for the
full range. Default is `qcow2`
- `capacity`: volume capacity (can be suffixed with M,G,T or MB,GB,TB, etc)
- `image`: (optional) a URL to an image with which the volume is initalised.
- `pool`: Name or UUID of the storage pool from which the volume should be
allocated.

- `interfaces`: a list of network interfaces to attach to the VM.
Each network interface is defined with the following dict:
- `network`: Name of the network to which an interface should be attached.

- `console_log_enabled`: if `true`, log console output to a file at the
path specified by `console_log_path`, **instead of** to a PTY. If
`false`, direct terminal output to a PTY at serial port 0. Default is
`false`.

- `console_log_path`: Path to console log file. Default is
`{{ libvirt_vm_default_console_log_dir }}/{{ name }}-console.log`.


N.B. the following variables are deprecated: `libvirt_vm_state`,
`libvirt_vm_name`, `libvirt_vm_memory_mb`, `libvirt_vm_vcpus`,
`libvirt_vm_engine`, `libvirt_vm_machine`, `libvirt_vm_cpu_mode`,
`libvirt_vm_volumes`, `libvirt_vm_interfaces` and
`libvirt_vm_console_log_path`. If the variable `libvirt_vms` is left unset, its
default value will be a singleton list containing a VM specification using
these deprecated variables.

Dependencies
------------
Expand All @@ -61,22 +86,37 @@ Example Playbook
----------------

---
- name: Create a VM
- name: Create VMs
hosts: hypervisor
roles:
- role: stackhpc.libvirt-vm
libvirt_vm_state: present
libvirt_vm_name: 'my-vm'
libvirt_vm_memory_mb: 512
libvirt_vm_vcpus: 2
libvirt_vm_volumes:
- name: 'data'
device: 'disk'
format: 'qcow2'
capacity: '400GB'
pool: 'my-pool'
libvirt_vm_interfaces:
- network: 'br-datacentre'
libvirt_vms:
- state: present
name: 'vm1'
memory_mb: 512
vcpus: 2
volumes:
- name: 'data1'
device: 'disk'
format: 'qcow2'
capacity: '400GB'
pool: 'my-pool'
interfaces:
- network: 'br-datacentre'

- state: present
name: 'vm2'
memory_mb: 1024
vcpus: 1
volumes:
- name: 'data2'
device: 'disk'
format: 'qcow2'
capacity: '200GB'
pool: 'my-pool'
interfaces:
- network: 'br-datacentre'


Author Information
------------------
Expand Down
70 changes: 48 additions & 22 deletions defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
---
# State of the VM. May be 'present' or 'absent'.
libvirt_vm_state: present

# Name of the VM.
libvirt_vm_name:
# The default directory in which to store VM console logs, if a VM-specific log
# file path is not given.
libvirt_vm_default_console_log_dir: "/var/log/libvirt-consoles/"

# Memory in MB.
libvirt_vm_memory_mb:
# Path to cache downloaded images.
libvirt_image_cache_path: "/tmp/"

# Number of vCPUs.
libvirt_vm_vcpus:
# CPU architecture.
libvirt_vm_arch: x86_64

# Virtualisation engine. If not set, the role will attempt to auto-detect the
# optimal engine to use.
Expand All @@ -19,23 +18,50 @@ libvirt_vm_engine:
# correct emulator to use.
libvirt_vm_emulator:

# CPU architecture.
libvirt_vm_arch: x86_64
# A list of specifications of VMs to be created.
# For backwards compatibility, libvirt_vms defaults to a singleton list using
# the values of the deprecated variables below.
# See README.md or tasks/main.yml for these attributes' defaults.
libvirt_vms:
# State of the VM. May be 'present' or 'absent'.
- state: "{{ libvirt_vm_state }}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't think the defaults will work correctly in this case, since the variable is defined - it will be None. How about just keeping the old defaults on the deprecated variables?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, true. It seems more consistent to use the boolean flag in the default filter for all the variables though, rather than putting some of the defaults in here.


# Virtual machine type.
libvirt_vm_machine: "{{ None if libvirt_vm_engine == 'kvm' else 'pc-1.0' }}"
# Name of the VM.
name: "{{ libvirt_vm_name }}"

# Virtual machine CPU mode.
libvirt_vm_cpu_mode: "{{ 'host-passthrough' if libvirt_vm_engine == 'kvm' else 'host-model' }}"
# Memory in MB.
memory_mb: "{{ libvirt_vm_memory_mb }}"

# List of volumes.
libvirt_vm_volumes: []
# Number of vCPUs.
vcpus: "{{ libvirt_vm_vcpus }}"

# List of network interfaces.
libvirt_vm_interfaces: []
# Virtual machine type.
machine: "{{ libvirt_vm_machine }}"

# Path to cache downloaded images.
libvirt_vm_image_cache_path:
# Virtual machine CPU mode.
cpu_mode: "{{ libvirt_vm_cpu_mode }}"

# List of volumes.
volumes: "{{ libvirt_vm_volumes }}"

# List of network interfaces.
interfaces: "{{ libvirt_vm_interfaces }}"

# Path to console log file.
console_log_path: "{{ libvirt_vm_console_log_path }}"


### DEPRECATED ###
# Use the above settings for each item within `libvirt_vms`, instead of the
# below deprecated variables.

# List of authorized SSH public keys.
#libvirt_vm_public_keys: []
libvirt_vm_state:
libvirt_vm_machine:
libvirt_vm_cpu_mode:
libvirt_vm_volumes:
libvirt_vm_interfaces:
libvirt_vm_console_log_path:
# Required, so we should fail if not set.
#libvirt_vm_name:
#libvirt_vm_memory_mb:
#libvirt_vm_vcpus:
2 changes: 1 addition & 1 deletion meta/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
galaxy_info:
author: Mark Goddard
description: >
Role to configure and create a VM on a Libvirt/KVM hypervisor
Role to configure and create VMs on a Libvirt/KVM hypervisor
company: StackHPC Ltd
license: Apache2
min_ansible_version: 2.0
Expand Down
11 changes: 7 additions & 4 deletions tasks/destroy-vm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@
# exists before destroying it.
- name: Check the VM's status
virt:
name: "{{ libvirt_vm_name }}"
name: "{{ vm.name }}"
command: list_vms
register: result
become: yes

- block:
- name: Ensure the VM is absent
virt:
name: "{{ libvirt_vm_name }}"
name: "{{ vm.name }}"
state: destroyed
become: yes

- name: Ensure the VM is undefined
virt:
name: "{{ libvirt_vm_name }}"
name: "{{ vm.name }}"
command: undefine
when: libvirt_vm_name in result.list_vms
become: yes
when: vm.name in result.list_vms
5 changes: 3 additions & 2 deletions tasks/destroy-volumes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
destroy_virt_volume.sh
{{ item.name }}
{{ item.pool }}
with_items: "{{ libvirt_vm_volumes }}"
with_items: "{{ volumes }}"
register: volume_result
changed_when:
- volume_result | success
- volume_result is success
- (volume_result.stdout | from_json).changed | default(True)
become: yes
50 changes: 40 additions & 10 deletions tasks/main.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
---
- block:
- include: autodetect.yml
- include: volumes.yml
- include: vm.yml
when: libvirt_vm_state == 'present'

- block:
- include: destroy-volumes.yml
- include: destroy-vm.yml
when: libvirt_vm_state == 'absent'
- include_tasks: autodetect.yml
# We need to know the engine and emulator if we're creating any new VMs.
when: libvirt_vms | rejectattr('state', 'eq', 'absent') | list

- include_tasks: volumes.yml
vars:
volumes: "{{ vm.volumes | default([], true) }}"
with_items: "{{ libvirt_vms }}"
loop_control:
loop_var: vm
when: (vm.state | default('present', true)) == 'present'

- include_tasks: vm.yml
vars:
console_log_enabled: "{{ vm.console_log_enabled | default(false) }}"
console_log_path: "{{ vm.console_log_path | default(libvirt_vm_default_console_log_dir + '/' + vm.name + '-console.log', true) }}"
machine_default: "{{ none if libvirt_vm_engine == 'kvm' else 'pc-1.0' }}"
machine: "{{ vm.machine | default(machine_default, true) }}"
cpu_mode_default: "{{ 'host-passthrough' if libvirt_vm_engine == 'kvm' else 'host-model' }}"
cpu_mode: "{{ vm.cpu_mode | default(cpu_mode_default, true) }}"
volumes: "{{ vm.volumes | default([], true) }}"
interfaces: "{{ vm.interfaces | default([], true) }}"
with_items: "{{ libvirt_vms }}"
loop_control:
loop_var: vm
when: (vm.state | default('present', true)) == 'present'

- include_tasks: destroy-volumes.yml
vars:
volumes: "{{ vm.volumes | default([], true) }}"
with_items: "{{ libvirt_vms }}"
loop_control:
loop_var: vm
when: (vm.state | default('present', true)) == 'absent'

- include_tasks: destroy-vm.yml
with_items: "{{ libvirt_vms }}"
loop_control:
loop_var: vm
when: (vm.state | default('present', true)) == 'absent'
17 changes: 15 additions & 2 deletions tasks/vm.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
---
- name: Ensure the VM console log directory exists
file:
path: "{{ console_log_path | dirname }}"
state: directory
owner: qemu
group: qemu
recurse: true
mode: 0770
when: console_log_enabled | bool
become: true

- name: Ensure the VM is defined
virt:
name: "{{ libvirt_vm_name }}"
name: "{{ vm.name }}"
command: define
xml: "{{ lookup('template', 'vm.xml.j2') }}"
become: true

- name: Ensure the VM is running and started at boot
virt:
name: "{{ libvirt_vm_name }}"
name: "{{ vm.name }}"
autostart: true
state: running
become: true
Loading