Use separate QubesVmCollection instance when running a task in background (#986)
QubesVmCollection is not thread safe. If for example update_table() will be called during some long-running task (like creating or removing VM), it will try to reload qubes.xml (so get read lock first), but the thread already holds a lock on this file. This would result in "Lock already taken" exception. Fixes qubesos/qubes-issues#986
This commit is contained in:
parent
fdf0fd1870
commit
f6941bd3d3
@ -250,39 +250,40 @@ class NewVmDlg (QDialog, Ui_NewVMDlg):
|
|||||||
|
|
||||||
self.done(0)
|
self.done(0)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def do_create_vm(vmclass, vmname, label, template_vm, netvm,
|
||||||
def do_create_vm (self, vmclass, vmname, label, template_vm, netvm, standalone, allow_networking, thread_monitor):
|
standalone, allow_networking, thread_monitor):
|
||||||
vm = None
|
vm = None
|
||||||
|
qc = QubesVmCollection()
|
||||||
|
qc.lock_db_for_writing()
|
||||||
|
qc.load()
|
||||||
try:
|
try:
|
||||||
self.qvm_collection.lock_db_for_writing()
|
|
||||||
self.qvm_collection.load()
|
|
||||||
|
|
||||||
if not standalone:
|
if not standalone:
|
||||||
vm = self.qvm_collection.add_new_vm(vmclass, name=vmname, template=template_vm, label=label)
|
vm = qc.add_new_vm(vmclass, name=vmname, template=template_vm,
|
||||||
|
label=label)
|
||||||
else:
|
else:
|
||||||
vm = self.qvm_collection.add_new_vm(vmclass, name=vmname, template=None, label=label)
|
vm = qc.add_new_vm(vmclass, name=vmname, template=None,
|
||||||
vm.create_on_disk(verbose=False, source_template = template_vm)
|
label=label)
|
||||||
|
vm.create_on_disk(verbose=False, source_template=template_vm)
|
||||||
|
|
||||||
if allow_networking == False:
|
if not allow_networking:
|
||||||
vm.uses_default_netvm = False
|
vm.uses_default_netvm = False
|
||||||
vm.netvm = None
|
vm.netvm = None
|
||||||
else:
|
else:
|
||||||
vm.netvm = netvm
|
vm.netvm = netvm
|
||||||
if vm.netvm == self.qvm_collection.get_default_netvm():
|
if vm.netvm.qid == qc.get_default_netvm().qid:
|
||||||
vm.uses_default_netvm = True
|
vm.uses_default_netvm = True
|
||||||
else:
|
else:
|
||||||
vm.uses_default_netvm = False
|
vm.uses_default_netvm = False
|
||||||
|
|
||||||
self.qvm_collection.save()
|
qc.save()
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
thread_monitor.set_error_msg (str(ex))
|
thread_monitor.set_error_msg(str(ex))
|
||||||
if vm:
|
if vm:
|
||||||
vm.remove_from_disk()
|
vm.remove_from_disk()
|
||||||
self.qvm_collection.pop(vm.qid)
|
qc.pop(vm.qid)
|
||||||
finally:
|
finally:
|
||||||
self.qvm_collection.unlock_db()
|
qc.unlock_db()
|
||||||
|
|
||||||
thread_monitor.set_finished()
|
thread_monitor.set_finished()
|
||||||
|
|
||||||
|
@ -1000,28 +1000,30 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
|||||||
"ERROR: {0}".format(
|
"ERROR: {0}".format(
|
||||||
thread_monitor.error_msg))
|
thread_monitor.error_msg))
|
||||||
|
|
||||||
def do_remove_vm(self, vm, thread_monitor):
|
@staticmethod
|
||||||
|
def do_remove_vm(vm, thread_monitor):
|
||||||
|
qc = QubesVmCollection()
|
||||||
|
qc.lock_db_for_writing()
|
||||||
try:
|
try:
|
||||||
self.qvm_collection.lock_db_for_writing()
|
qc.load()
|
||||||
self.qvm_collection.load()
|
vm = qc[vm.qid]
|
||||||
vm = self.qvm_collection[vm.qid]
|
|
||||||
|
|
||||||
# TODO: the following two conditions should really be checked
|
# TODO: the following two conditions should really be checked
|
||||||
# by qvm_collection.pop() overload...
|
# by qvm_collection.pop() overload...
|
||||||
if vm.is_template() and \
|
if vm.is_template() and \
|
||||||
self.qvm_collection.default_template_qid == vm.qid:
|
qc.default_template_qid == vm.qid:
|
||||||
self.qvm_collection.default_template_qid = None
|
qc.default_template_qid = None
|
||||||
if vm.is_netvm() and \
|
if vm.is_netvm() and \
|
||||||
self.qvm_collection.default_netvm_qid == vm.qid:
|
qc.default_netvm_qid == vm.qid:
|
||||||
self.qvm_collection.default_netvm_qid = None
|
qc.default_netvm_qid = None
|
||||||
|
|
||||||
self.qvm_collection.pop(vm.qid)
|
qc.pop(vm.qid)
|
||||||
self.qvm_collection.save()
|
qc.save()
|
||||||
vm.remove_from_disk()
|
vm.remove_from_disk()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
thread_monitor.set_error_msg(str(ex))
|
thread_monitor.set_error_msg(str(ex))
|
||||||
finally:
|
finally:
|
||||||
self.qvm_collection.unlock_db()
|
qc.unlock_db()
|
||||||
|
|
||||||
thread_monitor.set_finished()
|
thread_monitor.set_finished()
|
||||||
|
|
||||||
@ -1066,28 +1068,30 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
|||||||
"Exception while cloning:<br>{0}".format(
|
"Exception while cloning:<br>{0}".format(
|
||||||
thread_monitor.error_msg))
|
thread_monitor.error_msg))
|
||||||
|
|
||||||
def do_clone_vm(self, vm, dst_name, thread_monitor):
|
@staticmethod
|
||||||
|
def do_clone_vm(vm, dst_name, thread_monitor):
|
||||||
dst_vm = None
|
dst_vm = None
|
||||||
|
qc = QubesVmCollection()
|
||||||
|
qc.lock_db_for_writing()
|
||||||
|
qc.load()
|
||||||
try:
|
try:
|
||||||
self.qvm_collection.lock_db_for_writing()
|
src_vm = qc[vm.qid]
|
||||||
self.qvm_collection.load()
|
|
||||||
src_vm = self.qvm_collection[vm.qid]
|
|
||||||
|
|
||||||
dst_vm = self.qvm_collection.add_new_vm(src_vm.__class__.__name__,
|
dst_vm = qc.add_new_vm(src_vm.__class__.__name__,
|
||||||
name=dst_name,
|
name=dst_name,
|
||||||
template=src_vm.template,
|
template=src_vm.template,
|
||||||
installed_by_rpm=False)
|
installed_by_rpm=False)
|
||||||
|
|
||||||
dst_vm.clone_attrs(src_vm)
|
dst_vm.clone_attrs(src_vm)
|
||||||
dst_vm.clone_disk_files(src_vm=src_vm, verbose=False)
|
dst_vm.clone_disk_files(src_vm=src_vm, verbose=False)
|
||||||
self.qvm_collection.save()
|
qc.save()
|
||||||
self.qvm_collection.unlock_db()
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if dst_vm:
|
if dst_vm:
|
||||||
self.qvm_collection.pop(dst_vm.qid)
|
qc.pop(dst_vm.qid)
|
||||||
dst_vm.remove_from_disk()
|
dst_vm.remove_from_disk()
|
||||||
self.qvm_collection.unlock_db()
|
|
||||||
thread_monitor.set_error_msg(str(ex))
|
thread_monitor.set_error_msg(str(ex))
|
||||||
|
finally:
|
||||||
|
qc.unlock_db()
|
||||||
thread_monitor.set_finished()
|
thread_monitor.set_finished()
|
||||||
|
|
||||||
@pyqtSlot(name='on_action_resumevm_triggered')
|
@pyqtSlot(name='on_action_resumevm_triggered')
|
||||||
@ -1126,8 +1130,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
|||||||
self.set_error(vm.qid,
|
self.set_error(vm.qid,
|
||||||
"Error starting VM: %s" % thread_monitor.error_msg)
|
"Error starting VM: %s" % thread_monitor.error_msg)
|
||||||
|
|
||||||
# noinspection PyMethodMayBeStatic
|
@staticmethod
|
||||||
def do_start_vm(self, vm, thread_monitor):
|
def do_start_vm(vm, thread_monitor):
|
||||||
try:
|
try:
|
||||||
vm.verify_files()
|
vm.verify_files()
|
||||||
vm.start()
|
vm.start()
|
||||||
@ -1347,7 +1351,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
|||||||
"ERROR: {0}".format(
|
"ERROR: {0}".format(
|
||||||
thread_monitor.error_msg))
|
thread_monitor.error_msg))
|
||||||
|
|
||||||
def do_update_vm(self, vm, thread_monitor):
|
@staticmethod
|
||||||
|
def do_update_vm(vm, thread_monitor):
|
||||||
try:
|
try:
|
||||||
if vm.qid == 0:
|
if vm.qid == 0:
|
||||||
subprocess.check_call(
|
subprocess.check_call(
|
||||||
@ -1393,7 +1398,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
|||||||
"Exception while running command:<br>{0}".format(
|
"Exception while running command:<br>{0}".format(
|
||||||
thread_monitor.error_msg))
|
thread_monitor.error_msg))
|
||||||
|
|
||||||
def do_run_command_in_vm(self, vm, command_to_run, thread_monitor):
|
@staticmethod
|
||||||
|
def do_run_command_in_vm(vm, command_to_run, thread_monitor):
|
||||||
try:
|
try:
|
||||||
vm.run(command_to_run, verbose=False, autostart=True,
|
vm.run(command_to_run, verbose=False, autostart=True,
|
||||||
notify_function=lambda lvl, msg: trayIcon.showMessage(
|
notify_function=lambda lvl, msg: trayIcon.showMessage(
|
||||||
|
Loading…
Reference in New Issue
Block a user