--- # ============================================================================== # PLAY 1: Download RHCOS PXE Boot Artifacts # ============================================================================== - name: Phase 1 - Download RHCOS PXE Boot Artifacts hosts: localhost connection: local gather_facts: false vars: boot_artifacts_dir: "{{ playbook_dir }}/boot-artifacts" ocp_stable_release_url: "https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/stable/release.txt" rhcos_base_mirror: "https://mirror.openshift.com/pub/openshift-v4/x86_64/dependencies/rhcos" tasks: - name: Fetch latest stable release metadata ansible.builtin.uri: url: "{{ ocp_stable_release_url }}" return_content: true register: stable_release_info - name: Extract major.minor version ansible.builtin.set_fact: ocp_major_minor: "{{ stable_release_info.content | regex_search('Name:\\s+([0-9]+\\.[0-9]+)', '\\1') | first }}" - name: Set dynamic mirror URL ansible.builtin.set_fact: rhcos_base_url: "{{ rhcos_base_mirror }}/{{ ocp_major_minor }}/latest" - name: Fetch sha256sums metadata into memory ansible.builtin.uri: url: "{{ rhcos_base_url }}/sha256sum.txt" return_content: true register: sha256_info - name: Parse artifact filenames from in-memory checksums ansible.builtin.set_fact: rhcos_kernel_file: "{{ sha256_info.content | regex_search('rhcos-[^\\s]+live-kernel[^\\s]*') }}" rhcos_initramfs_file: "{{ sha256_info.content | regex_search('rhcos-[^\\s]+live-initramfs[^\\s]*') }}" rhcos_rootfs_file: "{{ sha256_info.content | regex_search('rhcos-[^\\s]+live-rootfs[^\\s]*') }}" - name: Ensure target boot-artifacts directory exists ansible.builtin.file: path: "{{ boot_artifacts_dir }}" state: directory mode: '0755' - name: Download only the 3 RHCOS PXE boot artifacts ansible.builtin.get_url: url: "{{ rhcos_base_url }}/{{ item }}" dest: "{{ boot_artifacts_dir }}/{{ item }}" mode: '0644' loop: - "{{ rhcos_kernel_file }}" - "{{ rhcos_initramfs_file }}" - "{{ rhcos_rootfs_file }}" # ============================================================================== # PLAY 2: Generate OpenShift Ignitions and MAC-Specific GRUB PXE Configs # ============================================================================== - name: Phase 2 - Generate Ignition Configs and GRUB PXE Files hosts: localhost connection: local gather_facts: false vars: # Cluster Parameters cluster_name: "ocp4-baremetal" base_domain: "example.com" # Directory Paths work_dir: "{{ playbook_dir }}/ocp-install" templates_dir: "{{ playbook_dir }}/templates" vault_dir: "{{ playbook_dir }}/vault_vars" boot_artifacts_dir: "{{ playbook_dir }}/boot-artifacts" # PXE HTTP Endpoints pxe_base_url: "https://opnsense.lab.cudanet.org/pxeboot/lab" pxe_url_path: "images/lab" # OpenShift Installer URL ocp_installer_url: "https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/stable/openshift-install-linux.tar.gz" ocp_stable_release_url: "https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/stable/release.txt" rhcos_base_mirror: "https://mirror.openshift.com/pub/openshift-v4/x86_64/dependencies/rhcos" vars_files: - "{{ vault_dir }}/pull-secret.yml" - "{{ vault_dir }}/ssh-key.yml" tasks: - name: Ensure working directory exists ansible.builtin.file: path: "{{ work_dir }}" state: directory mode: '0755' - name: Download and unarchive openshift-install binary directly ansible.builtin.unarchive: src: "{{ ocp_installer_url }}" dest: "{{ work_dir }}" remote_src: true creates: "{{ work_dir }}/openshift-install" - name: Generate install-config.yaml from Jinja2 template ansible.builtin.template: src: "{{ templates_dir }}/install-config.yaml.j2" dest: "{{ work_dir }}/install-config.yaml" mode: '0600' - name: Backup install-config.yaml before consumption ansible.builtin.copy: src: "{{ work_dir }}/install-config.yaml" dest: "{{ work_dir }}/install-config.yaml.bak" mode: '0600' force: true - name: Generate OpenShift ignition configs ansible.builtin.command: cmd: "./openshift-install create ignition-configs --dir=." chdir: "{{ work_dir }}" creates: "{{ work_dir }}/bootstrap.ign" - name: Find generated ignition files ansible.builtin.find: paths: "{{ work_dir }}" patterns: "*.ign" register: found_ignition_files - name: Move ignition configs to ./boot-artifacts directory ansible.builtin.copy: src: "{{ item.path }}" dest: "{{ boot_artifacts_dir }}/{{ item.path | basename }}" remote_src: true mode: '0644' loop: "{{ found_ignition_files.files }}" - name: Clean up original ignition files from work directory ansible.builtin.file: path: "{{ item.path }}" state: absent loop: "{{ found_ignition_files.files }}" - name: Fetch latest release metadata for GRUB variable parsing ansible.builtin.uri: url: "{{ ocp_stable_release_url }}" return_content: true register: stable_release_info - name: Extract OpenShift version facts ansible.builtin.set_fact: ocp_latest_version: "{{ stable_release_info.content | regex_search('Name:\\s+([0-9]+\\.[0-9]+\\.[0-9]+)', '\\1') | first }}" ocp_major_minor: "{{ stable_release_info.content | regex_search('Name:\\s+([0-9]+\\.[0-9]+)', '\\1') | first }}" - name: Fetch sha256sums metadata into memory ansible.builtin.uri: url: "{{ rhcos_base_mirror }}/{{ ocp_major_minor }}/latest/sha256sum.txt" return_content: true register: sha256_info - name: Parse artifact filenames for GRUB rendering ansible.builtin.set_fact: rhcos_kernel_file: "{{ sha256_info.content | regex_search('rhcos-[^\\s]+live-kernel[^\\s]*') }}" rhcos_initramfs_file: "{{ sha256_info.content | regex_search('rhcos-[^\\s]+live-initramfs[^\\s]*') }}" rhcos_rootfs_file: "{{ sha256_info.content | regex_search('rhcos-[^\\s]+live-rootfs[^\\s]*') }}" - name: Generate GRUB PXE configs for each MAC address ansible.builtin.template: src: "{{ templates_dir }}/grub.cfg.j2" dest: "{{ boot_artifacts_dir }}/grub.cfg-{{ node.mac }}" mode: '0644' loop: - { mac: "01-52-54-00-bc-74-35", role: "bootstrap" } - { mac: "01-98-b7-85-25-30-f5", role: "control-plane", disk_path: "pci-0000:02:00.0-nvme-1" } - { mac: "01-98-b7-85-25-2c-a7", role: "control-plane", disk_path: "pci-0000:02:00.0-nvme-1" } - { mac: "01-98-b7-85-25-2c-25", role: "control-plane", disk_path: "pci-0000:02:00.0-nvme-1" } - { mac: "01-98-b7-85-25-2c-ab", role: "compute-node", disk_path: "pci-0000:02:00.0-nvme-1" } - { mac: "01-98-b7-85-25-34-2b", role: "compute-node", disk_path: "pci-0000:03:00.0-nvme-1" } - { mac: "01-98-b7-85-25-30-b7", role: "compute-node", disk_path: "pci-0000:02:00.0-nvme-1" } loop_control: loop_var: node # ============================================================================== # PLAY 3: Setup Local Temporary Private Key # ============================================================================== - name: Phase 3a - Write Private SSH Key for OPNSense Access hosts: localhost connection: local gather_facts: false vars: vault_dir: "{{ playbook_dir }}/vault_vars" vars_files: - "{{ vault_dir }}/ssh-key.yml" tasks: - name: Write vaulted private key to temporary file ansible.builtin.copy: content: "{{ vault_ssh_private_key }}" dest: "/tmp/opnsense_ssh_key" mode: '0600' # ============================================================================== # PLAY 4: Deploy Boot Artifacts to OPNSense PXE Server # ============================================================================== - name: Phase 3b - Deploy PXE Boot Artifacts to OPNSense hosts: opnsense.lab.cudanet.org remote_user: root gather_facts: false vars: local_artifacts_dir: "{{ playbook_dir }}/boot-artifacts" tftp_root_dir: "/usr/local/tftp" tftp_images_dir: "/usr/local/tftp/images/lab" www_lab_dir: "/usr/local/www/pxeboot/lab" ansible_python_interpreter: "/usr/local/bin/python3" ansible_ssh_private_key_file: "/tmp/opnsense_ssh_key" tasks: - name: Ensure target directories exist on OPNSense ansible.builtin.file: path: "{{ item }}" state: directory mode: '0755' loop: - "{{ tftp_root_dir }}" - "{{ tftp_images_dir }}" - "{{ www_lab_dir }}" - name: Deploy GRUB configs to TFTP root ansible.builtin.copy: src: "{{ item }}" dest: "{{ tftp_root_dir }}/{{ item | basename }}" mode: '0644' with_fileglob: - "{{ local_artifacts_dir }}/grub.cfg-*" - name: Deploy Ignition files to WWW directory ansible.builtin.copy: src: "{{ item }}" dest: "{{ www_lab_dir }}/{{ item | basename }}" mode: '0644' with_fileglob: - "{{ local_artifacts_dir }}/*.ign" - name: Deploy RHCOS Live RootFS to WWW directory ansible.builtin.copy: src: "{{ item }}" dest: "{{ www_lab_dir }}/{{ item | basename }}" mode: '0644' with_fileglob: - "{{ local_artifacts_dir }}/*live-rootfs*" - name: Deploy RHCOS Live Kernel to TFTP images directory ansible.builtin.copy: src: "{{ item }}" dest: "{{ tftp_images_dir }}/{{ item | basename }}" mode: '0644' with_fileglob: - "{{ local_artifacts_dir }}/*live-kernel*" - name: Deploy RHCOS Live Initramfs to TFTP images directory ansible.builtin.copy: src: "{{ item }}" dest: "{{ tftp_images_dir }}/{{ item | basename }}" mode: '0644' with_fileglob: - "{{ local_artifacts_dir }}/*live-initramfs*" # ============================================================================== # PLAY 5: Cleanup Local Temporary Private Key # ============================================================================== - name: Phase 3c - Cleanup Private SSH Key File hosts: localhost connection: local gather_facts: false tasks: - name: Remove temporary private SSH key file ansible.builtin.file: path: "/tmp/opnsense_ssh_key" state: absent