diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py
index e9e61df..bde0775 100644
--- a/qubesmanager/qube_manager.py
+++ b/qubesmanager/qube_manager.py
@@ -493,17 +493,15 @@ class QubesTableModel(QAbstractTableModel):
return def_flags | Qt.ItemIsUserCheckable
return def_flags
-vm_shutdown_timeout = 20000 # in msec
vm_restart_check_timeout = 1000 # in msec
class VmShutdownMonitor(QObject):
- def __init__(self, vm, shutdown_time=vm_shutdown_timeout,
- check_time=vm_restart_check_timeout,
+ def __init__(self, vm, check_time=vm_restart_check_timeout,
and_restart=False, caller=None):
QObject.__init__(self)
self.vm = vm
- self.shutdown_time = shutdown_time
+ self.shutdown_timeout = vm.shutdown_timeout
self.check_time = check_time
self.and_restart = and_restart
self.shutdown_started = datetime.now()
@@ -519,7 +517,7 @@ class VmShutdownMonitor(QObject):
def timeout_reached(self):
actual = datetime.now() - self.shutdown_started
- allowed = timedelta(milliseconds=self.shutdown_time)
+ allowed = timedelta(seconds=self.shutdown_timeout)
return actual > allowed
@@ -541,12 +539,12 @@ class VmShutdownMonitor(QObject):
msgbox.setText(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_timeout))
kill_button = msgbox.addButton(
self.tr("Kill it!"), QMessageBox.YesRole)
wait_button = msgbox.addButton(
self.tr("Wait another {0} seconds...").format(
- self.shutdown_time / 1000),
+ self.shutdown_timeout),
QMessageBox.NoRole)
ignore_button = msgbox.addButton(self.tr("Don't ask again"),
QMessageBox.RejectRole)
@@ -668,7 +666,7 @@ class QubesProxyModel(QSortFilterProxyModel):
vm = self.sourceModel().data(index, Qt.UserRole)
if self.window.show_running.isChecked() and \
- vm.state['power'] == 'Running':
+ vm.state['power'] != 'Halted':
return super().filterAcceptsRow(sourceRow, sourceParent)
if self.window.show_halted.isChecked() and \
vm.state['power'] == 'Halted':
@@ -1407,24 +1405,52 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
if reply == QMessageBox.Yes:
self.shutdown_vm(vm)
- def shutdown_vm(self, vm, shutdown_time=vm_shutdown_timeout,
- check_time=vm_restart_check_timeout, and_restart=False):
+ def get_connected_vms(self, vm, connected_vms):
+ for connected_vm in vm.connected_vms:
+ if connected_vm.is_running():
+ connected_vms.append(connected_vm)
+ self.get_connected_vms(connected_vm, connected_vms)
+
+ def shutdown_vm(self, vm, force=False, check_time=vm_restart_check_timeout,
+ and_restart=False):
try:
- vm.shutdown()
+ connected_vms = []
+
+ if not and_restart:
+ self.get_connected_vms(vm, connected_vms)
+
+ if len(connected_vms) > 0:
+ reply = QMessageBox.question(
+ self, self.tr("Qube Shutdown Confirmation"),
+ self.tr("There are some qubes connected to '{0}'!"
+ "
Do you want to shutdown: "
+ "'{1}'?").format(vm.name,
+ ", ".join([x.name for x in connected_vms])),
+ QMessageBox.Yes | QMessageBox.Cancel)
+
+ if reply != QMessageBox.Yes:
+ return False
+
+ force = True
+ for connected_vm in connected_vms:
+ connected_vm.shutdown(force=force)
+
+ vm.shutdown(force=force)
except exc.QubesException as ex:
QMessageBox.warning(
self,
self.tr("Error shutting down Qube!"),
self.tr("ERROR: {0}").format(ex))
- return
+ return False
- self.shutdown_monitor[vm.qid] = VmShutdownMonitor(vm, shutdown_time,
- check_time,
+ self.shutdown_monitor[vm.qid] = VmShutdownMonitor(vm, check_time,
and_restart, self)
# noinspection PyCallByClass,PyTypeChecker
QTimer.singleShot(check_time, self.shutdown_monitor[
vm.qid].check_if_vm_has_shutdown)
+ return True
+
# noinspection PyArgumentList
@pyqtSlot(name='on_action_restartvm_triggered')
def action_restartvm_triggered(self):
@@ -1441,7 +1467,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
# in case the user shut down the VM in the meantime
try:
if manager_utils.is_running(vm, False):
- self.shutdown_vm(vm, and_restart=True)
+ self.shutdown_vm(vm, force=True, and_restart=True)
else:
self.start_vm(vm)
except exc.QubesException as ex:
diff --git a/qubesmanager/tests/test_qube_manager.py b/qubesmanager/tests/test_qube_manager.py
index 6efe283..d126535 100644
--- a/qubesmanager/tests/test_qube_manager.py
+++ b/qubesmanager/tests/test_qube_manager.py
@@ -457,11 +457,10 @@ class QubeManagerTest(unittest.TestCase):
with unittest.mock.patch.object(selected_vm, 'shutdown')\
as mock_shutdown:
self.dialog.action_shutdownvm.trigger()
- mock_shutdown.assert_called_once_with()
+ mock_shutdown.assert_called_once_with(force=False)
mock_monitor.assert_called_once_with(
- selected_vm,
- unittest.mock.ANY, unittest.mock.ANY,
- unittest.mock.ANY, unittest.mock.ANY)
+ selected_vm, unittest.mock.ANY, unittest.mock.ANY,
+ unittest.mock.ANY)
mock_timer.assert_called_once_with(unittest.mock.ANY,
unittest.mock.ANY)
@@ -546,10 +545,9 @@ class QubeManagerTest(unittest.TestCase):
with unittest.mock.patch.object(selected_vm, 'shutdown')\
as mock_shutdown:
action.trigger()
- mock_shutdown.assert_called_once_with()
+ mock_shutdown.assert_called_once_with(force=True)
mock_monitor.assert_called_once_with(
- selected_vm, unittest.mock.ANY,
- unittest.mock.ANY, True, unittest.mock.ANY)
+ selected_vm, 1000, True, unittest.mock.ANY)
@unittest.mock.patch('qubesmanager.qube_manager.StartVMThread')
@unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
@@ -1484,7 +1482,7 @@ class QubeManagerTest(unittest.TestCase):
for row in range(self.dialog.table.model().rowCount()):
template = self._get_table_item(row, "Template")
vm = self._get_table_vm(row)
- if template != 'AdminVM' and \
+ if template != 'AdminVM' and not vm.provides_network and \
(running is None
or (running and vm.is_running())
or (not running and not vm.is_running())):
@@ -1691,8 +1689,9 @@ class VMShutdownMonitorTest(unittest.TestCase):
mock_vm = unittest.mock.Mock()
mock_vm.is_running.return_value = True
mock_vm.start_time = datetime.datetime.now().timestamp() - 3000
+ mock_vm.shutdown_timeout = 60
- monitor = qube_manager.VmShutdownMonitor(mock_vm, shutdown_time=1)
+ monitor = qube_manager.VmShutdownMonitor(mock_vm)
time.sleep(3)
monitor.check_if_vm_has_shutdown()
@@ -1708,8 +1707,9 @@ class VMShutdownMonitorTest(unittest.TestCase):
mock_vm = unittest.mock.Mock()
mock_vm.is_running.return_value = True
mock_vm.start_time = datetime.datetime.now().timestamp() - 3000
+ mock_vm.shutdown_timeout = 1
- monitor = qube_manager.VmShutdownMonitor(mock_vm, shutdown_time=1)
+ monitor = qube_manager.VmShutdownMonitor(mock_vm)
time.sleep(3)
monitor.restart_vm_if_needed = unittest.mock.Mock()
@@ -1725,8 +1725,9 @@ class VMShutdownMonitorTest(unittest.TestCase):
mock_vm = unittest.mock.Mock()
mock_vm.is_running.return_value = True
mock_vm.start_time = datetime.datetime.now().timestamp() - 3000
+ mock_vm.shutdown_timeout = 30
- monitor = qube_manager.VmShutdownMonitor(mock_vm, shutdown_time=3000)
+ monitor = qube_manager.VmShutdownMonitor(mock_vm)
time.sleep(1)
monitor.check_if_vm_has_shutdown()