devices: drop 'data' and 'frontend_domain' fields, rename 'devclass' to 'bus'

Drop DeviceInfo.data - device extension should provide a subclass with
proper individual fields.
Drop DeviceAssignment.frontend_domain - this information is redundant -
frontend domain is defined by where DeviceAssignment is attached.
Rename DeviceCollection.devclass to bus - devclass if confusing here,
because this term is also used for DeviceInfo subclass.
This commit is contained in:
Marek Marczykowski-Górecki 2017-06-12 09:46:26 +02:00
parent 882abf2fb5
commit 3564250298
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
3 changed files with 32 additions and 48 deletions

View File

@ -817,12 +817,11 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
non_default_attrs = set(attr for attr in dir(dev) if non_default_attrs = set(attr for attr in dir(dev) if
not attr.startswith('_')).difference(( not attr.startswith('_')).difference((
'backend_domain', 'ident', 'frontend_domain', 'backend_domain', 'ident', 'frontend_domain',
'description', 'options', 'data')) 'description', 'options'))
properties_txt = ' '.join( properties_txt = ' '.join(
'{}={!s}'.format(prop, value) for prop, value '{}={!s}'.format(prop, value) for prop, value
in itertools.chain( in itertools.chain(
((key, getattr(dev, key)) for key in non_default_attrs), ((key, getattr(dev, key)) for key in non_default_attrs),
dev.data.items(),
# keep description as the last one, according to API # keep description as the last one, according to API
# specification # specification
(('description', dev.description),) (('description', dev.description),)

View File

@ -60,13 +60,12 @@ 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, persistent=False,
frontend_domain=None, devclass=None): bus=None):
self.backend_domain = backend_domain self.backend_domain = backend_domain
self.ident = ident self.ident = ident
self.options = options or {} self.options = options or {}
self.persistent = persistent self.persistent = persistent
self.frontend_domain = frontend_domain self.bus = bus
self.devclass = devclass
def __repr__(self): def __repr__(self):
return "[%s]:%s" % (self.backend_domain, self.ident) return "[%s]:%s" % (self.backend_domain, self.ident)
@ -89,14 +88,13 @@ class DeviceAssignment(object): # pylint: disable=too-few-public-methods
self.ident, self.ident,
self.options, self.options,
self.persistent, self.persistent,
self.frontend_domain, self.bus,
self.devclass,
) )
@property @property
def device(self): def device(self):
'''Get DeviceInfo object corresponding to this DeviceAssignment''' '''Get DeviceInfo object corresponding to this DeviceAssignment'''
return self.backend_domain.devices[self.devclass][self.ident] return self.backend_domain.devices[self.bus][self.ident]
class DeviceCollection(object): class DeviceCollection(object):
@ -105,7 +103,7 @@ class DeviceCollection(object):
Used as default value for :py:meth:`DeviceManager.__missing__` factory. Used as default value for :py:meth:`DeviceManager.__missing__` factory.
:param vm: VM for which we manage devices :param vm: VM for which we manage devices
:param class_: device class :param bus: device bus
This class emits following events on VM object: This class emits following events on VM object:
@ -154,13 +152,13 @@ class DeviceCollection(object):
''' '''
def __init__(self, vm, class_): def __init__(self, vm, bus):
self._vm = vm self._vm = vm
self._class = class_ self._bus = bus
self._set = PersistentCollection() self._set = PersistentCollection()
self.devclass = qubes.utils.get_entry_point_one( self.devclass = qubes.utils.get_entry_point_one(
'qubes.devices', self._class) 'qubes.devices', self._bus)
def attach(self, device_assignment: DeviceAssignment): def attach(self, device_assignment: DeviceAssignment):
'''Attach (add) device to domain. '''Attach (add) device to domain.
@ -168,16 +166,10 @@ class DeviceCollection(object):
:param DeviceInfo device: device object :param DeviceInfo device: device object
''' '''
if not device_assignment.frontend_domain: if device_assignment.bus is None:
device_assignment.frontend_domain = self._vm device_assignment.bus = self._bus
else: else:
assert device_assignment.frontend_domain == self._vm, \ assert device_assignment.bus == self._bus, \
"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" "Trying to attach DeviceAssignment of a different device class"
if not device_assignment.persistent and self._vm.is_halted(): if not device_assignment.persistent and self._vm.is_halted():
@ -187,12 +179,12 @@ class DeviceCollection(object):
if device in self.assignments(): if device in self.assignments():
raise DeviceAlreadyAttached( raise DeviceAlreadyAttached(
'device {!s} of class {} already attached to {!s}'.format( 'device {!s} of class {} already attached to {!s}'.format(
device, self._class, self._vm)) device, self._bus, self._vm))
self._vm.fire_event_pre('device-pre-attach:'+self._class, self._vm.fire_event_pre('device-pre-attach:'+self._bus,
device=device, options=device_assignment.options) device=device, options=device_assignment.options)
if device_assignment.persistent: if device_assignment.persistent:
self._set.add(device_assignment) self._set.add(device_assignment)
self._vm.fire_event('device-attach:' + self._class, self._vm.fire_event('device-attach:' + self._bus,
device=device, options=device_assignment.options) device=device, options=device_assignment.options)
def detach(self, device_assignment: DeviceAssignment): def detach(self, device_assignment: DeviceAssignment):
@ -201,13 +193,10 @@ class DeviceCollection(object):
:param DeviceInfo device: device object :param DeviceInfo device: device object
''' '''
if not device_assignment.frontend_domain: if device_assignment.bus is None:
device_assignment.frontend_domain = self._vm device_assignment.bus = self._bus
if device_assignment.devclass is None:
device_assignment.devclass = self._class
else: else:
assert device_assignment.devclass == self._class, \ assert device_assignment.bus == self._bus, \
"Trying to attach DeviceAssignment of a different device class" "Trying to attach DeviceAssignment of a different device class"
if device_assignment in self._set and not self._vm.is_halted(): if device_assignment in self._set and not self._vm.is_halted():
@ -216,19 +205,19 @@ class DeviceCollection(object):
if device_assignment not in self.assignments(): if device_assignment not in self.assignments():
raise DeviceNotAttached( raise DeviceNotAttached(
'device {!s} of class {} not attached to {!s}'.format( 'device {!s} of class {} not attached to {!s}'.format(
device_assignment.ident, self._class, self._vm)) device_assignment.ident, self._bus, self._vm))
device = device_assignment.device device = device_assignment.device
self._vm.fire_event_pre('device-pre-detach:'+self._class, device=device) self._vm.fire_event_pre('device-pre-detach:'+self._bus, device=device)
if device in self._set: if device in self._set:
device_assignment.persistent = True device_assignment.persistent = True
self._set.discard(device_assignment) self._set.discard(device_assignment)
self._vm.fire_event('device-detach:' + self._class, device=device) self._vm.fire_event('device-detach:' + self._bus, device=device)
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 '''
attached = self._vm.fire_event('device-list-attached:' + self._class) attached = self._vm.fire_event('device-list-attached:' + self._bus)
if attached: if attached:
return [dev for dev, _ in attached] return [dev for dev, _ in attached]
@ -252,7 +241,7 @@ class DeviceCollection(object):
attached persistently. attached persistently.
''' '''
devices = self._vm.fire_event('device-list-attached:' + self._class, devices = self._vm.fire_event('device-list-attached:' + self._bus,
persistent=persistent) persistent=persistent)
result = set() result = set()
for dev, options in devices: for dev, options in devices:
@ -267,15 +256,14 @@ class DeviceCollection(object):
DeviceAssignment( DeviceAssignment(
backend_domain=dev.backend_domain, backend_domain=dev.backend_domain,
ident=dev.ident, options=options, ident=dev.ident, options=options,
devclass=self._class, bus=self._bus))
frontend_domain=self._vm))
if persistent is not False: if persistent is not False:
result.update(self._set) result.update(self._set)
return result return result
def available(self): def available(self):
'''List devices exposed by this vm''' '''List devices exposed by this vm'''
devices = self._vm.fire_event('device-list:' + self._class) devices = self._vm.fire_event('device-list:' + self._bus)
return devices return devices
def __iter__(self): def __iter__(self):
@ -294,7 +282,7 @@ class DeviceCollection(object):
:raises AssertionError: when multiple devices with the same ident are :raises AssertionError: when multiple devices with the same ident are
found found
''' '''
dev = self._vm.fire_event('device-get:' + self._class, ident=ident) dev = self._vm.fire_event('device-get:' + self._bus, ident=ident)
if dev: if dev:
assert len(dev) == 1 assert len(dev) == 1
return dev[0] return dev[0]
@ -321,7 +309,7 @@ class DeviceInfo(object):
''' Holds all information about a device ''' ''' Holds all information about a device '''
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
def __init__(self, backend_domain, ident, description=None, def __init__(self, backend_domain, ident, description=None,
frontend_domain=None, options=None, **kwargs): frontend_domain=None):
#: domain providing this device #: domain providing this device
self.backend_domain = backend_domain self.backend_domain = backend_domain
#: device identifier (unique for given domain and device type) #: device identifier (unique for given domain and device type)
@ -337,8 +325,6 @@ class DeviceInfo(object):
self.frontend_domain = frontend_domain self.frontend_domain = frontend_domain
except AttributeError: except AttributeError:
pass pass
self.options = options or dict()
self.data = kwargs
if hasattr(self, 'regex'): if hasattr(self, 'regex'):
# pylint: disable=no-member # pylint: disable=no-member
@ -368,11 +354,11 @@ class UnknownDevice(DeviceInfo):
'''Unknown device - for example exposed by domain not running currently''' '''Unknown device - for example exposed by domain not running currently'''
def __init__(self, backend_domain, ident, description=None, def __init__(self, backend_domain, ident, description=None,
frontend_domain=None, **kwargs): frontend_domain=None):
if description is None: if description is None:
description = "Unknown device" description = "Unknown device"
super(UnknownDevice, self).__init__(backend_domain, ident, description, super(UnknownDevice, self).__init__(backend_domain, ident, description,
frontend_domain, **kwargs) frontend_domain)
class PersistentCollection(object): class PersistentCollection(object):
@ -385,7 +371,7 @@ class PersistentCollection(object):
def add(self, assignment: DeviceAssignment): def add(self, assignment: DeviceAssignment):
''' Add assignment to collection ''' ''' Add assignment to collection '''
assert assignment.persistent and assignment.frontend_domain assert assignment.persistent
vm = assignment.backend_domain vm = assignment.backend_domain
ident = assignment.ident ident = assignment.ident
key = (vm, ident) key = (vm, ident)
@ -395,7 +381,7 @@ class PersistentCollection(object):
def discard(self, assignment): def discard(self, assignment):
''' Discard assignment from collection ''' ''' Discard assignment from collection '''
assert assignment.persistent and assignment.frontend_domain assert assignment.persistent
vm = assignment.backend_domain vm = assignment.backend_domain
ident = assignment.ident ident = assignment.ident
key = (vm, ident) key = (vm, ident)

View File

@ -1349,7 +1349,6 @@ class TC_00_VMs(AdminAPITestCase):
return return
dev = qubes.devices.DeviceInfo(self.vm, '1234') dev = qubes.devices.DeviceInfo(self.vm, '1234')
dev.description = 'Some device' dev.description = 'Some device'
dev.data = {'other_property': 'property-value'}
dev.extra_prop = 'xx' dev.extra_prop = 'xx'
yield dev yield dev
dev = qubes.devices.DeviceInfo(self.vm, '4321') dev = qubes.devices.DeviceInfo(self.vm, '4321')
@ -1361,7 +1360,7 @@ class TC_00_VMs(AdminAPITestCase):
value = self.call_mgmt_func(b'admin.vm.device.testclass.Available', value = self.call_mgmt_func(b'admin.vm.device.testclass.Available',
b'test-vm1') b'test-vm1')
self.assertEqual(value, self.assertEqual(value,
'1234 extra_prop=xx other_property=property-value description=Some ' '1234 extra_prop=xx description=Some '
'device\n' 'device\n'
'4321 description=Some other device\n') '4321 description=Some other device\n')
self.assertFalse(self.app.save.called) self.assertFalse(self.app.save.called)