core: do not assume that libvirt domain is always defined
Define it only when really needed: - during VM creation - to generate UUID - just before VM startup As a consequence we must handle possible exception when accessing vm.libvirt_domain. It would be a good idea to make this field private in the future. It isn't possible for now because block_* are external for QubesVm class. This hopefully fixes race condition when Qubes Manager tries to access libvirt_domain (using some QubesVm.*) at the same time as other tool is removing the domain. Additionally if Qubes Manage would loose that race, it could define the domain again leaving some unused libvirt domain (blocking that domain name for future use).
This commit is contained in:
parent
e8a1e3469e
commit
075f35b873
@ -660,9 +660,14 @@ class QubesVm(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def xid(self):
|
def xid(self):
|
||||||
if self.libvirt_domain is None:
|
try:
|
||||||
return -1
|
|
||||||
return self.libvirt_domain.ID()
|
return self.libvirt_domain.ID()
|
||||||
|
except libvirt.libvirtError:
|
||||||
|
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def get_xid(self):
|
def get_xid(self):
|
||||||
# obsoleted
|
# obsoleted
|
||||||
@ -670,34 +675,17 @@ class QubesVm(object):
|
|||||||
|
|
||||||
def _update_libvirt_domain(self):
|
def _update_libvirt_domain(self):
|
||||||
domain_config = self.create_config_file()
|
domain_config = self.create_config_file()
|
||||||
try:
|
|
||||||
self._libvirt_domain = vmm.libvirt_conn.defineXML(domain_config)
|
self._libvirt_domain = vmm.libvirt_conn.defineXML(domain_config)
|
||||||
self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID())
|
self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID())
|
||||||
except libvirt.libvirtError:
|
|
||||||
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
|
||||||
# accept the fact that libvirt doesn't know anything about this
|
|
||||||
# domain...
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def libvirt_domain(self):
|
def libvirt_domain(self):
|
||||||
if self._libvirt_domain is not None:
|
if self._libvirt_domain is None:
|
||||||
return self._libvirt_domain
|
|
||||||
|
|
||||||
try:
|
|
||||||
if self.uuid is not None:
|
if self.uuid is not None:
|
||||||
self._libvirt_domain = vmm.libvirt_conn.lookupByUUID(self.uuid.bytes)
|
self._libvirt_domain = vmm.libvirt_conn.lookupByUUID(self.uuid.bytes)
|
||||||
else:
|
else:
|
||||||
self._libvirt_domain = vmm.libvirt_conn.lookupByName(self.name)
|
self._libvirt_domain = vmm.libvirt_conn.lookupByName(self.name)
|
||||||
self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID())
|
self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID())
|
||||||
except libvirt.libvirtError:
|
|
||||||
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
|
||||||
self._update_libvirt_domain()
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
return self._libvirt_domain
|
return self._libvirt_domain
|
||||||
|
|
||||||
def get_uuid(self):
|
def get_uuid(self):
|
||||||
@ -712,33 +700,41 @@ class QubesVm(object):
|
|||||||
if dry_run:
|
if dry_run:
|
||||||
return 666
|
return 666
|
||||||
|
|
||||||
if self.libvirt_domain is None:
|
try:
|
||||||
return 0
|
|
||||||
|
|
||||||
if not self.libvirt_domain.isActive():
|
if not self.libvirt_domain.isActive():
|
||||||
return 0
|
return 0
|
||||||
return self.libvirt_domain.info()[1]
|
return self.libvirt_domain.info()[1]
|
||||||
|
except libvirt.libvirtError:
|
||||||
|
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
def get_cputime(self):
|
def get_cputime(self):
|
||||||
if dry_run:
|
if dry_run:
|
||||||
return 666
|
return 666
|
||||||
|
|
||||||
if self.libvirt_domain is None:
|
try:
|
||||||
return 0
|
|
||||||
|
|
||||||
if not self.libvirt_domain.isActive():
|
if not self.libvirt_domain.isActive():
|
||||||
return 0
|
return 0
|
||||||
return self.libvirt_domain.info()[4]
|
return self.libvirt_domain.info()[4]
|
||||||
|
except libvirt.libvirtError:
|
||||||
|
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
def get_mem_static_max(self):
|
def get_mem_static_max(self):
|
||||||
if dry_run:
|
if dry_run:
|
||||||
return 666
|
return 666
|
||||||
|
|
||||||
if self.libvirt_domain is None:
|
try:
|
||||||
return 0
|
|
||||||
|
|
||||||
return self.libvirt_domain.maxMemory()
|
return self.libvirt_domain.maxMemory()
|
||||||
|
except libvirt.libvirtError:
|
||||||
|
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
def get_prefmem(self):
|
def get_prefmem(self):
|
||||||
# TODO: qmemman is still xen specific
|
# TODO: qmemman is still xen specific
|
||||||
@ -757,12 +753,17 @@ class QubesVm(object):
|
|||||||
import random
|
import random
|
||||||
return random.random() * 100
|
return random.random() * 100
|
||||||
|
|
||||||
libvirt_domain = self.libvirt_domain
|
try:
|
||||||
if libvirt_domain and libvirt_domain.isActive():
|
if self.libvirt_domain.isActive():
|
||||||
return libvirt_domain.getCPUStats(
|
return self.libvirt_domain.getCPUStats(
|
||||||
libvirt.VIR_NODE_CPU_STATS_ALL_CPUS, 0)[0]['cpu_time']/10**9
|
libvirt.VIR_NODE_CPU_STATS_ALL_CPUS, 0)[0]['cpu_time']/10**9
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
except libvirt.libvirtError:
|
||||||
|
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
def get_disk_utilization_root_img(self):
|
def get_disk_utilization_root_img(self):
|
||||||
return qubes.qubesutils.get_disk_usage(self.root_img)
|
return qubes.qubesutils.get_disk_usage(self.root_img)
|
||||||
@ -777,10 +778,8 @@ class QubesVm(object):
|
|||||||
if dry_run:
|
if dry_run:
|
||||||
return "NA"
|
return "NA"
|
||||||
|
|
||||||
|
try:
|
||||||
libvirt_domain = self.libvirt_domain
|
libvirt_domain = self.libvirt_domain
|
||||||
if libvirt_domain is None:
|
|
||||||
return "NA"
|
|
||||||
|
|
||||||
if libvirt_domain.isActive():
|
if libvirt_domain.isActive():
|
||||||
if libvirt_domain.state()[0] == libvirt.VIR_DOMAIN_PAUSED:
|
if libvirt_domain.state()[0] == libvirt.VIR_DOMAIN_PAUSED:
|
||||||
return "Paused"
|
return "Paused"
|
||||||
@ -799,8 +798,12 @@ class QubesVm(object):
|
|||||||
return "Running"
|
return "Running"
|
||||||
else:
|
else:
|
||||||
return 'Halted'
|
return 'Halted'
|
||||||
|
except libvirt.libvirtError:
|
||||||
|
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||||
return "NA"
|
return "NA"
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def is_guid_running(self):
|
def is_guid_running(self):
|
||||||
xid = self.xid
|
xid = self.xid
|
||||||
@ -824,17 +827,28 @@ class QubesVm(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def is_running(self):
|
def is_running(self):
|
||||||
if self.libvirt_domain and self.libvirt_domain.isActive():
|
try:
|
||||||
|
if self.libvirt_domain.isActive():
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
except libvirt.libvirtError:
|
||||||
|
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
def is_paused(self):
|
def is_paused(self):
|
||||||
if self.libvirt_domain and self.libvirt_domain.state()[0] == \
|
try:
|
||||||
libvirt.VIR_DOMAIN_PAUSED:
|
if self.libvirt_domain.state()[0] == libvirt.VIR_DOMAIN_PAUSED:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
except libvirt.libvirtError:
|
||||||
|
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
def get_start_time(self):
|
def get_start_time(self):
|
||||||
if not self.is_running():
|
if not self.is_running():
|
||||||
@ -1153,6 +1167,9 @@ class QubesVm(object):
|
|||||||
else:
|
else:
|
||||||
shutil.copy(self.label.icon_path, self.icon_path)
|
shutil.copy(self.label.icon_path, self.icon_path)
|
||||||
|
|
||||||
|
# Make sure that we have UUID allocated
|
||||||
|
self._update_libvirt_domain()
|
||||||
|
|
||||||
# fire hooks
|
# fire hooks
|
||||||
for hook in self.hooks_create_on_disk:
|
for hook in self.hooks_create_on_disk:
|
||||||
hook(self, verbose, source_template=source_template)
|
hook(self, verbose, source_template=source_template)
|
||||||
@ -1202,6 +1219,9 @@ class QubesVm(object):
|
|||||||
print >> sys.stderr, "--> Copying icon: {0} -> {1}".format(src_vm.icon_path, self.icon_path)
|
print >> sys.stderr, "--> Copying icon: {0} -> {1}".format(src_vm.icon_path, self.icon_path)
|
||||||
shutil.copy(src_vm.icon_path, self.icon_path)
|
shutil.copy(src_vm.icon_path, self.icon_path)
|
||||||
|
|
||||||
|
# Make sure that we have UUID allocated
|
||||||
|
self._update_libvirt_domain()
|
||||||
|
|
||||||
# fire hooks
|
# fire hooks
|
||||||
for hook in self.hooks_clone_disk_files:
|
for hook in self.hooks_clone_disk_files:
|
||||||
hook(self, src_vm, verbose)
|
hook(self, src_vm, verbose)
|
||||||
@ -1237,6 +1257,8 @@ class QubesVm(object):
|
|||||||
for hook in self.hooks_remove_from_disk:
|
for hook in self.hooks_remove_from_disk:
|
||||||
hook(self)
|
hook(self)
|
||||||
|
|
||||||
|
self.libvirt_domain.undefine()
|
||||||
|
|
||||||
self.storage.remove_from_disk()
|
self.storage.remove_from_disk()
|
||||||
|
|
||||||
def write_firewall_conf(self, conf):
|
def write_firewall_conf(self, conf):
|
||||||
@ -1771,6 +1793,7 @@ class QubesVm(object):
|
|||||||
raise QubesException ("VM already stopped!")
|
raise QubesException ("VM already stopped!")
|
||||||
|
|
||||||
self.libvirt_domain.destroy()
|
self.libvirt_domain.destroy()
|
||||||
|
self.refresh()
|
||||||
|
|
||||||
def suspend(self):
|
def suspend(self):
|
||||||
self.log.debug('suspend()')
|
self.log.debug('suspend()')
|
||||||
|
@ -75,6 +75,10 @@ class QubesAdminVm(QubesNetVm):
|
|||||||
def get_mem_static_max(self):
|
def get_mem_static_max(self):
|
||||||
return vmm.libvirt_conn.getInfo()[1]
|
return vmm.libvirt_conn.getInfo()[1]
|
||||||
|
|
||||||
|
def get_cputime(self):
|
||||||
|
# TODO: measure it somehow
|
||||||
|
return 0
|
||||||
|
|
||||||
def get_disk_usage(self, file_or_dir):
|
def get_disk_usage(self, file_or_dir):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@ -219,6 +219,9 @@ class QubesHVm(QubesVm):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print >> sys.stderr, "WARNING: Failed to set VM icon: %s" % str(e)
|
print >> sys.stderr, "WARNING: Failed to set VM icon: %s" % str(e)
|
||||||
|
|
||||||
|
# Make sure that we have UUID allocated
|
||||||
|
self._update_libvirt_domain()
|
||||||
|
|
||||||
# fire hooks
|
# fire hooks
|
||||||
for hook in self.hooks_create_on_disk:
|
for hook in self.hooks_create_on_disk:
|
||||||
hook(self, verbose, source_template=source_template)
|
hook(self, verbose, source_template=source_template)
|
||||||
|
@ -334,9 +334,18 @@ def block_check_attached(qvmc, device):
|
|||||||
raise QubesException("You need to pass qvmc argument")
|
raise QubesException("You need to pass qvmc argument")
|
||||||
|
|
||||||
for vm in qvmc.values():
|
for vm in qvmc.values():
|
||||||
|
try:
|
||||||
libvirt_domain = vm.libvirt_domain
|
libvirt_domain = vm.libvirt_domain
|
||||||
if libvirt_domain:
|
if libvirt_domain:
|
||||||
xml = vm.libvirt_domain.XMLDesc()
|
xml = libvirt_domain.XMLDesc()
|
||||||
|
else:
|
||||||
|
xml = None
|
||||||
|
except libvirt.libvirtError:
|
||||||
|
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||||
|
libvirt_domain = None
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
if xml:
|
||||||
parsed_xml = etree.fromstring(xml)
|
parsed_xml = etree.fromstring(xml)
|
||||||
disks = parsed_xml.xpath("//domain/devices/disk")
|
disks = parsed_xml.xpath("//domain/devices/disk")
|
||||||
for disk in disks:
|
for disk in disks:
|
||||||
@ -720,8 +729,15 @@ class QubesWatch(object):
|
|||||||
self._device_removed, None)
|
self._device_removed, None)
|
||||||
# TODO: device attach libvirt event
|
# TODO: device attach libvirt event
|
||||||
for vm in vmm.libvirt_conn.listAllDomains():
|
for vm in vmm.libvirt_conn.listAllDomains():
|
||||||
|
try:
|
||||||
if vm.isActive():
|
if vm.isActive():
|
||||||
self._register_watches(vm)
|
self._register_watches(vm)
|
||||||
|
except libvirt.libvirtError:
|
||||||
|
# this will happen if we loose a race with another tool,
|
||||||
|
# which can just remove the domain
|
||||||
|
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
# and for dom0
|
# and for dom0
|
||||||
self._register_watches(None)
|
self._register_watches(None)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user