Skip to content

Commit e3c1b74

Browse files
committed
Redo netvm setup after unpause
Fixes: QubesOS/qubes-issues#10173 For: QubesOS/qubes-issues#1512
1 parent 8de4cd0 commit e3c1b74

4 files changed

Lines changed: 142 additions & 6 deletions

File tree

qubes/tests/integ/network.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,34 @@ def test_000_simple_networking(self):
191191
self.assertEqual(self.run_cmd(self.testvm1, self.ping_ip), 0)
192192
self.assertEqual(self.run_cmd(self.testvm1, self.ping_name), 0)
193193

194+
def test_001_simple_networking_paused(self):
195+
"""
196+
:type self: qubes.tests.SystemTestCase | VMNetworkingMixin
197+
"""
198+
self.testvm1.netvm = None
199+
self.loop.run_until_complete(self.start_vm(self.testvm1))
200+
self.loop.run_until_complete(self.pause())
201+
self.testvm1.netvm = self.testnetvm
202+
self.assertEqual(self.testvm1.features.get("deferred-netvm"), None)
203+
self.loop.run_until_complete(self.unpause())
204+
self.assertEqual(
205+
self.testvm1.features.get("deferred-netvm", None), None
206+
)
207+
self.assertEqual(self.run_cmd(self.testvm1, self.ping_ip), 0)
208+
self.assertEqual(self.run_cmd(self.testvm1, self.ping_name), 0)
209+
210+
self.loop.run_until_complete(self.pause())
211+
self.testvm1.netvm = None
212+
self.assertEqual(
213+
self.testvm1.features.get("deferred-netvm"), self.testnetvm.name
214+
)
215+
self.loop.run_until_complete(self.unpause())
216+
self.assertEqual(
217+
self.testvm1.features.get("deferred-netvm", None), None
218+
)
219+
self.assertEqual(self.run_cmd(self.testvm1, self.ping_ip), 0)
220+
self.assertEqual(self.run_cmd(self.testvm1, self.ping_name), 0)
221+
194222
def test_010_simple_proxyvm(self):
195223
"""
196224
:type self: qubes.tests.SystemTestCase | VMNetworkingMixin
@@ -389,7 +417,7 @@ def test_030_firewallvm_firewall(self):
389417
# block all except ICMP
390418

391419
self.testvm1.firewall.rules = [
392-
(qubes.firewall.Rule(None, action="accept", proto="icmp"))
420+
qubes.firewall.Rule(None, action="accept", proto="icmp")
393421
]
394422
self.testvm1.firewall.save()
395423
# Ugly hack b/c there is no feedback when the rules are actually

qubes/tests/vm/mix/net.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,55 @@ def test_145_netvm_change(self):
159159
mock_detach.reset_mock()
160160
mock_attach.reset_mock()
161161

162+
def test_146_netvm_defer(self):
163+
vm = self.get_vm()
164+
self.setup_netvms(vm)
165+
with (
166+
patch("qubes.vm.qubesvm.QubesVM.is_running", lambda x: True),
167+
patch("qubes.vm.qubesvm.QubesVM.is_paused", lambda x: True),
168+
patch("qubes.vm.mix.net.NetVMMixin.attach_network") as mock_attach,
169+
patch("qubes.vm.mix.net.NetVMMixin.detach_network") as mock_detach,
170+
patch("qubes.vm.qubesvm.QubesVM.create_qdb_entries"),
171+
):
172+
173+
with self.subTest("changing netvm"):
174+
original_netvm = vm.netvm.name
175+
vm.netvm = self.netvm2
176+
mock_detach.assert_not_called()
177+
mock_attach.assert_not_called()
178+
self.assertEqual(vm.features.get("deferred-netvm", None), original_netvm)
179+
with patch("qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False):
180+
vm.reset_deferred_netvm()
181+
self.assertEqual(vm.features.get("deferred-netvm", None), None)
182+
mock_detach.assert_called()
183+
mock_attach.assert_called()
184+
mock_detach.reset_mock()
185+
mock_attach.reset_mock()
186+
187+
with self.subTest("setting netvm to none"):
188+
original_netvm = vm.netvm.name
189+
vm.netvm = None
190+
mock_detach.assert_not_called()
191+
mock_attach.assert_not_called()
192+
self.assertEqual(vm.features.get("deferred-netvm", None), original_netvm)
193+
with patch("qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False):
194+
vm.reset_deferred_netvm()
195+
self.assertEqual(vm.features.get("deferred-netvm", None), None)
196+
mock_detach.assert_not_called()
197+
mock_attach.assert_not_called()
198+
199+
with self.subTest("resetting netvm to default"):
200+
original_netvm = vm.netvm.name if vm.netvm else ""
201+
del vm.netvm
202+
mock_detach.assert_not_called()
203+
mock_attach.assert_not_called()
204+
self.assertEqual(vm.features.get("deferred-netvm", None), original_netvm)
205+
with patch("qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False):
206+
vm.reset_deferred_netvm()
207+
self.assertEqual(vm.features.get("deferred-netvm", None), None)
208+
mock_detach.assert_not_called()
209+
mock_attach.assert_called_once()
210+
162211
def test_150_ip(self):
163212
vm = self.get_vm()
164213
self.setup_netvms(vm)

qubes/vm/dispvm.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,13 +577,15 @@ def use_preload(self):
577577
self.log.info("Using preloaded qube")
578578
if not appvm.features.get("internal", None):
579579
del self.features["internal"]
580+
self.reset_deferred_netvm()
580581
self.preload_requested = None
581582
del self.features["preload-dispvm-in-progress"]
582583
else:
583584
# Happens when unpause/resume occurs without qube being requested.
584585
self.log.warning("Using a preloaded qube before requesting it")
585586
if not appvm.features.get("internal", None):
586587
del self.features["internal"]
588+
self.reset_deferred_netvm()
587589
appvm.remove_preload_from_list([self.name])
588590
self.features["preload-dispvm-in-progress"] = False
589591
self.app.save()

qubes/vm/mix/net.py

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# License along with this library; if not, see <https://www.gnu.org/licenses/>.
2121
#
2222

23-
""" This module contains the NetVMMixin """
23+
"""This module contains the NetVMMixin"""
2424
import ipaddress
2525
import os
2626
import re
@@ -341,6 +341,43 @@ def on_domain_started_net(self, event, **kwargs):
341341
except (qubes.exc.QubesException, libvirt.libvirtError):
342342
vm.log.warning("Cannot attach network", exc_info=1)
343343

344+
def reset_deferred_netvm(self):
345+
deferred_from = self.features.get("deferred-netvm", None)
346+
if deferred_from is None:
347+
return
348+
oldvalue = None
349+
if deferred_from in self.app.domains:
350+
oldvalue = self.app.domains[deferred_from]
351+
if oldvalue:
352+
self.fire_event(
353+
"property-pre-set:netvm",
354+
pre_event=True,
355+
name="netvm",
356+
newvalue=self.netvm,
357+
oldvalue=oldvalue,
358+
)
359+
if self.netvm:
360+
self.fire_event(
361+
"property-set:netvm",
362+
name="netvm",
363+
newvalue=self.netvm,
364+
oldvalue=oldvalue,
365+
)
366+
del self.features["deferred-netvm"]
367+
368+
@qubes.events.handler("domain-unpaused")
369+
def on_domain_unpaused_net(
370+
self, event, **kwargs
371+
): # pylint: disable=unused-argument
372+
"""Check for deferred netvm changes in case qube was paused while
373+
changes happened."""
374+
if getattr(self, "is_preload", False):
375+
# Networking of preloaded disposable must be done when it is being
376+
# marked as used, so we guarantee that it finishes before the user
377+
# can use the disposable.
378+
return
379+
self.reset_deferred_netvm()
380+
344381
@qubes.events.handler("domain-pre-shutdown")
345382
def on_domain_pre_shutdown(self, event, force=False):
346383
"""Checks before NetVM shutdown if any connected domains are running.
@@ -373,6 +410,12 @@ def attach_network(self):
373410
self.netvm.start()
374411

375412
self.netvm.set_mapped_ip_info_for_vm(self)
413+
414+
if self.is_paused():
415+
self.log.warning(
416+
"Deferred attaching libvirt net device because qube is paused"
417+
)
418+
return
376419
self.libvirt_domain.attachDevice(
377420
self.app.env.get_template("libvirt/devices/net.xml").render(vm=self)
378421
)
@@ -387,6 +430,11 @@ def detach_network(self):
387430
self, "netvm should not be {}".format(self.netvm)
388431
)
389432

433+
if self.is_paused():
434+
self.log.warning(
435+
"Deferred detaching libvirt net device because qube is paused"
436+
)
437+
return
390438
self.libvirt_domain.detachDevice(
391439
self.app.env.get_template("libvirt/devices/net.xml").render(vm=self)
392440
)
@@ -515,10 +563,18 @@ def on_property_pre_set_netvm(self, event, name, newvalue, oldvalue=None):
515563
),
516564
)
517565

566+
if self.is_paused():
567+
if "deferred-netvm" not in self.features:
568+
self.features["deferred-netvm"] = (
569+
oldvalue.name if oldvalue else None
570+
)
571+
return
518572
# don't check oldvalue, because it's missing if it was default
519-
if self.netvm is not None:
520-
if self.is_running() and self.netvm.is_running():
521-
self.detach_network()
573+
if self.netvm is None:
574+
return
575+
if not (self.is_running() and self.netvm.is_running()):
576+
return
577+
self.detach_network()
522578

523579
@qubes.events.handler("property-set:netvm")
524580
def on_property_set_netvm(self, event, name, newvalue, oldvalue=None):
@@ -539,7 +595,8 @@ def on_property_set_netvm(self, event, name, newvalue, oldvalue=None):
539595
if self.is_running():
540596
# refresh IP, DNS etc
541597
self.create_qdb_entries()
542-
self.attach_network()
598+
if not self.is_paused():
599+
self.attach_network()
543600

544601
newvalue.fire_event("net-domain-connect", vm=self)
545602

0 commit comments

Comments
 (0)