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>
|
||||
|
||||
<tags>
|
||||
<tag name="testtag">tagvalue</tag>
|
||||
<tag name="testtag"/>
|
||||
</tags>
|
||||
|
||||
<features>
|
||||
@ -101,7 +101,7 @@ class TC_10_BaseVM(qubes.tests.QubesTestCase):
|
||||
self.assertEqual(vm.testprop, 'testvalue')
|
||||
self.assertEqual(vm.testlabel, 'label-1')
|
||||
self.assertEqual(vm.defaultprop, 'defaultvalue')
|
||||
self.assertEqual(vm.tags, {'testtag': 'tagvalue'})
|
||||
self.assertEqual(vm.tags, {'testtag'})
|
||||
self.assertEqual(vm.features, {
|
||||
'testfeature_empty': '',
|
||||
'testfeature_aqq': 'aqq',
|
||||
|
@ -28,6 +28,7 @@
|
||||
import datetime
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import xml.parsers.expat
|
||||
@ -159,6 +160,82 @@ class Features(dict):
|
||||
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):
|
||||
'''Base class for all VMs
|
||||
|
||||
@ -192,7 +269,7 @@ class BaseVM(qubes.PropertyHolder):
|
||||
self.devices = devices or qubes.devices.DeviceManager(self)
|
||||
|
||||
#: user-specified tags
|
||||
self.tags = tags or {}
|
||||
self.tags = Tags(self, tags or ())
|
||||
|
||||
#: logger instance for logging messages related to this VM
|
||||
self.log = None
|
||||
@ -223,7 +300,7 @@ class BaseVM(qubes.PropertyHolder):
|
||||
|
||||
# tags
|
||||
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.
|
||||
|
||||
@ -262,7 +339,6 @@ class BaseVM(qubes.PropertyHolder):
|
||||
tags = lxml.etree.Element('tags')
|
||||
for tag in self.tags:
|
||||
node = lxml.etree.Element('tag', name=tag)
|
||||
node.text = self.tags[tag]
|
||||
tags.append(node)
|
||||
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 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)
|
||||
|
||||
The domain is performing a feature request.
|
||||
|
Loading…
Reference in New Issue
Block a user