2014-11-21 12:34:27 +01:00
|
|
|
:py:mod:`qubes` -- Common concepts
|
|
|
|
==================================
|
2014-11-13 18:10:27 +01:00
|
|
|
|
2015-01-22 19:25:00 +01:00
|
|
|
Global Qubes object
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
Because all objects in Qubes' world are interconnected, there is no possibility
|
|
|
|
to instantiate them separately. They are all loaded together and contained in
|
|
|
|
the one ``app`` object, an instance of :py:class:`qubes.Qubes` class.
|
|
|
|
|
2018-06-12 02:28:58 +02:00
|
|
|
Loading
|
|
|
|
^^^^^^^
|
|
|
|
|
|
|
|
The objects may come to existence in two ways: by explicit instantiation or by
|
|
|
|
loading from XML file.
|
|
|
|
|
2015-01-22 19:25:00 +01:00
|
|
|
The loading from XML is done in stages, because Qubes domains are dependent on
|
|
|
|
each other in what can be even a circular dependency. Therefore some properties
|
2018-06-12 02:28:58 +02:00
|
|
|
(especcialy those that refer to another domains) are loaded later.
|
|
|
|
|
|
|
|
.. image:: loading.svg
|
|
|
|
|
|
|
|
Refer to :py:class:`qubes.Qubes` class documentation to get description of every
|
|
|
|
stage.
|
2015-01-22 19:25:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
Properties
|
|
|
|
----------
|
|
|
|
|
|
|
|
Many parameters of Qubes can be changed -- from names of particular domains to
|
|
|
|
default NetVM for all AppVMs. All of those *configurable* parameters are called
|
|
|
|
*properties* and can be accessed like Python attributes on their owners::
|
|
|
|
|
|
|
|
>>> import qubes
|
|
|
|
>>> app = qubes.Qubes()
|
|
|
|
>>> app.domain[0] # docutils: +ELLIPSIS
|
|
|
|
<AdminVM ...>
|
|
|
|
>>> app.domain[0].name
|
|
|
|
'dom0'
|
|
|
|
|
|
|
|
Definition
|
|
|
|
^^^^^^^^^^
|
|
|
|
Properties are defined on global :py:class:`qubes.Qubes` application object and
|
|
|
|
on every domain. Those classess inherit from :py:class:`PropertyHolder` class,
|
|
|
|
which is responsible for operation of properties.
|
|
|
|
|
|
|
|
Each Qubes property is actually a *data descriptor* (a Python term), which means
|
|
|
|
they are attributes of their classess, but when trying to access it from
|
|
|
|
*instance*, they return it's underlying value instead. They can be thought of as
|
|
|
|
Python's builtin :py:class:`property`, but greatly enhanced. They are defined in
|
|
|
|
definition of their class::
|
|
|
|
|
|
|
|
>>> import qubes
|
|
|
|
>>> class MyTestHolder(qubes.PropertyHolder):
|
|
|
|
>>> testprop = qubes.property('testprop')
|
|
|
|
>>> instance = MyTestHolder()
|
|
|
|
>>> instance.testprop = 'aqq'
|
|
|
|
>>> instance.testprop
|
|
|
|
'aqq'
|
|
|
|
|
|
|
|
If you like to access some attributes of the property *itself*, you should refer
|
|
|
|
to instance's class::
|
|
|
|
|
|
|
|
>>> import qubes
|
|
|
|
>>> class MyTestHolder(qubes.PropertyHolder):
|
|
|
|
>>> testprop = qubes.property('testprop')
|
|
|
|
>>> instance = MyTestHolder()
|
|
|
|
>>> instance.testprop = 'aqq'
|
|
|
|
|
|
|
|
>>> type(instance.testprop)
|
|
|
|
<type 'str'>
|
|
|
|
>>> type(instance.__class__.testprop)
|
|
|
|
<class 'qubes.property'>
|
|
|
|
|
|
|
|
>>> instance.__class__.testprop.__name__
|
|
|
|
'testprop'
|
|
|
|
|
|
|
|
As a rule, properties are intended to be serialised and deserialised to/from XML
|
|
|
|
file. There are very few exceptions, but if you don't intend to save the
|
|
|
|
property to XML, you should generally go for builtin :py:class:`property`.
|
|
|
|
|
|
|
|
One important difference from builtin properties is that there is no getter
|
|
|
|
function, only setter. In other words, they are not dynamic, you cannot return
|
|
|
|
different value every time something wants to access it. This is to ensure that
|
|
|
|
while saving the property is not a moving target.
|
|
|
|
|
|
|
|
|
|
|
|
Property's properties
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
You can specify some parameters while defining the property. The most important
|
|
|
|
is the `type`: on setting, the value is coerced to this type. It is well suited
|
|
|
|
to builtin types like :py:class:`int`::
|
|
|
|
|
|
|
|
>>> import qubes
|
|
|
|
>>> class MyTestHolder(qubes.PropertyHolder):
|
|
|
|
>>> testprop = qubes.property('testprop')
|
|
|
|
>>> intprop = qubes.property('intprop', type=int)
|
|
|
|
|
|
|
|
>>> instance = MyTestHolder()
|
|
|
|
>>> instance.testprop = '123'
|
|
|
|
>>> instance.intprop = '123'
|
|
|
|
|
|
|
|
>>> instance.testprop
|
|
|
|
'123'
|
|
|
|
>>> instance.intprop
|
|
|
|
123
|
|
|
|
|
|
|
|
|
|
|
|
Every property should be documented. You should add a short description to your
|
|
|
|
property, which will appear, among others, in :program:`qvm-prefs` and
|
|
|
|
:program:`qvm-ls` programs. It should not use any Sphinx-specific markup::
|
|
|
|
|
|
|
|
>>> import qubes
|
|
|
|
>>> class MyTestHolder(qubes.PropertyHolder):
|
|
|
|
>>> testprop = qubes.property('testprop',
|
|
|
|
>>> doc='My new and shiny property.')
|
|
|
|
>>> MyTestHolder.testprop.__doc__
|
|
|
|
'My new and shiny property.'
|
|
|
|
|
|
|
|
|
|
|
|
In addition to `type`, properties also support `setter` parameter. It acts
|
|
|
|
similar to `type`, but is always executed (not only when types don't agree) and
|
|
|
|
accepts more parameters: `self`, `prop` and `value` being respectively: owners'
|
|
|
|
instance, property's instance and the value being set. There is also `saver`,
|
|
|
|
which does reverse: given value of the property it should return a string that
|
|
|
|
can be parsed by `saver`.
|
|
|
|
|
|
|
|
|
|
|
|
Unset properties and default values
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Properties may be unset, even if they are defined (that is, on access they raise
|
|
|
|
:py:exc:`AttributeError` -- that is the normal Python way to tell that the
|
|
|
|
attribute is absent). You can manually unset a property using Python's ``del``
|
|
|
|
statement::
|
|
|
|
|
|
|
|
>>> import qubes
|
|
|
|
>>> class MyTestHolder(qubes.PropertyHolder):
|
|
|
|
>>> testprop = qubes.property('testprop')
|
|
|
|
>>> instance = MyTestHolder()
|
|
|
|
>>> instance.testprop
|
|
|
|
AttributeError: ...
|
|
|
|
>>> instance.testprop = 123
|
|
|
|
>>> instance.testprop
|
|
|
|
123
|
|
|
|
>>> del instance.testprop
|
|
|
|
>>> instance.testprop
|
|
|
|
AttributeError: ...
|
|
|
|
|
|
|
|
Alternatively, some properties may return some other value instead, if that's
|
|
|
|
the reasonable thing to do. For example, when
|
|
|
|
:py:attr:`qubes.vm.qubesvm.QubesVM.netvm` is unset, we check global setting
|
|
|
|
:py:attr:`qubes.Qubes.default_netvm` instead. Returning :py:obj:`None` as
|
|
|
|
default would be wrong, as it is marker that means „no NetVM, machine
|
|
|
|
disconnected”.
|
|
|
|
|
|
|
|
You can define a default value either as constant or as a callable. In the
|
|
|
|
second case, the callable should accept one argument, the instance that owns the
|
|
|
|
property::
|
|
|
|
|
|
|
|
>>> import qubes
|
|
|
|
>>> class MyTestHolder(qubes.PropertyHolder):
|
|
|
|
>>> testprop = qubes.property('testprop')
|
|
|
|
>>> def1prop = qubes.property('testprop', default=123)
|
|
|
|
>>> netvm = qubes.property('testprop',
|
|
|
|
>>> default=(lambda self: self.app.default_netvm))
|
|
|
|
|
|
|
|
>>> instance = MyTestHolder()
|
|
|
|
>>> instance.testprop
|
|
|
|
AttributeError: ...
|
|
|
|
>>> instance.def1prop
|
|
|
|
123
|
|
|
|
>>> instance.netvm # doctest: +SKIP
|
|
|
|
<NetVM ...>
|
|
|
|
|
|
|
|
|
|
|
|
Setting netvm on particular domain of course does not affect global default, but
|
|
|
|
only this instance. But there are two problems:
|
|
|
|
|
|
|
|
- You don't know if the value of the property you just accessed was it's
|
|
|
|
true or default value.
|
|
|
|
- After ``del``'ing a property, you still will get a value on access. You
|
|
|
|
cannot count on `AttributeError` raised from them.
|
|
|
|
|
|
|
|
Therefore Qubes support alternative semantics. You can (and probably should,
|
|
|
|
wherever applicable) use no ``del``, but assignment of special magic object
|
|
|
|
:py:obj:`qubes.property.DEFAULT`. There is also method
|
|
|
|
:py:meth:`qubes.PropertyHolder.property_is_default`, which can be used to
|
|
|
|
distinguish unset from set properties::
|
|
|
|
|
|
|
|
>>> import qubes
|
|
|
|
>>> class MyTestHolder(qubes.PropertyHolder):
|
|
|
|
>>> testprop = qubes.property('testprop', default=123)
|
|
|
|
>>> instance.testprop
|
|
|
|
123
|
|
|
|
>>> instance.property_is_default('testprop')
|
|
|
|
True
|
|
|
|
>>> instance.testprop = 123
|
|
|
|
>>> instance.testprop
|
|
|
|
>>> instance.property_is_default('testprop')
|
|
|
|
False
|
|
|
|
>>> instance.testprop = qubes.property.DEFAULT
|
|
|
|
>>> instance.property_is_default('testprop')
|
|
|
|
True
|
|
|
|
|
|
|
|
|
|
|
|
Inheritance
|
|
|
|
^^^^^^^^^^^
|
|
|
|
Properties in subclassess overload properties from their parents, like
|
|
|
|
expected::
|
|
|
|
|
|
|
|
>>> import qubes
|
|
|
|
>>> class MyTestHolder(qubes.PropertyHolder):
|
|
|
|
>>> testprop = qubes.property('testprop')
|
|
|
|
|
|
|
|
>>> class MyOtherHolder(MyTestHolder):
|
|
|
|
>>> testprop = qubes.property('testprop', setter=qubes.property.forbidden)
|
|
|
|
|
|
|
|
>>> instance = MyOtherHolder()
|
|
|
|
>>> instane.testprop = 123
|
|
|
|
TypeError: ...
|
|
|
|
|
|
|
|
|
|
|
|
Module contents
|
|
|
|
---------------
|
|
|
|
|
2014-11-13 18:10:27 +01:00
|
|
|
.. automodule:: qubes
|
2014-11-17 16:25:14 +01:00
|
|
|
:members:
|
|
|
|
:show-inheritance:
|
2014-11-13 18:10:27 +01:00
|
|
|
|
|
|
|
.. vim: ts=3 sw=3 et
|