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.
348 lines
11 KiB
348 lines
11 KiB
##
|
|
## Pre-requisites on the Ansible Controller
|
|
##
|
|
## $ dnf install -y ansible python3-jmespath python3-netaddr
|
|
##
|
|
- name: Create a VM with cloud-init and using an OCI artefact as disk source
|
|
hosts: rhde
|
|
gather_facts: false
|
|
##
|
|
## Pre-requisites on the managed node
|
|
##
|
|
## $ dnf install -y genisoimage
|
|
##
|
|
vars:
|
|
##
|
|
## Default values
|
|
##
|
|
libvirt_images_path: /var/lib/libvirt/images
|
|
libvirt_domains_debug: '{{ debug | default(True) | bool }}'
|
|
has_podman_artifact_extract: false
|
|
libvirt_domain_mac_address: "{{ '04:00:' ~ (libvirt_domain_parameters.ipv4_address | ansible.utils.ipv4('address') | ansible.utils.ip4_hex(':')) }}"
|
|
|
|
##
|
|
## Parameters of the VM to create
|
|
##
|
|
|
|
libvirt_domain_parameters:
|
|
name: "{( vm_name )}"
|
|
ipv4_address: "{( vm_ip )}"/24
|
|
ipv4_gateway: 192.168.122.1
|
|
ipv4_nameserver: 192.168.122.1
|
|
network: default
|
|
rhel_version: "{( rhel_version )}"
|
|
ram: "{( vm_ram )}"
|
|
vcpu: "{( vm_vcpu )}"
|
|
disk: "{( vm_disk )}"
|
|
architecture: x86_64
|
|
|
|
##
|
|
## Template of the VM to create
|
|
##
|
|
|
|
libvirt_domain:
|
|
name: '{{ libvirt_domain_parameters.name }}'
|
|
state: '{{ state | default("present") }}'
|
|
# Cloud-init sources
|
|
cloud_init:
|
|
templates:
|
|
- src: cloud-init/user-data.j2
|
|
dest: user-data
|
|
- src: cloud-init/meta-data.j2
|
|
dest: meta-data
|
|
- src: cloud-init/network-config.j2
|
|
dest: network-config
|
|
# Root disk source
|
|
root_disk:
|
|
##
|
|
## HEADS UP ! The RHEL qcow2 is stored in an OCI registry as a Podman artifact
|
|
##
|
|
## $ podman artifact add edge-registry.itix.fr/demo-edge-retail/rhel9:x86_64 rhel-9.6-x86_64-kvm.qcow2
|
|
## $ podman artifact push edge-registry.itix.fr/demo-edge-retail/rhel9:x86_64
|
|
## $ podman artifact rm edge-registry.itix.fr/demo-edge-retail/rhel9:x86_64
|
|
##
|
|
src: edge-registry.itix.fr/demo-edge-retail/rhel{{ libvirt_domain_parameters.rhel_version }}:{{ libvirt_domain_parameters.architecture }}
|
|
dest: root.qcow2
|
|
# virt-install parameters
|
|
virt_install:
|
|
- autostart:
|
|
- cpu: host-passthrough
|
|
- vcpus: '{{ libvirt_domain_parameters.vcpu }}'
|
|
- ram: '{{ libvirt_domain_parameters.ram }}'
|
|
- os-variant: 'rhel{{ libvirt_domain_parameters.rhel_version }}-unknown'
|
|
- disk:
|
|
path: '{{ libvirt_images_path }}/{{ libvirt_domain_parameters.name }}/root.qcow2'
|
|
bus: virtio
|
|
serial: root
|
|
size: '{{ libvirt_domain_parameters.disk }}'
|
|
- network:
|
|
network: '{{ libvirt_domain_parameters.network }}'
|
|
mac.address: '{{ libvirt_domain_mac_address }}'
|
|
- console:
|
|
pty:
|
|
target.type: virtio
|
|
- serial:
|
|
pty:
|
|
- graphics: none
|
|
- disk:
|
|
path: '{{ libvirt_images_path }}/{{ libvirt_domain_parameters.name }}/cloud-init.iso'
|
|
readonly: on
|
|
- import:
|
|
- sysinfo:
|
|
system.serial: ds=nocloud
|
|
# Post-install: add_host parameters
|
|
add_host:
|
|
group: vm
|
|
ansible_user: demo
|
|
ansible_become: yes
|
|
ansible_ssh_common_args: -J {{ ansible_user }}@{{ inventory_hostname }} -o StrictHostKeyChecking=no
|
|
|
|
tasks:
|
|
|
|
##
|
|
## 1. Variable setup and debug
|
|
##
|
|
|
|
- debug:
|
|
var: libvirt_domain
|
|
when: libvirt_domains_debug|bool
|
|
|
|
- set_fact:
|
|
libvirt_domain_name: '{{ libvirt_domain.name }}'
|
|
libvirt_domain_path: '/var/lib/libvirt/images/{{ libvirt_domain.name }}'
|
|
libvirt_domain_action: none
|
|
|
|
- community.libvirt.virt:
|
|
command: list_vms
|
|
register: virsh_list
|
|
|
|
- community.libvirt.virt:
|
|
command: list_vms
|
|
state: running
|
|
register: virsh_list_running
|
|
|
|
- set_fact:
|
|
libvirt_existing_domains: '{{ virsh_list.list_vms }}'
|
|
libvirt_running_domains: '{{ virsh_list_running.list_vms }}'
|
|
|
|
- debug:
|
|
var: variables
|
|
vars:
|
|
variables:
|
|
libvirt_domain_name: '{{ libvirt_domain_name }}'
|
|
libvirt_domain_path: '{{ libvirt_domain_path }}'
|
|
libvirt_existing_domains: '{{ libvirt_existing_domains }}'
|
|
when: libvirt_domains_debug | bool
|
|
|
|
##
|
|
## 2. Delete the VM if state is "absent"
|
|
##
|
|
|
|
- block:
|
|
- name: Shutdown Libvirt domain
|
|
community.libvirt.virt:
|
|
name: '{{ libvirt_domain_name }}'
|
|
command: destroy
|
|
when: libvirt_domain_name in libvirt_running_domains
|
|
|
|
- name: Delete Libvirt domain
|
|
community.libvirt.virt:
|
|
name: '{{ libvirt_domain_name }}'
|
|
command: undefine
|
|
|
|
- name: Remove the domain files
|
|
ansible.builtin.file:
|
|
path: '{{ libvirt_domain_path }}'
|
|
state: absent
|
|
when: >
|
|
libvirt_domain.state | default("present") == "absent"
|
|
and libvirt_domain_name in libvirt_existing_domains
|
|
|
|
##
|
|
## 3. Create the VM if state is "present" and the VM does not already exist
|
|
##
|
|
|
|
- block:
|
|
##
|
|
## 3.1. Pre-requisites
|
|
##
|
|
- name: Ensure the domain root directory exists
|
|
file:
|
|
path: '{{ libvirt_domain_path }}'
|
|
owner: qemu
|
|
group: qemu
|
|
mode: 0700
|
|
state: directory
|
|
|
|
##
|
|
## 3.2. Cloud-init setup
|
|
##
|
|
- name: Create a directory for cloud-init
|
|
file:
|
|
path: '{{ libvirt_domain_path }}/cloud-init'
|
|
owner: qemu
|
|
group: qemu
|
|
mode: 0700
|
|
state: directory
|
|
|
|
- name: Template all cloud-init files
|
|
template:
|
|
dest: '{{ libvirt_domain_path }}/cloud-init/{{ cloud_init_template.dest }}'
|
|
src: '{{ cloud_init_template.src }}'
|
|
loop: '{{ libvirt_domain.cloud_init.templates }}'
|
|
loop_control:
|
|
label: '{{ cloud_init_template.dest }}'
|
|
loop_var: cloud_init_template
|
|
|
|
- name: Create the cloud-init.iso
|
|
command:
|
|
cmd: genisoimage -output {{ target_file }} -volid cidata -joliet -rock {{ source_files | join(" ") }}
|
|
chdir: '{{ libvirt_domain_path }}/cloud-init'
|
|
creates: '{{ target_file }}'
|
|
vars:
|
|
target_file: '{{ libvirt_domain_path }}/cloud-init.iso'
|
|
source_files: '{{ [ libvirt_domain_path ~ "/cloud-init/" ] | product(libvirt_domain.cloud_init.templates | map(attribute="dest")) | map("join") | list }}'
|
|
|
|
- name: Fix permissions on the cloud-init image
|
|
file:
|
|
path: '{{ libvirt_domain_path }}/cloud-init.iso'
|
|
owner: qemu
|
|
group: qemu
|
|
mode: 0600
|
|
|
|
##
|
|
## 3.3. Root disk setup
|
|
##
|
|
- name: Check if domain root disk exists
|
|
ansible.builtin.stat:
|
|
path: '{{ libvirt_domain_path }}/{{ libvirt_domain.root_disk.dest }}'
|
|
register: domain_root_disk
|
|
|
|
- block:
|
|
- name: Pull the OCI artefact
|
|
command:
|
|
cmd: podman artifact pull {{ libvirt_domain.root_disk.src }}
|
|
environment:
|
|
REGISTRY_AUTH_FILE: /etc/ostree/auth.json
|
|
|
|
- name: Extract the OCI artefact
|
|
command:
|
|
cmd: podman artifact extract {{ libvirt_domain.root_disk.src }} {{ libvirt_domain_path }}/{{ libvirt_domain.root_disk.dest }}
|
|
creates: '{{ libvirt_domain_path }}/{{ libvirt_domain.root_disk.dest }}'
|
|
environment:
|
|
REGISTRY_AUTH_FILE: /etc/ostree/auth.json
|
|
when: has_podman_artifact_extract | bool
|
|
|
|
- block:
|
|
- name: Get artifact metadata
|
|
ansible.builtin.command: "podman artifact inspect {{ libvirt_domain.root_disk.src }}"
|
|
register: artifact_inspect_result
|
|
changed_when: false
|
|
|
|
- name: Find blob in podman storage
|
|
ansible.builtin.find:
|
|
paths: /var/lib/containers/storage
|
|
patterns: "{{ blob_hash }}"
|
|
recurse: true
|
|
register: find_blob_result
|
|
|
|
- name: Blob not found
|
|
ansible.builtin.fail:
|
|
msg: "Blob with hash {{ blob_hash }} cannot be found."
|
|
when: find_blob_result.matched == 0
|
|
|
|
- name: Copy blob to final destination
|
|
ansible.builtin.copy:
|
|
src: "{{ find_blob_result.files[0].path }}"
|
|
dest: '{{ libvirt_domain_path }}/{{ libvirt_domain.root_disk.dest }}'
|
|
remote_src: true
|
|
owner: qemu
|
|
group: qemu
|
|
mode: 0600
|
|
|
|
when: not has_podman_artifact_extract | bool
|
|
vars:
|
|
artifact_manifest: "{{ artifact_inspect_result.stdout | from_json }}"
|
|
layer_info: "{{ artifact_manifest.Manifest.layers[0] }}"
|
|
blob_hash: "{{ layer_info.digest | replace('sha256:', '') }}"
|
|
|
|
when: not domain_root_disk.stat.exists
|
|
|
|
##
|
|
## 3.4. VM creation
|
|
##
|
|
- block:
|
|
- debug:
|
|
var: virt_install_commandline_s
|
|
when: libvirt_domains_debug | bool
|
|
vars:
|
|
virt_install_commandline_s: '{{ virt_install_commandline | join(" ") }}'
|
|
|
|
- name: Run virt-install
|
|
command:
|
|
argv: '{{ virt_install_commandline }}'
|
|
|
|
- set_fact:
|
|
libvirt_domain_action: created
|
|
vars:
|
|
virt_install_commandline: '{{ lookup("template", "virt-install-cmdline.j2") }}'
|
|
|
|
when: >
|
|
libvirt_domain.state | default("present") == "present"
|
|
and libvirt_domain_name not in libvirt_existing_domains
|
|
|
|
##
|
|
## 4. Post-installation tasks
|
|
##
|
|
|
|
- block:
|
|
##
|
|
## 4.1. Fetch the IP address of the VM
|
|
##
|
|
- name: Fetch addresses from the Qemu Guest agent
|
|
command: virsh domifaddr {{ libvirt_domain_name }} --source agent --full
|
|
environment:
|
|
LANG: C
|
|
LC_ALL: C
|
|
register: virsh_domifaddr
|
|
failed_when: >
|
|
virsh_domifaddr.rc != 0 or virsh_domifaddr.stdout_lines | length < 5
|
|
or external_ip | length < 1
|
|
until: "virsh_domifaddr is not failed"
|
|
retries: 100
|
|
delay: 5
|
|
|
|
- set_fact:
|
|
libvirt_domain_domifaddr: '{{ addresses }}'
|
|
libvirt_domain_domifaddr_external_ip: '{{ external_ip }}'
|
|
|
|
- debug:
|
|
var: variables
|
|
vars:
|
|
variables:
|
|
libvirt_domain_domifaddr: '{{ libvirt_domain_domifaddr }}'
|
|
libvirt_domain_domifaddr_external_ip: '{{ libvirt_domain_domifaddr_external_ip }}'
|
|
when: libvirt_domains_debug | bool
|
|
|
|
vars:
|
|
domifaddr_output: '{{ virsh_domifaddr.stdout_lines }}'
|
|
addresses: >
|
|
{{ domifaddr_output[2:] | map("split", None) | community.general.json_query('[*].{"ifname": [0], "mac": [1], "proto": [2], "address": [3]}') }}
|
|
external_ip: '{{ addresses | rejectattr("ifname", "eq", "lo") | selectattr("proto", "eq", "ipv4") | map(attribute="address") | ansible.utils.ipaddr("address") | list }}'
|
|
when: >
|
|
libvirt_domain.state | default("present") == "present"
|
|
|
|
##
|
|
## 4.2. Update SSH known_hosts
|
|
##
|
|
- name: Remove IP / hostnames of the previous VM from the SSH known_hosts file
|
|
local_action:
|
|
module: command
|
|
args: ssh-keygen -R {{ hostname }}
|
|
vars:
|
|
ansible_become: false
|
|
loop: '{{ libvirt_domain_domifaddr_external_ip | default([]) }}'
|
|
loop_control:
|
|
loop_var: hostname
|
|
label: '{{ hostname }}'
|
|
when: libvirt_domain_action == "created"
|
|
|