ci: Initial Windows image integration

This enables the Windows test module. One basic test is enabled,
while all others are disabled yet for aarch64. Jenkins file is
extended with the corresponding step for aarch64.

installAzureCli() is parametrized.

It seems that transferring a 30GB image would take >= 15 minutes. An
optimization here is having a gzip'ed image to 10GB which would unpack
in 3 minutes. Expect to be quicker than transferring an uncompressed
image while on another network.

Signed-off-by: Anatol Belski <ab@php.net>
This commit is contained in:
Anatol Belski 2022-08-03 23:52:53 +02:00 committed by Rob Bradford
parent c591a96ecb
commit 944d09208e
8 changed files with 166 additions and 59 deletions

45
Jenkinsfile vendored
View File

@ -103,6 +103,9 @@ pipeline{
return runWorkers return runWorkers
} }
} }
environment {
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
}
stages { stages {
stage ('Checkout') { stage ('Checkout') {
steps { steps {
@ -123,6 +126,42 @@ pipeline{
sh "scripts/dev_cli.sh tests --integration --libc musl" sh "scripts/dev_cli.sh tests --integration --libc musl"
} }
} }
stage ('Install azure-cli') {
steps {
installAzureCli("bionic", "arm64")
}
}
stage ('Download Windows image') {
steps {
sh '''#!/bin/bash -x
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
IMG_PATH=$HOME/workloads/$IMG_BASENAME
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
pushd "$HOME/workloads"
if sha1sum "$IMG_BASENAME.sha1" --check; then
exit
fi
popd
mkdir -p "$HOME/workloads"
az storage blob download \
--container-name private-images \
--file "$IMG_GZ_PATH" \
--name "$IMG_GZ_BLOB_NAME" \
--connection-string "$AZURE_CONNECTION_STRING"
gzip -d $IMG_GZ_PATH
'''
}
}
stage ('Run Windows guest integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
}
}
} }
post { post {
always { always {
@ -267,7 +306,7 @@ pipeline{
} }
stage ('Install azure-cli') { stage ('Install azure-cli') {
steps { steps {
installAzureCli() installAzureCli("jammy", "amd64")
} }
} }
stage ('Download assets') { stage ('Download assets') {
@ -397,10 +436,10 @@ def cancelPreviousBuilds() {
} }
} }
def installAzureCli() { def installAzureCli(distro, arch) {
sh "sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg" sh "sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg"
sh "curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null" sh "curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null"
sh "echo \"deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ jammy main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list" sh "echo \"deb [arch=${arch}] https://packages.microsoft.com/repos/azure-cli/ ${distro} main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
sh "sudo apt update" sh "sudo apt update"
sh "sudo apt install -y azure-cli" sh "sudo apt install -y azure-cli"
} }

49
scripts/common-aarch64.sh Normal file
View File

@ -0,0 +1,49 @@
#!/bin/bash
export BUILD_TARGET=${BUILD_TARGET-aarch64-unknown-linux-gnu}
WORKLOADS_DIR="$HOME/workloads"
mkdir -p "$WORKLOADS_DIR"
build_edk2() {
EDK2_BUILD_DIR="$WORKLOADS_DIR/edk2_build"
EDK2_REPO="https://github.com/tianocore/edk2.git"
EDK2_DIR="$EDK2_BUILD_DIR/edk2"
EDK2_PLAT_REPO="https://github.com/tianocore/edk2-platforms.git"
EDK2_PLAT_DIR="$EDK2_BUILD_DIR/edk2-platforms"
ACPICA_REPO="https://github.com/acpica/acpica.git"
ACPICA_DIR="$EDK2_BUILD_DIR/acpica"
export WORKSPACE="$EDK2_BUILD_DIR"
export PACKAGES_PATH="$EDK2_DIR:$EDK2_PLAT_DIR"
export IASL_PREFIX="$ACPICA_DIR/generate/unix/bin/"
if [ ! -d "$EDK2_BUILD_DIR" ]; then
mkdir -p "$EDK2_BUILD_DIR"
fi
# Prepare source code
checkout_repo "$EDK2_DIR" "$EDK2_REPO" master "46b4606ba23498d3d0e66b53e498eb3d5d592586"
pushd "$EDK2_DIR"
git submodule update --init
popd
checkout_repo "$EDK2_PLAT_DIR" "$EDK2_PLAT_REPO" master "8227e9e9f6a8aefbd772b40138f835121ccb2307"
checkout_repo "$ACPICA_DIR" "$ACPICA_REPO" master "b9c69f81a05c45611c91ea9cbce8756078d76233"
if [[ ! -f "$EDK2_DIR/.built" || \
! -f "$EDK2_PLAT_DIR/.built" || \
! -f "$ACPICA_DIR/.built" ]]; then
pushd "$EDK2_BUILD_DIR"
# Build
make -C acpica -j `nproc`
source edk2/edksetup.sh
make -C edk2/BaseTools -j `nproc`
build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtCloudHv.dsc -b RELEASE -n 0
cp Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd "$WORKLOADS_DIR"
touch "$EDK2_DIR"/.built
touch "$EDK2_PLAT_DIR"/.built
touch "$ACPICA_DIR"/.built
popd
fi
}

View File

@ -471,7 +471,7 @@ cmd_tests() {
--env USER="root" \ --env USER="root" \
--env CH_LIBC="${libc}" \ --env CH_LIBC="${libc}" \
"$CTR_IMAGE" \ "$CTR_IMAGE" \
./scripts/run_integration_tests_windows.sh "$@" || fix_dir_perms $? || exit $? ./scripts/run_integration_tests_windows_"$(uname -m)".sh "$@" || fix_dir_perms $? || exit $?
fi fi
if [ "$integration_live_migration" = true ]; then if [ "$integration_live_migration" = true ]; then

View File

@ -3,55 +3,10 @@ set -x
source $HOME/.cargo/env source $HOME/.cargo/env
source $(dirname "$0")/test-util.sh source $(dirname "$0")/test-util.sh
source $(dirname "$0")/common-aarch64.sh
export BUILD_TARGET=${BUILD_TARGET-aarch64-unknown-linux-gnu}
WORKLOADS_DIR="$HOME/workloads"
WORKLOADS_LOCK="$WORKLOADS_DIR/integration_test.lock" WORKLOADS_LOCK="$WORKLOADS_DIR/integration_test.lock"
mkdir -p "$WORKLOADS_DIR"
build_edk2() {
EDK2_BUILD_DIR="$WORKLOADS_DIR/edk2_build"
EDK2_REPO="https://github.com/tianocore/edk2.git"
EDK2_DIR="$EDK2_BUILD_DIR/edk2"
EDK2_PLAT_REPO="https://github.com/tianocore/edk2-platforms.git"
EDK2_PLAT_DIR="$EDK2_BUILD_DIR/edk2-platforms"
ACPICA_REPO="https://github.com/acpica/acpica.git"
ACPICA_DIR="$EDK2_BUILD_DIR/acpica"
export WORKSPACE="$EDK2_BUILD_DIR"
export PACKAGES_PATH="$EDK2_DIR:$EDK2_PLAT_DIR"
export IASL_PREFIX="$ACPICA_DIR/generate/unix/bin/"
if [ ! -d "$EDK2_BUILD_DIR" ]; then
mkdir -p "$EDK2_BUILD_DIR"
fi
# Prepare source code
checkout_repo "$EDK2_DIR" "$EDK2_REPO" master "46b4606ba23498d3d0e66b53e498eb3d5d592586"
pushd "$EDK2_DIR"
git submodule update --init
popd
checkout_repo "$EDK2_PLAT_DIR" "$EDK2_PLAT_REPO" master "8227e9e9f6a8aefbd772b40138f835121ccb2307"
checkout_repo "$ACPICA_DIR" "$ACPICA_REPO" master "b9c69f81a05c45611c91ea9cbce8756078d76233"
if [[ ! -f "$EDK2_DIR/.built" || \
! -f "$EDK2_PLAT_DIR/.built" || \
! -f "$ACPICA_DIR/.built" ]]; then
pushd "$EDK2_BUILD_DIR"
# Build
make -C acpica -j `nproc`
source edk2/edksetup.sh
make -C edk2/BaseTools -j `nproc`
build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtCloudHv.dsc -b RELEASE -n 0
cp Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd "$WORKLOADS_DIR"
touch "$EDK2_DIR"/.built
touch "$EDK2_PLAT_DIR"/.built
touch "$ACPICA_DIR"/.built
popd
fi
}
build_spdk_nvme() { build_spdk_nvme() {
SPDK_DIR="$WORKLOADS_DIR/spdk" SPDK_DIR="$WORKLOADS_DIR/spdk"
SPDK_REPO="https://github.com/spdk/spdk.git" SPDK_REPO="https://github.com/spdk/spdk.git"

View File

@ -0,0 +1,60 @@
#!/bin/bash
set -x
source $HOME/.cargo/env
source $(dirname "$0")/test-util.sh
source $(dirname "$0")/common-aarch64.sh
process_common_args "$@"
# For now these values are default for kvm
features=""
# aarch64 not supported for MSHV
if [[ "$hypervisor" = "mshv" ]]; then
echo "AArch64 is not supported in Microsoft Hypervisor"
exit 1
fi
WIN_IMAGE_BASENAME="windows-11-iot-enterprise-aarch64.raw"
WIN_IMAGE_FILE="$WORKLOADS_DIR/$WIN_IMAGE_BASENAME"
# Checkout and build EDK2
OVMF_FW="$WORKLOADS_DIR/CLOUDHV_EFI.fd"
build_edk2
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
CFLAGS=""
TARGET_CC=""
if [[ "${BUILD_TARGET}" == "aarch64-unknown-linux-musl" ]]; then
export TARGET_CC="musl-gcc"
export RUSTFLAGS="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
fi
# Check if the images are present
if [[ ! -f ${WIN_IMAGE_FILE} || ! -f ${OVMF_FW} ]]; then
echo "Windows image/firmware not present in the host"
exit 1
fi
# Use device mapper to create a snapshot of the Windows image
img_blk_size=$(du -b -B 512 ${WIN_IMAGE_FILE} | awk '{print $1;}')
loop_device=$(losetup --find --show --read-only ${WIN_IMAGE_FILE})
dmsetup create windows-base --table "0 $img_blk_size linear $loop_device 0"
dmsetup mknodes
dmsetup create windows-snapshot-base --table "0 $img_blk_size snapshot-origin /dev/mapper/windows-base"
dmsetup mknodes
export RUST_BACKTRACE=1
cargo build --all --release $features --target $BUILD_TARGET
strip target/$BUILD_TARGET/release/cloud-hypervisor
# Only run with 1 thread to avoid tests interfering with one another because
# Windows has a static IP configured
time cargo test $features "windows::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
RES=$?
dmsetup remove_all -f
losetup -D
exit $RES

View File

@ -0,0 +1 @@
707e939654b341717d3f54b41c57eea4432978e5 windows-11-iot-enterprise-aarch64.raw

View File

@ -24,7 +24,6 @@ use std::process::{Child, Command, Stdio};
use std::string::String; use std::string::String;
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
#[cfg(target_arch = "x86_64")]
use std::sync::Mutex; use std::sync::Mutex;
use std::thread; use std::thread;
use test_infra::*; use test_infra::*;
@ -59,6 +58,8 @@ mod aarch64 {
pub const FOCAL_IMAGE_NAME_VHD: &str = "focal-server-cloudimg-arm64-custom-20210929-0.vhd"; pub const FOCAL_IMAGE_NAME_VHD: &str = "focal-server-cloudimg-arm64-custom-20210929-0.vhd";
pub const FOCAL_IMAGE_NAME_VHDX: &str = "focal-server-cloudimg-arm64-custom-20210929-0.vhdx"; pub const FOCAL_IMAGE_NAME_VHDX: &str = "focal-server-cloudimg-arm64-custom-20210929-0.vhdx";
pub const JAMMY_IMAGE_NAME: &str = "jammy-server-cloudimg-arm64-custom-20220329-0.raw"; pub const JAMMY_IMAGE_NAME: &str = "jammy-server-cloudimg-arm64-custom-20220329-0.raw";
pub const WINDOWS_IMAGE_NAME: &str = "windows-11-iot-enterprise-aarch64.raw";
pub const OVMF_NAME: &str = "CLOUDHV_EFI.fd";
pub const GREP_SERIAL_IRQ_CMD: &str = "grep -c 'GICv3.*uart-pl011' /proc/interrupts || true"; pub const GREP_SERIAL_IRQ_CMD: &str = "grep -c 'GICv3.*uart-pl011' /proc/interrupts || true";
pub const GREP_PMU_IRQ_CMD: &str = "grep -c 'GICv3.*arm-pmu' /proc/interrupts || true"; pub const GREP_PMU_IRQ_CMD: &str = "grep -c 'GICv3.*arm-pmu' /proc/interrupts || true";
} }
@ -175,12 +176,11 @@ fn direct_kernel_boot_path() -> PathBuf {
kernel_path kernel_path
} }
#[cfg(target_arch = "aarch64")]
fn edk2_path() -> PathBuf { fn edk2_path() -> PathBuf {
let mut workload_path = dirs::home_dir().unwrap(); let mut workload_path = dirs::home_dir().unwrap();
workload_path.push("workloads"); workload_path.push("workloads");
let mut edk2_path = workload_path; let mut edk2_path = workload_path;
edk2_path.push("CLOUDHV_EFI.fd"); edk2_path.push(OVMF_NAME);
edk2_path edk2_path
} }
@ -6795,7 +6795,6 @@ mod sequential {
} }
} }
#[cfg(target_arch = "x86_64")]
mod windows { mod windows {
use crate::*; use crate::*;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -7096,14 +7095,10 @@ mod windows {
fn test_windows_guest() { fn test_windows_guest() {
let windows_guest = WindowsGuest::new(); let windows_guest = WindowsGuest::new();
let mut ovmf_path = dirs::home_dir().unwrap();
ovmf_path.push("workloads");
ovmf_path.push(OVMF_NAME);
let mut child = GuestCommand::new(windows_guest.guest()) let mut child = GuestCommand::new(windows_guest.guest())
.args(&["--cpus", "boot=2,kvm_hyperv=on"]) .args(&["--cpus", "boot=2,kvm_hyperv=on"])
.args(&["--memory", "size=4G"]) .args(&["--memory", "size=4G"])
.args(&["--kernel", ovmf_path.to_str().unwrap()]) .args(&["--kernel", edk2_path().to_str().unwrap()])
.args(&["--serial", "tty"]) .args(&["--serial", "tty"])
.args(&["--console", "off"]) .args(&["--console", "off"])
.default_disks() .default_disks()
@ -7139,6 +7134,7 @@ mod windows {
} }
#[test] #[test]
#[cfg(not(target_arch = "aarch64"))]
fn test_windows_guest_multiple_queues() { fn test_windows_guest_multiple_queues() {
let windows_guest = WindowsGuest::new(); let windows_guest = WindowsGuest::new();
@ -7205,6 +7201,7 @@ mod windows {
#[test] #[test]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
#[cfg(not(target_arch = "aarch64"))]
#[ignore = "See #4327"] #[ignore = "See #4327"]
fn test_windows_guest_snapshot_restore() { fn test_windows_guest_snapshot_restore() {
let windows_guest = WindowsGuest::new(); let windows_guest = WindowsGuest::new();
@ -7294,6 +7291,7 @@ mod windows {
#[test] #[test]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
#[cfg(not(target_arch = "aarch64"))]
fn test_windows_guest_cpu_hotplug() { fn test_windows_guest_cpu_hotplug() {
let windows_guest = WindowsGuest::new(); let windows_guest = WindowsGuest::new();
@ -7368,6 +7366,7 @@ mod windows {
#[test] #[test]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
#[cfg(not(target_arch = "aarch64"))]
fn test_windows_guest_ram_hotplug() { fn test_windows_guest_ram_hotplug() {
let windows_guest = WindowsGuest::new(); let windows_guest = WindowsGuest::new();
@ -7442,6 +7441,7 @@ mod windows {
#[test] #[test]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
#[cfg(not(target_arch = "aarch64"))]
fn test_windows_guest_netdev_hotplug() { fn test_windows_guest_netdev_hotplug() {
let windows_guest = WindowsGuest::new(); let windows_guest = WindowsGuest::new();
@ -7514,6 +7514,7 @@ mod windows {
#[test] #[test]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
#[cfg(not(target_arch = "aarch64"))]
fn test_windows_guest_disk_hotplug() { fn test_windows_guest_disk_hotplug() {
let windows_guest = WindowsGuest::new(); let windows_guest = WindowsGuest::new();
@ -7608,6 +7609,7 @@ mod windows {
#[test] #[test]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
#[cfg(not(target_arch = "aarch64"))]
fn test_windows_guest_disk_hotplug_multi() { fn test_windows_guest_disk_hotplug_multi() {
let windows_guest = WindowsGuest::new(); let windows_guest = WindowsGuest::new();
@ -7737,6 +7739,7 @@ mod windows {
#[test] #[test]
#[cfg(not(feature = "mshv"))] #[cfg(not(feature = "mshv"))]
#[cfg(not(target_arch = "aarch64"))]
fn test_windows_guest_netdev_multi() { fn test_windows_guest_netdev_multi() {
let windows_guest = WindowsGuest::new(); let windows_guest = WindowsGuest::new();