devices: adjust API for 'devices: add assignment.device property' change

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 also makes DeviceCollection._device method not needed anymore.
This commit is contained in:
Marek Marczykowski-Górecki 2017-05-22 01:17:18 +02:00
parent 3edbc85282
commit c826378579
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
2 changed files with 46 additions and 11 deletions

View File

@ -34,10 +34,11 @@ Devices are identified by pair of (backend domain, `ident`), where `ident` is
class DeviceAssignment(object): # pylint: disable=too-few-public-methods class DeviceAssignment(object): # pylint: disable=too-few-public-methods
''' Maps a device to a frontend_domain. ''' ''' Maps a device to a frontend_domain. '''
def __init__(self, backend_domain, ident, options=None, persistent=False, def __init__(self, backend_domain, ident, options=None,
frontend_domain=None): persistent=False, frontend_domain=None, devclass=None):
self.backend_domain = backend_domain self.backend_domain = backend_domain
self.ident = ident self.ident = ident
self.devclass = devclass
self.options = options or {} self.options = options or {}
self.persistent = persistent self.persistent = persistent
self.frontend_domain = frontend_domain self.frontend_domain = frontend_domain
@ -55,6 +56,22 @@ class DeviceAssignment(object): # pylint: disable=too-few-public-methods
return self.backend_domain == other.backend_domain \ return self.backend_domain == other.backend_domain \
and self.ident == other.ident and self.ident == other.ident
def clone(self):
'''Clone object instance'''
return self.__class__(
self.backend_domain,
self.ident,
self.options,
self.persistent,
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 DeviceInfo(object): class DeviceInfo(object):
''' Holds all information about a device ''' ''' Holds all information about a device '''
@ -120,6 +137,10 @@ class DeviceCollection(object):
else: else:
assert device_assignment.frontend_domain == self._vm, \ assert device_assignment.frontend_domain == self._vm, \
"Trying to attach DeviceAssignment belonging to other domain" "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
options = device_assignment.options.copy() options = device_assignment.options.copy()
if device_assignment.persistent: if device_assignment.persistent:
@ -143,6 +164,10 @@ class DeviceCollection(object):
else: else:
assert device_assignment.frontend_domain == self._vm, \ assert device_assignment.frontend_domain == self._vm, \
"Trying to detach DeviceAssignment belonging to other domain" "Trying to detach DeviceAssignment belonging to other domain"
if device_assignment.devclass is None:
device_assignment.devclass = self._class
else:
assert device_assignment.devclass == self._class
self._vm.qubesd_call(None, self._vm.qubesd_call(None,
'admin.vm.device.{}.Detach'.format(self._class), 'admin.vm.device.{}.Detach'.format(self._class),
@ -174,13 +199,14 @@ class DeviceCollection(object):
continue continue
backend_domain = self._vm.app.domains[backend_domain] backend_domain = self._vm.app.domains[backend_domain]
yield DeviceAssignment(backend_domain, ident, options, yield DeviceAssignment(backend_domain, ident, options,
persistent=dev_persistent, frontend_domain=self._vm) persistent=dev_persistent, frontend_domain=self._vm,
devclass=self._class)
def attached(self): def attached(self):
'''List devices which are (or may be) attached to this vm ''' '''List devices which are (or may be) attached to this vm '''
for assignment in self.assignments(): for assignment in self.assignments():
yield self._device(assignment) yield assignment.device
def persistent(self): def persistent(self):
''' Devices persistently attached and safe to access before libvirt ''' Devices persistently attached and safe to access before libvirt
@ -188,13 +214,7 @@ class DeviceCollection(object):
''' '''
for assignment in self.assignments(True): for assignment in self.assignments(True):
yield self._device(assignment) yield assignment.device
def _device(self, assignment):
''' Helper method for geting a `qubes.devices.DeviceInfo` object from
`qubes.devices.DeviceAssignment`. '''
return assignment.backend_domain.devices[self._class][assignment.ident]
def available(self): def available(self):
'''List devices exposed by this vm''' '''List devices exposed by this vm'''

View File

@ -161,6 +161,12 @@ class TC_00_DeviceCollection(qubesadmin.tests.QubesTestCase):
('test-vm', 'admin.vm.device.test.List', None, None)] = \ ('test-vm', 'admin.vm.device.test.List', None, None)] = \
b'0\0test-vm2+dev1\n' \ b'0\0test-vm2+dev1\n' \
b'test-vm3+dev2\n' b'test-vm3+dev2\n'
self.app.expected_calls[
('test-vm2', 'admin.vm.device.test.Available', None, None)] = \
b'0\0dev1 description=desc\n'
self.app.expected_calls[
('test-vm3', 'admin.vm.device.test.Available', None, None)] = \
b'0\0dev2 description=desc\n'
assigns = list(self.vm.devices['test'].assignments()) assigns = list(self.vm.devices['test'].assignments())
self.assertEqual(len(assigns), 2) self.assertEqual(len(assigns), 2)
self.assertIsInstance(assigns[0], qubesadmin.devices.DeviceAssignment) self.assertIsInstance(assigns[0], qubesadmin.devices.DeviceAssignment)
@ -170,6 +176,9 @@ class TC_00_DeviceCollection(qubesadmin.tests.QubesTestCase):
self.assertEqual(assigns[0].frontend_domain, self.assertEqual(assigns[0].frontend_domain,
self.app.domains['test-vm']) self.app.domains['test-vm'])
self.assertEqual(assigns[0].options, {}) self.assertEqual(assigns[0].options, {})
self.assertEqual(assigns[0].devclass, 'test')
self.assertEqual(assigns[0].device,
self.app.domains['test-vm2'].devices['test']['dev1'])
self.assertIsInstance(assigns[1], qubesadmin.devices.DeviceAssignment) self.assertIsInstance(assigns[1], qubesadmin.devices.DeviceAssignment)
self.assertEqual(assigns[1].backend_domain, self.assertEqual(assigns[1].backend_domain,
@ -178,6 +187,9 @@ class TC_00_DeviceCollection(qubesadmin.tests.QubesTestCase):
self.assertEqual(assigns[1].frontend_domain, self.assertEqual(assigns[1].frontend_domain,
self.app.domains['test-vm']) self.app.domains['test-vm'])
self.assertEqual(assigns[1].options, {}) self.assertEqual(assigns[1].options, {})
self.assertEqual(assigns[1].devclass, 'test')
self.assertEqual(assigns[1].device,
self.app.domains['test-vm3'].devices['test']['dev2'])
self.assertAllCalled() self.assertAllCalled()
@ -196,6 +208,7 @@ class TC_00_DeviceCollection(qubesadmin.tests.QubesTestCase):
self.app.domains['test-vm']) self.app.domains['test-vm'])
self.assertEqual(assigns[0].options, {'ro': 'True'}) self.assertEqual(assigns[0].options, {'ro': 'True'})
self.assertEqual(assigns[0].persistent, False) self.assertEqual(assigns[0].persistent, False)
self.assertEqual(assigns[0].devclass, 'test')
self.assertIsInstance(assigns[1], qubesadmin.devices.DeviceAssignment) self.assertIsInstance(assigns[1], qubesadmin.devices.DeviceAssignment)
self.assertEqual(assigns[1].backend_domain, self.assertEqual(assigns[1].backend_domain,
@ -205,6 +218,7 @@ class TC_00_DeviceCollection(qubesadmin.tests.QubesTestCase):
self.app.domains['test-vm']) self.app.domains['test-vm'])
self.assertEqual(assigns[1].options, {'ro': 'False'}) self.assertEqual(assigns[1].options, {'ro': 'False'})
self.assertEqual(assigns[1].persistent, True) self.assertEqual(assigns[1].persistent, True)
self.assertEqual(assigns[1].devclass, 'test')
self.assertAllCalled() self.assertAllCalled()
@ -223,6 +237,7 @@ class TC_00_DeviceCollection(qubesadmin.tests.QubesTestCase):
self.app.domains['test-vm']) self.app.domains['test-vm'])
self.assertEqual(assigns[0].options, {}) self.assertEqual(assigns[0].options, {})
self.assertEqual(assigns[0].persistent, True) self.assertEqual(assigns[0].persistent, True)
self.assertEqual(assigns[0].devclass, 'test')
self.assertAllCalled() self.assertAllCalled()
def test_042_assignments_non_persistent(self): def test_042_assignments_non_persistent(self):