Skip to content

Commit 6e8beb3

Browse files
authored
Merge pull request #403 from xcp-ng/gln/xen-guest-tools-typehints
2 parents 1358ffc + 4fb85e0 commit 6e8beb3

10 files changed

Lines changed: 127 additions & 81 deletions

tests/guest_tools/unix/test_guest_tools_unix.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
from __future__ import annotations
2+
13
import pytest
24

35
import logging
46
import time
57

68
from lib.common import PackageManagerEnum, wait_for
9+
from lib.host import Host
10+
from lib.sr import SR
711
from lib.vm import VM
812

913
# Requirements:
@@ -14,30 +18,32 @@
1418
# - A VM to import, supported by the Linux/install.sh script of the guest tools ISO
1519
# (without this flag you get an alpine, and that is not suitable)
1620

21+
1722
class State:
18-
def __init__(self):
19-
self.tools_version = None
20-
self.vm_distro = None
23+
def __init__(self) -> None:
24+
self.tools_version: str | None = None
25+
self.vm_distro: str | None = None
26+
2127

2228
@pytest.mark.multi_vms
2329
@pytest.mark.usefixtures("unix_vm")
2430
class TestGuestToolsUnix:
2531
@pytest.fixture(scope='class')
26-
def state(self):
32+
def state(self) -> State:
2733
return State()
2834

29-
def _check_tools_version(self, vm, tools_version):
35+
def _check_tools_version(self, vm: VM, tools_version: str | None) -> None:
3036
logging.info("Check that the detected tools version is '%s'" % tools_version)
3137
detected_version = vm.tools_version()
3238
assert detected_version == tools_version
3339

34-
def _check_os_info(self, vm, vm_distro):
40+
def _check_os_info(self, vm: VM, vm_distro: str | None) -> None:
3541
logging.info("Check that the detected distro is '%s'" % vm_distro)
3642
detected_distro = vm.distro()
3743
assert detected_distro == vm_distro
3844

3945
@pytest.fixture(scope="class", autouse=True)
40-
def vm_install(self, running_vm: VM, state):
46+
def vm_install(self, running_vm: VM, state: State) -> None:
4147
vm = running_vm
4248

4349
# skip test for some unixes
@@ -90,18 +96,18 @@ def vm_install(self, running_vm: VM, state):
9096
wait_for(lambda: vm.ssh_with_result('pgrep -f xe-daemon').returncode == 0,
9197
"Wait for xe-daemon running")
9298

93-
def test_check_tools(self, running_vm, state):
99+
def test_check_tools(self, running_vm: VM, state: State) -> None:
94100
vm = running_vm
95101
self._check_tools_version(vm, state.tools_version)
96102
self._check_os_info(vm, state.vm_distro)
97103

98-
def test_check_tools_after_reboot(self, running_vm, state):
104+
def test_check_tools_after_reboot(self, running_vm: VM, state: State) -> None:
99105
vm = running_vm
100106
vm.reboot(verify=True)
101107
self._check_tools_version(vm, state.tools_version)
102108
self._check_os_info(vm, state.vm_distro)
103109

104-
def test_xenstore(self, running_vm: VM):
110+
def test_xenstore(self, running_vm: VM) -> None:
105111
logging.info("Testing various xenstore commands from the guest")
106112
vm = running_vm
107113
vm.ssh('xenstore-ls')
@@ -113,14 +119,15 @@ def test_xenstore(self, running_vm: VM):
113119
vm.ssh('xenstore-rm data/test-xcp-ng')
114120
assert vm.ssh_with_result('xenstore-exists data/test-xcp-ng').returncode != 0
115121

116-
def test_clean_shutdown(self, running_vm):
122+
def test_clean_shutdown(self, running_vm: VM) -> None:
117123
vm = running_vm
118124
vm.shutdown(verify=True)
119125
# restore VM state
120126
vm.start()
121127
vm.wait_for_vm_running_and_ssh_up()
122128

123-
def test_storage_migration(self, running_vm, host, hostA2, local_sr_on_hostA2, state):
129+
def test_storage_migration(self, running_vm: VM, host: Host, hostA2: Host,
130+
local_sr_on_hostA2: SR, state: State) -> None:
124131
vm = running_vm
125132
# migrate to default SR on hostA2
126133
vm.migrate(hostA2, local_sr_on_hostA2)

tests/guest_tools/win/conftest.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import pytest
24

35
import logging
@@ -18,7 +20,7 @@
1820
from lib.windows.guest_tools import install_guest_tools
1921
from lib.windows.other_tools import install_other_drivers
2022

21-
from typing import Any, Dict, Tuple
23+
from typing import Any, Dict, Generator, Tuple
2224

2325
@pytest.fixture(scope="module")
2426
def running_windows_vm_without_tools(imported_vm: VM) -> VM:
@@ -36,7 +38,9 @@ def running_windows_vm_without_tools(imported_vm: VM) -> VM:
3638

3739

3840
@pytest.fixture(scope="module")
39-
def unsealed_windows_vm_and_snapshot(running_windows_vm_without_tools: VM):
41+
def unsealed_windows_vm_and_snapshot(
42+
running_windows_vm_without_tools: VM
43+
) -> Generator[Tuple[VM, Snapshot], None, None]:
4044
"""Unseal VM and get its IP, then shut it down. Cache the unsealed state in a snapshot to save time."""
4145
vm = running_windows_vm_without_tools
4246
vm_shutdown_without_tools(vm)
@@ -46,7 +50,7 @@ def unsealed_windows_vm_and_snapshot(running_windows_vm_without_tools: VM):
4650

4751

4852
@pytest.fixture
49-
def running_unsealed_windows_vm(unsealed_windows_vm_and_snapshot: Tuple[VM, Snapshot]):
53+
def running_unsealed_windows_vm(unsealed_windows_vm_and_snapshot: Tuple[VM, Snapshot]) -> Generator[VM, None, None]:
5054
vm, snapshot = unsealed_windows_vm_and_snapshot
5155
vm.start()
5256
wait_for_vm_running_and_ssh_up_without_tools(vm)
@@ -55,7 +59,9 @@ def running_unsealed_windows_vm(unsealed_windows_vm_and_snapshot: Tuple[VM, Snap
5559

5660

5761
@pytest.fixture(scope="class")
58-
def vm_install_test_tools_per_test_class(unsealed_windows_vm_and_snapshot, guest_tools_iso: Dict[str, Any]):
62+
def vm_install_test_tools_per_test_class(
63+
unsealed_windows_vm_and_snapshot: Tuple[VM, Snapshot], guest_tools_iso: Dict[str, Any]
64+
) -> Generator[VM, None, None]:
5965
vm, snapshot = unsealed_windows_vm_and_snapshot
6066
vm.start()
6167
wait_for_vm_running_and_ssh_up_without_tools(vm)
@@ -66,7 +72,7 @@ def vm_install_test_tools_per_test_class(unsealed_windows_vm_and_snapshot, guest
6672

6773

6874
@pytest.fixture
69-
def vm_install_test_tools_no_reboot(running_unsealed_windows_vm: VM, guest_tools_iso: Dict[str, Any]):
75+
def vm_install_test_tools_no_reboot(running_unsealed_windows_vm: VM, guest_tools_iso: Dict[str, Any]) -> VM:
7076
install_guest_tools(running_unsealed_windows_vm, guest_tools_iso, PowerAction.Nothing)
7177
return running_unsealed_windows_vm
7278

@@ -76,12 +82,14 @@ def vm_install_test_tools_no_reboot(running_unsealed_windows_vm: VM, guest_tools
7682
ids=list(WIN_GUEST_TOOLS_ISOS.keys()),
7783
params=list(WIN_GUEST_TOOLS_ISOS.values()),
7884
)
79-
def guest_tools_iso(host: Host, request: pytest.FixtureRequest, nfs_iso_sr: SR):
85+
def guest_tools_iso(
86+
host: Host, request: pytest.FixtureRequest, nfs_iso_sr: SR
87+
) -> Generator[Dict[str, Any], None, None]:
8088
yield from iso_create(host, nfs_iso_sr, request.param)
8189

8290

8391
@pytest.fixture(scope="module")
84-
def other_tools_iso(host: Host, nfs_iso_sr: SR):
92+
def other_tools_iso(host: Host, nfs_iso_sr: SR) -> Generator[Dict[str, Any], None, None]:
8593
yield from iso_create(host, nfs_iso_sr, OTHER_GUEST_TOOLS_ISO)
8694

8795

@@ -90,7 +98,7 @@ def vm_install_other_drivers(
9098
unsealed_windows_vm_and_snapshot: Tuple[VM, Snapshot],
9199
other_tools_iso: Dict[str, Any],
92100
request: pytest.FixtureRequest,
93-
):
101+
) -> Generator[Tuple[VM, Dict[str, Any]], None, None]:
94102
vm, snapshot = unsealed_windows_vm_and_snapshot
95103
param = request.param
96104
install_other_drivers(vm, other_tools_iso["name"], param)

tests/guest_tools/win/test_guest_tools_win.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import pytest
24

35
import logging
@@ -70,10 +72,10 @@
7072
@pytest.mark.multi_vms
7173
@pytest.mark.usefixtures("windows_vm")
7274
class TestGuestToolsWindows:
73-
def test_drivers_detected(self, vm_install_test_tools_per_test_class: VM):
75+
def test_drivers_detected(self, vm_install_test_tools_per_test_class: VM) -> None:
7476
pass
7577

76-
def test_vif_replug(self, vm_install_test_tools_per_test_class: VM):
78+
def test_vif_replug(self, vm_install_test_tools_per_test_class: VM) -> None:
7779
vm = vm_install_test_tools_per_test_class
7880
for _iter in range(3):
7981
vifs = vm.vifs()
@@ -87,7 +89,7 @@ def test_vif_replug(self, vm_install_test_tools_per_test_class: VM):
8789
vif.plug()
8890
wait_for(vm.is_ssh_up, "Wait for SSH up")
8991

90-
def test_rss(self, vm_install_test_tools_per_test_class: VM):
92+
def test_rss(self, vm_install_test_tools_per_test_class: VM) -> None:
9193
"""
9294
Receive-side scaling is known to be broken on some driver versions.
9395
@@ -98,7 +100,7 @@ def test_rss(self, vm_install_test_tools_per_test_class: VM):
98100
for vif in vifs:
99101
assert vif_has_rss(vif)
100102

101-
def test_reporting_after_xeniface_disable(self, vm_install_test_tools_per_test_class: VM):
103+
def test_reporting_after_xeniface_disable(self, vm_install_test_tools_per_test_class: VM) -> None:
102104
vm = vm_install_test_tools_per_test_class
103105
for _iter in range(3):
104106
logging.info("Disable Xeniface")
@@ -109,7 +111,7 @@ def test_reporting_after_xeniface_disable(self, vm_install_test_tools_per_test_c
109111
check_vm_distro(vm)
110112
check_vm_clipboard(vm)
111113

112-
def test_reporting_after_suspend(self, vm_install_test_tools_per_test_class: VM):
114+
def test_reporting_after_suspend(self, vm_install_test_tools_per_test_class: VM) -> None:
113115
vm = vm_install_test_tools_per_test_class
114116
for _iter in range(3):
115117
vm.suspend(verify=True)
@@ -118,7 +120,7 @@ def test_reporting_after_suspend(self, vm_install_test_tools_per_test_class: VM)
118120
check_vm_distro(vm)
119121
check_vm_clipboard(vm)
120122

121-
def test_xenvbd_unmap(self, vm_install_test_tools_per_test_class: VM):
123+
def test_xenvbd_unmap(self, vm_install_test_tools_per_test_class: VM) -> None:
122124
"""Xenvbd must always advertise unmap to allow migration between backends with different discard support."""
123125
vm = vm_install_test_tools_per_test_class
124126
trim_supported = strtobool(
@@ -127,7 +129,7 @@ def test_xenvbd_unmap(self, vm_install_test_tools_per_test_class: VM):
127129
)
128130
assert trim_supported
129131

130-
def test_xenvbd_ssd(self, vm_install_test_tools_per_test_class: VM):
132+
def test_xenvbd_ssd(self, vm_install_test_tools_per_test_class: VM) -> None:
131133
"""Xenvbd must always advertise as SSD to avoid unnecessary defragging by Windows."""
132134
vm = vm_install_test_tools_per_test_class
133135
is_ssd = strtobool(
@@ -144,7 +146,7 @@ def test_xenvbd_ssd(self, vm_install_test_tools_per_test_class: VM):
144146
@pytest.mark.multi_vms
145147
@pytest.mark.usefixtures("windows_vm")
146148
class TestGuestToolsWindowsDestructive:
147-
def test_uninstall_tools(self, vm_install_test_tools_no_reboot: VM):
149+
def test_uninstall_tools(self, vm_install_test_tools_no_reboot: VM) -> None:
148150
vm = vm_install_test_tools_no_reboot
149151
vm_shutdown_without_tools(vm)
150152
vm.start()
@@ -157,15 +159,15 @@ def test_uninstall_tools(self, vm_install_test_tools_no_reboot: VM):
157159
assert vm.are_windows_tools_uninstalled()
158160
check_vm_dns(vm)
159161

160-
def test_uninstall_tools_early(self, vm_install_test_tools_no_reboot: VM):
162+
def test_uninstall_tools_early(self, vm_install_test_tools_no_reboot: VM) -> None:
161163
vm = vm_install_test_tools_no_reboot
162164
logging.info("Uninstall Windows PV drivers before rebooting")
163165
uninstall_guest_tools(vm, action=PowerAction.Reboot)
164166
assert vm.are_windows_tools_uninstalled()
165167

166168
def test_install_with_other_tools(
167169
self, vm_install_other_drivers: Tuple[VM, dict[str, Any]], guest_tools_iso: dict[str, Any]
168-
):
170+
) -> None:
169171
vm, param = vm_install_other_drivers
170172
if param["upgradable"]:
171173
install_guest_tools(vm, guest_tools_iso, PowerAction.Reboot, check=False)
@@ -175,15 +177,15 @@ def test_install_with_other_tools(
175177
assert exitcode == ERROR_INSTALL_FAILURE
176178

177179
@pytest.mark.usefixtures("uefi_vm")
178-
def test_uefi_vm_suspend_refused_without_tools(self, running_unsealed_windows_vm: VM):
180+
def test_uefi_vm_suspend_refused_without_tools(self, running_unsealed_windows_vm: VM) -> None:
179181
vm = running_unsealed_windows_vm
180182
with pytest.raises(SSHCommandFailed, match="lacks the feature"):
181183
vm.suspend()
182184
wait_for_vm_running_and_ssh_up_without_tools(vm)
183185

184186
# Test of the unplug rework, where the driver must remain activated even if the device ID changes.
185187
# Also serves as a "close-enough" test of vendor device toggling.
186-
def test_toggle_device_id(self, running_unsealed_windows_vm: VM, guest_tools_iso: dict[str, Any]):
188+
def test_toggle_device_id(self, running_unsealed_windows_vm: VM, guest_tools_iso: dict[str, Any]) -> None:
187189
vm = running_unsealed_windows_vm
188190
assert vm.param_get("platform", "device_id") == "0002"
189191
install_guest_tools(vm, guest_tools_iso, PowerAction.Shutdown, check=False)

tests/guest_tools/win/test_xenclean.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import pytest
24

35
import logging
@@ -41,7 +43,7 @@ def run_xenclean(vm: VM, guest_tools_iso: Dict[str, Any], onboard: Literal[True]
4143
...
4244

4345

44-
def run_xenclean(vm: VM, guest_tools_iso: Dict[str, Any], onboard: bool = False):
46+
def run_xenclean(vm: VM, guest_tools_iso: Dict[str, Any], onboard: bool = False) -> str | None:
4547
"""
4648
Run XenClean from the provided guest tools.
4749
@@ -80,10 +82,12 @@ def run_xenclean(vm: VM, guest_tools_iso: Dict[str, Any], onboard: bool = False)
8082
onboarding_phase = ONBOARDING_PHASES[int(exitcode)]
8183
logging.info(f"Onboarding phase: {onboarding_phase}")
8284
return onboarding_phase
85+
else:
86+
return None
8387

8488

8589
@pytest.fixture(scope="module")
86-
def onboarding_guest_tools_iso(guest_tools_iso):
90+
def onboarding_guest_tools_iso(guest_tools_iso: Dict[str, Any]) -> Dict[str, Any]:
8791
if not guest_tools_iso.get("onboard_family"):
8892
pytest.skip("Onboarding info not declared in data.py")
8993
return guest_tools_iso
@@ -92,24 +96,30 @@ def onboarding_guest_tools_iso(guest_tools_iso):
9296
@pytest.mark.multi_vms
9397
@pytest.mark.usefixtures("windows_vm")
9498
class TestXenClean:
95-
def test_xenclean_without_tools(self, running_unsealed_windows_vm: VM, guest_tools_iso):
99+
def test_xenclean_without_tools(
100+
self, running_unsealed_windows_vm: VM, guest_tools_iso: Dict[str, Any]
101+
) -> None:
96102
vm = running_unsealed_windows_vm
97103
logging.info("XenClean with empty VM")
98104
run_xenclean(vm, guest_tools_iso)
99105
assert vm.are_windows_tools_uninstalled()
100106

101-
def test_xenclean_onboard_without_tools(self, running_unsealed_windows_vm: VM, onboarding_guest_tools_iso):
107+
def test_xenclean_onboard_without_tools(self, running_unsealed_windows_vm: VM,
108+
onboarding_guest_tools_iso: Dict[str, Any]) -> None:
102109
vm = running_unsealed_windows_vm
103110
logging.info("XenClean onboard with empty VM")
104111
assert run_xenclean(vm, onboarding_guest_tools_iso, onboard=True) == "ReadyForOnboard"
105112

106-
def test_xenclean_with_test_tools_early(self, vm_install_test_tools_no_reboot: VM, guest_tools_iso):
113+
def test_xenclean_with_test_tools_early(
114+
self, vm_install_test_tools_no_reboot: VM, guest_tools_iso: Dict[str, Any]
115+
) -> None:
107116
vm = vm_install_test_tools_no_reboot
108117
logging.info("XenClean with test tools (without reboot)")
109118
run_xenclean(vm, guest_tools_iso)
110119
assert vm.are_windows_tools_uninstalled()
111120

112-
def test_xenclean_with_test_tools(self, vm_install_test_tools_no_reboot: VM, guest_tools_iso):
121+
def test_xenclean_with_test_tools(self, vm_install_test_tools_no_reboot: VM,
122+
guest_tools_iso: Dict[str, Any]) -> None:
113123
vm = vm_install_test_tools_no_reboot
114124
vm.reboot()
115125
# HACK: In some cases, vm.reboot(verify=False) followed by vm.insert_cd() (as called by run_xenclean)
@@ -123,7 +133,8 @@ def test_xenclean_with_test_tools(self, vm_install_test_tools_no_reboot: VM, gue
123133
assert vm.are_windows_tools_uninstalled()
124134
check_vm_dns(vm)
125135

126-
def test_xenclean_onboard_with_test_tools(self, vm_install_test_tools_no_reboot: VM, onboarding_guest_tools_iso):
136+
def test_xenclean_onboard_with_test_tools(self, vm_install_test_tools_no_reboot: VM,
137+
onboarding_guest_tools_iso: Dict[str, Any]) -> None:
127138
vm = vm_install_test_tools_no_reboot
128139
vm.reboot()
129140
wait_for_vm_running_and_ssh_up_without_tools(vm)
@@ -133,7 +144,9 @@ def test_xenclean_onboard_with_test_tools(self, vm_install_test_tools_no_reboot:
133144
logging.info("Check tools still working")
134145
assert vm.are_windows_tools_working()
135146

136-
def test_xenclean_with_other_tools(self, vm_install_other_drivers: Tuple[VM, Dict], guest_tools_iso):
147+
def test_xenclean_with_other_tools(
148+
self, vm_install_other_drivers: Tuple[VM, Dict[str, Any]], guest_tools_iso: Dict[str, Any]
149+
) -> None:
137150
vm, param = vm_install_other_drivers
138151
if param.get("vendor_device"):
139152
pytest.skip("Skipping XenClean with vendor device present")
@@ -146,8 +159,8 @@ def test_xenclean_with_other_tools(self, vm_install_other_drivers: Tuple[VM, Dic
146159
check_vm_dns(vm)
147160

148161
def test_xenclean_onboard_with_other_tools(
149-
self, vm_install_other_drivers: Tuple[VM, Dict], onboarding_guest_tools_iso
150-
):
162+
self, vm_install_other_drivers: Tuple[VM, Dict[str, Any]], onboarding_guest_tools_iso: Dict[str, Any]
163+
) -> None:
151164
vm, param = vm_install_other_drivers
152165
onboarding_phase = param.get("onboarding_phase")
153166
if not param.get("onboarding_phase"):

0 commit comments

Comments
 (0)