From 147430b36ef694c43cef35ea145b135c31724133 Mon Sep 17 00:00:00 2001 From: Sandwich Date: Wed, 30 Oct 2024 00:29:46 +0100 Subject: [PATCH] Add RHEL8 and RHEL9 support --- README.md | 9 +++-- inventory_example.yml | 7 ++-- main.yml | 3 +- roles/bootstrap/tasks/main.yml | 43 ++++++++++++++++++---- roles/bootstrap/vars/packages.yml | 2 +- roles/cis/tasks/main.yml | 31 +++++++++------- roles/cleanup/tasks/main.yml | 6 +-- roles/configuration/tasks/main.yml | 47 ++++++++++++++++++------ roles/environment/tasks/main.yml | 19 +++++++++- roles/partitioning/tasks/main.yml | 4 +- roles/virtualization/tasks/proxmox.yml | 23 ++++++------ roles/virtualization/tasks/vmware.yml | 6 +++ roles/virtualization/templates/vm.xml.j2 | 7 ++++ templates/rhel8.repo.j2 | 13 +++++++ templates/rhel9.repo.j2 | 13 +++++++ vars_example.yml | 3 +- 16 files changed, 176 insertions(+), 60 deletions(-) create mode 100644 templates/rhel8.repo.j2 create mode 100644 templates/rhel9.repo.j2 diff --git a/README.md b/README.md index 5ea5b17..3627516 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@ An Ansible playbook for automating system bootstrap processes in an Infrastructu Most of the roles are adaptable for use with systems beyond ArchLinux, requiring only that the target system can install a necessary package manager, such as `dnf` for RHEL-based systems. Additionally, a replacement for the `arch-chroot` command may be required for these systems. **NOTE**: -- RHEL Systems are not currently supported due to restricted access to their repositories. - A workaround could involve using an ISO as a local repository or setting up a proxy repository to facilitate access. +- For RHEL 8 and RHEL 9, repository access requires the `rhel_iso` variable. This variable specifies a local ISO or proxy repository. +- RHEL systems do not support `btrfs`. Use `ext4` or `xfs` as alternatives. +- For RHEL 8, `xfs` may cause installation issues; `ext4` is recommended. # Supported Distributions @@ -20,11 +21,12 @@ This playbook supports multiple Linux distributions with specific versions tailo | debian11 | Debian 11 (Bullseye) | | debian12 | Debian 12 (Bookworm) | | fedora | Fedora 41 | +| rhel8 | Red Hat Enterprise Linux 8 | +| rhel9 | Red Hat Enterprise Linux 9 | | rocky | Rocky Linux 9.x | | ubuntu | Ubuntu 24.10 (Oracular Oriole) | | ubuntu-lts | Ubuntu 24.04 LTS (Noble Numbat) | - # Documentation ## Table of Contents @@ -48,6 +50,7 @@ Global variables apply across your Ansible project and are loaded from `vars.yml | Variable | Description | Example Value | |-----------------------|--------------------------------------------------------------------|-----------------------------------------| | `boot_iso` | Path to the boot ISO image. | `local-btrfs:iso/archlinux-x86_64.iso` | +| `rhel_iso` | Path to the RHEL ISO file, required for RHEL 8 and RHEL 9. |`local-btrfs:iso/rhel-9.4-x86_64-dvd.iso`| | `hypervisor` | Type of hypervisor. | `libvirt`, `proxmox`, `vmware`, `none` | | `hypervisor_cluster` | Name of the hypervisor cluster. | `default-cluster` | | `hypervisor_node` | Hypervisor node name. | `node01` | diff --git a/inventory_example.yml b/inventory_example.yml index e262c7e..b34f5e0 100644 --- a/inventory_example.yml +++ b/inventory_example.yml @@ -17,12 +17,13 @@ all: 192.168.122.11: hostname: database vm_id: 101 - os: archlinux - filesystem: btrfs + os: rhel9 + filesystem: xfs vm_memory: "6144" vm_ballo: "3072" vm_cpus: "4" vm_size: "40" vm_nif: vmbr1 vm_gw: 192.168.122.1 - vm_dns: 1.1.1.1 \ No newline at end of file + vm_dns: 1.1.1.1 + rhel_iso: "local-btrfs:iso/rhel-9.4-x86_64-dvd.iso" diff --git a/main.yml b/main.yml index fc33ee5..29db7d4 100644 --- a/main.yml +++ b/main.yml @@ -47,7 +47,8 @@ that: - hypervisor in ["libvirt", "proxmox", "vmware", "none"] - filesystem in ["btrfs", "ext4", "xfs"] - - os in ["archlinux", "almalinux", "debian11", "debian12", "fedora", "rocky", "ubuntu", "ubuntu-lts"] + - os in ["archlinux", "almalinux", "debian11", "debian12", "fedora", "rhel8", "rhel9", "rocky", "ubuntu", "ubuntu-lts"] + - os not in ["rhel8", "rhel9"] or rhel_iso is defined fail_msg: Invalid input specified, please try again - name: Set connection diff --git a/roles/bootstrap/tasks/main.yml b/roles/bootstrap/tasks/main.yml index ba3a8c7..4fa1dfc 100644 --- a/roles/bootstrap/tasks/main.yml +++ b/roles/bootstrap/tasks/main.yml @@ -64,16 +64,43 @@ changed_when: result.rc == 0 register: result with_items: - - dnf --releasever=9 --best --repo=rocky-baseos --installroot=/mnt --setopt=install_weak_deps=False groupinstall -y base core + - dnf --releasever=9 --best --repo=rocky-baseos --installroot=/mnt + --setopt=install_weak_deps=False --setopt=optional_metadata_types=filelists + groupinstall -y base core - ln -sf /run/systemd/resolve/resolv.conf /mnt/etc/resolv.conf - arch-chroot /mnt dnf --releasever=9 --setopt=install_weak_deps=False install -y {{ role_packages.rocky | join(' ') }} - name: Bootstrap RHEL System when: os | lower in ['rhel8', 'rhel9'] - ansible.builtin.command: "{{ item }}" - changed_when: result.rc == 0 - register: result - with_items: - - dnf --releasever={{ '8' if os == 'rhel8' else '9' }} --installroot=/mnt --setopt=install_weak_deps=False groupinstall -y base core - - ln -sf /run/systemd/resolve/resolv.conf /mnt/etc/resolv.conf - - arch-chroot /mnt dnf --releasever={{ '8' if os == 'rhel8' else '9' }} --setopt=install_weak_deps=False install -y {{ role_packages[os] | join(' ') }} + block: + - name: Install base packages in chroot environment + ansible.builtin.command: >- + dnf --releasever={{ '8' if os == 'rhel8' else '9' }} --repo={{ os | lower }}-baseos + --installroot=/mnt + --setopt=install_weak_deps=False --setopt=optional_metadata_types=filelists + groupinstall -y base core + changed_when: result.rc == 0 + register: result + + - name: Prepare chroot environment + ansible.builtin.shell: | + ln -sf /run/systemd/resolve/resolv.conf /mnt/etc/resolv.conf + mkdir -p /mnt/usr/local/install/redhat/dvd + mount --bind /usr/local/install/redhat/dvd /mnt/usr/local/install/redhat/dvd + arch-chroot /mnt rpm --rebuilddb + changed_when: result.rc == 0 + register: result + + - name: Copy RHEL repo file into chroot environment + ansible.builtin.copy: + src: /etc/yum.repos.d/{{ os | lower }}.repo + dest: /mnt/etc/yum.repos.d/{{ os | lower }}.repo + mode: '0644' + remote_src: true + + - name: Install additional packages in chroot + ansible.builtin.command: >- + arch-chroot /mnt dnf --releasever={{ '8' if os == 'rhel8' else '9' }} + --setopt=install_weak_deps=False install -y {{ role_packages[os] | join(' ') }} + changed_when: result.rc == 0 + register: result diff --git a/roles/bootstrap/vars/packages.yml b/roles/bootstrap/vars/packages.yml index 98982f2..ee22606 100644 --- a/roles/bootstrap/vars/packages.yml +++ b/roles/bootstrap/vars/packages.yml @@ -172,7 +172,7 @@ rhel8: - dhcp-client - efibootmgr - grub2 - - grub2-efi + - grub2-efi-x64 - lrzsz - lvm2 - nfs-utils diff --git a/roles/cis/tasks/main.yml b/roles/cis/tasks/main.yml index b4db3b2..51fd4c3 100644 --- a/roles/cis/tasks/main.yml +++ b/roles/cis/tasks/main.yml @@ -91,8 +91,8 @@ - { path: /mnt/etc/security/pwquality.conf, content: ucredit = -1 } - { path: /mnt/etc/security/pwquality.conf, content: ocredit = -1 } - { path: /mnt/etc/security/pwquality.conf, content: lcredit = -1 } - - { path: '/mnt/etc/{{ "bashrc" if os in ["almalinux", "fedora", "rocky"] else "bash.bashrc" }}', content: umask 077 } - - { path: '/mnt/etc/{{ "bashrc" if os in ["almalinux", "fedora", "rocky"] else "bash.bashrc" }}', content: export TMOUT=3000 } + - { path: '/mnt/etc/{{ "bashrc" if os in ["almalinux", "fedora", "rhel8", "rhel9", "rocky"] else "bash.bashrc" }}', content: umask 077 } + - { path: '/mnt/etc/{{ "bashrc" if os in ["almalinux", "fedora", "rhel8", "rhel9", "rocky"] else "bash.bashrc" }}', content: export TMOUT=3000 } - { path: '/mnt/{{ "usr/lib/systemd/journald.conf" if os == "fedora" else "etc/systemd/journald.conf" }}', content: Storage=persistent } - { path: /mnt/etc/sudoers, content: Defaults logfile="/var/log/sudo.log" } - { path: /mnt/etc/pam.d/su, content: auth required pam_wheel.so } @@ -112,18 +112,21 @@ owner: "{{ item.owner | default(omit) }}" group: "{{ item.group | default(omit) }}" mode: "{{ item.mode }}" - loop: - - { path: /mnt/etc/ssh/sshd_config, mode: "0600" } - - { path: /mnt/etc/cron.hourly, mode: "0700" } - - { path: /mnt/etc/cron.daily, mode: "0700" } - - { path: /mnt/etc/cron.weekly, mode: "0700" } - - { path: /mnt/etc/cron.monthly, mode: "0700" } - - { path: /mnt/etc/cron.d, mode: "0700" } - - { path: /mnt/etc/crontab, mode: "0600" } - - { path: /mnt/etc/logrotate.conf, mode: "0644" } - - { path: /mnt/usr/sbin/pppd, mode: "754" } - - { path: '/mnt/usr/bin/{{ "fusermount3" if os in ["archlinux", "debian12", "fedora", "rocky", "almalinux"] else "fusermount" }}', mode: "755" } - - { path: '/mnt/usr/bin/{{ "write.ul" if os == "debian11" else "write" }}', mode: "755" } + loop: > + {{ [ + { "path": "/mnt/etc/ssh/sshd_config", "mode": "0600" }, + { "path": "/mnt/etc/cron.hourly", "mode": "0700" }, + { "path": "/mnt/etc/cron.daily", "mode": "0700" }, + { "path": "/mnt/etc/cron.weekly", "mode": "0700" }, + { "path": "/mnt/etc/cron.monthly", "mode": "0700" }, + { "path": "/mnt/etc/cron.d", "mode": "0700" }, + { "path": "/mnt/etc/crontab", "mode": "0600" }, + { "path": "/mnt/etc/logrotate.conf", "mode": "0644" }, + { "path": "/mnt/usr/sbin/pppd", "mode": "0754" } if os not in ["rhel8", "rhel9"] else None, + { "path": "/mnt/usr/bin/" + ("fusermount3" if os in ["almalinux", "archlinux", "debian12", "fedora", "rhel9", "rocky"] + else "fusermount"), "mode": "755" }, + { "path": "/mnt/usr/bin/" + ("write.ul" if os == "debian11" else "write"), "mode": "755" } + ] | reject("none") }} - name: Adjust SSHD config ansible.builtin.lineinfile: diff --git a/roles/cleanup/tasks/main.yml b/roles/cleanup/tasks/main.yml index 5955a3b..f7b3de5 100644 --- a/roles/cleanup/tasks/main.yml +++ b/roles/cleanup/tasks/main.yml @@ -15,7 +15,7 @@ state: absent loop: - ide0 - - ide1 + - ide2 - name: Remove CD-ROM from VM in vCenter when: hypervisor == "vmware" @@ -30,7 +30,7 @@ name: "{{ hostname }}" cdrom: - controller_number: 0 - unit_number: 0 + unit_number: 1 controller_type: sata type: iso iso_path: "{{ boot_iso }}" @@ -63,7 +63,7 @@ - name: Remove CD-ROM devices when: cdrom_devices.stdout_lines | length > 0 ansible.builtin.command: virsh --connect qemu:///system detach-disk {{ hostname }} {{ item }} --persistent - with_items: "{{ cdrom_devices.stdout_lines }}" + with_items: "{{ cdrom_devices.stdout_lines | select('ne', 'sdc') | list }}" changed_when: result.rc == 0 register: result diff --git a/roles/configuration/tasks/main.yml b/roles/configuration/tasks/main.yml index a0d8ef6..2b5e17d 100644 --- a/roles/configuration/tasks/main.yml +++ b/roles/configuration/tasks/main.yml @@ -6,6 +6,22 @@ changed_when: result.rc == 0 register: result + - name: Remove depricated attr2 and disable large extent + when: os in ["almalinux", "rhel8", "rhel9", "rocky"] and filesystem == "xfs" + ansible.builtin.replace: + path: /mnt/etc/fstab + regexp: '(xfs.*?)(attr2)' + replace: '\1allocsize=64m' + + - name: Replace ISO UUID entry with /dev/sr0 in fstab + when: os in ["rhel8", "rhel9"] + ansible.builtin.lineinfile: + path: /mnt/etc/fstab + regexp: '^.*\/dvd.*$' + line: "{{ '/dev/loop0' if hypervisor == 'vmware' else '/dev/sr0' }} /usr/local/install/redhat/dvd iso9660 ro,relatime,nojoliet,check=s,map=n 0 0" + state: present + backrefs: true + - name: Append TempFS to fstab ansible.builtin.lineinfile: path: /mnt/etc/fstab @@ -74,12 +90,18 @@ replace: PasswordAuthentication yes - name: Enable Systemd Services - block: - - name: Enable sshd - when: os | lower == "archlinux" - ansible.builtin.command: arch-chroot /mnt systemctl enable sshd logrotate systemd-resolved systemd-timesyncd systemd-networkd NetworkManager - changed_when: result.rc == 0 - register: result + ansible.builtin.command: > + arch-chroot /mnt systemctl enable NetworkManager + {{ + ' ssh' if os | lower in ['ubuntu', 'ubuntu-lts'] else + (' sshd' if os | lower not in ['debian11', 'debian12'] else '') + }} + {{ + 'logrotate systemd-resolved systemd-timesyncd systemd-networkd' + if os | lower == 'archlinux' else '' + }} + changed_when: result.rc == 0 + register: result - name: Configure grub when: os | lower not in ['almalinux', 'fedora', 'rhel8', 'rhel9', 'rocky'] @@ -101,7 +123,8 @@ - name: Install Bootloader ansible.builtin.command: arch-chroot /mnt {% if os | lower not in ["archlinux", "debian11", "debian12", "ubuntu", "ubuntu-lts"] %} /usr/sbin/efibootmgr - -c -L '{{ os }}' -d "{{ install_drive }}" -p 1 -l '\efi\EFI\{{ os }}\shimx64.efi' + -c -L '{{ os }}' -d "{{ install_drive }}" -p 1 + -l '\efi\EFI\{% if os | lower in ["rhel8", "rhel9"] %}redhat{% else %}{{ os | lower }}{% endif %}\shimx64.efi' {% else %}/usr/sbin/grub-install --target=x86_64-efi --efi-directory={{ "/boot/efi" if os | lower in ["ubuntu", "ubuntu-lts"] else "/boot" }} --bootloader-id={{ "ubuntu" if os | lower in ["ubuntu", "ubuntu-lts"] else os }} {% endif %} @@ -110,10 +133,10 @@ - name: Generate grub config ansible.builtin.command: arch-chroot /mnt - {% if os | lower not in ["archlinux", "debian11", "debian12", "ubuntu", "ubuntu-lts"] %} /usr/sbin/grub2-mkconfig - -o /boot/efi/EFI/{{ os }}/grub.cfg - {% else %}/usr/sbin/grub-mkconfig -o - {{ "/boot/efi/EFI/ubuntu/grub.cfg" if os | lower in ["ubuntu", "ubuntu-lts"] else "/boot/grub/grub.cfg" }} + {% if os | lower not in ["archlinux", "debian11", "debian12", "ubuntu", "ubuntu-lts"] %} + /usr/sbin/grub2-mkconfig -o /boot/efi/EFI/{% if os | lower in ["rhel8", "rhel9"] %}redhat{% else %}{{ os | lower }}{% endif %}/grub.cfg + {% else %} + /usr/sbin/grub-mkconfig -o {{ "/boot/efi/EFI/ubuntu/grub.cfg" if os | lower in ["ubuntu", "ubuntu-lts"] else "/boot/grub/grub.cfg" }} {% endif %} changed_when: result.rc == 0 register: result @@ -214,7 +237,7 @@ block: - name: Relabel the filesystem when: os | lower in ['almalinux', 'rhel8', 'rhel9', 'rocky'] - ansible.builtin.command: touch /mnt/.autorelabel + ansible.builtin.command: "arch-chroot /mnt /sbin/fixfiles onboot" changed_when: result.rc == 0 register: result diff --git a/roles/environment/tasks/main.yml b/roles/environment/tasks/main.yml index c09b2af..551404f 100644 --- a/roles/environment/tasks/main.yml +++ b/roles/environment/tasks/main.yml @@ -69,8 +69,25 @@ retries: 4 delay: 15 + - name: Prepare /iso mount and repository for RHEL-based systems + when: os | lower in ["rhel8", "rhel9"] + block: + - name: Create /iso directory + ansible.builtin.file: + path: /usr/local/install/redhat/dvd + state: directory + mode: '0755' + + - name: Mount RHEL ISO + ansible.posix.mount: + src: /dev/sr2 + path: /usr/local/install/redhat/dvd + fstype: iso9660 + opts: "ro,loop" + state: mounted + - name: Configure RHEL Repos for installation - when: os | lower in ["almalinux", "fedora", "rocky"] + when: os | lower in ["almalinux", "fedora", "rhel8", "rhel9", "rocky"] block: - name: Create directories for repository files and RPM GPG keys ansible.builtin.file: diff --git a/roles/partitioning/tasks/main.yml b/roles/partitioning/tasks/main.yml index d5c0de8..a1499c0 100644 --- a/roles/partitioning/tasks/main.yml +++ b/roles/partitioning/tasks/main.yml @@ -55,7 +55,7 @@ community.general.filesystem: dev: "{{ install_drive }}{{ boot_partition_suffix }}" fstype: vfat - opts: -F32 + opts: -F32 -n BOOT force: true - name: Create filesystem @@ -136,7 +136,7 @@ - name: Mount boot filesystem ansible.posix.mount: - path: "{{ '/mnt/boot/efi' if os | lower in ['ubuntu', 'ubuntu-lts'] else '/mnt/boot' }}" + path: "{{ '/mnt/boot/efi' if os | lower in ['rhel8', 'ubuntu', 'ubuntu-lts'] else '/mnt/boot' }}" src: UUID={{ boot_uuid.stdout }} fstype: vfat state: mounted diff --git a/roles/virtualization/tasks/proxmox.yml b/roles/virtualization/tasks/proxmox.yml index 62ca711..2968757 100644 --- a/roles/virtualization/tasks/proxmox.yml +++ b/roles/virtualization/tasks/proxmox.yml @@ -8,20 +8,20 @@ ciuser: "{{ user_name }}" cipassword: "{{ user_password }}" ciupgrade: false - node: "{{ hypervisor_node }}" # Proxmox node name - vmid: "{{ vm_id }}" # Unique ID for the VM - name: "{{ hostname }}" # Name of the VM + node: "{{ hypervisor_node }}" + vmid: "{{ vm_id }}" + name: "{{ hostname }}" cpu: host - cores: "{{ vm_cpus }}" # Number of CPU cores - memory: "{{ vm_memory }}" # Memory size in MB - balloon: "{{ vm_ballo | default(omit) }}" # Minimum Memory size in MB + cores: "{{ vm_cpus }}" + memory: "{{ vm_memory }}" + balloon: "{{ vm_ballo | default(omit) }}" numa_enabled: true hotplug: network,disk bios: ovmf boot: ac scsihw: virtio-scsi-single scsi: - scsi0: "{{ hypervisor_storage }}:{{ vm_size }}" # Disk configuration + scsi0: "{{ hypervisor_storage }}:{{ vm_size }}" efidisk0: efitype: 4m format: raw @@ -29,14 +29,15 @@ storage: "{{ hypervisor_storage }}" ide: ide0: "{{ boot_iso }},media=cdrom" - ide1: "{{ hypervisor_storage }}:cloudinit" + ide1: "{{ rhel_iso | default(omit) }},media=cdrom" + ide2: "{{ hypervisor_storage }}:cloudinit" net: net0: virtio,bridge={{ vm_nif }}{% if vlan_name is defined and vlan_name %},tag={{ vlan_name }}{% endif %} ipconfig: ipconfig0: ip={{ vm_ip }},gw={{ vm_gw }} nameservers: "{{ vm_dns }}" - onboot: true # Start the VM on boot - state: present # Ensure the VM is present + onboot: true + state: present - name: Start VM on Proxmox delegate_to: localhost @@ -47,4 +48,4 @@ node: "{{ hypervisor_node }}" name: "{{ hostname }}" vmid: "{{ vm_id }}" - state: started # Ensure the VM is present + state: started diff --git a/roles/virtualization/tasks/vmware.yml b/roles/virtualization/tasks/vmware.yml index 4c571b5..9988e41 100644 --- a/roles/virtualization/tasks/vmware.yml +++ b/roles/virtualization/tasks/vmware.yml @@ -26,6 +26,12 @@ controller_type: sata state: present type: iso + iso_path: "{{ rhel_iso | default(omit) }}" + - controller_number: 0 + unit_number: 1 + controller_type: sata + state: present + type: iso iso_path: "{{ boot_iso }}" networks: - vlan: "{{ vlan_name }}" diff --git a/roles/virtualization/templates/vm.xml.j2 b/roles/virtualization/templates/vm.xml.j2 index 99e8a49..87c8128 100644 --- a/roles/virtualization/templates/vm.xml.j2 +++ b/roles/virtualization/templates/vm.xml.j2 @@ -37,6 +37,13 @@ + {% if rhel_iso is defined %} + + + + + + {% endif %} diff --git a/templates/rhel8.repo.j2 b/templates/rhel8.repo.j2 new file mode 100644 index 0000000..7638279 --- /dev/null +++ b/templates/rhel8.repo.j2 @@ -0,0 +1,13 @@ +[rhel8-baseos] +name=RHEL 8 BaseOS +baseurl=file:///usr/local/install/redhat/dvd/BaseOS +enabled=1 +gpgcheck=0 +gpgkey=file:///usr/local/install/redhat/dvd/RPM-GPG-KEY-redhat-release + +[rhel8-appstream] +name=RHEL 8 AppStream +baseurl=file:///usr/local/install/redhat/dvd/AppStream +enabled=1 +gpgcheck=0 +gpgkey=file:///usr/local/install/redhat/dvd/RPM-GPG-KEY-redhat-release diff --git a/templates/rhel9.repo.j2 b/templates/rhel9.repo.j2 new file mode 100644 index 0000000..4eeb34a --- /dev/null +++ b/templates/rhel9.repo.j2 @@ -0,0 +1,13 @@ +[rhel9-baseos] +name=RHEL 9 BaseOS +baseurl=file:///usr/local/install/redhat/dvd/BaseOS +enabled=1 +gpgcheck=0 +gpgkey=file:///usr/local/install/redhat/dvd/RPM-GPG-KEY-redhat-release + +[rhel9-appstream] +name=RHEL 9 AppStream +baseurl=file:///usr/local/install/redhat/dvd/AppStream +enabled=1 +gpgcheck=0 +gpgkey=file:///usr/local/install/redhat/dvd/RPM-GPG-KEY-redhat-release diff --git a/vars_example.yml b/vars_example.yml index d0320bc..e2363fa 100644 --- a/vars_example.yml +++ b/vars_example.yml @@ -12,4 +12,5 @@ hypervisor_username: "root@pam" hypervisor_password: "SomePassword" hypervisor_node: "NodeName" hypervisor_storage: "local-btrfs" -boot_iso: "local-btrfs:iso/archlinux-x86_64.iso" \ No newline at end of file +boot_iso: "local-btrfs:iso/archlinux-x86_64.iso" +rhel_iso: "local-btrfs:rhel-9.4-x86_64-dvd.iso"