90 lines
3.6 KiB
YAML
90 lines
3.6 KiB
YAML
---
|
|
# Create VMs that do not exist yet. Idempotent by existence check only — this
|
|
# role does NOT reconcile the config of a live VM (see defaults for why).
|
|
|
|
- name: Check which VMs already exist
|
|
ansible.builtin.command:
|
|
cmd: "qm status {{ item.vmid }}"
|
|
loop: "{{ pve_vms }}"
|
|
loop_control:
|
|
label: "{{ item.vmid }} {{ item.name }}"
|
|
register: _vm_exists
|
|
changed_when: false
|
|
failed_when: false
|
|
check_mode: false
|
|
run_once: true
|
|
delegate_to: "{{ pve_vm_node }}"
|
|
|
|
- name: Render cloud-init user-data for VMs that use it
|
|
# Written to the NFS `snippets` dir so every node can read it. Rendered even
|
|
# for existing VMs so the file stays in sync with the definition — but see the
|
|
# template header: cloud-init only APPLIES it on first boot.
|
|
ansible.builtin.template:
|
|
src: cloudinit-user.yml.j2
|
|
dest: "{{ pve_vm_snippet_path }}/{{ item.name }}-user.yml"
|
|
mode: "0644"
|
|
loop: "{{ pve_vms | selectattr('cloudinit', 'defined') | selectattr('cloudinit') | list }}"
|
|
loop_control:
|
|
label: "{{ item.name }}"
|
|
vars:
|
|
vm: "{{ item }}"
|
|
run_once: true
|
|
delegate_to: "{{ pve_vm_node }}"
|
|
|
|
|
|
- name: Create missing VMs
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
qm create {{ item.item.vmid }}
|
|
--name {{ item.item.name }}
|
|
--description '{{ item.item.description }}'
|
|
--machine {{ item.item.machine | default('q35') }}
|
|
--bios {{ item.item.bios | default('ovmf') }}
|
|
--memory {{ item.item.memory }} --cores {{ item.item.cores }} --cpu {{ item.item.cpu | default('x86-64-v2-AES') }}
|
|
--scsihw virtio-scsi-single
|
|
--scsi0 {{ item.item.disk }},discard=on,ssd=1
|
|
--efidisk0 {{ item.item.disk.split(':')[0] }}:1,efitype=4m,pre-enrolled-keys=0
|
|
--vga {{ item.item.vga | default('virtio') }}
|
|
{% if item.item.serial | default(false) %}--serial0 socket{% endif %}
|
|
{% for n in item.item.nets %}--net{{ n.id }} virtio{% if n.mac is defined %}={{ n.mac }}{% endif %},bridge={{ n.bridge }} {% endfor %}
|
|
{% if item.item.iso is defined %}--ide2 {{ item.item.iso }},media=cdrom --boot order=ide2;scsi0{% else %}--boot order=scsi0{% endif %}
|
|
{% if item.item.cloudinit | default(false) %}--ide2 {{ item.item.disk.split(':')[0] }}:cloudinit --ipconfig0 ip=dhcp --ciuser {{ item.item.ciuser }}{% endif %}
|
|
--agent enabled=1 --onboot {{ item.item.onboot | default(0) }} --ostype l26
|
|
loop: "{{ _vm_exists.results }}"
|
|
loop_control:
|
|
label: "{{ item.item.vmid }} {{ item.item.name }}"
|
|
when: item.rc != 0
|
|
run_once: true
|
|
delegate_to: "{{ pve_vm_node }}"
|
|
|
|
- name: Point those VMs at their user-data
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
qm set {{ item.vmid }} --cicustom user={{ pve_vm_snippet_storage }}:snippets/{{ item.name }}-user.yml
|
|
loop: "{{ pve_vms | selectattr('cloudinit', 'defined') | selectattr('cloudinit') | list }}"
|
|
loop_control:
|
|
label: "{{ item.name }}"
|
|
register: _cicustom
|
|
changed_when: "'update VM' in (_cicustom.stdout | default(''))"
|
|
run_once: true
|
|
delegate_to: "{{ pve_vm_node }}"
|
|
|
|
- name: Write base images onto newly created disks
|
|
ansible.builtin.include_tasks: image.yml
|
|
# ALL image-backed VMs, not just newly created ones: image.yml decides by
|
|
# checking for an MBR on the disk. Gating on "was just created" meant a failed
|
|
# image write could never be repaired by re-running.
|
|
loop: "{{ pve_vms | selectattr('image', 'defined') | list }}"
|
|
loop_control:
|
|
label: "{{ item.name }}"
|
|
run_once: true
|
|
|
|
- name: Report
|
|
ansible.builtin.debug:
|
|
msg: >-
|
|
{{ _vm_exists.results
|
|
| map(attribute='item')
|
|
| zip(_vm_exists.results | map(attribute='rc'))
|
|
| map('join', ' rc=') | list }}
|
|
run_once: true
|