vm: implement tag manager to fire events on change
While at it, adjust implementation to specification: tags don't have value, only one bit of information (present/not present). Fixes QubesOS/qubes-issues#2686
This commit is contained in:
parent
68a426f0ba
commit
ba86d6da79
@ -67,7 +67,7 @@ class TC_10_BaseVM(qubes.tests.QubesTestCase):
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<tags>
|
<tags>
|
||||||
<tag name="testtag">tagvalue</tag>
|
<tag name="testtag"/>
|
||||||
</tags>
|
</tags>
|
||||||
|
|
||||||
<features>
|
<features>
|
||||||
@ -101,7 +101,7 @@ class TC_10_BaseVM(qubes.tests.QubesTestCase):
|
|||||||
self.assertEqual(vm.testprop, 'testvalue')
|
self.assertEqual(vm.testprop, 'testvalue')
|
||||||
self.assertEqual(vm.testlabel, 'label-1')
|
self.assertEqual(vm.testlabel, 'label-1')
|
||||||
self.assertEqual(vm.defaultprop, 'defaultvalue')
|
self.assertEqual(vm.defaultprop, 'defaultvalue')
|
||||||
self.assertEqual(vm.tags, {'testtag': 'tagvalue'})
|
self.assertEqual(vm.tags, {'testtag'})
|
||||||
self.assertEqual(vm.features, {
|
self.assertEqual(vm.features, {
|
||||||
'testfeature_empty': '',
|
'testfeature_empty': '',
|
||||||
'testfeature_aqq': 'aqq',
|
'testfeature_aqq': 'aqq',
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import xml.parsers.expat
|
import xml.parsers.expat
|
||||||
@ -159,6 +160,82 @@ class Features(dict):
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
class Tags(set):
|
||||||
|
'''Manager of the tags.
|
||||||
|
|
||||||
|
Tags are simple: tag either can be present on qube or not. Tag is a
|
||||||
|
simple string consisting of ASCII alphanumeric characters, plus `_` and
|
||||||
|
`-`.
|
||||||
|
|
||||||
|
This class inherits from set, but has most of the methods that manipulate
|
||||||
|
the item disarmed (they raise NotImplementedError). The ones that are left
|
||||||
|
fire appropriate events on the qube that owns an instance of this class.
|
||||||
|
'''
|
||||||
|
|
||||||
|
#
|
||||||
|
# Those are the methods that affect contents. Either disarm them or make
|
||||||
|
# them report appropriate events. Good approach is to rewrite them carefully
|
||||||
|
# using official documentation, but use only our (overloaded) methods.
|
||||||
|
#
|
||||||
|
def __init__(self, vm, seq=()):
|
||||||
|
super(Tags, self).__init__()
|
||||||
|
self.vm = vm
|
||||||
|
self.update(seq)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
'''Remove all tags'''
|
||||||
|
for item in tuple(self):
|
||||||
|
self.remove(item)
|
||||||
|
|
||||||
|
def symmetric_difference_update(self, *args, **kwargs):
|
||||||
|
'''Not implemented
|
||||||
|
:raises: NotImplementedError
|
||||||
|
'''
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def intersection_update(self, *args, **kwargs):
|
||||||
|
'''Not implemented
|
||||||
|
:raises: NotImplementedError
|
||||||
|
'''
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
'''Not implemented
|
||||||
|
:raises: NotImplementedError
|
||||||
|
'''
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def discard(self, elem):
|
||||||
|
'''Remove a tag if present'''
|
||||||
|
if elem in self:
|
||||||
|
self.remove(elem)
|
||||||
|
|
||||||
|
def update(self, *others):
|
||||||
|
'''Add tags from iterable(s)'''
|
||||||
|
for other in others:
|
||||||
|
for elem in other:
|
||||||
|
self.add(elem)
|
||||||
|
|
||||||
|
def add(self, elem):
|
||||||
|
'''Add a tag'''
|
||||||
|
allowed_chars = string.ascii_letters + string.digits + '_-'
|
||||||
|
if any(i not in allowed_chars for i in elem):
|
||||||
|
raise ValueError('Invalid character in tag')
|
||||||
|
if elem in self:
|
||||||
|
return
|
||||||
|
self.vm.fire_event('domain-tag-add', tag=elem)
|
||||||
|
super(Tags, self).add(elem)
|
||||||
|
|
||||||
|
def remove(self, elem):
|
||||||
|
'''Remove a tag'''
|
||||||
|
super(Tags, self).remove(elem)
|
||||||
|
self.vm.fire_event('domain-tag-delete', tag=elem)
|
||||||
|
|
||||||
|
#
|
||||||
|
# end of overriding
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
class BaseVM(qubes.PropertyHolder):
|
class BaseVM(qubes.PropertyHolder):
|
||||||
'''Base class for all VMs
|
'''Base class for all VMs
|
||||||
|
|
||||||
@ -192,7 +269,7 @@ class BaseVM(qubes.PropertyHolder):
|
|||||||
self.devices = devices or qubes.devices.DeviceManager(self)
|
self.devices = devices or qubes.devices.DeviceManager(self)
|
||||||
|
|
||||||
#: user-specified tags
|
#: user-specified tags
|
||||||
self.tags = tags or {}
|
self.tags = Tags(self, tags or ())
|
||||||
|
|
||||||
#: logger instance for logging messages related to this VM
|
#: logger instance for logging messages related to this VM
|
||||||
self.log = None
|
self.log = None
|
||||||
@ -223,7 +300,7 @@ class BaseVM(qubes.PropertyHolder):
|
|||||||
|
|
||||||
# tags
|
# tags
|
||||||
for node in self.xml.xpath('./tags/tag'):
|
for node in self.xml.xpath('./tags/tag'):
|
||||||
self.tags[node.get('name')] = node.text
|
self.tags.add(node.get('name'))
|
||||||
|
|
||||||
# SEE:1815 firewall, policy.
|
# SEE:1815 firewall, policy.
|
||||||
|
|
||||||
@ -262,7 +339,6 @@ class BaseVM(qubes.PropertyHolder):
|
|||||||
tags = lxml.etree.Element('tags')
|
tags = lxml.etree.Element('tags')
|
||||||
for tag in self.tags:
|
for tag in self.tags:
|
||||||
node = lxml.etree.Element('tag', name=tag)
|
node = lxml.etree.Element('tag', name=tag)
|
||||||
node.text = self.tags[tag]
|
|
||||||
tags.append(node)
|
tags.append(node)
|
||||||
element.append(tags)
|
element.append(tags)
|
||||||
|
|
||||||
|
@ -304,6 +304,22 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
:param event: Event name (``'domain-feature-set'``)
|
:param event: Event name (``'domain-feature-set'``)
|
||||||
:param key: feature name
|
:param key: feature name
|
||||||
|
|
||||||
|
.. event:: domain-tag-add (subject, event, tag)
|
||||||
|
|
||||||
|
A tag was added.
|
||||||
|
|
||||||
|
:param subject: Event emitter (the qube object)
|
||||||
|
:param event: Event name (``'domain-tag-add'``)
|
||||||
|
:param tag: tag name
|
||||||
|
|
||||||
|
.. event:: domain-tag-delete (subject, event, tag)
|
||||||
|
|
||||||
|
A feature was removed.
|
||||||
|
|
||||||
|
:param subject: Event emitter (the qube object)
|
||||||
|
:param event: Event name (``'domain-tag-delete'``)
|
||||||
|
:param tag: tag name
|
||||||
|
|
||||||
.. event:: feature-request (subject, event, *, untrusted_features)
|
.. event:: feature-request (subject, event, *, untrusted_features)
|
||||||
|
|
||||||
The domain is performing a feature request.
|
The domain is performing a feature request.
|
||||||
|
Loading…
Reference in New Issue
Block a user