From afcdba302988b10dba32c4789c3c1f8d24ddd2d1 Mon Sep 17 00:00:00 2001 From: donoban Date: Thu, 18 Oct 2018 14:11:25 +0200 Subject: [PATCH 01/48] Replaced python threading with QThreads --- qubesmanager/qube_manager.py | 270 ++++++++++++++--------------------- 1 file changed, 108 insertions(+), 162 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 292b96e..3e5b4c2 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -28,7 +28,6 @@ import subprocess import time from datetime import datetime, timedelta import traceback -import threading import quamash import asyncio from contextlib import suppress @@ -44,7 +43,6 @@ from PyQt4 import QtCore # pylint: disable=import-error from qubesmanager.about import AboutDialog from . import ui_qubemanager # pylint: disable=no-name-in-module -from . import thread_monitor from . import table_widgets from . import settings from . import global_settings @@ -238,6 +236,81 @@ class VmShutdownMonitor(QtCore.QObject): 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): # pylint: disable=too-many-instance-attributes row_height = 30 @@ -410,6 +483,9 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): dispatcher.add_handler('property-load', self.on_domain_changed) + # It needs to store threads until they finish + self.threads_list = [] + # Check Updates Timer timer = QtCore.QTimer(self) timer.timeout.connect(self.check_updates) @@ -585,7 +661,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): vm = self.get_selected_vm() if vm is not None and vm in self.qubes_app.domains: - # TODO: add boot from device to menu and add windows tools there # Update available actions: self.action_settings.setEnabled(vm.klass != 'AdminVM') @@ -705,43 +780,16 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): else: # remove the VM - t_monitor = thread_monitor.ThreadMonitor() - thread = threading.Thread(target=self.do_remove_vm, - args=(vm, self.qubes_app, t_monitor)) - thread.daemon = True + thread = RemoveVMThread(vm, self.qubes_app) + self.threads_list.append(thread) + self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error) + thread.finished.connect(self.clear_threads) thread.start() - progress = QtGui.QProgressDialog( - self.tr( - "Removing Qube: {0}...").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.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() + def clear_threads(self): + for thread in self.threads_list: + if thread.isFinished(): + self.threads_list.remove(thread) # noinspection PyArgumentList @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 == "": return - t_monitor = thread_monitor.ThreadMonitor() - thread = threading.Thread(target=self.do_clone_vm, - args=(vm, self.qubes_app, - clone_name, t_monitor)) - thread.daemon = True - thread.start() + self.thread = CloneVMThread(vm, self.qubes_app, clone_name) + self.thread.start() + return progress = QtGui.QProgressDialog( self.tr("Cloning Qube {0} to {1}...").format( @@ -777,31 +822,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): progress.setModal(True) 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:
{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 @QtCore.pyqtSlot(name='on_action_resumevm_triggered') def action_resumevm_triggered(self): @@ -823,33 +843,18 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): def start_vm(self, vm): if vm.is_running(): return - t_monitor = thread_monitor.ThreadMonitor() - thread = threading.Thread(target=self.do_start_vm, - args=(vm, t_monitor)) - thread.daemon = True + + thread = StartVMThread(vm) + self.threads_list.append(thread) + self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error) + thread.finished.connect(self.clear_threads) thread.start() - while not t_monitor.is_finished(): - self.qt_app.processEvents() - time.sleep(0.1) - - if not t_monitor.success: - QtGui.QMessageBox.warning( - None, - self.tr("Error starting Qube!"), - self.tr("ERROR: {0}").format(t_monitor.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() + def show_error(self, error, error_msg): + QtGui.QMessageBox.warning( + None, + self.tr(error), + self.tr("ERROR: {0}").format(error_msg)) # noinspection PyArgumentList @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: return - self.qt_app.processEvents() - - t_monitor = thread_monitor.ThreadMonitor() - thread = threading.Thread(target=self.do_update_vm, - args=(vm, t_monitor)) - thread.daemon = True + thread = UpdateVMThread(vm) + self.threads_list.append(thread) + self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error) + thread.finished.connect(self.clear_threads) thread.start() - progress = QtGui.QProgressDialog( - self.tr( - "{0}
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 @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 {}:').format(vm.name)) if not ok or command_to_run == "": return - t_monitor = thread_monitor.ThreadMonitor() - thread = threading.Thread(target=self.do_run_command_in_vm, args=( - vm, command_to_run, t_monitor)) - thread.daemon = True + + thread = RunCommandThread(vm, command_to_run) + self.threads_list.append(thread) + self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error) + thread.finished.connect(self.clear_threads) 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:
{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 @QtCore.pyqtSlot(name='on_action_set_keyboard_layout_triggered') From b45f318a3a6db82502f696b9844c1ea6989f2e08 Mon Sep 17 00:00:00 2001 From: donoban Date: Thu, 18 Oct 2018 14:13:06 +0200 Subject: [PATCH 02/48] Just moved method to better place --- qubesmanager/qube_manager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 3e5b4c2..12ffb99 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -492,6 +492,11 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): timer.start(1000 * 30) # 30s self.check_updates() + def clear_threads(self): + for thread in self.threads_list: + if thread.isFinished(): + self.threads_list.remove(thread) + def closeEvent(self, event): # pylint: disable=invalid-name # save window size at close @@ -786,11 +791,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): thread.finished.connect(self.clear_threads) thread.start() - def clear_threads(self): - for thread in self.threads_list: - if thread.isFinished(): - self.threads_list.remove(thread) - # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_clonevm_triggered') def action_clonevm_triggered(self): From fea560b5cd0ebdeac33087b0c9b72dfb496a9a81 Mon Sep 17 00:00:00 2001 From: donoban Date: Thu, 18 Oct 2018 14:13:20 +0200 Subject: [PATCH 03/48] Deleted unneeded thread_monitor --- qubesmanager/thread_monitor.py | 42 ---------------------------------- 1 file changed, 42 deletions(-) delete mode 100644 qubesmanager/thread_monitor.py diff --git a/qubesmanager/thread_monitor.py b/qubesmanager/thread_monitor.py deleted file mode 100644 index 8515aff..0000000 --- a/qubesmanager/thread_monitor.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/python2 -# -# The Qubes OS Project, http://www.qubes-os.org -# -# Copyright (C) 2011 Marek Marczykowski -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with this program; if not, see . -# -# - - -import PyQt4.QtCore # pylint: disable=import-error - -import threading - -class ThreadMonitor(PyQt4.QtCore.QObject): - def __init__(self): - self.success = True - self.error_msg = None - self.event_finished = threading.Event() - - def set_error_msg(self, error_msg): - self.success = False - self.error_msg = error_msg - self.set_finished() - - def is_finished(self): - return self.event_finished.is_set() - - def set_finished(self): - self.event_finished.set() From af8bc69314043ea1a72d0fca6014b3b3269e6428 Mon Sep 17 00:00:00 2001 From: donoban Date: Thu, 18 Oct 2018 15:53:35 +0200 Subject: [PATCH 04/48] Revert "Deleted unneeded thread_monitor" This reverts commit fea560b5cd0ebdeac33087b0c9b72dfb496a9a81. --- qubesmanager/thread_monitor.py | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 qubesmanager/thread_monitor.py diff --git a/qubesmanager/thread_monitor.py b/qubesmanager/thread_monitor.py new file mode 100644 index 0000000..8515aff --- /dev/null +++ b/qubesmanager/thread_monitor.py @@ -0,0 +1,42 @@ +#!/usr/bin/python2 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2011 Marek Marczykowski +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with this program; if not, see . +# +# + + +import PyQt4.QtCore # pylint: disable=import-error + +import threading + +class ThreadMonitor(PyQt4.QtCore.QObject): + def __init__(self): + self.success = True + self.error_msg = None + self.event_finished = threading.Event() + + def set_error_msg(self, error_msg): + self.success = False + self.error_msg = error_msg + self.set_finished() + + def is_finished(self): + return self.event_finished.is_set() + + def set_finished(self): + self.event_finished.set() From 228aaed2f43c34d9b5a1b9389e2f76be0d89e5c6 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 16:28:19 +0200 Subject: [PATCH 05/48] Removed progress.setWindowFlags() call I am not sure what this wants, but it seems that doesn't affect xfce but on kde does the progress bar appear randomly in the screen without it on both cases it appears at center of screen --- qubesmanager/qube_manager.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 12ffb99..8700bd2 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -815,9 +815,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): progress = QtGui.QProgressDialog( self.tr("Cloning Qube {0} to {1}...").format( vm.name, clone_name), "", 0, 0) - progress.setWindowFlags(QtCore.Qt.Window | - QtCore.Qt.WindowTitleHint | - QtCore.Qt.CustomizeWindowHint) progress.setCancelButton(None) progress.setModal(True) progress.show() From 612cfc15d643d7374df152681286278d7ccd6154 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 16:33:25 +0200 Subject: [PATCH 06/48] Loading Qube Manager progress using setValue() This is the recommended QT way for updating the progress bar. setMininumDuration() sets the time when the progress dialog will appear, if it finishes before it won't be shown. Qt default is 4 seconds, maybe pretty high for this case. --- qubesmanager/qube_manager.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 8700bd2..30087e9 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -598,17 +598,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): return [vm for vm in self.qubes_app.domains] def fill_table(self): - progress = QtGui.QProgressDialog( - self.tr( - "Loading Qube Manager..."), "", 0, 0) - progress.setWindowTitle(self.tr("Qube Manager")) - progress.setWindowFlags(QtCore.Qt.Window | - QtCore.Qt.WindowTitleHint | - QtCore.Qt.CustomizeWindowHint) - progress.setCancelButton(None) - progress.setModal(True) - progress.show() - self.table.setSortingEnabled(False) vms_list = self.get_vms_list() @@ -616,19 +605,26 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.table.setRowCount(len(vms_list)) + progress = QtGui.QProgressDialog( + self.tr( + "Loading Qube Manager..."), "", 0, len(vms_list)) + progress.setWindowTitle(self.tr("Qube Manager")) + progress.setMinimumDuration(1000) + progress.setCancelButton(None) + row_no = 0 for vm in vms_list: + progress.setValue(row_no) vm_row = VmRowInTable(vm, row_no, self.table) vms_in_table[vm.qid] = vm_row row_no += 1 - self.qt_app.processEvents() + + progress.setValue(row_no) self.vms_list = vms_list self.vms_in_table = vms_in_table self.table.setSortingEnabled(True) - progress.hide() - def showhide_vms(self): if not self.search: for row_no in range(self.table.rowCount()): From 76b6de9d00c15e81f00513c9807516154f2b349f Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 16:43:15 +0200 Subject: [PATCH 07/48] removed subprocess.check_call() This way modal works better (in KDE the main window goes gray). --- qubesmanager/qube_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 30087e9..a68d6c4 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -48,6 +48,7 @@ from . import settings from . import global_settings from . import restore from . import backup +from . import create_new_vm from . import log_dialog from . import utils as manager_utils @@ -713,7 +714,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_createvm_triggered') def action_createvm_triggered(self): # pylint: disable=no-self-use - subprocess.check_call('qubes-vm-create') + create_window = create_new_vm.NewVmDlg(self.qt_app, self.qubes_app) + create_window.exec_() def get_selected_vm(self): # vm selection relies on the VmInfo widget's value used From 759dc5412c28a0919a9565987d6ebb5e07554385 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 16:49:50 +0200 Subject: [PATCH 08/48] Pylint ident warnings --- qubesmanager/qube_manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index a68d6c4..8869fde 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -208,7 +208,7 @@ class VmShutdownMonitor(QtCore.QObject): self.tr( "The Qube '{0}' hasn't shutdown within the last " "{1} seconds, do you want to kill it?
").format( - vm.name, self.shutdown_time / 1000), + vm.name, self.shutdown_time / 1000), self.tr("Kill it!"), self.tr("Wait another {0} seconds...").format( self.shutdown_time / 1000)) @@ -882,7 +882,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.tr("Are you sure you want to power down the Qube" " '{0}'?
This will shutdown all the " "running applications within this Qube.").format( - vm.name), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) + vm.name), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) self.qt_app.processEvents() @@ -935,7 +935,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): if not (vm.is_running() or vm.is_paused()): info = self.tr("Qube '{0}' is not running. Are you " "absolutely sure you want to try to kill it?
" - "This will end (not shutdown!) all " + "This will end (not shutdown!) all " "the running applications within this " "Qube.").format(vm.name) else: From 35cdf5e1e570b91146741fd5f42c03cc85ae9290 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 16:50:45 +0200 Subject: [PATCH 09/48] Modeless backup window Let the main window work while doing backups --- qubesmanager/qube_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 8869fde..63c8bf0 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -1080,7 +1080,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): @QtCore.pyqtSlot(name='on_action_backup_triggered') def action_backup_triggered(self): backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app) - backup_window.exec_() + backup_window.show() # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_exit_triggered') From 29ca4eb3a8985004f84d6df15d98d371911b41fd Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 18:34:15 +0200 Subject: [PATCH 10/48] Migration from python threads and to QThread obj Simple design and better performance, just start() thread and finished call back will show any error or msg, stop progress dialog and do cleanup. Full remove of QT process_events() calls. --- qubesmanager/backup.py | 151 ++++++++++++--------- qubesmanager/common_threads.py | 51 +++++++ qubesmanager/create_new_vm.py | 95 ++++++------- qubesmanager/qube_manager.py | 81 ++++------- qubesmanager/restore.py | 158 +++++++++++----------- qubesmanager/settings.py | 239 +++++++++++++++------------------ qubesmanager/thread_monitor.py | 42 ------ 7 files changed, 398 insertions(+), 419 deletions(-) create mode 100644 qubesmanager/common_threads.py delete mode 100644 qubesmanager/thread_monitor.py diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index d974842..fdc284c 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -23,9 +23,11 @@ import traceback import signal +import quamash from qubesadmin import Qubes, exc from qubesadmin import utils as admin_utils +from qubesadmin import events from qubes.storage.file import get_disk_usage from PyQt4 import QtCore # pylint: disable=import-error @@ -35,18 +37,38 @@ from . import multiselectwidget from . import backup_utils from . import utils + import grp import pwd import sys import os -from . import thread_monitor -import threading +import asyncio +from contextlib import suppress import time +class BackupThread(QtCore.QThread): + def __init__(self, vm): + QtCore.QThread.__init__(self) + self.vm = vm + self.msg = None + + def run(self): + msg = [] + try: + if not self.vm.is_running(): + self.vm.start() + self.vm.app.qubesd_call( + 'dom0', 'admin.backup.Execute', + backup_utils.get_profile_name(True)) + except Exception as ex: # pylint: disable=broad-except + msg.append(str(ex)) + + if msg: + self.msg = '\n'.join(msg) + class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): - - def __init__(self, qt_app, qubes_app, parent=None): + def __init__(self, qt_app, qubes_app, dispatcher, parent=None): super(BackupVMsWindow, self).__init__(parent) self.qt_app = qt_app @@ -54,8 +76,6 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): self.backup_settings = QtCore.QSettings() self.selected_vms = [] - self.canceled = False - self.thread_monitor = None self.setupUi(self) @@ -112,6 +132,17 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): selected = self.load_settings() self.__fill_vms_list__(selected) + # Connect backup events for progress_bar + self.progress_bar.setMinimum(0) + self.progress_bar.setMaximum(100) + self.dispatcher = dispatcher + dispatcher.add_handler('backup-progress', self.on_backup_progress) + + def on_backup_progress(self, __submitter, _event, **kwargs): + print(kwargs['progress']) + self.progress_bar.setValue(int(float(kwargs['progress']))) + + def load_settings(self): """ Helper function that tries to load existing backup profile @@ -260,24 +291,6 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): return True - def __do_backup__(self, t_monitor): - msg = [] - - try: - vm = self.qubes_app.domains[ - self.appvm_combobox.currentText()] - if not vm.is_running(): - vm.start() - self.qubes_app.qubesd_call( - 'dom0', 'admin.backup.Execute', - backup_utils.get_profile_name(True)) - except Exception as ex: # pylint: disable=broad-except - msg.append(str(ex)) - - if msg: - t_monitor.set_error_msg('\n'.join(msg)) - - t_monitor.set_finished() @staticmethod def cleanup_temporary_files(): @@ -310,33 +323,27 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): self.showFileDialog.setChecked(self.showFileDialog.isEnabled() and str(self.dir_line_edit.text()) .count("media/") > 0) - self.thread_monitor = thread_monitor.ThreadMonitor() - thread = threading.Thread( - target=self.__do_backup__, - args=(self.thread_monitor,)) - thread.daemon = True - thread.start() - while not self.thread_monitor.is_finished(): - self.qt_app.processEvents() - time.sleep(0.1) + vm = self.qubes_app.domains[ + self.appvm_combobox.currentText()] + + self.thread = BackupThread(vm) + self.thread.finished.connect(self.backup_finished) + self.thread.start() + + signal.signal(signal.SIGCHLD, old_sigchld_handler) + + def backup_finished(self): + if self.thread.msg: + self.progress_status.setText(self.tr("Backup error.")) + QtGui.QMessageBox.warning( + self, self.tr("Backup error!"), + self.tr("ERROR: {}").format( + self.thread.msg)) + else: + self.progress_bar.setValue(100) + self.progress_status.setText(self.tr("Backup finished.")) - if not self.thread_monitor.success: - if self.canceled: - self.progress_status.setText( - self.tr( - "Backup aborted. " - "Temporary file may be left at backup location.")) - else: - self.progress_status.setText(self.tr("Backup error.")) - QtGui.QMessageBox.warning( - self, self.tr("Backup error!"), - self.tr("ERROR: {}").format( - self.thread_monitor.error_msg)) - else: - self.progress_bar.setMaximum(100) - self.progress_bar.setValue(100) - self.progress_status.setText(self.tr("Backup finished.")) if self.showFileDialog.isChecked(): orig_text = self.progress_status.text self.progress_status.setText( @@ -344,31 +351,28 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): " Please unmount your backup volume and cancel " "the file selection dialog.")) backup_utils.select_path_button_clicked(self, False, True) + self.button(self.CancelButton).setEnabled(False) self.button(self.FinishButton).setEnabled(True) self.showFileDialog.setEnabled(False) self.cleanup_temporary_files() # turn off only when backup was successful - if self.thread_monitor.success and \ - self.turn_off_checkbox.isChecked(): + if self.turn_off_checkbox.isChecked(): os.system('systemctl poweroff') - signal.signal(signal.SIGCHLD, old_sigchld_handler) - def reject(self): if self.currentPage() is self.commit_page: - self.canceled = True + self.thread.terminate() self.qubes_app.qubesd_call( 'dom0', 'admin.backup.Cancel', backup_utils.get_profile_name(True)) - self.progress_bar.setMaximum(100) - self.progress_bar.setValue(0) - self.button(self.CancelButton).setDisabled(True) - self.cleanup_temporary_files() - else: - self.cleanup_temporary_files() - self.done(0) + QtGui.QMessageBox.warning( + self, self.tr("Backup aborted!"), + self.tr("ERROR: {}").format("Aborted!")) + + self.cleanup_temporary_files() + self.done(0) def has_selected_vms(self): return self.select_vms_widget.selected_list.count() > 0 @@ -402,9 +406,14 @@ def handle_exception(exc_type, exc_value, exc_traceback): error + "at line %d of file %s.

" % (line, filename)) +def loop_shutdown(): + pending = asyncio.Task.all_tasks() + for task in pending: + with suppress(asyncio.CancelledError): + task.cancel() + def main(): - qt_app = QtGui.QApplication(sys.argv) qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationDomain("http://qubes-os.org") @@ -412,14 +421,24 @@ def main(): sys.excepthook = handle_exception - app = Qubes() + qubes_app = Qubes() - backup_window = BackupVMsWindow(qt_app, app) + loop = quamash.QEventLoop(qt_app) + asyncio.set_event_loop(loop) + dispatcher = events.EventsDispatcher(qubes_app) + backup_window = BackupVMsWindow(qt_app, qubes_app, dispatcher) backup_window.show() - qt_app.exec_() - qt_app.exit() + try: + loop.run_until_complete( + asyncio.ensure_future(dispatcher.listen_for_events())) + except asyncio.CancelledError: + pass + except Exception: # pylint: disable=broad-except + loop_shutdown() + exc_type, exc_value, exc_traceback = sys.exc_info()[:3] + handle_exception(exc_type, exc_value, exc_traceback) if __name__ == "__main__": diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py new file mode 100644 index 0000000..c7fd1ff --- /dev/null +++ b/qubesmanager/common_threads.py @@ -0,0 +1,51 @@ +#!/usr/bin/python2 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2011 Marek Marczykowski +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with this program; if not, see . +# +# + + +from PyQt4 import QtCore # pylint: disable=import-error +from qubesadmin import exc + + +class RemoveVMThread(QtCore.QThread): + def __init__(self, vm): + QtCore.QThread.__init__(self) + self.vm = vm + self.error = None + + def run(self): + try: + del self.vm.app.domains[self.vm.name] + except exc.QubesException as ex: + self.error("Error removing Qube!", str(ex)) + + +class CloneVMThread(QtCore.QThread): + def __init__(self, src_vm, dst_name): + QtCore.QThread.__init__(self) + self.src_vm = src_vm + self.dst_name = dst_name + + def run(self): + try: + dst_vm = self.src_vm.app.clone_vm(self.src_vm, self.dst_name) + self.error = ("Sucess", "The qube was cloned sucessfully.") + except exc.QubesException as ex: + self.error = ("Error while cloning Qube!", str(ex)) diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py index 6e7fa52..73959ad 100644 --- a/qubesmanager/create_new_vm.py +++ b/qubesmanager/create_new_vm.py @@ -35,7 +35,39 @@ import qubesadmin.exc from . import utils from .ui_newappvmdlg import Ui_NewVMDlg # pylint: disable=import-error -from .thread_monitor import ThreadMonitor + +class CreateVMThread(QtCore.QThread): + def __init__(self, app, vmclass, name, label, template, properties): + QtCore.QThread.__init__(self) + self.app = app + self.vmclass = vmclass + self.name = name + self.label = label + self.template = template + self.properties = properties + self.error = None + + def run(self): + try: + if self.vmclass == 'StandaloneVM' and self.template is not None: + if self.template is qubesadmin.DEFAULT: + src_vm = self.app.default_template + else: + src_vm = self.template + vm = self.app.clone_vm(src_vm, self.name, self.vmclass) + vm.label = self.label + for k, v in self.properties.items(): + setattr(vm, k, v) + else: + vm = self.app.add_new_vm(self.vmclass, + name=self.name, label=self.label, template=self.template) + for k, v in self.properties.items(): + setattr(vm, k, v) + + except qubesadmin.exc.QubesException as qex: + self.error = str(qex) + except Exception as ex: # pylint: disable=broad-except + self.error = repr(ex) class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg): @@ -125,67 +157,36 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg): properties['virt_mode'] = 'hvm' properties['kernel'] = None - thread_monitor = ThreadMonitor() - thread = threading.Thread(target=self.do_create_vm, - args=(self.app, vmclass, name, label, template, properties, - thread_monitor)) - thread.daemon = True - thread.start() + self.thread = CreateVMThread(self.app, vmclass, name, label, + template, properties) + self.thread.finished.connect(self.create_finished) + self.thread.start() - progress = QtGui.QProgressDialog( + self.progress = QtGui.QProgressDialog( self.tr("Creating new qube {}...").format(name), "", 0, 0) - progress.setCancelButton(None) - progress.setModal(True) - progress.show() + self.progress.setCancelButton(None) + self.progress.setModal(True) + self.progress.show() - while not thread_monitor.is_finished(): - self.qtapp.processEvents() - time.sleep(0.1) + def create_finished(self): + self.progress.hide() - progress.hide() - - if not thread_monitor.success: + if self.thread.error: QtGui.QMessageBox.warning(None, self.tr("Error creating the qube!"), - self.tr("ERROR: {}").format(thread_monitor.error_msg)) + self.tr("ERROR: {}").format(thread.error)) self.done(0) - if thread_monitor.success: + if not self.thread.error: if self.launch_settings.isChecked(): - subprocess.check_call(['qubes-vm-settings', name]) + subprocess.check_call(['qubes-vm-settings', str(self.name)]) if self.install_system.isChecked(): subprocess.check_call( - ['qubes-vm-boot-from-device', name]) + ['qubes-vm-boot-from-device', srt(self.name)]) - @staticmethod - def do_create_vm(app, vmclass, name, label, template, properties, - thread_monitor): - try: - if vmclass == 'StandaloneVM' and template is not None: - if template is qubesadmin.DEFAULT: - src_vm = app.default_template - else: - src_vm = template - vm = app.clone_vm(src_vm, name, vmclass) - vm.label = label - for k, v in properties.items(): - setattr(vm, k, v) - else: - vm = app.add_new_vm(vmclass, - name=name, label=label, template=template) - for k, v in properties.items(): - setattr(vm, k, v) - - except qubesadmin.exc.QubesException as qex: - thread_monitor.set_error_msg(str(qex)) - except Exception as ex: # pylint: disable=broad-except - thread_monitor.set_error_msg(repr(ex)) - - thread_monitor.set_finished() def type_change(self): - # AppVM if self.vm_type.currentIndex() == 0: self.template_vm.setEnabled(True) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 63c8bf0..0022f59 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -25,12 +25,13 @@ import sys import os import os.path import subprocess -import time from datetime import datetime, timedelta import traceback +from contextlib import suppress +import time + import quamash import asyncio -from contextlib import suppress from qubesadmin import Qubes from qubesadmin import exc @@ -51,6 +52,7 @@ from . import backup from . import create_new_vm from . import log_dialog from . import utils as manager_utils +from . import common_threads class SearchBox(QtGui.QLineEdit): @@ -241,32 +243,20 @@ class StartVMThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) self.vm = vm + self.error = None 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)) + self.error = ("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 + self.error = None def run(self): try: @@ -279,23 +269,7 @@ class UpdateVMThread(QtCore.QThread): 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)) + self.error = ("Error on Qube update!", str(ex)) class RunCommandThread(QtCore.QThread): @@ -308,8 +282,7 @@ class RunCommandThread(QtCore.QThread): 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)) + self.error = ("Error while running command!", str(ex)) class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): @@ -330,7 +303,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): "IP": 8, "Backups": 9, "Last backup": 10, - } + } def __init__(self, qt_app, qubes_app, dispatcher, parent=None): # pylint: disable=unused-argument @@ -472,7 +445,10 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): dispatcher.add_handler('domain-start-failed', self.on_domain_status_changed) dispatcher.add_handler('domain-stopped', self.on_domain_status_changed) + dispatcher.add_handler('domain-pre-shutdown', self.on_domain_status_changed) dispatcher.add_handler('domain-shutdown', self.on_domain_status_changed) + dispatcher.add_handler('domain-paused', self.on_domain_status_changed) + dispatcher.add_handler('domain-unpaused', self.on_domain_status_changed) dispatcher.add_handler('domain-add', self.on_domain_added) dispatcher.add_handler('domain-delete', self.on_domain_removed) @@ -496,6 +472,12 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): def clear_threads(self): for thread in self.threads_list: if thread.isFinished(): + if thread.error: + (title, msg) = thread.error + QtGui.QMessageBox.warning( + None, + self.tr(title), + self.tr("ERROR: {0}").format(msg)) self.threads_list.remove(thread) def closeEvent(self, event): @@ -659,7 +641,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.manager_settings.sync() def table_selection_changed(self): - vm = self.get_selected_vm() if vm is not None and vm in self.qubes_app.domains: @@ -783,7 +764,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): else: # remove the VM - thread = RemoveVMThread(vm, self.qubes_app) + thread = common_threads.RemoveVMThread(vm) self.threads_list.append(thread) self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error) thread.finished.connect(self.clear_threads) @@ -806,16 +787,10 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): if not ok or clone_name == "": return - self.thread = CloneVMThread(vm, self.qubes_app, clone_name) - self.thread.start() - return - - progress = QtGui.QProgressDialog( - self.tr("Cloning Qube {0} to {1}...").format( - vm.name, clone_name), "", 0, 0) - progress.setCancelButton(None) - progress.setModal(True) - progress.show() + thread = common_threads.CloneVMThread(vm, clone_name) + thread.finished.connect(self.clear_threads) + self.threads_list.append(thread) + thread.start() # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_resumevm_triggered') @@ -825,8 +800,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): if vm.get_power_state() in ["Paused", "Suspended"]: try: vm.unpause() - self.vms_in_table[vm.qid].update() - self.table_selection_changed() except exc.QubesException as ex: QtGui.QMessageBox.warning( None, self.tr("Error unpausing Qube!"), @@ -841,7 +814,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): thread = StartVMThread(vm) self.threads_list.append(thread) - self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error) thread.finished.connect(self.clear_threads) thread.start() @@ -1016,7 +988,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): thread = UpdateVMThread(vm) self.threads_list.append(thread) - self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error) thread.finished.connect(self.clear_threads) thread.start() @@ -1035,11 +1006,9 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): thread = RunCommandThread(vm, command_to_run) self.threads_list.append(thread) - self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error) thread.finished.connect(self.clear_threads) thread.start() - # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_set_keyboard_layout_triggered') def action_set_keyboard_layout_triggered(self): @@ -1079,7 +1048,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_backup_triggered') def action_backup_triggered(self): - backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app) + backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app, self.dispatcher) backup_window.show() # noinspection PyArgumentList diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index 18e628c..985e346 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -38,7 +38,6 @@ from qubes import backup from . import ui_restoredlg # pylint: disable=no-name-in-module from . import multiselectwidget from . import backup_utils -from . import thread_monitor from multiprocessing import Queue, Event from multiprocessing.queues import Empty @@ -46,8 +45,37 @@ from qubesadmin import Qubes, exc from qubesadmin.backup import restore -class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): +class RestoreThread(QtCore.QThread): + def __init__(self, backup_restore, vms_to_restore): + QtCore.QThread.__init__(self) + self.backup_restore = backup_restore + self.vms_to_restore = vms_to_restore + self.error = None + self.msg = None + def run(self): + err_msg = [] + try: + self.backup_restore.restore_do(self.vms_to_restore) + + except backup.BackupCanceledError as ex: + self.canceled = True + err_msg.append(str(ex)) + except Exception as ex: # pylint: disable=broad-except + err_msg.append(str(ex)) + err_msg.append( + self.tr("Partially restored files left in /var/tmp/restore_*, " + "investigate them and/or clean them up")) + if err_msg: + self.error = '\n'.join(err_msg) + self.msg = '{0}'.format( + self.tr("Finished with errors!")) + else: + self.msg = '{0}'.format( + self.tr("Finished successfully!")) + + +class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): def __init__(self, qt_app, qubes_app, parent=None): super(RestoreVMsWindow, self).__init__(parent) @@ -64,9 +92,6 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): logger.addHandler(handler) logger.setLevel(logging.INFO) - self.canceled = False - self.error_detected = Event() - self.thread_monitor = None self.backup_restore = None self.target_appvm = None @@ -148,37 +173,8 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): def append_output(self, text): self.commit_text_edit.append(text) - def __do_restore__(self, t_monitor): - err_msg = [] - try: - self.backup_restore.restore_do(self.vms_to_restore) - - except backup.BackupCanceledError as ex: - self.canceled = True - err_msg.append(str(ex)) - except Exception as ex: # pylint: disable=broad-except - err_msg.append(str(ex)) - err_msg.append( - self.tr("Partially restored files left in /var/tmp/restore_*, " - "investigate them and/or clean them up")) - - if self.canceled: - self.append_output('{0}'.format( - self.tr("Restore aborted!"))) - elif err_msg or self.error_detected.is_set(): - if err_msg: - t_monitor.set_error_msg('\n'.join(err_msg)) - self.append_output('{0}'.format( - self.tr("Finished with errors!"))) - else: - self.append_output('{0}'.format( - self.tr("Finished successfully!"))) - - t_monitor.set_finished() - def current_page_changed(self, page_id): # pylint: disable=unused-argument - - old_sigchld_handler = signal.signal(signal.SIGCHLD, signal.SIG_DFL) + self.old_sigchld_handler = signal.signal(signal.SIGCHLD, signal.SIG_DFL) if self.currentPage() is self.select_vms_page: self.__fill_vms_list__() @@ -210,51 +206,57 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): and str(self.dir_line_edit.text()) .count("media/") > 0) - self.thread_monitor = thread_monitor.ThreadMonitor() - thread = threading.Thread(target=self.__do_restore__, - args=(self.thread_monitor,)) - thread.daemon = True - thread.start() - while not self.thread_monitor.is_finished(): - self.qt_app.processEvents() - time.sleep(0.1) - try: - log_record = self.feedback_queue.get_nowait() - while log_record: - if log_record.levelno == logging.ERROR or\ - log_record.levelno == logging.CRITICAL: - output = '{0}'.format( - log_record.getMessage()) - else: - output = log_record.getMessage() - self.append_output(output) - log_record = self.feedback_queue.get_nowait() - except Empty: - pass + self.thread = RestoreThread(self.backup_restore, self.vms_to_restore) + self.thread.finished.connect(self.thread_finished) - if not self.thread_monitor.success: - if not self.canceled: - QtGui.QMessageBox.warning( - None, - self.tr("Backup error!"), - self.tr("ERROR: {0}").format( - self.thread_monitor.error_msg)) - self.progress_bar.setMaximum(100) - self.progress_bar.setValue(100) + # Start log timer + timer = QtCore.QTimer(self) + timer.timeout.connect(self.update_log) + timer.start(1000) - if self.showFileDialog.isChecked(): - self.append_output( - '{0}'.format( - self.tr("Please unmount your backup volume and cancel " - "the file selection dialog."))) - self.qt_app.processEvents() - backup_utils.select_path_button_clicked(self, False, True) + self.thread.start() - self.button(self.FinishButton).setEnabled(True) - self.button(self.CancelButton).setEnabled(False) - self.showFileDialog.setEnabled(False) + def thread_finished(self): + self.progress_bar.setMaximum(100) + self.progress_bar.setValue(100) + + if self.thread.error: + QtGui.QMessageBox.warning( + None, + self.tr("Backup error!"), + self.tr("ERROR: {0}").format( + self.thread.error)) + + if self.thread.msg: + self.append_output(self.thread.msg) + + if self.showFileDialog.isChecked(): + self.append_output( + '{0}'.format( + self.tr("Please unmount your backup volume and cancel " + "the file selection dialog."))) + backup_utils.select_path_button_clicked(self, False, True) + + self.button(self.FinishButton).setEnabled(True) + self.button(self.CancelButton).setEnabled(False) + self.showFileDialog.setEnabled(False) + signal.signal(signal.SIGCHLD, self.old_sigchld_handler) + + def update_log(self): + try: + log_record = self.feedback_queue.get_nowait() + while log_record: + if log_record.levelno == logging.ERROR or\ + log_record.levelno == logging.CRITICAL: + output = '{0}'.format( + log_record.getMessage()) + else: + output = log_record.getMessage() + self.append_output(output) + log_record = self.feedback_queue.get_nowait() + except Empty: + pass - signal.signal(signal.SIGCHLD, old_sigchld_handler) def all_vms_good(self): for vm_info in self.vms_to_restore.values(): @@ -265,11 +267,13 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): return True def reject(self): - if self.currentPage() is self.commit_page: + if self.currentPage() is self.commit_page and self.thread.isRunning(): self.backup_restore.canceled = True self.append_output('{0}'.format( self.tr("Aborting the operation..."))) self.button(self.CancelButton).setDisabled(True) + + self.thread.terminate() else: self.done(0) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index fcb749d..87845da 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -39,7 +39,7 @@ import qubesadmin.exc from . import utils from . import multiselectwidget -from . import thread_monitor +from . import common_threads from . import device_list from .appmenu_select import AppmenuSelectManager @@ -49,6 +49,77 @@ from PyQt4 import QtCore, QtGui # pylint: disable=import-error from . import ui_settingsdlg # pylint: disable=no-name-in-module +class RenameVMThread(QtCore.QThread): + def __init__(self, vm, new_vm_name, dependencies): + QtCore.QThread.__init__(self) + self.vm = vm + self.new_vm_name = new_vm_name + self.dependencies = dependencies + + def run(self): + try: + new_vm = self.vm.app.clone_vm(self.vm, self.new_vm_name) + + failed_props = [] + + for (holder, prop) in self.dependencies: + try: + if holder is None: + setattr(self.vm.app, prop, new_vm) + else: + setattr(holder, prop, new_vm) + except exc.QubesException as qex: + failed_props += [(holder, prop)] + + if not failed_props: + del self.vm.app.domains[self.vm.name] + else: + list_text = utils.format_dependencies_list(failed_props) + + QtGui.QMessageBox.warning( + self, + self.tr("Warning: rename partially unsuccessful"), + self.tr("Some properties could not be changed to the new " + "name. The system has now both {} and {} qubes. " + "To resolve this, please check and change the " + "following properties and remove the qube {} " + "manually.
").format( + self.vm.name, name, self.vm.name) + list_text) + + except exc.QubesException as qex: + self.error = ("Rename error!", str(ex)) + except Exception as ex: # pylint: disable=broad-except + self.error = ("Rename error!", repr(ex)) + + +class RefreshAppsVMThread(QtCore.QThread): + def __init__(self, vm): + QtCore.QThread.__init__(self) + self.error = None + self.vm = vm + + def run(self): + try: + try: + target_vm = self.vm.template + except AttributeError: + target_vm = self.vm + + if not target_vm.is_running(): + not_running = True + target_vm.start() + else: + not_running = False + + subprocess.check_call(['qvm-sync-appmenus', target_vm.name]) + + if not_running: + target_vm.shutdown() + + except Exception as ex: + self.error = ("Refresh failed!", str(ex)) + + # pylint: disable=too-many-instance-attributes class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): tabs_indices = collections.OrderedDict(( @@ -65,6 +136,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.vm = vm self.qapp = qapp + self.threads_list = [] try: self.source_vm = self.vm.template except AttributeError: @@ -150,6 +222,18 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.refresh_apps_button.clicked.connect( self.refresh_apps_button_pressed) + def clear_threads(self): + for thread in self.threads_list: + if thread.isFinished(): + if thread.error: + (title, msg) = thread.error + QtGui.QMessageBox.warning( + None, + self.tr(title), + self.tr("ERROR: {0}").format(msg)) + + self.threads_list.remove(thread) + def keyPressEvent(self, event): # pylint: disable=invalid-name if event.key() == QtCore.Qt.Key_Enter \ or event.key() == QtCore.Qt.Key_Return: @@ -164,31 +248,14 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): pass def save_changes(self): - t_monitor = thread_monitor.ThreadMonitor() - thread = threading.Thread(target=self.__save_changes__, - args=(t_monitor,)) - thread.daemon = True - thread.start() + error = self.__save_changes__() - progress = QtGui.QProgressDialog( - self.tr("Applying settings to {0}...").format(self.vm.name), - "", 0, 0) - progress.setCancelButton(None) - progress.setModal(True) - progress.show() - - while not t_monitor.is_finished(): - self.qapp.processEvents() - time.sleep(0.1) - - progress.hide() - - if not t_monitor.success: + if error: QtGui.QMessageBox.warning( self, self.tr("Error while changing settings for {0}!" ).format(self.vm.name), - self.tr("ERROR: {0}").format(t_monitor.error_msg)) + self.tr("ERROR: {0}").format('\n'.join(ret))) def apply(self): self.save_changes() @@ -197,9 +264,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.save_changes() self.done(0) - def __save_changes__(self, t_monitor): - + def __save_changes__(self): ret = [] + try: ret_tmp = self.__apply_basic_tab__() if ret_tmp: @@ -237,12 +304,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): except Exception as ex: # pylint: disable=broad-except ret += [self.tr("Applications tab:"), repr(ex)] - if ret: - t_monitor.set_error_msg('\n'.join(ret)) - utils.debug('\n'.join(ret)) - - t_monitor.set_finished() + return ret def check_network_availability(self): netvm = self.vm.netvm @@ -464,61 +527,6 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): "allowed value.")) self.init_mem.setValue(self.max_mem_size.value() / 10) - def _run_in_thread(self, func, *args): - t_monitor = thread_monitor.ThreadMonitor() - thread = threading.Thread(target=func, args=(t_monitor, *args,)) - thread.daemon = True - thread.start() - - while not t_monitor.is_finished(): - self.qapp.processEvents() - time.sleep(0.1) - - if not t_monitor.success: - QtGui.QMessageBox.warning(self, - self.tr("Error!"), - self.tr("ERROR: {}").format( - t_monitor.error_msg)) - return False - return True - - def _rename_vm(self, t_monitor, name, dependencies): - try: - new_vm = self.vm.app.clone_vm(self.vm, name) - - failed_props = [] - - for (holder, prop) in dependencies: - try: - if holder is None: - setattr(self.vm.app, prop, new_vm) - else: - setattr(holder, prop, new_vm) - except qubesadmin.exc.QubesException as qex: - failed_props += [(holder, prop)] - - if not failed_props: - del self.vm.app.domains[self.vm.name] - else: - list_text = utils.format_dependencies_list(failed_props) - - QtGui.QMessageBox.warning( - self, - self.tr("Warning: rename partially unsuccessful"), - self.tr("Some properties could not be changed to the new " - "name. The system has now both {} and {} qubes. " - "To resolve this, please check and change the " - "following properties and remove the qube {} " - "manually.
").format( - self.vm.name, name, self.vm.name) + list_text) - - except qubesadmin.exc.QubesException as qex: - t_monitor.set_error_msg(str(qex)) - except Exception as ex: # pylint: disable=broad-except - t_monitor.set_error_msg(repr(ex)) - - t_monitor.set_finished() - def rename_vm(self): dependencies = admin_utils.vm_dependencies(self.vm.app, self.vm) @@ -544,19 +552,12 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.tr('New name: (WARNING: all other changes will be discarded)')) if ok: - if self._run_in_thread(self._rename_vm, new_vm_name, dependencies): - self.done(0) - - def _remove_vm(self, t_monitor): - try: - del self.vm.app.domains[self.vm.name] - - except qubesadmin.exc.QubesException as qex: - t_monitor.set_error_msg(str(qex)) - except Exception as ex: # pylint: disable=broad-except - t_monitor.set_error_msg(repr(ex)) - - t_monitor.set_finished() + thread = RenameVMThread(self.vm, new_vm_name, dependencies) + self.threads_list.append(thread) + thread.finished.connect(self.clear_threads) + thread.start() + thread.wait() + #self.done(0) def remove_vm(self): @@ -584,7 +585,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): 'qube\'s name below.')) if ok and answer == self.vm.name: - self._run_in_thread(self._remove_vm) + thread = common_threads.RemoveVMThread(self.vm) + thread.start() self.done(0) elif ok: @@ -612,11 +614,10 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.tr('Name for the cloned qube:')) if ok: - self._run_in_thread(self._clone_vm, cloned_vm_name) - QtGui.QMessageBox.warning( - self, - self.tr("Success"), - self.tr("The qube was cloned successfully.")) + thread = common_threads.CloneVMThread(self.vm, cloned_vm_name) + thread.finished.connect(self.clear_threads) + self.threads_list.append(thread) + thread.start() ######### advanced tab @@ -944,43 +945,19 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): ######## applications tab - def refresh_apps_in_vm(self, t_monitor): - try: - target_vm = self.vm.template - except AttributeError: - target_vm = self.vm - - if not target_vm.is_running(): - not_running = True - target_vm.start() - else: - not_running = False - - subprocess.check_call(['qvm-sync-appmenus', target_vm.name]) - - if not_running: - target_vm.shutdown() - - t_monitor.set_finished() - def refresh_apps_button_pressed(self): self.refresh_apps_button.setEnabled(False) self.refresh_apps_button.setText(self.tr('Refresh in progress...')) - t_monitor = thread_monitor.ThreadMonitor() - thread = threading.Thread( - target=self.refresh_apps_in_vm, - args=(t_monitor,)) - thread.daemon = True + thread = RefreshAppsVMThread(self.vm) + thread.finished.connect(self.clear_threads) + thread.finished.connect(self.refresh_finished) + self.threads_list.append(thread) thread.start() - while not t_monitor.is_finished(): - self.qapp.processEvents() - time.sleep(0.1) - + def refresh_finished(self): self.app_list_manager = AppmenuSelectManager(self.vm, self.app_list) - self.refresh_apps_button.setEnabled(True) self.refresh_apps_button.setText(self.tr('Refresh Applications')) diff --git a/qubesmanager/thread_monitor.py b/qubesmanager/thread_monitor.py deleted file mode 100644 index 8515aff..0000000 --- a/qubesmanager/thread_monitor.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/python2 -# -# The Qubes OS Project, http://www.qubes-os.org -# -# Copyright (C) 2011 Marek Marczykowski -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with this program; if not, see . -# -# - - -import PyQt4.QtCore # pylint: disable=import-error - -import threading - -class ThreadMonitor(PyQt4.QtCore.QObject): - def __init__(self): - self.success = True - self.error_msg = None - self.event_finished = threading.Event() - - def set_error_msg(self, error_msg): - self.success = False - self.error_msg = error_msg - self.set_finished() - - def is_finished(self): - return self.event_finished.is_set() - - def set_finished(self): - self.event_finished.set() From 11cc8b7a67bb185d0110202b43d72b49c8242413 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 19:20:39 +0200 Subject: [PATCH 11/48] Remove debug print --- qubesmanager/backup.py | 1 - qubesmanager/qube_manager.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index fdc284c..9847c9c 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -139,7 +139,6 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): dispatcher.add_handler('backup-progress', self.on_backup_progress) def on_backup_progress(self, __submitter, _event, **kwargs): - print(kwargs['progress']) self.progress_bar.setValue(int(float(kwargs['progress']))) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 0022f59..3e936fe 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -835,8 +835,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): vm = self.get_selected_vm() try: vm.pause() - self.vms_in_table[vm.qid].update() - self.table_selection_changed() except exc.QubesException as ex: QtGui.QMessageBox.warning( None, From ad1bcc55b935912a24a3671be249ad7809711864 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 21:25:13 +0200 Subject: [PATCH 12/48] Deleted unneded method _clone_vm() --- qubesmanager/settings.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 87845da..8d61d7d 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -595,17 +595,6 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.tr("Removal cancelled"), self.tr("The qube will not be removed.")) - def _clone_vm(self, t_monitor, name): - try: - self.vm.app.clone_vm(self.vm, name) - - except qubesadmin.exc.QubesException as qex: - t_monitor.set_error_msg(str(qex)) - except Exception as ex: # pylint: disable=broad-except - t_monitor.set_error_msg(repr(ex)) - - t_monitor.set_finished() - def clone_vm(self): cloned_vm_name, ok = QtGui.QInputDialog.getText( From 2347f7563329bd6895766bb5910827d9cc0927ff Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 21:54:25 +0200 Subject: [PATCH 13/48] Clear SearchBox with ESC key --- qubesmanager/qube_manager.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 3e936fe..23a8a42 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -71,6 +71,11 @@ class SearchBox(QtGui.QLineEdit): self.selectAll() self.focusing = False + def keyPressEvent(self, event): # pylint: disable=invalid-name + if event.key() == QtCore.Qt.Key_Escape: + self.clear() + super(SearchBox, self).keyPressEvent(event) + class VmRowInTable: # pylint: disable=too-few-public-methods From 38051ad2432cae86ad3b495198df812bb881a5e2 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 22:09:39 +0200 Subject: [PATCH 14/48] Fixes in ProgressDialogs and settings window close --- qubesmanager/qube_manager.py | 16 +++++++++++++++- qubesmanager/settings.py | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 23a8a42..ef9e495 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -467,6 +467,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): # It needs to store threads until they finish self.threads_list = [] + self.progress = None # Check Updates Timer timer = QtCore.QTimer(self) @@ -477,12 +478,17 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): def clear_threads(self): for thread in self.threads_list: if thread.isFinished(): + if self.progress: + self.progress.hide() + self.progress = None + if thread.error: (title, msg) = thread.error QtGui.QMessageBox.warning( None, self.tr(title), - self.tr("ERROR: {0}").format(msg)) + self.tr(msg)) + self.threads_list.remove(thread) def closeEvent(self, event): @@ -792,6 +798,14 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): if not ok or clone_name == "": return + self.progress = QtGui.QProgressDialog( + self.tr( + "Cloning Qube..."), "", 0, 0) + self.progress.setCancelButton(None) + self.progress.setModal(True) + self.thread_closes = True + self.progress.show() + thread = common_threads.CloneVMThread(vm, clone_name) thread.finished.connect(self.clear_threads) self.threads_list.append(thread) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 8d61d7d..51b697d 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -55,6 +55,7 @@ class RenameVMThread(QtCore.QThread): self.vm = vm self.new_vm_name = new_vm_name self.dependencies = dependencies + self.error = None def run(self): try: @@ -95,8 +96,8 @@ class RenameVMThread(QtCore.QThread): class RefreshAppsVMThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) - self.error = None self.vm = vm + self.error = None def run(self): try: @@ -137,6 +138,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.vm = vm self.qapp = qapp self.threads_list = [] + self.progress = None + self.thread_closes = False try: self.source_vm = self.vm.template except AttributeError: @@ -225,15 +228,22 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): def clear_threads(self): for thread in self.threads_list: if thread.isFinished(): + if self.progress: + self.progress.hide() + self.progress = None + if thread.error: (title, msg) = thread.error QtGui.QMessageBox.warning( None, self.tr(title), - self.tr("ERROR: {0}").format(msg)) + self.tr(msg)) self.threads_list.remove(thread) + if self.thread_closes: + self.done(0) + def keyPressEvent(self, event): # pylint: disable=invalid-name if event.key() == QtCore.Qt.Key_Enter \ or event.key() == QtCore.Qt.Key_Return: @@ -555,9 +565,16 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): thread = RenameVMThread(self.vm, new_vm_name, dependencies) self.threads_list.append(thread) thread.finished.connect(self.clear_threads) + + self.progress = QtGui.QProgressDialog( + self.tr( + "Renaming Qube..."), "", 0, 0) + self.progress.setCancelButton(None) + self.progress.setModal(True) + self.thread_closes = True + self.progress.show() + thread.start() - thread.wait() - #self.done(0) def remove_vm(self): @@ -606,6 +623,15 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): thread = common_threads.CloneVMThread(self.vm, cloned_vm_name) thread.finished.connect(self.clear_threads) self.threads_list.append(thread) + + self.progress = QtGui.QProgressDialog( + self.tr( + "Cloning Qube..."), "", 0, 0) + self.progress.setCancelButton(None) + self.progress.setModal(True) + self.thread_closes = True + self.progress.show() + thread.start() ######### advanced tab From 831769d2dd3a149ad09ed2daeb04baa8b8df7ba6 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 23:01:35 +0200 Subject: [PATCH 15/48] Fix travis build --- qubesmanager.pro | 2 +- qubesmanager/backup.py | 2 +- qubesmanager/common_threads.py | 3 ++- qubesmanager/create_new_vm.py | 9 +++++---- qubesmanager/qube_manager.py | 11 ++++++----- qubesmanager/restore.py | 13 ++++++++----- qubesmanager/settings.py | 18 ++++++++---------- 7 files changed, 31 insertions(+), 27 deletions(-) diff --git a/qubesmanager.pro b/qubesmanager.pro index 51f4d1c..f186c52 100644 --- a/qubesmanager.pro +++ b/qubesmanager.pro @@ -17,6 +17,7 @@ SOURCES = \ qubesmanager/block.py \ qubesmanager/clipboard.py \ qubesmanager/create_new_vm.py \ + qubesmanager/common_threads.py \ qubesmanager/firewall.py \ qubesmanager/global_settings.py \ qubesmanager/log_dialog.py \ @@ -27,7 +28,6 @@ SOURCES = \ qubesmanager/restore.py \ qubesmanager/settings.py \ qubesmanager/table_widgets.py \ - qubesmanager/thread_monitor.py \ qubesmanager/ui_about.py \ qubesmanager/ui_backupdlg.py \ qubesmanager/ui_globalsettingsdlg.py \ diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index 9847c9c..542df22 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -44,7 +44,6 @@ import sys import os import asyncio from contextlib import suppress -import time class BackupThread(QtCore.QThread): def __init__(self, vm): @@ -76,6 +75,7 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): self.backup_settings = QtCore.QSettings() self.selected_vms = [] + self.thread = None self.setupUi(self) diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index c7fd1ff..746f9ea 100644 --- a/qubesmanager/common_threads.py +++ b/qubesmanager/common_threads.py @@ -42,10 +42,11 @@ class CloneVMThread(QtCore.QThread): QtCore.QThread.__init__(self) self.src_vm = src_vm self.dst_name = dst_name + self.error = None def run(self): try: - dst_vm = self.src_vm.app.clone_vm(self.src_vm, self.dst_name) + self.src_vm.app.clone_vm(self.src_vm, self.dst_name) self.error = ("Sucess", "The qube was cloned sucessfully.") except exc.QubesException as ex: self.error = ("Error while cloning Qube!", str(ex)) diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py index 73959ad..88122c3 100644 --- a/qubesmanager/create_new_vm.py +++ b/qubesmanager/create_new_vm.py @@ -22,8 +22,6 @@ # import sys -import threading -import time import subprocess from PyQt4 import QtCore, QtGui # pylint: disable=import-error @@ -78,6 +76,9 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg): self.qtapp = qtapp self.app = app + self.thread = None + self.progress = None + # Theoretically we should be locking for writing here and unlock # only after the VM creation finished. But the code would be # more messy... @@ -174,7 +175,7 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg): if self.thread.error: QtGui.QMessageBox.warning(None, self.tr("Error creating the qube!"), - self.tr("ERROR: {}").format(thread.error)) + self.tr("ERROR: {}").format(self.thread.error)) self.done(0) @@ -183,7 +184,7 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg): subprocess.check_call(['qubes-vm-settings', str(self.name)]) if self.install_system.isChecked(): subprocess.check_call( - ['qubes-vm-boot-from-device', srt(self.name)]) + ['qubes-vm-boot-from-device', str(self.name)]) def type_change(self): diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index ef9e495..25ea5d3 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -28,7 +28,6 @@ import subprocess from datetime import datetime, timedelta import traceback from contextlib import suppress -import time import quamash import asyncio @@ -282,6 +281,7 @@ class RunCommandThread(QtCore.QThread): QtCore.QThread.__init__(self) self.vm = vm self.command_to_run = command_to_run + self.error = None def run(self): try: @@ -450,7 +450,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): dispatcher.add_handler('domain-start-failed', self.on_domain_status_changed) dispatcher.add_handler('domain-stopped', self.on_domain_status_changed) - dispatcher.add_handler('domain-pre-shutdown', self.on_domain_status_changed) + dispatcher.add_handler('domain-pre-shutdown', + self.on_domain_status_changed) dispatcher.add_handler('domain-shutdown', self.on_domain_status_changed) dispatcher.add_handler('domain-paused', self.on_domain_status_changed) dispatcher.add_handler('domain-unpaused', self.on_domain_status_changed) @@ -777,7 +778,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): # remove the VM thread = common_threads.RemoveVMThread(vm) self.threads_list.append(thread) - self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error) + self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), + self.show_error) thread.finished.connect(self.clear_threads) thread.start() @@ -803,7 +805,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): "Cloning Qube..."), "", 0, 0) self.progress.setCancelButton(None) self.progress.setModal(True) - self.thread_closes = True self.progress.show() thread = common_threads.CloneVMThread(vm, clone_name) @@ -871,7 +872,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.tr("Are you sure you want to power down the Qube" " '{0}'?
This will shutdown all the " "running applications within this Qube.").format( - vm.name), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) + vm.name), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) self.qt_app.processEvents() diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index 985e346..6cb46c7 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -23,8 +23,6 @@ import sys from PyQt4 import QtCore # pylint: disable=import-error from PyQt4 import QtGui # pylint: disable=import-error -import threading -import time import os import os.path import traceback @@ -39,7 +37,7 @@ from . import ui_restoredlg # pylint: disable=no-name-in-module from . import multiselectwidget from . import backup_utils -from multiprocessing import Queue, Event +from multiprocessing import Queue from multiprocessing.queues import Empty from qubesadmin import Qubes, exc from qubesadmin.backup import restore @@ -52,6 +50,7 @@ class RestoreThread(QtCore.QThread): self.vms_to_restore = vms_to_restore self.error = None self.msg = None + self.canceled = None def run(self): err_msg = [] @@ -85,6 +84,9 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): self.vms_to_restore = None self.func_output = [] + self.thread = None + self.old_sigchld_handler = None + # Set up logging self.feedback_queue = Queue() handler = logging.handlers.QueueHandler(self.feedback_queue) @@ -206,13 +208,14 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): and str(self.dir_line_edit.text()) .count("media/") > 0) - self.thread = RestoreThread(self.backup_restore, self.vms_to_restore) + self.thread = RestoreThread(self.backup_restore, + self.vms_to_restore) self.thread.finished.connect(self.thread_finished) # Start log timer timer = QtCore.QTimer(self) timer.timeout.connect(self.update_log) - timer.start(1000) + timer.start(1000) self.thread.start() diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 51b697d..742cc8e 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -28,8 +28,6 @@ import os.path import os import re import subprocess -import threading -import time import traceback import sys from qubesadmin.tools import QubesArgumentParser @@ -85,9 +83,9 @@ class RenameVMThread(QtCore.QThread): "To resolve this, please check and change the " "following properties and remove the qube {} " "manually.
").format( - self.vm.name, name, self.vm.name) + list_text) + self.vm.name, self.vm.name, self.vm.name) + list_text) - except exc.QubesException as qex: + except exc.QubesException as ex: self.error = ("Rename error!", str(ex)) except Exception as ex: # pylint: disable=broad-except self.error = ("Rename error!", repr(ex)) @@ -117,7 +115,7 @@ class RefreshAppsVMThread(QtCore.QThread): if not_running: target_vm.shutdown() - except Exception as ex: + except Exception as ex: # pylint: disable=broad-except self.error = ("Refresh failed!", str(ex)) @@ -265,7 +263,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self, self.tr("Error while changing settings for {0}!" ).format(self.vm.name), - self.tr("ERROR: {0}").format('\n'.join(ret))) + self.tr("ERROR: {0}").format('\n'.join(error))) def apply(self): self.save_changes() @@ -564,7 +562,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if ok: thread = RenameVMThread(self.vm, new_vm_name, dependencies) self.threads_list.append(thread) - thread.finished.connect(self.clear_threads) + thread.finished.connect(self.clear_threads) self.progress = QtGui.QProgressDialog( self.tr( @@ -621,7 +619,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if ok: thread = common_threads.CloneVMThread(self.vm, cloned_vm_name) - thread.finished.connect(self.clear_threads) + thread.finished.connect(self.clear_threads) self.threads_list.append(thread) self.progress = QtGui.QProgressDialog( @@ -966,8 +964,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.refresh_apps_button.setText(self.tr('Refresh in progress...')) thread = RefreshAppsVMThread(self.vm) - thread.finished.connect(self.clear_threads) - thread.finished.connect(self.refresh_finished) + thread.finished.connect(self.clear_threads) + thread.finished.connect(self.refresh_finished) self.threads_list.append(thread) thread.start() From f93908f5b796059adcb43d6cfb28ff3e9abc40c1 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 20 Oct 2018 23:23:06 +0200 Subject: [PATCH 16/48] Fix travis build2 --- qubesmanager/common_threads.py | 2 +- qubesmanager/qube_manager.py | 3 ++- qubesmanager/settings.py | 36 +++++++++++++++++----------------- rpm_spec/qmgr.spec.in | 2 +- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index 746f9ea..63bb051 100644 --- a/qubesmanager/common_threads.py +++ b/qubesmanager/common_threads.py @@ -34,7 +34,7 @@ class RemoveVMThread(QtCore.QThread): try: del self.vm.app.domains[self.vm.name] except exc.QubesException as ex: - self.error("Error removing Qube!", str(ex)) + self.error = ("Error removing Qube!", str(ex)) class CloneVMThread(QtCore.QThread): diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 25ea5d3..ae9322e 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -1066,7 +1066,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_backup_triggered') def action_backup_triggered(self): - backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app, self.dispatcher) + backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app, + self.dispatcher) backup_window.show() # noinspection PyArgumentList diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 742cc8e..197096c 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -33,7 +33,7 @@ import sys from qubesadmin.tools import QubesArgumentParser from qubesadmin import devices from qubesadmin import utils as admin_utils -import qubesadmin.exc +from qubesadmin import exc from . import utils from . import multiselectwidget @@ -67,7 +67,7 @@ class RenameVMThread(QtCore.QThread): setattr(self.vm.app, prop, new_vm) else: setattr(holder, prop, new_vm) - except exc.QubesException as qex: + except exc.QubesException: failed_props += [(holder, prop)] if not failed_props: @@ -288,7 +288,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): ret_tmp = self.__apply_services_tab__() if ret_tmp: ret += ["Sevices tab:"] + ret_tmp - except qubesadmin.exc.QubesException as qex: + except exc.QubesException as qex: ret.append(self.tr('Error while saving changes: ') + str(qex)) except Exception as ex: # pylint: disable=broad-except ret.append(repr(ex)) @@ -299,7 +299,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.policy_allow_radio_button.isChecked(), self.temp_full_access.isChecked(), self.temp_full_access_time.value()) - except qubesadmin.exc.QubesException as qex: + except exc.QubesException as qex: ret += [self.tr("Firewall tab:"), str(qex)] except Exception as ex: # pylint: disable=broad-except ret += [self.tr("Firewall tab:"), repr(ex)] @@ -307,7 +307,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): try: if self.tabWidget.isTabEnabled(self.tabs_indices["applications"]): self.app_list_manager.save_appmenu_select_changes() - except qubesadmin.exc.QubesException as qex: + except exc.QubesException as qex: ret += [self.tr("Applications tab:"), str(qex)] except Exception as ex: # pylint: disable=broad-except ret += [self.tr("Applications tab:"), repr(ex)] @@ -455,7 +455,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.vmlabel.currentIndex() != self.label_idx: label = self.label_list[self.vmlabel.currentIndex()] self.vm.label = label - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) # vm template changed @@ -463,14 +463,14 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.template_name.currentIndex() != self.template_idx: self.vm.template = \ self.template_list[self.template_name.currentIndex()] - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) # vm netvm changed try: if self.netVM.currentIndex() != self.netvm_idx: self.vm.netvm = self.netvm_list[self.netVM.currentIndex()] - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) # include in backups @@ -478,7 +478,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.vm.include_in_backups != \ self.include_in_backups.isChecked(): self.vm.include_in_backups = self.include_in_backups.isChecked() - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) # run_in_debug_mode @@ -486,7 +486,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.run_in_debug_mode.isVisible(): if self.vm.debug != self.run_in_debug_mode.isChecked(): self.vm.debug = self.run_in_debug_mode.isChecked() - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) # autostart_vm @@ -494,7 +494,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.autostart_vm.isVisible(): if self.vm.autostart != self.autostart_vm.isChecked(): self.vm.autostart = self.autostart_vm.isChecked() - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) # max priv storage @@ -502,7 +502,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.priv_img_size != priv_size: try: self.vm.volumes['private'].resize(priv_size * 1024**2) - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) # max sys storage @@ -510,7 +510,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.root_img_size != sys_size: try: self.vm.volumes['root'].resize(sys_size * 1024**2) - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) return msg @@ -695,7 +695,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.vcpus.value() != int(self.vm.vcpus): self.vm.vcpus = self.vcpus.value() - except qubesadmin.exc.QubesException as ex: + except besadmin.exc.QubesException as ex: msg.append(str(ex)) # include_in_memory_balancing applied in services tab @@ -706,7 +706,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.kernel.currentIndex() != self.kernel_idx: self.vm.kernel = self.kernel_list[ self.kernel.currentIndex()] - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) # vm default_dispvm changed @@ -714,7 +714,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.default_dispvm.currentIndex() != self.default_dispvm_idx: self.vm.default_dispvm = \ self.default_dispvm_list[self.default_dispvm.currentIndex()] - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) try: @@ -902,7 +902,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if ass.ident.replace('_', ':') not in new: self.vm.devices['pci'].detach(ass) - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: if utils.is_debug(): traceback.print_exc() msg.append(str(ex)) @@ -1081,7 +1081,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): service = feature[len('service.'):] if service not in self.new_srv_dict: del self.vm.features[feature] - except qubesadmin.exc.QubesException as ex: + except exc.QubesException as ex: msg.append(str(ex)) return msg diff --git a/rpm_spec/qmgr.spec.in b/rpm_spec/qmgr.spec.in index 554eeb0..b00e42e 100644 --- a/rpm_spec/qmgr.spec.in +++ b/rpm_spec/qmgr.spec.in @@ -89,7 +89,7 @@ rm -rf $RPM_BUILD_ROOT %{python3_sitelib}/qubesmanager/releasenotes.py %{python3_sitelib}/qubesmanager/informationnotes.py %{python3_sitelib}/qubesmanager/create_new_vm.py -%{python3_sitelib}/qubesmanager/thread_monitor.py +%{python3_sitelib}/qubesmanager/common_threads.py %{python3_sitelib}/qubesmanager/qube_manager.py %{python3_sitelib}/qubesmanager/utils.py %{python3_sitelib}/qubesmanager/bootfromdevice.py From c46b38ab42f24eedf4cc8812396a3826afd13118 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 21 Oct 2018 00:06:37 +0200 Subject: [PATCH 17/48] Travis3 --- qubesmanager/settings.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 197096c..6c8ae53 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -33,7 +33,7 @@ import sys from qubesadmin.tools import QubesArgumentParser from qubesadmin import devices from qubesadmin import utils as admin_utils -from qubesadmin import exc +import qubesadmin.ecx from . import utils from . import multiselectwidget @@ -67,7 +67,7 @@ class RenameVMThread(QtCore.QThread): setattr(self.vm.app, prop, new_vm) else: setattr(holder, prop, new_vm) - except exc.QubesException: + except qubesadmin.exc.QubesException: failed_props += [(holder, prop)] if not failed_props: @@ -85,7 +85,7 @@ class RenameVMThread(QtCore.QThread): "manually.
").format( self.vm.name, self.vm.name, self.vm.name) + list_text) - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: self.error = ("Rename error!", str(ex)) except Exception as ex: # pylint: disable=broad-except self.error = ("Rename error!", repr(ex)) @@ -288,7 +288,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): ret_tmp = self.__apply_services_tab__() if ret_tmp: ret += ["Sevices tab:"] + ret_tmp - except exc.QubesException as qex: + except qubesadmin.exc.QubesException as qex: ret.append(self.tr('Error while saving changes: ') + str(qex)) except Exception as ex: # pylint: disable=broad-except ret.append(repr(ex)) @@ -299,7 +299,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.policy_allow_radio_button.isChecked(), self.temp_full_access.isChecked(), self.temp_full_access_time.value()) - except exc.QubesException as qex: + except qubesadmin.exc.QubesException as qex: ret += [self.tr("Firewall tab:"), str(qex)] except Exception as ex: # pylint: disable=broad-except ret += [self.tr("Firewall tab:"), repr(ex)] @@ -307,7 +307,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): try: if self.tabWidget.isTabEnabled(self.tabs_indices["applications"]): self.app_list_manager.save_appmenu_select_changes() - except exc.QubesException as qex: + except qubesadmin.exc.QubesException as qex: ret += [self.tr("Applications tab:"), str(qex)] except Exception as ex: # pylint: disable=broad-except ret += [self.tr("Applications tab:"), repr(ex)] @@ -455,7 +455,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.vmlabel.currentIndex() != self.label_idx: label = self.label_list[self.vmlabel.currentIndex()] self.vm.label = label - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) # vm template changed @@ -463,14 +463,14 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.template_name.currentIndex() != self.template_idx: self.vm.template = \ self.template_list[self.template_name.currentIndex()] - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) # vm netvm changed try: if self.netVM.currentIndex() != self.netvm_idx: self.vm.netvm = self.netvm_list[self.netVM.currentIndex()] - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) # include in backups @@ -478,7 +478,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.vm.include_in_backups != \ self.include_in_backups.isChecked(): self.vm.include_in_backups = self.include_in_backups.isChecked() - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) # run_in_debug_mode @@ -486,7 +486,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.run_in_debug_mode.isVisible(): if self.vm.debug != self.run_in_debug_mode.isChecked(): self.vm.debug = self.run_in_debug_mode.isChecked() - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) # autostart_vm @@ -494,7 +494,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.autostart_vm.isVisible(): if self.vm.autostart != self.autostart_vm.isChecked(): self.vm.autostart = self.autostart_vm.isChecked() - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) # max priv storage @@ -502,7 +502,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.priv_img_size != priv_size: try: self.vm.volumes['private'].resize(priv_size * 1024**2) - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) # max sys storage @@ -510,7 +510,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.root_img_size != sys_size: try: self.vm.volumes['root'].resize(sys_size * 1024**2) - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) return msg @@ -695,7 +695,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.vcpus.value() != int(self.vm.vcpus): self.vm.vcpus = self.vcpus.value() - except besadmin.exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) # include_in_memory_balancing applied in services tab @@ -706,7 +706,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.kernel.currentIndex() != self.kernel_idx: self.vm.kernel = self.kernel_list[ self.kernel.currentIndex()] - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) # vm default_dispvm changed @@ -714,7 +714,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.default_dispvm.currentIndex() != self.default_dispvm_idx: self.vm.default_dispvm = \ self.default_dispvm_list[self.default_dispvm.currentIndex()] - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) try: @@ -902,7 +902,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if ass.ident.replace('_', ':') not in new: self.vm.devices['pci'].detach(ass) - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: if utils.is_debug(): traceback.print_exc() msg.append(str(ex)) @@ -1081,7 +1081,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): service = feature[len('service.'):] if service not in self.new_srv_dict: del self.vm.features[feature] - except exc.QubesException as ex: + except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) return msg From a7537a323a9f28fed0e2614acd71293304988896 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 21 Oct 2018 00:16:23 +0200 Subject: [PATCH 18/48] Wops I did not notice on my pylint because it complains about all imports --- qubesmanager/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 6c8ae53..9f8bb7a 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -33,7 +33,7 @@ import sys from qubesadmin.tools import QubesArgumentParser from qubesadmin import devices from qubesadmin import utils as admin_utils -import qubesadmin.ecx +import qubesadmin.exc from . import utils from . import multiselectwidget From df15c4e3aeb56672b4611fb8faf95e5c3456a212 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 21 Oct 2018 22:38:41 +0200 Subject: [PATCH 19/48] Fix pylint warnings and errors --- qubesmanager/settings.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 9f8bb7a..154eca9 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -122,12 +122,12 @@ class RefreshAppsVMThread(QtCore.QThread): # pylint: disable=too-many-instance-attributes class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): tabs_indices = collections.OrderedDict(( - ('basic', 0), - ('advanced', 1), - ('firewall', 2), - ('devices', 3), - ('applications', 4), - ('services', 5), + ('basic', 0), + ('advanced', 1), + ('firewall', 2), + ('devices', 3), + ('applications', 4), + ('services', 5), )) def __init__(self, vm, qapp, init_page="basic", parent=None): @@ -261,8 +261,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if error: QtGui.QMessageBox.warning( self, - self.tr("Error while changing settings for {0}!" - ).format(self.vm.name), + self.tr("Error while changing settings for {0}!"\ + ).format(self.vm.name),\ self.tr("ERROR: {0}").format('\n'.join(error))) def apply(self): @@ -331,18 +331,17 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): 'please enable networking.') ) if netvm is not None and \ - not netvm.features.check_with_template( - 'qubes-firewall', - False): + not netvm.features.check_with_template(\ + 'qubes-firewall', False): QtGui.QMessageBox.warning( self, self.tr("Qube configuration problem!"), - self.tr("The '{vm}' qube is network connected to " - "'{netvm}', which does not support firewall!
" - "You may edit the '{vm}' qube firewall rules, but " - "these will not take any effect until you connect it " - "to a working Firewall qube.").format( - vm=self.vm.name, netvm=netvm.name)) + self.tr("The '{vm}' qube is network connected to "\ + "'{netvm}', which does not support firewall!
"\ + "You may edit the '{vm}' qube firewall rules, but "\ + "these will not take any effect until you connect it "\ + "to a working Firewall qube.").format(\ + vm=self.vm.name, netvm=netvm.name)) def current_tab_changed(self, idx): if idx == self.tabs_indices["firewall"]: @@ -766,8 +765,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.virt_mode.clear() # pylint: disable=attribute-defined-outside-init - self.virt_mode_list, self.virt_mode_idx = utils.prepare_choice( - self.virt_mode, self.vm, 'virt_mode', choices, None, + self.virt_mode_list, self.virt_mode_idx = utils.prepare_choice(\ + self.virt_mode, self.vm, 'virt_mode', choices, None,\ allow_default=True, transform=(lambda x: str(x).upper())) if old_mode is not None: From 4aea47c9f7111d7e1a39e3907f47aa90aa9d62f2 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 21 Oct 2018 22:52:52 +0200 Subject: [PATCH 20/48] ... --- qubesmanager/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 154eca9..6df925a 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -83,7 +83,8 @@ class RenameVMThread(QtCore.QThread): "To resolve this, please check and change the " "following properties and remove the qube {} " "manually.
").format( - self.vm.name, self.vm.name, self.vm.name) + list_text) + self.vm.name, self.vm.name, self.vm.name)\ + + list_text) except qubesadmin.exc.QubesException as ex: self.error = ("Rename error!", str(ex)) From 60af90ec5f66b50846e1fb992bb1c7b8c03370ff Mon Sep 17 00:00:00 2001 From: donoban Date: Mon, 22 Oct 2018 10:14:26 +0200 Subject: [PATCH 21/48] Adding # pylint: disable=too-few-public-methods --- qubesmanager/backup.py | 1 + qubesmanager/common_threads.py | 2 ++ qubesmanager/create_new_vm.py | 1 + qubesmanager/qube_manager.py | 3 +++ qubesmanager/restore.py | 1 + qubesmanager/settings.py | 3 ++- 6 files changed, 10 insertions(+), 1 deletion(-) diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index 542df22..8646fd9 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -45,6 +45,7 @@ import os import asyncio from contextlib import suppress +# pylint: disable=too-few-public-methods class BackupThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index 63bb051..a5dac25 100644 --- a/qubesmanager/common_threads.py +++ b/qubesmanager/common_threads.py @@ -24,6 +24,7 @@ from PyQt4 import QtCore # pylint: disable=import-error from qubesadmin import exc +# pylint: disable=too-few-public-methods class RemoveVMThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) @@ -37,6 +38,7 @@ class RemoveVMThread(QtCore.QThread): self.error = ("Error removing Qube!", str(ex)) +# pylint: disable=too-few-public-methods class CloneVMThread(QtCore.QThread): def __init__(self, src_vm, dst_name): QtCore.QThread.__init__(self) diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py index 88122c3..e81714b 100644 --- a/qubesmanager/create_new_vm.py +++ b/qubesmanager/create_new_vm.py @@ -34,6 +34,7 @@ from . import utils from .ui_newappvmdlg import Ui_NewVMDlg # pylint: disable=import-error +# pylint: disable=too-few-public-methods class CreateVMThread(QtCore.QThread): def __init__(self, app, vmclass, name, label, template, properties): QtCore.QThread.__init__(self) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index ae9322e..bf7e509 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -243,6 +243,7 @@ class VmShutdownMonitor(QtCore.QObject): self.restart_vm_if_needed() +# pylint: disable=too-few-public-methods class StartVMThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) @@ -256,6 +257,7 @@ class StartVMThread(QtCore.QThread): self.error = ("Error starting Qube!", str(ex)) +# pylint: disable=too-few-public-methods class UpdateVMThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) @@ -276,6 +278,7 @@ class UpdateVMThread(QtCore.QThread): self.error = ("Error on Qube update!", str(ex)) +# pylint: disable=too-few-public-methods class RunCommandThread(QtCore.QThread): def __init__(self, vm, command_to_run): QtCore.QThread.__init__(self) diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index 6cb46c7..8312b08 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -43,6 +43,7 @@ from qubesadmin import Qubes, exc from qubesadmin.backup import restore +# pylint: disable=too-few-public-methods class RestoreThread(QtCore.QThread): def __init__(self, backup_restore, vms_to_restore): QtCore.QThread.__init__(self) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 6df925a..e065714 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -46,7 +46,7 @@ from PyQt4 import QtCore, QtGui # pylint: disable=import-error from . import ui_settingsdlg # pylint: disable=no-name-in-module - +# pylint: disable=too-few-public-methods class RenameVMThread(QtCore.QThread): def __init__(self, vm, new_vm_name, dependencies): QtCore.QThread.__init__(self) @@ -92,6 +92,7 @@ class RenameVMThread(QtCore.QThread): self.error = ("Rename error!", repr(ex)) +# pylint: disable=too-few-public-methods class RefreshAppsVMThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) From edb13aec176c2a34817b8ddc487070314ffae35e Mon Sep 17 00:00:00 2001 From: donoban Date: Mon, 22 Oct 2018 20:00:37 +0200 Subject: [PATCH 22/48] Thread.error renamed to Thread.msg Since it could be used for both error or sucess messages, I think msg is a better name --- qubesmanager/common_threads.py | 10 +++++----- qubesmanager/create_new_vm.py | 12 ++++++------ qubesmanager/qube_manager.py | 16 ++++++++-------- qubesmanager/restore.py | 9 ++++----- qubesmanager/settings.py | 14 +++++++------- 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index a5dac25..980c05c 100644 --- a/qubesmanager/common_threads.py +++ b/qubesmanager/common_threads.py @@ -29,13 +29,13 @@ class RemoveVMThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) self.vm = vm - self.error = None + self.msg = None def run(self): try: del self.vm.app.domains[self.vm.name] except exc.QubesException as ex: - self.error = ("Error removing Qube!", str(ex)) + self.msg = ("Error removing Qube!", str(ex)) # pylint: disable=too-few-public-methods @@ -44,11 +44,11 @@ class CloneVMThread(QtCore.QThread): QtCore.QThread.__init__(self) self.src_vm = src_vm self.dst_name = dst_name - self.error = None + self.msg = None def run(self): try: self.src_vm.app.clone_vm(self.src_vm, self.dst_name) - self.error = ("Sucess", "The qube was cloned sucessfully.") + self.msg = ("Sucess", "The qube was cloned sucessfully.") except exc.QubesException as ex: - self.error = ("Error while cloning Qube!", str(ex)) + self.msg = ("Error while cloning Qube!", str(ex)) diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py index e81714b..9c8a2ec 100644 --- a/qubesmanager/create_new_vm.py +++ b/qubesmanager/create_new_vm.py @@ -44,7 +44,7 @@ class CreateVMThread(QtCore.QThread): self.label = label self.template = template self.properties = properties - self.error = None + self.msg = None def run(self): try: @@ -64,9 +64,9 @@ class CreateVMThread(QtCore.QThread): setattr(vm, k, v) except qubesadmin.exc.QubesException as qex: - self.error = str(qex) + self.msg = str(qex) except Exception as ex: # pylint: disable=broad-except - self.error = repr(ex) + self.msg = repr(ex) class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg): @@ -173,14 +173,14 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg): def create_finished(self): self.progress.hide() - if self.thread.error: + if self.thread.msg: QtGui.QMessageBox.warning(None, self.tr("Error creating the qube!"), - self.tr("ERROR: {}").format(self.thread.error)) + self.tr("ERROR: {}").format(self.thread.msg)) self.done(0) - if not self.thread.error: + if not self.thread.msg: if self.launch_settings.isChecked(): subprocess.check_call(['qubes-vm-settings', str(self.name)]) if self.install_system.isChecked(): diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index bf7e509..d2522eb 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -248,13 +248,13 @@ class StartVMThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) self.vm = vm - self.error = None + self.msg = None def run(self): try: self.vm.start() except exc.QubesException as ex: - self.error = ("Error starting Qube!", str(ex)) + self.msg = ("Error starting Qube!", str(ex)) # pylint: disable=too-few-public-methods @@ -262,7 +262,7 @@ class UpdateVMThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) self.vm = vm - self.error = None + self.msg = None def run(self): try: @@ -275,7 +275,7 @@ class UpdateVMThread(QtCore.QThread): self.vm.run_service("qubes.InstallUpdatesGUI",\ user="root", wait=False) except (ChildProcessError, exc.QubesException) as ex: - self.error = ("Error on Qube update!", str(ex)) + self.msg = ("Error on Qube update!", str(ex)) # pylint: disable=too-few-public-methods @@ -284,13 +284,13 @@ class RunCommandThread(QtCore.QThread): QtCore.QThread.__init__(self) self.vm = vm self.command_to_run = command_to_run - self.error = None + self.msg = None def run(self): try: self.vm.run(self.command_to_run) except (ChildProcessError, exc.QubesException) as ex: - self.error = ("Error while running command!", str(ex)) + self.msg = ("Error while running command!", str(ex)) class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): @@ -486,8 +486,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.progress.hide() self.progress = None - if thread.error: - (title, msg) = thread.error + if thread.msg: + (title, msg) = thread.msg QtGui.QMessageBox.warning( None, self.tr(title), diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index 8312b08..a060568 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -49,7 +49,6 @@ class RestoreThread(QtCore.QThread): QtCore.QThread.__init__(self) self.backup_restore = backup_restore self.vms_to_restore = vms_to_restore - self.error = None self.msg = None self.canceled = None @@ -67,7 +66,7 @@ class RestoreThread(QtCore.QThread): self.tr("Partially restored files left in /var/tmp/restore_*, " "investigate them and/or clean them up")) if err_msg: - self.error = '\n'.join(err_msg) + self.msg = '\n'.join(err_msg) self.msg = '{0}'.format( self.tr("Finished with errors!")) else: @@ -224,12 +223,12 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): self.progress_bar.setMaximum(100) self.progress_bar.setValue(100) - if self.thread.error: + if self.thread.msg: QtGui.QMessageBox.warning( None, - self.tr("Backup error!"), + self.tr("Backup msg!"), self.tr("ERROR: {0}").format( - self.thread.error)) + self.thread.msg)) if self.thread.msg: self.append_output(self.thread.msg) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index e065714..952916c 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -53,7 +53,7 @@ class RenameVMThread(QtCore.QThread): self.vm = vm self.new_vm_name = new_vm_name self.dependencies = dependencies - self.error = None + self.msg = None def run(self): try: @@ -87,9 +87,9 @@ class RenameVMThread(QtCore.QThread): + list_text) except qubesadmin.exc.QubesException as ex: - self.error = ("Rename error!", str(ex)) + self.msg = ("Rename error!", str(ex)) except Exception as ex: # pylint: disable=broad-except - self.error = ("Rename error!", repr(ex)) + self.msg = ("Rename error!", repr(ex)) # pylint: disable=too-few-public-methods @@ -97,7 +97,7 @@ class RefreshAppsVMThread(QtCore.QThread): def __init__(self, vm): QtCore.QThread.__init__(self) self.vm = vm - self.error = None + self.msg = None def run(self): try: @@ -118,7 +118,7 @@ class RefreshAppsVMThread(QtCore.QThread): target_vm.shutdown() except Exception as ex: # pylint: disable=broad-except - self.error = ("Refresh failed!", str(ex)) + self.msg = ("Refresh failed!", str(ex)) # pylint: disable=too-many-instance-attributes @@ -232,8 +232,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.progress.hide() self.progress = None - if thread.error: - (title, msg) = thread.error + if thread.msg: + (title, msg) = thread.msg QtGui.QMessageBox.warning( None, self.tr(title), From 0e951dc1563a5d411f9ab09259d56a22ca2baf06 Mon Sep 17 00:00:00 2001 From: donoban Date: Mon, 22 Oct 2018 20:03:20 +0200 Subject: [PATCH 23/48] Deleted unused method show_error() clear_threads() handles messages now --- qubesmanager/qube_manager.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index d2522eb..011f2ee 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -781,8 +781,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): # remove the VM thread = common_threads.RemoveVMThread(vm) self.threads_list.append(thread) - self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), - self.show_error) thread.finished.connect(self.clear_threads) thread.start() @@ -840,12 +838,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): thread.finished.connect(self.clear_threads) thread.start() - def show_error(self, error, error_msg): - QtGui.QMessageBox.warning( - None, - self.tr(error), - self.tr("ERROR: {0}").format(error_msg)) - # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_startvm_tools_install_triggered') # TODO: replace with boot from device From 9102d200bd30db2c74ebeb2dc2e44271a3a66408 Mon Sep 17 00:00:00 2001 From: donoban Date: Mon, 22 Oct 2018 23:47:51 +0200 Subject: [PATCH 24/48] restart wizzard when wrong password or backup corrupted fix: https://github.com/QubesOS/qubes-issues/issues/1589 --- qubesmanager/restore.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index a060568..120c012 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -171,6 +171,7 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): self.select_vms_widget.available_list.addItem(vmname) except exc.QubesException as ex: QtGui.QMessageBox.warning(None, self.tr("Restore error!"), str(ex)) + self.restart() def append_output(self, text): self.commit_text_edit.append(text) From e124d83edc33bee4a415125ead83f8c82d2509d3 Mon Sep 17 00:00:00 2001 From: donoban Date: Tue, 23 Oct 2018 00:07:55 +0200 Subject: [PATCH 25/48] Removed processEvents() calls --- qubesmanager/qube_manager.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 011f2ee..369086d 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -752,7 +752,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): "or setting that uses it.
").format(list_text)) info_dialog.setModal(False) info_dialog.show() - self.qt_app.processEvents() return @@ -869,8 +868,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): "running applications within this Qube.").format( vm.name), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) - self.qt_app.processEvents() - if reply == QtGui.QMessageBox.Yes: self.shutdown_vm(vm) @@ -904,8 +901,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): "applications within this Qube.").format(vm.name), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) - self.qt_app.processEvents() - if reply == QtGui.QMessageBox.Yes: # in case the user shut down the VM in the meantime if vm.is_running(): @@ -934,8 +929,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel) - self.qt_app.processEvents() - if reply == QtGui.QMessageBox.Yes: try: vm.kill() From 23e9ae8d33294fd1b41988a99c2a004cb7ee2e50 Mon Sep 17 00:00:00 2001 From: donoban Date: Wed, 24 Oct 2018 00:03:17 +0200 Subject: [PATCH 26/48] Remove loop since we have vm name --- qubesmanager/qube_manager.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 369086d..09b3d1b 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -511,21 +511,14 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): pass def on_domain_added(self, _submitter, _event, vm, **_kwargs): + domain = self.qubes_app.domains[vm] self.table.setSortingEnabled(False) - row_no = self.table.rowCount() self.table.setRowCount(row_no + 1) - - for domain in self.qubes_app.domains: - if domain == vm: - vm_row = VmRowInTable(domain, row_no, self.table) - self.vms_in_table[domain.qid] = vm_row - self.table.setSortingEnabled(True) - self.showhide_vms() - return - - # Never should reach here - raise RuntimeError('Added domain not found') + vm_row = VmRowInTable(domain, row_no, self.table) + self.vms_in_table[domain.qid] = vm_row + self.table.setSortingEnabled(True) + self.showhide_vms() def on_domain_removed(self, _submitter, _event, **kwargs): row_to_delete = None From 461cb7ae8c8940dd0678c7d38cbf12311b9e5866 Mon Sep 17 00:00:00 2001 From: donoban Date: Wed, 24 Oct 2018 09:45:29 +0200 Subject: [PATCH 27/48] Don't close main about dialog when close some child dialog --- qubesmanager/about.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qubesmanager/about.py b/qubesmanager/about.py index b12ae36..9ac818f 100644 --- a/qubesmanager/about.py +++ b/qubesmanager/about.py @@ -47,9 +47,7 @@ class AboutDialog(ui_about.Ui_AboutDialog, QDialog): def on_release_notes_clicked(self): release_notes_dialog = ReleaseNotesDialog() release_notes_dialog.exec_() - self.accept() def on_information_notes_clicked(self): information_notes_dialog = InformationNotesDialog() information_notes_dialog.exec_() - self.accept() From 531b9daad298d46eabacf07eadc3df340256ac4b Mon Sep 17 00:00:00 2001 From: donoban Date: Wed, 24 Oct 2018 09:46:16 +0200 Subject: [PATCH 28/48] Signal rewrite to new style old is not supported in pyqt5 --- qubesmanager/about.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/qubesmanager/about.py b/qubesmanager/about.py index 9ac818f..5e0a348 100644 --- a/qubesmanager/about.py +++ b/qubesmanager/about.py @@ -38,11 +38,9 @@ class AboutDialog(ui_about.Ui_AboutDialog, QDialog): with open('/etc/qubes-release', 'r') as release_file: self.release.setText(release_file.read()) - self.connect(self.ok, SIGNAL("clicked()"), SLOT("accept()")) - self.connect(self.releaseNotes, SIGNAL("clicked()"), - self.on_release_notes_clicked) - self.connect(self.informationNotes, SIGNAL("clicked()"), - self.on_information_notes_clicked) + self.ok.clicked.connect(self.accept) + self.releaseNotes.clicked.connect(self.on_release_notes_clicked) + self.informationNotes.clicked.connect(self.on_information_notes_clicked) def on_release_notes_clicked(self): release_notes_dialog = ReleaseNotesDialog() From 971ec085eedc791961edb12e8543883309d1ca56 Mon Sep 17 00:00:00 2001 From: donoban Date: Wed, 24 Oct 2018 10:05:43 +0200 Subject: [PATCH 29/48] Workaround for backup dialog modeless behaviour Since now it's modeless, user could have already it opened but we have to destroy it and recreate when closed for vm list being refreshed (at least for now) --- qubesmanager/qube_manager.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 09b3d1b..bee3760 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -336,6 +336,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.sort_by_column = "Type" self.sort_order = QtCore.Qt.AscendingOrder + self.backup_window = None + self.vms_list = [] self.vms_in_table = {} @@ -1047,9 +1049,17 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_backup_triggered') def action_backup_triggered(self): - backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app, - self.dispatcher) - backup_window.show() + if not self.backup_window: + self.backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app, + self.dispatcher, self) + self.backup_window.finished.connect(self._backup_finished) + + self.backup_window.show() + self.backup_window.activateWindow() + + def _backup_finished(self): + self.backup_window.deleteLater() + self.backup_window = None # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_exit_triggered') From 042ff808b309f77d12f9219a1b9110dbd977d41a Mon Sep 17 00:00:00 2001 From: donoban Date: Wed, 24 Oct 2018 10:14:48 +0200 Subject: [PATCH 30/48] Removed unused import --- qubesmanager/about.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qubesmanager/about.py b/qubesmanager/about.py index 5e0a348..5b8a915 100644 --- a/qubesmanager/about.py +++ b/qubesmanager/about.py @@ -20,7 +20,6 @@ # with this program; if not, see . # # -from PyQt4.QtCore import SIGNAL, SLOT # pylint: disable=import-error from PyQt4.QtGui import QDialog, QIcon # pylint: disable=import-error from qubesmanager.releasenotes import ReleaseNotesDialog from qubesmanager.informationnotes import InformationNotesDialog From fd1e5d740a8364908ca00135603761314144b410 Mon Sep 17 00:00:00 2001 From: donoban Date: Wed, 24 Oct 2018 10:58:36 +0200 Subject: [PATCH 31/48] Fix travis errors --- qubesmanager/about.py | 16 ++++++++-------- qubesmanager/qube_manager.py | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/qubesmanager/about.py b/qubesmanager/about.py index 5b8a915..6b2abb2 100644 --- a/qubesmanager/about.py +++ b/qubesmanager/about.py @@ -38,13 +38,13 @@ class AboutDialog(ui_about.Ui_AboutDialog, QDialog): self.release.setText(release_file.read()) self.ok.clicked.connect(self.accept) - self.releaseNotes.clicked.connect(self.on_release_notes_clicked) - self.informationNotes.clicked.connect(self.on_information_notes_clicked) + self.releaseNotes.clicked.connect(on_release_notes_clicked) + self.informationNotes.clicked.connect(on_information_notes_clicked) - def on_release_notes_clicked(self): - release_notes_dialog = ReleaseNotesDialog() - release_notes_dialog.exec_() +def on_release_notes_clicked(): + release_notes_dialog = ReleaseNotesDialog() + release_notes_dialog.exec_() - def on_information_notes_clicked(self): - information_notes_dialog = InformationNotesDialog() - information_notes_dialog.exec_() +def on_information_notes_clicked(): + information_notes_dialog = InformationNotesDialog() + information_notes_dialog.exec_() diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index bee3760..bf5dea4 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -1050,8 +1050,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): @QtCore.pyqtSlot(name='on_action_backup_triggered') def action_backup_triggered(self): if not self.backup_window: - self.backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app, - self.dispatcher, self) + self.backup_window = backup.BackupVMsWindow(self.qt_app, + self.qubes_app, self.dispatcher, self) self.backup_window.finished.connect(self._backup_finished) self.backup_window.show() From 7f572b5c8158c38c4ab40ca069c8425dd0088128 Mon Sep 17 00:00:00 2001 From: donoban Date: Wed, 24 Oct 2018 11:27:47 +0200 Subject: [PATCH 32/48] Add pylint disable too-few-methods --- qubesmanager/about.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qubesmanager/about.py b/qubesmanager/about.py index 6b2abb2..da54508 100644 --- a/qubesmanager/about.py +++ b/qubesmanager/about.py @@ -27,6 +27,7 @@ from qubesmanager.informationnotes import InformationNotesDialog from . import ui_about # pylint: disable=no-name-in-module +# pylint: disable=too-few-public-methods class AboutDialog(ui_about.Ui_AboutDialog, QDialog): def __init__(self): super(AboutDialog, self).__init__() From 6045f451c68f05f893669279d5105a98e72d50fc Mon Sep 17 00:00:00 2001 From: donoban Date: Thu, 25 Oct 2018 09:45:12 +0200 Subject: [PATCH 33/48] Fix opening settings/boot dialog after VM creation --- qubesmanager/create_new_vm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py index 9c8a2ec..7b968a6 100644 --- a/qubesmanager/create_new_vm.py +++ b/qubesmanager/create_new_vm.py @@ -182,10 +182,10 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg): if not self.thread.msg: if self.launch_settings.isChecked(): - subprocess.check_call(['qubes-vm-settings', str(self.name)]) + subprocess.check_call(['qubes-vm-settings', str(self.name.text())]) if self.install_system.isChecked(): subprocess.check_call( - ['qubes-vm-boot-from-device', str(self.name)]) + ['qubes-vm-boot-from-device', str(self.name.text())]) def type_change(self): From 988b7a647914efe6d8842344c634989326585caa Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 27 Oct 2018 12:58:58 +0200 Subject: [PATCH 34/48] Fix error/success message on dialog --- qubesmanager/restore.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index 120c012..c5d21af 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -227,9 +227,8 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): if self.thread.msg: QtGui.QMessageBox.warning( None, - self.tr("Backup msg!"), - self.tr("ERROR: {0}").format( - self.thread.msg)) + self.tr("Restore qubes"), + self.tr(self.thread.msg) if self.thread.msg: self.append_output(self.thread.msg) From c42203098f73b86bcbf49ca601787ee43d4e3d09 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 27 Oct 2018 13:00:19 +0200 Subject: [PATCH 35/48] Do not terminate the thread self.back_restore.canceled will do the abort --- qubesmanager/restore.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index c5d21af..f61068e 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -275,8 +275,6 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): self.append_output('{0}'.format( self.tr("Aborting the operation..."))) self.button(self.CancelButton).setDisabled(True) - - self.thread.terminate() else: self.done(0) From 5c8f19b5983e3b022d4d751c109de00d61ffd4a8 Mon Sep 17 00:00:00 2001 From: donoban Date: Sat, 27 Oct 2018 13:21:33 +0200 Subject: [PATCH 36/48] Fix pylint --- qubesmanager/create_new_vm.py | 3 ++- qubesmanager/restore.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py index 7b968a6..5949833 100644 --- a/qubesmanager/create_new_vm.py +++ b/qubesmanager/create_new_vm.py @@ -182,7 +182,8 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg): if not self.thread.msg: if self.launch_settings.isChecked(): - subprocess.check_call(['qubes-vm-settings', str(self.name.text())]) + subprocess.check_call(['qubes-vm-settings', + str(self.name.text())]) if self.install_system.isChecked(): subprocess.check_call( ['qubes-vm-boot-from-device', str(self.name.text())]) diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index f61068e..10cfda8 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -228,7 +228,7 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): QtGui.QMessageBox.warning( None, self.tr("Restore qubes"), - self.tr(self.thread.msg) + self.tr(self.thread.msg)) if self.thread.msg: self.append_output(self.thread.msg) From cbc5d364bfb1e4ce24d43db83bb1031b58fbc6f6 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 4 Nov 2018 18:19:16 +0100 Subject: [PATCH 37/48] Removed sigchld_handler old code --- qubesmanager/restore.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index 10cfda8..0c1d07c 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -29,8 +29,6 @@ import traceback import logging import logging.handlers -import signal - from qubes import backup from . import ui_restoredlg # pylint: disable=no-name-in-module @@ -85,7 +83,6 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): self.func_output = [] self.thread = None - self.old_sigchld_handler = None # Set up logging self.feedback_queue = Queue() @@ -177,7 +174,6 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): self.commit_text_edit.append(text) def current_page_changed(self, page_id): # pylint: disable=unused-argument - self.old_sigchld_handler = signal.signal(signal.SIGCHLD, signal.SIG_DFL) if self.currentPage() is self.select_vms_page: self.__fill_vms_list__() @@ -243,7 +239,6 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): self.button(self.FinishButton).setEnabled(True) self.button(self.CancelButton).setEnabled(False) self.showFileDialog.setEnabled(False) - signal.signal(signal.SIGCHLD, self.old_sigchld_handler) def update_log(self): try: From a39f3a64d0ae8b3112e72e4d2b778aa91bff1b87 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 18 Nov 2018 20:14:57 +0100 Subject: [PATCH 38/48] Create backup_window on the stack Otherwise it can casue an error: RuntimeError: wrapped C/C++ object of type QProgressBar has been deleted at line 1277 of file /usr/lib/python3.5/site-packages/qubesmanager/qube_manager.py. I don't know how to avoid it. --- qubesmanager/qube_manager.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index bf5dea4..8f1b1fe 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -336,8 +336,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.sort_by_column = "Type" self.sort_order = QtCore.Qt.AscendingOrder - self.backup_window = None - self.vms_list = [] self.vms_in_table = {} @@ -1049,17 +1047,9 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_backup_triggered') def action_backup_triggered(self): - if not self.backup_window: - self.backup_window = backup.BackupVMsWindow(self.qt_app, - self.qubes_app, self.dispatcher, self) - self.backup_window.finished.connect(self._backup_finished) - - self.backup_window.show() - self.backup_window.activateWindow() - - def _backup_finished(self): - self.backup_window.deleteLater() - self.backup_window = None + backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app, + self.dispatcher, self) + backup_window.show() # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_exit_triggered') From 935a6ca6c756f495db3d6c902ca3e5c437f8d1ec Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 18 Nov 2018 20:22:27 +0100 Subject: [PATCH 39/48] Fix authorship and python version --- qubesmanager/common_threads.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index 980c05c..4c17c1e 100644 --- a/qubesmanager/common_threads.py +++ b/qubesmanager/common_threads.py @@ -1,8 +1,8 @@ -#!/usr/bin/python2 +#!/usr/bin/python3 # # The Qubes OS Project, http://www.qubes-os.org # -# Copyright (C) 2011 Marek Marczykowski +# Copyright (C) 2018 Donoban # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License From 287fc441f31283de13afab58b5e2a130f5befb01 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 18 Nov 2018 20:23:26 +0100 Subject: [PATCH 40/48] Catch possible KeyError when starting dispVM --- qubesmanager/common_threads.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index 4c17c1e..52f19f5 100644 --- a/qubesmanager/common_threads.py +++ b/qubesmanager/common_threads.py @@ -34,8 +34,8 @@ class RemoveVMThread(QtCore.QThread): def run(self): try: del self.vm.app.domains[self.vm.name] - except exc.QubesException as ex: - self.msg = ("Error removing Qube!", str(ex)) + except (exc.QubesException, KeyError) as ex: + self.msg = ("Error removing qube!", str(ex)) # pylint: disable=too-few-public-methods From 9a2975616df43423b0e4ab9b375ab17d50b619c0 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 18 Nov 2018 20:25:41 +0100 Subject: [PATCH 41/48] Qube -> qube --- qubesmanager/common_threads.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index 52f19f5..4f35539 100644 --- a/qubesmanager/common_threads.py +++ b/qubesmanager/common_threads.py @@ -51,4 +51,4 @@ class CloneVMThread(QtCore.QThread): self.src_vm.app.clone_vm(self.src_vm, self.dst_name) self.msg = ("Sucess", "The qube was cloned sucessfully.") except exc.QubesException as ex: - self.msg = ("Error while cloning Qube!", str(ex)) + self.msg = ("Error while cloning qube!", str(ex)) From 0ce73df57df94b585e4ed7a4135b2a9e95b9a359 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 25 Nov 2018 18:16:40 +0100 Subject: [PATCH 42/48] Use 'qube' instead 'Qube' --- qubesmanager/qube_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 8f1b1fe..9570fa4 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -275,7 +275,7 @@ class UpdateVMThread(QtCore.QThread): self.vm.run_service("qubes.InstallUpdatesGUI",\ user="root", wait=False) except (ChildProcessError, exc.QubesException) as ex: - self.msg = ("Error on Qube update!", str(ex)) + self.msg = ("Error on qube update!", str(ex)) # pylint: disable=too-few-public-methods From 22a9823871a8ea17880a584a83be57e44da05bb6 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 25 Nov 2018 18:37:34 +0100 Subject: [PATCH 43/48] Fix iterating and removing over list Return after removing and raise exception if removable item not found --- qubesmanager/qube_manager.py | 3 +++ qubesmanager/settings.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 9570fa4..48a3532 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -494,6 +494,9 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.tr(msg)) self.threads_list.remove(thread) + return + + raise RuntimeError('No finished thread found') def closeEvent(self, event): # pylint: disable=invalid-name diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 952916c..ffa5fe3 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -240,10 +240,13 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.tr(msg)) self.threads_list.remove(thread) + return if self.thread_closes: self.done(0) + raise RuntimeError('No finished thread found') + def keyPressEvent(self, event): # pylint: disable=invalid-name if event.key() == QtCore.Qt.Key_Enter \ or event.key() == QtCore.Qt.Key_Return: From f477084bfeaf28c5f7b320b37cb4ee3610d3ad8e Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 25 Nov 2018 18:49:02 +0100 Subject: [PATCH 44/48] Don't terminate backup thread when aborting --- qubesmanager/backup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index 8646fd9..2549488 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -363,7 +363,6 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): def reject(self): if self.currentPage() is self.commit_page: - self.thread.terminate() self.qubes_app.qubesd_call( 'dom0', 'admin.backup.Cancel', backup_utils.get_profile_name(True)) From 6fdf16278e4bae622a44a71408e6fe496ec76d5a Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 25 Nov 2018 18:53:31 +0100 Subject: [PATCH 45/48] wait thread to finish when aborting --- qubesmanager/backup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index 2549488..358f1be 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -366,6 +366,7 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): self.qubes_app.qubesd_call( 'dom0', 'admin.backup.Cancel', backup_utils.get_profile_name(True)) + self.thread.wait() QtGui.QMessageBox.warning( self, self.tr("Backup aborted!"), self.tr("ERROR: {}").format("Aborted!")) From 4deaedd1e0383519c9847aec7b3af9cfbee5bd7d Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 25 Nov 2018 19:15:56 +0100 Subject: [PATCH 46/48] Fix errors when some domain fails to start --- qubesmanager/qube_manager.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 48a3532..d031840 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -514,12 +514,17 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): pass def on_domain_added(self, _submitter, _event, vm, **_kwargs): - domain = self.qubes_app.domains[vm] + row_no = 0 self.table.setSortingEnabled(False) - row_no = self.table.rowCount() - self.table.setRowCount(row_no + 1) - vm_row = VmRowInTable(domain, row_no, self.table) - self.vms_in_table[domain.qid] = vm_row + try: + domain = self.qubes_app.domains[vm] + row_no = self.table.rowCount() + self.table.setRowCount(row_no + 1) + vm_row = VmRowInTable(domain, row_no, self.table) + self.vms_in_table[domain.qid] = vm_row + except (exc.QubesException, KeyError): + if row_no != 0: + self.table.removeRow(row_no) self.table.setSortingEnabled(True) self.showhide_vms() From b2bdae2c2d48f28e102c84826800996ff4f02ed2 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 25 Nov 2018 19:28:07 +0100 Subject: [PATCH 47/48] Clear searchbox pressing esc without need of selecting it --- qubesmanager/qube_manager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index d031840..2d2221f 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -70,11 +70,6 @@ class SearchBox(QtGui.QLineEdit): self.selectAll() self.focusing = False - def keyPressEvent(self, event): # pylint: disable=invalid-name - if event.key() == QtCore.Qt.Key_Escape: - self.clear() - super(SearchBox, self).keyPressEvent(event) - class VmRowInTable: # pylint: disable=too-few-public-methods @@ -479,6 +474,11 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): timer.start(1000 * 30) # 30s self.check_updates() + def keyPressEvent(self, event): # pylint: disable=invalid-name + if event.key() == QtCore.Qt.Key_Escape: + self.searchbox.clear() + super(VmManagerWindow, self).keyPressEvent(event) + def clear_threads(self): for thread in self.threads_list: if thread.isFinished(): From fd845a121957b1b99c4ff44ba3b78ad9936da6b0 Mon Sep 17 00:00:00 2001 From: donoban Date: Sun, 25 Nov 2018 19:33:16 +0100 Subject: [PATCH 48/48] Fixed unreachable code thanks to travis --- qubesmanager/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index ffa5fe3..40d2370 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -240,11 +240,12 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.tr(msg)) self.threads_list.remove(thread) - return if self.thread_closes: self.done(0) + return + raise RuntimeError('No finished thread found') def keyPressEvent(self, event): # pylint: disable=invalid-name