core3: some properties can be set only once
Some properties should not be changed by user at will (like UUID). The solution is to make them write-once, so they will be set when loading from XML and frozen for the lifespan of the object holding the property. When desperately needed, users may edit XML by hand. fixes QubesOS/qubes-issues#1235
This commit is contained in:
parent
a017d78174
commit
b4d51b016b
@ -585,7 +585,8 @@ class property(object): # pylint: disable=redefined-builtin,invalid-name
|
||||
_NO_DEFAULT = object()
|
||||
|
||||
def __init__(self, name, setter=None, saver=None, type=None,
|
||||
default=_NO_DEFAULT, load_stage=2, order=0, save_via_ref=False,
|
||||
default=_NO_DEFAULT, write_once=False, load_stage=2, order=0,
|
||||
save_via_ref=False,
|
||||
ls_head=None, ls_width=None, doc=None):
|
||||
# pylint: disable=redefined-builtin
|
||||
self.__name__ = name
|
||||
@ -594,6 +595,7 @@ class property(object): # pylint: disable=redefined-builtin,invalid-name
|
||||
lambda self, prop, value: str(value))
|
||||
self._type = type
|
||||
self._default = default
|
||||
self._write_once = write_once
|
||||
self.order = order
|
||||
self.load_stage = load_stage
|
||||
self.save_via_ref = save_via_ref
|
||||
@ -628,6 +630,8 @@ class property(object): # pylint: disable=redefined-builtin,invalid-name
|
||||
|
||||
|
||||
def __set__(self, instance, value):
|
||||
self._enforce_write_once(instance)
|
||||
|
||||
if value is self.__class__.DEFAULT:
|
||||
self.__delete__(instance)
|
||||
return
|
||||
@ -661,6 +665,8 @@ class property(object): # pylint: disable=redefined-builtin,invalid-name
|
||||
|
||||
|
||||
def __delete__(self, instance):
|
||||
self._enforce_write_once(instance)
|
||||
|
||||
try:
|
||||
oldvalue = getattr(instance, self.__name__)
|
||||
has_oldvalue = True
|
||||
@ -700,6 +706,13 @@ class property(object): # pylint: disable=redefined-builtin,invalid-name
|
||||
return self.__name__ == other.__name__
|
||||
|
||||
|
||||
def _enforce_write_once(self, instance):
|
||||
if self._write_once and not instance.property_is_default(self):
|
||||
raise AttributeError(
|
||||
'property {!r} is write-once and already set'.format(
|
||||
self.__name__))
|
||||
|
||||
|
||||
#
|
||||
# exceptions
|
||||
#
|
||||
@ -806,17 +819,20 @@ class PropertyHolder(qubes.events.Emitter):
|
||||
'''
|
||||
|
||||
def __init__(self, xml, **kwargs):
|
||||
super(PropertyHolder, self).__init__()
|
||||
self.xml = xml
|
||||
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
propvalues = {}
|
||||
|
||||
all_names = set(prop.__name__ for prop in self.property_list())
|
||||
for key in list(kwargs.keys()):
|
||||
if not key in all_names:
|
||||
continue
|
||||
setattr(self, key, kwargs.pop(key))
|
||||
propvalues[key] = kwargs.pop(key)
|
||||
|
||||
super(PropertyHolder, self).__init__(**kwargs)
|
||||
|
||||
for key, value in propvalues.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
@classmethod
|
||||
|
@ -134,7 +134,7 @@ class TC_10_property(qubes.tests.QubesTestCase):
|
||||
self.assertEqual(holder.testprop1, 5)
|
||||
self.assertNotEqual(holder.testprop1, '5')
|
||||
|
||||
def test_090_delete(self):
|
||||
def test_080_delete(self):
|
||||
self.holder.testprop1 = 'testvalue'
|
||||
try:
|
||||
if self.holder.testprop1 != 'testvalue':
|
||||
@ -147,7 +147,7 @@ class TC_10_property(qubes.tests.QubesTestCase):
|
||||
with self.assertRaises(AttributeError):
|
||||
self.holder.testprop1
|
||||
|
||||
def test_090_delete_by_assign(self):
|
||||
def test_081_delete_by_assign(self):
|
||||
self.holder.testprop1 = 'testvalue'
|
||||
try:
|
||||
if self.holder.testprop1 != 'testvalue':
|
||||
@ -160,7 +160,7 @@ class TC_10_property(qubes.tests.QubesTestCase):
|
||||
with self.assertRaises(AttributeError):
|
||||
self.holder.testprop1
|
||||
|
||||
def test_092_delete_default(self):
|
||||
def test_082_delete_default(self):
|
||||
class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
|
||||
testprop1 = qubes.property('testprop1', default='defaultvalue')
|
||||
holder = MyTestHolder(None)
|
||||
@ -176,6 +176,26 @@ class TC_10_property(qubes.tests.QubesTestCase):
|
||||
|
||||
self.assertEqual(holder.testprop1, 'defaultvalue')
|
||||
|
||||
def test_090_write_once_set(self):
|
||||
class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
|
||||
testprop1 = qubes.property('testprop1', write_once=True)
|
||||
holder = MyTestHolder(None)
|
||||
|
||||
holder.testprop1 = 'testvalue'
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
holder.testprop1 = 'testvalue2'
|
||||
|
||||
def test_091_write_once_delete(self):
|
||||
class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
|
||||
testprop1 = qubes.property('testprop1', write_once=True)
|
||||
holder = MyTestHolder(None)
|
||||
|
||||
holder.testprop1 = 'testvalue'
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
del holder.testprop1
|
||||
|
||||
|
||||
class TestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
|
||||
testprop1 = qubes.property('testprop1', order=0)
|
||||
|
@ -59,15 +59,16 @@ class TC_00_Column(qubes.tests.QubesTestCase):
|
||||
class TC_90_globals(qubes.tests.QubesTestCase):
|
||||
# @qubes.tests.skipUnlessDom0
|
||||
def test_100_simple_flag(self):
|
||||
flag = qubes.tools.qvm_ls.simple_flag(1, 'T', 'qid')
|
||||
flag = qubes.tools.qvm_ls.simple_flag(1, 'T', 'internal')
|
||||
|
||||
# TODO after serious testing of QubesVM and Qubes app, this should be
|
||||
# using normal components
|
||||
app = qubes.tests.vm.adminvm.TestApp()
|
||||
vm = qubes.vm.adminvm.AdminVM(app, None, qid=0, name='dom0')
|
||||
vm = qubes.vm.adminvm.AdminVM(app, None,
|
||||
qid=0, name='dom0', internal='False')
|
||||
|
||||
self.assertFalse(flag(None, vm))
|
||||
vm.qid = 1
|
||||
vm.internal = 'True'
|
||||
self.assertTrue(flag(None, vm))
|
||||
|
||||
|
||||
|
@ -154,7 +154,7 @@ class QubesVM(qubes.vm.BaseVM):
|
||||
# type=bool, setter=qubes.property.bool,
|
||||
# doc='`True` if it is NetVM or ProxyVM, false otherwise.')
|
||||
|
||||
qid = qubes.property('qid', type=int,
|
||||
qid = qubes.property('qid', type=int, write_once=True,
|
||||
setter=_setter_qid,
|
||||
ls_width=3,
|
||||
doc='''Internal, persistent identificator of particular domain. Note
|
||||
@ -164,7 +164,7 @@ class QubesVM(qubes.vm.BaseVM):
|
||||
ls_width=31,
|
||||
doc='User-specified name of the domain.')
|
||||
|
||||
uuid = qubes.property('uuid', type=uuid.UUID,
|
||||
uuid = qubes.property('uuid', type=uuid.UUID, write_once=True,
|
||||
ls_width=36,
|
||||
doc='UUID from libvirt.')
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user