Demo about Edge Computing in the Retail vertical using Red Hat products
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.
 
 

383 lines
12 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: test-vm
ipv4_address: 192.168.122.10/24
ipv4_gateway: 192.168.122.1
ipv4_nameserver: 192.168.122.1
network: default
rhel_version: 9
ram: 2048
vcpu: 2
disk: 20
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"
##
## 4.3. Add the VM to Ansible inventory
##
- block:
- debug:
var: add_host_params
when: libvirt_domains_debug | bool
- name: Add the VM to Ansible inventory
ansible.builtin.add_host: '{{ add_host_params }}'
vars:
add_host_params: '{{ original_args | combine(extra_arg_ip) | combine(extra_arg_hostname) }}'
extra_arg_hostname: '{{ {"hostname": libvirt_domain_name } if "name" not in original_args and "hostname" not in original_args and "host" not in original_args else {} }}'
extra_arg_ip: '{{ {"ansible_ssh_host": vm_ip } if "ansible_ssh_host" not in original_args else {} }}'
vm_ip: '{{ libvirt_domain_domifaddr_external_ip | ansible.utils.ipaddr("ipv4") | list | first }}'
original_args: '{{ libvirt_domain.add_host }}'
when: >
libvirt_domain.state | default("present") == "present"
##
## 5. Configure the VM
##
- name: Configure a RHEL VM created with cloud-init
hosts: vm
gather_facts: no
tasks:
- wait_for_connection:
- ansible.builtin.setup:
- debug:
msg: "Hello from {{ inventory_hostname }} in {{ group_names | join(',') }} !"