Replaced python threading with QThreads

This commit is contained in:
donoban 2018-10-18 14:11:25 +02:00
parent 693f3e411c
commit afcdba3029
No known key found for this signature in database
GPG Key ID: 141310D8E3ED08A5

View File

@ -28,7 +28,6 @@ import subprocess
import time import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
import traceback import traceback
import threading
import quamash import quamash
import asyncio import asyncio
from contextlib import suppress from contextlib import suppress
@ -44,7 +43,6 @@ from PyQt4 import QtCore # pylint: disable=import-error
from qubesmanager.about import AboutDialog from qubesmanager.about import AboutDialog
from . import ui_qubemanager # pylint: disable=no-name-in-module from . import ui_qubemanager # pylint: disable=no-name-in-module
from . import thread_monitor
from . import table_widgets from . import table_widgets
from . import settings from . import settings
from . import global_settings from . import global_settings
@ -238,6 +236,81 @@ class VmShutdownMonitor(QtCore.QObject):
self.restart_vm_if_needed() self.restart_vm_if_needed()
class StartVMThread(QtCore.QThread):
def __init__(self, vm):
QtCore.QThread.__init__(self)
self.vm = vm
def run(self):
try:
self.vm.start()
except exc.QubesException as ex:
self.emit(QtCore.SIGNAL('show_error(QString, QString)'),\
"Error starting Qube!", str(ex))
class RemoveVMThread(QtCore.QThread):
def __init__(self, vm, qubes_app):
QtCore.QThread.__init__(self)
self.vm = vm
self.qubes_app = qubes_app
def run(self):
try:
del self.qubes_app.domains[self.vm.name]
except exc.QubesException as ex:
self.emit(QtCore.SIGNAL('show_error(QString, QString)'),\
"Error removing Qube!", str(ex))
class UpdateVMThread(QtCore.QThread):
def __init__(self, vm):
QtCore.QThread.__init__(self)
self.vm = vm
def run(self):
try:
if self.vm.qid == 0:
subprocess.check_call(
["/usr/bin/qubes-dom0-update", "--clean", "--gui"])
else:
if not self.vm.is_running():
self.vm.start()
self.vm.run_service("qubes.InstallUpdatesGUI",\
user="root", wait=False)
except (ChildProcessError, exc.QubesException) as ex:
self.emit(QtCore.SIGNAL('show_error(QString, QString)'),\
"Error on Qube update!", str(ex))
class CloneVMThread(QtCore.QThread):
def __init__(self, src_vm, qubes_app, dst_name):
QtCore.QThread.__init__(self)
self.src_vm = src_vm
self.qubes_app = qubes_app
self.dst_name = dst_name
def run(self):
try:
dst_vm = self.qubes_app.clone_vm(self.src_vm, self.dst_name)
except exc.QubesException as ex:
self.emit(QtCore.SIGNAL('show_error(QString, QString)'),\
"Error while cloning Qube!", str(ex))
class RunCommandThread(QtCore.QThread):
def __init__(self, vm, command_to_run):
QtCore.QThread.__init__(self)
self.vm = vm
self.command_to_run = command_to_run
def run(self):
try:
self.vm.run(self.command_to_run)
except (ChildProcessError, exc.QubesException) as ex:
self.emit(QtCore.SIGNAL('show_error(QString, QString)'),\
"Error while running command!", str(ex))
class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
row_height = 30 row_height = 30
@ -410,6 +483,9 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
dispatcher.add_handler('property-load', dispatcher.add_handler('property-load',
self.on_domain_changed) self.on_domain_changed)
# It needs to store threads until they finish
self.threads_list = []
# Check Updates Timer # Check Updates Timer
timer = QtCore.QTimer(self) timer = QtCore.QTimer(self)
timer.timeout.connect(self.check_updates) timer.timeout.connect(self.check_updates)
@ -585,7 +661,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
vm = self.get_selected_vm() vm = self.get_selected_vm()
if vm is not None and vm in self.qubes_app.domains: if vm is not None and vm in self.qubes_app.domains:
# TODO: add boot from device to menu and add windows tools there # TODO: add boot from device to menu and add windows tools there
# Update available actions: # Update available actions:
self.action_settings.setEnabled(vm.klass != 'AdminVM') self.action_settings.setEnabled(vm.klass != 'AdminVM')
@ -705,43 +780,16 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
else: else:
# remove the VM # remove the VM
t_monitor = thread_monitor.ThreadMonitor() thread = RemoveVMThread(vm, self.qubes_app)
thread = threading.Thread(target=self.do_remove_vm, self.threads_list.append(thread)
args=(vm, self.qubes_app, t_monitor)) self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error)
thread.daemon = True thread.finished.connect(self.clear_threads)
thread.start() thread.start()
progress = QtGui.QProgressDialog( def clear_threads(self):
self.tr( for thread in self.threads_list:
"Removing Qube: <b>{0}</b>...").format(vm.name), "", 0, 0) if thread.isFinished():
progress.setWindowFlags(QtCore.Qt.Window | self.threads_list.remove(thread)
QtCore.Qt.WindowTitleHint |
QtCore.Qt.CustomizeWindowHint)
progress.setCancelButton(None)
progress.setModal(True)
progress.show()
while not t_monitor.is_finished():
self.qt_app.processEvents()
time.sleep(0.1)
progress.hide()
if t_monitor.success:
pass
else:
QtGui.QMessageBox.warning(None, self.tr("Error removing Qube!"),
self.tr("ERROR: {0}").format(
t_monitor.error_msg))
@staticmethod
def do_remove_vm(vm, qubes_app, t_monitor):
try:
del qubes_app.domains[vm.name]
except exc.QubesException as ex:
t_monitor.set_error_msg(str(ex))
t_monitor.set_finished()
# noinspection PyArgumentList # noinspection PyArgumentList
@QtCore.pyqtSlot(name='on_action_clonevm_triggered') @QtCore.pyqtSlot(name='on_action_clonevm_triggered')
@ -760,12 +808,9 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
if not ok or clone_name == "": if not ok or clone_name == "":
return return
t_monitor = thread_monitor.ThreadMonitor() self.thread = CloneVMThread(vm, self.qubes_app, clone_name)
thread = threading.Thread(target=self.do_clone_vm, self.thread.start()
args=(vm, self.qubes_app, return
clone_name, t_monitor))
thread.daemon = True
thread.start()
progress = QtGui.QProgressDialog( progress = QtGui.QProgressDialog(
self.tr("Cloning Qube <b>{0}</b> to <b>{1}</b>...").format( self.tr("Cloning Qube <b>{0}</b> to <b>{1}</b>...").format(
@ -777,31 +822,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
progress.setModal(True) progress.setModal(True)
progress.show() progress.show()
while not t_monitor.is_finished():
self.qt_app.processEvents()
time.sleep(0.2)
progress.hide()
if not t_monitor.success:
QtGui.QMessageBox.warning(
None,
self.tr("Error while cloning Qube"),
self.tr("Exception while cloning:<br>{0}").format(
t_monitor.error_msg))
@staticmethod
def do_clone_vm(src_vm, qubes_app, dst_name, t_monitor):
dst_vm = None
try:
dst_vm = qubes_app.clone_vm(src_vm, dst_name)
except exc.QubesException as ex:
t_monitor.set_error_msg(str(ex))
if dst_vm:
pass
t_monitor.set_finished()
# noinspection PyArgumentList # noinspection PyArgumentList
@QtCore.pyqtSlot(name='on_action_resumevm_triggered') @QtCore.pyqtSlot(name='on_action_resumevm_triggered')
def action_resumevm_triggered(self): def action_resumevm_triggered(self):
@ -823,33 +843,18 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
def start_vm(self, vm): def start_vm(self, vm):
if vm.is_running(): if vm.is_running():
return return
t_monitor = thread_monitor.ThreadMonitor()
thread = threading.Thread(target=self.do_start_vm, thread = StartVMThread(vm)
args=(vm, t_monitor)) self.threads_list.append(thread)
thread.daemon = True self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error)
thread.finished.connect(self.clear_threads)
thread.start() thread.start()
while not t_monitor.is_finished(): def show_error(self, error, error_msg):
self.qt_app.processEvents()
time.sleep(0.1)
if not t_monitor.success:
QtGui.QMessageBox.warning( QtGui.QMessageBox.warning(
None, None,
self.tr("Error starting Qube!"), self.tr(error),
self.tr("ERROR: {0}").format(t_monitor.error_msg)) self.tr("ERROR: {0}").format(error_msg))
@staticmethod
def do_start_vm(vm, t_monitor):
try:
vm.start()
except exc.QubesException as ex:
t_monitor.set_error_msg(str(ex))
t_monitor.set_finished()
return
t_monitor.set_finished()
# noinspection PyArgumentList # noinspection PyArgumentList
@QtCore.pyqtSlot(name='on_action_startvm_tools_install_triggered') @QtCore.pyqtSlot(name='on_action_startvm_tools_install_triggered')
@ -1014,55 +1019,12 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
if reply != QtGui.QMessageBox.Yes: if reply != QtGui.QMessageBox.Yes:
return return
self.qt_app.processEvents() thread = UpdateVMThread(vm)
self.threads_list.append(thread)
t_monitor = thread_monitor.ThreadMonitor() self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error)
thread = threading.Thread(target=self.do_update_vm, thread.finished.connect(self.clear_threads)
args=(vm, t_monitor))
thread.daemon = True
thread.start() thread.start()
progress = QtGui.QProgressDialog(
self.tr(
"<b>{0}</b><br>Please wait for the updater to "
"launch...").format(vm.name), "", 0, 0)
progress.setWindowFlags(QtCore.Qt.Window |
QtCore.Qt.WindowTitleHint |
QtCore.Qt.CustomizeWindowHint)
progress.setCancelButton(None)
progress.setModal(True)
progress.show()
while not t_monitor.is_finished():
self.qt_app.processEvents()
time.sleep(0.2)
progress.hide()
if vm.qid != 0:
if not t_monitor.success:
QtGui.QMessageBox.warning(
None,
self.tr("Error on Qube update!"),
self.tr("ERROR: {0}").format(t_monitor.error_msg))
@staticmethod
def do_update_vm(vm, t_monitor):
try:
if vm.qid == 0:
subprocess.check_call(
["/usr/bin/qubes-dom0-update", "--clean", "--gui"])
else:
if not vm.is_running():
vm.start()
vm.run_service("qubes.InstallUpdatesGUI",
user="root", wait=False)
except (ChildProcessError, exc.QubesException) as ex:
t_monitor.set_error_msg(str(ex))
t_monitor.set_finished()
return
t_monitor.set_finished()
# noinspection PyArgumentList # noinspection PyArgumentList
@QtCore.pyqtSlot(name='on_action_run_command_in_vm_triggered') @QtCore.pyqtSlot(name='on_action_run_command_in_vm_triggered')
@ -1075,29 +1037,13 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
self.tr('Run command in <b>{}</b>:').format(vm.name)) self.tr('Run command in <b>{}</b>:').format(vm.name))
if not ok or command_to_run == "": if not ok or command_to_run == "":
return return
t_monitor = thread_monitor.ThreadMonitor()
thread = threading.Thread(target=self.do_run_command_in_vm, args=( thread = RunCommandThread(vm, command_to_run)
vm, command_to_run, t_monitor)) self.threads_list.append(thread)
thread.daemon = True self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error)
thread.finished.connect(self.clear_threads)
thread.start() thread.start()
while not t_monitor.is_finished():
self.qt_app.processEvents()
time.sleep(0.2)
if not t_monitor.success:
QtGui.QMessageBox.warning(
None, self.tr("Error while running command"),
self.tr("Exception while running command:<br>{0}").format(
t_monitor.error_msg))
@staticmethod
def do_run_command_in_vm(vm, command_to_run, t_monitor):
try:
vm.run(command_to_run)
except (ChildProcessError, exc.QubesException) as ex:
t_monitor.set_error_msg(str(ex))
t_monitor.set_finished()
# noinspection PyArgumentList # noinspection PyArgumentList
@QtCore.pyqtSlot(name='on_action_set_keyboard_layout_triggered') @QtCore.pyqtSlot(name='on_action_set_keyboard_layout_triggered')