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:
Marta Marczykowska-Górecka 2018-09-10 17:48:14 +02:00
parent 20ee51f3d1
commit 4fc295e6a7
No known key found for this signature in database
GPG Key ID: 9A752C30B26FD04B

View File

@ -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,73 +449,33 @@ 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':
self.vms_in_table[vm.qid].update()
def on_domain_changed(self, vm, event, **kwargs):
self.vms_in_table[vm.qid].update()
def load_manager_settings(self):
# visible columns
@ -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__":