diff --git a/.github/actions/shared-steps/action.yml b/.github/actions/shared-steps/action.yml index fe7ff586..5444cfe6 100644 --- a/.github/actions/shared-steps/action.yml +++ b/.github/actions/shared-steps/action.yml @@ -500,15 +500,15 @@ runs: esac # Image file format: raw or qcow2 - # Image file name is redefined for the Hyper-V and the QEMU-built VMware - # and Parallels aarch64 boxes, as they are built using Qemu and converted - # to .box file + # Image file name is redefined for the Hyper-V and the QEMU-built VMware, + # Parallels and VirtualBox aarch64 boxes, as they are built using Qemu + # and converted to .box file case ${{ inputs.type }} in oci|gencloud|gencloud_ext4|opennebula) format=qcow2 image_file=${{ env.IMAGE_FILE }} ;; - hyperv|vmware|parallels) + hyperv|vmware|parallels|virtualbox) format=raw image_file=$(echo ${{ env.IMAGE_FILE }} | sed 's/\.box$/.raw/') ;; diff --git a/.github/scripts/resolve-image-config.sh b/.github/scripts/resolve-image-config.sh index a65388ea..6e09e82b 100755 --- a/.github/scripts/resolve-image-config.sh +++ b/.github/scripts/resolve-image-config.sh @@ -112,6 +112,27 @@ case "${type}_${version_major}" in aws_s3_path="images/kitten/10/vagrant/${timestamp}" ;; + # VirtualBox aarch64 box built under QEMU (TCG) and packed by + # tools/raw-to-vagrant-virtualbox.sh, like the Parallels box (the x86_64 + # VirtualBox box is the vagrant_virtualbox type, built by VirtualBox) + virtualbox_9) + output_mask="AlmaLinux-${version_major}-Vagrant-virtualbox-*.${alma_arch}.box" + packer_source="qemu.${packer_source}" + aws_s3_path="images/${version_major}/${release}/vagrant/${timestamp}" + ;; + + virtualbox_10) + packer_source="qemu.almalinux_10_vagrant_virtualbox_qemu_${alma_arch}" + output_mask="AlmaLinux-${version_major}-Vagrant-virtualbox-*.${alma_arch}.box" + aws_s3_path="images/${version_major}/${release}/vagrant/${timestamp}" + ;; + + virtualbox*kitten*) + packer_source="qemu.almalinux_kitten_10_vagrant_virtualbox_qemu_${alma_arch}" + output_mask="AlmaLinux-Kitten-Vagrant-virtualbox-10-*.${alma_arch}.box" + aws_s3_path="images/kitten/10/vagrant/${timestamp}" + ;; + azure*kitten*) packer_source="almalinux_kitten_10_${type}_${alma_arch}" [[ ${variant} == *"v2"* ]] && packer_source="${packer_source}_v2" diff --git a/.github/workflows/vagrant-build-test-publish.yml b/.github/workflows/vagrant-build-test-publish.yml index a0f7ca8c..cce5990b 100644 --- a/.github/workflows/vagrant-build-test-publish.yml +++ b/.github/workflows/vagrant-build-test-publish.yml @@ -37,6 +37,15 @@ run-name: >- # test either (run_test forced off, offline validation of the raw disk only). # It is excluded for the -v2 variants, and there is no AlmaLinux 8 box. # +# The VirtualBox aarch64 box (vagrant_type ALL or vagrant_virtualbox; for +# VirtualBox 7.1+ on Apple Silicon) is built the same way, as the +# virtualbox-aarch64 leg of build-gh-hosted: installed and provisioned under +# QEMU (TCG), with the VirtualBox Guest Additions ISO downloaded first, and +# packed into a virtualbox box by tools/raw-to-vagrant-virtualbox.sh. No +# runner can run VirtualBox ARM guests, so it has no live `vagrant up` test +# either (run_test forced off, offline validation of the raw disk only). It +# is excluded for the -v2 variants, and there is no AlmaLinux 8 box. +# # Stage implementations live in composite actions: # .github/actions/shared-steps - Packer build + vagrant up smoke test # .github/actions/vagrant-publish-steps - HCP Vagrant Registry publish of the local box @@ -178,6 +187,12 @@ jobs: # Hyper-V; no live test, see the header. There is no AlmaLinux 8 box. VARIANTS_GH+=("parallels-aarch64") fi + if [ "${{ inputs.vagrant_type }}" = "vagrant_virtualbox" -o "${{ inputs.vagrant_type }}" = "ALL" ] \ + && [ "${{ inputs.version_major }}" != "8" ]; then + # VirtualBox aarch64: built under QEMU (TCG) and converted, like + # Parallels; no live test, see the header. There is no AlmaLinux 8 box. + VARIANTS_GH+=("virtualbox-aarch64") + fi [ ${#VARIANTS_GH[@]} -ne 0 ] && matrix_gh=$(printf '"%s",' "${VARIANTS_GH[@]}") matrix_gh=${matrix_gh%,} # Remove the trailing comma @@ -218,7 +233,8 @@ jobs: needs: [init-data] if: ${{ needs.init-data.outputs.matrix_gh != '[]' }} # use runs-on runners if within the almalinux org, otherwise GH runners. - # The QEMU-built aarch64 legs (vmware-aarch64, parallels-aarch64): + # The QEMU-built aarch64 legs (vmware-aarch64, parallels-aarch64, + # virtualbox-aarch64): # - in the AlmaLinux org: RunsOn arm64 bare metal (a1.metal or a newer # Graviton .metal, whichever is free), where shared-steps # builds natively under KVM (-cpu host). No Vagrant exists for arm64 @@ -255,6 +271,10 @@ jobs: matrix_gh: parallels-aarch64 - variant: 10-kitten-v2 matrix_gh: parallels-aarch64 + - variant: 10-v2 + matrix_gh: virtualbox-aarch64 + - variant: 10-kitten-v2 + matrix_gh: virtualbox-aarch64 env: TIME_STAMP: ${{ needs.init-data.outputs.time_stamp }} @@ -267,11 +287,11 @@ jobs: IFS=- read -r type arch <<< "${{ matrix.matrix_gh }}" echo "type=$type" >> $GITHUB_ENV echo "ARCH=$arch" >> $GITHUB_ENV - # Hyper-V and the QEMU-built VMware / Parallels aarch64 boxes have no - # live `vagrant up` test (their guests can't boot on the Linux runner), - # so force run_test off for them; honor the input for the libvirt / - # virtualbox legs. - if [ "$type" = "hyperv" ] || [ "$type" = "vmware" ] || [ "$type" = "parallels" ]; then + # Hyper-V and the QEMU-built VMware / Parallels / VirtualBox aarch64 + # boxes have no live `vagrant up` test (their guests can't boot on the + # Linux runner), so force run_test off for them; honor the input for + # the libvirt / x86_64 virtualbox legs. + if [ "$type" = "hyperv" ] || [ "$type" = "vmware" ] || [ "$type" = "parallels" ] || [ "$type" = "virtualbox" ]; then echo "RUN_TEST=false" >> $GITHUB_ENV else echo "RUN_TEST=${{ inputs.run_test }}" >> $GITHUB_ENV @@ -295,6 +315,27 @@ jobs: curl -fsSL --retry 3 -o "${PRL_TOOLS_ISO}" "${PRL_TOOLS_ISO_URL}" ls -la "${PRL_TOOLS_ISO}" + - name: Download the VirtualBox Guest Additions ISO + if: env.type == 'virtualbox' + env: + # 7.2.20+: the first Guest Additions whose vboxsf builds against + # the EL10 / Kitten 6.12 kernels; its ISO carries the arm64 Linux + # installer (VBoxLinuxAdditions-arm64.run) + VBOX_VERSION: 7.2.20 + VBOX_GA_ISO: VBoxGuestAdditions.iso + run: | + # The virtualbox-iso builder takes the Guest Additions ISO from the + # installed VirtualBox; the QEMU-built box installs it from this + # copy, which the packer template uploads into the guest + # (virtualbox_guest_additions_iso_aarch64). Verified against + # Oracle's published checksums. + base_url="https://download.virtualbox.org/virtualbox/${VBOX_VERSION}" + curl -fsSL --retry 3 -o "${VBOX_GA_ISO}" "${base_url}/VBoxGuestAdditions_${VBOX_VERSION}.iso" + expected=$(curl -fsSL --retry 3 "${base_url}/SHA256SUMS" | awk -v f="*VBoxGuestAdditions_${VBOX_VERSION}.iso" '$2 == f { print $1 }') + [ -n "${expected}" ] || { echo "[Error] No checksum for VBoxGuestAdditions_${VBOX_VERSION}.iso"; exit 1; } + echo "${expected} ${VBOX_GA_ISO}" | sha256sum -c - + ls -la "${VBOX_GA_ISO}" + - uses: ./.github/actions/shared-steps name: ${{ matrix.variant }} ${{ matrix.matrix_gh }} image with: diff --git a/VAGRANT_BUILD_TEST_PUBLISH.md b/VAGRANT_BUILD_TEST_PUBLISH.md index 1aa33345..0371f0af 100644 --- a/VAGRANT_BUILD_TEST_PUBLISH.md +++ b/VAGRANT_BUILD_TEST_PUBLISH.md @@ -7,8 +7,8 @@ box lifecycle in a single `workflow_dispatch`: 1. **Build** the boxes with Packer (one Packer builder per provider: `libvirt`, `virtualbox`, `vmware`, `hyperv`; x86_64, plus the - `x86_64_v2` microarch for AL10 / Kitten, and the VMware and Parallels - aarch64 boxes for 9, 10 and Kitten). + `x86_64_v2` microarch for AL10 / Kitten, and the VMware, Parallels and + VirtualBox aarch64 boxes for 9, 10 and Kitten). 2. **Test** each box with a live `vagrant up` smoke test on the build runner (via `shared-steps`, gated by `run_test`). Hyper-V and the QEMU-built aarch64 boxes are the exceptions - see below. @@ -96,6 +96,47 @@ so the Parallels aarch64 box is built the Hyper-V way, as a is published like the others. It is excluded for the `-v2` variants and for AlmaLinux 8 (no Parallels aarch64 box for 8). +### VirtualBox aarch64 is built under QEMU (no live test) + +VirtualBox 7.1+ runs arm64 guests only on Apple silicon Macs, so no runner +can run them either, and the VirtualBox aarch64 box is built the Parallels +way, as a `virtualbox-aarch64` leg of `build-gh-hosted`, for `vagrant_type` +`ALL` or `vagrant_virtualbox` (the x86_64 VirtualBox box is still built by +VirtualBox itself, as the `vagrant_virtualbox` leg): + +- the leg first downloads the VirtualBox Guest Additions ISO + (`https://download.virtualbox.org/virtualbox/7.2.20/VBoxGuestAdditions_7.2.20.iso`, + checked against Oracle's `SHA256SUMS`), which the `virtualbox-iso` builder + would take from the installed VirtualBox. 7.2.20 is the first release whose + `vboxsf` builds against the EL10 / Kitten 6.12 kernels; its ISO carries the + arm64 Linux installer (`VBoxLinuxAdditions-arm64.run`); +- the `*virtualbox*aarch64` qemu sources (`almalinux-9-virtualbox-aarch64`, + `almalinux_10_vagrant_virtualbox_qemu_aarch64`, + `almalinux_kitten_10_vagrant_virtualbox_qemu_aarch64`) install from the + boot ISO under QEMU the same way as the Parallels aarch64 box (natively on + the org's arm64 runner, under TCG elsewhere). Packer uploads the Guest + Additions ISO (`virtualbox_guest_additions_iso_aarch64`) and + `ansible/vagrant.yml` provisions the guest as a VirtualBox box + (`packer_provider=virtualbox-iso`): on aarch64 the `virtualbox_guest_arm64` + role installs the Guest Additions with the arm64 installer (the + `ezamriy.vbox_guest` role runs only the x86 one). Under QEMU the installer + builds the `vboxguest` / `vboxsf` modules but cannot start the services, so + the role checks the built modules instead of the installer's exit code; +- `tools/raw-to-vagrant-virtualbox.sh` packs the raw disk into a + `virtualbox` box: `box.ovf` from `tpl/vagrant/virtualbox/box-aarch64.ovf` + (exported by VirtualBox 7.2 from an ARM VM of its defaults for Red Hat + 9.x / 10.x ARM 64-bit: `ARMv8Virtual` chipset, EFI, one VirtioSCSI disk, a + virtio NIC on NAT, 2 vCPUs / 2048 MB; fresh VM/disk UUIDs and MAC + address), the disk as a streamOptimized VMDK (`box-disk001.vmdk`, converted + by `qemu-img`), a `Vagrantfile` with the base MAC, and `metadata.json` + with `arm64`. The kickstart's generic initramfs boots on the VirtioSCSI + disk and virtio NIC, and shim's fallback boot loader + (`EFI/BOOT/BOOTAA64.EFI`) boots it without saved EFI variables; +- the leg forces `run_test=false`: only `shared-steps`' offline validation + of the raw disk (release, architecture, package list) runs, then the box + is published like the others. It is excluded for the `-v2` variants and + for AlmaLinux 8 (no VirtualBox aarch64 box for 8). + ### When to use which | Use | Workflow | @@ -132,7 +173,7 @@ The input set is [`vagrant-build.yml`](BUILD_VAGRANT.md)'s plus | `vagrant_type` | `ALL` | `ALL`, `vagrant_libvirt`, `vagrant_virtualbox`, `vagrant_vmware`, `vagrant_hyperv`, `vagrant_parallels`. | | `self-hosted` | `true` | Build the self-hosted providers on a self-hosted runner. | | `self_hosted_runner` | `aws-ec2` | `self-hosted` (manual) or `aws-ec2`. Routes libvirt/virtualbox to the self-hosted leg when set to `self-hosted`. | -| `run_test` | `true` | Live `vagrant up` test (ignored for the hyperv, vmware-aarch64 and parallels-aarch64 legs, which are always build-only). | +| `run_test` | `true` | Live `vagrant up` test (ignored for the hyperv, vmware-aarch64, parallels-aarch64 and virtualbox-aarch64 legs, which are always build-only). | | `store_as_artifact` | `false` | Upload boxes as workflow artifacts. | | `upload_to_s3` | `true` | Upload to S3 in parallel; also used for the publish summary/notification link. | | `release_to_hcp` | `false` | Publish boxes to the HCP Vagrant Registry. `false` = build + test + S3 upload only. Off by default: see [HCP Vagrant is shutting down](#hcp-vagrant-is-shutting-down). | @@ -143,13 +184,15 @@ The input set is [`vagrant-build.yml`](BUILD_VAGRANT.md)'s plus ``` init-data (computes matrix_gh / matrix_sh from vagrant_type; adds | hyperv-x86_64 to matrix_gh for ALL / vagrant_hyperv, - | vmware-aarch64 for ALL / vagrant_vmware and parallels-aarch64 - | for ALL / vagrant_parallels, except AlmaLinux 8) + | vmware-aarch64 for ALL / vagrant_vmware, parallels-aarch64 + | for ALL / vagrant_parallels and virtualbox-aarch64 for ALL / + | vagrant_virtualbox, except AlmaLinux 8) | |- build-gh-hosted (matrix_gh x variant) -. shared-steps build (+ vagrant up | libvirt / virtualbox / hyperv / | test, except hyperv and - | vmware-aarch64 / parallels-aarch64 | the aarch64 legs), then - | | | vagrant-publish-steps in-job + | vmware- / parallels- / virtualbox- | the aarch64 legs), then + | aarch64 | vagrant-publish-steps in-job + | | | | '- publish-aarch64 (org only: the | | aarch64 boxes built on arm64) | |- start-self-hosted-runner (fork EC2) | @@ -172,6 +215,7 @@ an x86_64 runner. Provider routing (matching `vagrant-build.yml`): | hyperv | `build-gh-hosted` leg | no (build-only) | | vmware aarch64 | `build-gh-hosted` leg on arm64 under KVM (forks: under QEMU TCG); published by `publish-aarch64` in the org | no (build-only) | | parallels aarch64 | `build-gh-hosted` leg on arm64 under KVM (forks: under QEMU TCG); published by `publish-aarch64` in the org | no (build-only) | +| virtualbox aarch64 | `build-gh-hosted` leg on arm64 under KVM (forks: under QEMU TCG); published by `publish-aarch64` in the org | no (build-only) | ### Publishing: parallel, with retry-on-collision @@ -210,7 +254,7 @@ falls back to an interactive browser login that hangs in CI). | Job | Runner (AlmaLinux org) | Runner (forks) | | :--- | :--- | :--- | -| `build-gh-hosted` | `r8i.2xlarge`, `image=ubuntu24-full-x64`, `volume=60g`, `nested-virt`, `spot=false`; the `vmware-aarch64` / `parallels-aarch64` legs: arm64 `a1.metal` or a newer Graviton bare metal (`c6g`/`m6g`/`c7g`/`m7g` `.metal`, `*8gd.metal*`), `image=ubuntu24-full-arm64`, `volume=60g`, `spot=false` | `ubuntu-24.04` | +| `build-gh-hosted` | `r8i.2xlarge`, `image=ubuntu24-full-x64`, `volume=60g`, `nested-virt`, `spot=false`; the `vmware-aarch64` / `parallels-aarch64` / `virtualbox-aarch64` legs: arm64 `a1.metal` or a newer Graviton bare metal (`c6g`/`m6g`/`c7g`/`m7g` `.metal`, `*8gd.metal*`), `image=ubuntu24-full-arm64`, `volume=60g`, `spot=false` | `ubuntu-24.04` | | `publish-aarch64` | `ubuntu-24.04` | - (not run) | | `build-self-hosted` | `r8i.2xlarge`, `ami=`, `volume=60g`, `nested-virt`, `spot=false` | EC2 `c5n.metal` (`EC2_AMI_ID_AL9_X86_64`) or a manual self-hosted runner | @@ -221,9 +265,9 @@ lives on the self-hosted leg. **Hyper-V now builds on `build-gh-hosted`'s `hyperv-build.yml` used - this matches the metal-to-`r8i.2xlarge` migration the vagrant build already made; KVM is available either way. -The `vmware-aarch64` and `parallels-aarch64` legs build on arm64 bare -metal (`a1.metal` or a newer Graviton `.metal`, whichever is free) in the -AlmaLinux org, where shared-steps runs QEMU natively under KVM (`-cpu +The `vmware-aarch64`, `parallels-aarch64` and `virtualbox-aarch64` legs +build on arm64 bare metal (`a1.metal` or a newer Graviton `.metal`, +whichever is free) in the AlmaLinux org, where shared-steps runs QEMU natively under KVM (`-cpu host`, the runner's own firmware), like the GenericCloud aarch64 build. Forks have no arm64 runner, so the legs run on GitHub's x86_64 Ubuntu 24.04 like the others, and shared-steps emulates their install under QEMU diff --git a/almalinux-9-vagrant.pkr.hcl b/almalinux-9-vagrant.pkr.hcl index def924e8..e3791258 100644 --- a/almalinux-9-vagrant.pkr.hcl +++ b/almalinux-9-vagrant.pkr.hcl @@ -299,6 +299,47 @@ source "qemu" "almalinux-9-parallels-aarch64" { ) } +# The VirtualBox aarch64 box (VirtualBox 7.1+ on Apple Silicon) built under +# QEMU, like the Parallels one: there is no runner that can run VirtualBox ARM +# guests, so the install and provisioning run in QEMU (TCG on an x86_64 +# runner, see the aarch64_* variables) and tools/raw-to-vagrant-virtualbox.sh +# packs the raw disk into a virtualbox box (OVF + streamOptimized VMDK). The +# kickstart installs a generic initramfs (dracut-config-generic), so the +# VirtioSCSI disk and virtio NIC VirtualBox presents are handled on the first +# boot there. +source "qemu" "almalinux-9-virtualbox-aarch64" { + iso_url = local.iso_url_9_aarch64 + iso_checksum = local.iso_checksum_9_aarch64 + http_directory = var.http_directory + shutdown_command = var.vagrant_shutdown_command + ssh_username = var.vagrant_ssh_username + ssh_password = var.vagrant_ssh_password + ssh_timeout = var.ssh_timeout + boot_command = local.aarch64_vagrant_boot_command_9 + boot_wait = var.boot_wait + accelerator = var.aarch64_accelerator + firmware = var.aavmf_code + use_pflash = false + disk_interface = "virtio-scsi" + disk_size = var.vagrant_disk_size + disk_cache = "unsafe" + disk_discard = "unmap" + disk_detect_zeroes = "unmap" + format = "raw" + headless = var.headless + machine_type = "virt,gic-version=max" + memory = var.memory_aarch64 + net_device = "virtio-net" + qemu_binary = var.qemu_binary + vm_name = "almalinux-9-virtualbox-aarch64.raw" + cpu_model = var.aarch64_cpu_model + cpus = var.cpus + qemuargs = concat( + [["-boot", "strict=on"], ["-monitor", "none"]], + var.aarch64_console_log != "" ? [["-chardev", "vc,id=serial0,logfile=${var.aarch64_console_log}"], ["-serial", "chardev:serial0"]] : [], + ) +} + build { sources = [ "source.qemu.almalinux-9", @@ -311,6 +352,7 @@ build { "source.vmware-iso.almalinux-9-aarch64", "source.qemu.almalinux-9-vmware-aarch64", "source.qemu.almalinux-9-parallels-aarch64", + "source.qemu.almalinux-9-virtualbox-aarch64", "source.parallels-iso.almalinux-9-aarch64", ] @@ -456,6 +498,37 @@ build { only = ["qemu.almalinux-9-parallels-aarch64"] } + # The QEMU-built VirtualBox box is provisioned as a VirtualBox box: the + # Guest Additions (virtualbox_guest_arm64 role) instead of the QEMU guest + # agent. The virtualbox-iso builder uploads the Guest Additions ISO itself; + # here it comes from var.virtualbox_guest_additions_iso_aarch64, to where + # the role looks for it. + provisioner "file" { + source = var.virtualbox_guest_additions_iso_aarch64 + destination = "/home/vagrant/VBoxGuestAdditions.iso" + only = ["qemu.almalinux-9-virtualbox-aarch64"] + } + + provisioner "ansible" { + user = "vagrant" + galaxy_file = "./ansible/requirements.yml" + galaxy_force_install = true + collections_path = "./ansible/collections" + roles_path = "./ansible/roles" + playbook_file = "./ansible/vagrant.yml" + ansible_env_vars = [ + "ANSIBLE_PIPELINING=True", + "ANSIBLE_REMOTE_TEMP=/tmp", + "ANSIBLE_SSH_TRANSFER_METHOD=scp", + "ANSIBLE_SCP_EXTRA_ARGS=-O", + ] + extra_arguments = [ + "--extra-vars", + "packer_provider=virtualbox-iso", + ] + only = ["qemu.almalinux-9-virtualbox-aarch64"] + } + post-processors { post-processor "vagrant" { @@ -537,6 +610,21 @@ build { only = ["qemu.almalinux-9-parallels-aarch64"] } + post-processor "shell-local" { + inline = [ + "tools/raw-to-vagrant-virtualbox.sh ${source.name} AlmaLinux-9-Vagrant-virtualbox-${var.os_ver_9}-${formatdate("YYYYMMDD", timestamp())}.aarch64", + ] + only = ["qemu.almalinux-9-virtualbox-aarch64"] + } + + post-processor "artifice" { + files = [ + "AlmaLinux-9-Vagrant-virtualbox-${var.os_ver_9}-${formatdate("YYYYMMDD", timestamp())}.aarch64.box", + "AlmaLinux-9-Vagrant-virtualbox-${var.os_ver_9}-${formatdate("YYYYMMDD", timestamp())}.aarch64.raw", + ] + only = ["qemu.almalinux-9-virtualbox-aarch64"] + } + post-processor "artifice" { files = [ "AlmaLinux-9-Vagrant-vmware-${var.os_ver_9}-${formatdate("YYYYMMDD", timestamp())}.aarch64.box", diff --git a/almalinux_10_vagrant.pkr.hcl b/almalinux_10_vagrant.pkr.hcl index ec4eebd6..f1239797 100644 --- a/almalinux_10_vagrant.pkr.hcl +++ b/almalinux_10_vagrant.pkr.hcl @@ -420,6 +420,47 @@ source "qemu" "almalinux_10_vagrant_parallels_qemu_aarch64" { ) } +# The VirtualBox aarch64 box (VirtualBox 7.1+ on Apple Silicon) built under +# QEMU, like the Parallels one: there is no runner that can run VirtualBox ARM +# guests, so the install and provisioning run in QEMU (TCG on an x86_64 +# runner, see the aarch64_* variables) and tools/raw-to-vagrant-virtualbox.sh +# packs the raw disk into a virtualbox box (OVF + streamOptimized VMDK). The +# kickstart installs a generic initramfs (dracut-config-generic), so the +# VirtioSCSI disk and virtio NIC VirtualBox presents are handled on the first +# boot there. +source "qemu" "almalinux_10_vagrant_virtualbox_qemu_aarch64" { + iso_url = local.iso_url_10_aarch64 + iso_checksum = local.iso_checksum_10_aarch64 + http_directory = var.http_directory + shutdown_command = var.vagrant_shutdown_command + ssh_username = var.vagrant_ssh_username + ssh_password = var.vagrant_ssh_password + ssh_timeout = var.ssh_timeout + boot_command = local.aarch64_vagrant_boot_command_10 + boot_wait = var.boot_wait + accelerator = var.aarch64_accelerator + firmware = var.aavmf_code + use_pflash = false + disk_interface = "virtio-scsi" + disk_size = var.vagrant_disk_size + disk_cache = "unsafe" + disk_discard = "unmap" + disk_detect_zeroes = "unmap" + format = "raw" + headless = var.headless + machine_type = "virt,gic-version=max" + memory = var.memory_aarch64 + net_device = "virtio-net" + qemu_binary = var.qemu_binary + vm_name = "almalinux_10_vagrant_virtualbox_qemu_aarch64.raw" + cpu_model = var.aarch64_cpu_model + cpus = var.cpus + qemuargs = concat( + [["-boot", "strict=on"], ["-monitor", "none"]], + var.aarch64_console_log != "" ? [["-chardev", "vc,id=serial0,logfile=${var.aarch64_console_log}"], ["-serial", "chardev:serial0"]] : [], + ) +} + build { sources = [ "source.hyperv-iso.almalinux_10_vagrant_hyperv_x86_64", @@ -432,6 +473,7 @@ build { "source.vmware-iso.almalinux_10_vagrant_vmware_aarch64", "source.qemu.almalinux_10_vagrant_vmware_qemu_aarch64", "source.qemu.almalinux_10_vagrant_parallels_qemu_aarch64", + "source.qemu.almalinux_10_vagrant_virtualbox_qemu_aarch64", "source.hyperv-iso.almalinux_10_vagrant_hyperv_x86_64_v2", "source.qemu.almalinux_10_vagrant_hyperv_x86_64_v2", "source.qemu.almalinux_10_vagrant_libvirt_x86_64_v2", @@ -591,6 +633,37 @@ build { only = ["qemu.almalinux_10_vagrant_parallels_qemu_aarch64"] } + # The QEMU-built VirtualBox box is provisioned as a VirtualBox box: the + # Guest Additions (virtualbox_guest_arm64 role) instead of the QEMU guest + # agent. The virtualbox-iso builder uploads the Guest Additions ISO itself; + # here it comes from var.virtualbox_guest_additions_iso_aarch64, to where + # the role looks for it. + provisioner "file" { + source = var.virtualbox_guest_additions_iso_aarch64 + destination = "/home/vagrant/VBoxGuestAdditions.iso" + only = ["qemu.almalinux_10_vagrant_virtualbox_qemu_aarch64"] + } + + provisioner "ansible" { + user = "vagrant" + galaxy_file = "./ansible/requirements.yml" + galaxy_force_install = true + collections_path = "./ansible/collections" + roles_path = "./ansible/roles" + playbook_file = "./ansible/vagrant.yml" + ansible_env_vars = [ + "ANSIBLE_PIPELINING=True", + "ANSIBLE_REMOTE_TEMP=/tmp", + "ANSIBLE_SSH_TRANSFER_METHOD=scp", + "ANSIBLE_SCP_EXTRA_ARGS=-O", + ] + extra_arguments = [ + "--extra-vars", + "packer_provider=virtualbox-iso", + ] + only = ["qemu.almalinux_10_vagrant_virtualbox_qemu_aarch64"] + } + post-processors { post-processor "vagrant" { @@ -710,6 +783,21 @@ build { only = ["qemu.almalinux_10_vagrant_parallels_qemu_aarch64"] } + post-processor "shell-local" { + inline = [ + "tools/raw-to-vagrant-virtualbox.sh ${source.name} AlmaLinux-10-Vagrant-virtualbox-${var.os_ver_10}-${formatdate("YYYYMMDD", timestamp())}.${var.build_number}.aarch64", + ] + only = ["qemu.almalinux_10_vagrant_virtualbox_qemu_aarch64"] + } + + post-processor "artifice" { + files = [ + "AlmaLinux-10-Vagrant-virtualbox-${var.os_ver_10}-${formatdate("YYYYMMDD", timestamp())}.${var.build_number}.aarch64.box", + "AlmaLinux-10-Vagrant-virtualbox-${var.os_ver_10}-${formatdate("YYYYMMDD", timestamp())}.${var.build_number}.aarch64.raw", + ] + only = ["qemu.almalinux_10_vagrant_virtualbox_qemu_aarch64"] + } + post-processor "artifice" { files = [ "AlmaLinux-10-Vagrant-vmware-${var.os_ver_10}-${formatdate("YYYYMMDD", timestamp())}.${var.build_number}.aarch64.box", diff --git a/almalinux_kitten_10_vagrant.pkr.hcl b/almalinux_kitten_10_vagrant.pkr.hcl index 4320b3d3..ef250c76 100644 --- a/almalinux_kitten_10_vagrant.pkr.hcl +++ b/almalinux_kitten_10_vagrant.pkr.hcl @@ -420,6 +420,47 @@ source "qemu" "almalinux_kitten_10_vagrant_parallels_qemu_aarch64" { ) } +# The VirtualBox aarch64 box (VirtualBox 7.1+ on Apple Silicon) built under +# QEMU, like the Parallels one: there is no runner that can run VirtualBox ARM +# guests, so the install and provisioning run in QEMU (TCG on an x86_64 +# runner, see the aarch64_* variables) and tools/raw-to-vagrant-virtualbox.sh +# packs the raw disk into a virtualbox box (OVF + streamOptimized VMDK). The +# kickstart installs a generic initramfs (dracut-config-generic), so the +# VirtioSCSI disk and virtio NIC VirtualBox presents are handled on the first +# boot there. +source "qemu" "almalinux_kitten_10_vagrant_virtualbox_qemu_aarch64" { + iso_url = var.iso_url_kitten_10_aarch64 + iso_checksum = var.iso_checksum_kitten_10_aarch64 + http_directory = var.http_directory + shutdown_command = var.vagrant_shutdown_command + ssh_username = var.vagrant_ssh_username + ssh_password = var.vagrant_ssh_password + ssh_timeout = var.ssh_timeout + boot_command = local.aarch64_vagrant_boot_command_kitten_10 + boot_wait = var.boot_wait + accelerator = var.aarch64_accelerator + firmware = var.aavmf_code + use_pflash = false + disk_interface = "virtio-scsi" + disk_size = var.vagrant_disk_size + disk_cache = "unsafe" + disk_discard = "unmap" + disk_detect_zeroes = "unmap" + format = "raw" + headless = var.headless + machine_type = "virt,gic-version=max" + memory = var.memory_aarch64 + net_device = "virtio-net" + qemu_binary = var.qemu_binary + vm_name = "almalinux_kitten_10_vagrant_virtualbox_qemu_aarch64.raw" + cpu_model = var.aarch64_cpu_model + cpus = var.cpus + qemuargs = concat( + [["-boot", "strict=on"], ["-monitor", "none"]], + var.aarch64_console_log != "" ? [["-chardev", "vc,id=serial0,logfile=${var.aarch64_console_log}"], ["-serial", "chardev:serial0"]] : [], + ) +} + build { sources = [ "source.hyperv-iso.almalinux_kitten_10_vagrant_hyperv_x86_64", @@ -432,6 +473,7 @@ build { "source.vmware-iso.almalinux_kitten_10_vagrant_vmware_aarch64", "source.qemu.almalinux_kitten_10_vagrant_vmware_qemu_aarch64", "source.qemu.almalinux_kitten_10_vagrant_parallels_qemu_aarch64", + "source.qemu.almalinux_kitten_10_vagrant_virtualbox_qemu_aarch64", "source.hyperv-iso.almalinux_kitten_10_vagrant_hyperv_x86_64_v2", "source.qemu.almalinux_kitten_10_vagrant_hyperv_x86_64_v2", "source.qemu.almalinux_kitten_10_vagrant_libvirt_x86_64_v2", @@ -591,6 +633,37 @@ build { only = ["qemu.almalinux_kitten_10_vagrant_parallels_qemu_aarch64"] } + # The QEMU-built VirtualBox box is provisioned as a VirtualBox box: the + # Guest Additions (virtualbox_guest_arm64 role) instead of the QEMU guest + # agent. The virtualbox-iso builder uploads the Guest Additions ISO itself; + # here it comes from var.virtualbox_guest_additions_iso_aarch64, to where + # the role looks for it. + provisioner "file" { + source = var.virtualbox_guest_additions_iso_aarch64 + destination = "/home/vagrant/VBoxGuestAdditions.iso" + only = ["qemu.almalinux_kitten_10_vagrant_virtualbox_qemu_aarch64"] + } + + provisioner "ansible" { + user = "vagrant" + galaxy_file = "./ansible/requirements.yml" + galaxy_force_install = true + collections_path = "./ansible/collections" + roles_path = "./ansible/roles" + playbook_file = "./ansible/vagrant.yml" + ansible_env_vars = [ + "ANSIBLE_PIPELINING=True", + "ANSIBLE_REMOTE_TEMP=/tmp", + "ANSIBLE_SSH_TRANSFER_METHOD=scp", + "ANSIBLE_SCP_EXTRA_ARGS=-O", + ] + extra_arguments = [ + "--extra-vars", + "packer_provider=virtualbox-iso", + ] + only = ["qemu.almalinux_kitten_10_vagrant_virtualbox_qemu_aarch64"] + } + post-processors { post-processor "vagrant" { @@ -710,6 +783,21 @@ build { only = ["qemu.almalinux_kitten_10_vagrant_parallels_qemu_aarch64"] } + post-processor "shell-local" { + inline = [ + "tools/raw-to-vagrant-virtualbox.sh ${source.name} AlmaLinux-Kitten-Vagrant-virtualbox-10-${formatdate("YYYYMMDD", timestamp())}.${var.build_number}.aarch64", + ] + only = ["qemu.almalinux_kitten_10_vagrant_virtualbox_qemu_aarch64"] + } + + post-processor "artifice" { + files = [ + "AlmaLinux-Kitten-Vagrant-virtualbox-10-${formatdate("YYYYMMDD", timestamp())}.${var.build_number}.aarch64.box", + "AlmaLinux-Kitten-Vagrant-virtualbox-10-${formatdate("YYYYMMDD", timestamp())}.${var.build_number}.aarch64.raw", + ] + only = ["qemu.almalinux_kitten_10_vagrant_virtualbox_qemu_aarch64"] + } + post-processor "artifice" { files = [ "AlmaLinux-Kitten-Vagrant-vmware-10-${formatdate("YYYYMMDD", timestamp())}.${var.build_number}.aarch64.box", diff --git a/ansible/roles/virtualbox_guest_arm64/defaults/main.yml b/ansible/roles/virtualbox_guest_arm64/defaults/main.yml new file mode 100644 index 00000000..c2f4817a --- /dev/null +++ b/ansible/roles/virtualbox_guest_arm64/defaults/main.yml @@ -0,0 +1,4 @@ +--- +# Where the packer template uploads the Guest Additions ISO (the same path +# the virtualbox-iso builder and the ezamriy.vbox_guest role use) +vbox_additions_iso_path: /home/vagrant/VBoxGuestAdditions.iso diff --git a/ansible/roles/virtualbox_guest_arm64/meta/main.yml b/ansible/roles/virtualbox_guest_arm64/meta/main.yml new file mode 100644 index 00000000..f1d4a67e --- /dev/null +++ b/ansible/roles/virtualbox_guest_arm64/meta/main.yml @@ -0,0 +1,16 @@ +galaxy_info: + role_name: virtualbox_guest_arm64 + description: Installs the VirtualBox Guest Additions for arm64 Linux from the Guest Additions ISO + license: MIT + min_ansible_version: 2.5 + platforms: + - name: EL + versions: + - 9 + - 10 + galaxy_tags: + - guest + - system + - virtualbox + +dependencies: [] diff --git a/ansible/roles/virtualbox_guest_arm64/tasks/main.yml b/ansible/roles/virtualbox_guest_arm64/tasks/main.yml new file mode 100644 index 00000000..fcd74b54 --- /dev/null +++ b/ansible/roles/virtualbox_guest_arm64/tasks/main.yml @@ -0,0 +1,112 @@ +--- +# The VirtualBox Guest Additions for an arm64 guest (VirtualBox 7.1+ on +# Apple Silicon). The ezamriy.vbox_guest role runs the x86 installer +# (VBoxLinuxAdditions.run); the ISO carries a separate arm64 one. +# +# The QEMU-built box installs them while running under QEMU, not +# VirtualBox: the installer builds and installs the kernel modules and the +# services, which then load on the first boot in VirtualBox. Its exit code +# reflects the services it could not start here, so success is judged by the +# built modules instead. + +- name: Gather package facts + package_facts: + manager: rpm + +- name: Detect missing Guest Additions build dependencies + set_fact: + vbox_guest_missing_deps: "{{ vbox_guest_missing_deps | default([]) + [item] }}" + loop: + - bzip2 + - elfutils-libelf-devel + - gcc + - make + - perl + when: item not in ansible_facts.packages | list + +# The modules are built for the running kernel, so its own kernel-devel +- name: Check for the running kernel's kernel-devel + command: rpm -q kernel-devel-{{ ansible_kernel }} + register: vbox_guest_kernel_devel + failed_when: false + changed_when: false + +- name: Add the running kernel's kernel-devel to the dependencies + set_fact: + vbox_guest_missing_deps: "{{ vbox_guest_missing_deps | default([]) + ['kernel-devel-' ~ ansible_kernel] }}" + when: vbox_guest_kernel_devel.rc != 0 + +- name: Install the Guest Additions build dependencies + dnf: + name: "{{ vbox_guest_missing_deps }}" + state: present + when: vbox_guest_missing_deps is defined + +- name: Create mount point for the Guest Additions ISO + file: + path: /mnt/vbox-additions + state: directory + mode: "0755" + +- name: Mount the Guest Additions ISO + command: mount -o loop,ro {{ vbox_additions_iso_path | quote }} /mnt/vbox-additions + args: + creates: /mnt/vbox-additions/VBoxLinuxAdditions-arm64.run + +- name: Install the VirtualBox Guest Additions (arm64) + command: /mnt/vbox-additions/VBoxLinuxAdditions-arm64.run --nox11 + register: vbox_additions_install + failed_when: false + changed_when: true + +- name: Show the Guest Additions installer output + debug: + var: vbox_additions_install.stdout_lines + +- name: Find the built Guest Additions kernel modules + find: + paths: /lib/modules/{{ ansible_kernel }} + patterns: "vboxguest.ko*,vboxsf.ko*" + recurse: true + register: vbox_additions_modules + +- name: Fail if the Guest Additions kernel modules were not built + assert: + that: vbox_additions_modules.files | map(attribute='path') | map('basename') | map('regex_replace', '\.ko.*$', '') | unique | length == 2 + fail_msg: >- + vboxguest/vboxsf not built for {{ ansible_kernel }} (installer exit code + {{ vbox_additions_install.rc }}); see /var/log/vboxadd-setup.log + success_msg: "{{ vbox_additions_modules.files | map(attribute='path') | list }}" + +- name: Unmount the Guest Additions ISO + command: umount /mnt/vbox-additions + args: + removes: /mnt/vbox-additions/VBoxLinuxAdditions-arm64.run + +- name: Remove the Guest Additions mount point + file: + path: /mnt/vbox-additions + state: absent + +- name: Remove the Guest Additions ISO + file: + path: "{{ vbox_additions_iso_path }}" + state: absent + +- name: Remove the Guest Additions build dependencies + dnf: + name: "{{ vbox_guest_missing_deps }}" + state: absent + when: vbox_guest_missing_deps is defined + +- name: Find /var/log/vboxadd-* logs + find: + paths: /var/log + patterns: "vboxadd-*" + register: vboxadd_logs + +- name: Delete /var/log/vboxadd-* logs + file: + path: "{{ item.path }}" + state: absent + loop: "{{ vboxadd_logs.files }}" diff --git a/ansible/vagrant.yml b/ansible/vagrant.yml index 6caf63a2..4e2b337b 100644 --- a/ansible/vagrant.yml +++ b/ansible/vagrant.yml @@ -11,7 +11,10 @@ when: is_unified_boot is defined - vagrant_guest - role: ezamriy.vbox_guest - when: packer_provider == 'virtualbox-iso' + when: packer_provider == 'virtualbox-iso' and ansible_architecture == 'x86_64' + # The ezamriy.vbox_guest role runs the x86 installer only + - role: virtualbox_guest_arm64 + when: packer_provider == 'virtualbox-iso' and ansible_architecture == 'aarch64' - role: vmware_guest when: packer_provider == 'vmware-iso' - role: parallels_guest diff --git a/http/almalinux-10.vagrant-aarch64.ks b/http/almalinux-10.vagrant-aarch64.ks index bdb58cc8..ec458a73 100644 --- a/http/almalinux-10.vagrant-aarch64.ks +++ b/http/almalinux-10.vagrant-aarch64.ks @@ -28,8 +28,8 @@ tar -dracut-config-rescue -firewalld # anaconda adds qemu-guest-agent when it installs under KVM, as the -# QEMU-built VMware and Parallels boxes are on the AlmaLinux org's arm64 -# runner; none of the aarch64 boxes wants it +# QEMU-built VMware, Parallels and VirtualBox boxes are on the AlmaLinux +# org's arm64 runner; none of the aarch64 boxes wants it -qemu-guest-agent %end @@ -49,13 +49,13 @@ sed -i "s/^.*requiretty/# Defaults requiretty/" /etc/sudoers # permit root login via SSH with password authetication echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/01-permitrootlogin.conf -# A TCG build (the QEMU-built VMware or Parallels box, or any aarch64 build -# on an x86_64 runner) types console=ttyAMA0 into the installer's GRUB -# command line (so anaconda's output goes to the captured serial console), -# and anaconda copies the installer's console= arguments into the installed -# boot loader configuration. Take it out again so the image's kernel command -# line is the same as from an arm64 host, where nothing is added and these -# two lines are no-ops. +# A TCG build (the QEMU-built VMware, Parallels or VirtualBox box, or any +# aarch64 build on an x86_64 runner) types console=ttyAMA0 into the +# installer's GRUB command line (so anaconda's output goes to the captured +# serial console), and anaconda copies the installer's console= arguments +# into the installed boot loader configuration. Take it out again so the +# image's kernel command line is the same as from an arm64 host, where +# nothing is added and these two lines are no-ops. sed -i 's/ console=ttyAMA0//' /etc/default/grub grubby --update-kernel=ALL --remove-args="console=ttyAMA0" # The serial console= argument also makes anaconda switch GRUB to a serial diff --git a/http/almalinux-9.vagrant-aarch64.ks b/http/almalinux-9.vagrant-aarch64.ks index 44089f8d..5ebdafa7 100644 --- a/http/almalinux-9.vagrant-aarch64.ks +++ b/http/almalinux-9.vagrant-aarch64.ks @@ -29,8 +29,8 @@ rsyslog-logrotate -dracut-config-rescue -firewalld # anaconda adds qemu-guest-agent when it installs under KVM, as the -# QEMU-built VMware and Parallels boxes are on the AlmaLinux org's arm64 -# runner; none of the aarch64 boxes wants it +# QEMU-built VMware, Parallels and VirtualBox boxes are on the AlmaLinux +# org's arm64 runner; none of the aarch64 boxes wants it -qemu-guest-agent %end @@ -50,13 +50,13 @@ sed -i "s/^.*requiretty/# Defaults requiretty/" /etc/sudoers # permit root login via SSH with password authetication echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/01-permitrootlogin.conf -# A TCG build (the QEMU-built VMware or Parallels box, or any aarch64 build -# on an x86_64 runner) types console=ttyAMA0 into the installer's GRUB -# command line (so anaconda's output goes to the captured serial console), -# and anaconda copies the installer's console= arguments into the installed -# boot loader configuration. Take it out again so the image's kernel command -# line is the same as from an arm64 host, where nothing is added and these -# two lines are no-ops. +# A TCG build (the QEMU-built VMware, Parallels or VirtualBox box, or any +# aarch64 build on an x86_64 runner) types console=ttyAMA0 into the +# installer's GRUB command line (so anaconda's output goes to the captured +# serial console), and anaconda copies the installer's console= arguments +# into the installed boot loader configuration. Take it out again so the +# image's kernel command line is the same as from an arm64 host, where +# nothing is added and these two lines are no-ops. sed -i 's/ console=ttyAMA0//' /etc/default/grub grubby --update-kernel=ALL --remove-args="console=ttyAMA0" # The serial console= argument also makes anaconda switch GRUB to a serial diff --git a/http/almalinux-kitten-10.vagrant-aarch64.ks b/http/almalinux-kitten-10.vagrant-aarch64.ks index c53de069..040cc891 100644 --- a/http/almalinux-kitten-10.vagrant-aarch64.ks +++ b/http/almalinux-kitten-10.vagrant-aarch64.ks @@ -28,8 +28,8 @@ tar -dracut-config-rescue -firewalld # anaconda adds qemu-guest-agent when it installs under KVM, as the -# QEMU-built VMware and Parallels boxes are on the AlmaLinux org's arm64 -# runner; none of the aarch64 boxes wants it +# QEMU-built VMware, Parallels and VirtualBox boxes are on the AlmaLinux +# org's arm64 runner; none of the aarch64 boxes wants it -qemu-guest-agent %end @@ -49,13 +49,13 @@ sed -i "s/^.*requiretty/# Defaults requiretty/" /etc/sudoers # permit root login via SSH with password authetication echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/01-permitrootlogin.conf -# A TCG build (the QEMU-built VMware or Parallels box, or any aarch64 build -# on an x86_64 runner) types console=ttyAMA0 into the installer's GRUB -# command line (so anaconda's output goes to the captured serial console), -# and anaconda copies the installer's console= arguments into the installed -# boot loader configuration. Take it out again so the image's kernel command -# line is the same as from an arm64 host, where nothing is added and these -# two lines are no-ops. +# A TCG build (the QEMU-built VMware, Parallels or VirtualBox box, or any +# aarch64 build on an x86_64 runner) types console=ttyAMA0 into the +# installer's GRUB command line (so anaconda's output goes to the captured +# serial console), and anaconda copies the installer's console= arguments +# into the installed boot loader configuration. Take it out again so the +# image's kernel command line is the same as from an arm64 host, where +# nothing is added and these two lines are no-ops. sed -i 's/ console=ttyAMA0//' /etc/default/grub grubby --update-kernel=ALL --remove-args="console=ttyAMA0" # The serial console= argument also makes anaconda switch GRUB to a serial diff --git a/tools/raw-to-vagrant-virtualbox.sh b/tools/raw-to-vagrant-virtualbox.sh new file mode 100755 index 00000000..e37d805b --- /dev/null +++ b/tools/raw-to-vagrant-virtualbox.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +# The script creates a Vagrant box for the VirtualBox provider from a raw disk +# image built with QEMU, the way raw-to-vagrant-parallels.sh does for +# Parallels Desktop. It is used for the aarch64 box (VirtualBox 7.1+ on Apple +# Silicon), built under QEMU (TCG on an x86_64 runner, natively on an arm64 +# one) because there is no runner that can run VirtualBox ARM guests. The box +# contains: +# - box.ovf, rendered from tpl/vagrant/virtualbox/box-aarch64.ovf (exported by +# VirtualBox 7.2 from an ARM VM of its own defaults for the guest type: +# ARMv8Virtual chipset, EFI, one VirtioSCSI disk, a virtio NIC on NAT), with +# fresh VM and disk UUIDs and MAC address +# - box-disk001.vmdk, converted with qemu-img from the raw image +# (streamOptimized, like the disks VirtualBox exports) +# - Vagrantfile, with the base MAC address, as the packer vagrant +# post-processor writes it for the x86_64 box +# - metadata.json, with the architecture and provider information +# +# These files are packed by tar+gzip to match the Vagrant box format. +# +# The script is called by the shell-local post-processor from the packer +# template. The raw image is kept for the GitHub Actions workflow to check +# the release and extract the package list from it. + +# Usage: +# tools/raw-to-vagrant-virtualbox.sh [ ] + +# Source Name, like: +# almalinux-9-virtualbox-aarch64 +# almalinux_10_vagrant_virtualbox_qemu_aarch64 +# almalinux_kitten_10_vagrant_virtualbox_qemu_aarch64 +source_name=$1 + +# Box Name, like: +# AlmaLinux-9-Vagrant-virtualbox-9.8-YYYYMMDD.aarch64 +# AlmaLinux-10-Vagrant-virtualbox-10.2-YYYYMMDD.0.aarch64 +# AlmaLinux-Kitten-Vagrant-virtualbox-10-YYYYMMDD.0.aarch64 +box_name=$2 + +# The VM defaults the box ships with: VirtualBox's own memory default for the +# guest type, and two vCPUs (EFI VirtualBox guests with one vCPU have been +# seen to hang early in boot) +numvcpus=${3:-2} +memsize=${4:-2048} + +# Creates: +# .box +# .raw + +set -euo pipefail + +case "${box_name}" in + *.aarch64) architecture=arm64 ;; + *) echo "[Error] ${box_name}: only aarch64 boxes are built this way" >&2; exit 1 ;; +esac + +# The VirtualBox guest OS type: Red Hat 9.x or 10.x (ARM 64-bit) +case "${box_name}" in + AlmaLinux-9-*) ostype=RedHat9_arm64 ;; + AlmaLinux-10-*|AlmaLinux-Kitten-*) ostype=RedHat10_arm64 ;; + *) echo "[Error] ${box_name}: unknown AlmaLinux version" >&2; exit 1 ;; +esac + +raw="output-${source_name}/${source_name}.raw" + +uuid() { cat /proc/sys/kernel/random/uuid; } +# A MAC address in the VirtualBox range (08:00:27), as VirtualBox assigns them +mac() { printf '080027%s' "$(od -An -N3 -tx1 /dev/urandom | tr -d ' \n' | tr 'a-f' 'A-F')"; } + +vm_name=${box_name/-Vagrant-virtualbox-/-Vagrant-VirtualBox-} +disk_file=box-disk001.vmdk +base_mac=$(mac) + +rm -rf "${source_name}" +mkdir -p "${source_name}" + +# The disk: streamOptimized, the VMDK flavour VirtualBox exports and imports +qemu-img convert -f raw -O vmdk -o subformat=streamOptimized "${raw}" "${source_name}/${disk_file}" +qemu-img check -f vmdk "${source_name}/${disk_file}" + +sed -e "s/@VM_UUID@/$(uuid)/" -e "s/@VM_NAME@/${vm_name}/g" \ + -e "s/@CREATION_DATE@/$(date -u '+%Y-%m-%dT%H:%M:%SZ')/" \ + -e "s/@OSTYPE@/${ostype}/g" -e "s/@DISK_UUID@/$(uuid)/g" \ + -e "s/@DISK_FILE@/${disk_file}/" -e "s/@DISK_CAPACITY@/$(stat -c %s "${raw}")/" \ + -e "s/@NUMVCPUS@/${numvcpus}/g" -e "s/@MEMSIZE@/${memsize}/g" \ + -e "s/@MAC@/${base_mac}/" \ + ./tpl/vagrant/virtualbox/box-aarch64.ovf > "${source_name}/box.ovf" +if grep -nE '@(VM_UUID|VM_NAME|CREATION_DATE|OSTYPE|DISK_UUID|DISK_FILE|DISK_CAPACITY|NUMVCPUS|MEMSIZE|MAC)@' "${source_name}/box.ovf"; then + echo "[Error] unrendered placeholders in box.ovf" >&2 + exit 1 +fi + +cat > "${source_name}/Vagrantfile" < "${source_name}/metadata.json" +ls -la "${source_name}" +cd "${source_name}" || exit 1 +tar --use-compress-program='gzip -9' -cvf "../${box_name}.box" . +cd - > /dev/null 2>&1 || exit 1 +mv "${raw}" "${box_name}.raw" +rm -rf "${source_name}" "output-${source_name}" diff --git a/tpl/vagrant/virtualbox/box-aarch64.ovf b/tpl/vagrant/virtualbox/box-aarch64.ovf new file mode 100644 index 00000000..db467b69 --- /dev/null +++ b/tpl/vagrant/virtualbox/box-aarch64.ovf @@ -0,0 +1,126 @@ + + + + + + + List of the virtual disks used in the package + + + + Logical networks used in the package + + Logical network used by this appliance. + + + + A virtual machine + + The kind of installed guest operating system + RedHat_64 + @OSTYPE@ + + + Virtual hardware requirements for a virtual machine + + Virtual Hardware Family + 0 + @VM_NAME@ + virtualbox-2.2 + + + @NUMVCPUS@ virtual CPUs + Number of virtual CPUs + @NUMVCPUS@ virtual CPUs + 1 + 3 + @NUMVCPUS@ + + + MegaBytes + @MEMSIZE@ MB of memory + Memory Size + @MEMSIZE@ MB of memory + 2 + 4 + @MEMSIZE@ + + + 0 + virtioSCSIController0 + VirtioSCSI Controller + virtioSCSIController0 + 3 + VirtioSCSI + 20 + + + 3 + false + sound + Sound Card + sound + 4 + ensoniq1371 + 35 + + + 0 + disk1 + Disk Image + disk1 + ovf:/disk/vmdisk1 + 5 + 3 + 17 + + + true + Ethernet adapter on 'NAT' + NAT + Ethernet adapter on 'NAT' + 6 + 10 + + + + Complete VirtualBox machine configuration in VirtualBox format + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/variables.pkr.hcl b/variables.pkr.hcl index 22cc9f9a..abe00077 100644 --- a/variables.pkr.hcl +++ b/variables.pkr.hcl @@ -1903,6 +1903,15 @@ variable "parallels_tools_iso_aarch64" { default = "prl-tools-lin-arm.iso" } +# VirtualBox + +variable "virtualbox_guest_additions_iso_aarch64" { + description = "Local path of the VirtualBox Guest Additions ISO the QEMU-built VirtualBox AArch64 box installs (the virtualbox-iso builder brings its own)" + + type = string + default = "VBoxGuestAdditions.iso" +} + # Oracle Cloud Infrastructure (OCI) local "oci_boot_command_8_x86_64" {