qubes: fix VM instantiation and loading
This commit is contained in:
parent
8c437f4053
commit
45977fc873
@ -807,13 +807,20 @@ class PropertyHolder(qubes.events.Emitter):
|
|||||||
Members:
|
Members:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, xml, *args, **kwargs):
|
def __init__(self, xml, **kwargs):
|
||||||
super(PropertyHolder, self).__init__(*args)
|
|
||||||
self.xml = xml
|
self.xml = xml
|
||||||
|
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
super(PropertyHolder, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def property_list(cls, load_stage=None):
|
def property_list(cls, load_stage=None):
|
||||||
@ -889,10 +896,9 @@ class PropertyHolder(qubes.events.Emitter):
|
|||||||
|
|
||||||
``property-set`` events are not fired for each individual property.
|
``property-set`` events are not fired for each individual property.
|
||||||
|
|
||||||
:param lxml.etree._Element xml: XML node reference
|
:param int load_stage: Stage of loading.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.events_enabled = False
|
|
||||||
all_names = set(
|
all_names = set(
|
||||||
prop.__name__ for prop in self.property_list(load_stage))
|
prop.__name__ for prop in self.property_list(load_stage))
|
||||||
for node in self.xml.xpath('./properties/property'):
|
for node in self.xml.xpath('./properties/property'):
|
||||||
@ -900,15 +906,10 @@ class PropertyHolder(qubes.events.Emitter):
|
|||||||
value = node.get('ref') or node.text
|
value = node.get('ref') or node.text
|
||||||
|
|
||||||
if not name in all_names:
|
if not name in all_names:
|
||||||
raise AttributeError(
|
continue
|
||||||
'No property {!r} found in {!r}'.format(
|
|
||||||
name, self.__class__))
|
|
||||||
|
|
||||||
setattr(self, name, value)
|
setattr(self, name, value)
|
||||||
|
|
||||||
self.events_enabled = True
|
|
||||||
self.fire_event('property-loaded')
|
|
||||||
|
|
||||||
|
|
||||||
def xml_properties(self, with_defaults=False):
|
def xml_properties(self, with_defaults=False):
|
||||||
'''Iterator that yields XML nodes representing set properties.
|
'''Iterator that yields XML nodes representing set properties.
|
||||||
@ -1202,8 +1203,10 @@ class Qubes(PropertyHolder):
|
|||||||
|
|
||||||
# stage 2: load VMs
|
# stage 2: load VMs
|
||||||
for node in self._xml.xpath('./domains/domain'):
|
for node in self._xml.xpath('./domains/domain'):
|
||||||
cls = qubes.vm.load(node.get("class"))
|
# pylint: disable=no-member
|
||||||
vm = cls.fromxml(self, node)
|
cls = qubes.vm.BaseVM.register[node.get('class')]
|
||||||
|
vm = cls(self, node)
|
||||||
|
vm.load_properties(load_stage=2)
|
||||||
self.domains.add(vm)
|
self.domains.add(vm)
|
||||||
|
|
||||||
if not 0 in self.domains:
|
if not 0 in self.domains:
|
||||||
@ -1236,6 +1239,10 @@ class Qubes(PropertyHolder):
|
|||||||
else:
|
else:
|
||||||
self.clockvm.services['ntpd'] = False
|
self.clockvm.services['ntpd'] = False
|
||||||
|
|
||||||
|
for vm in self.domains:
|
||||||
|
vm.events_enabled = True
|
||||||
|
vm.fire_event('domain-loaded')
|
||||||
|
|
||||||
|
|
||||||
def _init(self):
|
def _init(self):
|
||||||
self._open_store()
|
self._open_store()
|
||||||
|
@ -41,8 +41,6 @@ class Plugin(type):
|
|||||||
# we've got root class
|
# we've got root class
|
||||||
cls.register = {}
|
cls.register = {}
|
||||||
|
|
||||||
def __getitem__(cls, name):
|
|
||||||
return cls.register[name]
|
|
||||||
|
|
||||||
def load(modfile):
|
def load(modfile):
|
||||||
'''Load (import) all plugins from subpackage.
|
'''Load (import) all plugins from subpackage.
|
||||||
|
@ -136,7 +136,8 @@ class TC_10_BaseVM(qubes.tests.QubesTestCase):
|
|||||||
|
|
||||||
def test_000_load(self):
|
def test_000_load(self):
|
||||||
node = self.xml.xpath('//domain')[0]
|
node = self.xml.xpath('//domain')[0]
|
||||||
vm = TestVM.fromxml(None, node)
|
vm = TestVM(None, node)
|
||||||
|
vm.load_properties(load_stage=None)
|
||||||
|
|
||||||
self.assertEqual(vm.qid, 1)
|
self.assertEqual(vm.qid, 1)
|
||||||
self.assertEqual(vm.testprop, 'testvalue')
|
self.assertEqual(vm.testprop, 'testvalue')
|
||||||
@ -144,13 +145,15 @@ class TC_10_BaseVM(qubes.tests.QubesTestCase):
|
|||||||
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': 'tagvalue'})
|
||||||
self.assertEqual(vm.devices, {'pci': ['00:11.22']})
|
|
||||||
self.assertEqual(vm.services, {
|
self.assertEqual(vm.services, {
|
||||||
'testservice': True,
|
'testservice': True,
|
||||||
'enabledservice': True,
|
'enabledservice': True,
|
||||||
'disabledservice': False,
|
'disabledservice': False,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.assertItemsEqual(vm.devices.keys(), ('pci',))
|
||||||
|
self.assertItemsEqual(vm.devices['pci'], ('00:11.22',))
|
||||||
|
|
||||||
self.assertXMLIsValid(vm.__xml__(), 'domain.rng')
|
self.assertXMLIsValid(vm.__xml__(), 'domain.rng')
|
||||||
|
|
||||||
def test_001_nxproperty(self):
|
def test_001_nxproperty(self):
|
||||||
@ -159,6 +162,8 @@ class TC_10_BaseVM(qubes.tests.QubesTestCase):
|
|||||||
<domains>
|
<domains>
|
||||||
<domain id="domain-1" class="TestVM">
|
<domain id="domain-1" class="TestVM">
|
||||||
<properties>
|
<properties>
|
||||||
|
<property name="qid">1</property>
|
||||||
|
<property name="name">domain1</property>
|
||||||
<property name="nxproperty">nxvalue</property>
|
<property name="nxproperty">nxvalue</property>
|
||||||
</properties>
|
</properties>
|
||||||
</domain>
|
</domain>
|
||||||
@ -168,5 +173,5 @@ class TC_10_BaseVM(qubes.tests.QubesTestCase):
|
|||||||
|
|
||||||
node = xml.xpath('//domain')[0]
|
node = xml.xpath('//domain')[0]
|
||||||
|
|
||||||
with self.assertRaises(AttributeError):
|
with self.assertRaises(TypeError):
|
||||||
TestVM.fromxml(None, node)
|
TestVM(None, node)
|
||||||
|
@ -47,9 +47,6 @@ import qubes.plugins
|
|||||||
|
|
||||||
class BaseVMMeta(qubes.plugins.Plugin, qubes.events.EmitterMeta):
|
class BaseVMMeta(qubes.plugins.Plugin, qubes.events.EmitterMeta):
|
||||||
'''Metaclass for :py:class:`.BaseVM`'''
|
'''Metaclass for :py:class:`.BaseVM`'''
|
||||||
def __init__(cls, name, bases, dict_):
|
|
||||||
super(BaseVMMeta, cls).__init__(name, bases, dict_)
|
|
||||||
cls.__hooks__ = collections.defaultdict(list)
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceCollection(object):
|
class DeviceCollection(object):
|
||||||
@ -122,7 +119,8 @@ class DeviceManager(dict):
|
|||||||
self._vm = vm
|
self._vm = vm
|
||||||
|
|
||||||
def __missing__(self, key):
|
def __missing__(self, key):
|
||||||
return DeviceCollection(self._vm, key)
|
self[key] = DeviceCollection(self._vm, key)
|
||||||
|
return self[key]
|
||||||
|
|
||||||
|
|
||||||
class BaseVM(qubes.PropertyHolder):
|
class BaseVM(qubes.PropertyHolder):
|
||||||
@ -142,8 +140,12 @@ class BaseVM(qubes.PropertyHolder):
|
|||||||
__metaclass__ = BaseVMMeta
|
__metaclass__ = BaseVMMeta
|
||||||
|
|
||||||
def __init__(self, app, xml, services=None, devices=None, tags=None,
|
def __init__(self, app, xml, services=None, devices=None, tags=None,
|
||||||
*args, **kwargs):
|
**kwargs):
|
||||||
# pylint: disable=redefined-outer-name
|
# pylint: disable=redefined-outer-name
|
||||||
|
|
||||||
|
self.events_enabled = False
|
||||||
|
super(BaseVM, self).__init__(xml, **kwargs)
|
||||||
|
|
||||||
#: mother :py:class:`qubes.Qubes` object
|
#: mother :py:class:`qubes.Qubes` object
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
@ -157,21 +159,35 @@ class BaseVM(qubes.PropertyHolder):
|
|||||||
#: user-specified tags
|
#: user-specified tags
|
||||||
self.tags = tags or {}
|
self.tags = tags or {}
|
||||||
|
|
||||||
self.events_enabled = False
|
if self.xml is not None:
|
||||||
all_names = set(prop.__name__
|
# services
|
||||||
for prop in self.property_list(load_stage=2))
|
for node in xml.xpath('./services/service'):
|
||||||
for key in list(kwargs.keys()):
|
self.services[node.text] = bool(
|
||||||
if not key in all_names:
|
ast.literal_eval(node.get('enabled', 'True').capitalize()))
|
||||||
raise AttributeError(
|
|
||||||
'No property {!r} found in {!r}'.format(
|
|
||||||
key, self.__class__))
|
|
||||||
setattr(self, key, kwargs[key])
|
|
||||||
del kwargs[key]
|
|
||||||
|
|
||||||
super(BaseVM, self).__init__(xml, *args, **kwargs)
|
# devices (pci, usb, ...)
|
||||||
|
for parent in xml.xpath('./devices'):
|
||||||
|
devclass = parent.get('class')
|
||||||
|
for node in parent.xpath('./device'):
|
||||||
|
self.devices[devclass].attach(node.text)
|
||||||
|
|
||||||
self.events_enabled = True
|
# tags
|
||||||
self.fire_event('property-load')
|
for node in xml.xpath('./tags/tag'):
|
||||||
|
self.tags[node.get('name')] = node.text
|
||||||
|
|
||||||
|
# TODO: firewall, policy
|
||||||
|
|
||||||
|
# check if properties are appropriate
|
||||||
|
all_names = set(prop.__name__ for prop in self.property_list())
|
||||||
|
|
||||||
|
sys.stderr.write('all_names={!r}\n'.format(all_names))
|
||||||
|
|
||||||
|
for node in self.xml.xpath('./properties/property'):
|
||||||
|
name = node.get('name')
|
||||||
|
if not name in all_names:
|
||||||
|
raise TypeError(
|
||||||
|
'property {!r} not applicable to {!r}'.format(
|
||||||
|
name, self.__class__.__name__))
|
||||||
|
|
||||||
|
|
||||||
def add_new_vm(self, vm):
|
def add_new_vm(self, vm):
|
||||||
@ -211,47 +227,6 @@ class BaseVM(qubes.PropertyHolder):
|
|||||||
|
|
||||||
return vm
|
return vm
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fromxml(cls, app, xml, load_stage=2):
|
|
||||||
'''Create VM from XML node
|
|
||||||
|
|
||||||
:param qubes.Qubes app: :py:class:`qubes.Qubes` application instance
|
|
||||||
:param lxml.etree._Element xml: XML node reference
|
|
||||||
:param int load_stage: do not change the default (2) unless you know, \
|
|
||||||
what you are doing
|
|
||||||
''' # pylint: disable=redefined-outer-name
|
|
||||||
|
|
||||||
if xml is None:
|
|
||||||
return cls(app)
|
|
||||||
|
|
||||||
services = {}
|
|
||||||
devices = collections.defaultdict(list)
|
|
||||||
tags = {}
|
|
||||||
|
|
||||||
# services
|
|
||||||
for node in xml.xpath('./services/service'):
|
|
||||||
services[node.text] = bool(
|
|
||||||
ast.literal_eval(node.get('enabled', 'True')))
|
|
||||||
|
|
||||||
# devices (pci, usb, ...)
|
|
||||||
for parent in xml.xpath('./devices'):
|
|
||||||
devclass = parent.get('class')
|
|
||||||
for node in parent.xpath('./device'):
|
|
||||||
devices[devclass].append(node.text)
|
|
||||||
|
|
||||||
# tags
|
|
||||||
for node in xml.xpath('./tags/tag'):
|
|
||||||
tags[node.get('name')] = node.text
|
|
||||||
|
|
||||||
# properties
|
|
||||||
self = cls(app, xml=xml, services=services, devices=devices, tags=tags)
|
|
||||||
self.load_properties(load_stage=load_stage)
|
|
||||||
|
|
||||||
# TODO: firewall, policy
|
|
||||||
|
|
||||||
# sys.stderr.write('{}.fromxml return\n'.format(cls.__name__))
|
|
||||||
return self
|
|
||||||
|
|
||||||
|
|
||||||
def __xml__(self):
|
def __xml__(self):
|
||||||
element = lxml.etree.Element('domain')
|
element = lxml.etree.Element('domain')
|
||||||
@ -614,9 +589,6 @@ class BaseVM(qubes.PropertyHolder):
|
|||||||
|
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
def load(class_, D):
|
|
||||||
cls = BaseVM[class_]
|
|
||||||
return cls(D)
|
|
||||||
|
|
||||||
__all__ = ['BaseVMMeta', 'DeviceCollection', 'DeviceManager', 'BaseVM'] \
|
__all__ = ['BaseVMMeta', 'DeviceCollection', 'DeviceManager', 'BaseVM'] \
|
||||||
+ qubes.plugins.load(__file__)
|
+ qubes.plugins.load(__file__)
|
||||||
|
Loading…
Reference in New Issue
Block a user