qubes: fix Qubes instantiation

This commit is contained in:
Wojtek Porczyk 2015-01-15 12:57:44 +01:00
parent 8e16dd2b28
commit d937d39c9e
3 changed files with 69 additions and 36 deletions

View File

@ -16,7 +16,9 @@ __version__ = 'R3'
import ast import ast
import atexit import atexit
import collections import collections
import errno
import grp import grp
import logging
import os import os
import os.path import os.path
import sys import sys
@ -102,7 +104,7 @@ class VMMConnection(object):
self._xs = xen.lowlevel.xs.xs() self._xs = xen.lowlevel.xs.xs()
if 'xen.lowlevel.cs' in sys.modules: if 'xen.lowlevel.cs' in sys.modules:
self._xc = xen.lowlevel.xc.xc() self._xc = xen.lowlevel.xc.xc()
self._libvirt_conn = libvirt.open(defaults['libvirt_uri']) self._libvirt_conn = libvirt.open(qubes.config.defaults['libvirt_uri'])
if self._libvirt_conn is None: if self._libvirt_conn is None:
raise QubesException("Failed connect to libvirt driver") raise QubesException("Failed connect to libvirt driver")
libvirt.registerErrorHandler(self._libvirt_error_handler, None) libvirt.registerErrorHandler(self._libvirt_error_handler, None)
@ -150,7 +152,7 @@ class QubesHost(object):
''' '''
def __init__(self, app): def __init__(self, app):
self._app = app self.app = app
self._no_cpus = None self._no_cpus = None
@ -159,7 +161,7 @@ class QubesHost(object):
return return
(model, memory, cpus, mhz, nodes, socket, cores, threads) = \ (model, memory, cpus, mhz, nodes, socket, cores, threads) = \
self._app.vmm.libvirt_conn.getInfo() self.app.vmm.libvirt_conn.getInfo()
self._total_mem = long(memory) * 1024 self._total_mem = long(memory) * 1024
self._no_cpus = cpus self._no_cpus = cpus
@ -214,7 +216,7 @@ class QubesHost(object):
previous_time = time.time() previous_time = time.time()
previous = {} previous = {}
try: try:
info = self._app.vmm.xc.domain_getinfo(0, qubes_max_qid) info = self.app.vmm.xc.domain_getinfo(0, qubes_max_qid)
except AttributeError: except AttributeError:
raise NotImplementedError( raise NotImplementedError(
'This function requires Xen hypervisor') 'This function requires Xen hypervisor')
@ -761,9 +763,12 @@ class PropertyHolder(qubes.events.Emitter):
''' '''
def __init__(self, xml, *args, **kwargs): def __init__(self, xml, *args, **kwargs):
super(PropertyHolder, self).__init__(*args, **kwargs) super(PropertyHolder, self).__init__(*args)
self.xml = xml self.xml = xml
for key, value in kwargs.items():
setattr(self, key, value)
@classmethod @classmethod
def get_props_list(cls, load_stage=None): def get_props_list(cls, load_stage=None):
@ -1051,11 +1056,15 @@ class Qubes(PropertyHolder):
def __init__(self, store='/var/lib/qubes/qubes.xml'): def __init__(self, store='/var/lib/qubes/qubes.xml'):
super(Qubes, self).__init__(xml=None)
self.log = logging.getLogger('app')
self._extensions = set(ext(self) self._extensions = set(ext(self)
for ext in qubes.ext.Extension.register.values()) for ext in qubes.ext.Extension.register.values())
#: collection of all VMs managed by this Qubes instance #: collection of all VMs managed by this Qubes instance
self.domains = VMCollection() self.domains = VMCollection(self)
#: collection of all available labels for VMs #: collection of all available labels for VMs
self.labels = {} self.labels = {}
@ -1067,28 +1076,46 @@ class Qubes(PropertyHolder):
self.host = QubesHost(self) self.host = QubesHost(self)
self._store = store self._store = store
self.load()
try:
self.load()
except IOError:
self._init()
super(Qubes, self).__init__(
xml=lxml.etree.parse(self.qubes_store_file))
def _open_store(self): def _open_store(self):
'''Open qubes.xml
This method takes care of creation of the store when it does not exist.
:raises OSError: on failure
:raises lxml.etree.XMLSyntaxError: on syntax error in qubes.xml
'''
if hasattr(self, '_storefd'): if hasattr(self, '_storefd'):
return return
self._storefd = open(self._store, 'r+') try:
fd = os.open(self._store,
os.O_RDWR | os.O_CREAT | os.O_EXCL | 0o660)
parsexml = False
except OSError as e:
if e.errno != errno.EEXIST:
raise
# file does exist
fd = os.open(self._store, os.O_RDWR)
parsexml = True
self._storefd = os.fdopen(fd, 'r+b')
if os.name == 'posix': if os.name == 'posix':
fcntl.lockf(self.qubes_store_file, fcntl.LOCK_EX) fcntl.lockf(self._storefd, fcntl.LOCK_EX)
elif os.name == 'nt': elif os.name == 'nt':
overlapped = pywintypes.OVERLAPPED() win32file.LockFileEx(
win32file.LockFileEx(win32file._get_osfhandle(self.qubes_store_file.fileno()), win32file._get_osfhandle(self._storefd.fileno()),
win32con.LOCKFILE_EXCLUSIVE_LOCK, 0, -0x10000, overlapped) win32con.LOCKFILE_EXCLUSIVE_LOCK,
0, -0x10000,
pywintypes.OVERLAPPED())
if parsexml:
self.xml = lxml.etree.parse(self._storefd)
# else: it will remain None, as set by PropertyHolder
def load(self): def load(self):
@ -1098,6 +1125,10 @@ class Qubes(PropertyHolder):
''' '''
self._open_store() self._open_store()
if self.xml is None:
self._init()
return
# stage 1: load labels # stage 1: load labels
for node in self._xml.xpath('./labels/label'): for node in self._xml.xpath('./labels/label'):
label = Label.fromxml(node) label = Label.fromxml(node)
@ -1110,7 +1141,8 @@ class Qubes(PropertyHolder):
self.domains.add(vm) self.domains.add(vm)
if not 0 in self.domains: if not 0 in self.domains:
self.domains.add(qubes.vm.adminvm.AdminVM(self)) self.domains.add(qubes.vm.adminvm.AdminVM(
self, None, qid=0, name='dom0'))
# stage 3: load global properties # stage 3: load global properties
self.load_properties(self.xml, load_stage=3) self.load_properties(self.xml, load_stage=3)
@ -1153,12 +1185,16 @@ class Qubes(PropertyHolder):
8: Label(8, '0x000000', 'black'), 8: Label(8, '0x000000', 'black'),
} }
self.domains.add(qubes.vm.adminvm.AdminVM(
self, None, qid=0, name='dom0'))
def __del__(self): def __del__(self):
# intentionally do not call explicit unlock to not unlock the file # intentionally do not call explicit unlock to not unlock the file
# before all buffers are flushed # before all buffers are flushed
self._storefd.close() if hasattr(self, '_storefd'):
del self._storefd self._storefd.close()
del self._storefd
def __xml__(self): def __xml__(self):

View File

@ -120,6 +120,10 @@ class DeviceCollection(object):
return item in self._set return item in self._set
def __len__(self):
return len(self._set)
class DeviceManager(dict): class DeviceManager(dict):
'''Device manager that hold all devices by their classess. '''Device manager that hold all devices by their classess.

View File

@ -54,9 +54,9 @@ except ImportError:
def _setter_qid(self, prop, value): def _setter_qid(self, prop, value):
if not 0 <= value <= qubes.MAX_QID: if not 0 <= value <= qubes.config.max_qid:
raise ValueError( raise ValueError(
'{} value must be between 0 and qubes.MAX_QID'.format( '{} value must be between 0 and qubes.config.max_qid'.format(
prop.__name__)) prop.__name__))
return value return value
@ -182,7 +182,8 @@ class QubesVM(qubes.vm.BaseVM):
# XXX not applicable to HVM? # XXX not applicable to HVM?
kernelopts = qubes.property('kernelopts', type=str, load_stage=4, kernelopts = qubes.property('kernelopts', type=str, load_stage=4,
default=(lambda self: defaults['kernelopts_pcidevs'] \ default=(lambda self: defaults['kernelopts_pcidevs'] \
if len(self.devices['pci']) > 0 else defaults['kernelopts']), if len(self.devices['pci']) > 0 \
else qubes.config.defaults['kernelopts']),
doc='Kernel command line passed to domain.') doc='Kernel command line passed to domain.')
mac = qubes.property('mac', type=str, mac = qubes.property('mac', type=str,
@ -398,25 +399,17 @@ class QubesVM(qubes.vm.BaseVM):
# constructor # constructor
# #
def __init__(self, app, xml): def __init__(self, app, xml, **kwargs):
super(QubesVM, self).__init__(app, xml) super(QubesVM, self).__init__(app, xml, **kwargs)
#Init private attrs #Init private attrs
self._libvirt_domain = None self._libvirt_domain = None
self._qdb_connection = None self._qdb_connection = None
assert self.__qid < qubes_max_qid, "VM id out of bounds!" assert self.qid < qubes.config.max_qid, "VM id out of bounds!"
assert self.name is not None assert self.name is not None
if not self.verify_name(self.name):
msg = ("'%s' is invalid VM name (invalid characters, over 31 chars long, "
"or one of 'none', 'true', 'false')") % self.name
if xml is not None:
print >>sys.stderr, "WARNING: %s" % msg
else:
raise QubesException(msg)
# Not in generic way to not create QubesHost() to frequently # Not in generic way to not create QubesHost() to frequently
# XXX this doesn't apply, host is instantiated once # XXX this doesn't apply, host is instantiated once
if self.maxmem is None and not self.app.vmm.offline_mode: if self.maxmem is None and not self.app.vmm.offline_mode: