qubes.devices
– Devices¶
Main concept is that some domain (backend) may expose (potentially multiple)
devices, which can be attached to other domains (frontend). Devices can be of
different buses (like ‘pci’, ‘usb’, etc.). Each device bus is implemented by
an extension (see qubes.ext
).
Devices are identified by pair of (backend domain, ident), where ident is
str
and can contain only characters from [a-zA-Z0-9._-] set.
Device Assignment vs Attachment¶
qubes.device_protocol.DeviceAssignment
describes the assignment of a device
to a frontend VM. For clarity let’s us introduce two types of assignments:
potential and real (attachment). Attachment indicates that the device
has been attached by the Qubes backend to its frontend VM and is visible
from its perspective. Potential assignment, on the other hand,
has two additional options: automatically_attach and required.
For detailed descriptions, refer to the DeviceAssignment documentation.
In general we refer to potential assignment as assignment
and real assignment as attachment. To check whether the device is currently
attached, we check qubes.device_protocol.DeviceAssignment.attached()
,
while to check whether an (potential) assignment exists,
we check qubes.device_protocol.DeviceAssignment.attach_automatically()
.
Potential and real connections may coexist at the same time,
in which case both values will be true.
Actions¶
The assign action signifies that a device will be assigned to the frontend VM in a potential form (this does not change the current system state). This will result in an attempt to automatically attach the device upon the next VM startup. If required=True, and the device cannot be attached, the VM startup will fail. Additionally, upon device detection (device-added), an attempt will be made to attach the device. However, at any time (unless required=True), the user can manually modify this state by performing attach or detach on the device, changing the current system state. This will not alter the assignment, and automatic attachment attempts will still be made in the future. To remove the assignment the user need to perform unassign (see next section).
Assignment Management¶
Assignments can be edited at any time: regardless of whether the VM is running or the device is currently attached. An exception is required=True, in which case the VM must be shut down. Removing the assignment does not change the real system state, so if the device is currently attached and the user remove the assignment, it will not be detached, but it will not be automatically attached in the future. Similarly, it works the other way around with assign.
Proper Assignment States¶
In short, we can think of device assignment in terms of three flags: #. attached - indicating whether the device is currently assigned, #. attach_automatically - indicating whether the device will be automatically attached by the system daemon, #. required - determining whether the failure of automatic attachment should result in the domain startup being interrupted.
Then the proper states of assignment (attached, automatically_attached, required) are as follow: #. (True, False, False) -> domain is running, device is manually attached and could be manually detach any time. #. (True, True, False) -> domain is running, device is attached and could be manually detach any time (see 4.), but in the future will be auto-attached again. #. (True, True, True) -> domain is running, device is attached and couldn’t be detached. #. (False, True, False) -> device is assigned to domain, but not attached because either (i) domain is halted, device (ii) manually detached or (iii) attach to different domain. #. (False, True, True) -> domain is halted, device assigned to domain and required to start domain.
PCI Devices¶
PCI devices cannot be manually attached to a VM at any time. We must first create an assignment (assign) as required (in client we can use –required flag) while the VM is turned off. Then, it will be automatically attached upon each VM startup. However, if a PCI device is currently in use by another VM, the startup of the second VM will fail. PCI devices can only be assigned with the required=True, which does not allow for manual modification of the state during VM operation (attach/detach).
Microphone¶
The microphone cannot be assigned (potentially) to any VM (attempting to attach the microphone during VM startup fails).
Understanding Device Self Identity¶
It is important to understand that qubes.device_protocol.Device
does not
correspond to the device itself, but rather to the port to which the device
is connected. Therefore, when assigning a device to a VM, such as
sys-usb:1-1.1, the port 1-1.1 is actually assigned, and thus
every devices connected to it will be automatically attached.
Similarly, when assigning vm:sda, every block device with the name sda
will be automatically attached. We can limit this using qubes.device_protocol.DeviceInfo.self_identity()
, which returns a string containing information
presented by the device, such as, vendor_id, product_id, serial_number,
and encoded interfaces. In the case of block devices, self_identity
consists of the parent port to which the device is connected (if any),
the parent’s self_identity, and the interface/partition number.
In practice, this means that, a partition on a USB drive will only be
automatically attached to a frontend domain if the parent presents
the correct serial number etc., and is connected to a specific port.
Port Assignment¶
It is possible to not assign a specific device but rather a port, (e.g., we can use the –port flag in the client). In this case, the value any will appear in the identity field of the qubes.xml file. This indicates that the identity presented by the devices will be ignored, and all connected devices will be automatically attached. Note that to create an assignment, any device must currently be connected to the port.
API for various types of devices.
Main concept is that some domain may expose (potentially multiple) devices, which can be attached to other domains. Devices can be of different buses (like ‘pci’, ‘usb’, etc.). Each device bus is implemented by an extension.
Devices are identified by pair of (backend domain, ident), where ident is
str
and can contain only characters from [a-zA-Z0-9._-] set.
- Such extension should:
provide qubes.devices endpoint - a class descendant from
qubes.device_protocol.DeviceInfo
, designed to hold device description (including bus-specific properties)handle device-attach:bus and device-detach:bus events for performing the attach/detach action; events are fired even when domain isn’t running and extension should be prepared for this; handlers for those events can be coroutines
handle device-list:bus event - list devices exposed by particular domain; it should return a list of appropriate DeviceInfo objects
handle device-get:bus event - get one device object exposed by this domain of given identifier
handle device-list-attached:bus event - list devices currently attached to this domain
fire device-list-change:bus and following device-added:bus or device-removed:bus events when a device list change is detected (new/removed device)
Note that device-listing event handlers cannot be asynchronous. This for example means you cannot call qrexec service there. This is intentional to keep device listing operation cheap. You need to design the extension to take this into account (for example by using QubesDB).
Extension may use QubesDB watch API (QubesVM.watch_qdb_path(path), then handle domain-qdb-change:path) to detect changes and fire device-list-change:class event.
- exception qubes.devices.DeviceAlreadyAssigned[source]¶
Bases:
QubesException
,KeyError
Trying to assign already assigned device.
- exception qubes.devices.DeviceAlreadyAttached[source]¶
Bases:
QubesException
,KeyError
Trying to attach already attached device.
- exception qubes.devices.DeviceNotAssigned[source]¶
Bases:
QubesException
,KeyError
Trying to unassign not assigned device.
- exception qubes.devices.UnrecognizedDevice[source]¶
Bases:
QubesException
,ValueError
Device identity is not as expected.
- class qubes.devices.AssignedCollection[source]¶
Bases:
object
Helper object managing assigned devices.
- class qubes.devices.DeviceCollection(vm, bus)[source]¶
Bases:
object
Bag for devices.
Used as default value for
DeviceManager.__missing__()
factory.- Parameters:
vm – VM for which we manage devices
bus – device bus
This class emits following events on VM object:
- device-added:<class>(device)¶
Fired when new device is discovered.
- device-removed:<class>(device)¶
Fired when device is no longer exposed by a backend VM.
- device-attach:<class>(device, options)¶
Fired when device is attached to a VM.
Handler for this event may be asynchronous.
- Parameters:
device –
DeviceInfo
object to be attachedoptions –
dict
of attachment options
- device-pre-attach:<class>(device)¶
Fired before device is attached to a VM
Handler for this event may be asynchronous.
- Parameters:
device –
DeviceInfo
object to be attached
- device-detach:<class>(device)¶
Fired when device is detached from a VM.
Handler for this event can be asynchronous (a coroutine).
- Parameters:
device –
DeviceInfo
object to be attached
- device-pre-detach:<class>(device)¶
Fired before device is detached from a VM
Handler for this event can be asynchronous (a coroutine).
- Parameters:
device –
DeviceInfo
object to be attached
- device-assign:<class>(device, options)¶
Fired when device is assigned to a VM.
Handler for this event may be asynchronous.
- Parameters:
device –
DeviceInfo
object to be assignedoptions –
dict
of assignment options
- device-unassign:<class>(device)¶
Fired when device is unassigned from a VM.
Handler for this event can be asynchronous (a coroutine).
- Parameters:
device –
DeviceInfo
object to be unassigned
- device-list:<class>¶
Fired to get list of devices exposed by a VM. Handlers of this event should return a list of py:class:DeviceInfo objects (or appropriate class specific descendant)
- device-get:<class>(ident)¶
Fired to get a single device, given by the ident parameter. Handlers of this event should either return appropriate object of
DeviceInfo
, orNone
. Especially should not raiseexceptions.KeyError
.
- device-list-attached:<class>¶
Fired to get list of currently attached devices to a VM. Handlers of this event should return list of devices actually attached to a domain, regardless of its settings.
- get_assigned_devices(required_only: bool = False) Iterable[DeviceAssignment] [source]¶
Devices assigned to this vm (included in
qubes.xml
).Safe to access before libvirt bootstrap.
- get_attached_devices() Iterable[DeviceAssignment] [source]¶
List devices which are attached to this vm.
- get_dedicated_devices() Iterable[DeviceAssignment] [source]¶
List devices which are attached or assigned to this vm.
- load_assignment(device_assignment: DeviceAssignment)[source]¶
Load DeviceAssignment retrieved from qubes.xml
This can be used only for loading qubes.xml, when VM events are not enabled yet.
- async update_required(device: Device, required: bool)[source]¶
Update required flag of an already attached device.
- Parameters:
device (Device) – device for which change required flag
required (bool) – new assignment: False -> device will be auto-attached to qube True -> device is required to start qube