Implemented 'Restart' button

QubesOS/qubes-issues#1499
This commit is contained in:
Ivan Konov 2016-06-05 22:19:18 +03:00
parent 8da27e646f
commit 7cd1afbb3c
4 changed files with 81 additions and 21 deletions

BIN
icons/restartvm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -320,6 +320,7 @@
<addaction name="action_resumevm"/> <addaction name="action_resumevm"/>
<addaction name="action_pausevm"/> <addaction name="action_pausevm"/>
<addaction name="action_shutdownvm"/> <addaction name="action_shutdownvm"/>
<addaction name="action_restartvm"/>
<addaction name="action_killvm"/> <addaction name="action_killvm"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_settings"/> <addaction name="action_settings"/>
@ -369,6 +370,7 @@
<addaction name="action_resumevm"/> <addaction name="action_resumevm"/>
<addaction name="action_pausevm"/> <addaction name="action_pausevm"/>
<addaction name="action_shutdownvm"/> <addaction name="action_shutdownvm"/>
<addaction name="action_restartvm"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_settings"/> <addaction name="action_settings"/>
<addaction name="action_editfwrules"/> <addaction name="action_editfwrules"/>
@ -455,6 +457,21 @@
<string>Shutdown selected VM</string> <string>Shutdown selected VM</string>
</property> </property>
</action> </action>
<action name="action_restartvm">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/restartvm.png</normaloff>:/restartvm.png</iconset>
</property>
<property name="text">
<string>Restart VM</string>
</property>
<property name="toolTip">
<string>Restart selected VM</string>
</property>
</action>
<action name="action_appmenus"> <action name="action_appmenus">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>

View File

@ -240,36 +240,48 @@ class VmRowInTable(object):
vm_shutdown_timeout = 20000 # in msec vm_shutdown_timeout = 20000 # 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, shutdown_time=vm_shutdown_timeout, check_time=vm_restart_check_timeout, and_restart=False, caller=None):
QObject.__init__(self) QObject.__init__(self)
self.vm = vm self.vm = vm
self.shutdown_time = shutdown_time self.shutdown_time = shutdown_time
self.check_time = check_time
self.and_restart = and_restart
self.shutdown_started = datetime.now()
self.caller = caller
def check_if_vm_has_shutdown(self): def check_if_vm_has_shutdown(self):
vm = self.vm vm = self.vm
vm_start_time = vm.get_start_time() vm_start_time = vm.get_start_time()
if not vm.is_running() or ( if vm.is_running() and vm_start_time and vm_start_time < self.shutdown_started:
vm_start_time and vm_start_time >= datetime.now() - if (datetime.now()-self.shutdown_started) > timedelta(milliseconds=self.shutdown_time):
timedelta(0, self.shutdown_time / 1000)): reply = QMessageBox.question(
return None, "VM Shutdown",
"The VM <b>'{0}'</b> hasn't shutdown within the last {1} seconds, "
reply = QMessageBox.question( "do you want to kill it?<br>".format(
None, "VM Shutdown", vm.name, self.shutdown_time / 1000),
"The VM <b>'{0}'</b> hasn't shutdown within the last {1} seconds, " "Kill it!",
"do you want to kill it?<br>".format( "Wait another {0} seconds...".format(
vm.name, self.shutdown_time / 1000), self.shutdown_time / 1000))
"Kill it!", if reply == 0:
"Wait another {0} seconds...".format( vm.force_shutdown()
self.shutdown_time / 1000)) if self.and_restart:
if reply == 0: if self.caller:
vm.force_shutdown() self.caller.start_vm(vm)
else:
# noinspection PyTypeChecker,PyCallByClass
self.shutdown_started=datetime.now()
QTimer.singleShot(self.check_time, self.check_if_vm_has_shutdown)
else:
QTimer.singleShot(self.check_time, self.check_if_vm_has_shutdown)
else: else:
# noinspection PyTypeChecker,PyCallByClass
QTimer.singleShot(self.shutdown_time, self.check_if_vm_has_shutdown)
if self.and_restart:
if self.caller:
self.caller.start_vm(vm)
class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
row_height = 30 row_height = 30
@ -408,6 +420,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
self.context_menu.addAction(self.action_startvm_tools_install) self.context_menu.addAction(self.action_startvm_tools_install)
self.context_menu.addAction(self.action_pausevm) self.context_menu.addAction(self.action_pausevm)
self.context_menu.addAction(self.action_shutdownvm) self.context_menu.addAction(self.action_shutdownvm)
self.context_menu.addAction(self.action_restartvm)
self.context_menu.addAction(self.action_killvm) self.context_menu.addAction(self.action_killvm)
self.context_menu.addSeparator() self.context_menu.addSeparator()
@ -879,6 +892,10 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
vm.last_running and vm.last_running and
vm.last_power_state != "Paused" and vm.last_power_state != "Paused" and
vm.qid != 0) vm.qid != 0)
self.action_restartvm.setEnabled(
vm.last_running and
vm.last_power_state != "Paused" and
vm.qid != 0)
self.action_killvm.setEnabled((vm.last_running or self.action_killvm.setEnabled((vm.last_running or
vm.last_power_state == "Paused") and vm.last_power_state == "Paused") and
vm.qid != 0) vm.qid != 0)
@ -905,6 +922,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
self.action_resumevm.setEnabled(False) self.action_resumevm.setEnabled(False)
self.action_pausevm.setEnabled(False) self.action_pausevm.setEnabled(False)
self.action_shutdownvm.setEnabled(False) self.action_shutdownvm.setEnabled(False)
self.action_restartvm.setEnabled(False)
self.action_killvm.setEnabled(False) self.action_killvm.setEnabled(False)
self.action_appmenus.setEnabled(False) self.action_appmenus.setEnabled(False)
self.action_editfwrules.setEnabled(False) self.action_editfwrules.setEnabled(False)
@ -1129,6 +1147,10 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
"ERROR: {0}".format(ex)) "ERROR: {0}".format(ex))
return return
self.start_vm(vm)
def start_vm(self, vm):
assert not vm.is_running() assert not vm.is_running()
thread_monitor = ThreadMonitor() thread_monitor = ThreadMonitor()
thread = threading.Thread(target=self.do_start_vm, thread = threading.Thread(target=self.do_start_vm,
@ -1238,7 +1260,7 @@ class VmManagerWindow(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 shutdown_vm(self, vm, shutdown_time=vm_shutdown_timeout, check_time=vm_restart_check_timeout, and_restart=False):
try: try:
vm.shutdown() vm.shutdown()
except Exception as ex: except Exception as ex:
@ -1249,11 +1271,31 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
trayIcon.showMessage("VM '{0}' is shutting down...".format(vm.name), trayIcon.showMessage("VM '{0}' is shutting down...".format(vm.name),
msecs=3000) msecs=3000)
self.shutdown_monitor[vm.qid] = VmShutdownMonitor(vm, shutdown_time) self.shutdown_monitor[vm.qid] = VmShutdownMonitor(vm, shutdown_time, check_time, and_restart, self)
# noinspection PyCallByClass,PyTypeChecker # noinspection PyCallByClass,PyTypeChecker
QTimer.singleShot(shutdown_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)
@pyqtSlot(name='on_action_restartvm_triggered')
def action_restartvm_triggered(self):
vm = self.get_selected_vm()
assert vm.is_running()
self.blk_manager.check_if_serves_as_backend(vm)
reply = QMessageBox.question(
None, "VM Restart Confirmation",
"Are you sure you want to restart the VM <b>'{0}'</b>?<br>"
"<small>This will shutdown all the running applications "
"within this VM.</small>".format(vm.name),
QMessageBox.Yes | QMessageBox.Cancel)
app.processEvents()
if reply == QMessageBox.Yes:
self.shutdown_vm(vm, and_restart=True)
@pyqtSlot(name='on_action_killvm_triggered') @pyqtSlot(name='on_action_killvm_triggered')
def action_killvm_triggered(self): def action_killvm_triggered(self):
vm = self.get_selected_vm() vm = self.get_selected_vm()

View File

@ -48,5 +48,6 @@
<file alias="pausevm.png">icons/pausevm.png</file> <file alias="pausevm.png">icons/pausevm.png</file>
<file alias="showcpuload.png">icons/showcpuload.png</file> <file alias="showcpuload.png">icons/showcpuload.png</file>
<file alias="mic.png">icons/mic.png</file> <file alias="mic.png">icons/mic.png</file>
<file alias="restartvm.png">icons/restartvm.png</file>
</qresource> </qresource>
</RCC> </RCC>