Browse Source

devices: add assignment.device property

Make it easy to retrieve DeviceInfo object out of DeviceAssignment
object. The only missing piece of information for that is device class,
so add it. Make it optional, as it can be filled on demand when passing
the object through DeviceCollection (either by listing devices, or
attaching/detaching).

This is mostly to ease handling options in libvirt template - to get
them, you need to use `assignments()`, istead of `persistent()` or
`attached()`, but there were no _simple_ way of getting actual device
object.

This also makes DeviceCollection._device method not needed anymore.
Marek Marczykowski-Górecki 7 years ago
parent
commit
d0b8933374
1 changed files with 30 additions and 14 deletions
  1. 30 14
      qubes/devices.py

+ 30 - 14
qubes/devices.py

@@ -60,12 +60,13 @@ class DeviceAssignment(object): # pylint: disable=too-few-public-methods
     ''' Maps a device to a frontend_domain. '''
 
     def __init__(self, backend_domain, ident, options=None, persistent=False,
-                 frontend_domain=None):
+            frontend_domain=None, devclass=None):
         self.backend_domain = backend_domain
         self.ident = ident
         self.options = options or {}
         self.persistent = persistent
         self.frontend_domain = frontend_domain
+        self.devclass = devclass
 
     def __repr__(self):
         return "[%s]:%s" % (self.backend_domain, self.ident)
@@ -88,9 +89,15 @@ class DeviceAssignment(object): # pylint: disable=too-few-public-methods
             self.ident,
             self.options,
             self.persistent,
-            self.frontend_domain
+            self.frontend_domain,
+            self.devclass,
         )
 
+    @property
+    def device(self):
+        '''Get DeviceInfo object corresponding to this DeviceAssignment'''
+        return self.backend_domain.devices[self.devclass][self.ident]
+
 
 class DeviceCollection(object):
     '''Bag for devices.
@@ -167,10 +174,16 @@ class DeviceCollection(object):
             assert device_assignment.frontend_domain == self._vm, \
                 "Trying to attach DeviceAssignment belonging to other domain"
 
+        if device_assignment.devclass is None:
+            device_assignment.devclass = self._class
+        else:
+            assert device_assignment.devclass == self._class, \
+                "Trying to attach DeviceAssignment of a different device class"
+
         if not device_assignment.persistent and self._vm.is_halted():
             raise qubes.exc.QubesVMNotRunningError(self._vm,
                 "Devices can only be attached  non-persistent to a running vm")
-        device = self._device(device_assignment)
+        device = device_assignment.device
         if device in self.assignments():
             raise DeviceAlreadyAttached(
                 'device {!s} of class {} already attached to {!s}'.format(
@@ -191,6 +204,12 @@ class DeviceCollection(object):
         if not device_assignment.frontend_domain:
             device_assignment.frontend_domain = self._vm
 
+        if device_assignment.devclass is None:
+            device_assignment.devclass = self._class
+        else:
+            assert device_assignment.devclass == self._class, \
+                "Trying to attach DeviceAssignment of a different device class"
+
         if device_assignment in self._set and not self._vm.is_halted():
             raise qubes.exc.QubesVMNotHaltedError(self._vm,
                 "Can not remove a persistent attachment from a non halted vm")
@@ -199,7 +218,7 @@ class DeviceCollection(object):
                 'device {!s} of class {} not attached to {!s}'.format(
                     device_assignment.ident, self._class, self._vm))
 
-        device = self._device(device_assignment)
+        device = device_assignment.device
         self._vm.fire_event_pre('device-pre-detach:'+self._class, device=device)
         if device in self._set:
             device_assignment.persistent = True
@@ -219,7 +238,7 @@ class DeviceCollection(object):
         ''' Devices persistently attached and safe to access before libvirt
             bootstrap.
         '''
-        return [self._device(a) for a in self._set]
+        return [a.device for a in self._set]
 
     def assignments(self, persistent=None):
         '''List assignments for devices which are (or may be) attached to the
@@ -245,9 +264,11 @@ class DeviceCollection(object):
                 continue
             else:
                 result.add(
-                    DeviceAssignment(backend_domain=dev.backend_domain,
-                                     ident=dev.ident, options=options,
-                                     frontend_domain=self._vm))
+                    DeviceAssignment(
+                        backend_domain=dev.backend_domain,
+                        ident=dev.ident, options=options,
+                        devclass=self._class,
+                        frontend_domain=self._vm))
         if persistent is not False:
             result.update(self._set)
         return result
@@ -257,12 +278,6 @@ class DeviceCollection(object):
         devices = self._vm.fire_event('device-list:' + self._class)
         return devices
 
-    def _device(self, assignment: DeviceAssignment):
-        ''' Helper method for geting a `qubes.devices.DeviceInfo` object from
-            `qubes.devices.DeviceAssignment`. '''
-
-        return assignment.backend_domain.devices[self._class][assignment.ident]
-
     def __iter__(self):
         return iter(self.available())
 
@@ -347,6 +362,7 @@ class DeviceInfo(object):
     def __str__(self):
         return '{!s}:{!s}'.format(self.backend_domain, self.ident)
 
+
 class UnknownDevice(DeviceInfo):
     # pylint: disable=too-few-public-methods
     '''Unknown device - for example exposed by domain not running currently'''