mirror of
https://github.com/QubesOS/qubes-doc.git
synced 2024-12-18 04:04:39 -05:00
143 lines
4.4 KiB
ReStructuredText
143 lines
4.4 KiB
ReStructuredText
|
=============================
|
||
|
Developing Qubes OS GUI tools
|
||
|
=============================
|
||
|
|
||
|
|
||
|
In order to avoid installing Qubes OS frontend tools you are working on
|
||
|
in your own ``dom0`` or just to test them with less problems, you can
|
||
|
use the mock Qubes object from the ``qubesadmin`` package.
|
||
|
|
||
|
Running programs using mock Qubes object
|
||
|
----------------------------------------
|
||
|
|
||
|
|
||
|
Where you would normally provide the Qubes object, use the
|
||
|
``qubesadmin.tests.mock_app`` package and one of the mock Qubes objects
|
||
|
from it.
|
||
|
|
||
|
For example, the following code can be used to run the ``qui-domains``
|
||
|
tool using the mock Qubes object (this code would replace the initial
|
||
|
part of the main function):
|
||
|
|
||
|
.. code:: python
|
||
|
|
||
|
def main():
|
||
|
''' main function '''
|
||
|
# qapp = qubesadmin.Qubes()
|
||
|
# dispatcher = qubesadmin.events.EventsDispatcher(qapp)
|
||
|
# stats_dispatcher = qubesadmin.events.EventsDispatcher(qapp, api_method='admin.vm.Stats')
|
||
|
|
||
|
import qubesadmin.tests.mock_app as mock_app
|
||
|
qapp = mock_app.MockQubesComplete()
|
||
|
dispatcher = mock_app.MockDispatcher(qapp)
|
||
|
stats_dispatcher = mock_app.MockDispatcher(
|
||
|
qapp, api_method='admin.vm.Stats')
|
||
|
|
||
|
# continue as normal
|
||
|
|
||
|
|
||
|
To run a mocked program without installing it in a qube, remember to
|
||
|
extend PYTHONPATH appropriately, for example:
|
||
|
|
||
|
.. code:: bash
|
||
|
|
||
|
~/qubes-sources/manager $ PYTHONPATH=../core-admin-client:. python3 qui/tray/domains.py
|
||
|
|
||
|
|
||
|
The mock object does not provide events (yet).
|
||
|
|
||
|
Note: in order to see all qubes-relevant icons (like VM icons), install
|
||
|
the ``qubes-artwork`` package.
|
||
|
|
||
|
How does it actually work
|
||
|
-------------------------
|
||
|
|
||
|
|
||
|
The mock Qubes object has a collection of expected Qubes RPC calls and
|
||
|
the responses that a real system would provide. Writing these calls
|
||
|
manually is a bit tedious, given that most frontend tools query a lot of
|
||
|
qube properties. For example, on a medium-sized system, initializing
|
||
|
Qube Manager involves about 300 separate RPC calls.
|
||
|
|
||
|
If you need more calls, you can add them to the mock object using the
|
||
|
following syntax (the following example adds listing available vm
|
||
|
kernels):
|
||
|
|
||
|
.. code:: python
|
||
|
|
||
|
mock_app.expected_calls[('dom0', 'admin.pool.volume.List', 'linux-kernel', None)] = \
|
||
|
b'0\x006.1.57-1.fc37\n6.1.43-1.fc37\ncustom_kernel\n'
|
||
|
|
||
|
|
||
|
If error should be thrown, you need to provide the error code and name,
|
||
|
for example:
|
||
|
|
||
|
.. code:: python
|
||
|
|
||
|
mock_app.expected_calls[("vmname", "admin.vm.property.Get", "property_name", None)] = \
|
||
|
b'2\x00QubesNoSuchPropertyError\x00\x00No such property\x00'
|
||
|
|
||
|
|
||
|
For details of particular calls, you can use `Extending the mock Qubes object <#extending-the-mock-qubes-object>`__.
|
||
|
|
||
|
Available mocks
|
||
|
---------------
|
||
|
|
||
|
|
||
|
Three mocks are available in the ``mock_app`` file:
|
||
|
|
||
|
- MockQubes, an extremely bare-bones Qubes testing instance, with just
|
||
|
dom0, sys-net, and one template (fedora-36).
|
||
|
|
||
|
- MockQubesComplete, a more complex setup |Qubes Manager running
|
||
|
MockQubesComplete|
|
||
|
|
||
|
- MockQubesWhonix, the setup above extended with several Whonix-related
|
||
|
qubes
|
||
|
|
||
|
|
||
|
|
||
|
Extending the mock Qubes object
|
||
|
-------------------------------
|
||
|
|
||
|
|
||
|
To collect information to modify this script, you can use the wrapper
|
||
|
function to wrap and output all qubesd calls used by a program running
|
||
|
on a live qubes instance.
|
||
|
|
||
|
.. code:: python
|
||
|
|
||
|
qapp = qubesadmin.Qubes()
|
||
|
import qubesadmin.tests.mock_app as mock_app
|
||
|
qapp.qubesd_call = mock_app.wrapper(qapp.qubesd_call)
|
||
|
qapp._parse_qubesd_response = mock_app.wrapper(qapp._parse_qubesd_response)
|
||
|
|
||
|
|
||
|
Writing tests
|
||
|
-------------
|
||
|
|
||
|
|
||
|
The same mock Qubes can also be used to write tests. You can use the
|
||
|
wrappers above to check which calls are made when certain actions are
|
||
|
performed, and add them to the mock objects in the following way:
|
||
|
|
||
|
.. code:: python
|
||
|
|
||
|
# this is an excerpt from tests for Qubes Global Config tool
|
||
|
clockvm_combo.set_active_id('test-blue')
|
||
|
|
||
|
mock_qapp.expected_calls[('dom0', 'admin.property.Set',
|
||
|
'clockvm', b'test-blue')] = b'0\x00'
|
||
|
basics_handler.save()
|
||
|
|
||
|
|
||
|
If the call is made correctly, the test will continue successfully; if
|
||
|
an unexpected call is made, the test will fail.
|
||
|
|
||
|
Caution: the mock Qubes object does not react to changes like a normal
|
||
|
Qubes object does. Further queries to the test object will continue to
|
||
|
return initial values.
|
||
|
|
||
|
.. |Qubes Manager running MockQubesComplete| image:: /attachment/doc/doc-mock-app-ex1.png
|
||
|
|