ci: build and run a QEMU guest VM for testing
authorVincent Fu <vincent.fu@samsung.com>
Fri, 6 Sep 2024 18:52:01 +0000 (18:52 +0000)
committerVincent Fu <vincent.fu@samsung.com>
Thu, 26 Sep 2024 17:29:45 +0000 (13:29 -0400)
Since GitHub Actions' free runners now support nested virtualization,
let's create a guest VM image and use it to run some tests that cannot
be run directly via a GitHub Actions runner (because these runners do
not allow insertion of kernel modules).

This patch adds a workflow that runs nightly and:
- creates a Debian guest VM image using libguestfs
- builds QEMU
- starts the VM
- clones/builds fio on the VM
- runs the test

Separate instances of the job are spawned to run the following tests:
- basic io_uring_cmd
- NVMe protection information w/16-bit Guard PI
- NVMe PI 4096+16 w/64-bit Guard PI
- NVMe PI 4096+64 w/64-bit Guard PI
- FDP
- t/zbd/run-tests-against-nullb

Signed-off-by: Vincent Fu <vincent.fu@samsung.com>
.github/actions/build-qemu/action.yml [new file with mode: 0644]
.github/actions/create-guest-image/action.yml [new file with mode: 0644]
.github/actions/start-vm/action.yml [new file with mode: 0644]
.github/workflows/qemu.yml [new file with mode: 0644]
ci/actions-install.sh

diff --git a/.github/actions/build-qemu/action.yml b/.github/actions/build-qemu/action.yml
new file mode 100644 (file)
index 0000000..06804b8
--- /dev/null
@@ -0,0 +1,31 @@
+name: 'Build and Install QEMU on Ubuntu'
+desription: 'Build QEMU and Install on Ubuntu'
+
+inputs:
+  version: # What QEMU version to build/install
+    description: 'QEMU version to build and install'
+    required: false
+    default: '9.1.0'
+
+
+runs:
+  using: "composite"
+  steps:
+    - name: Install QEMU build dependencies
+      run: sudo apt-get -qq install libglib2.0-dev libfdt-dev libpixman-1-dev ninja-build flex bison libsdl2-dev libaio-dev python3-tomli libslirp-dev
+      shell: bash
+
+    - name: Build and install QEMU
+      run: |
+        wget -nv https://download.qemu.org/qemu-$INPUT_VER.tar.xz
+        tar xJf qemu-$INPUT_VER.tar.xz
+        rm qemu-$INPUT_VER.tar.xz
+        cd qemu-$INPUT_VER
+        ./configure --enable-kvm --target-list=x86_64-softmmu
+        make -j $(nproc)
+        sudo make install
+        cd ..
+        rm -rf qemu-$INPUT_VER
+      shell: bash
+      env:
+        INPUT_VER: ${{ inputs.version }}
diff --git a/.github/actions/create-guest-image/action.yml b/.github/actions/create-guest-image/action.yml
new file mode 100644 (file)
index 0000000..ce41337
--- /dev/null
@@ -0,0 +1,45 @@
+name: 'Create guest image'
+description: 'Create VM guest image on Ubuntu runner'
+
+inputs:
+  distro:
+    description: 'Linux distribution to use for guest image'
+    required: false
+    default: 'debian-12'
+  extra_pkgs:
+    description: 'Extra packages to install for guest image'
+    required: false
+    default:
+
+runs:
+  using: "composite"
+  steps:
+    - name: Install libguestfs
+      run: sudo apt-get -qq install libguestfs-tools
+      shell: bash
+    - name: Setup steps for virt-builder
+      run: |
+        sudo chmod a+r /boot/vmlinuz*
+        sudo chmod 0666 /dev/kvm
+        ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
+      shell: bash
+    - name: Create Debian image
+      run: |
+        virt-builder ${{ inputs.distro }} \
+          --quiet \
+          --hostname fio-tester \
+          --ssh-inject root \
+          --run-command "ssh-keygen -A" \
+          --run-command "sed -i 's/ens2/enp0s2/g' /etc/network/interfaces" \
+          --append-line '/etc/environment:PYTHONUNBUFFERED=1' \
+          --append-line '/etc/environment:GITHUB_SERVER_URL=${{ github.server_url }}' \
+          --append-line '/etc/environment:GITHUB_REPOSITORY=${{ github.repository }}' \
+          --append-line '/etc/environment:GITHUB_REF=${{ github.ref }}' \
+          --append-line '/etc/environment:GITHUB_SHA=${{ github.sha }}' \
+          --append-line '/etc/environment:GITHUB_JOB=${{ github.job }}' \
+          --append-line '/etc/environment:EXTRA_PKGS=${{ inputs.extra_pkgs }}' \
+          --append-line '/etc/environment:CI_TARGET_BUILD=${{ env.CI_TARGET_BUILD }}' \
+          --append-line '/etc/environment:CI_TARGET_OS=${{ env.CI_TARGET_OS }}'
+
+      shell: bash
+
diff --git a/.github/actions/start-vm/action.yml b/.github/actions/start-vm/action.yml
new file mode 100644 (file)
index 0000000..d47977c
--- /dev/null
@@ -0,0 +1,54 @@
+name: 'Start QEMU VM'
+description: 'Start QEMU virtual machine'
+
+inputs:
+  qemu: # QEMU binary to use
+    required: false
+    default: "qemu-system-x86_64"
+  image: # VM image file
+    required: true
+  ssh_fwd_port: # forward this host port to the guest's SSH port
+    required: false
+    default: 2022
+  options: # Custom QEMU invocation options no \n at the end!
+    required: false
+  ram: # how much RAM to allocate to VM
+    required: false
+    default: "12G"
+  host_key: # If true add guest host key to known_hosts
+    required: false
+    default: "false"
+
+runs:
+  using: "composite"
+  steps:
+    - name: install wait-for-it
+      shell: bash
+      run: sudo apt-get -qq install wait-for-it
+    - name: Start VM in background
+      shell: bash
+      run: |
+        ${{ inputs.qemu }} \
+          -cpu host \
+          -drive file=${{ inputs.image }},format=raw,if=virtio \
+          -enable-kvm \
+          -smp $(nproc) \
+          -nographic \
+          -m ${{ inputs.ram }} \
+          -display none \
+          -machine q35,accel=kvm \
+          -nic user,model=virtio-net-pci,hostfwd=tcp::${{ inputs.ssh_fwd_port }}-:22 \
+          ${{ inputs.options }} \
+          &
+    - name: Wait for VM to boot
+      shell: bash
+      run: |
+        wait-for-it localhost:${{ inputs.ssh_fwd_port }} -t 15
+        sleep 3
+    - name: Add guest host key to known_hosts
+      shell: bash
+      run: |
+        if echo ${{ inputs.host_key }} | grep -c "true"
+        then
+          ssh root@localhost -p ${{ inputs.ssh_fwd_port }} -o StrictHostKeyChecking=no echo
+        fi
diff --git a/.github/workflows/qemu.yml b/.github/workflows/qemu.yml
new file mode 100644 (file)
index 0000000..4ba8bfa
--- /dev/null
@@ -0,0 +1,104 @@
+name: CI
+
+on:
+  schedule:
+    - cron: "50 3 * * *"  # daily at 4:50 UTC (00:50 EST)
+
+jobs:
+  qemu-guest:
+    runs-on: ubuntu-22.04
+
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - config: basic io_uring_cmd tests
+            device: >-
+              -device nvme,id=nvme0,serial=deadbeef
+              -drive id=nvm-0,file=nvme0.img,format=raw,if=none,discard=unmap,media=disk
+              -device nvme-ns,id=nvm-0,drive=nvm-0,bus=nvme0,nsid=1
+            test_cmd: "python3 t/run-fio-tests.py --nvmecdev /dev/ng0n1 --run-only 1014 1015"
+            extra_pkgs: "nvme-cli"
+          - config: 16-bit Guard PI tests (long)
+            device: >-
+              -device nvme,id=nvme0,serial=deadbeef
+              -drive id=nvm-0,file=nvme0.img,format=raw,if=none,discard=unmap,media=disk
+              -device nvme-ns,id=nvm-0,drive=nvm-0,bus=nvme0,nsid=1
+            test_cmd: "python3 t/nvmept_pi.py --fio ./fio --dut /dev/ng0n1"
+            extra_pkgs: "nvme-cli"
+          - config: 4K+16 w/64-bit Guard PI
+            device: >- # 4K+16 w/64-bit Guard PI
+              -device nvme,id=nvme1,serial=deadbeee
+              -drive id=nvm-1,file=nvme0.img,format=raw,if=none,discard=unmap,media=disk
+              -device nvme-ns,id=nvm-1,drive=nvm-1,bus=nvme1,nsid=1,pif=2,ms=16,mset=1,pi=1,pil=0,logical_block_size=4096,physical_block_size=4096
+            test_cmd: "python3 t/nvmept_pi.py --fio ./fio --dut /dev/ng0n1 --lbaf 6"
+            extra_pkgs: "nvme-cli"
+          - config: 4K+64 w/64-bit Guard PI
+            device: >-
+              -device nvme,id=nvme2,serial=deadeeef
+              -drive id=nvm-2,file=nvme0.img,format=raw,if=none,discard=unmap,media=disk
+              -device nvme-ns,id=nvm-2,drive=nvm-2,bus=nvme2,nsid=1,pif=2,ms=64,mset=1,pi=1,pil=0,logical_block_size=4096,physical_block_size=4096
+            test_cmd: "python3 t/nvmept_pi.py --fio ./fio --dut /dev/ng0n1 --lbaf 7"
+            extra_pkgs: "nvme-cli"
+          - config: FDP
+            device: >-
+              -device nvme-subsys,id=nvme-subsys0,fdp=on,fdp.runs=128K,fdp.nrg=8,fdp.nruh=64
+              -device nvme,id=nvme0,serial=deadbeef,subsys=nvme-subsys0
+              -drive id=nvm-1,file=nvme0.img,format=raw,if=none,discard=unmap,media=disk
+              -device nvme-ns,id=nvm-1,drive=nvm-1,bus=nvme0,nsid=1,logical_block_size=4096,physical_block_size=4096,fdp.ruhs=0-63
+            test_cmd: "nvme fdp status /dev/ng0n1 && python3 t/nvmept_fdp.py --fio ./fio --dut /dev/ng0n1"
+            extra_pkgs: "nvme-cli"
+          - config: ZBD
+            device:
+            test_cmd: "./t/zbd/run-tests-against-nullb"
+            extra_pkgs: sg3-utils
+
+    env:
+      DISTRO: debian-12
+      SSHCMD: ssh root@localhost -p 2022
+      CI_TARGET_BUILD: linux
+      CI_TARGET_OS: debian
+
+    steps:
+    - name: Check out repository
+      uses: actions/checkout@v4
+
+    - name: Create guest VM image
+      uses: ./.github/actions/create-guest-image
+      with:
+        distro: ${{ env.DISTRO }}
+        extra_pkgs: ${{ matrix.extra_pkgs }}
+
+    - name: Build and install QEMU
+      uses: ./.github/actions/build-qemu
+
+    - name: Create backing file for NVMe device
+      run: truncate -s 1G nvme0.img
+
+    - name: Start VM
+      uses: ./.github/actions/start-vm
+      with:
+        image: ${{ env.DISTRO }}.img
+        host_key: true
+        options: ${{ matrix.device }}
+
+    - name: Clone fio on guest
+      run: |
+          $SSHCMD "apt-get update && apt-get install -qq git"
+          $SSHCMD "git clone https://github.com/taiki-e/checkout-action --branch v1.3.0"
+          $SSHCMD "mkdir fio && cd fio && ../checkout-action/main.sh && git log -1"
+
+    - name: Install dependencies on guest
+      run: $SSHCMD "cd fio && ./ci/actions-install.sh"
+
+    - name: Build fio on guest
+      run: $SSHCMD "cd fio && ./ci/actions-build.sh"
+
+    - name: Show nvme device configuration
+      if: ${{ contains( matrix.extra_pkgs, 'nvme-cli' ) }}
+      run: |
+        $SSHCMD "nvme id-ns /dev/ng0n1 -H"
+        $SSHCMD "nvme nvm-id-ns /dev/ng0n1 -v"
+
+    - name: Run test
+      run: $SSHCMD "cd fio && ${{ matrix.test_cmd }}"
index 5ad7b959294e55dd28cf884a96706e2b92957e1c..5a32ec91b8335c5be00ecc42c5e6b180e0287ab1 100755 (executable)
@@ -82,8 +82,9 @@ DPKGCFG
        python3-sphinx
        python3-statsmodels
        sudo
+       ${EXTRA_PKGS:-}
     )
-    if [ "${GITHUB_JOB}" == "build-containers" ]; then
+    if [ "${GITHUB_JOB}" == "build-containers" ] || [ "${GITHUB_JOB}" == "qemu-guest" ]; then
         pkgs+=(
             bison
             build-essential
@@ -121,6 +122,7 @@ install_fedora() {
         python3-sphinx
         sudo
         valgrind-devel
+       ${EXTRA_PKGS:-}
     )
 
     case "${CI_TARGET_OS}" in