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_()