Skip to content

Commit 857b54b

Browse files
committed
Preload disposables
For: QubesOS/qubes-issues#1512
1 parent 2069b1d commit 857b54b

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

qubes/api/internal.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,3 +361,65 @@ async def suspend_post(self):
361361
qubes.config.suspend_timeout,
362362
"qubes.SuspendPostAll",
363363
)
364+
365+
@qubes.api.method(
366+
"internal.dispvm.preload.Create", scope="local", write=True
367+
)
368+
async def dispvm_preload_create(self):
369+
"""
370+
Method called to create a preloaded DispVM.
371+
372+
:return:
373+
"""
374+
dispvm_template = self.arg
375+
preload_dispvm = dispvm_template.features.get("preload-dispvm", None)
376+
## TODO: should this function receive disposable name as argument
377+
## or call admin.vm.CreateDisposable?
378+
dispvm = TODO
379+
if preload_dispvm:
380+
preload_dispvm += " " + dispvm
381+
else:
382+
preload_dispvm = dispvm
383+
dispvm_template.features["preload-dispvm"] = preload_dispvm
384+
dispvm.features["internal"] = True
385+
386+
@qubes.api.method(
387+
"internal.dispvm.preload.Use", scope="local", write=True
388+
)
389+
async def dispvm_preload_use(self):
390+
"""
391+
Method called when usage of preloaded DispVM is requested.
392+
393+
To use the first preloaded disposable, provide as argument the
394+
disposable template, else, provide as argument the disposable name.
395+
396+
:return:
397+
"""
398+
used_dispvm = self.arg
399+
if used_dispvm.klass == "DispVM":
400+
dispvm_template = getattr(used_dispvm, "template")
401+
use_first = False
402+
elif (
403+
used_dispvm.klass == "AppVM"
404+
and getattr(used_dispvm, "template_for_dispvms", False)
405+
):
406+
dispvm_template = used_dispvm
407+
use_first = True
408+
else:
409+
raise qubes.exc.QubesException("Invalid argument provided")
410+
preload_dispvm = dispvm_template.features.get("preload-dispvm", None)
411+
if not preload_dispvm:
412+
return
413+
414+
preload_dispvm = preload_dispvm.split(" ")
415+
if use_first:
416+
used_dispvm_name = preload_dispvm[1:]
417+
else:
418+
used_dispvm_name = used_dispvm.name
419+
420+
unused_dispvm = preload_dispvm.remove(used_dispvm_name)
421+
dispvm_template.features["preload-dispvm"] = unused_dispvm
422+
used_dispvm.features["internal"] = False
423+
## TODO: preload next disposable with delay. Should this function
424+
## emit an event to handle the delay and creation of the next
425+
## preloaded disposable?

qubes/vm/dispvm.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,37 @@ def __init__(self, app, xml, *args, **kwargs):
187187
self.features.update(template.features)
188188
self.tags.update(template.tags)
189189

190+
def is_domain_preloaded(self):
191+
preload_dispvm = (
192+
self.features.check_with_template("preload-dispvm", None)
193+
)
194+
if not preload_dispvm:
195+
return False
196+
preload_dispvm = preload_dispvm.split(" ")
197+
if self.name not in preload_dispvm:
198+
return False
199+
return True
200+
201+
190202
@qubes.events.handler("domain-load")
191203
def on_domain_loaded(self, event):
192204
"""When domain is loaded assert that this vm has a template.""" # pylint: disable=unused-argument
193205
assert self.template
194206

207+
@qubes.events.handler("domain-started")
208+
def on_domain_started(self, event, **kwargs):
209+
"""Pause preloaded domains as soon as they start."""
210+
if self.is_domain_preloaded():
211+
self.pause()
212+
213+
@qubes.events.handler("domain-unpaused")
214+
def on_domain_unpaused(self):
215+
"""Mark unpaused preloaded domains as used."""
216+
if self.is_domain_preloaded():
217+
self.app.qubesd_call(
218+
"dom0", "internal.dispvm.preload.Use", self.name
219+
)
220+
195221
@qubes.events.handler("property-pre-reset:template")
196222
def on_property_pre_reset_template(self, event, name, oldvalue=None):
197223
"""Forbid deleting template of VM""" # pylint: disable=unused-argument
@@ -251,6 +277,15 @@ async def from_appvm(cls, appvm, **kwargs):
251277
"template_for_dispvms=False"
252278
)
253279
app = appvm.app
280+
281+
preload_dispvm = appvm.features.get("preload-dispvm", None)
282+
if preload_dispvm:
283+
dispvm = preload_dispvm.split(" ")[0]
284+
app.qubesd_call(
285+
"dom0", "internal.dispvm.preload.Use", dispvm
286+
)
287+
return dispvm
288+
254289
dispvm = app.add_new_vm(
255290
cls, template=appvm, auto_cleanup=True, **kwargs
256291
)

0 commit comments

Comments
 (0)