vm: include tag/feature name in event name

Rename events:
 - domain-feature-set -> domain-feature-set:feature
 - domain-feature-delete -> domain-feature-delete:feature
 - domain-tag-add -> domain-tag-add:tag
 - domain-tag-delete -> domain-tag-delete:tag

Make it consistent with property-* events. It makes more sense to
include tag/feature name in event name, so handler can watch a single
tag/feature - which is the most common case. Otherwise, most handlers
would begin with `if feature == '...'` anyway, wasting time on most
events.

In cases where multiple features/tags should be handled by a single
handler, it is now possible to register a handler with wildcard, for
example `domain-feature-set:*`.
This commit is contained in:
Marek Marczykowski-Górecki 2018-01-06 15:05:34 +01:00
parent 5a39e77708
commit 50d34755fa
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
4 changed files with 42 additions and 38 deletions

View File

@ -39,7 +39,7 @@ class ServicesExtension(qubes.ext.Extension):
vm.untrusted_qdb.write('/qubes-service/{}'.format(service), vm.untrusted_qdb.write('/qubes-service/{}'.format(service),
str(int(bool(value)))) str(int(bool(value))))
@qubes.ext.handler('domain-feature-set') @qubes.ext.handler('domain-feature-set:*')
def on_domain_feature_set(self, vm, event, feature, value, oldvalue=None): def on_domain_feature_set(self, vm, event, feature, value, oldvalue=None):
'''Update /qubes-service/ QubesDB tree in runtime''' '''Update /qubes-service/ QubesDB tree in runtime'''
# pylint: disable=unused-argument # pylint: disable=unused-argument
@ -52,7 +52,7 @@ class ServicesExtension(qubes.ext.Extension):
vm.untrusted_qdb.write('/qubes-service/{}'.format(service), vm.untrusted_qdb.write('/qubes-service/{}'.format(service),
str(int(bool(value)))) str(int(bool(value))))
@qubes.ext.handler('domain-feature-delete') @qubes.ext.handler('domain-feature-delete:*')
def on_domain_feature_delete(self, vm, event, feature): def on_domain_feature_delete(self, vm, event, feature):
'''Update /qubes-service/ QubesDB tree in runtime''' '''Update /qubes-service/ QubesDB tree in runtime'''
# pylint: disable=unused-argument # pylint: disable=unused-argument

View File

@ -155,37 +155,37 @@ class TC_20_Tags(qubes.tests.QubesTestCase):
def test_000_add(self): def test_000_add(self):
self.tags.add('testtag') self.tags.add('testtag')
self.assertEventFired(self.vm, 'domain-tag-add', self.assertEventFired(self.vm, 'domain-tag-add:testtag',
kwargs={'tag': 'testtag'}) kwargs={'tag': 'testtag'})
def test_001_add_existing(self): def test_001_add_existing(self):
self.tags.add('testtag') self.tags.add('testtag')
self.vm.fired_events.clear() self.vm.fired_events.clear()
self.tags.add('testtag') self.tags.add('testtag')
self.assertEventNotFired(self.vm, 'domain-tag-add') self.assertEventNotFired(self.vm, 'domain-tag-add:testtag')
def test_002_remove(self): def test_002_remove(self):
self.tags.add('testtag') self.tags.add('testtag')
self.vm.fired_events.clear() self.vm.fired_events.clear()
self.tags.remove('testtag') self.tags.remove('testtag')
self.assertEventFired(self.vm, 'domain-tag-delete', self.assertEventFired(self.vm, 'domain-tag-delete:testtag',
kwargs={'tag': 'testtag'}) kwargs={'tag': 'testtag'})
def test_003_remove_not_present(self): def test_003_remove_not_present(self):
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
self.tags.remove('testtag') self.tags.remove('testtag')
self.assertEventNotFired(self.vm, 'domain-tag-delete') self.assertEventNotFired(self.vm, 'domain-tag-delete:testtag')
def test_004_discard_not_present(self): def test_004_discard_not_present(self):
with self.assertNotRaises(KeyError): with self.assertNotRaises(KeyError):
self.tags.discard('testtag') self.tags.discard('testtag')
self.assertEventNotFired(self.vm, 'domain-tag-delete') self.assertEventNotFired(self.vm, 'domain-tag-delete:testtag')
def test_005_discard_present(self): def test_005_discard_present(self):
self.tags.add('testtag') self.tags.add('testtag')
with self.assertNotRaises(KeyError): with self.assertNotRaises(KeyError):
self.tags.discard('testtag') self.tags.discard('testtag')
self.assertEventFired(self.vm, 'domain-tag-delete', self.assertEventFired(self.vm, 'domain-tag-delete:testtag',
kwargs={'tag': 'testtag'}) kwargs={'tag': 'testtag'})
def test_006_clear(self): def test_006_clear(self):
@ -193,9 +193,9 @@ class TC_20_Tags(qubes.tests.QubesTestCase):
self.tags.add('testtag2') self.tags.add('testtag2')
self.vm.fired_events.clear() self.vm.fired_events.clear()
self.tags.clear() self.tags.clear()
self.assertEventFired(self.vm, 'domain-tag-delete', self.assertEventFired(self.vm, 'domain-tag-delete:testtag',
kwargs={'tag': 'testtag'}) kwargs={'tag': 'testtag'})
self.assertEventFired(self.vm, 'domain-tag-delete', self.assertEventFired(self.vm, 'domain-tag-delete:testtag2',
kwargs={'tag': 'testtag2'}) kwargs={'tag': 'testtag2'})
def test_007_update(self): def test_007_update(self):
@ -203,9 +203,9 @@ class TC_20_Tags(qubes.tests.QubesTestCase):
self.tags.add('testtag2') self.tags.add('testtag2')
self.vm.fired_events.clear() self.vm.fired_events.clear()
self.tags.update(('testtag2', 'testtag3')) self.tags.update(('testtag2', 'testtag3'))
self.assertEventFired(self.vm, 'domain-tag-add', self.assertEventFired(self.vm, 'domain-tag-add:testtag3',
kwargs={'tag': 'testtag3'}) kwargs={'tag': 'testtag3'})
self.assertEventNotFired(self.vm, 'domain-tag-add', self.assertEventNotFired(self.vm, 'domain-tag-add:testtag2',
kwargs={'tag': 'testtag2'}) kwargs={'tag': 'testtag2'})
@ -217,14 +217,14 @@ class TC_21_Features(qubes.tests.QubesTestCase):
def test_000_set(self): def test_000_set(self):
self.features['testfeature'] = 'value' self.features['testfeature'] = 'value'
self.assertEventFired(self.vm, 'domain-feature-set', self.assertEventFired(self.vm, 'domain-feature-set:testfeature',
kwargs={'feature': 'testfeature', 'value': 'value'}) kwargs={'feature': 'testfeature', 'value': 'value'})
def test_001_set_existing(self): def test_001_set_existing(self):
self.features['test'] = 'oldvalue' self.features['test'] = 'oldvalue'
self.vm.fired_events.clear() self.vm.fired_events.clear()
self.features['test'] = 'value' self.features['test'] = 'value'
self.assertEventFired(self.vm, 'domain-feature-set', self.assertEventFired(self.vm, 'domain-feature-set:test',
kwargs={'feature': 'test', 'value': 'value', 'oldvalue': kwargs={'feature': 'test', 'value': 'value', 'oldvalue':
'oldvalue'}) 'oldvalue'})
@ -232,29 +232,30 @@ class TC_21_Features(qubes.tests.QubesTestCase):
self.features['test'] = 'value' self.features['test'] = 'value'
self.vm.fired_events.clear() self.vm.fired_events.clear()
del self.features['test'] del self.features['test']
self.assertEventFired(self.vm, 'domain-feature-delete', self.assertEventFired(self.vm, 'domain-feature-delete:test',
kwargs={'feature': 'test'}) kwargs={'feature': 'test'})
def test_003_unset_not_present(self): def test_003_unset_not_present(self):
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
del self.features['test'] del self.features['test']
self.assertEventNotFired(self.vm, 'domain-feature-delete') self.assertEventNotFired(self.vm, 'domain-feature-delete')
self.assertEventNotFired(self.vm, 'domain-feature-delete:test')
def test_004_set_bool_true(self): def test_004_set_bool_true(self):
self.features['test'] = True self.features['test'] = True
self.assertTrue(self.features['test']) self.assertTrue(self.features['test'])
self.assertEventFired(self.vm, 'domain-feature-set', self.assertEventFired(self.vm, 'domain-feature-set:test',
kwargs={'feature': 'test', 'value': '1'}) kwargs={'feature': 'test', 'value': '1'})
def test_005_set_bool_false(self): def test_005_set_bool_false(self):
self.features['test'] = False self.features['test'] = False
self.assertFalse(self.features['test']) self.assertFalse(self.features['test'])
self.assertEventFired(self.vm, 'domain-feature-set', self.assertEventFired(self.vm, 'domain-feature-set:test',
kwargs={'feature': 'test', 'value': ''}) kwargs={'feature': 'test', 'value': ''})
def test_006_set_int(self): def test_006_set_int(self):
self.features['test'] = 123 self.features['test'] = 123
self.assertEventFired(self.vm, 'domain-feature-set', self.assertEventFired(self.vm, 'domain-feature-set:test',
kwargs={'feature': 'test', 'value': '123'}) kwargs={'feature': 'test', 'value': '123'})
def test_007_clear(self): def test_007_clear(self):
@ -262,9 +263,9 @@ class TC_21_Features(qubes.tests.QubesTestCase):
self.features['test2'] = 'value2' self.features['test2'] = 'value2'
self.vm.fired_events.clear() self.vm.fired_events.clear()
self.features.clear() self.features.clear()
self.assertEventFired(self.vm, 'domain-feature-delete', self.assertEventFired(self.vm, 'domain-feature-delete:test',
kwargs={'feature': 'test'}) kwargs={'feature': 'test'})
self.assertEventFired(self.vm, 'domain-feature-delete', self.assertEventFired(self.vm, 'domain-feature-delete:test2',
kwargs={'feature': 'test2'}) kwargs={'feature': 'test2'})
def test_008_update(self): def test_008_update(self):
@ -275,8 +276,8 @@ class TC_21_Features(qubes.tests.QubesTestCase):
self.assertEqual(self.features['test2'], 'value3') self.assertEqual(self.features['test2'], 'value3')
self.assertEqual(self.features['test3'], 'value4') self.assertEqual(self.features['test3'], 'value4')
self.assertEqual(self.features['test'], 'value') self.assertEqual(self.features['test'], 'value')
self.assertEventFired(self.vm, 'domain-feature-set', self.assertEventFired(self.vm, 'domain-feature-set:test2',
kwargs={'feature': 'test2', 'value': 'value3', kwargs={'feature': 'test2', 'value': 'value3',
'oldvalue': 'value2'}) 'oldvalue': 'value2'})
self.assertEventFired(self.vm, 'domain-feature-set', self.assertEventFired(self.vm, 'domain-feature-set:test3',
kwargs={'feature': 'test3', 'value': 'value4'}) kwargs={'feature': 'test3', 'value': 'value4'})

View File

@ -113,7 +113,7 @@ class Features(dict):
def __delitem__(self, key): def __delitem__(self, key):
super(Features, self).__delitem__(key) super(Features, self).__delitem__(key)
self.vm.fire_event('domain-feature-delete', feature=key) self.vm.fire_event('domain-feature-delete:' + key, feature=key)
def __setitem__(self, key, value): def __setitem__(self, key, value):
if value is None or isinstance(value, bool): if value is None or isinstance(value, bool):
@ -127,10 +127,11 @@ class Features(dict):
has_oldvalue = False has_oldvalue = False
super(Features, self).__setitem__(key, value) super(Features, self).__setitem__(key, value)
if has_oldvalue: if has_oldvalue:
self.vm.fire_event('domain-feature-set', feature=key, value=value, self.vm.fire_event('domain-feature-set:' + key, feature=key,
oldvalue=oldvalue) value=value, oldvalue=oldvalue)
else: else:
self.vm.fire_event('domain-feature-set', feature=key, value=value) self.vm.fire_event('domain-feature-set:' + key, feature=key,
value=value)
def clear(self): def clear(self):
for key in tuple(self): for key in tuple(self):
@ -265,12 +266,12 @@ class Tags(set):
if elem in self: if elem in self:
return return
super(Tags, self).add(elem) super(Tags, self).add(elem)
self.vm.fire_event('domain-tag-add', tag=elem) self.vm.fire_event('domain-tag-add:' + elem, tag=elem)
def remove(self, elem): def remove(self, elem):
'''Remove a tag''' '''Remove a tag'''
super(Tags, self).remove(elem) super(Tags, self).remove(elem)
self.vm.fire_event('domain-tag-delete', tag=elem) self.vm.fire_event('domain-tag-delete:' + elem, tag=elem)
# #
# end of overriding # end of overriding

View File

@ -292,40 +292,42 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
:param subject: Event emitter (the qube object) :param subject: Event emitter (the qube object)
:param event: Event name (``'domain-restore'``) :param event: Event name (``'domain-restore'``)
.. event:: domain-feature-set (subject, event, feature, value .. event:: domain-feature-set:feature (subject, event, feature, value
[, oldvalue]) [, oldvalue])
A feature was changed. A feature was changed. This event is fired before bare
`domain-feature-set` event.
*oldvalue* is present only when there was any. *oldvalue* is present only when there was any.
:param subject: Event emitter (the qube object) :param subject: Event emitter (the qube object)
:param event: Event name (``'domain-feature-set'``) :param event: Event name (``'domain-feature-set:' feature``)
:param feature: feature name :param feature: feature name
:param value: new value :param value: new value
:param oldvalue: old value, if any :param oldvalue: old value, if any
.. event:: domain-feature-delete (subject, event, feature) .. event:: domain-feature-delete:feature (subject, event, feature)
A feature was removed. A feature was removed. This event is fired before bare
`domain-feature-delete` event.
:param subject: Event emitter (the qube object) :param subject: Event emitter (the qube object)
:param event: Event name (``'domain-feature-delete'``) :param event: Event name (``'domain-feature-delete:' feature``)
:param feature: feature name :param feature: feature name
.. event:: domain-tag-add (subject, event, tag) .. event:: domain-tag-add:tag (subject, event, tag)
A tag was added. A tag was added.
:param subject: Event emitter (the qube object) :param subject: Event emitter (the qube object)
:param event: Event name (``'domain-tag-add'``) :param event: Event name (``'domain-tag-add:' tag``)
:param tag: tag name :param tag: tag name
.. event:: domain-tag-delete (subject, event, tag) .. event:: domain-tag-delete:tag (subject, event, tag)
A feature was removed. A feature was removed.
:param subject: Event emitter (the qube object) :param subject: Event emitter (the qube object)
:param event: Event name (``'domain-tag-delete'``) :param event: Event name (``'domain-tag-delete:' tag``)
:param tag: tag name :param tag: tag name
.. event:: feature-request (subject, event, *, untrusted_features) .. event:: feature-request (subject, event, *, untrusted_features)