Skip to content

Commit 75c1084

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

5 files changed

Lines changed: 177 additions & 8 deletions

File tree

qubes/tests/integ/network.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,37 @@ 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.testvm1.pause())
201+
self.testvm1.netvm = self.testnetvm
202+
self.assertEqual(
203+
self.testvm1.features.get("deferred-netvm-original", None), ""
204+
)
205+
self.loop.run_until_complete(self.testvm1.unpause())
206+
self.assertEqual(
207+
self.testvm1.features.get("deferred-netvm-original", None), None
208+
)
209+
self.assertEqual(self.run_cmd(self.testvm1, self.ping_ip), 0)
210+
self.assertEqual(self.run_cmd(self.testvm1, self.ping_name), 0)
211+
212+
self.loop.run_until_complete(self.testvm1.pause())
213+
self.testvm1.netvm = None
214+
self.assertEqual(
215+
self.testvm1.features.get("deferred-netvm-original", None),
216+
self.testnetvm.name,
217+
)
218+
self.loop.run_until_complete(self.testvm1.unpause())
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+
self.assertEqual(
222+
self.testvm1.features.get("deferred-netvm-original", None), None
223+
)
224+
194225
def test_010_simple_proxyvm(self):
195226
"""
196227
:type self: qubes.tests.SystemTestCase | VMNetworkingMixin
@@ -389,7 +420,7 @@ def test_030_firewallvm_firewall(self):
389420
# block all except ICMP
390421

391422
self.testvm1.firewall.rules = [
392-
(qubes.firewall.Rule(None, action="accept", proto="icmp"))
423+
qubes.firewall.Rule(None, action="accept", proto="icmp")
393424
]
394425
self.testvm1.firewall.save()
395426
# Ugly hack b/c there is no feedback when the rules are actually

qubes/tests/vm/mix/net.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,76 @@ 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(
179+
vm.features.get("deferred-netvm-original", None),
180+
original_netvm,
181+
)
182+
with patch(
183+
"qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False
184+
):
185+
vm.reset_deferred_netvm()
186+
self.assertEqual(
187+
vm.features.get("deferred-netvm-original", None), None
188+
)
189+
mock_detach.assert_called()
190+
mock_attach.assert_called()
191+
mock_detach.reset_mock()
192+
mock_attach.reset_mock()
193+
194+
with self.subTest("setting netvm to none"):
195+
original_netvm = vm.netvm.name
196+
vm.netvm = None
197+
mock_detach.assert_not_called()
198+
mock_attach.assert_not_called()
199+
self.assertEqual(
200+
vm.features.get("deferred-netvm-original", None),
201+
original_netvm,
202+
)
203+
with patch(
204+
"qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False
205+
):
206+
vm.reset_deferred_netvm()
207+
self.assertEqual(
208+
vm.features.get("deferred-netvm-original", None), None
209+
)
210+
mock_detach.assert_not_called()
211+
mock_attach.assert_not_called()
212+
213+
with self.subTest("resetting netvm to default"):
214+
original_netvm = vm.netvm.name if vm.netvm else ""
215+
del vm.netvm
216+
mock_detach.assert_not_called()
217+
mock_attach.assert_not_called()
218+
self.assertEqual(
219+
vm.features.get("deferred-netvm-original", None),
220+
original_netvm,
221+
)
222+
with patch(
223+
"qubes.vm.qubesvm.QubesVM.is_paused", lambda x: False
224+
):
225+
vm.reset_deferred_netvm()
226+
self.assertEqual(
227+
vm.features.get("deferred-netvm-original", None), None
228+
)
229+
mock_detach.assert_not_called()
230+
mock_attach.assert_called_once()
231+
162232
def test_150_ip(self):
163233
vm = self.get_vm()
164234
self.setup_netvms(vm)

qubes/tests/vm/qubesvm.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,7 +1691,12 @@ def test_600_libvirt_xml_hvm_pcidev(self):
16911691
)
16921692
dom0 = self.get_vm(name="dom0", qid=0)
16931693
vm = self.get_vm(uuid=my_uuid)
1694-
vm.netvm = None
1694+
vm.paused = lambda x: False
1695+
with unittest.mock.patch(
1696+
"qubes.vm.qubesvm.QubesVM.is_paused"
1697+
) as mock_is_paused:
1698+
mock_is_paused.return_value = False
1699+
vm.netvm = None
16951700
vm.virt_mode = "hvm"
16961701
vm.kernel = None
16971702
# even with meminfo-writer enabled, should have memory==maxmem
@@ -1803,7 +1808,11 @@ def test_600_libvirt_xml_hvm_pcidev_s0ix(self):
18031808
dom0 = self.get_vm(name="dom0", qid=0)
18041809
dom0.features["suspend-s0ix"] = True
18051810
vm = self.get_vm(uuid=my_uuid)
1806-
vm.netvm = None
1811+
with unittest.mock.patch(
1812+
"qubes.vm.qubesvm.QubesVM.is_paused"
1813+
) as mock_is_paused:
1814+
mock_is_paused.return_value = False
1815+
vm.netvm = None
18071816
vm.virt_mode = "hvm"
18081817
vm.kernel = None
18091818
# even with meminfo-writer enabled, should have memory==maxmem

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-original", 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-original"]
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-original" not in self.features:
568+
self.features["deferred-netvm-original"] = (
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)