Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,30 @@ jobs:
build-mode: ${{ matrix.build-mode }}
binary-path: ${{ matrix.binary }}

- name: Build Linux pipette
if: matrix.build-mode == 'musl'
shell: bash
run: |
set -euo pipefail
rustup target add "${{ matrix.target }}"
cargo build \
--manifest-path openvmm/Cargo.toml \
--release \
--target "${{ matrix.target }}" \
--package pipette \
--bin pipette

- name: Upload Linux pipette
if: matrix.build-mode == 'musl'
uses: actions/upload-artifact@v7
with:
name: openvmm-linux-pipette
path: openvmm/target/${{ matrix.target }}/release/pipette
if-no-files-found: error
compression-level: 0
overwrite: true
retention-days: 1

- name: Upload OpenVMM executable
uses: actions/upload-artifact@v7
with:
Expand All @@ -250,7 +274,7 @@ jobs:

openvmm-tests:
name: OpenVMM tests / ${{ matrix.name }}
needs: [openvmm-changes]
needs: [openvmm-changes, openvmm-binaries]
Comment thread
ppenna marked this conversation as resolved.
if: >-
${{
always() &&
Expand All @@ -263,6 +287,7 @@ jobs:
github.event_name == 'push' ||
github.event.pull_request.head.repo.full_name == github.repository
) &&
needs.openvmm-binaries.result == 'success' &&
(
needs.openvmm-changes.result != 'success' ||
needs.openvmm-changes.outputs.run-tests == 'true'
Expand Down Expand Up @@ -306,6 +331,13 @@ jobs:
- name: Set up retrying curl
uses: ./.github/actions/setup-curl

- name: Download Linux pipette
if: runner.os == 'Windows'
uses: actions/download-artifact@v8
with:
name: openvmm-linux-pipette
path: ${{ runner.temp }}/openvmm-linux-pipette

- name: Prepare Windows cache tools
if: runner.os == 'Windows'
shell: powershell
Expand Down Expand Up @@ -418,6 +450,7 @@ jobs:
shell: powershell
env:
GH_TOKEN: ${{ github.token }}
OPENVMM_LINUX_PIPETTE: ${{ runner.temp }}\openvmm-linux-pipette\pipette
run: python scripts\nvx.py test-openvmm --backend "${{ matrix.backend }}"

- name: Save OpenVMM inputs on Linux
Expand Down
14 changes: 9 additions & 5 deletions doc/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ The OpenVMM restore step excludes the compatibility IGVM artifact, which NVX
does not build or package, so builds do not depend on unrelated upstream
workflow artifacts.

OpenVMM's microVM tests build their own minimal Xen PVH guest from source in
the OpenVMM checkout. They do not consume `build/vmlinux` or
`build/initramfs.cpio.gz`. NVX uses those two artifacts only for its Linux and
OpenVMM's tests build their own minimal Xen PVH guest and Linux pipette agent
from source in the OpenVMM checkout. They also restore OpenVMM's packaged
Linux-direct test kernel and initrd. They do not consume `build/vmlinux` or
`build/initramfs.cpio.gz`; NVX uses those two artifacts only for its Linux and
device correctness tests, benchmarks, and packaged runtime.

On a Linux host, build the guest directly:
Expand Down Expand Up @@ -51,8 +52,11 @@ python3 scripts/nvx.py test-openvmm --backend kvm
python3 scripts/nvx.py test-microvm --backend kvm
```

The first command needs only the OpenVMM checkout. The second needs the
standard build outputs above and writes complete per-scenario logs under
On Linux, the first command needs only the initialized OpenVMM checkout and the
Rust targets provisioned by the host setup. A local Windows/WHP run additionally
needs `OPENVMM_LINUX_PIPETTE` to identify a same-revision Linux pipette; see
[Usage](usage.md#test-openvmm). The second command needs the standard build
outputs above and writes complete per-scenario logs under
`build/test-results/microvm` by default.

The initramfs includes the sandbox PID-1 bootstrap, its container namespace
Expand Down
12 changes: 10 additions & 2 deletions doc/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ three runners labeled by operating system, backend, and `virtual-machine`.
Jobs target the shared backend labels so any available matching runner can
execute them. This allows the backend lanes to execute concurrently without
binding a workload to a specific host. `openvmm-tests` builds its Xen PVH probe
entirely from the OpenVMM checkout and exercises OpenVMM lifecycle, TTRPC, and
snapshot contracts without restoring NVX guest artifacts.
entirely from the OpenVMM checkout and exercises OpenVMM lifecycle, TTRPC,
snapshot, and the canonical `multiarch::openvmm_linux_x64_boot` Linux-direct
boot contract without restoring NVX guest artifacts. The selector uses an exact
nextest name matcher so similarly named boot variants are not included.
Linux runners build the static musl pipette locally. The Linux musl OpenVMM
binary job also uploads a same-revision pipette for WHP; Windows prepares the
remaining test bundle, adds that pipette, and runs the complete selector with
`vmm-tests-run-target`. Linux runner provisioning installs both
`x86_64-unknown-none` and `x86_64-unknown-linux-musl` because the protected
Rustup state is read-only to workflow jobs.
`nvx-microvm-tests` consumes the NVX Linux kernel and Alpine initramfs and
exercises Linux, SMP, virtio, sandbox, and snapshot behavior through the public
OpenVMM CLI. Failure logs from the NVX layer are uploaded per backend.
Expand Down
30 changes: 28 additions & 2 deletions doc/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ python3 scripts/nvx.py performance gate --help
| `build-initramfs` | Build the Alpine initramfs natively. |
| `build-openvmm` | Build the OpenVMM release binary. |
| `setup-cross-os-cache` | Install GNU tar and zstd for GitHub Actions cross-OS caches. |
| `test-openvmm` | Run self-contained OpenVMM microVM control-plane tests. |
| `test-openvmm` | Run checkout-owned OpenVMM control-plane and Linux-direct boot tests. |
| `test-microvm` | Run NVX Linux and device correctness tests through OpenVMM. |
| `build` | Build the guest artifacts and OpenVMM. |
| `download` | Download and install the latest matching GitHub release. |
Expand Down Expand Up @@ -127,7 +127,33 @@ python3 scripts/nvx.py test-openvmm --backend {kvm,mshv,whp}
```

Builds and runs OpenVMM's checkout-owned microVM tests. The test artifacts are
produced by OpenVMM itself; NVX's kernel and initramfs are not required.
produced from the pinned OpenVMM source; NVX's kernel and initramfs are not
required. The selector includes only the canonical Linux-direct boot test
`multiarch::openvmm_linux_x64_boot`, not similarly named boot variants.

Linux hosts build the required static Linux pipette guest agent locally and
therefore need the `x86_64-unknown-linux-musl` Rust target. On Windows, set
`OPENVMM_LINUX_PIPETTE` to a same-revision Linux pipette before running the WHP
test:

```powershell
$env:OPENVMM_LINUX_PIPETTE = "C:\path\to\pipette"
python scripts\nvx.py test-openvmm --backend whp
```

Build that artifact from the OpenVMM checkout on Linux:

```bash
cd openvmm
cargo xflowey restore-packages --no-compat-igvm
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl \
--package pipette --bin pipette
```

The resulting file is
`openvmm/target/x86_64-unknown-linux-musl/release/pipette`. GitHub Actions
builds, uploads, and downloads this artifact automatically for the WHP lane.

### `test-microvm`

Expand Down
43 changes: 38 additions & 5 deletions scripts/nvx_tools/ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@
ZSTD_SHA256 = "acb4e8111511749dc7a3ebedca9b04190e37a17afeb73f55d4425dbf0b90fad9"
OPENVMM_TEST_BACKENDS = ("kvm", "mshv", "whp")
OPENVMM_GUEST_RUST_TARGET = "x86_64-unknown-none"
OPENVMM_MICROVM_TEST_FILTER = (
OPENVMM_LINUX_RUST_TARGET = "x86_64-unknown-linux-musl"
OPENVMM_MICROVM_BASE_TEST_FILTER = (
"test(openvmm_microvm_test_pvh_x64_phase_1_lifecycle) + "
"test(test_ttrpc_microvm_pvh_snapshot)"
)
OPENVMM_LINUX_X64_BOOT_TEST_FILTER = "test(=multiarch::openvmm_linux_x64_boot)"
OPENVMM_MICROVM_TEST_FILTER = (
f"{OPENVMM_MICROVM_BASE_TEST_FILTER} + {OPENVMM_LINUX_X64_BOOT_TEST_FILTER}"
)


def validate_openvmm_test_backend(backend: str) -> None:
Expand Down Expand Up @@ -59,11 +64,15 @@ def run_openvmm_tests(backend: str) -> None:
cargo = require_tool("cargo")
rustup = require_tool("rustup")

run_checked([rustup, "target", "add", OPENVMM_GUEST_RUST_TARGET])
targets = [OPENVMM_GUEST_RUST_TARGET]
if os.name != "nt":
targets.append(OPENVMM_LINUX_RUST_TARGET)
run_checked([rustup, "target", "add", *targets])
Comment thread
goiri marked this conversation as resolved.
run_checked(
[cargo, "xflowey", "restore-packages", "--no-compat-igvm"],
cwd=OPENVMM_DIR,
)
test_dir = Path(os.environ.get("RUNNER_TEMP", "C:/ovm-tests")) / backend
command = [
cargo,
"xflowey",
Expand All @@ -75,12 +84,36 @@ def run_openvmm_tests(backend: str) -> None:
OPENVMM_MICROVM_TEST_FILTER,
]
if os.name == "nt":
pipette = os.environ.get("OPENVMM_LINUX_PIPETTE")
if not pipette:
raise ScriptError("OPENVMM_LINUX_PIPETTE is required for WHP OpenVMM tests")
pipette_path = require_file(Path(pipette), "OpenVMM Linux pipette")
build_command = [
*command,
"--build-only",
"--dir",
os.fspath(test_dir),
]
build_command[build_command.index("--filter") + 1] = (
OPENVMM_MICROVM_BASE_TEST_FILTER
)
Comment thread
ppenna marked this conversation as resolved.
run_checked(build_command, cwd=OPENVMM_DIR)
test_dir.mkdir(parents=True, exist_ok=True)
shutil.copy2(pipette_path, test_dir / "pipette")
command = [
cargo,
"xflowey",
"vmm-tests-run-target",
"--needs-whp",
"--ci-profile",
"--skip-vhd-prompt",
"--filter",
OPENVMM_MICROVM_TEST_FILTER,
]
command.extend(
(
"--dir",
os.fspath(
Path(os.environ.get("RUNNER_TEMP", "C:/ovm-tests")) / backend
),
os.fspath(test_dir),
)
)
run_checked(command, cwd=OPENVMM_DIR)
Expand Down
12 changes: 5 additions & 7 deletions scripts/setup/setup-linux-runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -448,13 +448,11 @@ install_rust_tools() {
RUSTUP_HOME="$trusted_rustup_home" \
RUSTUP_TOOLCHAIN="$RUST_TOOLCHAIN" \
"$rustup" target add x86_64-unknown-none
if [ "$backend" = mshv ]; then
run_as_root env \
CARGO_HOME="$trusted_cargo_home" \
RUSTUP_HOME="$trusted_rustup_home" \
RUSTUP_TOOLCHAIN="$RUST_TOOLCHAIN" \
"$rustup" target add x86_64-unknown-linux-musl
fi
run_as_root env \
CARGO_HOME="$trusted_cargo_home" \
RUSTUP_HOME="$trusted_rustup_home" \
RUSTUP_TOOLCHAIN="$RUST_TOOLCHAIN" \
"$rustup" target add x86_64-unknown-linux-musl

rust_version=$(run_as_root env \
CARGO_HOME="$trusted_cargo_home" \
Expand Down
68 changes: 59 additions & 9 deletions scripts/test_nvx_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,8 @@ def test_openvmm_tests_are_independent_of_nvx_guest_artifacts(self):
openvmm = root / "openvmm"
openvmm.mkdir()
(openvmm / "Cargo.toml").touch()
pipette = root / "pipette"
pipette.write_bytes(b"pipette")
backend = "whp" if os.name == "nt" else "kvm"

with (
Expand All @@ -655,43 +657,91 @@ def test_openvmm_tests_are_independent_of_nvx_guest_artifacts(self):
"OPENVMM_MICROVM_PVH_INITRD": "nvx-initrd",
"PETRI_CAPABILITIES": "vpci",
"RUNNER_TEMP": os.fspath(root),
"OPENVMM_LINUX_PIPETTE": os.fspath(pipette),
},
),
):
ci.run_openvmm_tests(backend)

self.assertEqual(run_checked.call_count, 3)
install_target, restore, tests = run_checked.call_args_list
self.assertEqual(run_checked.call_count, 4 if os.name == "nt" else 3)
install_target, restore, *test_commands = run_checked.call_args_list
self.assertEqual(
install_target.args[0],
["rustup", "target", "add", "x86_64-unknown-none"],
[
"rustup",
"target",
"add",
"x86_64-unknown-none",
*([] if os.name == "nt" else ["x86_64-unknown-linux-musl"]),
],
)
self.assertEqual(
restore.args[0],
["cargo", "xflowey", "restore-packages", "--no-compat-igvm"],
)
command = tests.args[0]
self.assertEqual(command[:3], ["cargo", "xflowey", "vmm-tests-run"])
command = test_commands[-1].args[0]
self.assertEqual(
command[:3],
[
"cargo",
"xflowey",
"vmm-tests-run-target" if os.name == "nt" else "vmm-tests-run",
],
)
self.assertIn("--ci-profile", command)
self.assertIn("--skip-vhd-prompt", command)
Comment on lines +691 to +692
if os.name == "nt":
self.assertIn("--needs-whp", command)
self.assertNotIn("--needs-hyperv", command)
else:
self.assertIn("--release", command)
filter_index = command.index("--filter")
self.assertEqual(command[filter_index + 1], ci.OPENVMM_MICROVM_TEST_FILTER)
self.assertIn(
"test_ttrpc_microvm_pvh_snapshot",
self.assertEqual(
ci.OPENVMM_MICROVM_TEST_FILTER,
"test(openvmm_microvm_test_pvh_x64_phase_1_lifecycle) + "
"test(test_ttrpc_microvm_pvh_snapshot) + "
"test(=multiarch::openvmm_linux_x64_boot)",
)
self.assertEqual(tests.kwargs["cwd"], openvmm)
self.assertNotIn("env", tests.kwargs)
self.assertEqual(
ci.OPENVMM_LINUX_X64_BOOT_TEST_FILTER,
"test(=multiarch::openvmm_linux_x64_boot)",
)
self.assertEqual(test_commands[-1].kwargs["cwd"], openvmm)
self.assertNotIn("env", test_commands[-1].kwargs)
if os.name == "nt":
build = test_commands[0].args[0]
self.assertIn("--build-only", build)
self.assertIn("--release", build)
self.assertEqual(
build[build.index("--filter") + 1],
ci.OPENVMM_MICROVM_BASE_TEST_FILTER,
)
Comment thread
ppenna marked this conversation as resolved.
self.assertEqual(
command[command.index("--dir") + 1],
os.fspath(root / backend),
)
self.assertEqual((root / backend / "pipette").read_bytes(), b"pipette")

def test_openvmm_tests_reject_unknown_backend(self):
with self.assertRaisesRegex(common.ScriptError, "unsupported.*backend"):
ci.run_openvmm_tests("unknown")


class CiConfigurationTests(unittest.TestCase):
def test_openvmm_tests_require_binary_handoff(self):
workflow = (common.REPO_ROOT / ".github" / "workflows" / "ci.yml").read_text(
encoding="utf-8"
)
openvmm_tests = workflow.split(" openvmm-tests:", maxsplit=1)[1].split(
"\n nvx-microvm-tests:", maxsplit=1
)[0]

self.assertIn(
"needs.openvmm-binaries.result == 'success'",
openvmm_tests,
)

def test_flowey_downloads_use_retrying_curl(self):
action = (
common.REPO_ROOT / ".github" / "actions" / "setup-curl" / "action.yml"
Expand Down
Loading