diff --git a/Jenkinsfile b/Jenkinsfile index d6fe09cf..be0a0d5d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -62,7 +62,7 @@ pipeline { } } } - stage ('AArch64 Unit Tests') { + stage ('AArch64 Tests') { agent { node { label 'focal-arm64' } } stages { stage ('Checkout') { @@ -75,6 +75,14 @@ pipeline { sh "scripts/dev_cli.sh tests --unit" } } + stage('Run integration tests') { + options { + timeout(time: 1, unit: 'HOURS') + } + steps { + sh "scripts/dev_cli.sh tests --integration" + } + } } } } diff --git a/scripts/dev_cli.sh b/scripts/dev_cli.sh index 4c3c46c2..f8b18ed7 100755 --- a/scripts/dev_cli.sh +++ b/scripts/dev_cli.sh @@ -317,7 +317,7 @@ cmd_tests() { shift arg_vols="$1" ;; - "--all") { cargo=true; unit=true; [ "$arch" = "x86_64" ] && integration=true; } ;; + "--all") { cargo=true; unit=true; integration=true; } ;; "--") { shift; break; } ;; *) die "Unknown tests argument: $1. Please use --help for help." @@ -326,10 +326,7 @@ cmd_tests() { shift done - if [ "$(uname -m)" = "aarch64" ] ; then - if [ "$integration" = true ] ; then - die "Integration test is not supported for aarch64." - fi + if [ "$arch" = "aarch64" ] ; then if [ "$integration_coreboot" = true ] ; then die "coreboot integration test is not supported for aarch64." fi diff --git a/scripts/fetch_images.sh b/scripts/fetch_images.sh index b68f6b25..3fd0c64d 100755 --- a/scripts/fetch_images.sh +++ b/scripts/fetch_images.sh @@ -3,8 +3,14 @@ set -x fetch_ch() { CH_PATH="$1" + CH_ARCH="$2" CH_VERSION="v32.0" - CH_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$CH_VERSION/cloud-hypervisor" + CH_URL_BASE="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$CH_VERSION" + + [ "$CH_ARCH" = "aarch64" ] && CH_NAME="cloud-hypervisor-static-aarch64" + [ "$CH_ARCH" = "x86_64" ] && CH_NAME="cloud-hypervisor" + CH_URL="$CH_URL_BASE/$CH_NAME" + if [ ! -f "$CH_PATH" ]; then wget --quiet $CH_URL -O $CH_PATH chmod +x $CH_PATH @@ -30,28 +36,40 @@ convert_image() { fi } -fetch_disk_images() { - WORKLOADS_DIR="$1" - pushd "$WORKLOADS_DIR" +fetch_raw_ubuntu_image() { + OS_NAME="$1" + OS_ARCH="$2" + OS_IMAGE_NAME="$OS_NAME-server-cloudimg-$OS_ARCH.img" + OS_RAW_IMAGE_NAME="$OS_NAME-server-cloudimg-$OS_ARCH-raw.img" + OS_IMAGE_BASE="https://cloud-images.ubuntu.com" + OS_IMAGE_URL="$OS_IMAGE_BASE/$OS_NAME/current/$OS_IMAGE_NAME" + fetch_image "$OS_IMAGE_NAME" "$OS_IMAGE_URL" + convert_image "$OS_IMAGE_NAME" "$OS_RAW_IMAGE_NAME" +} + +aarch64_fetch_disk_images() { + fetch_raw_ubuntu_image "focal" "arm64" + fetch_raw_ubuntu_image "jammy" "arm64" +} +x86_64_fetch_disk_images() { CLEAR_OS_IMAGE_NAME="clear-31311-cloudguest.img" CLEAR_OS_URL_BASE="https://cloud-hypervisor.azureedge.net/" CLEAR_OS_IMAGE_URL="$CLEAR_OS_URL_BASE/$CLEAR_OS_IMAGE_NAME" fetch_image "$CLEAR_OS_IMAGE_NAME" "$CLEAR_OS_IMAGE_URL" - FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64.img" - FOCAL_OS_RAW_IMAGE_NAME="focal-server-cloudimg-amd64-raw.img" - FOCAL_OS_IMAGE_BASE="https://cloud-images.ubuntu.com/focal/current" - FOCAL_OS_IMAGE_URL="$FOCAL_OS_IMAGE_BASE/$FOCAL_OS_IMAGE_NAME" - fetch_image "$FOCAL_OS_IMAGE_NAME" "$FOCAL_OS_IMAGE_URL" - convert_image "$FOCAL_OS_IMAGE_NAME" "$FOCAL_OS_RAW_IMAGE_NAME" - - JAMMY_OS_IMAGE_NAME="jammy-server-cloudimg-amd64.img" - JAMMY_OS_RAW_IMAGE_NAME="jammy-server-cloudimg-amd64-raw.img" - JAMMY_OS_IMAGE_BASE="https://cloud-images.ubuntu.com/jammy/current" - JAMMY_OS_IMAGE_URL="$JAMMY_OS_IMAGE_BASE/$JAMMY_OS_IMAGE_NAME" - fetch_image "$JAMMY_OS_IMAGE_NAME" "$JAMMY_OS_IMAGE_URL" - convert_image "$JAMMY_OS_IMAGE_NAME" "$JAMMY_OS_RAW_IMAGE_NAME" + fetch_raw_ubuntu_image "focal" "amd64" + fetch_raw_ubuntu_image "jammy" "amd64" +} + +fetch_disk_images() { + WORKLOADS_DIR="$1" + ARCH="$2" + + pushd "$WORKLOADS_DIR" + + [ "$ARCH" = "aarch64" ] && aarch64_fetch_disk_images + [ "$ARCH" = "x86_64" ] && x86_64_fetch_disk_images popd } diff --git a/scripts/run_coreboot_integration_tests.sh b/scripts/run_coreboot_integration_tests.sh index 062b17ad..96cc6f7c 100755 --- a/scripts/run_coreboot_integration_tests.sh +++ b/scripts/run_coreboot_integration_tests.sh @@ -6,15 +6,19 @@ RHF_ROOT_DIR=$(cd "$(dirname "$0")/../" && pwd) source "${CARGO_HOME:-$HOME/.cargo}/env" source "$(dirnam "$0")/fetch_images.sh" +arch="$(uname -m)" + WORKLOADS_DIR="$HOME/workloads" mkdir -p "$WORKLOADS_DIR" -fetch_disk_images "$WORKLOADS_DIR" +fetch_disk_images "$WORKLOADS_DIR" "$arch" + +[ "$arch" = "x86_64" ] && target="x86_64-unknown-none" rustup component add rust-src -cargo build --release --target x86_64-unknown-none.json -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem --features "coreboot" +cargo build --release --target "$target.json" -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem --features "coreboot" -RHF_BIN="$RHF_ROOT_DIR/target/x86_64-unknown-none/release/hypervisor-fw" +RHF_BIN="$RHF_ROOT_DIR/target/$target/release/hypervisor-fw" COREBOOT_CONFIG_IN="$RHF_ROOT_DIR/resources/coreboot/qemu-q35-config.in" cat $COREBOOT_CONFIG_IN | sed -e "s#@CONFIG_PAYLOAD_FILE@#$RHF_BIN#g" > "$COREBOOT_DIR/.config" @@ -22,4 +26,4 @@ make -C $COREBOOT_DIR olddefconfig make -C $COREBOOT_DIR -j"$(nproc)" export RUST_BACKTRACE=1 -cargo test --features "coreboot integration_tests" "integration::tests::linux" +cargo test --features "coreboot integration_tests" "integration::tests::linux::$arch" diff --git a/scripts/run_integration_tests.sh b/scripts/run_integration_tests.sh index 225ca960..f52616d3 100755 --- a/scripts/run_integration_tests.sh +++ b/scripts/run_integration_tests.sh @@ -4,16 +4,21 @@ set -x source "${CARGO_HOME:-$HOME/.cargo}/env" source "$(dirname "$0")/fetch_images.sh" +arch="$(uname -m)" + WORKLOADS_DIR="$HOME/workloads" mkdir -p "$WORKLOADS_DIR" CH_PATH="$WORKLOADS_DIR/cloud-hypervisor" -fetch_ch "$CH_PATH" +fetch_ch "$CH_PATH" "$arch" + +fetch_disk_images "$WORKLOADS_DIR" "$arch" -fetch_disk_images "$WORKLOADS_DIR" +[ "$arch" = "aarch64" ] && target="aarch64-unknown-none" +[ "$arch" = "x86_64" ] && target="x86_64-unknown-none" rustup component add rust-src -cargo build --release --target x86_64-unknown-none.json -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem +cargo build --release --target "$target.json" -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem export RUST_BACKTRACE=1 -time cargo test --features "integration_tests" "integration::tests::linux" +time cargo test --features "integration_tests" "integration::tests::linux::$arch" diff --git a/scripts/run_integration_tests_windows.sh b/scripts/run_integration_tests_windows.sh index f651bf4b..b389fbc0 100755 --- a/scripts/run_integration_tests_windows.sh +++ b/scripts/run_integration_tests_windows.sh @@ -4,6 +4,8 @@ set -x source "${CARGO_HOME:-$HOME/.cargo}/env" source "$(dirname "$0")/fetch_images.sh" +arch="$(uname -m)" + WORKLOADS_DIR="$HOME/workloads" mkdir -p "$WORKLOADS_DIR" @@ -16,7 +18,7 @@ if [ ! -f "$WIN_IMAGE_FILE" ]; then fi CH_PATH="$WORKLOADS_DIR/cloud-hypervisor" -fetch_ch "$CH_PATH" +fetch_ch "$CH_PATH" "$arch" # Use device mapper to create a snapshot of the Windows image img_blk_size=$(du -b -B 512 ${WIN_IMAGE_FILE} | awk '{print $1;}') @@ -26,11 +28,13 @@ dmsetup mknodes dmsetup create windows-snapshot-base --table "0 $img_blk_size snapshot-origin /dev/mapper/windows-base" dmsetup mknodes +[ "$arch" = "x86_64" ] && target="x86_64-unknown-none" + rustup component add rust-src -cargo build --release --target x86_64-unknown-none.json -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem +cargo build --release --target "$target.json" -Zbuild-std=core,alloc -Zbuild-std-features=compiler-builtins-mem export RUST_BACKTRACE=1 -time cargo test --features "integration_tests" "integration::tests::windows" +time cargo test --features "integration_tests" "integration::tests::windows::$arch" RES=$? dmsetup remove_all -f diff --git a/src/integration.rs b/src/integration.rs index e282994d..be985775 100644 --- a/src/integration.rs +++ b/src/integration.rs @@ -465,6 +465,16 @@ mod tests { path: &'a str, } + #[cfg(target_arch = "aarch64")] + const TARGET_TRIPLE: &str = "aarch64-unknown-none"; + #[cfg(target_arch = "x86_64")] + const TARGET_TRIPLE: &str = "x86_64-unknown-none"; + + #[cfg(target_arch = "aarch64")] + const QEMU_NAME: &str = "qemu-system-aarch64"; + #[cfg(target_arch = "x86_64")] + const QEMU_NAME: &str = "qemu-system-x86_64"; + mod linux { use crate::integration::tests::*; @@ -480,7 +490,7 @@ mod tests { "--serial", "tty", "--kernel", - "target/x86_64-unknown-none/release/hypervisor-fw", + &format!("target/{TARGET_TRIPLE}/release/hypervisor-fw"), "--disk", &format!("path={os}"), "--disk", @@ -506,7 +516,7 @@ mod tests { ci: &str, net: &GuestNetworkConfig, ) -> Child { - let mut c = Command::new("qemu-system-x86_64"); + let mut c = Command::new(QEMU_NAME); c.args([ "-machine", "q35,accel=kvm", @@ -550,9 +560,10 @@ mod tests { #[cfg(not(feature = "coreboot"))] fn spawn_qemu(tmp_dir: &TempDir, os: &str, ci: &str, net: &GuestNetworkConfig) -> Child { + let path = format!("target/{TARGET_TRIPLE}/release/hypervisor-fw"); let fw = Firmware { fw_type: "-kernel", - path: "target/x86_64-unknown-none/release/hypervisor-fw", + path: path.as_str(), }; spawn_qemu_common(tmp_dir, &fw, os, ci, net) } @@ -598,41 +609,64 @@ mod tests { handle_child_output(&tmp_dir, r, &output); } - const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-raw.img"; - const JAMMY_IMAGE_NAME: &str = "jammy-server-cloudimg-amd64-raw.img"; - const CLEAR_IMAGE_NAME: &str = "clear-31311-cloudguest.img"; + mod aarch64 { + use super::*; - #[test] - fn test_boot_qemu_focal() { - test_boot(FOCAL_IMAGE_NAME, &UbuntuCloudInit {}, spawn_qemu) - } + const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-arm64-raw.img"; + const JAMMY_IMAGE_NAME: &str = "jammy-server-cloudimg-arm64-raw.img"; - #[test] - fn test_boot_qemu_jammy() { - test_boot(JAMMY_IMAGE_NAME, &UbuntuCloudInit {}, spawn_qemu) - } + #[test] + #[cfg(not(feature = "coreboot"))] + fn test_boot_ch_focal() { + test_boot(FOCAL_IMAGE_NAME, &UbuntuCloudInit {}, spawn_ch) + } - #[test] - fn test_boot_qemu_clear() { - test_boot(CLEAR_IMAGE_NAME, &ClearCloudInit {}, spawn_qemu) + #[test] + #[cfg(not(feature = "coreboot"))] + fn test_boot_ch_jammy() { + test_boot(JAMMY_IMAGE_NAME, &UbuntuCloudInit {}, spawn_ch) + } } - #[test] - #[cfg(not(feature = "coreboot"))] - fn test_boot_ch_focal() { - test_boot(FOCAL_IMAGE_NAME, &UbuntuCloudInit {}, spawn_ch) - } + mod x86_64 { + use super::*; - #[test] - #[cfg(not(feature = "coreboot"))] - fn test_boot_ch_jammy() { - test_boot(JAMMY_IMAGE_NAME, &UbuntuCloudInit {}, spawn_ch) - } + const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-raw.img"; + const JAMMY_IMAGE_NAME: &str = "jammy-server-cloudimg-amd64-raw.img"; + const CLEAR_IMAGE_NAME: &str = "clear-31311-cloudguest.img"; - #[test] - #[cfg(not(feature = "coreboot"))] - fn test_boot_ch_clear() { - test_boot(CLEAR_IMAGE_NAME, &ClearCloudInit {}, spawn_ch) + #[test] + fn test_boot_qemu_focal() { + test_boot(FOCAL_IMAGE_NAME, &UbuntuCloudInit {}, spawn_qemu) + } + + #[test] + fn test_boot_qemu_jammy() { + test_boot(JAMMY_IMAGE_NAME, &UbuntuCloudInit {}, spawn_qemu) + } + + #[test] + fn test_boot_qemu_clear() { + test_boot(CLEAR_IMAGE_NAME, &ClearCloudInit {}, spawn_qemu) + } + + #[test] + #[cfg(not(feature = "coreboot"))] + fn test_boot_ch_focal() { + test_boot(FOCAL_IMAGE_NAME, &UbuntuCloudInit {}, spawn_ch) + } + + #[test] + #[cfg(not(feature = "coreboot"))] + fn test_boot_ch_jammy() { + test_boot(JAMMY_IMAGE_NAME, &UbuntuCloudInit {}, spawn_ch) + } + + #[test] + #[cfg(not(feature = "coreboot"))] + fn test_boot_ch_clear() { + test_boot(CLEAR_IMAGE_NAME, &ClearCloudInit {}, spawn_ch) + } } } @@ -656,7 +690,7 @@ mod tests { prepare_tap(&net); - let mut c = Command::new("qemu-system-x86_64"); + let mut c = Command::new(QEMU_NAME); c.args([ "-machine", "q35,accel=kvm", @@ -709,78 +743,84 @@ mod tests { handle_child_output(&tmp_dir, r, &output); } - #[test] - #[ignore] // Windows guest test on QEMU is not supported yet. - #[cfg(not(feature = "coreboot"))] - fn test_boot_qemu_windows() { - let fw = Firmware { - fw_type: "-kernel", - path: "target/x86_64-unknown-none/release/hypervisor-fw", - }; - test_boot_qemu_windows_common(&fw); - } - - #[test] - #[ignore] // Windows guest test on QEMU is not supported yet. - #[cfg(feature = "coreboot")] - fn test_boot_qemu_windows() { - let fw = Firmware { - fw_type: "-bios", - path: "resources/coreboot/coreboot/build/coreboot.rom", - }; - test_boot_qemu_windows_common(&fw); - } - - #[test] - #[cfg(not(feature = "coreboot"))] - fn test_boot_ch_windows() { - let mut disk = WindowsDiskConfig::new(WINDOWS_IMAGE_NAME.to_string()); - let tmp_dir = TempDir::new().expect("Expect creating temporary directory to succeed"); - prepare_windows_os_disk(&mut disk, &tmp_dir); - - let clh_path = dirs::home_dir() - .unwrap() - .join("workloads") - .join("cloud-hypervisor"); - let mut c = Command::new(clh_path.to_str().unwrap()); - c.args([ - "--cpus", - "boot=2,kvm_hyperv=on", - "--memory", - "size=4G", - "--console", - "off", - "--serial", - "tty", - "--kernel", - "target/x86_64-unknown-none/release/hypervisor-fw", - "--disk", - &format!("path={}", disk.osdisk_path), - "--net", - "tap=", - ]); - - let stdout = fs::File::create(tmp_dir.path().join("stdout")).unwrap(); - let stderr = fs::File::create(tmp_dir.path().join("stderr")).unwrap(); + mod x86_64 { + use super::*; + + #[test] + #[ignore] // Windows guest test on QEMU is not supported yet. + #[cfg(not(feature = "coreboot"))] + fn test_boot_qemu_windows() { + let path = format!("target/{TARGET_TRIPLE}/release/hypervisor-fw"); + let fw = Firmware { + fw_type: "-kernel", + path: path.as_str(), + }; + test_boot_qemu_windows_common(&fw); + } - eprintln!("Spawning: {:?}", c); - let mut child = c - .stdout(Stdio::from(stdout)) - .stderr(Stdio::from(stderr)) - .spawn() - .expect("Expect launching Cloud Hypervisor to succeed"); + #[test] + #[ignore] // Windows guest test on QEMU is not supported yet. + #[cfg(feature = "coreboot")] + fn test_boot_qemu_windows() { + let fw = Firmware { + fw_type: "-bios", + path: "resources/coreboot/coreboot/build/coreboot.rom", + }; + test_boot_qemu_windows_common(&fw); + } - thread::sleep(std::time::Duration::from_secs(60)); - let r = std::panic::catch_unwind(|| { - let auth = windows_auth(); - ssh_command_with_auth("192.168.249.2", "shutdown /s", &auth) - .expect("Expect SSH command to work"); - }); + #[test] + #[cfg(not(feature = "coreboot"))] + fn test_boot_ch_windows() { + let mut disk = WindowsDiskConfig::new(WINDOWS_IMAGE_NAME.to_string()); + let tmp_dir = + TempDir::new().expect("Expect creating temporary directory to succeed"); + prepare_windows_os_disk(&mut disk, &tmp_dir); + + let clh_path = dirs::home_dir() + .unwrap() + .join("workloads") + .join("cloud-hypervisor"); + let mut c = Command::new(clh_path.to_str().unwrap()); + c.args([ + "--cpus", + "boot=2,kvm_hyperv=on", + "--memory", + "size=4G", + "--console", + "off", + "--serial", + "tty", + "--kernel", + &format!("target/{TARGET_TRIPLE}/release/hypervisor-fw"), + "--disk", + &format!("path={}", disk.osdisk_path), + "--net", + "tap=", + ]); + + let stdout = fs::File::create(tmp_dir.path().join("stdout")).unwrap(); + let stderr = fs::File::create(tmp_dir.path().join("stderr")).unwrap(); + + eprintln!("Spawning: {:?}", c); + let mut child = c + .stdout(Stdio::from(stdout)) + .stderr(Stdio::from(stderr)) + .spawn() + .expect("Expect launching Cloud Hypervisor to succeed"); + + thread::sleep(std::time::Duration::from_secs(60)); + let r = std::panic::catch_unwind(|| { + let auth = windows_auth(); + ssh_command_with_auth("192.168.249.2", "shutdown /s", &auth) + .expect("Expect SSH command to work"); + }); - child.kill().unwrap(); - let output = child.wait_with_output().unwrap(); + child.kill().unwrap(); + let output = child.wait_with_output().unwrap(); - handle_child_output(&tmp_dir, r, &output); + handle_child_output(&tmp_dir, r, &output); + } } } }