Files
swarm-master/ansible/roles/vm/tasks/main.yml
William Valentin ea5e2c2ef3 Add orb and sun VMs with virtiofs swarm-common share
- Add orb (192.168.122.183) and sun (192.168.122.184) to inventory
- Create host_vars for orb and sun (fresh install, brew_packages: [])
- Add brew_packages to zap host_vars (gogcli, himalaya, kubernetes-cli, opencode)
- customize.yml: parameterize brew_packages via host_vars, add /mnt/swarm-common
  virtiofs+bindfs mount for all VMs, install bindfs, fix Homebrew install
- provision-vm.yml: remove become requirement; use virsh vol commands for all
  disk/image operations (no sudo needed)
- roles/vm/tasks/main.yml: rewrite disk provisioning to use virsh vol-create-as
  and vol-upload; fix vol name quoting for names with spaces; use qcow2 backing
- domain.xml.j2: always include swarm-common virtiofs share; make main share
  conditional on vm_virtiofs_source/tag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 11:06:08 -07:00

202 lines
6.9 KiB
YAML

---
# Provision a KVM/libvirt VM from an Ubuntu cloud image.
# Runs on the hypervisor host (localhost) without requiring sudo.
# All writes to /var/lib/libvirt/images/ go through libvirtd (virsh vol-upload).
- name: Validate required variables
ansible.builtin.assert:
that:
- vm_domain | length > 0
- vm_hostname | length > 0
- vm_disk_path | length > 0
- vm_mac | length > 0
- vm_ip | length > 0
fail_msg: "vm_domain, vm_hostname, vm_disk_path, vm_mac, and vm_ip must all be set in host_vars"
# ── Cloud image ────────────────────────────────────────────────────────────
- name: Check if cloud image volume exists in pool
ansible.builtin.command:
cmd: virsh -c {{ vm_libvirt_uri }} vol-info --pool default {{ vm_cloud_image_cache | basename }}
register: cloud_image_vol_stat
failed_when: false
changed_when: false
- name: Download Ubuntu cloud image to temp path
ansible.builtin.get_url:
url: "{{ vm_cloud_image_url }}"
dest: "/tmp/{{ vm_cloud_image_cache | basename }}"
mode: "0644"
timeout: 600
when: cloud_image_vol_stat.rc != 0
- name: Create cloud image volume in pool
ansible.builtin.shell:
cmd: >
virsh -c {{ vm_libvirt_uri }} vol-create-as default
'{{ vm_cloud_image_cache | basename }}' 4G --format raw
register: cloud_vol_create
failed_when:
- cloud_vol_create.rc != 0
- "'exists already' not in cloud_vol_create.stderr"
changed_when: cloud_vol_create.rc == 0
when: cloud_image_vol_stat.rc != 0
- name: Upload cloud image to pool
ansible.builtin.shell:
cmd: >
virsh -c {{ vm_libvirt_uri }} vol-upload
--pool default '{{ vm_cloud_image_cache | basename }}'
'/tmp/{{ vm_cloud_image_cache | basename }}'
when: cloud_image_vol_stat.rc != 0 and (cloud_vol_create.rc | default(1)) == 0
- name: Remove temp cloud image download
ansible.builtin.file:
path: "/tmp/{{ vm_cloud_image_cache | basename }}"
state: absent
when: cloud_image_vol_stat.rc != 0
# ── Disk image ─────────────────────────────────────────────────────────────
- name: Check if VM disk volume exists in pool
ansible.builtin.shell:
cmd: virsh -c {{ vm_libvirt_uri }} vol-info --pool default '{{ vm_disk_path | basename }}'
register: vm_disk_vol_stat
failed_when: false
changed_when: false
- name: Create VM disk volume backed by cloud image
ansible.builtin.shell:
cmd: >
virsh -c {{ vm_libvirt_uri }} vol-create-as default
'{{ vm_disk_path | basename }}' {{ vm_disk_size }} --format qcow2
--backing-vol '{{ vm_cloud_image_cache | basename }}'
--backing-vol-format qcow2
register: disk_vol_create
failed_when:
- disk_vol_create.rc != 0
- "'exists already' not in disk_vol_create.stderr"
changed_when: disk_vol_create.rc == 0
when: vm_disk_vol_stat.rc != 0
# ── Cloud-init seed ISO ────────────────────────────────────────────────────
- name: Create cloud-init temp directory
ansible.builtin.tempfile:
state: directory
suffix: cloud-init
register: cloud_init_dir
- name: Write cloud-init user-data
ansible.builtin.template:
src: cloud-init-user-data.j2
dest: "{{ cloud_init_dir.path }}/user-data"
mode: "0644"
vars:
vm_ssh_keys: "{{ openclaw_ssh_keys | default([]) }}"
- name: Write cloud-init meta-data
ansible.builtin.template:
src: cloud-init-meta-data.j2
dest: "{{ cloud_init_dir.path }}/meta-data"
mode: "0644"
- name: Set seed ISO facts
ansible.builtin.set_fact:
vm_seed_iso_name: "{{ vm_hostname }}-seed.iso"
vm_seed_iso_tmp: "/tmp/{{ vm_hostname }}-seed.iso"
vm_seed_iso: "/var/lib/libvirt/images/{{ vm_hostname }}-seed.iso"
- name: Create cloud-init seed ISO in temp path
ansible.builtin.command:
cmd: >
genisoimage -output {{ vm_seed_iso_tmp }}
-volid cidata -joliet -rock
{{ cloud_init_dir.path }}/user-data
{{ cloud_init_dir.path }}/meta-data
changed_when: true
- name: Create seed ISO volume in pool
ansible.builtin.shell:
cmd: >
virsh -c {{ vm_libvirt_uri }} vol-create-as default
'{{ vm_seed_iso_name }}' 4M --format raw
register: seed_vol_create
failed_when:
- seed_vol_create.rc != 0
- "'exists already' not in seed_vol_create.stderr"
changed_when: seed_vol_create.rc == 0
- name: Upload seed ISO to pool
ansible.builtin.shell:
cmd: >
virsh -c {{ vm_libvirt_uri }} vol-upload
--pool default '{{ vm_seed_iso_name }}' '{{ vm_seed_iso_tmp }}'
changed_when: true
- name: Clean up cloud-init temp files
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- "{{ cloud_init_dir.path }}"
- "{{ vm_seed_iso_tmp }}"
# ── VM definition ──────────────────────────────────────────────────────────
- name: Check if VM domain already exists
community.libvirt.virt:
command: list_vms
uri: "{{ vm_libvirt_uri }}"
register: existing_vms
- name: Define VM from XML template
community.libvirt.virt:
command: define
xml: "{{ lookup('template', 'domain.xml.j2') }}"
uri: "{{ vm_libvirt_uri }}"
when: vm_domain not in existing_vms.list_vms
# ── Network ────────────────────────────────────────────────────────────────
- name: Add static DHCP reservation
ansible.builtin.command:
cmd: >
virsh -c {{ vm_libvirt_uri }} net-update {{ vm_network }}
add ip-dhcp-host
'<host mac="{{ vm_mac }}" name="{{ vm_hostname }}" ip="{{ vm_ip }}"/>'
--live --config
register: dhcp_result
failed_when:
- dhcp_result.rc != 0
- "'existing dhcp host entry' not in dhcp_result.stderr"
- "'already exists' not in dhcp_result.stderr"
changed_when: dhcp_result.rc == 0
# ── Autostart & boot ───────────────────────────────────────────────────────
- name: Enable autostart
community.libvirt.virt:
name: "{{ vm_domain }}"
autostart: true
uri: "{{ vm_libvirt_uri }}"
- name: Start VM
community.libvirt.virt:
name: "{{ vm_domain }}"
state: running
uri: "{{ vm_libvirt_uri }}"
- name: Wait for SSH to become available
ansible.builtin.wait_for:
host: "{{ vm_ip }}"
port: 22
delay: 10
timeout: 300
state: started
delegate_to: localhost
- name: VM is ready
ansible.builtin.debug:
msg: "VM '{{ vm_domain }}' is up at {{ vm_ip }}. Run install.yml + customize.yml to provision the guest."