From 225a827e4fa9d058bb6c717ed7afd047249ed974 Mon Sep 17 00:00:00 2001 From: Tino Reichardt Date: Tue, 16 Jul 2024 09:22:15 +0200 Subject: [PATCH] ZTS: Use QEMU for tests on Linux and FreeBSD This commit adds functional tests for these systems: - AlmaLinux 8, AlmaLinux 9 - ArchLinux - CentOS Stream 9 - Fedora 39, Fedora 40 - Debian 11, Debian 12 - FreeBSD 13, FreeBSD 14, FreeBSD 15 - Ubuntu 20.04, Ubuntu 22.04, Ubuntu 24.04 Workflow for each operating system: - install QEMU on the github runner - download current cloud image - start and init that image via cloud-init - install deps and poweroff system - start system and build openzfs and then poweroff again - clone the system and start 3 qemu workers for the testings - do the functional testings, hopefully < 3h TODO - some tests are failing, they need to be fixed Signed-off-by: Tino Reichardt Signed-off-by: Tony Hutter --- .github/workflows/scripts/README.md | 10 + .github/workflows/scripts/merge_summary.awk | 104 +++++++++++ .github/workflows/scripts/qemu-1-setup.sh | 35 ++++ .github/workflows/scripts/qemu-2-start.sh | 166 +++++++++++++++++ .github/workflows/scripts/qemu-3-deps.sh | 191 +++++++++++++++++++ .github/workflows/scripts/qemu-4-build.sh | 146 +++++++++++++++ .github/workflows/scripts/qemu-5-setup.sh | 69 +++++++ .github/workflows/scripts/qemu-6-tests.sh | 123 +++++++++++++ .github/workflows/scripts/qemu-7-reports.sh | 43 +++++ .github/workflows/scripts/qemu-8-summary.sh | 192 ++++++++++++++++++++ .github/workflows/zfs-qemu.yml | 160 ++++++++++++++++ 11 files changed, 1239 insertions(+) create mode 100644 .github/workflows/scripts/README.md create mode 100755 .github/workflows/scripts/merge_summary.awk create mode 100755 .github/workflows/scripts/qemu-1-setup.sh create mode 100755 .github/workflows/scripts/qemu-2-start.sh create mode 100755 .github/workflows/scripts/qemu-3-deps.sh create mode 100755 .github/workflows/scripts/qemu-4-build.sh create mode 100755 .github/workflows/scripts/qemu-5-setup.sh create mode 100755 .github/workflows/scripts/qemu-6-tests.sh create mode 100755 .github/workflows/scripts/qemu-7-reports.sh create mode 100755 .github/workflows/scripts/qemu-8-summary.sh create mode 100644 .github/workflows/zfs-qemu.yml diff --git a/.github/workflows/scripts/README.md b/.github/workflows/scripts/README.md new file mode 100644 index 000000000000..d1d594df0eb3 --- /dev/null +++ b/.github/workflows/scripts/README.md @@ -0,0 +1,10 @@ + +Workflow for each operating system: +- install QEMU on the github runner +- download current cloud image +- start and init that image via cloud-init +- install deps and poweroff system +- start system and build openzfs and then poweroff again +- clone the system and start 4 qemu workers for the testings (4x 3GB RAM) +- use trimable virtual disks (3x 1GB) for each testing system +- do the functional testings < 3h for each os diff --git a/.github/workflows/scripts/merge_summary.awk b/.github/workflows/scripts/merge_summary.awk new file mode 100755 index 000000000000..20370463e2ed --- /dev/null +++ b/.github/workflows/scripts/merge_summary.awk @@ -0,0 +1,104 @@ +#!/bin/awk -f +# +# Merge multiple ZTS tests results summaries into a single summary. This is +# needed when you're running different parts of ZTS on different tests +# runners or VMs. +# +# Usage: +# +# ./merge_summary.awk summary1.txt [summary2.txt] [summary3.txt] ... +# +# or: +# +# cat summary*.txt | ./merge_summary.awk +# +BEGIN { + i=-1 + pass=0 + fail=0 + skip=0 + state="" + cl=0 + el=0 + upl=0 + ul=0 + + # Total seconds of tests runtime + total=0; +} + +# Skip empty lines +/^\s*$/{next} + +# Skip Configuration and Test lines +/^Test:/{state=""; next} +/Configuration/{state="";next} + +# When we see "test-runner.py" stop saving config lines, and +# save test runner lines +/test-runner.py/{state="testrunner"; runner=runner$0"\n"; next} + +# We need to differentiate the PASS counts from test result lines that start +# with PASS, like: +# +# PASS mv_files/setup +# +# Use state="pass_count" to differentiate +# +/Results Summary/{state="pass_count"; next} +/PASS/{ if (state=="pass_count") {pass += $2}} +/FAIL/{ if (state=="pass_count") {fail += $2}} +/SKIP/{ if (state=="pass_count") {skip += $2}} +/Running Time/{ + state=""; + running[i]=$3; + split($3, arr, ":") + total += arr[1] * 60 * 60; + total += arr[2] * 60; + total += arr[3] + next; +} + +/Tests with results other than PASS that are expected/{state="expected_lines"; next} +/Tests with result of PASS that are unexpected/{state="unexpected_pass_lines"; next} +/Tests with results other than PASS that are unexpected/{state="unexpected_lines"; next} +{ + if (state == "expected_lines") { + expected_lines[el] = $0 + el++ + } + + if (state == "unexpected_pass_lines") { + unexpected_pass_lines[upl] = $0 + upl++ + } + if (state == "unexpected_lines") { + unexpected_lines[ul] = $0 + ul++ + } +} + +# Reproduce summary +END { + print runner; + print "\nResults Summary" + print "PASS\t"pass + print "FAIL\t"fail + print "SKIP\t"skip + print "" + print "Running Time:\t"strftime("%T", total, 1) + if (pass+fail+skip > 0) { + percent_passed=(pass/(pass+fail+skip) * 100) + } + printf "Percent passed:\t%3.2f%", percent_passed + + print "\n\nTests with results other than PASS that are expected:" + for (j in expected_lines) + print expected_lines[j] + print "\n\nTests with result of PASS that are unexpected:" + for (j in unexpected_pass_lines) + print unexpected_pass_lines[j] + print "\n\nTests with results other than PASS that are unexpected:" + for (j in unexpected_lines) + print unexpected_lines[j] +} diff --git a/.github/workflows/scripts/qemu-1-setup.sh b/.github/workflows/scripts/qemu-1-setup.sh new file mode 100755 index 000000000000..9527aef56f57 --- /dev/null +++ b/.github/workflows/scripts/qemu-1-setup.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +###################################################################### +# 1) setup the action runner to start some qemu instance +###################################################################### + +set -eu + +# this one is fast and used for /mnt + swap +DISK="/dev/disk/cloud/azure_resource" +sudo swapoff -a +sudo sed -e "s|^$DISK.*||g" -i /etc/fstab +sudo umount -l /mnt + +# xfs seems to have a bit better vm handling +sudo sgdisk --zap-all $DISK >/dev/null +sudo sgdisk -n 1:0:0 -c 1:"testfs" $DISK -p +sleep 1 +sudo mkfs.xfs -f -s size=4096 $DISK-part1 +sudo mount -t xfs -o discard,noatime $DISK-part1 /mnt + +# activate new swap +sudo dd if=/dev/zero of=/mnt/swapfile count=8k bs=1M +sudo chmod 600 /mnt/swapfile +sudo mkswap /mnt/swapfile +sudo swapon /mnt/swapfile + +# install needed packages +sudo apt-get update +sudo apt-get install axel cloud-image-utils daemonize guestfs-tools \ + virt-manager linux-modules-extra-`uname -r` + +# generate ssh keys +rm -f ~/.ssh/id_ed25519 +ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -q -N "" diff --git a/.github/workflows/scripts/qemu-2-start.sh b/.github/workflows/scripts/qemu-2-start.sh new file mode 100755 index 000000000000..d9721150bff4 --- /dev/null +++ b/.github/workflows/scripts/qemu-2-start.sh @@ -0,0 +1,166 @@ +#!/usr/bin/env bash + +###################################################################### +# 2) start qemu with some operating system, init via cloud-init +###################################################################### + +set -eu + +# valid ostypes: virt-install --os-variant list +OS="$1" +OSv=$OS + +# compressed with .zst extension +FREEBSD="https://github.com/mcmilk/openzfs-freebsd-images/releases/download/v2024-07-07" +URLzs="" +# Ubuntu mirrors +#UBMIRROR="https://cloud-images.ubuntu.com" +#UBMIRROR="https://mirrors.cloud.tencent.com/ubuntu-cloud-images" +UBMIRROR="https://mirror.citrahost.com/ubuntu-cloud-images" + +case "$OS" in + almalinux8) + OSNAME="AlmaLinux 8" + URL="https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-latest.x86_64.qcow2" + ;; + almalinux9) + OSNAME="AlmaLinux 9" + URL="https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2" + ;; + archlinux) + OSNAME="Archlinux" + URL="https://geo.mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg.qcow2" + ;; + centos-stream9) + OSNAME="CentOS Stream 9" + URL="https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2" + ;; + debian11) + OSNAME="Debian 11" + URL="https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-generic-amd64.qcow2" + ;; + debian12) + OSNAME="Debian 12" + URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2" + ;; + fedora39) + OSNAME="Fedora 39" + OSv="fedora39" + URL="https://download.fedoraproject.org/pub/fedora/linux/releases/39/Cloud/x86_64/images/Fedora-Cloud-Base-39-1.5.x86_64.qcow2" + ;; + fedora40) + OSNAME="Fedora 40" + OSv="fedora39" + URL="https://download.fedoraproject.org/pub/fedora/linux/releases/40/Cloud/x86_64/images/Fedora-Cloud-Base-Generic.x86_64-40-1.14.qcow2" + ;; + freebsd13) + OSNAME="FreeBSD 13" + OSv="freebsd13.0" + URLzs="$FREEBSD/amd64-freebsd-13.3-STABLE.qcow2.zst" + BASH="/usr/local/bin/bash" + ;; + freebsd14) + OSNAME="FreeBSD 14" + OSv="freebsd14.0" + URLzs="$FREEBSD/amd64-freebsd-14.1-STABLE.qcow2.zst" + BASH="/usr/local/bin/bash" + ;; + freebsd15) + OSNAME="FreeBSD 15" + OSv="freebsd14.0" + URLzs="$FREEBSD/amd64-freebsd-15.0-CURRENT.qcow2.zst" + BASH="/usr/local/bin/bash" + ;; + tumbleweed) + OSNAME="openSUSE Tumbleweed" + OSv="opensusetumbleweed" + MIRROR="http://opensuse-mirror-gce-us.susecloud.net" + URL="$MIRROR/tumbleweed/appliances/openSUSE-MicroOS.x86_64-OpenStack-Cloud.qcow2" + ;; + ubuntu20) + OSNAME="Ubuntu 20.04" + OSv="ubuntu20.04" + URL="$UBMIRROR/focal/current/focal-server-cloudimg-amd64.img" + ;; + ubuntu22) + OSNAME="Ubuntu 22.04" + OSv="ubuntu22.04" + URL="$UBMIRROR/jammy/current/jammy-server-cloudimg-amd64.img" + ;; + ubuntu24) + OSNAME="Ubuntu 24.04" + OSv="ubuntu24.04" + URL="$UBMIRROR/noble/current/noble-server-cloudimg-amd64.img" + ;; + *) + echo "Wrong value for variable OS!" + exit 111 + ;; +esac + +IMG="/mnt/cloudimg.qcow2" +DISK="/mnt/openzfs.qcow2" +sudo chown -R $(whoami) /mnt + +if [ ! -z "$URLzs" ]; then + echo "Loading image $URLzs ..." + time axel -q -o "$IMG.zst" "$URLzs" + zstd -q -d --rm "$IMG.zst" +else + echo "Loading image $URL ..." + time axel -q -o "$IMG" "$URL" +fi + +# for later use +echo "$OS" > /var/tmp/os.txt +echo "$OSv" > /var/tmp/osvariant.txt +echo "$OSNAME" > /var/tmp/osname.txt + +qemu-img convert -q -f qcow2 -O qcow2 -c -o compression_type=zstd $IMG $DISK +rm -f $IMG + +echo "Resizing image to 40GiB ..." +qemu-img resize -q $DISK 40G + +PUBKEY=`cat ~/.ssh/id_ed25519.pub` +cat < /tmp/user-data +#cloud-config + +fqdn: $OS + +# user:zfs password:1 +users: +- name: root + shell: $BASH +- name: zfs + sudo: ALL=(ALL) NOPASSWD:ALL + shell: $BASH + lock-passwd: false + passwd: \$1\$EjKAQetN\$O7Tw/rZOHaeBP1AiCliUg/ + ssh_authorized_keys: + - $PUBKEY + +growpart: + mode: auto + devices: ['/'] + ignore_growroot_disabled: false +EOF + +for i in `seq 0 3`; do + sudo virsh net-update default add ip-dhcp-host \ + "" --live --config +done + +sudo virt-install \ + --os-variant $OSv \ + --name "openzfs" \ + --cpu host-passthrough \ + --virt-type=kvm --hvm \ + --vcpus=4,sockets=1 \ + --memory $((1024*4)) \ + --memballoon model=none \ + --graphics none \ + --network bridge=virbr0,model=e1000,mac='52:54:00:83:79:00' \ + --cloud-init user-data=/tmp/user-data \ + --disk $DISK,bus=virtio,cache=none,format=qcow2,driver.discard=unmap \ + --import --noautoconsole >/dev/null diff --git a/.github/workflows/scripts/qemu-3-deps.sh b/.github/workflows/scripts/qemu-3-deps.sh new file mode 100755 index 000000000000..050e0e47428c --- /dev/null +++ b/.github/workflows/scripts/qemu-3-deps.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env bash + +###################################################################### +# 3) install dependencies for compiling and loading +###################################################################### + +set -eu + +function archlinux() { + echo "##[group]Running pacman -Syu" + sudo pacman -Syu --noconfirm + echo "##[endgroup]" + + echo "##[group]Install Development Tools" + sudo pacman -Sy --noconfirm base-devel bc cpio dhclient dkms fakeroot \ + fio gdb inetutils less linux linux-headers lsscsi nfs-utils parted pax \ + perf python-packaging python-setuptools ksh samba sysstat rng-tools \ + rsync wget + echo "##[endgroup]" +} + +function debian() { + export DEBIAN_FRONTEND="noninteractive" + + echo "##[group]Running apt-get update+upgrade" + sudo apt-get update -y + sudo apt-get upgrade -y + echo "##[endgroup]" + + echo "##[group]Install Development Tools" + sudo apt-get install -y \ + acl alien attr autoconf bc cpio curl dbench dh-python \ + dkms fakeroot fio gdb gdebi git ksh lcov \ + isc-dhcp-client libacl1-dev libaio-dev libattr1-dev libblkid-dev \ + libcurl4-openssl-dev libdevmapper-dev libelf-dev libffi-dev \ + libmount-dev libpam0g-dev libselinux-dev libssl-dev libtool \ + libtool-bin libudev-dev linux-headers-$(uname -r) lsscsi \ + nfs-kernel-server pamtester parted python3 python3-all-dev \ + python3-cffi python3-dev python3-distlib python3-packaging \ + python3-setuptools python3-sphinx rng-tools rpm2cpio rsync samba \ + sysstat uuid-dev watchdog wget xfslibs-dev zlib1g-dev + echo "##[endgroup]" +} + +function freebsd() { + export ASSUME_ALWAYS_YES="YES" + + echo "##[group]Install Development Tools" + sudo pkg install -y autoconf automake autotools base64 checkbashisms fio \ + gdb git gmake gsed pkgconf python python3 gettext gettext-runtime \ + lcov libtool lscpu ksh93 pamtester pamtester rsync + sudo pkg install -xy \ + '^samba4[[:digit:]]+$' \ + '^py3[[:digit:]]+-cffi$' \ + '^py3[[:digit:]]+-sysctl$' \ + '^py3[[:digit:]]+-packaging$' + echo "##[endgroup]" +} + +# common packages for: almalinux, centos, redhat +function rhel() { + echo "##[group]Running dnf update" + echo "max_parallel_downloads=10" | sudo -E tee -a /etc/dnf/dnf.conf + sudo dnf clean all + sudo dnf update -y --setopt=fastestmirror=1 --refresh + echo "##[endgroup]" + + echo "##[group]Install Development Tools" + sudo dnf group install -y "Development Tools" + sudo dnf install -y \ + acl attr bc bzip2 curl dbench dkms elfutils-libelf-devel fio gdb git \ + kernel-rpm-macros ksh libacl-devel libaio-devel libargon2-devel \ + libattr-devel libblkid-devel libcurl-devel libffi-devel ncompress \ + libselinux-devel libtirpc-devel libtool libudev-devel libuuid-devel \ + lsscsi mdadm nfs-utils openssl-devel pam-devel pamtester parted perf \ + python3 python3-cffi python3-devel python3-packaging kernel-devel \ + python3-setuptools rng-tools rpcgen rpm-build rsync samba sysstat \ + systemd watchdog wget xfsprogs-devel zlib-devel + echo "##[endgroup]" +} + +function tumbleweed() { + echo "##[group]Running zypper is TODO!" + sleep 23456 + echo "##[endgroup]" +} + +# Install dependencies +case "$1" in + almalinux8) + echo "##[group]Enable epel and powertools repositories" + sudo dnf config-manager -y --set-enabled powertools + sudo dnf install -y epel-release + echo "##[endgroup]" + rhel + echo "##[group]Install kernel-abi-whitelists" + sudo dnf install -y kernel-abi-whitelists + echo "##[endgroup]" + ;; + almalinux9|centos-stream9) + echo "##[group]Enable epel and crb repositories" + sudo dnf config-manager -y --set-enabled crb + sudo dnf install -y epel-release + echo "##[endgroup]" + rhel + echo "##[group]Install kernel-abi-stablelists" + sudo dnf install -y kernel-abi-stablelists + echo "##[endgroup]" + ;; + archlinux) + archlinux + ;; + debian*) + debian + echo "##[group]Install Debian specific" + sudo apt-get install -yq linux-perf dh-sequence-dkms + echo "##[endgroup]" + ;; + fedora*) + rhel + ;; + freebsd*) + freebsd + ;; + tumbleweed) + tumbleweed + ;; + ubuntu*) + debian + echo "##[group]Install Ubuntu specific" + sudo apt-get install -yq linux-tools-common libtirpc-dev + if [ "$1" != "ubuntu20" ]; then + sudo apt-get install -yq dh-sequence-dkms + fi + echo "##[endgroup]" + echo "##[group]Delete Ubuntu OpenZFS modules" + for i in `find /lib/modules -name zfs -type d`; do sudo rm -rvf $i; done + echo "##[endgroup]" + ;; +esac + +# Start services +echo "##[group]Enable services" +case "$1" in + freebsd*) + echo "fdescfs /dev/fd fdescfs rw 0 0" | sudo -E tee -a /etc/fstab + sudo -E mount /dev/fd + sudo -E touch /etc/zfs/exports + sudo -E sysrc mountd_flags="/etc/zfs/exports" + sudo -E service nfsd enable + echo '[global]' | sudo -E tee /usr/local/etc/smb4.conf >/dev/null + sudo -E service samba_server enable + ;; + debian*|ubuntu*) + sudo -E systemctl enable nfs-kernel-server + sudo -E systemctl enable smbd + ;; + *) + # All other linux distros + sudo -E systemctl enable nfs-server + sudo -E systemctl enable smb + ;; +esac +echo "##[endgroup]" + +# Enable serial console and remove 'quiet' from linux kernel cmdline +case "$1" in + freebsd*) + true + ;; + *) + echo "##[group]Enable serial output" + sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="console=ttyS0,115200n8 random.trust_cpu=on/g; s/quiet //g' /etc/default/grub || true + for i in /boot/grub/grub.cfg /etc/grub2.cfg /etc/grub2-efi.cfg /boot/grub2/grub.cfg ; do + test -e $i || continue + echo sudo grub-mkconfig -o $i + sudo grub-mkconfig -o $i + done + echo "##[endgroup]" + ;; +esac + +# ssh config +mkdir -p $HOME/.ssh +echo "StrictHostKeyChecking no" >> $HOME/.ssh/config +echo "ConnectTimeout 1" >> $HOME/.ssh/config + +# poweroff the machine +# - use it for building openzfs when restarted +sleep 2 && sudo poweroff & +exit 0 diff --git a/.github/workflows/scripts/qemu-4-build.sh b/.github/workflows/scripts/qemu-4-build.sh new file mode 100755 index 000000000000..71f8da0878fe --- /dev/null +++ b/.github/workflows/scripts/qemu-4-build.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env bash + +###################################################################### +# 4) configure and build openzfs modules +###################################################################### + +set -eu + +function run() { + LOG="/var/tmp/build-stderr.txt" + echo "**************************************************" + echo "`date` ($*)" + echo "**************************************************" + $@ 2>>$LOG +} + +function freebsd() { + export MAKE="gmake" + echo "##[group]Autogen.sh" + run ./autogen.sh + echo "##[endgroup]" + + echo "##[group]Configure" + run ./configure \ + --prefix=/usr/local \ + --with-libintl-prefix=/usr/local \ + --enable-pyzfs \ + --enable-debug \ + --enable-debuginfo + echo "##[endgroup]" + + echo "##[group]Build" + run gmake -j`sysctl -n hw.ncpu` + echo "##[endgroup]" + + echo "##[group]Install" + run sudo gmake install + echo "##[endgroup]" +} + +function linux() { + echo "##[group]Autogen.sh" + run ./autogen.sh + echo "##[endgroup]" + + echo "##[group]Configure" + run ./configure \ + --prefix=/usr \ + --enable-pyzfs \ + --enable-debug \ + --enable-debuginfo + echo "##[endgroup]" + + echo "##[group]Build" + run make -j$(nproc) + echo "##[endgroup]" + + echo "##[group]Install" + run sudo make install + echo "##[endgroup]" +} + +function rpm_build_and_install() { + EXTRA_CONFIG="${1:-}" + echo "##[group]Autogen.sh" + run ./autogen.sh + echo "##[endgroup]" + + echo "##[group]Configure" + run ./configure --enable-debug --enable-debuginfo $EXTRA_CONFIG + echo "##[endgroup]" + + echo "##[group]Build" + run make pkg-kmod pkg-utils + echo "##[endgroup]" + + echo "##[group]Install" + run sudo yum -y --skip-broken localinstall $(ls *.rpm | grep -v src.rpm) + echo "##[endgroup]" + +} + +function deb_build_and_install() { +echo "##[group]Autogen.sh" + run ./autogen.sh + echo "##[endgroup]" + + echo "##[group]Configure" + run ./configure \ + --prefix=/usr \ + --enable-pyzfs \ + --enable-debug \ + --enable-debuginfo + echo "##[endgroup]" + + echo "##[group]Build" + run make native-deb-kmod native-deb-utils + echo "##[endgroup]" + + echo "##[group]Install" + # Do kmod install. Note that when you build the native debs, the + # packages themselves are placed in parent directory '../' rather than + # in the source directory like the rpms are. + run sudo apt-get -y install `find ../ | grep -E '\.deb$' | grep -Ev 'dkms|dracut'` + echo "##[endgroup]" +} + +# Debug: show kernel cmdline +if [ -e /proc/cmdline ] ; then + cat /proc/cmdline || true +fi + +cd $HOME/zfs +export PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" + +# remove old configuration files +sudo cloud-init clean --logs +sudo rm -rf /var/lib/cloud \ +/etc/systemd/network/10-cloud-init-eth0.network \ +/etc/network/interfaces.d/50-cloud-init.cfg \ +/etc/netplan/50-cloud-init.yaml + +# build +case "$1" in + freebsd*) + freebsd + ;; + alma*|centos*) + rpm_build_and_install "--with-spec=redhat" + ;; + fedora*) + rpm_build_and_install + ;; + debian*|ubuntu*) + deb_build_and_install + ;; + *) + linux + ;; +esac + +# poweroff the machine +# - use it for the testings when restarted +sync +sleep 2 && sudo poweroff & +exit 0 diff --git a/.github/workflows/scripts/qemu-5-setup.sh b/.github/workflows/scripts/qemu-5-setup.sh new file mode 100755 index 000000000000..cb20e277cfb2 --- /dev/null +++ b/.github/workflows/scripts/qemu-5-setup.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +###################################################################### +# 5) start test machines and load openzfs module +###################################################################### + +set -eu + +# machine not needed anymore +while pidof /usr/bin/qemu-system-x86_64 >/dev/null; do sleep 1; done +sudo virsh undefine openzfs + +############################################################### +# 1) 4GB RAM for host +# 2) 3x 4GB RAM for qemu +############################################################### +PUBKEY=`cat ~/.ssh/id_ed25519.pub` +OSv=`cat /var/tmp/osvariant.txt` +OS=`cat /var/tmp/os.txt` +for i in `seq 1 3`; do + echo "Generating disk for vm$i ..." + DISK="/mnt/vm$i.qcow2" + sudo qemu-img convert -q -f qcow2 -O qcow2 \ + -o compression_type=zstd,preallocation=metadata,cluster_size=128K \ + /mnt/openzfs.qcow2 $DISK + + cat < /tmp/user-data +#cloud-config + +fqdn: vm$i + +# user:zfs password:1 +users: +- name: root + shell: $BASH +- name: zfs + sudo: ALL=(ALL) NOPASSWD:ALL + shell: $BASH + lock-passwd: false + passwd: \$1\$EjKAQetN\$O7Tw/rZOHaeBP1AiCliUg/ + ssh_authorized_keys: + - $PUBKEY + +growpart: + mode: auto + devices: ['/'] + ignore_growroot_disabled: false +EOF + + sudo virt-install \ + --os-variant $OSv \ + --name "vm$i" \ + --cpu host-passthrough \ + --virt-type=kvm --hvm \ + --vcpus=2,sockets=1 \ + --memory $((1024*4)) \ + --memballoon model=none \ + --graphics none \ + --cloud-init user-data=/tmp/user-data \ + --network bridge=virbr0,model=e1000,mac="52:54:00:83:79:0$i" \ + --disk $DISK,bus=virtio,format=qcow2,driver.discard=unmap \ + --import --noautoconsole >/dev/null +done + +# check if the machines are okay +echo "Waiting for vm's to come up..." +while true; do ssh 2>/dev/null zfs@192.168.122.11 "uname -a" && break; done +while true; do ssh 2>/dev/null zfs@192.168.122.12 "uname -a" && break; done +while true; do ssh 2>/dev/null zfs@192.168.122.13 "uname -a" && break; done diff --git a/.github/workflows/scripts/qemu-6-tests.sh b/.github/workflows/scripts/qemu-6-tests.sh new file mode 100755 index 000000000000..1263dd3f787c --- /dev/null +++ b/.github/workflows/scripts/qemu-6-tests.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash + +###################################################################### +# 6) load openzfs module and run the tests +###################################################################### + +set -o pipefail + +if [ -z "$1" ]; then + + # called directly on the runner + P="/var/tmp" + cd $P + + OS=`cat os.txt` + IP1="192.168.122.11" + IP2="192.168.122.12" + IP3="192.168.122.13" + + df -h > df-prerun.txt + + # start as daemon and log stdout + SSH=`which ssh` + CMD='$HOME/zfs/.github/workflows/scripts/qemu-6-tests.sh' + daemonize -c $P -p vm1.pid -o vm1log.txt -- \ + $SSH zfs@$IP1 $CMD $OS part1 + daemonize -c $P -p vm2.pid -o vm2log.txt -- \ + $SSH zfs@$IP2 $CMD $OS part2 + daemonize -c $P -p vm3.pid -o vm3log.txt -- \ + $SSH zfs@$IP3 $CMD $OS part3 + + # give us the output of stdout + stderr - with prefix ;) + BASE="$HOME/work/zfs/zfs" + CMD="$BASE/scripts/zfs-tests-color.sh" + tail -fq vm1log.txt | $CMD | sed -e "s/^/vm1: /g" & + tail -fq vm2log.txt | $CMD | sed -e "s/^/vm2: /g" & + tail -fq vm3log.txt | $CMD | sed -e "s/^/vm3: /g" & + + # wait for all vm's to finnish + tail --pid=`cat vm1.pid` -f /dev/null + tail --pid=`cat vm2.pid` -f /dev/null + tail --pid=`cat vm3.pid` -f /dev/null + + df -h > df-postrun.txt + + # kill the tail/sed combo + killall tail + exit 0 +fi + +function freebsd() { + # when freebsd zfs is loaded, unload this one + kldstat -n zfs 2>/dev/null && sudo kldunload zfs + sudo -E ./scripts/zfs.sh + sudo kldstat -n openzfs + sudo dmesg -c > /var/tmp/dmesg-prerun.txt +} + +function linux() { + # remount rootfs with relatime + trim + mount -o remount,rw,relatime,discard / + sudo -E modprobe zfs + sudo dmesg -c > /var/tmp/dmesg-prerun.txt +} + +function gettests() { + TF="$TDIR/zfs-tests/tests/functional" + echo -n "-T " + case "$1" in + part1) + # ~1h 30m @ Almalinux 9 + echo "cli_root" + ;; + part2) + # ~1h 50m @ Almalinux 9 + ls $TF|grep '^[a-n]'|grep -v "cli_root"|xargs|tr -s ' ' ',' + ;; + part3) + # ~1h 50m @ Almalinux 9 + ls $TF|grep '^[o-z]'|xargs|tr -s ' ' ',' + ;; + esac +} + +function gettestsD() { + TF="$TDIR/zfs-tests/tests/functional" + echo -n "-T " + case "$1" in + part1) + echo "checksum" + ;; + part2) + echo "casenorm,trim" + ;; + part3) + echo "zpool_add,zpool_create,zpool_export" + ;; + esac +} + +# called within vm +export PATH="$PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin:/usr/local/bin" + +case "$1" in + freebsd*) + TDIR="/usr/local/share/zfs" + freebsd + OPTS=`gettests $2` + ;; + *) + TDIR="/usr/share/zfs" + OPTS=`gettests $2` + linux + ;; +esac + +# this part runs inside qemu, finally: run tests +cd /var/tmp +uname -a > /var/tmp/uname.txt +$TDIR/zfs-tests.sh -vKR -s 3G $OPTS +RV=$? +echo $RV > /var/tmp/exitcode.txt +exit $RV diff --git a/.github/workflows/scripts/qemu-7-reports.sh b/.github/workflows/scripts/qemu-7-reports.sh new file mode 100755 index 000000000000..7a78a69064bd --- /dev/null +++ b/.github/workflows/scripts/qemu-7-reports.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +###################################################################### +# 7) output the results of the previous stage in an ordered way +###################################################################### + +set -o pipefail +cd /var/tmp + +echo "VM disk usage before:" +cat disk-before.txt +echo "and afterwards:" +cat disk-afterwards.txt + +FAIL="FAIL" +PASS="PASS" + +BASE="$HOME/work/zfs/zfs" +MERGE="$BASE/.github/workflows/scripts/merge_summary.awk" +EXIT=0 + +msg2=$PASS +for i in `seq 1 3`; do + f="exitcode.vm$i" + scp 2>/dev/null zfs@192.168.122.1$i:/var/tmp/exitcode.txt $f + test -f $f || echo 2 > $f + rv=`cat $f` + if [ $rv != 0 ]; then + msg=$FAIL + msg2=$FAIL + EXIT=$rv + else + msg=$PASS + fi + + echo "##[group]Results vm$i [$msg]" + cat "vm${i}log.txt" | $BASE/scripts/zfs-tests-color.sh + echo "##[endgroup]" +done + +# all tests without grouping: +$MERGE vm{1,2,3}log.txt | $BASE/scripts/zfs-tests-color.sh +exit $EXIT diff --git a/.github/workflows/scripts/qemu-8-summary.sh b/.github/workflows/scripts/qemu-8-summary.sh new file mode 100755 index 000000000000..42d66d5dd5d9 --- /dev/null +++ b/.github/workflows/scripts/qemu-8-summary.sh @@ -0,0 +1,192 @@ +#!/usr/bin/env bash + +###################################################################### +# generate github summary page of all the testings +# /tr 2024-06-17 +###################################################################### + +# XXX +# set -eu + +# max size in KiB of debug output +DEBUG_MAX="100" + +function output() { + echo -e $* >> "out-$logfile.md" +} + +function outfile() { + CUR=`stat --printf="%s" "out-$logfile.md"` + ADD=`stat --printf="%s" "$1"` + X=$((CUR+ADD)) + if [ $X -gt $((1024*1023)) ]; then + logfile=$((logfile+1)) + fi + cat "$1" >> "out-$logfile.md" +} + +function showfile() { + cont=`cat $1` + hl="$2" +cat < tmp +
+ +$hl + +
$cont
+ +
+EOF + outfile tmp + rm -f tmp +} + +function send2github() { + test -f "$1" || exit 0 + dd if="$1" bs=1023k count=1 >> $GITHUB_STEP_SUMMARY +} + +function check_copy() { + test -s "$1" || return 0 + cat $1 > $2 +} + +function check_append() { + test -s "$1" || return 0 + cat $1 >> $2 +} + +# generate summary of one test +function generate() { + VMs=3 + #################################################################### + # osname.txt -> used for headline + # vm{1,2,3}log.txt (colored, used when current/log isn't there) + #################################################################### + # vm{1,2,3}/uname.txt -> used once + # vm{1,2,3}/build-stderr.txt -> used once + # vm{1,2,3}/dmesg-prerun.txt -> used once + # vm{1,2,3}/console.txt -> all 3 used + #################################################################### + # vm{1,2,3}/current/log -> if not there, kernel panic loading + # vm{1,2,3}/current/results -> if not there, kernel panic testings + # vm{1,2,3}/exitcode.txt + #################################################################### + + # headline of this summary + logfile=$((logfile+1)) + output "\n# Functional Tests: $osname\n" + for i in `seq 1 $VMs`; do + for f in build-stderr.txt dmesg-prerun.txt; do + check_copy vm$i/$f $f + touch $f + done + for f in uname.txt; do + check_append vm$i/$f $f + touch $f + done + done + + output "
"
+  outfile uname.txt
+  output "
" + + showfile "build-stderr.txt" "Module build (stderr output)" + showfile "dmesg-prerun.txt" "Dmesg output - before tests" + + for i in `seq 1 $VMs`; do + mkdir -p "vm$i/current" + + log="vm$i/current/log" + if [ ! -f $log ]; then + output ":exclamation: Logfile of vm$i tests is missing :exclamation:" + + # output the console contents and continue with next vm + if [ -s "vm$i/console.txt" ]; then + cat "vm$i/console.txt" | \ + sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" > "vm${i}log" + showfile "vm${i}log" "vm$i: serial console output" + rm -f "vm${i}log" + fi + touch $log + continue + fi + + results="vm$i/current/results" + if [ ! -f $results ]; then + output ":exclamation: Results file of vm$i tests is missing :exclamation:" + + # generate results file from log + cat $log | grep '^Test[: ]' > tests.txt + ./zts-report.py --no-maybes ./tests.txt > $results || true + # Running Time: 01:30:09 + # Running Time: not finished!! + echo -e "\nRunning Time:\tKernel panic or timeout!" >> $results + touch $results + fi + + if [ -s "vm$i/console.txt" ]; then + showfile "vm$i/console.txt" "vm$i: serial console output" + fi + + awk '/\[FAIL\]|\[KILLED\]/{ show=1; print; next; } \ + /\[SKIP\]|\[PASS\]/{ show=0; } show' $log > debug.txt + S=`stat --printf="%s" "debug.txt"` + if [ $S -gt $((1024*DEBUG_MAX)) ]; then + dd if=debug.txt of=debug-vm$i.txt count=$DEBUG_MAX bs=1024 2>/dev/null + echo "..." >> debug-vm$i.txt + echo "!!! THIS FILE IS BIGGER !!!" >> debug-vm$i.txt + echo "Please download the zip archiv for full content!" >> debug-vm$i.txt + else + mv -f debug.txt debug-vm$i.txt + fi + done + + # /home/runner/work/zfs/zfs/.github/workflows/scripts/merge_summary.awk + BASE="$HOME/work/zfs/zfs" + MERGE="$BASE/.github/workflows/scripts/merge_summary.awk" + $MERGE vm{1,2,3}log.txt > merge.txt + showfile "merge.txt" "Test Summary" + + for f in debug-vm*.txt; do cat $f >> debug.txt; done + touch debug.txt + S=`stat --printf="%s" "debug.txt"` + KB=`echo "$S/1024"|bc` + showfile "debug.txt" "Debug file ($KB KiB)" +} + +# functional tests via qemu +function summarize() { + for tarfile in Logs-functional-*/qemu-*.tar; do + if [ ! -s "$tarfile" ]; then + output "\n# Functional Tests: unknown\n" + output ":exclamation: Tarfile $tarfile is empty :exclamation:" + continue + fi + rm -rf vm* *.txt + tar xf "$tarfile" + osname=`cat osname.txt` + generate + done +} + +# https://docs.github.com/en/enterprise-server@3.6/actions/using-workflows/workflow-commands-for-github-actions#step-isolation-and-limits +# Job summaries are isolated between steps and each step is restricted to a maximum size of 1MiB. +# [ ] can not show all error findings here +# [x] split files into smaller ones and create additional steps + +# first call, generate all summaries +if [ ! -f out-1.md ]; then + # create ./zts-report.py for generate() + TEMPLATE="tests/test-runner/bin/zts-report.py.in" + cat $TEMPLATE| sed -e 's|@PYTHON_SHEBANG@|python3|' > ./zts-report.py + chmod +x ./zts-report.py + + logfile="0" + summarize + send2github out-1.md +else + send2github out-$1.md +fi + +exit 0 diff --git a/.github/workflows/zfs-qemu.yml b/.github/workflows/zfs-qemu.yml new file mode 100644 index 000000000000..f4b5963fea3b --- /dev/null +++ b/.github/workflows/zfs-qemu.yml @@ -0,0 +1,160 @@ +name: zfs-qemu + +on: + push: + pull_request: + +jobs: + + qemu-vm: + name: QEMU + strategy: + fail-fast: false + matrix: + # os: [almalinux8, almalinux9, archlinux, centos-stream9, fedora39, fedora40, debian11, debian12, freebsd13, freebsd14, freebsd15, ubuntu20, ubuntu22, ubuntu24] + os: [almalinux8, almalinux9, centos-stream9, fedora39, fedora40, freebsd13, freebsd14, freebsd15] + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Setup QEMU + timeout-minutes: 10 + run: .github/workflows/scripts/qemu-1-setup.sh + + - name: Start build machine + timeout-minutes: 10 + run: .github/workflows/scripts/qemu-2-start.sh ${{ matrix.os }} + + - name: Install dependencies + timeout-minutes: 20 + run: | + echo "Install dependencies in QEMU machine" + echo "ConnectTimeout 1" >> $HOME/.ssh/config + echo "StrictHostKeyChecking no" >> $HOME/.ssh/config + while pidof /usr/bin/qemu-system-x86_64 >/dev/null; do + ssh 2>/dev/null zfs@192.168.122.10 "uname -a" && break + done + scp .github/workflows/scripts/qemu-3-deps.sh zfs@192.168.122.10:qemu-3-deps.sh + PID=`pidof /usr/bin/qemu-system-x86_64` + ssh zfs@192.168.122.10 '$HOME/qemu-3-deps.sh' ${{ matrix.os }} + # wait for poweroff to succeed + tail --pid=$PID -f /dev/null + sleep 5 # avoid this: "error: Domain is already active" + + - name: Build modules + timeout-minutes: 30 + run: | + echo "Build modules in QEMU machine" + sudo virsh start openzfs + rm -f $HOME/.ssh/known_hosts + while pidof /usr/bin/qemu-system-x86_64 >/dev/null; do + ssh 2>/dev/null zfs@192.168.122.10 "uname -a" && break + done + rsync -ar $HOME/work/zfs/zfs zfs@192.168.122.10:./ + ssh zfs@192.168.122.10 '$HOME/zfs/.github/workflows/scripts/qemu-4-build.sh' ${{ matrix.os }} + + - name: Setup testing machines + timeout-minutes: 5 + run: | + .github/workflows/scripts/qemu-5-setup.sh + # Save the VM's serial output (ttyS0) to /var/tmp/console.txt + # - ttyS0 on the VM corresponds to a local /dev/pty/N entry + # - use 'virsh ttyconsole' to lookup the /dev/pty/N entry + RESPATH="/var/tmp/test_results" + mkdir -p $RESPATH/vm{1,2,3} + sudo mkdir -p /var/tmp + read "pty1" <<< $(sudo virsh ttyconsole "vm1") + read "pty2" <<< $(sudo virsh ttyconsole "vm2") + read "pty3" <<< $(sudo virsh ttyconsole "vm3") + sudo nohup bash -c "cat "$pty1" > $RESPATH/vm1/console.txt" & + sudo nohup bash -c "cat "$pty2" > $RESPATH/vm2/console.txt" & + sudo nohup bash -c "cat "$pty3" > $RESPATH/vm3/console.txt" & + echo "Console logging for $pty1, $pty2 and $pty3 started." + + - name: Run tests + timeout-minutes: 210 + run: | + .github/workflows/scripts/qemu-6-tests.sh + + - name: Test reports + timeout-minutes: 10 + run: | + .github/workflows/scripts/qemu-7-reports.sh + + - name: Prepare artifacts + timeout-minutes: 10 + if: success() || failure() + run: | + RESPATH="/var/tmp/test_results" + rsync -arL zfs@192.168.122.11:$RESPATH/current $RESPATH/vm1 || true + rsync -arL zfs@192.168.122.12:$RESPATH/current $RESPATH/vm2 || true + rsync -arL zfs@192.168.122.13:$RESPATH/current $RESPATH/vm3 || true + scp zfs@192.168.122.11:"/var/tmp/*.txt" $RESPATH/vm1 || true + scp zfs@192.168.122.12:"/var/tmp/*.txt" $RESPATH/vm2 || true + scp zfs@192.168.122.13:"/var/tmp/*.txt" $RESPATH/vm3 || true + cp -f /var/tmp/*.txt $RESPATH || true + ls -la /var/tmp + tar cf qemu-${{ matrix.os }}.tar -C $RESPATH -h . || true + + - uses: actions/upload-artifact@v4 + if: success() || failure() + with: + name: Logs-functional-${{ matrix.os }} + path: qemu-${{ matrix.os }}.tar + if-no-files-found: ignore + + cleanup: + if: always() + name: Cleanup + runs-on: ubuntu-latest + needs: [ qemu-vm ] + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - uses: actions/download-artifact@v4 + - name: Generating summary + run: .github/workflows/scripts/qemu-8-summary.sh + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 2 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 3 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 4 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 5 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 6 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 7 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 8 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 9 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 10 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 11 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 12 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 13 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 14 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 15 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 16 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 17 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 18 + - name: Generating summary... + run: .github/workflows/scripts/qemu-8-summary.sh 19 + - uses: actions/upload-artifact@v4 + with: + name: Summary Files + path: out-*