Skip to content
This repository was archived by the owner on Apr 28, 2026. It is now read-only.
Merged
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
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,20 @@ test attributes are available. Each attribute can be run via
- `numa_hosts`
- tests that check migrations between hosts with different NUMA configurations
- `cpu_profiles`
- tests that run on hosts with different CPU profiles, including migration tests
- tests that run on hosts with different CPU profiles, including migration
tests
- need to run an a CPU compatible with CPU profile used in the respective test
- `windows`
- tests run with Windows Server 2025 as guest OS
- the OS image is quite large so you might want to have a look at
`XDG_RUNTIME_DIR` (see above)
- `windows_cpu_profiles`
- tests run with Windows Server 2025 as guest OS on with additional CPU
profiles
- the OS image is quite large so you might want to have a look at
`XDG_RUNTIME_DIR` (see above)
- need to run an a CPU compatible with the CPU profile used in the respective
test

### Obtaining debug logs

Expand Down Expand Up @@ -110,3 +122,4 @@ To directly access the Cloud Hypervisor VM, you can run

## More Documentation
- [VM networks overview](./docs/networks.md)
- [Information on Windows Server 2025 image](./docs/windows_image.md)
44 changes: 44 additions & 0 deletions docs/windows_image.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Windows Server 2025 Images Provision

* The image originates from a standard Windows Server 2025 installation
* An SSH server is running, it was activated by executing the command below
* The firewall is turned off as described in the basic setup
* As a side note, Windows does not support Post Quantum KEX algorithms.

```powershell
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic
```

* There exists a script that binds the IP address 192.186.1.2 to the MAC `52:54:00:e5:b8:01`
* This script can be found at `C:\bind-mac.ps1`
* It contains the following code:

```powershell
$targetMac = "52-54-00-E5-B8-01"

for ($i=0; $i -lt 10; $i++) {
$iface = Get-NetAdapter | Where-Object {
$_.MacAddress -eq $targetMac -and $_.Status -eq "Up"
}
if ($iface) { break }
Start-Sleep -Seconds 2
}

if ($iface) {
New-NetIPAddress -InterfaceIndex $iface.ifIndex `
-IPAddress 192.168.1.2 -PrefixLength 24 `
-DefaultGateway 192.168.1.1 -ErrorAction SilentlyContinue
}
```

* There is a service scheduled that runs the binding script. It was scheduled with the command below.
* Running the script on startup is necessary as we can guarantee this way that the interface with the correct MAC receives the desired IP
* Windows creates Interfaces in a weird way, so we cannot guarantee that the VM has the same interface as when we provisioned the image in Qemu
* Otherwise running `bind-mac.ps1` once would be enough

```powershell
schtasks /create /tn "BindIPToMAC" `
/tr "powershell -ExecutionPolicy Bypass -File C:\bind-mac.ps1" `
/sc onstart /ru SYSTEM
```
42 changes: 31 additions & 11 deletions test_helper/test_helper/test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,17 @@ def save_logs(self, test, message):
self.save_machine_log(machine, "/tmp/vm_serial.log", dst_path)


def initialControllerVMSetup(controllerVM: Machine) -> None:
def initialControllerVMSetup(
controllerVM: Machine, target_os: Literal["linux", "windows"] = "linux"
) -> None:
"""
This method configures the controllerVM initially, before the test suite
runs. It sets up e.g. the NFS share with the correct OS images.
This method configures the controllerVM initially, before the test
suite runs. It sets up e.g. the NFS share with the correct OS
images.

:param controllerVM: machine object of the controllerVM
:param target_os: If "windows", prepare the NFS with the Windows
Server image. Otherwise places the Linux images in the NFS.
:raises RuntimeError: If the machine object is not the controllerVM
"""
if controllerVM.name != "controllerVM":
Expand All @@ -113,14 +118,18 @@ def initialControllerVMSetup(controllerVM: Machine) -> None:
)

controllerVM.wait_for_unit("multi-user.target")
controllerVM.succeed("cp /etc/nixos.img /nfs-root/")
controllerVM.succeed("chmod 0666 /nfs-root/nixos.img")
controllerVM.succeed("cp /etc/cirros.img /nfs-root/")
controllerVM.succeed("chmod 0666 /nfs-root/cirros.img")
controllerVM.succeed("cp /etc/ubuntu.raw /nfs-root/")
controllerVM.succeed("chmod 0666 /nfs-root/ubuntu.raw")
controllerVM.succeed("cp /etc/ubuntu-seed.iso /nfs-root/")
controllerVM.succeed("chmod 0666 /nfs-root/ubuntu-seed.iso")
if target_os == "windows":
controllerVM.succeed("cp /etc/windows-server.img /nfs-root/windows-server.img")
controllerVM.succeed("chmod 0666 /nfs-root/windows-server.img")
else:
controllerVM.succeed("cp /etc/nixos.img /nfs-root/")
controllerVM.succeed("chmod 0666 /nfs-root/nixos.img")
controllerVM.succeed("cp /etc/cirros.img /nfs-root/")
controllerVM.succeed("chmod 0666 /nfs-root/cirros.img")
controllerVM.succeed("cp /etc/ubuntu.raw /nfs-root/")
controllerVM.succeed("chmod 0666 /nfs-root/ubuntu.raw")
controllerVM.succeed("cp /etc/ubuntu-seed.iso /nfs-root/")
controllerVM.succeed("chmod 0666 /nfs-root/ubuntu-seed.iso")
Comment thread
amphi marked this conversation as resolved.

controllerVM.succeed("mkdir -p /var/lib/libvirt/storage-pools/nfs-share")

Expand Down Expand Up @@ -332,6 +341,17 @@ def teardownTestControllerVM(controllerVM: Machine, test: unittest.TestCase) ->
"rsync -aL --no-perms --inplace --checksum /etc/nixos.img /nfs-root/nixos.img"
)

# Currently, we don't store any data about if we copied the windows image to
# the NFS. We therefore have to check if it's there before resetting it
# `test -e` returns 0 if a file exists and 1 otherwise.
test_return_code, _ = controllerVM.execute("test -e /nfs-root/windows-server.img")
if test_return_code == 0:
controllerVM.succeed(
"rsync -aL --no-perms --inplace --checksum /etc/windows-server.img /nfs-root/windows-server.img"
)

# Check the sanitizer last, as the assertion can fail. Otherwise, we might
# skip some clean up.
test.assertNotEqual(
statusController, 0, msg=f"Sanitizer detected an issue: {outController}"
)
Expand Down
51 changes: 51 additions & 0 deletions tests/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ let
inherit nixos-image;
inherit (pkgs) writeText;
};

windowsLibvirtDomainCfg = import ./windows-domain-xml.nix {
inherit pkgs;
};

createTestSuite =
{
testScriptFile,
Expand Down Expand Up @@ -157,6 +162,52 @@ let
inherit enablePortForwarding;
testScriptFile = ./testsuite_cpu_profiles_host.py;
};

windows = createTestSuite {
inherit enablePortForwarding;
testScriptFile = ./testsuite_windows_default.py;
extraControllerConfig = [
windowsLibvirtDomainCfg
];
extraComputeConfig = [
windowsLibvirtDomainCfg
];
};

# The test requires a host with a recent Intel processor. The test is not
# enabled in our generic CI because of these hardware restrictions.
windows_cpu_profiles = createTestSuite {
inherit enablePortForwarding;
testScriptFile = ./testsuite_windows_cpu_profiles.py;
extraControllerConfig = [
windowsLibvirtDomainCfg
(
{ ... }:
{
virtualisation.qemu.options =

[
"-cpu"
"host,+vmx"
];
}
)
];
extraComputeConfig = [
windowsLibvirtDomainCfg
(
{ ... }:
{
virtualisation.qemu.options =

[
"-cpu"
"host,+vmx"
];
}
)
];
};
Comment thread
hertrste marked this conversation as resolved.
};

# Convenience attribute containing all nixos test driver attributes mainly
Expand Down
2 changes: 1 addition & 1 deletion tests/libvirt-test.nix
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pkgs.testers.nixosTest {
cores = 4;
memorySize = 4096;
interfaces.eth1.vlan = 1;
diskSize = 8192;
diskSize = 28672;
Comment thread
phip1611 marked this conversation as resolved.
forwardPorts =
# Port forwarding prevents us from executing the nixos tests in
# parallel in the CI, as they run in the same context and ports are
Expand Down
73 changes: 73 additions & 0 deletions tests/testsuite_windows_cpu_profiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import unittest

# Following import statement allows for proper python IDE support and proper
# nix build support. The duplicate listing of imported functions is a bit
# unfortunate, but it seems to be the best compromise. This way the python IDE
# support works out of the box in VSCode and IntelliJ without requiring
# additional IDE configuration.
try:
from ..test_helper.test_helper import ( # type: ignore
LibvirtTestsBase,
initialComputeVMSetup,
initialControllerVMSetup,
wait_for_ssh,
)
except Exception:
from test_helper import (
LibvirtTestsBase,
initialComputeVMSetup,
initialControllerVMSetup,
wait_for_ssh,
)

# pyright: reportPossiblyUnboundVariable=false

# Following is required to allow proper linting of the python code in IDEs.
# Because certain functions like start_all() and certain objects like computeVM
# or other machines are added by Nix, we need to provide certain stub objects
# in order to allow the IDE to lint the python code successfully.
if "start_all" not in globals():
from ..test_helper.test_helper.nixos_test_stubs import ( # type: ignore
computeVM,
controllerVM,
start_all,
)


class LibvirtTests(LibvirtTestsBase): # type: ignore
def __init__(self, methodName):
super().__init__(methodName, controllerVM, computeVM)

@classmethod
def setUpClass(cls):
start_all()
initialControllerVMSetup(controllerVM, target_os="windows")
initialComputeVMSetup(computeVM)

def test_windows_boot(self):
"""
Test that we do not introduce a regression with respect to booting windows.
"""

controllerVM.succeed(
"virsh define /etc/domain-windows-server-sapphire-rapids.xml"
)
controllerVM.succeed("virsh start testvm")
wait_for_ssh(controllerVM, user="administrator", password="FOO99bar!!")


def suite():
# Test cases involving live migration sorted in alphabetical order.
testcases = [
LibvirtTests.test_windows_boot,
]

suite = unittest.TestSuite()
for testcaseMethod in testcases:
suite.addTest(LibvirtTests(testcaseMethod.__name__))
return suite


runner = unittest.TextTestRunner()
if not runner.run(suite()).wasSuccessful():
raise Exception("Test Run unsuccessful")
71 changes: 71 additions & 0 deletions tests/testsuite_windows_default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import unittest

# Following import statement allows for proper python IDE support and proper
# nix build support. The duplicate listing of imported functions is a bit
# unfortunate, but it seems to be the best compromise. This way the python IDE
# support works out of the box in VSCode and IntelliJ without requiring
# additional IDE configuration.
try:
from ..test_helper.test_helper import ( # type: ignore
LibvirtTestsBase,
initialComputeVMSetup,
initialControllerVMSetup,
wait_for_ssh,
)
except Exception:
from test_helper import (
LibvirtTestsBase,
initialComputeVMSetup,
initialControllerVMSetup,
wait_for_ssh,
)

# pyright: reportPossiblyUnboundVariable=false

# Following is required to allow proper linting of the python code in IDEs.
# Because certain functions like start_all() and certain objects like computeVM
# or other machines are added by Nix, we need to provide certain stub objects
# in order to allow the IDE to lint the python code successfully.
if "start_all" not in globals():
from ..test_helper.test_helper.nixos_test_stubs import ( # type: ignore
computeVM,
controllerVM,
start_all,
)


class LibvirtTests(LibvirtTestsBase): # type: ignore
def __init__(self, methodName):
super().__init__(methodName, controllerVM, computeVM)

@classmethod
def setUpClass(cls):
start_all()
initialControllerVMSetup(controllerVM, target_os="windows")
initialComputeVMSetup(computeVM)

def test_windows_boot(self):
"""
Test that we do not introduce a regression with respect to booting windows.
"""

controllerVM.succeed("virsh define /etc/domain-windows-server.xml")
controllerVM.succeed("virsh start testvm")
wait_for_ssh(controllerVM, user="administrator", password="FOO99bar!!")


def suite():
# Test cases sorted in alphabetical order.
testcases = [
LibvirtTests.test_windows_boot,
]

suite = unittest.TestSuite()
for testcaseMethod in testcases:
suite.addTest(LibvirtTests(testcaseMethod.__name__))
return suite


runner = unittest.TextTestRunner()
if not runner.run(suite()).wasSuccessful():
raise Exception("Test Run unsuccessful")
Loading