diff --git a/backupdlg.ui b/backupdlg.ui index f26c0af..955b8fc 100644 --- a/backupdlg.ui +++ b/backupdlg.ui @@ -22,20 +22,80 @@ - - - - 9 - 50 - false - false - false - - - - Select VMs to backup: - - + + + + + + 0 + 0 + + + + Shutdown all running selected VMs + + + + :/shutdownvm.png:/shutdownvm.png + + + + + + + + 0 + 0 + + + + Refresh running states. + + + + + + + + 0 + 0 + + + + + 75 + true + true + + + + color:rgb(255, 0, 0) + + + Some of the selected VMs are running (red). Running VMs can not be backuped! + + + true + + + + + + + + 9 + 50 + false + false + false + + + + Select VMs to backup: + + + + @@ -187,6 +247,8 @@ p, li { white-space: pre-wrap; } - + + + diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index 47efca4..7991f6f 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -54,12 +54,13 @@ class BackupVMsWindow(Ui_Backup, QWizard): __pyqtSignals__ = ("backup_progress(int)",) - def __init__(self, app, qvm_collection, blk_manager, parent=None): + def __init__(self, app, qvm_collection, blk_manager, shutdown_vm_func, parent=None): super(BackupVMsWindow, self).__init__(parent) self.app = app self.qvm_collection = qvm_collection self.blk_manager = blk_manager + self.shutdown_vm_func = shutdown_vm_func self.dev_mount_path = None self.backup_dir = None @@ -75,12 +76,16 @@ class BackupVMsWindow(Ui_Backup, QWizard): self.setupUi(self) + self.show_running_vms_warning(False) self.dir_line_edit.setReadOnly(True) self.select_vms_widget = MultiSelectWidget(self) self.verticalLayout.insertWidget(1, self.select_vms_widget) self.connect(self, SIGNAL("currentIdChanged(int)"), self.current_page_changed) + self.connect(self.select_vms_widget, SIGNAL("selected_changed()"), self.check_running) + self.refresh_button.clicked.connect(self.check_running) + self.shutdown_running_vms_button.clicked.connect(self.shutdown_all_running_selected) self.connect(self.dev_combobox, SIGNAL("activated(int)"), self.dev_combobox_activated) self.connect(self, SIGNAL("backup_progress(int)"), self.progress_bar.setValue) @@ -93,26 +98,92 @@ class BackupVMsWindow(Ui_Backup, QWizard): self.__fill_vms_list__() fill_devs_list(self) + def show_running_vms_warning(self, show): + self.running_vms_warning.setVisible(show) + self.shutdown_running_vms_button.setVisible(show) + self.refresh_button.setVisible(show) + + class VmListItem(QListWidgetItem): + def __init__(self, vm): + super(BackupVMsWindow.VmListItem, self).__init__(vm.name) + self.vm = vm + def __fill_vms_list__(self): for vm in self.qvm_collection.values(): - if vm.is_running() and vm.qid != 0: - self.excluded.append(vm.name) - continue - if vm.is_appvm() and vm.internal: - self.excluded.append(vm.name) continue - if vm.is_template() and vm.installed_by_rpm: - self.excluded.append(vm.name) continue + item = BackupVMsWindow.VmListItem(vm) if vm.include_in_backups == True: - self.select_vms_widget.selected_list.addItem(vm.name) + self.select_vms_widget.selected_list.addItem(item) else: - self.select_vms_widget.available_list.addItem(vm.name) + self.select_vms_widget.available_list.addItem(item) + self.check_running() + + def check_running(self): + some_selected_vms_running = False + for i in range(self.select_vms_widget.selected_list.count()): + item = self.select_vms_widget.selected_list.item(i) + if item.vm.is_running() and item.vm.qid != 0: + item.setForeground(QBrush(QColor(255, 0, 0))) + some_selected_vms_running = True + else: + item.setForeground(QBrush(QColor(0, 0, 0))) + + self.show_running_vms_warning(some_selected_vms_running) + + for i in range(self.select_vms_widget.available_list.count()): + item = self.select_vms_widget.available_list.item(i) + if item.vm.is_running() and item.vm.qid != 0: + item.setForeground(QBrush(QColor(255, 0, 0))) + else: + item.setForeground(QBrush(QColor(0, 0, 0))) + + return some_selected_vms_running + + def shutdown_all_running_selected(self): + names = [] + vms = [] + for i in range(self.select_vms_widget.selected_list.count()): + item = self.select_vms_widget.selected_list.item(i) + if item.vm.is_running() and item.vm.qid != 0: + names.append(item.vm.name) + vms.append(item.vm) + + if len(vms) == 0: + return; + + reply = QMessageBox.question(None, "VM Shutdown Confirmation", + "Are you sure you want to power down the following VMs: {0}?
" + "This will shutdown all the running applications within them.".format(', '.join(names)), + QMessageBox.Yes | QMessageBox.Cancel) + + self.app.processEvents() + + if reply == QMessageBox.Yes: + for vm in vms: + self.shutdown_vm_func(vm) + + progress = QProgressDialog ("Shutting down VMs {0}...".format(', '.join(names)), "", 0, 0) + progress.setModal(True) + progress.show() + + wait_time = 15.0 + wait_for = wait_time + while self.check_running() and wait_for > 0: + self.app.processEvents() + time.sleep (0.1) + wait_for -= 0.1 + + progress.hide() + + if self.check_running(): + QMessageBox.information(None, "Strange", "Could not power down the VMs in {0} seconds...".format(wait_time)) + + - def dev_combobox_activated(self, idx): dev_combobox_activated(self, idx) @@ -123,9 +194,16 @@ class BackupVMsWindow(Ui_Backup, QWizard): def validateCurrentPage(self): if self.currentPage() is self.select_vms_page: + for i in range(self.select_vms_widget.selected_list.count()): + if self.check_running() == True: + QMessageBox.information(None, "Wait!", "Some selected VMs are running. Running VMs can not be backuped. Please shut them down or remove them from the list.") + return False + + del self.excluded[:] for i in range(self.select_vms_widget.available_list.count()): - vmname = self.select_vms_widget.available_list.item(i).text() + vmname = str(self.select_vms_widget.available_list.item(i).text()) self.excluded.append(vmname) + return True def gather_output(self, s): @@ -152,7 +230,10 @@ class BackupVMsWindow(Ui_Backup, QWizard): def current_page_changed(self, id): if self.currentPage() is self.confirm_page: del self.func_output[:] - self.files_to_backup = qubesutils.backup_prepare(str(self.backup_dir), exclude_list = self.excluded, print_callback = self.gather_output) + try: + self.files_to_backup = qubesutils.backup_prepare(str(self.backup_dir), exclude_list = self.excluded, print_callback = self.gather_output) + except Exception as ex: + QMessageBox.critical(None, "Error while prepering backup.", "ERROR: {0}".format(ex)) self.textEdit.setReadOnly(True) self.textEdit.setFontFamily("Monospace") @@ -213,8 +294,6 @@ def handle_exception( exc_type, exc_value, exc_traceback ): % ( line, filename )) - - def main(): global qubes_host diff --git a/qubesmanager/main.py b/qubesmanager/main.py index 9b46970..49ff193 100755 --- a/qubesmanager/main.py +++ b/qubesmanager/main.py @@ -1142,16 +1142,22 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): app.processEvents() if reply == QMessageBox.Yes: - try: - subprocess.check_call (["/usr/sbin/xl", "shutdown", vm.name]) - except Exception as ex: - QMessageBox.warning (None, "Error shutting down VM!", "ERROR: {0}".format(ex)) - return + self.shutdown_vm(vm) + + + def shutdown_vm(self, vm): + try: + subprocess.check_call (["/usr/sbin/xl", "shutdown", vm.name]) + except Exception as ex: + QMessageBox.warning (None, "Error shutting down VM!", "ERROR: {0}".format(ex)) + return + + trayIcon.showMessage ("Qubes Manager", "VM '{0}' is shutting down...".format(vm.name), msecs=3000) + + self.shutdown_monitor[vm.qid] = VmShutdownMonitor (vm) + QTimer.singleShot (vm_shutdown_timeout, self.shutdown_monitor[vm.qid].check_if_vm_has_shutdown) - trayIcon.showMessage ("Qubes Manager", "VM '{0}' is shutting down...".format(vm.name), msecs=3000) - self.shutdown_monitor[vm.qid] = VmShutdownMonitor (vm) - QTimer.singleShot (vm_shutdown_timeout, self.shutdown_monitor[vm.qid].check_if_vm_has_shutdown) @pyqtSlot(name='on_action_settings_triggered') def action_settings_triggered(self): @@ -1235,7 +1241,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): @pyqtSlot(name='on_action_backup_triggered') def action_backup_triggered(self): - backup_window = BackupVMsWindow(app, self.qvm_collection, self.blk_manager) + backup_window = BackupVMsWindow(app, self.qvm_collection, self.blk_manager, self.shutdown_vm) backup_window.exec_()