qubes – Common concepts

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 qubes.Qubes class.

Loading

The objects may come to existence in two ways: by explicit instantiation or by loading from XML file.

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 (especcialy those that refer to another domains) are loaded later.

_images/loading.svg

Refer to qubes.Qubes class documentation to get description of every stage.

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 qubes.Qubes application object and on every domain. Those classess inherit from 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 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 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 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 qvm-prefs and 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 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 qubes.vm.qubesvm.QubesVM.netvm is unset, we check global setting qubes.Qubes.default_netvm instead. Returning 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 
<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 qubes.property.DEFAULT. There is also method 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

Qubes OS

copyright:© 2010-2015 Invisible Things Lab
class qubes.Label(index, color, name)[source]

Bases: object

Label definition for virtual machines

Label specifies colour of the padlock displayed next to VM’s name. When this is a qubes.vm.dispvm.DispVM, padlock is overlayed with recycling pictogram.

Parameters:
  • index (int) – numeric identificator of label
  • color (str) – colour specification as in HTML (#abcdef)
  • name (str) – label’s name like “red” or “green”
classmethod fromxml(xml)[source]

Create label definition from XML node

Parameters:xml (lxml.etree._Element) – XML node reference
Return type:qubes.Label
color = None

colour specification as in HTML (#abcdef)

icon = None

freedesktop icon name, suitable for use in PyQt4.QtGui.QIcon.fromTheme()

icon_dispvm = None

freedesktop icon name, suitable for use in PyQt4.QtGui.QIcon.fromTheme() on DispVMs

icon_path

Icon path

Deprecated since version 2.0: use PyQt4.QtGui.QIcon.fromTheme() and icon

icon_path_dispvm

Icon path

Deprecated since version 2.0: use PyQt4.QtGui.QIcon.fromTheme() and icon_dispvm

index = None

numeric identificator of label

name = None

label’s name like “red” or “green”

class qubes.PropertyHolder(xml, **kwargs)[source]

Bases: qubes.events.Emitter

Abstract class for holding qubes.property

Events fired by instances of this class:

property-load(subject, event)

Fired once after all properties are loaded from XML. Individual property-set events are not fired.

property-set:<propname>(subject, event, name, newvalue[, oldvalue])

Fired when property changes state. Signature is variable, oldvalue is present only if there was an old value.

Parameters:
  • name – Property name
  • newvalue – New value of the property
  • oldvalue – Old value of the property
property-pre-set:<propname>(subject, event, name, newvalue[, oldvalue])

Fired before property changes state. Signature is variable, oldvalue is present only if there was an old value.

Parameters:
  • name – Property name
  • newvalue – New value of the property
  • oldvalue – Old value of the property
property-del:<propname>(subject, event, name[, oldvalue])

Fired when property gets deleted (is set to default). Signature is variable, oldvalue is present only if there was an old value.

Parameters:
  • name – Property name
  • oldvalue – Old value of the property
property-pre-del:<propname>(subject, event, name[, oldvalue])

Fired before property gets deleted (is set to default). Signature is variable, oldvalue is present only if there was an old value.

Parameters:
  • name – Property name
  • oldvalue – Old value of the property
clone-properties(subject, event, src, proplist)
Parameters:
  • src – object, from which we are cloning
  • proplist – list of properties

Members:

clone_properties(src, proplist=None)[source]

Clone properties from other object.

Parameters:
  • src (PropertyHolder) – source object
  • proplist (iterable) – list of properties (None or omit for all properties except those with property.clone set to False)
load_properties(load_stage=None)[source]

Load properties from immediate children of XML node.

property-set events are not fired for each individual property.

Parameters:load_stage (int) – Stage of loading.
classmethod property_dict(load_stage=None)[source]

List all properties attached to this VM’s class

Parameters:load_stage (int() or None) – Filter by load stage
classmethod property_get_def(prop)[source]

Return property definition object.

If prop is already qubes.property instance, return the same object.

Parameters:prop (qubes.property or str) – property object or name
Return type:qubes.property
property_get_default(prop)[source]

Get property default value.

Parameters:or str prop (qubes.property) – property object of particular

interest

property_is_default(prop)[source]

Check whether property is in it’s default value.

Properties when unset may return some default value, so hasattr(vm, prop.__name__) is wrong in some circumstances. This method allows for checking if the value returned is in fact it’s default value.

Parameters:prop (qubes.property) – property object of particular interest
Return type:bool
classmethod property_list(load_stage=None)[source]

List all properties attached to this VM’s class

Parameters:load_stage (int() or None) – Filter by load stage
property_require(prop, allow_none=False, hard=False)[source]

Complain badly when property is not set.

Parameters:
xml_properties(with_defaults=False)[source]

Iterator that yields XML nodes representing set properties.

Parameters:with_defaults (bool) – If True, then it also includes properties which were not set explicite, but have default values filled.
class qubes.Qubes(store=None, load=True, offline_mode=None, lock=False, **kwargs)[source]

Bases: qubes.PropertyHolder

Main Qubes application

Parameters:store (str) – path to qubes.xml

The store is loaded in stages:

  1. In the first stage there are loaded some basic features from store (currently labels).
  2. In the second stage stubs for all VMs are loaded. They are filled with their basic properties, like qid and name.
  3. In the third stage all global properties are loaded. They often reference VMs, like default netvm, so they should be filled after loading VMs.
  4. In the fourth stage all remaining VM properties are loaded. They also need all VMs loaded, because they represent dependencies between VMs like aforementioned netvm.
  5. In the fifth stage there are some fixups to ensure sane system operation.

This class emits following events:

domain-add(subject, event, vm)

When domain is added.

Parameters:
  • subject – Event emitter
  • event – Event name ('domain-add')
  • vm – Domain object
domain-pre-delete(subject, event, vm)

When domain is deleted. VM still has reference to app object, and is contained within VMCollection. You may prevent removal by raising an exception.

Parameters:
  • subject – Event emitter
  • event – Event name ('domain-pre-delete')
  • vm – Domain object
domain-delete(subject, event, vm)

When domain is deleted. VM still has reference to app object, but is not contained within VMCollection.

Parameters:
  • subject – Event emitter
  • event – Event name ('domain-delete')
  • vm – Domain object

Methods and attributes:

add_new_vm(cls, qid=None, **kwargs)[source]

Add new Virtual Machine to collection

add_pool(name, **kwargs)[source]

Add a storage pool to config.

close()[source]

Deconstruct the object and break circular references

After calling this the object is unusable, not even for saving.

get_label(label)[source]

Get label as identified by index or name

Throws KeyError:
 when label is not found
get_pool(pool)[source]

Returns a qubes.storage.Pool instance

static get_vm_class(clsname)[source]

Find the class for a domain.

Classes are registered as setuptools’ entry points in qubes.vm group. Any package may supply their own classes.

Parameters:clsname (str) – name of the class
Return type:class
load(lock=False)[source]

Open qubes.xml

Throws EnvironmentError:
 failure on parsing store
Throws xml.parsers.expat.ExpatError:
 failure on parsing store
Raises:lxml.etree.XMLSyntaxError – on syntax error in qubes.xml
register_event_handlers()[source]

Register libvirt event handlers, which will translate libvirt events into qubes.events. This function should be called only in ‘qubesd’ process and only when mainloop has been already set.

remove_pool(name)[source]

Remove a storage pool from config file.

save(lock=True)[source]

Save all data to qubes.xml

There are several problems with saving qubes.xml which must be mitigated:

  • Running out of disk space. No space left should not result in empty file. This is done by writing to temporary file and then renaming.
  • Attempts to write two or more files concurrently. This is done by sophisticated locking.
Parameters:lock (bool) – keep file locked after saving
Throws EnvironmentError:
 failure on saving
setup_pools()[source]

Run implementation specific setup for each storage pool.

xml_labels()[source]

Serialise labels

Return type:lxml.etree._Element
check_updates_vm

check for updates inside qubes

clockvm

Which VM to use as NTP proxy for updating AdminVM

default_dispvm

Default DispVM base for service calls

default_kernel

Which kernel to use when not overriden in VM

default_netvm

Default NetVM for AppVMs. Initial state is None, which means that AppVMs are not connected to the Internet.

default_pool

Default storage pool

default_pool_kernel

Default storage pool for kernel volumes

default_pool_private

Default storage pool for private volumes

default_pool_root

Default storage pool for root volumes

default_pool_volatile

Default storage pool for volatile volumes

default_qrexec_timeout

Default time in seconds after which qrexec connection attempt is deemed failed

default_shutdown_timeout

Default time in seconds for VM shutdown to complete

default_template

Default template for new AppVMs

domains = None

collection of all VMs managed by this Qubes instance

env = None

jinja2 environment for libvirt XML templates

host = None

Information about host system

labels = None

collection of all available labels for VMs

log = None

logger instance for logging global messages

management_dispvm

Default DispVM base for managing VMs

pools = None

collection of all pools

stats_interval

Interval in seconds for VM stats reporting (memory, CPU usage)

updatevm

Which VM to use as yum proxy for updating AdminVM and TemplateVMs

vmm = None

Connection to VMM

class qubes.VMProperty(name, vmclass=<class 'qubes.vm.BaseVM'>, allow_none=False, **kwargs)[source]

Bases: qubes.property

Property that is referring to a VM

Parameters:vmclass (type) – class that returned VM is supposed to be instance of

and all supported by property with the exception of type and setter

sanitize(*, untrusted_newvalue)[source]

Coarse sanitization of value to be set, before sending it to a setter. Can raise QubesValueError if the value is invalid.

Parameters:untrusted_newvalue – value to be validated
Returns:sanitized value
Raises:qubes.exc.QubesValueError
class qubes.property(name, setter=None, saver=None, type=None, default=<object object>, write_once=False, load_stage=2, order=0, save_via_ref=False, clone=True, doc=None)[source]

Bases: object

Qubes property.

This class holds one property that can be saved to and loaded from qubes.xml. It is used for both global and per-VM properties.

Property can be unset by ordinary del statement or assigning DEFAULT special value to it. After deletion (or before first assignment/load) attempting to read a property will get its default value or, when no default, py:class:exceptions.AttributeError.

Parameters:
  • name (str) – name of the property
  • setter (collections.Callable) – if not None, this is used to initialise value; first parameter to the function is holder instance and the second is value; this is called before type
  • saver (collections.Callable) – function to coerce value to something readable by setter
  • type (type) – if not None, value is coerced to this type
  • default (object) – default value; if callable, will be called with holder as first argument
  • load_stage (int) – stage when property should be loaded (see Qubes for description of stages)
  • order (int) – order of evaluation (bigger order values are later)
  • clone (bool) – PropertyHolder.clone_properties() will not include this property by default if False
  • doc (str) – docstring; this should be one paragraph of plain RST, no sphinx-specific features

Setters and savers have following signatures:

exception DontSave[source]

Bases: Exception

This exception may be raised from saver to sign that property should not be saved.

static bool(self, prop, value)[source]

Property setter for boolean properties.

It accepts (case-insensitive) '0', 'no' and false as False and '1', 'yes' and 'true' as True.

static dontsave(self, prop, value)[source]

Dummy saver that never saves anything.

static forbidden(self, prop, value)[source]

Property setter that forbids loading a property.

This is used to effectively disable property in classes which inherit unwanted property. When someone attempts to load such a property, it

Throws AttributeError:
 always
sanitize(*, untrusted_newvalue)[source]

Coarse sanitization of value to be set, before sending it to a setter. Can raise QubesValueError if the value is invalid.

Parameters:untrusted_newvalue – value to be validated
Returns:sanitized value
Raises:qubes.exc.QubesValueError
DEFAULT = <object object>

Assigning this value to property means setting it to its default value. If property has no default value, this will unset it.