Merge remote-tracking branch 'origin/pr/281'

* origin/pr/281:
  Tests suite fixes
  Use internal vm.shutdown_timeout
  Fix pylint warning
  Show all non halted vm's as running
  use force=True when restarting a netvm with connected vms
  Join all connected VM's in same warning
  Rewrite without cascade var
  Don't wait!
  First initiate shutdown for all vm's, then wait
  Fix wrong identation (thanks pylint)
  Avoid infite loop while waiting vm to shutdown
  Fix pylint warnings
  Cascade shutdown
This commit is contained in:
Marek Marczykowski-Górecki 2021-05-30 00:56:11 +02:00
commit aaf99091ef
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
2 changed files with 53 additions and 26 deletions

View File

@ -493,17 +493,15 @@ class QubesTableModel(QAbstractTableModel):
return def_flags | Qt.ItemIsUserCheckable return def_flags | Qt.ItemIsUserCheckable
return def_flags return def_flags
vm_shutdown_timeout = 20000 # in msec
vm_restart_check_timeout = 1000 # in msec vm_restart_check_timeout = 1000 # in msec
class VmShutdownMonitor(QObject): class VmShutdownMonitor(QObject):
def __init__(self, vm, shutdown_time=vm_shutdown_timeout, def __init__(self, vm, check_time=vm_restart_check_timeout,
check_time=vm_restart_check_timeout,
and_restart=False, caller=None): and_restart=False, caller=None):
QObject.__init__(self) QObject.__init__(self)
self.vm = vm self.vm = vm
self.shutdown_time = shutdown_time self.shutdown_timeout = vm.shutdown_timeout
self.check_time = check_time self.check_time = check_time
self.and_restart = and_restart self.and_restart = and_restart
self.shutdown_started = datetime.now() self.shutdown_started = datetime.now()
@ -519,7 +517,7 @@ class VmShutdownMonitor(QObject):
def timeout_reached(self): def timeout_reached(self):
actual = datetime.now() - self.shutdown_started actual = datetime.now() - self.shutdown_started
allowed = timedelta(milliseconds=self.shutdown_time) allowed = timedelta(seconds=self.shutdown_timeout)
return actual > allowed return actual > allowed
@ -541,12 +539,12 @@ class VmShutdownMonitor(QObject):
msgbox.setText(self.tr( msgbox.setText(self.tr(
"The Qube <b>'{0}'</b> hasn't shutdown within the last " "The Qube <b>'{0}'</b> hasn't shutdown within the last "
"{1} seconds, do you want to kill it?<br>").format( "{1} seconds, do you want to kill it?<br>").format(
vm.name, self.shutdown_time / 1000)) vm.name, self.shutdown_timeout))
kill_button = msgbox.addButton( kill_button = msgbox.addButton(
self.tr("Kill it!"), QMessageBox.YesRole) self.tr("Kill it!"), QMessageBox.YesRole)
wait_button = msgbox.addButton( wait_button = msgbox.addButton(
self.tr("Wait another {0} seconds...").format( self.tr("Wait another {0} seconds...").format(
self.shutdown_time / 1000), self.shutdown_timeout),
QMessageBox.NoRole) QMessageBox.NoRole)
ignore_button = msgbox.addButton(self.tr("Don't ask again"), ignore_button = msgbox.addButton(self.tr("Don't ask again"),
QMessageBox.RejectRole) QMessageBox.RejectRole)
@ -668,7 +666,7 @@ class QubesProxyModel(QSortFilterProxyModel):
vm = self.sourceModel().data(index, Qt.UserRole) vm = self.sourceModel().data(index, Qt.UserRole)
if self.window.show_running.isChecked() and \ if self.window.show_running.isChecked() and \
vm.state['power'] == 'Running': vm.state['power'] != 'Halted':
return super().filterAcceptsRow(sourceRow, sourceParent) return super().filterAcceptsRow(sourceRow, sourceParent)
if self.window.show_halted.isChecked() and \ if self.window.show_halted.isChecked() and \
vm.state['power'] == 'Halted': vm.state['power'] == 'Halted':
@ -1407,24 +1405,52 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
if reply == QMessageBox.Yes: if reply == QMessageBox.Yes:
self.shutdown_vm(vm) self.shutdown_vm(vm)
def shutdown_vm(self, vm, shutdown_time=vm_shutdown_timeout, def get_connected_vms(self, vm, connected_vms):
check_time=vm_restart_check_timeout, and_restart=False): 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: 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 <b>'{0}'</b>!"
"<br><small>Do you want to shutdown: </small>"
"<b>'{1}'</b>?").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: except exc.QubesException as ex:
QMessageBox.warning( QMessageBox.warning(
self, self,
self.tr("Error shutting down Qube!"), self.tr("Error shutting down Qube!"),
self.tr("ERROR: {0}").format(ex)) self.tr("ERROR: {0}").format(ex))
return return False
self.shutdown_monitor[vm.qid] = VmShutdownMonitor(vm, shutdown_time, self.shutdown_monitor[vm.qid] = VmShutdownMonitor(vm, check_time,
check_time,
and_restart, self) and_restart, self)
# noinspection PyCallByClass,PyTypeChecker # noinspection PyCallByClass,PyTypeChecker
QTimer.singleShot(check_time, self.shutdown_monitor[ QTimer.singleShot(check_time, self.shutdown_monitor[
vm.qid].check_if_vm_has_shutdown) vm.qid].check_if_vm_has_shutdown)
return True
# noinspection PyArgumentList # noinspection PyArgumentList
@pyqtSlot(name='on_action_restartvm_triggered') @pyqtSlot(name='on_action_restartvm_triggered')
def action_restartvm_triggered(self): 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 # in case the user shut down the VM in the meantime
try: try:
if manager_utils.is_running(vm, False): if manager_utils.is_running(vm, False):
self.shutdown_vm(vm, and_restart=True) self.shutdown_vm(vm, force=True, and_restart=True)
else: else:
self.start_vm(vm) self.start_vm(vm)
except exc.QubesException as ex: except exc.QubesException as ex:

View File

@ -457,11 +457,10 @@ class QubeManagerTest(unittest.TestCase):
with unittest.mock.patch.object(selected_vm, 'shutdown')\ with unittest.mock.patch.object(selected_vm, 'shutdown')\
as mock_shutdown: as mock_shutdown:
self.dialog.action_shutdownvm.trigger() 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( mock_monitor.assert_called_once_with(
selected_vm, selected_vm, unittest.mock.ANY, unittest.mock.ANY,
unittest.mock.ANY, unittest.mock.ANY, unittest.mock.ANY)
unittest.mock.ANY, unittest.mock.ANY)
mock_timer.assert_called_once_with(unittest.mock.ANY, mock_timer.assert_called_once_with(unittest.mock.ANY,
unittest.mock.ANY) unittest.mock.ANY)
@ -546,10 +545,9 @@ class QubeManagerTest(unittest.TestCase):
with unittest.mock.patch.object(selected_vm, 'shutdown')\ with unittest.mock.patch.object(selected_vm, 'shutdown')\
as mock_shutdown: as mock_shutdown:
action.trigger() action.trigger()
mock_shutdown.assert_called_once_with() mock_shutdown.assert_called_once_with(force=True)
mock_monitor.assert_called_once_with( mock_monitor.assert_called_once_with(
selected_vm, unittest.mock.ANY, selected_vm, 1000, True, unittest.mock.ANY)
unittest.mock.ANY, True, unittest.mock.ANY)
@unittest.mock.patch('qubesmanager.qube_manager.StartVMThread') @unittest.mock.patch('qubesmanager.qube_manager.StartVMThread')
@unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question", @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
@ -1484,7 +1482,7 @@ class QubeManagerTest(unittest.TestCase):
for row in range(self.dialog.table.model().rowCount()): for row in range(self.dialog.table.model().rowCount()):
template = self._get_table_item(row, "Template") template = self._get_table_item(row, "Template")
vm = self._get_table_vm(row) vm = self._get_table_vm(row)
if template != 'AdminVM' and \ if template != 'AdminVM' and not vm.provides_network and \
(running is None (running is None
or (running and vm.is_running()) or (running and vm.is_running())
or (not running and not 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 = unittest.mock.Mock()
mock_vm.is_running.return_value = True mock_vm.is_running.return_value = True
mock_vm.start_time = datetime.datetime.now().timestamp() - 3000 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) time.sleep(3)
monitor.check_if_vm_has_shutdown() monitor.check_if_vm_has_shutdown()
@ -1708,8 +1707,9 @@ class VMShutdownMonitorTest(unittest.TestCase):
mock_vm = unittest.mock.Mock() mock_vm = unittest.mock.Mock()
mock_vm.is_running.return_value = True mock_vm.is_running.return_value = True
mock_vm.start_time = datetime.datetime.now().timestamp() - 3000 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) time.sleep(3)
monitor.restart_vm_if_needed = unittest.mock.Mock() monitor.restart_vm_if_needed = unittest.mock.Mock()
@ -1725,8 +1725,9 @@ class VMShutdownMonitorTest(unittest.TestCase):
mock_vm = unittest.mock.Mock() mock_vm = unittest.mock.Mock()
mock_vm.is_running.return_value = True mock_vm.is_running.return_value = True
mock_vm.start_time = datetime.datetime.now().timestamp() - 3000 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) time.sleep(1)
monitor.check_if_vm_has_shutdown() monitor.check_if_vm_has_shutdown()