Skip to content

Commit f105a73

Browse files
committed
Handle global disposable preload feature
For: QubesOS/qubes-issues#1512 For: QubesOS/qubes-desktop-linux-manager#262
1 parent 78aacd1 commit f105a73

File tree

9 files changed

+636
-199
lines changed

9 files changed

+636
-199
lines changed

qubes/app.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,9 +1732,12 @@ def on_property_set_default_netvm(
17321732
"property-reset:netvm", name="netvm", oldvalue=oldvalue
17331733
)
17341734

1735-
@qubes.events.handler("property-set:default_dispvm")
1735+
@qubes.events.handler(
1736+
"property-set:default_dispvm",
1737+
"property-reset:default_dispvm",
1738+
)
17361739
def on_property_set_default_dispvm(
1737-
self, event, name, newvalue, oldvalue=None
1740+
self, event, name, newvalue=None, oldvalue=None
17381741
):
17391742
# pylint: disable=unused-argument
17401743
for vm in self.domains:
@@ -1749,6 +1752,33 @@ def on_property_set_default_dispvm(
17491752
oldvalue=oldvalue,
17501753
)
17511754

1755+
if not newvalue:
1756+
newvalue = self.default_dispvm
1757+
if newvalue == oldvalue:
1758+
return
1759+
old_appvm = None
1760+
appvm = None
1761+
if oldvalue:
1762+
old_appvm = self.domains[oldvalue]
1763+
old_appvm.preload_max_ignore_global = True
1764+
if newvalue:
1765+
appvm = self.domains[newvalue]
1766+
appvm.preload_max_ignore_global = False
1767+
if old_appvm and old_appvm.is_global_preload_distinct():
1768+
asyncio.ensure_future(
1769+
old_appvm.fire_event_async(
1770+
"domain-preload-dispvm-start",
1771+
reason="it was the default_dispvm",
1772+
)
1773+
)
1774+
if appvm and appvm.is_global_preload_distinct():
1775+
asyncio.ensure_future(
1776+
appvm.fire_event_async(
1777+
"domain-preload-dispvm-start",
1778+
reason="it became the default_dispvm",
1779+
)
1780+
)
1781+
17521782
@qubes.events.handler("property-pre-set:default_kernel")
17531783
# pylint: disable-next=invalid-name
17541784
def on_property_pre_set_default_kernel(

qubes/tests/app.py

Lines changed: 99 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -689,13 +689,6 @@ def test_101_property_migrate_label(self):
689689

690690

691691
class TC_90_Qubes(qubes.tests.QubesTestCase):
692-
def tearDown(self):
693-
try:
694-
os.unlink("/tmp/qubestest.xml")
695-
except:
696-
pass
697-
super().tearDown()
698-
699692
def setUp(self):
700693
super(TC_90_Qubes, self).setUp()
701694
self.app = qubes.Qubes(
@@ -707,12 +700,43 @@ def setUp(self):
707700
self.template = self.app.add_new_vm(
708701
"TemplateVM", name="test-template", label="green"
709702
)
703+
self.template.features["qrexec"] = True
704+
self.template.features["supported-rpc.qubes.WaitForRunningSystem"] = (
705+
True
706+
)
707+
self.appvm = self.app.add_new_vm(
708+
"AppVM",
709+
name="test-dvm",
710+
template=self.template,
711+
label="red",
712+
)
713+
self.appvm_alt = self.app.add_new_vm(
714+
qubes.vm.appvm.AppVM,
715+
name="test-alt-dvm",
716+
template=self.template,
717+
label="red",
718+
)
719+
for qube in [self.appvm, self.appvm_alt]:
720+
qube.features["gui"] = False
721+
qube.template_for_dispvms = True
722+
self.app.save()
723+
self.emitter = qubes.tests.TestEmitter()
724+
725+
def tearDown(self):
726+
try:
727+
os.unlink("/tmp/qubestest.xml")
728+
except:
729+
pass
730+
del self.emitter
731+
super().tearDown()
710732

711733
def cleanup_qubes(self):
712734
self.app.close()
713735
del self.app
714736
try:
715737
del self.template
738+
del self.appvm
739+
del self.appvm_alt
716740
except AttributeError:
717741
pass
718742

@@ -932,11 +956,7 @@ def test_116_remotevm_add_and_remove(self):
932956

933957
assert remotevm1 in self.app.domains
934958
del self.app.domains["remote-vm1"]
935-
936-
self.assertCountEqual(
937-
{d.name for d in self.app.domains},
938-
{"dom0", "test-template", "test-vm", "remote-vm2"},
939-
)
959+
assert "remote-vm1" not in self.app.domains
940960

941961
def test_117_remotevm_status(self):
942962
remotevm1 = self.app.add_new_vm(
@@ -1096,6 +1116,73 @@ def test_207_default_kernel(self):
10961116
existence.side_effect = [True, False]
10971117
self.app.default_kernel = "unittest_GNU_Hurd_1.0.0"
10981118

1119+
def test_300_preload_default_dispvm(self):
1120+
"""Fire event for new setting from no previous one."""
1121+
self.appvm.features["preload-dispvm-max"] = "1"
1122+
with mock.patch.object(self.appvm, "fire_event_async") as mock_events:
1123+
self.app.default_dispvm = self.appvm
1124+
mock_events.assert_called_once()
1125+
self.assertEqual(
1126+
"domain-preload-dispvm-start", mock_events.call_args[0][0]
1127+
)
1128+
1129+
def test_301_preload_default_dispvm_change(self):
1130+
"""Fire event for old and new setting."""
1131+
self.appvm.features["preload-dispvm-max"] = "1"
1132+
self.app.default_dispvm = self.appvm
1133+
self.appvm_alt.features["preload-dispvm-max"] = "1"
1134+
with mock.patch.object(
1135+
self.appvm, "fire_event_async"
1136+
) as mock_old, mock.patch.object(
1137+
self.appvm_alt, "fire_event_async"
1138+
) as mock_new:
1139+
self.app.default_dispvm = self.appvm_alt
1140+
mock_old.assert_called_once()
1141+
mock_new.assert_called_once()
1142+
self.assertEqual(
1143+
"domain-preload-dispvm-start", mock_new.call_args[0][0]
1144+
)
1145+
self.assertEqual(
1146+
"domain-preload-dispvm-start", mock_new.call_args[0][0]
1147+
)
1148+
1149+
def test_302_preload_default_dispvm_change_noop(self):
1150+
"""If global feature has the same value as the new and old setting,
1151+
don't fire the event.
1152+
"""
1153+
self.appvm.features["preload-dispvm-max"] = "1"
1154+
self.appvm_alt.features["preload-dispvm-max"] = "1"
1155+
self.app.domains["dom0"].features["preload-dispvm-max"] = "1"
1156+
self.app.default_dispvm = self.appvm
1157+
with mock.patch.object(
1158+
self.appvm, "fire_event_async"
1159+
) as mock_old, mock.patch.object(
1160+
self.appvm_alt, "fire_event_async"
1161+
) as mock_new:
1162+
self.app.default_dispvm = self.appvm_alt
1163+
mock_old.assert_not_called()
1164+
mock_new.assert_not_called()
1165+
1166+
def test_303_preload_default_dispvm_change_partial_noop(self):
1167+
"""If global feature has the same value as the new or old setting,
1168+
don't fire the event.
1169+
"""
1170+
self.appvm.features["preload-dispvm-max"] = "2"
1171+
self.appvm_alt.features["preload-dispvm-max"] = "1"
1172+
self.app.domains["dom0"].features["preload-dispvm-max"] = "2"
1173+
self.app.default_dispvm = self.appvm
1174+
with mock.patch.object(
1175+
self.appvm, "fire_event_async"
1176+
) as mock_old, mock.patch.object(
1177+
self.appvm_alt, "fire_event_async"
1178+
) as mock_new:
1179+
self.app.default_dispvm = self.appvm_alt
1180+
mock_old.assert_not_called()
1181+
mock_new.assert_called_once()
1182+
self.assertEqual(
1183+
"domain-preload-dispvm-start", mock_new.call_args[0][0]
1184+
)
1185+
10991186
@qubes.tests.skipUnlessGit
11001187
def test_900_example_xml_in_doc(self):
11011188
self.assertXMLIsValid(

0 commit comments

Comments
 (0)