Skip to content

Commit 1a63c89

Browse files
committed
Merge remote-tracking branch 'origin/pr/359' into pr-359-merge
* origin/pr/359: Late GUID for preloaded disposables
2 parents ced1d2f + d8dedb2 commit 1a63c89

File tree

4 files changed

+228
-28
lines changed

4 files changed

+228
-28
lines changed

doc/manpages/qvm-features.rst

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -429,10 +429,10 @@ on it.
429429
preload-dispvm-max
430430
^^^^^^^^^^^^^^^^^^
431431

432-
Number of disposables to preload. Upon setting, the number of running preloaded
433-
disposables will be adjusted to match the maximum configured, if there is not
434-
enough of them and there is enough available memory on the system, new ones will
435-
be created, if there are more than enough, the excess will be removed.
432+
Number of disposables to preload. Upon setting, the quantity of running
433+
preloaded disposables will be adjusted to match the maximum configured, if there
434+
is not enough of them and there is enough available memory on the system, new
435+
ones will be created, if there are more than enough, the excess will be removed.
436436

437437
|
438438
| **Valid on**: disposable template
@@ -455,12 +455,6 @@ disposable in the list. As soon as the preloaded disposable is requested to be
455455
used, it is removed from the `preload-dispvm` list, GUI applications entries
456456
become visible, followed by a new disposable being preloaded.
457457

458-
.. warning::
459-
460-
Applications configured to autostart by the disposable template or the
461-
template itself will be interactive before the preloaded disposable can be
462-
paused.
463-
464458
|
465459
| **Managed by**: system
466460
| **Valid on**: disposable template

qubesadmin/tests/mock_app.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -986,12 +986,19 @@ def __init__(self):
986986
self._qubes["default-dvm"] = MockQube(
987987
name="default-dvm",
988988
qapp=self,
989-
klass="DispVM",
990-
template_for_dispvms="True",
989+
klass="AppVM",
990+
template_for_dispvms=True,
991991
template="fedora-36",
992992
features={"appmenus-dispvm": "1"},
993993
)
994994

995+
self._qubes["test-disp"] = MockQube(
996+
name="test-disp",
997+
qapp=self,
998+
klass="DispVM",
999+
template="default-dvm",
1000+
)
1001+
9951002
self._qubes["test-vm"] = MockQube(
9961003
name="test-vm",
9971004
qapp=self,

qubesadmin/tests/tools/qvm_start_daemon.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ def test_020_start_gui_for_vm(self, proc_mock):
231231
self.app.expected_calls[
232232
('test-vm', 'admin.vm.property.Get', 'virt_mode', None)] = \
233233
b'0\x00default=False type=str pv'
234+
self.app.expected_calls[
235+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
236+
b'2\x00QubesNoSuchPropertyError\x00\x00Invalid property ' \
237+
b"'is_preload' of test-vm\x00"
234238
self.app.expected_calls[
235239
('test-vm', 'admin.vm.feature.CheckWithTemplate',
236240
'no-monitor-layout', None)] = \
@@ -272,6 +276,10 @@ def test_021_start_gui_for_vm_hvm(self, proc_mock):
272276
self.app.expected_calls[
273277
('test-vm', 'admin.vm.property.Get', 'debug', None)] = \
274278
b'0\x00default=False type=bool False'
279+
self.app.expected_calls[
280+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
281+
b'2\x00QubesNoSuchPropertyError\x00\x00Invalid property ' \
282+
b"'is_preload' of test-vm\x00"
275283
self.app.expected_calls[
276284
('test-vm', 'admin.vm.feature.CheckWithTemplate',
277285
'no-monitor-layout', None)] = \
@@ -312,6 +320,10 @@ def test_022_start_gui_for_vm_hvm_stubdom(self):
312320
self.app.expected_calls[
313321
('test-vm', 'admin.vm.property.Get', 'debug', None)] = \
314322
b'0\x00default=False type=bool False'
323+
self.app.expected_calls[
324+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
325+
b'2\x00QubesNoSuchPropertyError\x00\x00Invalid property ' \
326+
b"'is_preload' of test-vm\x00"
315327
self.app.expected_calls[
316328
('test-vm', 'admin.vm.feature.CheckWithTemplate',
317329
'no-monitor-layout', None)] = \
@@ -360,6 +372,10 @@ def test_030_start_gui_for_stubdomain(self):
360372
self.app.expected_calls[
361373
('test-vm', 'admin.vm.property.Get', 'stubdom_xid', None)] = \
362374
b'0\x00default=False type=int 3001'
375+
self.app.expected_calls[
376+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
377+
b'2\x00QubesNoSuchPropertyError\x00\x00Invalid property ' \
378+
b"'is_preload' of test-vm\x00"
363379
self.app.expected_calls[
364380
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'gui', None)] = \
365381
b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
@@ -391,6 +407,10 @@ def test_031_start_gui_for_stubdomain_forced(self):
391407
self.app.expected_calls[
392408
('test-vm', 'admin.vm.property.Get', 'stubdom_xid', None)] = \
393409
b'0\x00default=False type=int 3001'
410+
self.app.expected_calls[
411+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
412+
b'2\x00QubesNoSuchPropertyError\x00\x00Invalid property ' \
413+
b"'is_preload' of test-vm\x00"
394414
# self.app.expected_calls[
395415
# ('test-vm', 'admin.vm.feature.CheckWithTemplate', 'gui', None)] = \
396416
# b'0\x00'
@@ -411,6 +431,128 @@ def test_031_start_gui_for_stubdomain_forced(self):
411431
async def mock_coroutine(self, mock, *args, **kwargs):
412432
mock(*args, **kwargs)
413433

434+
def test_038_start_gui_skip_preload(self):
435+
loop = asyncio.new_event_loop()
436+
asyncio.set_event_loop(loop)
437+
self.addCleanup(loop.close)
438+
439+
self.app.expected_calls[
440+
('dom0', 'admin.vm.List', None, None)] = \
441+
b'0\x00test-disp class=DispVM state=Running\n' \
442+
b'gui-vm class=AppVM state=Running'
443+
self.app.expected_calls[
444+
('test-disp', 'admin.vm.CurrentState', None, None)] = \
445+
b'0\x00power_state=Running'
446+
self.app.expected_calls[
447+
('test-disp', 'admin.vm.feature.CheckWithTemplate', 'gui', None)] = \
448+
b'0\x00True'
449+
self.app.expected_calls[
450+
('test-disp', 'admin.vm.feature.CheckWithTemplate',
451+
'no-monitor-layout', None)] = \
452+
b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
453+
self.app.expected_calls[
454+
('test-disp', 'admin.vm.property.Get', 'virt_mode', None)] = \
455+
b'0\x00default=False type=str hvm'
456+
self.app.expected_calls[
457+
('test-disp', 'admin.vm.property.Get', 'xid', None)] = \
458+
b'0\x00default=False type=int 3000'
459+
self.app.expected_calls[
460+
('test-disp', 'admin.vm.property.Get', 'stubdom_xid', None)] = \
461+
b'0\x00default=False type=int 3001'
462+
self.app.expected_calls[
463+
('test-disp', 'admin.vm.property.Get', 'guivm', None)] = \
464+
b'0\x00default=False type=vm gui-vm'
465+
self.app.expected_calls[
466+
('test-disp', 'admin.vm.property.Set', 'is_preload', b'True')] = \
467+
b'0\x00default=False type=vm gui-vm'
468+
self.app.expected_calls[
469+
('test-disp', 'admin.vm.property.Get', 'is_preload', None)] = \
470+
b'0\x00default=False type=bool True'
471+
472+
# pylint: disable=protected-access
473+
self.app._local_name = 'gui-vm'
474+
vm = self.app.domains['test-disp']
475+
vm.is_preload = True
476+
mock_start_vm = unittest.mock.Mock()
477+
mock_start_stubdomain = unittest.mock.Mock()
478+
patch_start_vm = unittest.mock.patch.object(
479+
self.launcher, 'start_gui_for_vm', functools.partial(
480+
self.mock_coroutine, mock_start_vm))
481+
patch_start_stubdomain = unittest.mock.patch.object(
482+
self.launcher, 'start_gui_for_stubdomain', lambda vm_, force:
483+
self.mock_coroutine(mock_start_stubdomain, vm_))
484+
try:
485+
patch_start_vm.start()
486+
patch_start_stubdomain.start()
487+
loop.run_until_complete(self.launcher.start_gui(vm))
488+
mock_start_vm.assert_not_called()
489+
mock_start_stubdomain.assert_not_called()
490+
finally:
491+
unittest.mock.patch.stopall()
492+
493+
def test_039_start_gui_for_preload(self):
494+
loop = asyncio.new_event_loop()
495+
asyncio.set_event_loop(loop)
496+
self.addCleanup(loop.close)
497+
498+
self.app.expected_calls[
499+
('dom0', 'admin.vm.List', None, None)] = \
500+
b'0\x00test-disp class=DispVM state=Running\n' \
501+
b'gui-vm class=AppVM state=Running'
502+
self.app.expected_calls[
503+
('test-disp', 'admin.vm.CurrentState', None, None)] = \
504+
b'0\x00power_state=Running'
505+
self.app.expected_calls[
506+
('test-disp', 'admin.vm.feature.CheckWithTemplate', 'gui', None)] = \
507+
b'0\x00True'
508+
self.app.expected_calls[
509+
('test-disp', 'admin.vm.feature.CheckWithTemplate',
510+
'no-monitor-layout', None)] = \
511+
b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
512+
self.app.expected_calls[
513+
('test-disp', 'admin.vm.property.Get', 'virt_mode', None)] = \
514+
b'0\x00default=False type=str hvm'
515+
self.app.expected_calls[
516+
('test-disp', 'admin.vm.property.Get', 'xid', None)] = \
517+
b'0\x00default=False type=int 3000'
518+
self.app.expected_calls[
519+
('test-disp', 'admin.vm.property.Get', 'stubdom_xid', None)] = \
520+
b'0\x00default=False type=int 3001'
521+
self.app.expected_calls[
522+
('test-disp', 'admin.vm.property.Get', 'guivm', None)] = \
523+
b'0\x00default=False type=vm gui-vm'
524+
self.app.expected_calls[
525+
('test-disp', 'admin.vm.property.Get', 'is_preload', None)] = \
526+
b"2\x00QubesNoSuchPropertyError\x00\x00Invalid property " \
527+
b"'is_preload' of test-disp\x00"
528+
529+
# pylint: disable=protected-access
530+
self.app._local_name = 'gui-vm'
531+
vm = self.app.domains['test-disp']
532+
533+
with \
534+
unittest.mock.patch('asyncio.ensure_future'), \
535+
unittest.mock.patch.object(
536+
self.launcher, 'common_guid_args', lambda vm: []
537+
), \
538+
unittest.mock.patch.object(
539+
qubesadmin.tools.qvm_start_daemon,
540+
'get_monitor_layout',
541+
unittest.mock.Mock(return_value=None)
542+
), \
543+
unittest.mock.patch.object(
544+
self.launcher, 'start_gui', unittest.mock.Mock()
545+
) as mock_start, \
546+
unittest.mock.patch.object(
547+
self.launcher, 'start_audio', unittest.mock.Mock()
548+
) as mock_start_audio:
549+
# Execute and validate
550+
self.launcher.on_property_preload_set(
551+
vm, 'property-reset:is_preload'
552+
)
553+
mock_start_audio.assert_called_once_with(vm)
554+
mock_start.assert_called_once_with(vm, monitor_layout=None)
555+
414556
def test_040_start_gui(self):
415557
loop = asyncio.new_event_loop()
416558
asyncio.set_event_loop(loop)
@@ -442,6 +584,10 @@ def test_040_start_gui(self):
442584
self.app.expected_calls[
443585
('test-vm', 'admin.vm.property.Get', 'guivm', None)] = \
444586
b'0\x00default=False type=vm gui-vm'
587+
self.app.expected_calls[
588+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
589+
b'2\x00QubesNoSuchPropertyError\x00\x00Invalid property ' \
590+
b"'is_preload' of test-vm\x00"
445591

446592
# pylint: disable=protected-access
447593
self.app._local_name = 'gui-vm'
@@ -562,6 +708,10 @@ def test_060_send_monitor_layout(self):
562708
('test-vm', 'admin.vm.feature.CheckWithTemplate',
563709
'no-monitor-layout', None)] = \
564710
b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
711+
self.app.expected_calls[
712+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
713+
b'2\x00QubesNoSuchPropertyError\x00\x00Invalid property ' \
714+
b"'is_preload' of test-vm\x00"
565715

566716
vm = self.app.domains['test-vm']
567717
mock_run_service = unittest.mock.Mock(spec={})
@@ -649,6 +799,10 @@ def test_063_send_monitor_layout_signal_existing(self):
649799
self.app.expected_calls[
650800
('test-vm', 'admin.vm.property.Get', 'stubdom_xid', None)] = \
651801
b'0\x00default=False type=int 124'
802+
self.app.expected_calls[
803+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
804+
b'2\x00QubesNoSuchPropertyError\x00\x00Invalid property ' \
805+
b"'is_preload' of test-vm\x00"
652806
self.app.expected_calls[
653807
('test-vm', 'admin.vm.feature.CheckWithTemplate',
654808
'no-monitor-layout', None)] = \
@@ -691,6 +845,14 @@ def test_070_send_monitor_layout_all(self):
691845
b'test-vm3 class=AppVM state=Running\n' \
692846
b'test-vm4 class=AppVM state=Halted\n' \
693847
b'gui-vm class=AppVM state=Running'
848+
self.app.expected_calls[
849+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
850+
b'2\x00QubesNoSuchPropertyError\x00\x00Invalid property ' \
851+
b"'is_preload' of test-vm\x00"
852+
self.app.expected_calls[
853+
('test-vm2', 'admin.vm.property.Get', 'is_preload', None)] = \
854+
b'2\x00QubesNoSuchPropertyError\x00\x00Invalid property ' \
855+
b"'is_preload' of test-vm2\x00"
694856
self.app.expected_calls[
695857
('test-vm', 'admin.vm.CurrentState', None, None)] = \
696858
b'0\x00power_state=Running'

0 commit comments

Comments
 (0)