Skip to content

Commit 48e3c99

Browse files
committed
Switch autostart event in favor of only start
The autostart made a refresh, which cleaned all preloaded disposables, served to: - Invalidate previous incomplete remnants - Get fresh ones after a change was made to a template in chain Both of these issues were handled, "domain-load" of the disposable template invalidates incomplete ones while "domain-shutdown" of a template in chain triggers a refresh. For: QubesOS/qubes-issues#1512
1 parent 1186794 commit 48e3c99

6 files changed

Lines changed: 49 additions & 56 deletions

File tree

linux/aux-tools/preload-dispvm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ async def main():
5454
maximum = get_preload_max(qube)
5555
msg = f"{qube}:{maximum}"
5656
print(repr(msg))
57-
exec_args = qube.qubesd_call, qube.name, method, "preload-autostart"
57+
exec_args = qube.qubesd_call, qube.name, method, "preload"
5858
future = loop.run_in_executor(executor, *exec_args)
5959
tasks.append(future)
6060
await asyncio.gather(*tasks)

qubes/api/admin.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,13 +1308,13 @@ async def _vm_create(
13081308
@qubes.api.method("admin.vm.CreateDisposable", scope="global", write=True)
13091309
async def create_disposable(self, untrusted_payload):
13101310
"""
1311-
Create a disposable. If the RPC argument is ``preload-autostart``,
1311+
Create a disposable. If the RPC argument is ``preload``,
13121312
cleanse the preload list and start preloading fresh disposables.
13131313
"""
1314-
self.enforce(self.arg in [None, "", "preload-autostart"])
1315-
preload_autostart = False
1316-
if self.arg == "preload-autostart":
1317-
preload_autostart = True
1314+
self.enforce(self.arg in [None, "", "preload"])
1315+
preload = False
1316+
if self.arg == "preload":
1317+
preload = True
13181318
if untrusted_payload not in (b"", b"uuid"):
13191319
raise qubes.exc.QubesValueError(
13201320
"Invalid payload for admin.vm.CreateDisposable: "
@@ -1327,8 +1327,10 @@ async def create_disposable(self, untrusted_payload):
13271327
appvm = self.dest
13281328

13291329
self.fire_event_for_permission(dispvm_template=appvm)
1330-
if preload_autostart:
1331-
await appvm.fire_event_async("domain-preload-dispvm-autostart")
1330+
if preload:
1331+
await appvm.fire_event_async(
1332+
"domain-preload-dispvm-start", reason="autostart was requested"
1333+
)
13321334
return
13331335
dispvm = await qubes.vm.dispvm.DispVM.from_appvm(appvm)
13341336
# TODO: move this to extension (in race-free fashion, better than here)

qubes/api/internal.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,5 +413,7 @@ async def suspend_post(self):
413413
preload_templates = qubes.vm.dispvm.get_preload_templates(self.app)
414414
for qube in preload_templates:
415415
asyncio.ensure_future(
416-
qube.fire_event_async("domain-preload-dispvm-autostart")
416+
qube.fire_event_async(
417+
"domain-preload-dispvm-start", reason="system resumed"
418+
)
417419
)

qubes/tests/api_admin.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3992,7 +3992,7 @@ def test_643_vm_create_disposable_preload_autostart(
39923992
self.vm.template_for_dispvms = True
39933993
self.app.default_dispvm = self.vm
39943994
self.vm.add_handler(
3995-
"domain-preload-dispvm-autostart", self._test_event_handler
3995+
"domain-preload-dispvm-start", self._test_event_handler
39963996
)
39973997
self.vm.features["qrexec"] = "1"
39983998
self.vm.features["supported-rpc.qubes.WaitForRunningSystem"] = "1"
@@ -4005,27 +4005,23 @@ def test_643_vm_create_disposable_preload_autostart(
40054005
self.fail("didn't preload in time")
40064006
old_preload = self.vm.get_feat_preload()
40074007
retval = self.call_mgmt_func(
4008-
b"admin.vm.CreateDisposable", b"dom0", arg=b"preload-autostart"
4008+
b"admin.vm.CreateDisposable", b"dom0", arg=b"preload"
40094009
)
40104010
self.assertTrue(
40114011
self._test_event_was_handled(
4012-
self.vm.name, "domain-preload-dispvm-autostart"
4012+
self.vm.name, "domain-preload-dispvm-start"
40134013
)
40144014
)
4015-
for _ in range(10):
4016-
if (
4017-
old_preload != self.vm.get_feat_preload()
4018-
and self.vm.get_feat_preload() != []
4019-
):
4020-
break
4021-
self.loop.run_until_complete(asyncio.sleep(1))
4022-
else:
4023-
self.fail("didn't preload again in time")
4024-
dispvm_preload = self.vm.get_feat_preload()
4025-
self.assertEqual(len(dispvm_preload), 1)
4015+
self.call_mgmt_func(
4016+
b"admin.vm.CreateDisposable", b"dom0", arg=b"preload"
4017+
)
4018+
self.assertEqual(1, mock_storage_create.call_count)
4019+
new_preload = self.vm.get_feat_preload()
4020+
self.assertEqual(len(old_preload), 1)
4021+
self.assertEqual(len(new_preload), 1)
4022+
self.assertEqual(old_preload, new_preload)
40264023
self.assertIsNone(retval)
4027-
self.assertEqual(2, mock_storage_create.call_count)
4028-
self.assertEqual(2, mock_dispvm_start.call_count)
4024+
self.assertEqual(1, mock_dispvm_start.call_count)
40294025
self.assertTrue(self.app.save.called)
40304026

40314027
def test_650_vm_device_set_mode_required(self):

qubes/tests/integ/dispvm.py

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,6 @@ def _test_event_was_handled(self, vm, event):
285285
def _register_handlers(self, vm): # pylint: disable=unused-argument
286286
events = [
287287
# appvm
288-
"domain-preload-dispvm-autostart",
289288
"domain-preload-dispvm-start",
290289
"domain-preload-dispvm-used",
291290
# dispvm
@@ -333,7 +332,9 @@ def cleanup_preload(self):
333332
self.disp_base_alt,
334333
]:
335334
continue
336-
logger.info("removing preloaded disposables: '%s'", qube.name)
335+
logger.info(
336+
"removing preloaded disposables configured in: '%s'", qube.name
337+
)
337338
target = qube
338339
if qube.klass == "AdminVM" and default_dispvm:
339340
target = default_dispvm
@@ -453,7 +454,6 @@ async def run_preload(self):
453454
stdout = await self.run_preload_proc()
454455
self.assertEqual(stdout, dispvm_name)
455456
test_cases = [
456-
(False, appvm.name, "domain-preload-dispvm-autostart", True),
457457
(False, appvm.name, "domain-preload-dispvm-start", True),
458458
(True, appvm.name, "domain-preload-dispvm-used", True),
459459
(
@@ -637,15 +637,12 @@ async def _test_016_preload_race_less(self):
637637
logger.info("end")
638638

639639
def test_017_preload_autostart(self):
640-
"""The script triggers the API call
641-
'admin.vm.CreateDisposable+preload-autostart' which fires the event
642-
'domain-preload-dispvm-autostart', clearing the current preload list
643-
and filling with new ones."""
640+
"""The script triggers the API call 'admin.vm.CreateDisposable+preload'
641+
which is responsible for bootstrapping."""
644642
logger.info("start")
645643
self.app.default_dispvm = self.disp_base
646644

647-
preload_max = 1
648-
logger.info("no refresh to be made")
645+
logger.info("no refill to be made: max is 0")
649646
proc = self.loop.run_until_complete(
650647
asyncio.create_subprocess_exec("/usr/lib/qubes/preload-dispvm")
651648
)
@@ -654,38 +651,40 @@ def test_017_preload_autostart(self):
654651
)
655652
self.assertEqual(self.disp_base.get_feat_preload(), [])
656653

657-
logger.info("refresh to be made")
654+
preload_max = 1
655+
logger.info("refill to be made: max is %s", preload_max)
658656
self.disp_base.features["preload-dispvm-max"] = str(preload_max)
659657
self.loop.run_until_complete(self.wait_preload(preload_max))
660658
old_preload = self.disp_base.get_feat_preload()
661659
proc = self.loop.run_until_complete(
662660
asyncio.create_subprocess_exec("/usr/lib/qubes/preload-dispvm")
663661
)
664-
self.loop.run_until_complete(asyncio.wait_for(proc.wait(), timeout=40))
662+
self.loop.run_until_complete(asyncio.wait_for(proc.wait(), timeout=10))
665663
preload_dispvm = self.disp_base.get_feat_preload()
666-
self.assertEqual(len(old_preload), preload_max)
667-
self.assertEqual(len(preload_dispvm), preload_max)
668-
self.assertTrue(
669-
set(old_preload).isdisjoint(preload_dispvm),
670-
f"old_preload={old_preload} preload_dispvm={preload_dispvm}",
664+
self.assertEqual(
665+
old_preload,
666+
preload_dispvm,
667+
msg=f"old_preload={old_preload} preload_dispvm={preload_dispvm}",
671668
)
672669

673-
logger.info("global refresh to be made")
674670
preload_max += 1
671+
logger.info("global refill to be made: max is %s", preload_max)
675672
self.adminvm.features["preload-dispvm-max"] = str(preload_max)
676673
self.loop.run_until_complete(self.wait_preload(preload_max))
677674
del self.disp_base.features["preload-dispvm-max"]
678675
old_preload = self.disp_base.get_feat_preload()
676+
self.loop.run_until_complete(self.cleanup_preload_run(self.disp_base))
679677
proc = self.loop.run_until_complete(
680678
asyncio.create_subprocess_exec("/usr/lib/qubes/preload-dispvm")
681679
)
682680
self.loop.run_until_complete(asyncio.wait_for(proc.wait(), timeout=40))
683681
preload_dispvm = self.disp_base.get_feat_preload()
684682
self.assertEqual(len(old_preload), preload_max)
685683
self.assertEqual(len(preload_dispvm), preload_max)
686-
self.assertTrue(
687-
set(old_preload).isdisjoint(preload_dispvm),
688-
f"old_preload={old_preload} preload_dispvm={preload_dispvm}",
684+
self.assertEqual(
685+
old_preload,
686+
preload_dispvm,
687+
msg=f"old_preload={old_preload} preload_dispvm={preload_dispvm}",
689688
)
690689

691690
self.app.default_dispvm = None

qubes/vm/mix/dvmtemplate.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def on_domain_loaded(self, event) -> None:
6363
# pylint: disable=unused-argument
6464
assert isinstance(self, qubes.vm.BaseVM)
6565
changes = False
66-
# Preloading began and host rebooted and autostart event didn't run yet.
66+
# Began preloading, host rebooted, autostart script didn't run yet.
6767
old_preload = self.get_feat_preload()
6868
clean_preload = old_preload.copy()
6969
for unwanted_disp in old_preload:
@@ -411,7 +411,6 @@ def __on_property_set_template(
411411

412412
@qubes.events.handler(
413413
"domain-preload-dispvm-used",
414-
"domain-preload-dispvm-autostart",
415414
"domain-preload-dispvm-start",
416415
)
417416
async def on_domain_preload_dispvm_used(
@@ -423,14 +422,11 @@ async def on_domain_preload_dispvm_used(
423422
**kwargs, # pylint: disable=unused-argument
424423
) -> None:
425424
"""
426-
Offloads on excess and preload on vacancy. On ``autostart``, the
427-
preloaded list is emptied before preloading.
425+
Offloads on excess and preload on vacancy.
428426
429427
:param str event: Event which was fired. Events have the prefix \
430-
``domain-preload-dispvm-``. If the suffix is ``autostart``, the \
431-
preload list is emptied before attempting to preload. If the \
432-
suffix is ``used`` or ``start``, tries to preload until it fills \
433-
gaps.
428+
``domain-preload-dispvm-``. It always tries to preload until it \
429+
fills the gaps if there is enough memory.
434430
:param qubes.vm.dispvm.DispVM dispvm: Disposable that was used
435431
:param str reason: Why the event was fired
436432
:param float delay: Proceed only after sleeping that many seconds
@@ -454,9 +450,7 @@ async def on_domain_preload_dispvm_used(
454450
if delay:
455451
await asyncio.sleep(delay)
456452

457-
if event == "autostart":
458-
self.remove_preload_excess(0, reason="event autostart was called")
459-
elif not self.can_preload():
453+
if not self.can_preload():
460454
self.remove_preload_excess(reason="there may be absent qubes")
461455
# Absent qubes might be removed above.
462456
if not self.can_preload():

0 commit comments

Comments
 (0)