4 Commits

10 changed files with 105 additions and 54 deletions
+7 -4
View File
@@ -5,7 +5,9 @@ This project provides an Ansible-based framework to automatically provision virt
## 🚀 Features ## 🚀 Features
- **Automated Host Setup**: Installs and configures `libvirt`, `qemu-kvm`, and `libguestfs-tools`. - **Automated Host Setup**: Installs and configures `libvirt`, `qemu-kvm`, and `libguestfs-tools`.
- **Ignition Support**: Generates and injects Ignition configuration for all supported OSs via the `fw_cfg` QEMU feature. - **Immutable OS Support**: Handles the specific boot-time configuration requirements for:
- **CoreOS/Flatcar**: Generates and injects Ignition JSON configurations.
- **MicroOS**: Generates and injects Cloud-init user-data.
- **Custom User Provisioning**: Automatically creates a default user with a hashed password and injects your SSH public key. - **Custom User Provisioning**: Automatically creates a default user with a hashed password and injects your SSH public key.
- **Modular Design**: Uses Ansible roles for host preparation, configuration generation, and VM provisioning. - **Modular Design**: Uses Ansible roles for host preparation, configuration generation, and VM provisioning.
@@ -20,7 +22,7 @@ ansible-kvm-vms/
│ └── vms.yml # List of VMs to create with CPU, RAM, and Disk specs │ └── vms.yml # List of VMs to create with CPU, RAM, and Disk specs
├── roles/ ├── roles/
│ ├── kvm_host_setup/ # Installs virtualization dependencies on the host │ ├── kvm_host_setup/ # Installs virtualization dependencies on the host
│ ├── os_config/ # Generates Ignition configuration files │ ├── os_config/ # Generates Ignition/Cloud-init config files
│ └── vm_provision/ # Downloads images and creates VMs via virt-install │ └── vm_provision/ # Downloads images and creates VMs via virt-install
└── playbooks/ └── playbooks/
└── create_vms.yml # Main orchestration playbook └── create_vms.yml # Main orchestration playbook
@@ -65,8 +67,9 @@ Edit `vars/vms.yml` to add or modify the VMs you wish to deploy. You can specify
## 🔍 How it Works ## 🔍 How it Works
Since immutable OSs do not use traditional installers, this setup uses a "seed" approach: Since immutable OSs do not use traditional installers, this setup uses a "seed" approach:
1. **Config Generation**: The `os_config` role creates a JSON Ignition configuration file based on your variables. 1. **Config Generation**: The `os_config` role creates a JSON (Ignition) or YAML (Cloud-init) file based on your variables.
2. **Deployment**: `virt-install` is used to create the VM with UEFI boot. The `--sysinfo` flag is used to provide the Ignition config via the `fw_cfg` device, which the immutable OSs (CoreOS, Flatcar, MicroOS) process at first boot. 2. **Image Customization**: The `vm_provision` role downloads the official `.qcow2` cloud image and uses `virt-customize` (from `libguestfs-tools`) to inject the configuration directly into the disk image before the VM is started.
3. **Deployment**: `virt-install` is used to create the VM with UEFI boot and the customized disk.
## 🌐 Accessing your VMs ## 🌐 Accessing your VMs
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
# VM names from vars/vms.yml
VMS=("coreos-vm" "flatcar-vm" "microos-vm")
# Image directory from group_vars/all.yml
IMAGES_DIR="/var/lib/libvirt/images"
for vm in "${VMS[@]}"; do
echo "Cleaning up VM: $vm"
# Destroy the VM (force stop)
sudo virsh destroy "$vm" 2>/dev/null || echo "VM $vm is not running."
# Undefine the VM (remove configuration)
sudo virsh undefine "$vm" 2>/dev/null || echo "VM $vm is not defined."
# Remove the disk image
sudo rm -f "$IMAGES_DIR/$vm.qcow2"
sudo rm -f "$IMAGES_DIR/$vm.download"
echo "VM $vm cleaned up."
done
echo "Cleanup complete."
+1 -1
View File
@@ -7,7 +7,7 @@ vm_ssh_public_key: "~/.ssh/id_vms.pub" # Path to your public key for SSH access
# Default VM resources # Default VM resources
default_cpu: 2 default_cpu: 2
default_ram: 2048 default_ram: 2048
default_disk: "20G" default_disk: 20 # GB
# Storage path for images # Storage path for images
vm_images_dir: "/var/lib/libvirt/images" vm_images_dir: "/var/lib/libvirt/images"
-2
View File
@@ -27,6 +27,4 @@
cpu: "{{ item.cpu }}" cpu: "{{ item.cpu }}"
ram: "{{ item.ram }}" ram: "{{ item.ram }}"
disk: "{{ item.disk }}" disk: "{{ item.disk }}"
disk_bus: "{{ item.disk_bus }}"
loop: "{{ vms }}" loop: "{{ vms }}"
+14 -2
View File
@@ -1,6 +1,18 @@
--- ---
- name: Generate Ignition config
- name: Generate Ignition configuration for CoreOS/Flatcar
template: template:
src: ignition.json.j2 src: ignition.json.j2
dest: "/tmp/{{ vm_name }}.ign" dest: "{{ vm_images_dir }}/{{ vm_name }}.ign"
when: os_type == 'coreos' or os_type == 'flatcar'
- name: Generate Cloud-init configuration for MicroOS
template:
src: user-data.yaml.j2
dest: "{{ vm_images_dir }}/{{ vm_name }}_user-data"
when: os_type == 'microos'
- name: Generate dummy meta-data file
copy:
content: "instance-id: {{ vm_name }}\nlocal-hostname: {{ vm_name }}\n"
dest: "{{ vm_images_dir }}/{{ vm_name }}_meta-data"
@@ -1,13 +1,14 @@
{ {
"ignition": { "ignition": {
"version": "3.0.0" "version": "3.4.0"
}, },
"passwd": { "passwd": {
"users": [ "users": [
{ {
"name": "{{ vm_user }}", "name": "{{ vm_user }}",
"passwordHash": "{{ vm_password | password_hash('sha512') }}",
"sshAuthorizedKeys": [ "sshAuthorizedKeys": [
"{{ lookup('file', vm_ssh_public_key | replace('~', lookup('env', 'HOME'))) }}" "{{ lookup('file', vm_ssh_public_key) | trim }}"
] ]
} }
] ]
@@ -1,11 +0,0 @@
#cloud-config
users:
- name: {{ vm_user }}
passwd: {{ vm_password | password_hash('sha512') }}
ssh_authorized_keys:
- {{ lookup('file', vm_ssh_public_key | replace('~', lookup('env', 'HOME'))) }}
sudo: ALL=(ALL) NOPASSWD:ALL
write_files:
- path: /etc/ssh/sshd_config.d/permit_root_login.conf
content: |
PermitRootLogin yes
@@ -1,7 +1,19 @@
#cloud-config #cloud-config
users: users:
- name: {{ vm_user }} - name: {{ vm_user }}
sudo: ALL=(ALL) NOPASSWD:ALL passwd: {{ vm_password | password_hash('sha512') }}
ssh_authorized_keys: ssh_authorized_keys:
- {{ lookup('file', vm_ssh_public_key | replace('~', lookup('env', 'HOME'))) }} - {{ lookup('file', vm_ssh_public_key) | trim }}
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: false lock_passwd: false
- name: root
passwd: {{ vm_password | password_hash('sha512') }}
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: false
runcmd:
- mkdir -p /etc/ssh/sshd_config.d
- echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/permit_root_login.conf
- systemctl restart sshd
@@ -1,10 +1,9 @@
--- ---
- name: Define image URLs - name: Define image URLs
set_fact: set_fact:
os_images: os_images:
coreos: "https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/44.20260510.3.1/x86_64/fedora-coreos-44.20260510.3.1-qemu.x86_64.qcow2.xz" coreos: "https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/44.20260510.3.1/x86_64/fedora-coreos-44.20260510.3.1-qemu.x86_64.qcow2.xz"
flatcar: "https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_qemu_uefi_image.img" flatcar: "https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_qemu_image.img"
microos: "https://ftp.halifax.rwth-aachen.de/opensuse/tumbleweed/appliances/openSUSE-MicroOS.x86_64-kvm-and-xen.qcow2" microos: "https://ftp.halifax.rwth-aachen.de/opensuse/tumbleweed/appliances/openSUSE-MicroOS.x86_64-kvm-and-xen.qcow2"
- name: Verify internet connectivity - name: Verify internet connectivity
@@ -23,50 +22,66 @@
- name: Handle compressed or raw images - name: Handle compressed or raw images
shell: | shell: |
DOWNLOAD_FILE="{{ vm_images_dir }}/{{ vm_name }}.download" DOWNLOAD_FILE="{{ vm_images_dir }}/{{ vm_name }}.download"
FINAL_FILE="{{ vm_images_dir }}/{{ vm_name }}.qcow2" FINAL_FILE="{{ vm_images_dir }}/{{ vm_name }}"
# 1. Handle XZ compression # 1. Handle XZ compression
if [[ "{{ os_images[os_type] }}" == *.xz ]]; then if [[ "{{ os_images[os_type] }}" == *.xz ]]; then
echo "Decompressing XZ image..." echo "Decompressing XZ image..."
unxz -c "$DOWNLOAD_FILE" > "$FINAL_FILE" unxz -c "$DOWNLOAD_FILE" > "$FINAL_FILE".qcow2
elif [[ "{{ os_images[os_type] }}" == *.img ]]; then elif [[ "{{ os_images[os_type] }}" == *.img ]]; then
echo "Converting RAW image to QCOW2..." echo "Moving IMG image to final destination..."
qemu-img convert -f raw -O qcow2 "$DOWNLOAD_FILE" "$FINAL_FILE" mv "$DOWNLOAD_FILE" "$FINAL_FILE".img
else else
echo "Moving QCOW2 image to final destination..." echo "Moving QCOW2 image to final destination..."
mv "$DOWNLOAD_FILE" "$FINAL_FILE" mv "$DOWNLOAD_FILE" "$FINAL_FILE".qcow2
fi fi
rm -f "$DOWNLOAD_FILE" rm -f "$DOWNLOAD_FILE"
become: yes become: yes
args: args:
creates: "{{ vm_images_dir }}/{{ vm_name }}.qcow2" creates: "{{ vm_images_dir }}/{{ vm_name }}.qcow2"
- name: Debug Provisioning Vars
debug:
msg: "Provisioning {{ vm_name }} with disk_bus={{ disk_bus | default('virtio') }} and os_variant={{ os_variant }}"
- name: Remove existing VM definition
shell: |
virsh destroy {{ vm_name }} || true
virsh undefine {{ vm_name }} --nvram || true
rm -f /etc/libvirt/qemu/{{ vm_name }}.xml
become: yes
- name: Provision VM using virt-install - name: Provision VM using virt-install
shell: | shell: |
{% if os_type == 'coreos' %}
virt-install \
--connect qemu:///system \
--name {{ vm_name }} \
--vcpus {{ cpu | default(default_cpu) }} \
--memory {{ ram | default(default_ram) }} \
--disk size={{ disk | default('10G') }},backing_store={{ vm_images_dir }}/{{ vm_name }}.qcow2,backing_format=qcow2 \
--os-variant {{ os_variant }} \
--network network=default \
--graphics none \
--noautoconsole \
--boot uefi \
--sysinfo type=fwcfg,entry0.name=opt/com.coreos/config,entry0.file={{ vm_images_dir }}/{{ vm_name }}.ign
{% elif os_type == 'flatcar' %}
virt-install \
--connect qemu:///system \
--name {{ vm_name }} \
--vcpus {{ cpu | default(default_cpu) }} \
--memory {{ ram | default(default_ram) }} \
--disk path={{ vm_images_dir }}/{{ vm_name }}.img,format=qcow2,bus=virtio \
--import \
--os-variant {{ os_variant }} \
--network network=default \
--graphics none \
--noautoconsole \
--sysinfo system.serial=flatcar.first_boot=1 \
--qemu-commandline="-fw_cfg name=opt/org.flatcar-linux/config,file=/{{ vm_images_dir }}/{{ vm_name }}.ign"
{% elif os_type == 'microos' %}
virt-install \ virt-install \
--name {{ vm_name }} \ --name {{ vm_name }} \
--vcpus {{ cpu | default(default_cpu) }} \ --vcpus {{ cpu | default(default_cpu) }} \
--memory {{ ram | default(default_ram) }} \ --memory {{ ram | default(default_ram) }} \
--machine q35 \ --disk path={{ vm_images_dir }}/{{ vm_name }}.qcow2,bus=virtio \
--disk path={{ vm_images_dir }}/{{ vm_name }}.qcow2,bus={{ disk_bus | default('virtio') }} \
--import \ --import \
--os-variant {{ os_variant }} \ --os-variant {{ os_variant }} \
--network network=default \ --network network=default \
--graphics none \ --graphics none \
--noautoconsole \ --noautoconsole \
--boot uefi \ --boot uefi \
--sysinfo type=fwcfg,entry0.name="opt/com.coreos/config",entry0.file="/tmp/{{ vm_name }}.ign" --cloud-init user-data={{ vm_images_dir }}/{{ vm_name }}_user-data,meta-data={{ vm_images_dir }}/{{ vm_name }}_meta-data
{% endif %}
args: args:
creates: "/etc/libvirt/qemu/{{ vm_name }}.xml" creates: "/etc/libvirt/qemu/{{ vm_name }}.xml"
become: yes
+3 -6
View File
@@ -3,23 +3,20 @@ vms:
- name: coreos-vm - name: coreos-vm
os_type: coreos os_type: coreos
os_variant: "fedora-coreos-stable" os_variant: "fedora-coreos-stable"
disk_bus: "virtio"
cpu: 2 cpu: 2
ram: 2048 ram: 2048
disk: "20G" disk: 20 # GB
- name: flatcar-vm - name: flatcar-vm
os_type: flatcar os_type: flatcar
os_variant: "fedora-coreos-stable" os_variant: "fedora-coreos-stable"
disk_bus: "virtio"
cpu: 2 cpu: 2
ram: 2048 ram: 2048
disk: "20G" disk: 20 # GB
- name: microos-vm - name: microos-vm
os_type: microos os_type: microos
os_variant: "generic" os_variant: "opensusemicroos"
disk_bus: "virtio"
cpu: 2 cpu: 2
ram: 2048 ram: 2048
disk: "20G" disk: "20G"