--- - name: Collect the boot media this run attaches ansible.builtin.set_fact: system_check_media: >- {{ [{'name': 'boot_iso', 'value': boot_iso}] + ([{'name': 'rhel_iso', 'value': rhel_iso}] if (rhel_iso | default('') | string | length > 0) else []) }} - name: Verify vCenter boot media and placement target when: hypervisor_type == "vmware" delegate_to: localhost become: false module_defaults: group/community.vmware.vmware: hostname: "{{ hypervisor_cfg.url }}" username: "{{ hypervisor_cfg.username }}" password: "{{ hypervisor_cfg.password }}" validate_certs: "{{ hypervisor_cfg.certs | bool }}" block: - name: Assert the boot media is a datastore path ansible.builtin.assert: that: - item.value is match('^\\[[^\\]]+\\]\\s*\\S') fail_msg: | {{ item.name }} is "{{ item.value }}", which is not a vSphere datastore path. Set {{ item.name }} to the form "[datastore] folder/file.iso", for example "[ssdmsa_lun1] ISO/SGBoot-2026.06.09-x86_64.iso". quiet: true loop: "{{ system_check_media }}" loop_control: label: "{{ item.name }}" - name: Resolve the datastores this run depends on ansible.builtin.set_fact: system_check_datastores_needed: >- {%- set ns = namespace(refs=[{'source': 'hypervisor.storage', 'datastore': hypervisor_cfg.storage, 'path': ''}]) -%} {%- for media in system_check_media -%} {%- set ns.refs = ns.refs + [{ 'source': media.name, 'datastore': media.value | regex_replace('^\\[([^\\]]+)\\].*$', '\\1'), 'path': media.value | regex_replace('^\\[[^\\]]+\\]\\s*', '') }] -%} {%- endfor -%} {{ ns.refs }} - name: Query the ESXi placement host when: hypervisor_cfg.node | default('') | length > 0 community.vmware.vmware_host_facts: esxi_hostname: "{{ hypervisor_cfg.node }}" register: system_check_esxi delegate_facts: true failed_when: false changed_when: false no_log: true - name: Assert the ESXi placement host is readable when: hypervisor_cfg.node | default('') | length > 0 ansible.builtin.assert: that: - system_check_esxi.ansible_facts is defined fail_msg: | ESXi host {{ hypervisor_cfg.node }} (hypervisor.node) could not be read from vCenter {{ hypervisor_cfg.url }}. {{ system_check_esxi.msg | default('Unknown error') }} Set hypervisor.node to an ESXi host in datacenter {{ hypervisor_cfg.datacenter }}, or clear hypervisor.node and set hypervisor.cluster instead. quiet: true - name: Assert the ESXi placement host can take a new VM when: hypervisor_cfg.node | default('') | length > 0 ansible.builtin.assert: that: - system_check_esxi.ansible_facts.ansible_host_connection_state == "connected" - not (system_check_esxi.ansible_facts.ansible_in_maintenance_mode | bool) fail_msg: | ESXi host {{ hypervisor_cfg.node }} (hypervisor.node) cannot take a new VM: connection state is {{ system_check_esxi.ansible_facts.ansible_host_connection_state }}, maintenance mode is {{ system_check_esxi.ansible_facts.ansible_in_maintenance_mode }}. Wait for the host to come back, or set hypervisor.node to another host. quiet: true - name: Query the datastores of the placement cluster when: hypervisor_cfg.node | default('') | length == 0 community.vmware.vmware_datastore_info: cluster: "{{ hypervisor_cfg.cluster }}" register: system_check_cluster failed_when: false changed_when: false no_log: true - name: Assert the placement cluster is readable when: hypervisor_cfg.node | default('') | length == 0 ansible.builtin.assert: that: - system_check_cluster.datastores is defined fail_msg: | Cluster {{ hypervisor_cfg.cluster }} (hypervisor.cluster) could not be read from vCenter {{ hypervisor_cfg.url }}. {{ system_check_cluster.msg | default('Unknown error') }} Set hypervisor.cluster to a cluster in datacenter {{ hypervisor_cfg.datacenter }}, or clear hypervisor.cluster and pin hypervisor.node instead. quiet: true - name: Resolve the datastores available at the placement target ansible.builtin.set_fact: system_check_target: >- {{ ('ESXi host ' ~ hypervisor_cfg.node ~ ' (hypervisor.node)') if (hypervisor_cfg.node | default('') | length > 0) else ('cluster ' ~ hypervisor_cfg.cluster ~ ' (hypervisor.cluster)') }} system_check_datastores_available: >- {{ (system_check_esxi.ansible_facts.ansible_datastore | map(attribute='name') | list) if (hypervisor_cfg.node | default('') | length > 0) else (system_check_cluster.datastores | map(attribute='name') | list) }} - name: Assert every datastore this run needs is available at the placement target ansible.builtin.assert: that: - item.datastore in system_check_datastores_available fail_msg: | Datastore {{ item.datastore }} (from {{ item.source }}) is not available on {{ system_check_target }}. Datastores available there: {{ system_check_datastores_available | join(', ') }}. Point {{ item.source }} at one of those datastores, or move placement to a host or cluster that mounts {{ item.datastore }}. quiet: true loop: "{{ system_check_datastores_needed }}" loop_control: label: "{{ item.source }}: {{ item.datastore }}" - name: Query the boot media on its datastore community.vmware.vsphere_file: datacenter: "{{ hypervisor_cfg.datacenter }}" datastore: "{{ item.datastore }}" path: "{{ item.path }}" state: file register: system_check_media_files failed_when: false changed_when: false loop: "{{ system_check_datastores_needed | rejectattr('source', 'equalto', 'hypervisor.storage') | list }}" loop_control: label: "{{ item.source }}" - name: Assert the boot media exists on its datastore ansible.builtin.assert: that: - (item.status | default(0)) != 404 fail_msg: | {{ item.item.source }} points at "[{{ item.item.datastore }}] {{ item.item.path }}", which does not exist on datastore {{ item.item.datastore }}. Set {{ item.item.source }} to a file that exists there, normally the current SGBoot ISO. quiet: true loop: "{{ system_check_media_files.results }}" loop_control: label: "{{ item.item.source }}" - name: Verify Proxmox boot media when: hypervisor_type == "proxmox" delegate_to: localhost become: false module_defaults: community.proxmox.proxmox_storage_contents_info: api_host: "{{ hypervisor_cfg.url }}" api_user: "{{ hypervisor_cfg.username }}" api_password: "{{ hypervisor_cfg.password | default(omit, true) }}" api_token_id: "{{ hypervisor_cfg.token_id | default(omit, true) }}" api_token_secret: "{{ hypervisor_cfg.token_secret | default(omit, true) }}" block: - name: Assert the boot media is a storage volume id ansible.builtin.assert: that: - item.value is match('^[^:]+:iso/\\S') fail_msg: | {{ item.name }} is "{{ item.value }}", which is not a Proxmox volume id. Set {{ item.name }} to the form ":iso/.iso", for example "local:iso/SGBoot-2026.06.09-x86_64.iso". quiet: true loop: "{{ system_check_media }}" loop_control: label: "{{ item.name }}" - name: Query the ISO volumes on the Proxmox node community.proxmox.proxmox_storage_contents_info: node: "{{ hypervisor_cfg.node }}" storage: "{{ item.value | regex_replace('^([^:]+):.*$', '\\1') }}" content: iso register: system_check_proxmox_media failed_when: false changed_when: false loop: "{{ system_check_media }}" loop_control: label: "{{ item.name }}" - name: Assert the boot media storage serves ISOs on the Proxmox node ansible.builtin.assert: that: - item.proxmox_storage_content is defined fail_msg: | Storage {{ item.item.value | regex_replace('^([^:]+):.*$', '\\1') }} (from {{ item.item.name }}) does not serve ISO content on Proxmox node {{ hypervisor_cfg.node }}. {{ item.msg | default('Unknown error') }} Point {{ item.item.name }} at a storage that is enabled for ISO images on that node, or change hypervisor.node. quiet: true loop: "{{ system_check_proxmox_media.results }}" loop_control: label: "{{ item.item.name }}" - name: Assert the boot media exists on the Proxmox node ansible.builtin.assert: that: - item.item.value in (item.proxmox_storage_content | map(attribute='volid') | list) fail_msg: | {{ item.item.name }} points at "{{ item.item.value }}", which does not exist on Proxmox node {{ hypervisor_cfg.node }}. ISO volumes on that storage: {{ item.proxmox_storage_content | map(attribute='volid') | join(', ') }}. Set {{ item.item.name }} to one of those volumes. quiet: true loop: "{{ system_check_proxmox_media.results }}" loop_control: label: "{{ item.item.name }}" - name: Verify libvirt boot media when: hypervisor_type == "libvirt" delegate_to: localhost become: false block: - name: Stat the libvirt boot media ansible.builtin.stat: path: "{{ item.value }}" register: system_check_libvirt_media loop: "{{ system_check_media }}" loop_control: label: "{{ item.name }}" - name: Assert the libvirt boot media exists ansible.builtin.assert: that: - item.stat.exists fail_msg: | {{ item.item.name }} points at "{{ item.item.value }}", which does not exist on the libvirt host. Set {{ item.item.name }} to the path of an ISO file that exists there. quiet: true loop: "{{ system_check_libvirt_media.results }}" loop_control: label: "{{ item.item.name }}"