Merge branch 'master' of git.qubes-os.org:/var/lib/qubes/git/marmarek/qubes-manager

This commit is contained in:
Joanna Rutkowska 2012-07-06 10:16:28 +02:00
commit 6e1afe0ae7
8 changed files with 238 additions and 124 deletions

BIN
icons/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,15 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- This config allows anyone to control QubesManager -->
<policy context="default">
<allow send_destination="org.qubesos.QubesManager"/>
</policy>
<policy group="qubes">
<allow own="org.qubesos.QubesManager"/>
</policy>
</busconfig>

View File

@ -0,0 +1,19 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.qubesos.QubesManager">
<method name="notify_error">
<arg name="vmname" type="s" direction="in"/>
<arg name="message" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="clear_error_exact">
<arg name="vmname" type="s" direction="in"/>
<arg name="message" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="clear_error">
<arg name="vmname" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
</interface>
</node>

View File

@ -1,3 +1,4 @@
qrc_resources.py
ui_newappvmdlg.py
ui_newfwruledlg.py
resources_rc.py
ui_*.py
*.py[co]

View File

@ -154,7 +154,7 @@ class NewVmDlg (QDialog, Ui_NewVMDlg):
progress.hide()
if thread_monitor.success:
self.trayIcon.showMessage ("Qubes VM Manager", "VM '{0}' has been created.".format(vmname), msecs=3000)
self.trayIcon.showMessage ("VM '{0}' has been created.".format(vmname), msecs=3000)
else:
QMessageBox.warning (None, "Error creating AppVM!", "ERROR: {0}".format(thread_monitor.error_msg))

View File

@ -26,6 +26,8 @@ import signal
import fcntl
import errno
import dbus
import dbus.service
from dbus.mainloop.qt import DBusQtMainLoop
from PyQt4.QtCore import *
from PyQt4.QtGui import *
@ -62,6 +64,9 @@ qubes_guid_path = '/usr/bin/qubes_guid'
update_suggestion_interval = 14 # 14 days
dbus_object_path = '/org/qubesos/QubesManager'
dbus_interface = 'org.qubesos.QubesManager'
power_order = Qt.DescendingOrder
update_order = Qt.AscendingOrder
@ -78,20 +83,25 @@ class VmIconWidget (QWidget):
def __init__(self, icon_path, enabled=True, size_multiplier=0.7, tooltip = None, parent=None):
super(VmIconWidget, self).__init__(parent)
label_icon = QLabel()
self.label_icon = QLabel()
icon = QIcon (icon_path)
icon_sz = QSize (VmManagerWindow.row_height * size_multiplier, VmManagerWindow.row_height * size_multiplier)
icon_pixmap = icon.pixmap(icon_sz, QIcon.Disabled if not enabled else QIcon.Normal)
label_icon.setPixmap (icon_pixmap)
label_icon.setFixedSize (icon_sz)
self.label_icon.setPixmap (icon_pixmap)
self.label_icon.setFixedSize (icon_sz)
if tooltip != None:
label_icon.setToolTip(tooltip)
self.label_icon.setToolTip(tooltip)
layout = QHBoxLayout()
layout.addWidget(label_icon)
layout.addWidget(self.label_icon)
layout.setContentsMargins(0,0,0,0)
self.setLayout(layout)
def setToolTip(self, tooltip):
if tooltip is not None:
self.label_icon.setToolTip(tooltip)
else:
self.label_icon.setToolTip('')
class VmTypeWidget(VmIconWidget):
@ -235,10 +245,12 @@ class VmInfoWidget (QWidget):
self.on_icon = VmStatusIcon(vm)
self.upd_info = VmUpdateInfoWidget(vm, show_text=False)
self.error_icon = VmIconWidget(":/warning.png")
self.blk_icon = VmIconWidget(":/mount.png")
layout.addWidget(self.on_icon)
layout.addWidget(self.upd_info)
layout.addWidget(self.error_icon)
layout.addItem(QSpacerItem(0, 10, QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding))
layout.addWidget(self.blk_icon)
@ -246,6 +258,7 @@ class VmInfoWidget (QWidget):
self.setLayout(layout)
self.blk_icon.setVisible(False)
self.error_icon.setVisible(False)
self.tableItem = self.VmInfoItem(self.upd_info.tableItem, vm)
@ -254,6 +267,8 @@ class VmInfoWidget (QWidget):
self.upd_info.update_outdated(vm)
if blk_visible != None:
self.blk_icon.setVisible(blk_visible)
self.error_icon.setToolTip(vm.error_msg)
self.error_icon.setVisible(vm.error_msg is not None)
class VmTemplateItem (QTableWidgetItem):
@ -633,7 +648,7 @@ class VmShutdownMonitor(QObject):
vm_start_time = vm.get_start_time()
if not vm.is_running() or (vm_start_time and vm_start_time >= datetime.utcnow() - timedelta(0,self.shutdown_time/1000)):
if vm.is_template():
trayIcon.showMessage ("Qubes VM Manager", "You have just modified template '{0}'. You should now restart all the VMs based on it, so they could see the changes.".format(vm.name), msecs=8000)
trayIcon.showMessage ("You have just modified template '{0}'. You should now restart all the VMs based on it, so they could see the changes.".format(vm.name), msecs=8000)
return
reply = QMessageBox.question(None, "VM Shutdown",
@ -695,6 +710,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
self.running_vms_count = 0
self.vm_errors = {}
self.frame_width = 0
self.frame_height = 0
@ -857,6 +874,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
vm.last_running = vm.last_power_state in ["Running", "Transient"]
if vm.last_running:
running_count += 1
vm.error_msg = self.vm_errors[vm.qid] if vm.qid in self.vm_errors else None
self.running_vms_count = running_count
@ -927,6 +945,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
if not prev_running and vm.last_running:
self.running_vms_count += 1
some_vms_have_changed_power_state = True
# Clear error state when VM just started
self.clear_error(vm.qid)
elif prev_running and not vm.last_running:
self.running_vms_count -= 1
some_vms_have_changed_power_state = True
@ -1012,7 +1032,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
res, msg = self.blk_manager.check_for_updates()
if msg != None and len(msg) > 0:
str = "\n".join(msg)
trayIcon.showMessage ("Qubes VM Manager", str, msecs=5000)
trayIcon.showMessage (str, msecs=5000)
return res
@ -1085,6 +1105,26 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
event.ignore()
def set_error(self, qid, message):
for vm in self.vms_list:
if vm.qid == qid:
vm.error_msg = message
# Store error in separate dict to make it immune to VM list reload
self.vm_errors[qid] = message
def clear_error(self, qid):
self.vm_errors.pop(qid, None)
for vm in self.vms_list:
if vm.qid == qid:
vm.error_msg = None
def clear_error_exact(self, qid, message):
for vm in self.vms_list:
if vm.qid == qid:
if vm.error_msg == message:
vm.error_msg = None
self.vm_errors.pop(qid, None)
@pyqtSlot(name='on_action_createvm_triggered')
def action_createvm_triggered(self):
dialog = NewVmDlg(app, self.qvm_collection, trayIcon)
@ -1112,6 +1152,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
self.qvm_collection.lock_db_for_reading()
self.qvm_collection.load()
self.qvm_collection.unlock_db()
vm = self.qvm_collection[vm.qid]
if vm.is_template():
dependent_vms = self.qvm_collection.get_vms_based_on(vm.qid)
@ -1148,7 +1189,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
progress.hide()
if thread_monitor.success:
trayIcon.showMessage ("Qubes VM Manager", "VM '{0}' has been removed.".format(vm.name), msecs=3000)
trayIcon.showMessage ("VM '{0}' has been removed.".format(vm.name), msecs=3000)
else:
QMessageBox.warning (None, "Error removing VM!", "ERROR: {0}".format(thread_monitor.error_msg))
@ -1156,6 +1197,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
try:
self.qvm_collection.lock_db_for_writing()
self.qvm_collection.load()
vm = self.qvm_collection[vm.qid]
#TODO: the following two conditions should really be checked by qvm_collection.pop() overload...
if vm.is_template() and self.qvm_collection.default_template_qid == vm.qid:
@ -1190,16 +1232,17 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
thread.daemon = True
thread.start()
trayIcon.showMessage ("Qubes VM Manager", "Starting '{0}'...".format(vm.name), msecs=3000)
trayIcon.showMessage ("Starting '{0}'...".format(vm.name), msecs=3000)
while not thread_monitor.is_finished():
app.processEvents()
time.sleep (0.1)
if thread_monitor.success:
trayIcon.showMessage ("Qubes VM Manager", "VM '{0}' has been started.".format(vm.name), msecs=3000)
trayIcon.showMessage ("VM '{0}' has been started.".format(vm.name), msecs=3000)
else:
trayIcon.showMessage ("Qubes VM Manager", "Error starting VM <b>'{0}'</b>: {1}".format(vm.name, thread_monitor.error_msg ), msecs=3000)
trayIcon.showMessage ("Error starting VM <b>'{0}'</b>: {1}".format(vm.name, thread_monitor.error_msg ), msecs=3000)
self.set_error(vm.qid, "Error starting VM: %s" % thread_monitor.error_msg)
def do_start_vm(self, vm, thread_monitor):
try:
@ -1210,10 +1253,6 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
thread_monitor.set_finished()
return
retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon, "-l", str(vm.label.index)])
if (retcode != 0):
thread_monitor.set_error_msg("Cannot start qubes_guid!")
thread_monitor.set_finished()
@pyqtSlot(name='on_action_pausevm_triggered')
@ -1252,7 +1291,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
QMessageBox.warning (None, "Error shutting down VM!", "ERROR: {0}".format(ex))
return
trayIcon.showMessage ("Qubes VM Manager", "VM '{0}' is shutting down...".format(vm.name), msecs=3000)
trayIcon.showMessage ("VM '{0}' is shutting down...".format(vm.name), msecs=3000)
self.shutdown_monitor[vm.qid] = VmShutdownMonitor (vm, shutdown_time)
QTimer.singleShot (shutdown_time, self.shutdown_monitor[vm.qid].check_if_vm_has_shutdown)
@ -1277,7 +1316,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
QMessageBox.critical (None, "Error while killing VM!", "<b>An exception ocurred while killing {0}.</b><br>ERROR: {1}".format(vm.name, ex))
return
trayIcon.showMessage ("Qubes VM Manager", "VM '{0}' killed!".format(vm.name), msecs=3000)
trayIcon.showMessage ("VM '{0}' killed!".format(vm.name), msecs=3000)
@ -1305,7 +1344,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
QMessageBox.Yes | QMessageBox.Cancel)
if reply != QMessageBox.Yes:
return
trayIcon.showMessage ("Qubes VM Manager", "Starting '{0}'...".format(vm.name), msecs=3000)
trayIcon.showMessage ("Starting '{0}'...".format(vm.name), msecs=3000)
app.processEvents()
@ -1334,7 +1373,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
if vm.qid == 0:
subprocess.check_call (["/usr/bin/qubes-dom0-update", "--clean", "--gui"])
else:
vm.run("user:gpk-update-viewer", verbose=False, autostart=True)
vm.run("user:gpk-update-viewer", verbose=False, autostart=True,
notify_function=lambda lvl, msg: trayIcon.showMessage(msg, msecs=3000) )
except Exception as ex:
thread_monitor.set_error_msg(str(ex))
thread_monitor.set_finished()
@ -1363,31 +1403,19 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
cmd = ['kdialog', '--title', 'Qubes command entry', '--inputbox', 'Run command in <b>'+vm.name+'</b>:']
kdialog = subprocess.Popen(cmd, stdout = subprocess.PIPE)
command_to_run = kdialog.stdout.read()
command_to_run = "'"+command_to_run.strip()+"'"
command_to_run = command_to_run.strip()
if command_to_run != "":
command_to_run = "qvm-run -a "+ vm.name + " " + command_to_run
try:
subprocess.check_call(command_to_run, shell=True)
except OSError as ex:
if ex.errno == errno.EINTR:
pass
else:
thread_monitor.set_error_msg(str(ex))
thread_monitor.set_finished()
vm.run("user:" + command_to_run, verbose=False, autostart=True,
notify_function=lambda lvl, msg: trayIcon.showMessage(msg, msecs=3000) )
except Exception as ex:
thread_monitor.set_error_msg(str(ex))
thread_monitor.set_finished()
thread_monitor.set_finished()
@pyqtSlot(name='on_action_set_keyboard_layout_triggered')
def action_set_keyboard_layout_triggered(self):
vm = self.get_selected_vm()
subprocess.Popen( ['qvm-run', vm.name, 'qubes-change-keyboard-layout'])
vm.run('user:qubes-change-keyboard-layout', verbose = False)
@pyqtSlot(name='on_action_showallvms_triggered')
def action_showallvms_triggered(self):
@ -1644,7 +1672,6 @@ class QubesBlockDevicesManager():
return ret
def update(self):
blk = qubesutils.block_list()
for b in blk:
@ -1697,13 +1724,13 @@ class QubesBlockDevicesManager():
backend_vm_name = self.free_devs[dev]['backend_name']
dev_id = self.free_devs[dev]['dev']
backend_vm = self.qvm_collection.get_vm_by_name(backend_vm_name)
trayIcon.showMessage ("Qubes VM Manager", "{0} - attaching {1}".format(vm.name, dev), msecs=3000)
trayIcon.showMessage ("{0} - attaching {1}".format(vm.name, dev), msecs=3000)
qubesutils.block_attach(vm, backend_vm, dev_id)
def detach_device(self, vm, dev_name):
dev_id = self.attached_devs[dev_name]['attached_to']['devid']
vm_xid = self.attached_devs[dev_name]['attached_to']['xid']
trayIcon.showMessage ("Qubes VM Manager", "{0} - detaching {1}".format(vm.name, dev_name), msecs=3000)
trayIcon.showMessage ("{0} - detaching {1}".format(vm.name, dev_name), msecs=3000)
qubesutils.block_detach(None, dev_id, vm_xid)
def check_if_serves_as_backend(self, vm):
@ -1760,8 +1787,8 @@ class QubesTrayIcon(QSystemTrayIcon):
else:
target.addAction(action)
def showMessage(self, title, message, msecs):
self.tray_notifier.Notify("Qubes", 0, "/usr/share/qubes/icons/qubes.png", title, message, [], [], msecs)
def showMessage(self, message, msecs):
self.tray_notifier.Notify("Qubes", 0, "/usr/share/qubes/icons/qubes.png", "Qubes VM Manager", message, [], [], msecs)
def createAction(self, text, slot=None, shortcut=None, icon=None,
tip=None, checkable=False, signal="triggered()"):
@ -1779,6 +1806,42 @@ class QubesTrayIcon(QSystemTrayIcon):
action.setCheckable(True)
return action
class QubesDbusNotify(dbus.service.Object):
def __init__(self, bus, manager_window):
# export to system bus (instead of session) to make possible
# communication from outside of session (eg. from qmemman)
dbus.service.Object.__init__(self, bus, dbus_object_path)
self.manager_window = manager_window
@dbus.service.method(dbus_interface=dbus_interface,
in_signature='ss', out_signature='')
def notify_error(self, vmname, message):
vm = self.manager_window.qvm_collection.get_vm_by_name(vmname)
if vm:
self.manager_window.set_error(vm.qid, message)
else:
# ignore VM-not-found error
pass
@dbus.service.method(dbus_interface=dbus_interface,
in_signature='ss', out_signature='')
def clear_error_exact(self, vmname, message):
vm = self.manager_window.qvm_collection.get_vm_by_name(vmname)
if vm:
self.manager_window.clear_error_exact(vm.qid, message)
else:
# ignore VM-not-found error
pass
@dbus.service.method(dbus_interface=dbus_interface,
in_signature='s', out_signature='')
def clear_error(self, vmname):
vm = self.manager_window.qvm_collection.get_vm_by_name(vmname)
if vm:
self.manager_window.clear_error(vm.qid, message)
else:
# ignore VM-not-found error
pass
def get_frame_size():
w = 0
h = 0
@ -1892,6 +1955,10 @@ def main():
sys.excepthook = handle_exception
# setup dbus connection
DBusQtMainLoop(set_as_default=True)
global manager_window
manager_window = VmManagerWindow()
wm = WatchManager()
@ -1902,6 +1969,11 @@ def main():
notifier.start()
wdd = wm.add_watch(qubes_store_filename, mask)
global system_bus
system_bus = dbus.SystemBus()
name = dbus.service.BusName('org.qubesos.QubesManager', system_bus)
dbus_notifier = QubesDbusNotify(system_bus, manager_window)
global trayIcon
trayIcon = QubesTrayIcon(QIcon(":/qubes.png"))
trayIcon.show()

View File

@ -11,6 +11,7 @@
<file alias="update-recommended.png">icons/update-recommended.png</file>
<file alias="show-all-running.png">icons/show-all-running.png</file>
<file alias="mount.png">icons/mount.png</file>
<file alias="warning.png">icons/warning.png</file>
<file alias="log.png">icons/log.png</file>
<file alias="run-command.png">icons/run-command.png</file>
<file alias="kbd-layout.png">icons/kbd-layout.png</file>

View File

@ -13,6 +13,7 @@ License: GPL
URL: http://fixme
Requires: python, PyQt4, qubes-core-dom0 > 1.7.23, kdebase
Requires: pmount, cryptsetup, wmctrl
Requires: dbus
BuildRequires: PyQt4-devel
AutoReq: 0
@ -64,6 +65,9 @@ cp qubes-manager.desktop $RPM_BUILD_ROOT/usr/share/applications
mkdir -p $RPM_BUILD_ROOT/etc/xdg/autostart/
cp qubes-manager.desktop $RPM_BUILD_ROOT/etc/xdg/autostart/
install -D org.qubesos.QubesManager.conf $RPM_BUILD_ROOT/etc/dbus-1/system.d/org.qubesos.QubesManager.conf
install -D org.qubesos.QubesManager.xml $RPM_BUILD_ROOT/usr/share/dbus-1/interfaces/org.qubesos.QubesManager.xml
%post
update-desktop-database &> /dev/null || :
killall -1 qubes-manager || :
@ -152,3 +156,5 @@ rm -rf $RPM_BUILD_ROOT
/usr/share/applications/qubes-manager.desktop
/etc/xdg/autostart/qubes-manager.desktop
/etc/dbus-1/system.d/org.qubesos.QubesManager.conf
/usr/share/dbus-1/interfaces/org.qubesos.QubesManager.xml