Added AdminAPI events to Qube Manager
rewritten parts of Qube Manager to use AdminAPI events (with quamash to combine PyQt with asyncio). hopefully fixes QubesOS/qubes-issues#4116 hopefully fixes QubesOS/qubes-issues#4258 hopefully fixes QubesOS/qubes-issues#4244 obsoletes/fixes QubesOS/qubes-issues#4190 hopefully fixes QubesOS/qubes-issues#3675
This commit is contained in:
parent
20ee51f3d1
commit
4fc295e6a7
@ -21,7 +21,6 @@
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
@ -30,12 +29,14 @@ import time
|
||||
from datetime import datetime, timedelta
|
||||
import traceback
|
||||
import threading
|
||||
|
||||
from pydbus import SessionBus
|
||||
import quamash
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
|
||||
from qubesadmin import Qubes
|
||||
from qubesadmin import exc
|
||||
from qubesadmin import utils
|
||||
from qubesadmin import events
|
||||
|
||||
from PyQt4 import QtGui # pylint: disable=import-error
|
||||
from PyQt4 import QtCore # pylint: disable=import-error
|
||||
@ -257,7 +258,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
"Last backup": 10,
|
||||
}
|
||||
|
||||
def __init__(self, qt_app, qubes_app, parent=None):
|
||||
def __init__(self, qt_app, qubes_app, dispatcher, parent=None):
|
||||
# pylint: disable=unused-argument
|
||||
super(VmManagerWindow, self).__init__()
|
||||
self.setupUi(self)
|
||||
@ -390,15 +391,24 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
self.shutdown_monitor = {}
|
||||
|
||||
# Connect dbus events
|
||||
self.bus = SessionBus()
|
||||
manager = self.bus.get("org.qubes.DomainManager1")
|
||||
manager.DomainAdded.connect(self.on_domain_added)
|
||||
manager.DomainRemoved.connect(self.on_domain_removed)
|
||||
manager.Failed.connect(self.on_failed)
|
||||
manager.Halted.connect(self.on_halted)
|
||||
manager.Halting.connect(self.on_halting)
|
||||
manager.Starting.connect(self.on_starting)
|
||||
manager.Started.connect(self.on_started)
|
||||
self.dispatcher = dispatcher
|
||||
dispatcher.add_handler('domain-pre-start',
|
||||
self.on_domain_status_changed)
|
||||
dispatcher.add_handler('domain-start', self.on_domain_status_changed)
|
||||
dispatcher.add_handler('domain-start-failed',
|
||||
self.on_domain_status_changed)
|
||||
dispatcher.add_handler('domain-stopped', self.on_domain_status_changed)
|
||||
dispatcher.add_handler('domain-shutdown', self.on_domain_status_changed)
|
||||
|
||||
dispatcher.add_handler('domain-add', self.on_domain_added)
|
||||
dispatcher.add_handler('domain-delete', self.on_domain_removed)
|
||||
|
||||
dispatcher.add_handler('property-set:*',
|
||||
self.on_domain_changed)
|
||||
dispatcher.add_handler('property-del:*',
|
||||
self.on_domain_changed)
|
||||
dispatcher.add_handler('property-load',
|
||||
self.on_domain_changed)
|
||||
|
||||
# Check Updates Timer
|
||||
timer = QtCore.QTimer(self)
|
||||
@ -421,19 +431,15 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
# the VM might have vanished in the meantime
|
||||
pass
|
||||
|
||||
def on_domain_added(self, _, domain):
|
||||
#needs to clear cache
|
||||
self.qubes_app.domains.clear_cache()
|
||||
qid = int(domain.split('/')[-1])
|
||||
def on_domain_added(self, submitter, event, **kwargs):
|
||||
|
||||
self.table.setSortingEnabled(False)
|
||||
|
||||
row_no = self.table.rowCount()
|
||||
self.table.setRowCount(row_no + 1)
|
||||
|
||||
|
||||
for vm in self.qubes_app.domains:
|
||||
if vm.qid == qid:
|
||||
if vm == kwargs['vm']:
|
||||
vm_row = VmRowInTable(vm, row_no, self.table)
|
||||
self.vms_in_table[vm.qid] = vm_row
|
||||
self.table.setSortingEnabled(True)
|
||||
@ -443,72 +449,32 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
# Never should reach here
|
||||
raise RuntimeError('Added domain not found')
|
||||
|
||||
def on_domain_removed(self, _, domain):
|
||||
#needs to clear cache
|
||||
self.qubes_app.domains.clear_cache()
|
||||
qid = int(domain.split('/')[-1])
|
||||
def on_domain_removed(self, submitter, event, **kwargs):
|
||||
|
||||
# Find row and remove
|
||||
try:
|
||||
row_index = 0
|
||||
vm_item = self.table.item(row_index, self.columns_indices["Name"])
|
||||
while vm_item.qid != qid:
|
||||
row_index += 1
|
||||
vm_item = self.table.item(row_index,\
|
||||
self.columns_indices["Name"])
|
||||
except:
|
||||
raise RuntimeError('Deleted domain not found')
|
||||
row_to_delete = None
|
||||
qid_to_delete = None
|
||||
for qid, row in self.vms_in_table.items():
|
||||
if row.vm.name == kwargs['vm']:
|
||||
row_to_delete = row
|
||||
qid_to_delete = qid
|
||||
if not row_to_delete:
|
||||
return # for some reason, the VM was removed in some other way
|
||||
|
||||
self.table.removeRow(row_index)
|
||||
del self.vms_in_table[qid]
|
||||
del self.vms_in_table[qid_to_delete]
|
||||
self.table.removeRow(row_to_delete.name_widget.row())
|
||||
|
||||
def on_failed(self, _, domain):
|
||||
qid = int(domain.split('/')[-1])
|
||||
self.vms_in_table[qid].update()
|
||||
def on_domain_status_changed(self, vm, event, **kwargs):
|
||||
self.vms_in_table[vm.qid].info_widget.update_vm_state()
|
||||
|
||||
if self.vms_in_table[qid].vm == self.get_selected_vm():
|
||||
if vm == self.get_selected_vm():
|
||||
self.table_selection_changed()
|
||||
|
||||
def on_halted(self, _, domain):
|
||||
qid = int(domain.split('/')[-1])
|
||||
self.vms_in_table[qid].update()
|
||||
if vm.klass == 'TemplateVM':
|
||||
for row in self.vms_in_table:
|
||||
if row.vm.template == vm:
|
||||
row.info_widget.update_vm_state()
|
||||
|
||||
if self.vms_in_table[qid].vm == self.get_selected_vm():
|
||||
self.table_selection_changed()
|
||||
|
||||
# Check if is TemplatVM and update related AppVMs
|
||||
starting_vm = self.vms_in_table[qid]
|
||||
if starting_vm.vm.klass == 'TemplateVM':
|
||||
for vm in starting_vm.vm.appvms:
|
||||
if vm.klass == 'AppVM':
|
||||
self.vms_in_table[vm.qid].update()
|
||||
|
||||
def on_halting(self, _, domain):
|
||||
qid = int(domain.split('/')[-1])
|
||||
self.vms_in_table[qid].update()
|
||||
|
||||
if self.vms_in_table[qid].vm == self.get_selected_vm():
|
||||
self.table_selection_changed()
|
||||
|
||||
def on_started(self, _, domain):
|
||||
qid = int(domain.split('/')[-1])
|
||||
self.vms_in_table[qid].update()
|
||||
|
||||
if self.vms_in_table[qid].vm == self.get_selected_vm():
|
||||
self.table_selection_changed()
|
||||
|
||||
def on_starting(self, _, domain):
|
||||
qid = int(domain.split('/')[-1])
|
||||
self.vms_in_table[qid].update()
|
||||
|
||||
if self.vms_in_table[qid].vm == self.get_selected_vm():
|
||||
self.table_selection_changed()
|
||||
|
||||
# Check if is TemplatVM and update related AppVMs
|
||||
starting_vm = self.vms_in_table[qid]
|
||||
if starting_vm.vm.klass == 'TemplateVM':
|
||||
for vm in starting_vm.vm.appvms:
|
||||
if vm.klass == 'AppVM':
|
||||
def on_domain_changed(self, vm, event, **kwargs):
|
||||
self.vms_in_table[vm.qid].update()
|
||||
|
||||
def load_manager_settings(self):
|
||||
@ -1345,20 +1311,39 @@ def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
msg_box.exec_()
|
||||
|
||||
|
||||
def loop_shutdown():
|
||||
pending = asyncio.Task.all_tasks()
|
||||
for task in pending:
|
||||
with suppress(asyncio.CancelledError):
|
||||
task.cancel()
|
||||
|
||||
|
||||
def main():
|
||||
qt_app = QtGui.QApplication(sys.argv)
|
||||
qt_app.setOrganizationName("The Qubes Project")
|
||||
qt_app.setOrganizationDomain("http://qubes-os.org")
|
||||
qt_app.setApplicationName("Qube Manager")
|
||||
qt_app.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager"))
|
||||
|
||||
sys.excepthook = handle_exception
|
||||
qt_app.lastWindowClosed.connect(loop_shutdown)
|
||||
|
||||
qubes_app = Qubes()
|
||||
|
||||
manager_window = VmManagerWindow(qt_app, qubes_app)
|
||||
loop = quamash.QEventLoop(qt_app)
|
||||
asyncio.set_event_loop(loop)
|
||||
dispatcher = events.EventsDispatcher(qubes_app)
|
||||
|
||||
manager_window = VmManagerWindow(qt_app, qubes_app, dispatcher)
|
||||
manager_window.show()
|
||||
qt_app.exec_()
|
||||
|
||||
try:
|
||||
done, _ = loop.run_until_complete(
|
||||
asyncio.ensure_future(dispatcher.listen_for_events()))
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
except Exception:
|
||||
loop_shutdown()
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()[:3]
|
||||
handle_exception(exc_type, exc_value, exc_traceback)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Loading…
Reference in New Issue
Block a user