Skip to content

Commit c81b3ba

Browse files
committed
Set deferred netvm for paused clients on shutdown
Also don't skip connecting if netvm was changed to the original value, as the netvm may have restarted in the meantime. For: #722 Fixes: QubesOS/qubes-issues#10173 For: QubesOS/qubes-issues#1512
1 parent 6b978a3 commit c81b3ba

6 files changed

Lines changed: 190 additions & 40 deletions

File tree

qubes/tests/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,10 +1404,10 @@ def enter_keys_in_window(self, title, keys):
14041404
] + keys
14051405
subprocess.check_call(command)
14061406

1407-
def shutdown_and_wait(self, vm, timeout=60):
1407+
def shutdown_and_wait(self, vm, force=False, timeout=60):
14081408
try:
14091409
self.loop.run_until_complete(
1410-
vm.shutdown(wait=True, timeout=timeout)
1410+
vm.shutdown(wait=True, force=force, timeout=timeout)
14111411
)
14121412
except qubes.exc.QubesException:
14131413
name = vm.name

qubes/tests/integ/network.py

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,11 @@ def tearDown(self):
165165
del self.netvms
166166
super(VmNetworkingMixin, self).tearDown()
167167

168-
def configure_netvm(self):
168+
def configure_netvm(self, netvms: list = None):
169169
"""
170170
:type self: qubes.tests.SystemTestCase | VmNetworkingMixin
171+
172+
:param list netvms: Use specified netvms or self.netvms.
171173
"""
172174

173175
def run_netvm_cmd(qube, cmd):
@@ -181,7 +183,9 @@ def run_netvm_cmd(qube, cmd):
181183
% (qube, cmd, e.stdout.decode(), e.stderr.decode())
182184
)
183185

184-
for qube in self.netvms:
186+
if not netvms:
187+
netvms = self.netvms
188+
for qube in netvms:
185189
if not qube.is_running():
186190
self.loop.run_until_complete(self.start_vm(qube))
187191
# Ensure that dnsmasq is installed:
@@ -254,8 +258,8 @@ def _networking_paused_from_none_to_existent(
254258
0,
255259
"Ping by name on " + test + " failed",
256260
)
257-
self.assertEqual(
258-
self.testvm1.features.get("deferred-netvm-original", None), None
261+
self.assertIsNone(
262+
self.testvm1.features.get("deferred-netvm-original", None)
259263
)
260264
self.shutdown_and_wait(self.testvm1)
261265

@@ -284,8 +288,8 @@ def _networking_paused_from_existent_to_none(
284288
0,
285289
"Ping by name on " + test + " succeeded but should have failed",
286290
)
287-
self.assertEqual(
288-
self.testvm1.features.get("deferred-netvm-original", None), None
291+
self.assertIsNone(
292+
self.testvm1.features.get("deferred-netvm-original", None)
289293
)
290294
self.shutdown_and_wait(self.testvm1)
291295

@@ -317,8 +321,8 @@ def _networking_paused_change_shutdown_old(
317321
0,
318322
"Ping by name on " + test + " failed",
319323
)
320-
self.assertEqual(
321-
self.testvm1.features.get("deferred-netvm-original", None), None
324+
self.assertIsNone(
325+
self.testvm1.features.get("deferred-netvm-original", None)
322326
)
323327
self.shutdown_and_wait(self.testvm1)
324328

@@ -355,8 +359,75 @@ def _networking_paused_change_purge_old(
355359
0,
356360
"Ping by name on " + test + " failed",
357361
)
362+
self.assertIsNone(
363+
self.testvm1.features.get("deferred-netvm-original", None)
364+
)
365+
self.shutdown_and_wait(self.testvm1)
366+
367+
def _networking_paused_restart_netvm(
368+
self, ip, name, ip_deadline, name_deadline
369+
):
370+
test = "restart netvm and unpause client"
371+
self.log.critical(test)
372+
print(test)
373+
self.testvm1.netvm = self.testnetvm
374+
self.loop.run_until_complete(self.start_vm(self.testvm1))
375+
self.loop.run_until_complete(self.testvm1.pause())
376+
self.shutdown_and_wait(self.testnetvm, force=True)
377+
self.assertEqual(
378+
self.testvm1.features.get("deferred-netvm-original", None),
379+
self.testnetvm.name,
380+
)
381+
self.loop.run_until_complete(self.start_vm(self.testnetvm))
382+
self.configure_netvm([self.testnetvm])
383+
self.loop.run_until_complete(self.testvm1.unpause())
384+
self._run_cmd_and_log_output(self.testvm1, ip_deadline)
385+
self._run_cmd_and_log_output(self.testvm1, name_deadline)
386+
self.assertEqual(
387+
self.run_cmd(self.testvm1, ip),
388+
0,
389+
"Ping by IP on " + test + " failed",
390+
)
391+
self.assertEqual(
392+
self.run_cmd(self.testvm1, name),
393+
0,
394+
"Ping by name on " + test + " failed",
395+
)
396+
self.assertIsNone(
397+
self.testvm1.features.get("deferred-netvm-original", None)
398+
)
399+
self.shutdown_and_wait(self.testvm1)
400+
401+
def _networking_paused_shutdown_netvm(
402+
self, ip, name, ip_deadline, name_deadline
403+
):
404+
test = "shutdown netvm and unpause client"
405+
self.log.critical(test)
406+
print(test)
407+
self.testvm1.netvm = self.testnetvm
408+
self.loop.run_until_complete(self.start_vm(self.testvm1))
409+
self.loop.run_until_complete(self.testvm1.pause())
410+
self.shutdown_and_wait(self.testnetvm, force=True)
411+
self.assertEqual(
412+
self.testvm1.features.get("deferred-netvm-original", None),
413+
self.testnetvm.name,
414+
)
415+
self.loop.run_until_complete(self.testvm1.unpause())
416+
self.configure_netvm([self.testnetvm])
417+
self._run_cmd_and_log_output(self.testvm1, ip_deadline)
418+
self._run_cmd_and_log_output(self.testvm1, name_deadline)
358419
self.assertEqual(
359-
self.testvm1.features.get("deferred-netvm-original", None), None
420+
self.run_cmd(self.testvm1, ip),
421+
0,
422+
"Ping by IP on " + test + " failed",
423+
)
424+
self.assertEqual(
425+
self.run_cmd(self.testvm1, name),
426+
0,
427+
"Ping by name on " + test + " failed",
428+
)
429+
self.assertIsNone(
430+
self.testvm1.features.get("deferred-netvm-original", None)
360431
)
361432
self.shutdown_and_wait(self.testvm1)
362433

@@ -406,6 +477,28 @@ def test_001_simple_networking_paused_change_purge_old(self):
406477
self.ping_deadline_name,
407478
)
408479

480+
def test_001_simple_networking_paused_restart_netvm(self):
481+
"""
482+
:type self: qubes.tests.SystemTestCase | VmNetworkingMixin
483+
"""
484+
self._networking_paused_restart_netvm(
485+
self.ping_ip,
486+
self.ping_name,
487+
self.ping_deadline_ip,
488+
self.ping_deadline_name,
489+
)
490+
491+
def test_001_simple_networking_paused_shutdown_netvm(self):
492+
"""
493+
:type self: qubes.tests.SystemTestCase | VmNetworkingMixin
494+
"""
495+
self._networking_paused_shutdown_netvm(
496+
self.ping_ip,
497+
self.ping_name,
498+
self.ping_deadline_ip,
499+
self.ping_deadline_name,
500+
)
501+
409502
def test_010_simple_proxyvm(self):
410503
"""
411504
:type self: qubes.tests.SystemTestCase | VmNetworkingMixin

qubes/tests/integ/network_ipv6.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,11 @@ def tearDown(self):
7474
)
7575
super().tearDown()
7676

77-
def configure_netvm(self):
77+
def configure_netvm(self, netvms: list = None):
7878
"""
7979
:type self: qubes.tests.SystemTestCase | VmIPv6NetworkingMixin
80+
81+
:param list netvms: Use specified netvms or self.netvms.
8082
"""
8183

8284
def run_netvm_cmd(qube, cmd):
@@ -90,11 +92,13 @@ def run_netvm_cmd(qube, cmd):
9092
% (cmd, e.stdout.decode(), e.stderr.decode())
9193
)
9294

93-
for qube in self.netvms:
95+
if not netvms:
96+
netvms = self.netvms
97+
for qube in netvms:
9498
qube.features["ipv6"] = True
9599
super(VmIPv6NetworkingMixin, self).configure_netvm()
96100

97-
for qube in self.netvms:
101+
for qube in netvms:
98102
run_netvm_cmd(
99103
qube, "ip addr add {}/128 dev test0".format(self.test_ip6)
100104
)
@@ -167,6 +171,28 @@ def test_501_simple_networking_paused_change_purge_old(self):
167171
self.ping6_deadline_name,
168172
)
169173

174+
def test_501_simple_networking_paused_restart_netvm(self):
175+
"""
176+
:type self: qubes.tests.SystemTestCase | VmIPv6NetworkingMixin
177+
"""
178+
self._networking_paused_restart_netvm(
179+
self.ping6_ip,
180+
self.ping6_name,
181+
self.ping6_deadline_ip,
182+
self.ping6_deadline_name,
183+
)
184+
185+
def test_501_simple_networking_paused_shutdown_netvm(self):
186+
"""
187+
:type self: qubes.tests.SystemTestCase | VmIPv6NetworkingMixin
188+
"""
189+
self._networking_paused_shutdown_netvm(
190+
self.ping6_ip,
191+
self.ping6_name,
192+
self.ping6_deadline_ip,
193+
self.ping6_deadline_name,
194+
)
195+
170196
def test_510_ipv6_simple_proxyvm(self):
171197
"""
172198
:type self: qubes.tests.SystemTestCase | VmIPv6NetworkingMixin

qubes/tests/vm/mix/net.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -178,31 +178,38 @@ def test_146_netvm_defer(self):
178178
self.loop.run_until_complete(vm.apply_deferred_netvm())
179179
mock_detach.assert_not_called()
180180
mock_attach.assert_not_called()
181-
182181
mock_detach.reset_mock()
183182
mock_attach.reset_mock()
183+
184184
with self.subTest("changing netvm and restoring original netvm"):
185185
original_netvm = vm.netvm.name
186-
vm.netvm = self.netvm2
186+
vm.netvm = vm.netvm
187187
mock_detach.assert_not_called()
188188
mock_attach.assert_not_called()
189189
self.assertEqual(
190190
vm.features.get("deferred-netvm-original", None),
191191
original_netvm,
192192
)
193-
vm.netvm = original_netvm
193+
with patch(
194+
"qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False
195+
):
196+
self.loop.run_until_complete(vm.apply_deferred_netvm())
194197
self.assertEqual(
195198
vm.features.get("deferred-netvm-original", None), None
196199
)
197-
mock_detach.assert_not_called()
198-
mock_attach.assert_not_called()
199-
200+
mock_detach.assert_called()
201+
mock_attach.assert_called()
202+
with patch(
203+
"qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False
204+
):
205+
vm.netvm = vm.netvm
200206
mock_detach.reset_mock()
201207
mock_attach.reset_mock()
208+
209+
original_netvm = vm.netvm.name
202210
with self.subTest(
203211
"changing netvm and restoring original netvm from none"
204212
):
205-
original_netvm = vm.netvm.name
206213
with patch(
207214
"qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False
208215
):
@@ -218,17 +225,27 @@ def test_146_netvm_defer(self):
218225
)
219226
vm.netvm = None
220227
self.assertEqual(
221-
vm.features.get("deferred-netvm-original", None), None
228+
vm.features.get("deferred-netvm-original", None), ""
222229
)
223230
mock_detach.assert_not_called()
224231
mock_attach.assert_not_called()
225232
with patch(
226233
"qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False
227234
):
228-
vm.netvm = original_netvm
235+
self.loop.run_until_complete(vm.apply_deferred_netvm())
236+
self.assertEqual(
237+
vm.features.get("deferred-netvm-original", None), None
238+
)
239+
mock_detach.assert_called()
240+
mock_attach.assert_not_called()
229241

242+
with patch(
243+
"qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False
244+
):
245+
vm.netvm = original_netvm
230246
mock_detach.reset_mock()
231247
mock_attach.reset_mock()
248+
232249
with self.subTest("changing netvm"):
233250
original_netvm = vm.netvm.name
234251
vm.netvm = self.netvm2

qubes/vm/dispvm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -749,18 +749,18 @@ async def use_preload(self) -> None:
749749
self.log.info("Using preloaded qube")
750750
if not appvm.features.get("internal", None):
751751
del self.features["internal"]
752-
await self.apply_deferred_netvm()
753752
self.preload_requested = False
753+
await self.apply_deferred_netvm()
754754
del self.features["preload-dispvm-in-progress"]
755755
else:
756756
# Happens when unpause/resume occurs without qube being requested.
757757
self.log.warning("Using a preloaded qube before requesting it")
758758
if not appvm.features.get("internal", None):
759759
del self.features["internal"]
760-
await self.apply_deferred_netvm()
761760
appvm.remove_preload_from_list(
762761
[self.name], reason="qube was used without being requested"
763762
)
763+
await self.apply_deferred_netvm()
764764
self.features["preload-dispvm-in-progress"] = False
765765
self.app.save()
766766
asyncio.ensure_future(

0 commit comments

Comments
 (0)