Merge branch 'windows-tools'

* windows-tools:
  doc: add info what properties are inherited from template
  Add 'gui-emulated' feature
  qvm-start-gui: fix handlign rpc-clipboard feature
This commit is contained in:
Marek Marczykowski-Górecki 2018-07-16 22:06:44 +02:00
commit 87122e54c9
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
4 changed files with 131 additions and 25 deletions

View File

@ -59,13 +59,28 @@ List of known features
gui gui
^^^ ^^^
Qube provide any kind of GUI. Setting this feature to :py:obj:`False` disable Qube has gui-agent installed. Setting this feature to :py:obj:`True` enables GUI
GUI for given qubes - both gui-agent based and emulated VGA based one. Setting based on a gui-agent installed inside the VM.
this feature to :py:obj:`True` enable gui-agent based GUI (i.e. with support of See also `gui-emulated` feature.
tools installed inside of qube). Not setting this feature at all, enable showing
VGA emulated output.
Default: show emulated VGA output only If neither `gui` nor `gui-emulated` is set, emulated VGA is used (if
applicable for given VM virtualization mode).
gui-emulated
^^^^^^^^^^^^
Qube provides GUI through emulated VGA. Setting this feature to
:py:obj:`True` enables emulated VGA output. Note that when gui-agent connects to
actual VM, emulated VGA output is closed (unless `debug` property is set to
:py:obj:`True`). It's possible to open emulated VGA output for a running qube,
regardless of this feature, using `qvm-start-gui --force-stubdomain QUBE_NAME`
command.
This feature is applicable only when qube's `virt_mode` is set to `hvm`.
See also `gui` feature.
If neither `gui` nor `gui-emulated` is set, emulated VGA is used (if
applicable for given VM virtualization mode).
qrexec qrexec
^^^^^^ ^^^^^^

View File

@ -95,6 +95,8 @@ default_user
Default user used by :manpage:`qvm-run(1)`. Note that it make sense only on Default user used by :manpage:`qvm-run(1)`. Note that it make sense only on
non-standard template, as the standard one always have "user" account. non-standard template, as the standard one always have "user" account.
TemplateBasedVM use its template's value as a default.
dispvm_allowed dispvm_allowed
Property type: bool Property type: bool
@ -117,7 +119,9 @@ kernel
Accepted values: kernel version, empty Accepted values: kernel version, empty
Kernel version to use. Setting to empty value will use bootloader installed Kernel version to use. Setting to empty value will use bootloader installed
in root volume (of VM's template) - available only for HVM in root volume (of VM's template) - available only for HVM.
TemplateBasedVM use its template's value as a default.
kernelopts kernelopts
Accepted values: string Accepted values: string
@ -128,6 +132,8 @@ kernelopts
Some helpful options (for debugging purposes): ``earlyprintk=xen``, Some helpful options (for debugging purposes): ``earlyprintk=xen``,
``init=/bin/bash`` ``init=/bin/bash``
TemplateBasedVM use its template's value as a default.
label label
Accepted values: ``red``, ``orange``, ``yellow``, ``green``, ``gray``, Accepted values: ``red``, ``orange``, ``yellow``, ``green``, ``gray``,
``blue``, ``purple``, ``black`` ``blue``, ``purple``, ``black``
@ -151,6 +157,8 @@ maxmem
qmemman disabled, this will be overridden by *memory* property (at VM qmemman disabled, this will be overridden by *memory* property (at VM
startup). startup).
TemplateBasedVM use its template's value as a default.
memory memory
Accepted values: memory size in MB Accepted values: memory size in MB
@ -158,6 +166,8 @@ memory
- before qmemman starts managing memory for this VM. For VM with qmemman - before qmemman starts managing memory for this VM. For VM with qmemman
disabled, this is static memory size. disabled, this is static memory size.
TemplateBasedVM use its template's value as a default.
name name
Accepted values: alphanumerical name Accepted values: alphanumerical name
@ -184,6 +194,8 @@ qrexec_timeout
Ignored if qrexec not installed at all (`qrexec` feature not set, see Ignored if qrexec not installed at all (`qrexec` feature not set, see
:manpage:`qvm-features(1)`). :manpage:`qvm-features(1)`).
TemplateBasedVM use its template's value as a default.
stubdom_mem stubdom_mem
Accepted values: memory in MB Accepted values: memory in MB
@ -202,12 +214,16 @@ vcpus
Number of CPU (cores) available to VM. Some VM types (eg DispVM) will not Number of CPU (cores) available to VM. Some VM types (eg DispVM) will not
work properly with more than one CPU. work properly with more than one CPU.
TemplateBasedVM use its template's value as a default.
virt_mode virt_mode
Accepted values: ``hvm``, ``pv`` Accepted values: ``hvm``, ``pv``
Virtualisation mode in VM should be started. ``hvm`` allow to install Virtualisation mode in VM should be started. ``hvm`` allow to install
operating system without Xen-specific integration. operating system without Xen-specific integration.
TemplateBasedVM use its template's value as a default.
Authors Authors
------- -------

View File

@ -87,6 +87,10 @@ class TC_00_qvm_start_gui(qubesadmin.tests.QubesTestCase):
self.app.expected_calls[ self.app.expected_calls[
('dom0', 'admin.label.Index', 'red', None)] = \ ('dom0', 'admin.label.Index', 'red', None)] = \
b'0\x001' b'0\x001'
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate',
'rpc-clipboard', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
with unittest.mock.patch.object(self.launcher, 'kde_guid_args') as \ with unittest.mock.patch.object(self.launcher, 'kde_guid_args') as \
kde_mock: kde_mock:
@ -117,6 +121,10 @@ class TC_00_qvm_start_gui(qubesadmin.tests.QubesTestCase):
self.app.expected_calls[ self.app.expected_calls[
('dom0', 'admin.label.Index', 'red', None)] = \ ('dom0', 'admin.label.Index', 'red', None)] = \
b'0\x001' b'0\x001'
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate',
'rpc-clipboard', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
with unittest.mock.patch.object(self.launcher, 'kde_guid_args') as \ with unittest.mock.patch.object(self.launcher, 'kde_guid_args') as \
kde_mock: kde_mock:
@ -131,6 +139,40 @@ class TC_00_qvm_start_gui(qubesadmin.tests.QubesTestCase):
self.assertAllCalled() self.assertAllCalled()
def test_012_common_args_rpc_clipboard(self):
self.app.expected_calls[
('dom0', 'admin.vm.List', None, None)] = \
b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.property.Get', 'label', None)] = \
b'0\x00default=False type=label red'
self.app.expected_calls[
('test-vm', 'admin.vm.property.Get', 'debug', None)] = \
b'0\x00default=False type=bool False'
self.app.expected_calls[
('dom0', 'admin.label.Get', 'red', None)] = \
b'0\x000xff0000'
self.app.expected_calls[
('dom0', 'admin.label.Index', 'red', None)] = \
b'0\x001'
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate',
'rpc-clipboard', None)] = \
b'0\x001'
with unittest.mock.patch.object(self.launcher, 'kde_guid_args') as \
kde_mock:
kde_mock.return_value = []
args = self.launcher.common_guid_args(self.app.domains['test-vm'])
self.assertEqual(args, [
'/usr/bin/qubes-guid', '-N', 'test-vm',
'-c', '0xff0000',
'-i', '/usr/share/icons/hicolor/128x128/devices/appvm-red.png',
'-l', '1', '-q', '-Q'])
self.assertAllCalled()
@unittest.mock.patch('asyncio.create_subprocess_exec') @unittest.mock.patch('asyncio.create_subprocess_exec')
def test_020_start_gui_for_vm(self, proc_mock): def test_020_start_gui_for_vm(self, proc_mock):
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
@ -186,10 +228,6 @@ class TC_00_qvm_start_gui(qubesadmin.tests.QubesTestCase):
self.app.expected_calls[ self.app.expected_calls[
('test-vm', 'admin.vm.property.Get', 'debug', None)] = \ ('test-vm', 'admin.vm.property.Get', 'debug', None)] = \
b'0\x00default=False type=bool False' b'0\x00default=False type=bool False'
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'rpc-clipboard',
None)] = \
b'0\x00True'
self.app.expected_calls[ self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', ('test-vm', 'admin.vm.feature.CheckWithTemplate',
'no-monitor-layout', None)] = \ 'no-monitor-layout', None)] = \
@ -199,7 +237,7 @@ class TC_00_qvm_start_gui(qubesadmin.tests.QubesTestCase):
loop.run_until_complete(self.launcher.start_gui_for_vm( loop.run_until_complete(self.launcher.start_gui_for_vm(
self.app.domains['test-vm'])) self.app.domains['test-vm']))
# common arguments dropped for simplicity # common arguments dropped for simplicity
proc_mock.assert_called_once_with('-d', '3000', '-n', '-Q') proc_mock.assert_called_once_with('-d', '3000', '-n')
self.assertAllCalled() self.assertAllCalled()
@ -226,10 +264,6 @@ class TC_00_qvm_start_gui(qubesadmin.tests.QubesTestCase):
self.app.expected_calls[ self.app.expected_calls[
('test-vm', 'admin.vm.property.Get', 'debug', None)] = \ ('test-vm', 'admin.vm.property.Get', 'debug', None)] = \
b'0\x00default=False type=bool False' b'0\x00default=False type=bool False'
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'rpc-clipboard',
None)] = \
b'0\x00True'
self.app.expected_calls[ self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', ('test-vm', 'admin.vm.feature.CheckWithTemplate',
'no-monitor-layout', None)] = \ 'no-monitor-layout', None)] = \
@ -252,7 +286,7 @@ class TC_00_qvm_start_gui(qubesadmin.tests.QubesTestCase):
self.app.domains['test-vm'])) self.app.domains['test-vm']))
# common arguments dropped for simplicity # common arguments dropped for simplicity
mock_proc.assert_called_once_with( mock_proc.assert_called_once_with(
'-d', '3000', '-n', '-Q', '-K', '1234') '-d', '3000', '-n', '-K', '1234')
finally: finally:
unittest.mock.patch.stopall() unittest.mock.patch.stopall()
@ -275,6 +309,43 @@ class TC_00_qvm_start_gui(qubesadmin.tests.QubesTestCase):
self.app.expected_calls[ self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'gui', None)] = \ ('test-vm', 'admin.vm.feature.CheckWithTemplate', 'gui', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00' b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'gui-emulated',
None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
proc_mock = unittest.mock.Mock()
with unittest.mock.patch('asyncio.create_subprocess_exec',
lambda *args: self.mock_coroutine(proc_mock, *args)):
with unittest.mock.patch.object(self.launcher,
'common_guid_args', lambda vm: []):
loop.run_until_complete(self.launcher.start_gui_for_stubdomain(
self.app.domains['test-vm']))
# common arguments dropped for simplicity
proc_mock.assert_called_once_with('-d', '3001', '-t', '3000')
self.assertAllCalled()
def test_031_start_gui_for_stubdomain_forced(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
self.addCleanup(loop.close)
self.app.expected_calls[
('dom0', 'admin.vm.List', None, None)] = \
b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.property.Get', 'xid', None)] = \
b'0\x00default=False type=int 3000'
self.app.expected_calls[
('test-vm', 'admin.vm.property.Get', 'stubdom_xid', None)] = \
b'0\x00default=False type=int 3001'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.feature.CheckWithTemplate', 'gui', None)] = \
# b'0\x00'
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'gui-emulated',
None)] = \
b'0\x001'
proc_mock = unittest.mock.Mock() proc_mock = unittest.mock.Mock()
with unittest.mock.patch('asyncio.create_subprocess_exec', with unittest.mock.patch('asyncio.create_subprocess_exec',
lambda *args: self.mock_coroutine(proc_mock, *args)): lambda *args: self.mock_coroutine(proc_mock, *args)):

View File

@ -167,6 +167,9 @@ class GUILauncher(object):
else: else:
guid_cmd += ['-q'] guid_cmd += ['-q']
if vm.features.check_with_template('rpc-clipboard', False):
guid_cmd.extend(['-Q'])
guid_cmd += self.kde_guid_args(vm) guid_cmd += self.kde_guid_args(vm)
return guid_cmd return guid_cmd
@ -191,9 +194,6 @@ class GUILauncher(object):
if vm.virt_mode == 'hvm': if vm.virt_mode == 'hvm':
guid_cmd.extend(['-n']) guid_cmd.extend(['-n'])
if vm.features.check_with_template('rpc-clipboard', False):
guid_cmd.extend(['-Q'])
stubdom_guid_pidfile = self.guid_pidfile(vm.stubdom_xid) stubdom_guid_pidfile = self.guid_pidfile(vm.stubdom_xid)
if not vm.debug and os.path.exists(stubdom_guid_pidfile): if not vm.debug and os.path.exists(stubdom_guid_pidfile):
# Terminate stubdom guid once "real" gui agent connects # Terminate stubdom guid once "real" gui agent connects
@ -215,9 +215,13 @@ class GUILauncher(object):
This function is a coroutine. This function is a coroutine.
''' '''
want_stubdom = force want_stubdom = force
# if no 'gui' feature set at all, assume no gui agent installed
if not want_stubdom and \ if not want_stubdom and \
vm.features.check_with_template('gui', None) is None: vm.features.check_with_template('gui-emulated', False):
want_stubdom = True
# if no 'gui' or 'gui-emulated' feature set at all, use emulated GUI
if not want_stubdom and \
vm.features.check_with_template('gui', None) is None and \
vm.features.check_with_template('gui-emulated', None) is None:
want_stubdom = True want_stubdom = True
if not want_stubdom and vm.debug: if not want_stubdom and vm.debug:
want_stubdom = True want_stubdom = True
@ -241,13 +245,13 @@ class GUILauncher(object):
:param force_stubdom: Force GUI daemon for stubdomain, even if the :param force_stubdom: Force GUI daemon for stubdomain, even if the
one for target AppVM is running. one for target AppVM is running.
''' '''
if not vm.features.check_with_template('gui', True):
return
if vm.virt_mode == 'hvm': if vm.virt_mode == 'hvm':
yield from self.start_gui_for_stubdomain(vm, yield from self.start_gui_for_stubdomain(vm,
force=force_stubdom) force=force_stubdom)
if not vm.features.check_with_template('gui', True):
return
if not os.path.exists(self.guid_pidfile(vm.xid)): if not os.path.exists(self.guid_pidfile(vm.xid)):
yield from self.start_gui_for_vm(vm, monitor_layout=monitor_layout) yield from self.start_gui_for_vm(vm, monitor_layout=monitor_layout)