diff --git a/qubesmanager/main.py b/qubesmanager/main.py index 2f3f9f2..82ac8ed 100755 --- a/qubesmanager/main.py +++ b/qubesmanager/main.py @@ -22,7 +22,6 @@ # # -# TODO: cleanup imports import sys import os import os.path @@ -33,52 +32,20 @@ from datetime import datetime, timedelta import traceback from qubesadmin import Qubes -from qubesadmin import exc from PyQt4 import QtGui from PyQt4 import QtCore -from PyQt4 import Qt -from PyQt4.QtDBus import QDBusVariant, QDBusMessage -from PyQt4.QtDBus import QDBusConnection -from PyQt4.QtDBus import QDBusInterface, QDBusAbstractAdaptor -from pyinotify import WatchManager, ThreadedNotifier, EventsCodes, \ - ProcessEvent from . import ui_vtmanager from . import thread_monitor from . import table_widgets +from . import settings +from . import global_settings +from . import restore +from . import backup import threading -# from qubes.qubes import QubesVmCollection -# from qubes.qubes import QubesException -# from qubes.qubes import system_path -# from qubes.qubes import QubesDaemonPidfile -# from qubes.qubes import QubesHost from qubesmanager.about import AboutDialog -# import table_widgets -# from block import QubesBlockDevicesManager -# from table_widgets import VmTypeWidget, VmLabelWidget, VmNameItem, \ -# VmInfoWidget, VmTemplateItem, VmNetvmItem, VmUsageBarWidget, ChartWidget, \ -# VmSizeOnDiskItem, VmInternalItem, VmIPItem, VmIncludeInBackupsItem, \ -# VmLastBackupItem -# from qubes.qubes import QubesHVm -# from qubes import qubesutils -# from ui_mainwindow import * -# from create_new_vm import NewVmDlg -# from settings import VMSettingsWindow -# from restore import RestoreVMsWindow -# from backup import BackupVMsWindow -# from global_settings import GlobalSettingsWindow -# from networknotes import NetworkNotesDialog -# from log_dialog import LogDialog - -# TODO: probably unneeded -update_suggestion_interval = 14 # 14 days -# TODO: probably unneeded -dbus_object_path = '/org/qubesos/QubesManager' -dbus_interface = 'org.qubesos.QubesManager' -system_bus = None -session_bus = None # TODO: probably unneeded @@ -88,7 +55,6 @@ class QMVmState: AudioRecAllowed = 3 -#TODO: this is actually needed O_O class SearchBox(QtGui.QLineEdit): def __init__(self, parent=None): super(SearchBox, self).__init__(parent) @@ -107,12 +73,12 @@ class SearchBox(QtGui.QLineEdit): class VmRowInTable(object): - cpu_graph_hue = 210 - mem_graph_hue = 120 def __init__(self, vm, row_no, table): self.vm = vm self.row_no = row_no + # TODO: replace a million different widgets with a more generic + # VmFeatureWidget or VMPropertyWidget table_widgets.row_height = VmManagerWindow.row_height table.setRowHeight(row_no, VmManagerWindow.row_height) @@ -147,44 +113,6 @@ class VmRowInTable(object): table.setItem(row_no, VmManagerWindow.columns_indices['NetVM'], self.netvm_widget) - # self.cpu_usage_widget = table_widgets.VmUsageBarWidget( - # 0, 100, "%v %", - # # lambda v, val: val if v.last_running else 0, - # lambda v, val: val, - # vm, 0, self.cpu_graph_hue) - # table.setCellWidget(row_no, VmManagerWindow.columns_indices['CPU'], - # self.cpu_usage_widget) - # table.setItem(row_no, VmManagerWindow.columns_indices['CPU'], - # self.cpu_usage_widget.tableItem) - - # self.load_widget = table_widgets.ChartWidget( - # vm, - # lambda v, val: val if v.last_running else 0, - # self.cpu_graph_hue, 0) - # table.setCellWidget(row_no, - # VmManagerWindow.columns_indices['CPU Graph'], - # self.load_widget) - # table.setItem(row_no, VmManagerWindow.columns_indices['CPU Graph'], - # self.load_widget.tableItem) - - # self.mem_usage_widget = table_widgets.VmUsageBarWidget( - # 0, qubes_host.memory_total / 1024, "%v MB", - # lambda v, val: v.get_mem() / 1024, - # vm, 0, self.mem_graph_hue) - # table.setCellWidget(row_no, VmManagerWindow.columns_indices['MEM'], - # self.mem_usage_widget) - # table.setItem(row_no, VmManagerWindow.columns_indices['MEM'], - # self.mem_usage_widget.tableItem) - # - # # self.mem_widget = table_widgets.ChartWidget( - # # vm, lambda v, val: v.get_mem() * 100 / qubes_host.memory_total, - # # self.mem_graph_hue, 0) - # table.setCellWidget(row_no, - # VmManagerWindow.columns_indices['MEM Graph'], - # self.mem_widget) - # table.setItem(row_no, VmManagerWindow.columns_indices['MEM Graph'], - # self.mem_widget.tableItem) - self.size_widget = table_widgets.VmSizeOnDiskItem(vm) table.setItem(row_no, VmManagerWindow.columns_indices['Size'], self.size_widget) @@ -197,7 +125,8 @@ class VmRowInTable(object): table.setItem(row_no, VmManagerWindow.columns_indices['IP'], self.ip_widget) - self.include_in_backups_widget = table_widgets.VmIncludeInBackupsItem(vm) + self.include_in_backups_widget = \ + table_widgets.VmIncludeInBackupsItem(vm) table.setItem(row_no, VmManagerWindow.columns_indices[ 'Backups'], self.include_in_backups_widget) @@ -205,35 +134,26 @@ class VmRowInTable(object): table.setItem(row_no, VmManagerWindow.columns_indices[ 'Last backup'], self.last_backup_widget) - def update(self, blk_visible=None, cpu_load=None, update_size_on_disk=False, - rec_visible=None): + def update(self, update_size_on_disk=False): """ Update info in a single VM row - :param blk_visible: if not None, show/hide block icon, otherwise - don't change its visibility - :param cpu_load: current CPU load (if applicable), in percents :param update_size_on_disk: should disk utilization be updated? the widget will extract the data from VM object - :param rec_visible: if not None, show/hide mic icon, otherwise don't - change its visibility :return: None """ - self.info_widget.update_vm_state(self.vm, blk_visible, rec_visible) - if cpu_load is not None: - self.cpu_usage_widget.update_load(self.vm, cpu_load) - self.mem_usage_widget.update_load(self.vm, None) - self.load_widget.update_load(self.vm, cpu_load) - self.mem_widget.update_load(self.vm, None) + self.info_widget.update_vm_state(self.vm) if update_size_on_disk: self.size_widget.update() vm_shutdown_timeout = 20000 # in msec -vm_restart_check_timeout= 1000 # in msec +vm_restart_check_timeout = 1000 # in msec class VmShutdownMonitor(QtCore.QObject): - def __init__(self, vm, shutdown_time=vm_shutdown_timeout, check_time=vm_restart_check_timeout, and_restart=False, caller=None): + def __init__(self, vm, shutdown_time=vm_shutdown_timeout, + check_time=vm_restart_check_timeout, + and_restart=False, caller=None): QtCore.QObject.__init__(self) self.vm = vm self.shutdown_time = shutdown_time @@ -245,6 +165,7 @@ class VmShutdownMonitor(QtCore.QObject): def restart_vm_if_needed(self): if self.and_restart and self.caller: self.caller.start_vm(self.vm) +# TODO: can i kill running vm def check_again_later(self): # noinspection PyTypeChecker,PyCallByClass @@ -260,12 +181,14 @@ class VmShutdownMonitor(QtCore.QObject): vm = self.vm vm_is_running = vm.is_running() vm_start_time = vm.get_start_time() - if vm_is_running and vm_start_time and vm_start_time < self.shutdown_started: + if vm_is_running and vm_start_time \ + and vm_start_time < self.shutdown_started: if self.timeout_reached(): reply = QtGui.QMessageBox.question( None, self.tr("VM Shutdown"), - self.tr("The VM '{0}' hasn't shutdown within the last " - "{1} seconds, do you want to kill it?
").format( + self.tr( + "The VM '{0}' hasn't shutdown within the last " + "{1} seconds, do you want to kill it?
").format( vm.name, self.shutdown_time / 1000), self.tr("Kill it!"), self.tr("Wait another {0} seconds...").format( @@ -289,26 +212,20 @@ class VmShutdownMonitor(QtCore.QObject): self.restart_vm_if_needed() + class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): row_height = 30 column_width = 200 min_visible_rows = 10 - show_inactive_vms = True - show_internal_vms = False search = "" # suppress saving settings while initializing widgets settings_loaded = False - # TODO: does this work columns_indices = {"Type": 0, "Label": 1, "Name": 2, "State": 3, "Template": 4, "NetVM": 5, - # "CPU": 6, # delete - # "CPU Graph": 7, # delete - # "MEM": 8, # delete - # "MEM Graph": 9, # delete "Size": 6, "Internal": 7, "IP": 8, @@ -321,18 +238,11 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.setupUi(self) self.toolbar = self.toolBar - self.manager_settings = QtCore.QSettings() #TODO use Qt settings mechanism + self.manager_settings = QtCore.QSettings(self) - # self.qubes_watch = qubesutils.QubesWatch() self.qvm_collection = qvm_collection - # self.qubes_watch.setup_domain_watch(self.domain_state_changed_callback) - # self.qubes_watch.setup_block_watch(self.blk_manager.block_devs_event) - # self.blk_watch_thread = threading.Thread( - # target=self.qubes_watch.watch_loop) - # self.blk_watch_thread.daemon = True - # self.blk_watch_thread.start() - self.searchbox = SearchBox() #TODO check if this works + self.searchbox = SearchBox() # TODO check if this works self.searchbox.setValidator(QtGui.QRegExpValidator( QtCore.QRegExp("[a-zA-Z0-9-]*", QtCore.Qt.CaseInsensitive), None)) self.searchContainer.addWidget(self.searchbox) @@ -351,8 +261,6 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.vms_list = [] self.vms_in_table = {} self.reload_table = False - self.running_vms_count = 0 - self.internal_vms_count = 0 self.vm_errors = {} self.vm_rec = {} @@ -369,10 +277,6 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.columns_indices["State"]: self.action_state, self.columns_indices["Template"]: self.action_template, self.columns_indices["NetVM"]: self.action_netvm, - # self.columns_indices["CPU"]: self.action_cpu, - # self.columns_indices["CPU Graph"]: self.action_cpu_graph, - # self.columns_indices["MEM"]: self.action_mem, - # self.columns_indices["MEM Graph"]: self.action_mem_graph, self.columns_indices["Size"]: self.action_size_on_disk, self.columns_indices["Internal"]: self.action_internal, self.columns_indices["IP"]: self @@ -383,12 +287,6 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): # TODO: make refresh button self.visible_columns_count = len(self.columns_indices) - # self.table.setColumnHidden(self.columns_indices["CPU"], True) - # self.action_cpu.setChecked(False) - # self.table.setColumnHidden(self.columns_indices["CPU Graph"], True) - # self.action_cpu_graph.setChecked(False) - # self.table.setColumnHidden(self.columns_indices["MEM Graph"], True) - # self.action_mem_graph.setChecked(False) self.table.setColumnHidden(self.columns_indices["Size"], True) self.action_size_on_disk.setChecked(False) self.table.setColumnHidden(self.columns_indices["Internal"], True) @@ -417,11 +315,11 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.context_menu = QtGui.QMenu(self) + # TODO: check if this works, check all options self.context_menu.addAction(self.action_settings) self.context_menu.addAction(self.action_editfwrules) self.context_menu.addAction(self.action_appmenus) self.context_menu.addAction(self.action_set_keyboard_layout) - self.context_menu.addMenu(self.blk_menu) self.context_menu.addAction(self.action_toggle_audio_input) self.context_menu.addSeparator() @@ -463,12 +361,11 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): QtCore.SIGNAL("customContextMenuRequested(const QPoint&)"), lambda pos: self.open_tools_context_menu(self.toolBar, pos)) - self.connect(self.blk_menu, QtCore.SIGNAL("triggered(QAction *)"), - self.attach_dettach_device_triggered) self.connect(self.logs_menu, QtCore.SIGNAL("triggered(QAction *)"), self.show_log) - self.connect(self.searchbox, QtCore.SIGNAL("textChanged(const QString&)"), + self.connect(self.searchbox, + QtCore.SIGNAL("textChanged(const QString&)"), self.do_search) self.table.setContentsMargins(0, 0, 0, 0) @@ -480,13 +377,8 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.connect(self.action_toolbar, QtCore.SIGNAL("toggled(bool)"), self.showhide_toolbar) - self.register_dbus_watches() - self.load_manager_settings() - self.action_showallvms.setChecked(self.show_inactive_vms) - self.action_showinternalvms.setChecked(self.show_internal_vms) - self.fill_table() self.counter = 0 @@ -494,129 +386,39 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.shutdown_monitor = {} self.last_measure_results = {} self.last_measure_time = time.time() - # noinspection PyCallByClass,PyTypeChecker - # QtCore.QTimer.singleShot(self.update_interval, self.update_table) - QubesDbusNotifyServerAdaptor(self) - - def load_manager_settings(self): #TODO: replace with a Qt-based settings gizmo also make it work + def load_manager_settings(self): # visible columns # self.manager_settings.beginGroup("columns") - # for col in self.columns_indices.keys(): - # col_no = self.columns_indices[col] - # visible = self.manager_settings.value( - # col, - # defaultValue=not self.table.isColumnHidden(col_no)) - # self.columns_actions[col_no].setChecked(visible) + for col in self.columns_indices.keys(): + col_no = self.columns_indices[col] + visible = self.manager_settings.value( + 'columns/%s' % col, + defaultValue=not self.table.isColumnHidden(col_no)) + self.columns_actions[col_no].setChecked(visible == "true") # self.manager_settings.endGroup() - # self.show_inactive_vms = self.manager_settings.value( - # "view/show_inactive_vms", defaultValue=False) - # self.show_internal_vms = self.manager_settings.value( - # "view/show_internal_vms", defaultValue=False) - # self.sort_by_column = str( - # self.manager_settings.value("view/sort_column", - # defaultValue=self.sort_by_column)) - # # self.sort_order = QtCore.Qt.SortOrder( #TODO does not seem to work - # # self.manager_settings.value("view/sort_order", - # # defaultValue=self.sort_order)[ - # # 0]) - # self.table.sortItems(self.columns_indices[self.sort_by_column], - # self.sort_order) - # if not self.manager_settings.value("view/menubar_visible", - # defaultValue=True): - # self.action_menubar.setChecked(False) - # if not self.manager_settings.value("view/toolbar_visible", - # defaultValue=True): - # self.action_toolbar.setChecked(False) - # x = self.manager_settings.value('position/x', defaultValue=-1)[ - # 0] - # y = self.manager_settings.value('position/y', defaultValue=-1)[ - # 0] - # if x != -1 or y != -1: - # self.move(x, y) + + self.sort_by_column = str( + self.manager_settings.value("view/sort_column", + defaultValue=self.sort_by_column)) + self.sort_order = QtCore.Qt.SortOrder( + self.manager_settings.value("view/sort_order", + defaultValue=self.sort_order)[ + 0]) + self.table.sortItems(self.columns_indices[self.sort_by_column], + self.sort_order) + if not self.manager_settings.value("view/menubar_visible", + defaultValue=True): + self.action_menubar.setChecked(False) + if not self.manager_settings.value("view/toolbar_visible", + defaultValue=True): + self.action_toolbar.setChecked(False) self.settings_loaded = True def show(self): super(VmManagerWindow, self).show() self.screen_number = app.desktop().screenNumber(self) - # def set_table_geom_size(self): - # - # desktop_width = app.desktop().availableGeometry( - # self).width() - self.frame_width # might be wrong... - # desktop_height = app.desktop().availableGeometry( - # self).height() - self.frame_height # might be wrong... - # desktop_height -= self.row_height # UGLY! to somehow ommit taskbar... - # - # w = self.table.horizontalHeader().length() + \ - # self.table.verticalScrollBar().width() + \ - # 2 * self.table.frameWidth() + 1 - # - # h = self.table.horizontalHeader().height() + \ - # 2 * self.table.frameWidth() - # - # mainwindow_to_add = 0 - # - # available_space = desktop_height - # if self.menubar.isVisible(): - # menubar_height = (self.menubar.sizeHint().height() + - # self.menubar.contentsMargins().top() + - # self.menubar.contentsMargins().bottom()) - # available_space -= menubar_height - # mainwindow_to_add += menubar_height - # if self.toolbar.isVisible(): - # toolbar_height = (self.toolbar.sizeHint().height() + - # self.toolbar.contentsMargins().top() + - # self.toolbar.contentsMargins().bottom()) - # available_space -= toolbar_height - # mainwindow_to_add += toolbar_height - # if w >= desktop_width: - # available_space -= self.table.horizontalScrollBar().height() - # h += self.table.horizontalScrollBar().height() - # - # # account for search box height - # available_space -= self.searchbox.height() - # h += self.searchbox.height() - # - # default_rows = int(available_space / self.row_height) - # - # n = sum(not self.table.isRowHidden(row) for row in - # range(self.table.rowCount())) - # - # if n > default_rows: - # h += default_rows * self.row_height - # self.table.verticalScrollBar().show() - # else: - # h += n * self.row_height - # self.table.verticalScrollBar().hide() - # w -= self.table.verticalScrollBar().width() - # - # w = min(desktop_width, w) - # - # self.centralwidget.setFixedHeight(h) - # - # h += mainwindow_to_add - # - # self.setMaximumHeight(h) - # self.setMinimumHeight(h) - # - # self.table.setFixedWidth(w) - # self.centralwidget.setFixedWidth(w) - # # don't change the following two lines to setFixedWidth! - # self.setMaximumWidth(w) - # self.setMinimumWidth(w) - - def moveEvent(self, event): - super(VmManagerWindow, self).moveEvent(event) - screen_number = app.desktop().screenNumber(self) - if self.screen_number != screen_number: - self.screen_changed = True - self.screen_number = screen_number - if self.settings_loaded: - self.manager_settings.setValue('position/x', self.x()) - self.manager_settings.setValue('position/y', self.y()) - # do not sync for performance reasons - def domain_state_changed_callback(self, name=None, uuid=None): if name is not None: vm = self.qvm_collection.domains[name] @@ -624,30 +426,7 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): vm.refresh() def get_vms_list(self): - # self.qvm_collection.lock_db_for_reading() - # self.qvm_collection.load() - # self.qvm_collection.unlock_db() - - running_count = 0 - internal_count = 0 - - vms_list = [vm for vm in self.qvm_collection.domains] - for vm in vms_list: - # vm.last_power_state = vm.get_power_state() - # vm.last_running = vm.is_running() - # if vm.last_running: - # running_count += 1 - # if vm.internal: - # internal_count += 1 - # vm.qubes_manager_state = {} - # self.update_audio_rec_info(vm) - # vm.qubes_manager_state[QMVmState.ErrorMsg] = self.vm_errors[ - # vm.qid] if vm.qid in self.vm_errors else None - running_count +=1 - - self.running_vms_count = running_count - self.internal_vms_count = internal_count - return vms_list + return [vm for vm in self.qvm_collection.domains] def fill_table(self): # save current selection @@ -684,25 +463,16 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.table.setSortingEnabled(True) self.showhide_vms() - # self.set_table_geom_size() - def showhide_vms(self): - if self.show_inactive_vms and self.show_internal_vms and not self.search: + def showhide_vms(self): # TODO: just show all all the time? + if not self.search: for row_no in range(self.table.rowCount()): self.table.setRowHidden(row_no, False) else: for row_no in range(self.table.rowCount()): widget = self.table.cellWidget(row_no, self.columns_indices["State"]) - running = False - internal = False - # running = widget.vm.last_running - # internal = widget.vm.internal - name = widget.vm.name - - show = (running or self.show_inactive_vms) and \ - (not internal or self.show_internal_vms) and \ - (self.search in widget.vm.name or not self.search) + show = (self.search in widget.vm.name or not self.search) self.table.setRowHidden(row_no, not show) @QtCore.pyqtSlot(str) @@ -721,7 +491,6 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): # When calling update_table() directly, always use out_of_schedule=True! def update_table(self, out_of_schedule=False): - update_devs = self.update_block_devices() or out_of_schedule reload_table = self.reload_table if manager_window.isVisible(): @@ -768,72 +537,20 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.fill_table() update_devs = True - # if not self.show_inactive_vms and \ - # some_vms_have_changed_power_state: - # self.showhide_vms() - # self.set_table_geom_size() - # if self.sort_by_column == \ # "State" and some_vms_have_changed_power_state: - # self.table.sortItems(self.columns_indices[self.sort_by_column], + # self.table.sortItems(self.columns_indices[self.sort_by_column], # self.sort_order) - blk_visible = None - rows_with_blk = None - # if update_devs: - # rows_with_blk = [] - # self.blk_manager.blk_lock.acquire() - # for d in self.blk_manager.attached_devs: - # rows_with_blk.append( - # self.blk_manager.attached_devs[d]['attached_to'][ - # 'vm'].qid) - # self.blk_manager.blk_lock.release() - - if (not self.table.isColumnHidden(self.columns_indices['Size'])) and \ - self.counter % 60 == 0 or out_of_schedule: + if (not self.table.isColumnHidden(self.columns_indices['Size'])) \ + and self.counter % 60 == 0 or out_of_schedule: self.update_size_on_disk = True - if self.counter % 3 == 0 or out_of_schedule: - # (self.last_measure_time, self.last_measure_results) = \ - # qubes_host.measure_cpu_usage(self.qvm_collection, - # self.last_measure_results, - # self.last_measure_time) + for vm_row in self.vms_in_table.values(): + vm_row.update(update_size_on_disk=self.update_size_on_disk) + # TODO: fix these for saner opts TODO2: is it fixed? - for vm_row in self.vms_in_table.values(): - cur_cpu_load = None - # if vm_row.vm.get_xid() in self.last_measure_results: - # cur_cpu_load = self.last_measure_results[vm_row.vm.xid][ - # 'cpu_usage'] - # else: - # cur_cpu_load = 0 - - if rows_with_blk is not None: - if vm_row.vm.qid in rows_with_blk: - blk_visible = True - else: - blk_visible = False - - vm_row.update(blk_visible=blk_visible, - cpu_load=cur_cpu_load, - update_size_on_disk=self.update_size_on_disk, - rec_visible=self.vm_rec.get(vm_row.vm.name, - False)) - - else: - for vm_row in self.vms_in_table.values(): - if rows_with_blk is not None: - if vm_row.vm.qid in rows_with_blk: - blk_visible = True - else: - blk_visible = False - - vm_row.update(blk_visible=blk_visible, - update_size_on_disk=self.update_size_on_disk, - rec_visible=self.vm_rec.get(vm_row.vm.name, - False)) - - if self.sort_by_column in ["CPU", "CPU Graph", "MEM", "MEM Graph", - "State", "Size", "Internal"]: + if self.sort_by_column in ["CPU", "State", "Size", "Internal"]: # "State": needed to sort after reload (fill_table sorts items # with setSortingEnabled, but by that time the widgets values # are not correct yet). @@ -843,36 +560,12 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.table_selection_changed() self.update_size_on_disk = False - if not out_of_schedule: - self.counter += 1 - # noinspection PyCallByClass,PyTypeChecker - # QtCore.QTimer.singleShot(self.update_interval, self.update_table) - - def update_block_devices(self): - pass - # res, msg = self.blk_manager.check_for_updates() - # if msg is not None and len(msg) > 0: - # trayIcon.showMessage('\n'.join(msg), msecs=5000) - # return res # noinspection PyPep8Naming @QtCore.pyqtSlot(bool, str) def recAllowedChanged(self, state, vmname): self.vm_rec[str(vmname)] = bool(state) - def register_dbus_watches(self): - global session_bus - - if not session_bus: - session_bus = QDBusConnection.sessionBus() - - if not session_bus.connect("", # service - "", # path - "org.QubesOS.Audio", # interface - "RecAllowedChanged", # name - self.recAllowedChanged): # slot - print(session_bus.lastError().message()) - # noinspection PyPep8Naming def sortIndicatorChanged(self, column, order): self.sort_by_column = [name for name in self.columns_indices.keys() if @@ -885,18 +578,19 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.manager_settings.sync() def table_selection_changed(self): + # TODO: and this should actually work, fixit vm = self.get_selected_vm() if vm is not None: # Update available actions: self.action_settings.setEnabled(vm.qid != 0) - self.action_removevm.setEnabled( - not vm.installed_by_rpm and not vm.last_running) - self.action_clonevm.setEnabled( - not vm.last_running and not vm.is_netvm()) - # self.action_resumevm.setEnabled(not vm.last_running or - # vm.last_power_state == "Paused") + self.action_removevm.setEnabled(vm.klass != 'AdminVM' + and not vm.is_running()) + # TODO: think about this + self.action_clonevm.setEnabled(vm.klass != 'AdminVM') + self.action_resumevm.setEnabled(vm.get_power_state() == "Paused") + # TODO: check try: pass # self.action_startvm_tools_install.setVisible( @@ -904,37 +598,33 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): except NameError: # ignore non existing QubesHVm pass - self.action_startvm_tools_install.setEnabled(not vm.last_running) - # self.action_pausevm.setEnabled( - # vm.last_running and - # vm.last_power_state != "Paused" and - # vm.qid != 0) - # self.action_shutdownvm.setEnabled( - # vm.last_running and - # vm.last_power_state != "Paused" and - # vm.qid != 0) - # self.action_restartvm.setEnabled( - # vm.last_running and - # vm.last_power_state != "Paused" and - # vm.qid != 0 and - # not vm.is_disposablevm()) - # self.action_killvm.setEnabled((vm.last_running or - # vm.last_power_state == "Paused") and - # vm.qid != 0) - # self.action_appmenus.setEnabled(vm.qid != 0 and - # not vm.internal and - # not vm.is_disposablevm()) - # self.action_editfwrules.setEnabled(vm.is_networked() and not ( - # vm.is_netvm() and not vm.is_proxyvm())) + self.action_startvm_tools_install.setEnabled( + getattr(vm, 'updateable', False)) + # TODO: add qvm boot from device to this and add there windows tools + self.action_pausevm.setEnabled(vm.get_power_state() != "Paused" and + vm.qid != 0) + self.action_shutdownvm.setEnabled(vm.get_power_state() != "Paused" + and vm.qid != 0) + self.action_restartvm.setEnabled(vm.get_power_state() != "Paused" + and vm.qid != 0 + and vm.klass != 'DisposableVM') + self.action_killvm.setEnabled(vm.get_power_state() == "Paused" and + vm.qid != 0) + # TODO: check conditions + self.action_appmenus.setEnabled( + vm.klass != 'AdminVM' and vm.klass != 'DisposableMV' + and not vm.features.get('internal', False)) + self.action_editfwrules.setEnabled(True) # TODO: remove this and make sure the option is enabled in designer + # TODO: this should work # self.action_updatevm.setEnabled(vm.is_updateable() or vm.qid == 0) + # TODO: this should work # self.action_toggle_audio_input.setEnabled( # vm.qubes_manager_state[QMVmState.AudioRecAvailable]) - # self.action_run_command_in_vm.setEnabled( - # not vm.last_power_state == "Paused" and vm.qid != 0) - # self.action_set_keyboard_layout.setEnabled( - # vm.qid != 0 and - # vm.last_running and - # vm.last_power_state != "Paused") + self.action_run_command_in_vm.setEnabled( + not vm.get_power_state() == "Paused" and vm.qid != 0) + self.action_set_keyboard_layout.setEnabled( + vm.qid != 0 and + vm.get_power_state() != "Paused") else: self.action_settings.setEnabled(False) self.action_removevm.setEnabled(False) @@ -953,13 +643,6 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.action_run_command_in_vm.setEnabled(False) self.action_set_keyboard_layout.setEnabled(False) - def closeEvent(self, event): - # There is something borked in Qt, - # as the logic here is inverted on X11 - if event.spontaneous(): - self.hide() - event.ignore() - def set_error(self, qid, message): for vm in self.vms_list: if vm.qid == qid: @@ -981,10 +664,8 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.vm_errors.pop(qid, None) @QtCore.pyqtSlot(name='on_action_createvm_triggered') - def action_createvm_triggered(self): - pass - # dialog = NewVmDlg(app, self.qvm_collection, trayIcon) - # dialog.exec_() + def action_createvm_triggered(self): # TODO: this should work + subprocess.check_call('qubes-vm-create') def get_selected_vm(self): # vm selection relies on the VmInfo widget's value used @@ -1004,7 +685,7 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): return None @QtCore.pyqtSlot(name='on_action_removevm_triggered') - def action_removevm_triggered(self): + def action_removevm_triggered(self): # TODO: this should work vm = self.get_selected_vm() assert not vm.is_running() @@ -1013,25 +694,27 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): vm = self.qvm_collection[vm.qid] if vm.is_template(): - dependent_vms = self.qvm_collection.domains[vm.qid] - if len(dependent_vms) > 0: + dependent_vms = 0 + for single_vm in self.qvm_collection.domains: + if getattr(single_vm, 'template', None) == vm: + dependent_vms += 1 + if dependent_vms > 0: QtGui.QMessageBox.warning( None, self.tr("Warning!"), - self.tr("This Template VM cannot be removed, because there is at " - "least one AppVM that is based on it.
" - "If you want to remove this Template VM and all " - "the AppVMs based on it," - "you should first remove each individual AppVM that uses " - "this template.")) - + self.tr("This Template VM cannot be removed, " + "because there is at least one AppVM that is based " + "on it.
If you want to remove this " + "Template VM and all the AppVMs based on it, you " + "should first remove each individual AppVM that " + "uses this template.")) return (requested_name, ok) = QtGui.QInputDialog.getText( None, self.tr("VM Removal Confirmation"), - self.tr("Are you sure you want to remove the VM '{0}'?
" - "All data on this VM's private storage will be lost!

" - "Type the name of the VM ({1}) below to confirm:") - .format(vm.name, vm.name)) + self.tr("Are you sure you want to remove the VM '{0}'?
" + "All data on this VM's private storage will be lost!" + "

Type the name of the VM ({1}) below to " + "confirm:").format(vm.name, vm.name)) if not ok: # user clicked cancel @@ -1039,15 +722,19 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): elif requested_name != vm.name: # name did not match - QtGui.QMessageBox.warning(None, self.tr("VM removal confirmation failed"), - self.tr("Entered name did not match! Not removing {0}.").format(vm.name)) + QtGui.QMessageBox.warning( + None, + self.tr("VM removal confirmation failed"), + self.tr( + "Entered name did not match! Not removing " + "{0}.").format(vm.name)) return else: # remove the VM t_monitor = thread_monitor.ThreadMonitor() thread = threading.Thread(target=self.do_remove_vm, - args=(vm, t_monitor)) + args=(vm, self.qvm_collection, t_monitor)) thread.daemon = True thread.start() @@ -1057,45 +744,30 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): progress.setModal(True) progress.show() - while not thread_monitor.is_finished(): + while not t_monitor.is_finished(): app.processEvents() time.sleep(0.1) progress.hide() - if thread_monitor.success: - trayIcon.showMessage( - self.tr("VM '{0}' has been removed.").format(vm.name), msecs=3000) + if t_monitor.success: + pass else: QtGui.QMessageBox.warning(None, self.tr("Error removing VM!"), - self.tr("ERROR: {0}").format( - thread_monitor.error_msg)) + self.tr("ERROR: {0}").format( + t_monitor.error_msg)) @staticmethod - def do_remove_vm(vm, thread_monitor): - qc = Qubes() + def do_remove_vm(vm, qvm_collection, t_monitor): try: - vm = qc.domains[vm.qid] - - # TODO: the following two conditions should really be checked - # by qvm_collection.pop() overload... - if vm.is_template() and \ - qc.default_template_qid == vm.qid: - qc.default_template_qid = None - if vm.is_netvm() and \ - qc.default_netvm_qid == vm.qid: - qc.default_netvm_qid = None - - # qc.pop(vm.qid) - # qc.save() - vm.remove_from_disk() + del qvm_collection.domains[vm.name] except Exception as ex: - thread_monitor.set_error_msg(str(ex)) + t_monitor.set_error_msg(str(ex)) - thread_monitor.set_finished() + t_monitor.set_finished() @QtCore.pyqtSlot(name='on_action_clonevm_triggered') - def action_clonevm_triggered(self): + def action_clonevm_triggered(self): # TODO: this should work vm = self.get_selected_vm() name_number = 1 @@ -1112,14 +784,14 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): t_monitor = thread_monitor.ThreadMonitor() thread = threading.Thread(target=self.do_clone_vm, - args=(vm, str(clone_name), t_monitor)) + args=(vm, self.qvm_collection, + clone_name, t_monitor)) thread.daemon = True thread.start() progress = QtGui.QProgressDialog( - self.tr("Cloning VM {0} to {1}...").format(vm.name, - clone_name), "", 0, - 0) + self.tr("Cloning VM {0} to {1}...").format( + vm.name, clone_name), "", 0, 0) progress.setCancelButton(None) progress.setModal(True) progress.show() @@ -1131,48 +803,38 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): progress.hide() if not t_monitor.success: - QtGui.QMessageBox.warning(None, self.tr("Error while cloning VM"), - self.tr("Exception while cloning:
{0}").format( - t_monitor.error_msg)) + QtGui.QMessageBox.warning( + None, + self.tr("Error while cloning VM"), + self.tr("Exception while cloning:
{0}").format( + t_monitor.error_msg)) @staticmethod - def do_clone_vm(vm, dst_name, thread_monitor): + def do_clone_vm(src_vm, qvm_collection, dst_name, t_monitor): dst_vm = None - qc = Qubes() try: - src_vm = qc[vm.qid] - - dst_vm = qc.add_new_vm(src_vm.__class__.__name__, - name=dst_name, - template=src_vm.template, - installed_by_rpm=False) - - dst_vm.clone_attrs(src_vm) - dst_vm.clone_disk_files(src_vm=src_vm, verbose=False) - qc.save() + dst_vm = qvm_collection.clone_vm(src_vm, dst_name) except Exception as ex: if dst_vm: - qc.pop(dst_vm.qid) - dst_vm.remove_from_disk() - thread_monitor.set_error_msg(str(ex)) - thread_monitor.set_finished() + pass # TODO: should I remove any remnants? + t_monitor.set_error_msg(str(ex)) + t_monitor.set_finished() @QtCore.pyqtSlot(name='on_action_resumevm_triggered') def action_resumevm_triggered(self): vm = self.get_selected_vm() - # if vm.get_power_state() in ["Paused", "Suspended"]: - # try: - # vm.resume() - # except Exception as ex: - # QtGui.QMessageBox.warning(None, self.tr("Error unpausing VM!"), - # self.tr("ERROR: {0}").format(ex)) - # return - + if vm.get_power_state() in ["Paused", "Suspended"]: + try: + vm.unpause() + except Exception as ex: + QtGui.QMessageBox.warning(None, self.tr("Error unpausing VM!"), + self.tr("ERROR: {0}").format(ex)) + return self.start_vm(vm) - def start_vm(self, vm): + def start_vm(self, vm): # TODO: this should work assert not vm.is_running() t_monitor = thread_monitor.ThreadMonitor() thread = threading.Thread(target=self.do_start_vm, @@ -1180,22 +842,14 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): thread.daemon = True thread.start() - trayIcon.showMessage(self.tr("Starting '{0}'...").format(vm.name), msecs=3000) - while not t_monitor.is_finished(): app.processEvents() time.sleep(0.1) - if t_monitor.success: - trayIcon.showMessage(self.tr("VM '{0}' has been started.").format(vm.name), - msecs=3000) - else: - trayIcon.showMessage( - self.tr("Error starting VM '{0}': {1}").format( - vm.name, t_monitor.error_msg), - msecs=3000) - self.set_error(vm.qid, - self.tr("Error starting VM: %s") % t_monitor.error_msg) + if not t_monitor.success: + self.set_error( + vm.qid, + self.tr("Error starting VM: %s") % t_monitor.error_msg) @staticmethod def do_start_vm(vm, t_monitor): @@ -1209,6 +863,7 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): t_monitor.set_finished() @QtCore.pyqtSlot(name='on_action_startvm_tools_install_triggered') + # TODO: replace with boot from device def action_startvm_tools_install_triggered(self): vm = self.get_selected_vm() assert not vm.is_running() @@ -1218,49 +873,40 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): if not windows_tools_installed: msg = QtGui.QMessageBox() msg.warning(self, self.tr("Error starting VM!"), - self.tr("You need to install 'qubes-windows-tools' " - "package to use this option")) + self.tr("You need to install 'qubes-windows-tools' " + "package to use this option")) return - t_monitor = thread_monitor.ThreadMonitor() thread = threading.Thread(target=self.do_start_vm_tools_install, args=(vm, t_monitor)) thread.daemon = True thread.start() - trayIcon.showMessage(self.tr("Starting '{0}'...").format(vm.name), msecs=3000) - while not t_monitor.is_finished(): app.processEvents() time.sleep(0.1) - if t_monitor.success: - trayIcon.showMessage(self.tr("VM '{0}' has been started. Start Qubes " - "Tools installation from attached CD") - .format(vm.name), msecs=3000) - else: - trayIcon.showMessage( - self.tr("Error starting VM '{0}': {1}") - .format(vm.name, t_monitor.error_msg), - msecs=3000) - self.set_error(vm.qid, - self.tr("Error starting VM: %s") % t_monitor.error_msg) + if not t_monitor.success: + self.set_error( + vm.qid, + self.tr("Error starting VM: %s") % t_monitor.error_msg) # noinspection PyMethodMayBeStatic - def do_start_vm_tools_install(self, vm, thread_monitor): + def do_start_vm_tools_install(self, vm, t_monitor): + # TODO: should this work? prev_drive = vm.drive try: vm.drive = 'cdrom:dom0:/usr/lib/qubes/qubes-windows-tools.iso' vm.start() except Exception as ex: - thread_monitor.set_error_msg(str(ex)) - thread_monitor.set_finished() + t_monitor.set_error_msg(str(ex)) + t_monitor.set_finished() return finally: vm.drive = prev_drive - thread_monitor.set_finished() + t_monitor.set_finished() @QtCore.pyqtSlot(name='on_action_pausevm_triggered') def action_pausevm_triggered(self): @@ -1269,8 +915,10 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): try: vm.pause() except Exception as ex: - QtGui.QMessageBox.warning(None, self.tr("Error pausing VM!"), - self.tr("ERROR: {0}").format(ex)) + QtGui.QMessageBox.warning( + None, + self.tr("Error pausing VM!"), + self.tr("ERROR: {0}").format(ex)) return @QtCore.pyqtSlot(name='on_action_shutdownvm_triggered') @@ -1278,34 +926,32 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): vm = self.get_selected_vm() assert vm.is_running() - # self.blk_manager.check_if_serves_as_backend(vm) - reply = QtGui.QMessageBox.question( None, self.tr("VM Shutdown Confirmation"), - self.tr("Are you sure you want to power down the VM '{0}'?
" - "This will shutdown all the running applications " - "within this VM.").format(vm.name), - QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) + self.tr("Are you sure you want to power down the VM" + " '{0}'?
This will shutdown all the " + "running applications within this VM.").format( + vm.name), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) - app.processEvents() + app.processEvents() # TODO: is this needed?? if reply == QtGui.QMessageBox.Yes: self.shutdown_vm(vm) def shutdown_vm(self, vm, shutdown_time=vm_shutdown_timeout, - check_time=vm_restart_check_timeout, and_restart=False): + check_time=vm_restart_check_timeout, and_restart=False): try: vm.shutdown() except Exception as ex: - QtGui.QMessageBox.warning(None, self.tr("Error shutting down VM!"), - self.tr("ERROR: {0}").format(ex)) + QtGui.QMessageBox.warning( + None, + self.tr("Error shutting down VM!"), + self.tr("ERROR: {0}").format(ex)) return - trayIcon.showMessage(self.tr("VM '{0}' is shutting down...").format(vm.name), - msecs=3000) - self.shutdown_monitor[vm.qid] = VmShutdownMonitor(vm, shutdown_time, - check_time, and_restart, self) + check_time, + and_restart, self) # noinspection PyCallByClass,PyTypeChecker QtCore.QTimer.singleShot(check_time, self.shutdown_monitor[ vm.qid].check_if_vm_has_shutdown) @@ -1315,13 +961,11 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): vm = self.get_selected_vm() assert vm.is_running() - # self.blk_manager.check_if_serves_as_backend(vm) - reply = QtGui.QMessageBox.question( None, self.tr("VM Restart Confirmation"), self.tr("Are you sure you want to restart the VM '{0}'?
" - "This will shutdown all the running applications " - "within this VM.").format(vm.name), + "This will shutdown all the running applications " + "within this VM.").format(vm.name), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) app.processEvents() @@ -1337,8 +981,9 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): reply = QtGui.QMessageBox.question( None, self.tr("VM Kill Confirmation"), self.tr("Are you sure you want to kill the VM '{0}'?
" - "This will end (not shutdown!) all the running " - "applications within this VM.").format(vm.name), + "This will end (not shutdown!) all the " + "running applications within this VM.").format( + vm.name), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel) @@ -1350,65 +995,26 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): except Exception as ex: QtGui.QMessageBox.critical( None, self.tr("Error while killing VM!"), - self.tr("An exception ocurred while killing {0}.
" - "ERROR: {1}").format(vm.name, ex)) + self.tr( + "An exception ocurred while killing {0}.
" + "ERROR: {1}").format(vm.name, ex)) return - trayIcon.showMessage(self.tr("VM '{0}' killed!") - .format(vm.name), msecs=3000) - @QtCore.pyqtSlot(name='on_action_settings_triggered') def action_settings_triggered(self): - pass - #TODO this should work, actually, but please not now - # vm = self.get_selected_vm() - # settings_window = VMSettingsWindow(vm, app, self.qvm_collection, - # "basic") - # settings_window.exec_() + vm = self.get_selected_vm() + settings_window = settings.VMSettingsWindow(vm, app, "basic") + settings_window.exec_() @QtCore.pyqtSlot(name='on_action_appmenus_triggered') def action_appmenus_triggered(self): pass - #TODO this should work, actually, but please not now + # TODO this should work, actually, but please not now # vm = self.get_selected_vm() # settings_window = VMSettingsWindow(vm, app, self.qvm_collection, # "applications") # settings_window.exec_() - def update_audio_rec_info(self, vm): - vm.qubes_manager_state[QMVmState.AudioRecAvailable] = ( - session_bus.interface().isServiceRegistered( - 'org.QubesOS.Audio.%s' % vm.name).value()) - if vm.qubes_manager_state[QMVmState.AudioRecAvailable]: - vm.qubes_manager_state[ - QMVmState.AudioRecAllowed] = self.get_audio_rec_allowed(vm.name) - else: - vm.qubes_manager_state[QMVmState.AudioRecAllowed] = False - - # noinspection PyMethodMayBeStatic - def get_audio_rec_allowed(self, vmname): - properties = QDBusInterface('org.QubesOS.Audio.%s' % vmname, - '/org/qubesos/audio', - 'org.freedesktop.DBus.Properties', - session_bus) - - current_audio = properties.call('Get', 'org.QubesOS.Audio', - 'RecAllowed') - if current_audio.type() == current_audio.ReplyMessage: - value = current_audio.arguments()[0].toPyObject() - return bool(value) - return False - - @QtCore.pyqtSlot(name='on_action_toggle_audio_input_triggered') - def action_toggle_audio_input_triggered(self): - vm = self.get_selected_vm() - properties = QDBusInterface('org.QubesOS.Audio.%s' % vm.name, - '/org/qubesos/audio', - 'org.freedesktop.DBus.Properties', - session_bus) - properties.call('Set', 'org.QubesOS.Audio', 'RecAllowed', - QDBusVariant(not self.get_audio_rec_allowed(vm.name))) - # icon will be updated based on dbus signal @QtCore.pyqtSlot(name='on_action_updatevm_triggered') def action_updatevm_triggered(self): @@ -1422,8 +1028,6 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) if reply != QtGui.QMessageBox.Yes: return - trayIcon.showMessage(self.tr("Starting '{0}'...").format(vm.name), - msecs=3000) app.processEvents() @@ -1453,16 +1057,13 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): t_monitor.error_msg)) @staticmethod - def do_update_vm(vm, thread_monitor): + def do_update_vm(vm, thread_monitor): #TODO: fixme try: if vm.qid == 0: subprocess.check_call( ["/usr/bin/qubes-dom0-update", "--clean", "--gui"]) else: if not vm.is_running(): - trayIcon.showMessage( - "Starting the '{0}' VM...".format(vm.name), - msecs=3000) vm.start() vm.run_service("qubes.InstallUpdatesGUI", gui=True, user="root", wait=False) @@ -1498,80 +1099,49 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): t_monitor.error_msg)) @staticmethod - def do_run_command_in_vm(vm, command_to_run, thread_monitor): + def do_run_command_in_vm(vm, command_to_run, t_monitor): try: - vm.run(command_to_run, verbose=False, autostart=True, - notify_function=lambda lvl, msg: trayIcon.showMessage( - msg, msecs=3000)) + vm.run(command_to_run, verbose=False, autostart=True) except Exception as ex: - thread_monitor.set_error_msg(str(ex)) - thread_monitor.set_finished() + t_monitor.set_error_msg(str(ex)) + t_monitor.set_finished() @QtCore.pyqtSlot(name='on_action_set_keyboard_layout_triggered') def action_set_keyboard_layout_triggered(self): vm = self.get_selected_vm() vm.run('qubes-change-keyboard-layout', verbose=False) - @QtCore.pyqtSlot(name='on_action_showallvms_triggered') - def action_showallvms_triggered(self): - self.show_inactive_vms = self.action_showallvms.isChecked() - - self.showhide_vms() - # self.set_table_geom_size() - if self.settings_loaded: - self.manager_settings.setValue('view/show_inactive_vms', - self.show_inactive_vms) - self.manager_settings.sync() - - @QtCore.pyqtSlot(name='on_action_showinternalvms_triggered') - def action_showinternalvms_triggered(self): - self.show_internal_vms = self.action_showinternalvms.isChecked() - - self.showhide_vms() - # self.set_table_geom_size() - if self.settings_loaded: - self.manager_settings.setValue('view/show_internal_vms', - self.show_internal_vms) - self.manager_settings.sync() + #TODO: remove showallvms / show inactive vms / show internal vms @QtCore.pyqtSlot(name='on_action_editfwrules_triggered') def action_editfwrules_triggered(self): - pass - #TODO: revive - # vm = self.get_selected_vm() - # settings_window = VMSettingsWindow(vm, app, self.qvm_collection, - # "firewall") - # settings_window.exec_() + vm = self.get_selected_vm() + settings_window = settings.VMSettingsWindow(vm, app, "firewall") + settings_window.exec_() @QtCore.pyqtSlot(name='on_action_global_settings_triggered') def action_global_settings_triggered(self): - pass - #TODO: revive - # global_settings_window = GlobalSettingsWindow(app, self.qvm_collection) - # global_settings_window.exec_() + global_settings_window = global_settings.GlobalSettingsWindow( + app, + self.qvm_collection) + global_settings_window.exec_() @QtCore.pyqtSlot(name='on_action_show_network_triggered') def action_show_network_triggered(self): pass - #TODO: revive + #TODO: revive TODO: what is this thing?? # network_notes_dialog = NetworkNotesDialog() # network_notes_dialog.exec_() @QtCore.pyqtSlot(name='on_action_restore_triggered') def action_restore_triggered(self): - pass - #TODO: revive - # restore_window = RestoreVMsWindow(app, self.qvm_collection, - # self.blk_manager) - # restore_window.exec_() + restore_window = restore.RestoreVMsWindow(app, self.qvm_collection) + restore_window.exec_() @QtCore.pyqtSlot(name='on_action_backup_triggered') def action_backup_triggered(self): - pass - #TODO: revive - # backup_window = BackupVMsWindow(app, self.qvm_collection, - # self.blk_manager, self.shutdown_vm) - # backup_window.exec_() + backup_window = backup.BackupVMsWindow(app, self.qvm_collection) + backup_window.exec_() def showhide_menubar(self, checked): self.menubar.setVisible(checked) @@ -1597,17 +1167,17 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): def showhide_column(self, col_num, show): self.table.setColumnHidden(col_num, not show) - # self.set_table_geom_size() + val = 1 if show else -1 self.visible_columns_count += val - if self.visible_columns_count == 1: + if self.visible_columns_count == 1: # TODO: is this working at all?? # disable hiding the last one for c in self.columns_actions: if self.columns_actions[c].isChecked(): self.columns_actions[c].setEnabled(False) break - elif self.visible_columns_count == 2 and val == 1: + elif self.visible_columns_count == 2 and val == 1: # TODO: likewise?? # enable hiding previously disabled column for c in self.columns_actions: if not self.columns_actions[c].isEnabled(): @@ -1650,18 +1220,6 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): def on_action_netvm_toggled(self, checked): self.showhide_column(self.columns_indices['NetVM'], checked) - def on_action_cpu_toggled(self, checked): - self.showhide_column(self.columns_indices['CPU'], checked) - - def on_action_cpu_graph_toggled(self, checked): - self.showhide_column(self.columns_indices['CPU Graph'], checked) - - def on_action_mem_toggled(self, checked): - self.showhide_column(self.columns_indices['MEM'], checked) - - def on_action_mem_graph_toggled(self, checked): - self.showhide_column(self.columns_indices['MEM Graph'], checked) - def on_action_size_on_disk_toggled(self, checked): self.showhide_column(self.columns_indices['Size'], checked) @@ -1707,48 +1265,6 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): self.logs_menu.setEnabled(not menu_empty) - # blk menu - if not running: - self.blk_menu.setEnabled(False) - else: - self.blk_menu.clear() - self.blk_menu.setEnabled(True) - - # self.blk_manager.blk_lock.acquire() - # if len(self.blk_manager.attached_devs) > 0: - # for d in self.blk_manager.attached_devs: - # if (self.blk_manager.attached_devs[d] - # ['attached_to']['vm'].qid == vm.qid): - # text = self.tr("Detach {dev} {size} {desc}").format( - # dev=d, - # size= - # self.blk_manager.attached_devs[d]['size'], - # desc=self.blk_manager.attached_devs[d]['desc']) - # action = self.blk_menu.addAction(QtGui.QIcon(":/remove.png"), - # text) - # action.setData(QtCore.QVariant(d)) - # - # if len(self.blk_manager.free_devs) > 0: - # for d in self.blk_manager.free_devs: - # if d.startswith(vm.name): - # continue - # # skip partitions heuristic - # if d[-1].isdigit() and \ - # d[0:-1] in self.blk_manager.current_blk: - # continue - # text = self.tr("Attach {dev} {size} {desc}").format( - # dev=d, - # size= - # self.blk_manager.free_devs[d]['size'], - # desc=self.blk_manager.free_devs[d]['desc']) - # action = self.blk_menu.addAction(QtGui.QIcon(":/add.png"), text) - # action.setData(QtCore.QVariant(d)) - # - # self.blk_manager.blk_lock.release() - - if self.blk_menu.isEmpty(): - self.blk_menu.setEnabled(False) - self.context_menu.exec_(self.table.mapToGlobal(point)) @QtCore.pyqtSlot('QAction *') @@ -1759,312 +1275,15 @@ class VmManagerWindow(ui_vtmanager.Ui_VmManagerWindow, QtGui.QMainWindow): # log_dialog = LogDialog(app, log) # log_dialog.exec_() - @QtCore.pyqtSlot('QAction *') - def attach_dettach_device_triggered(self, action): - pass - # dev = str(action.data()) - # vm = self.get_selected_vm() - # - # self.blk_manager.blk_lock.acquire() - # try: - # if dev in self.blk_manager.attached_devs: - # self.blk_manager.detach_device(vm, dev) - # else: - # self.blk_manager.attach_device(vm, dev) - # self.blk_manager.blk_lock.release() - # except exc.QubesException as e: - # self.blk_manager.blk_lock.release() - # QtGui.QMessageBox.critical(None, - # self.tr("Block attach/detach error!"), str(e)) - - -class QubesTrayIcon(QtGui.QSystemTrayIcon): - def __init__(self, icon): - QtGui.QSystemTrayIcon.__init__(self, icon) - self.menu = QtGui.QMenu() - - action_showmanager = self.create_action( - self.tr("Open VM Manager"), - slot=show_manager, icon="qubes") - # action_copy = self.create_action( - # self.tr("Copy Dom0 clipboard"), icon="copy", - # slot=do_dom0_copy) - action_backup = self.create_action(self.tr("Make backup")) - action_preferences = self.create_action(self.tr("Preferences")) - action_set_netvm = self.create_action(self.tr("Set default NetVM"), - icon="networking") - action_sys_info = self.create_action(self.tr("System Info"), icon="dom0") - action_exit = self.create_action(self.tr("Exit"), slot=exit_app) - - action_backup.setDisabled(True) - action_preferences.setDisabled(True) - action_set_netvm.setDisabled(True) - action_sys_info.setDisabled(True) - - # self.blk_manager = blk_manager - - self.blk_menu = QtGui.QMenu(self.menu) - self.blk_menu.setTitle(self.tr("Block devices")) - action_blk_menu = self.create_action(self.tr("Block devices")) - action_blk_menu.setMenu(self.blk_menu) - - self.add_actions(self.menu, (action_showmanager, - # action_copy, - action_blk_menu, - action_backup, - action_sys_info, - None, - action_preferences, - action_set_netvm, - None, - action_exit)) - - self.setContextMenu(self.menu) - - # self.connect(self, - # QtCore.SIGNAL("activated (QtGui.QSystemTrayIcon::ActivationReason)"), - # self.icon_clicked) - - self.tray_notifier_type = None - self.tray_notifier = QDBusInterface("org.freedesktop.Notifications", - "/org/freedesktop/Notifications", - "org.freedesktop.Notifications", - session_bus) - srv_info = self.tray_notifier.call("GetServerInformation") - if srv_info.type() == QDBusMessage.ReplyMessage and \ - len(srv_info.arguments()) > 1: - self.tray_notifier_type = srv_info.arguments()[1] - - if os.path.exists(table_widgets.qubes_dom0_updates_stat_file): - self.showMessage(self.tr("Qubes dom0 updates available."), msecs=0) - - def update_blk_menu(self): - global manager_window - - def create_vm_submenu(dev): - blk_vm_menu = QtGui.QMenu(self.blk_menu) - blk_vm_menu.triggered.connect( - lambda a, trig_dev=dev: self.attach_device_triggered(a, - trig_dev)) - for this_vm in sorted(manager_window.qvm_collection.domains, - key=lambda x: x.name): - if not this_vm.is_running(): - continue - if this_vm.qid == 0: - # skip dom0 to prevent (fatal) mistakes - continue - this_action = blk_vm_menu.addAction(QtGui.QIcon(":/add.png"), - this_vm.name) - this_action.setData(QtCore.QVariant(this_vm)) - return blk_vm_menu - - self.blk_menu.clear() - self.blk_menu.setEnabled(True) - - # self.blk_manager.blk_lock.acquire() - # if len(self.blk_manager.attached_devs) > 0: - # for d in self.blk_manager.attached_devs: - # vm = self.blk_manager.attached_devs[d]['attached_to']['vm'] - # text = self.tr("Detach {dev} {desc} ({size}) from {vm}").format( - # dev=d, - # desc=self.blk_manager.attached_devs[d]['desc'], - # size=self.blk_manager.attached_devs[d]['size'], - # vm=vm.name) - # action = self.blk_menu.addAction(QtGui.QIcon(":/remove.png"), text) - # action.setData(QtCore.QVariant(d)) - # action.triggered.connect( - # lambda b, a=action: self.dettach_device_triggered(a)) - # - # if len(self.blk_manager.free_devs) > 0: - # for d in self.blk_manager.free_devs: - # # skip partitions heuristic - # if d[-1].isdigit() and d[0:-1] in self.blk_manager.current_blk: - # continue - # text = self.tr("Attach {dev} {size} {desc}").format( - # dev=d, - # size=self.blk_manager.free_devs[d]['size'], - # desc=self.blk_manager.free_devs[d]['desc'] - # ) - # action = self.blk_menu.addAction(QtGui.QIcon(":/add.png"), text) - # action.setMenu(create_vm_submenu(d)) - # - # self.blk_manager.blk_lock.release() - - if self.blk_menu.isEmpty(): - self.blk_menu.setEnabled(False) - - @QtCore.pyqtSlot('QAction *') - def attach_device_triggered(self, action, dev): - pass - # vm = action.data().toPyObject() - # - # self.blk_manager.blk_lock.acquire() - # try: - # self.blk_manager.attach_device(vm, dev) - # self.blk_manager.blk_lock.release() - # except exc.QubesException as e: - # self.blk_manager.blk_lock.release() - # QtGui.QMessageBox.critical(None, - # self.tr("Block attach/detach error!"), str(e)) - - @QtCore.pyqtSlot('QAction *') - def dettach_device_triggered(self, action): - pass - # dev = str(action.data()) - # vm = self.blk_manager.attached_devs[dev]['attached_to']['vm'] - # - # self.blk_manager.blk_lock.acquire() - # try: - # self.blk_manager.detach_device(vm, dev) - # self.blk_manager.blk_lock.release() - # except exc.QubesException as e: - # self.blk_manager.blk_lock.release() - # QtGui.QMessageBox.critical(None, - # self.tr("Block attach/detach error!"), str(e)) - - def icon_clicked(self, reason): - if reason == QtGui.QSystemTrayIcon.Context: - self.update_blk_menu() - # Handle the right click normally, i.e. display the context menu - return - else: - bring_manager_to_front() - - # noinspection PyMethodMayBeStatic - def add_actions(self, target, actions): - for action in actions: - if action is None: - target.addSeparator() - else: - target.addAction(action) - - def showMessage(self, message, msecs, **kwargs): - # QtDBus bindings doesn't use introspection to get proper method - # parameters types, so must cast explicitly - # v_replace_id = QtCore.QVariant(0) - # v_replace_id.convert(QtCore.QVariant.UInt) - # v_actions = QtCore.QVariant([]) - # v_actions.convert(QtCore.QVariant.StringList) - # if self.tray_notifier_type == "KDE": - # message = message.replace('\n', '
\n') - # self.tray_notifier.call("Notify", "Qubes", v_replace_id, - # "qubes-manager", "Qubes VM Manager", - # message, v_actions, QtCore.QVariant.fromMap({}), msecs) - pass - - def create_action(self, text, slot=None, shortcut=None, icon=None, - tip=None, checkable=False, signal="triggered()"): - action = QtGui.QAction(text, self) - if icon is not None: - action.setIcon(QtGui.QIcon(":/%s.png" % icon)) - if shortcut is not None: - action.setShortcut(shortcut) - if tip is not None: - action.setToolTip(tip) - action.setStatusTip(tip) - if slot is not None: - self.connect(action, QtCore.SIGNAL(signal), slot) - if checkable: - action.setCheckable(True) - return action - - -class QubesDbusNotifyServerAdaptor(QDBusAbstractAdaptor): - """ This provides the DBus adaptor to the outside world""" - - # Q_CLASSINFO("D-Bus Interface", dbus_interface) - - @QtCore.pyqtSlot(str, str) - def notify_error(self, vmname, message): - vm = self.parent().qvm_collection.domains[vmname] - if vm: - self.parent().set_error(vm.qid, message) - else: - # ignore VM-not-found error - pass - - @QtCore.pyqtSlot(str, str) - def clear_error_exact(self, vmname, message): - vm = self.parent().qvm_collection.domains[vmname] - if vm: - self.parent().clear_error_exact(vm.qid, message) - else: - # ignore VM-not-found error - pass - - @QtCore.pyqtSlot(str) - def clear_error(self, vmname): - vm = self.parent().qvm_collection.domains[vmname] - if vm: - self.parent().clear_error(vm.qid) - else: - # ignore VM-not-found error - pass - - @QtCore.pyqtSlot() - def show_manager(self): - bring_manager_to_front() - - -# def get_frame_size(): -# w = 0 -# h = 0 -# cmd = ['/usr/bin/xprop', '-name', 'Qubes VM Manager', '|', 'grep', -# '_NET_FRAME_EXTENTS'] -# xprop = subprocess.Popen(cmd, stdout=subprocess.PIPE) -# for l in xprop.stdout: -# line = l.split('=') -# if len(line) == 2: -# line = line[1].strip().split(',') -# if len(line) == 4: -# w = int(line[0].strip()) + int(line[1].strip()) -# h = int(line[2].strip()) + int(line[3].strip()) -# break -# # in case of some weird window managers we have to assume sth... -# if w <= 0: -# w = 10 -# if h <= 0: -# h = 30 -# -# manager_window.frame_width = w -# manager_window.frame_height = h -# return - def show_manager(): manager_window.show() - # manager_window.set_table_geom_size() manager_window.repaint() manager_window.update_table(out_of_schedule=True) app.processEvents() - # get_frame_size() - # print manager_window.frame_width, " x ", manager_window.frame_height - # manager_window.set_table_geom_size() - - -def bring_manager_to_front(): - if manager_window.isVisible(): - subprocess.check_call( - ['/usr/bin/wmctrl', '-R', str(manager_window.windowTitle())]) - - else: - show_manager() - - -def show_running_manager_via_dbus(): - global system_bus - if system_bus is None: - system_bus = QDBusConnection.systemBus() - - qubes_manager = QDBusInterface('org.qubesos.QubesManager', - '/org/qubesos/QubesManager', - 'org.qubesos.QubesManager', system_bus) - qubes_manager.call('show_manager') - def exit_app(): - # notifier.stop() app.exit() @@ -2093,42 +1312,17 @@ def sighup_handler(signum, frame): def main(): signal.signal(signal.SIGHUP, sighup_handler) - global system_bus - system_bus = QDBusConnection.systemBus() - # Avoid starting more than one instance of the app - # if not system_bus.registerService('org.qubesos.QubesManager'): - # show_running_manager_via_dbus() - # return - - # global qubes_host - # qubes_host = QubesHost() - global app app = QtGui.QApplication(sys.argv) app.setOrganizationName("The Qubes Project") app.setOrganizationDomain("http://qubes-os.org") app.setApplicationName("Qubes VM Manager") app.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager")) - # app.setAttribute(Qt.AA_DontShowIconsInMenus, False) - - # qt_translator = QTranslator() - # locale = QLocale.system().name() - # i18n_dir = os.path.join( - # os.path.dirname(os.path.realpath(__file__)), - # 'i18n') - # qt_translator.load("qubesmanager_{!s}.qm".format(locale), i18n_dir) - # app.installTranslator(qt_translator) sys.excepthook = handle_exception - global session_bus - session_bus = QDBusConnection.sessionBus() - qvm_collection = Qubes() - global trayIcon - trayIcon = QubesTrayIcon(QtGui.QIcon.fromTheme("qubes-manager")) - global manager_window manager_window = VmManagerWindow(qvm_collection) @@ -2150,16 +1344,13 @@ def main(): # wm.add_watch(os.path.dirname(table_widgets.qubes_dom0_updates_stat_file), # EventsCodes.OP_FLAGS.get('IN_CREATE')) - system_bus.registerObject(dbus_object_path, manager_window) - threading.currentThread().setName("QtMainThread") - trayIcon.show() show_manager() app.exec_() - trayIcon = None - if __name__ == "__main__": main() + +# TODO: change file name to something better \ No newline at end of file diff --git a/qubesmanager/table_widgets.py b/qubesmanager/table_widgets.py index fe2cd49..9acc05e 100644 --- a/qubesmanager/table_widgets.py +++ b/qubesmanager/table_widgets.py @@ -23,7 +23,6 @@ from PyQt4 import QtGui from PyQt4 import QtCore # TODO: are those needed? -qubes_dom0_updates_stat_file = '/var/lib/qubes/updates/dom0-updates-available' power_order = QtCore.Qt.DescendingOrder update_order = QtCore.Qt.AscendingOrder @@ -31,7 +30,6 @@ update_order = QtCore.Qt.AscendingOrder row_height = 30 -# TODO: do I need to find icons? class VmIconWidget(QtGui.QWidget): def __init__(self, icon_path, enabled=True, size_multiplier=0.7, tooltip=None, parent=None, icon_sz=(32, 32)): @@ -81,7 +79,7 @@ class VmTypeWidget(VmIconWidget): elif other.vm.qid == 0: return False elif self.value == other.value: - return self.vm.qid < other.vm.qid + return self.vm.name < other.vm.name else: return self.value < other.value @@ -137,7 +135,7 @@ class VmLabelWidget(VmIconWidget): elif other.vm.qid == 0: return False elif self.value == other.value: - return self.vm.qid < other.vm.qid + return self.vm.name < other.vm.name else: return self.value < other.value @@ -217,14 +215,14 @@ class VmInfoWidget (QtGui.QWidget): self_val = self.upd_info_item.value other_val = other.upd_info_item.value - # TODO: is this shit needed? + if self.tableWidget().\ horizontalHeader().sortIndicatorOrder() == update_order: # the result will be sorted by upd, sorting order: Ascending self_val += 1 if self.vm.is_running() else 0 other_val += 1 if other.vm.is_running() else 0 if self_val == other_val: - return self.vm.qid < other.vm.qid + return self.vm.name < other.vm.name else: return self_val > other_val elif self.tableWidget().\ @@ -236,7 +234,7 @@ class VmInfoWidget (QtGui.QWidget): other_val = -(other_val/10 + 10*(1 if other.vm.is_running() else 0)) if self_val == other_val: - return self.vm.qid < other.vm.qid + return self.vm.name < other.vm.name else: return self_val > other_val else: @@ -272,19 +270,10 @@ class VmInfoWidget (QtGui.QWidget): self.tableItem = self.VmInfoItem(self.upd_info.tableItem, vm) - def update_vm_state(self, vm, blk_visible, rec_visible=None): + def update_vm_state(self, vm): self.on_icon.update() self.upd_info.update_outdated(vm) - if blk_visible is not None: - self.blk_icon.setVisible(blk_visible) - if rec_visible is not None: - self.rec_icon.setVisible(rec_visible) - # TODO: are these needed? - # self.error_icon.setToolTip(vm.qubes_manager_state[main.QMVmState - # .ErrorMsg]) - # self.error_icon.setVisible(vm.qubes_manager_state[main.QMVmState - # .ErrorMsg] is not None) - + # TODO: add updating things like label? name? evrything? size? # TODO add main to git history as a saner name and with a decent comment # TODO and rename that shit @@ -294,7 +283,7 @@ class VmTemplateItem (QtGui.QTableWidgetItem): self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.vm = vm - if vm.template is not None: + if getattr(vm, 'template', None) is not None: self.setText(vm.template.name) else: font = QtGui.QFont() @@ -312,7 +301,7 @@ class VmTemplateItem (QtGui.QTableWidgetItem): elif other.vm.qid == 0: return False elif self.text() == other.text(): - return self.vm.qid < other.vm.qid + return self.vm.name < other.vm.name else: return super(VmTemplateItem, self).__lt__(other) @@ -325,7 +314,7 @@ class VmNetvmItem (QtGui.QTableWidgetItem): # TODO: differentiate without no net vm/ no networking? # TODO: mark provides network somehow? - if vm.netvm is None: + if getattr(vm, 'netvm', None) is None: self.setText("n/a") else: self.setText(vm.netvm.name) @@ -338,7 +327,7 @@ class VmNetvmItem (QtGui.QTableWidgetItem): elif other.vm.qid == 0: return False elif self.text() == other.text(): - return self.vm.qid < other.vm.qid + return self.vm.name < other.vm.name else: return super(VmNetvmItem, self).__lt__(other) @@ -350,7 +339,6 @@ class VmInternalItem(QtGui.QTableWidgetItem): self.vm = vm self.internal = vm.features.get('internal', False) - # TODO: should default be false self.setText("Yes" if self.internal else "") @@ -386,7 +374,7 @@ class VmUpdateInfoWidget(QtGui.QWidget): elif other.vm.qid == 0: return False elif self.value == other.value: - return self.vm.qid < other.vm.qid + return self.vm.name < other.vm.name else: return self.value < other.value @@ -419,7 +407,8 @@ class VmUpdateInfoWidget(QtGui.QWidget): except AttributeError: pass - if not outdated_state and vm.template and vm.template.is_running(): + if not outdated_state and getattr(vm, 'template', None)\ + and vm.template.is_running(): outdated_state = "to-be-outdated" if outdated_state != self.previous_outdated_state: self.update_status_widget(outdated_state) @@ -491,24 +480,19 @@ class VmSizeOnDiskItem (QtGui.QTableWidgetItem): elif other.vm.qid == 0: return False elif self.value == other.value: - return self.vm.qid < other.vm.qid + return self.vm.name < other.vm.name else: return self.value < other.value - +# TODO: replace these widgets with a generic widgets class VmIPItem(QtGui.QTableWidgetItem): def __init__(self, vm): super(VmIPItem, self).__init__() self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - # TODO: check if you don't need a try around here self.vm = vm - self.ip = self.vm.ip - if self.ip: - self.setText(self.ip) - else: - self.setText("n/a") - self.setText("n/a") + self.ip = getattr(self.vm, 'ip', None) + self.setText(self.ip if self.ip is not None else 'n/a') def __lt__(self, other): if self.vm.qid == 0: @@ -524,7 +508,7 @@ class VmIncludeInBackupsItem(QtGui.QTableWidgetItem): self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.vm = vm - if self.vm.include_in_backups: + if getattr(self.vm, 'include_in_backups', None): self.setText("Yes") else: self.setText("") @@ -536,7 +520,7 @@ class VmIncludeInBackupsItem(QtGui.QTableWidgetItem): elif other.vm.qid == 0: return False elif self.vm.include_in_backups == other.vm.include_in_backups: - return self.vm.qid < other.vm.qid + return self.vm.name < other.vm.name else: return self.vm.include_in_backups < other.vm.include_in_backups @@ -547,7 +531,7 @@ class VmLastBackupItem(QtGui.QTableWidgetItem): self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.vm = vm - if self.vm.backup_timestamp: + if getattr(self.vm, 'backup_timestamp', None): self.setText(self.vm.backup_timestamp) else: self.setText("") @@ -559,7 +543,7 @@ class VmLastBackupItem(QtGui.QTableWidgetItem): elif other.vm.qid == 0: return False elif self.vm.backup_timestamp == other.vm.backup_timestamp: - return self.vm.qid < other.vm.qid + return self.vm.name < other.vm.name elif not self.vm.backup_timestamp: return False elif not other.vm.backup_timestamp: diff --git a/rpm_spec/qmgr.spec b/rpm_spec/qmgr.spec index 6ee0f64..27ed4e5 100644 --- a/rpm_spec/qmgr.spec +++ b/rpm_spec/qmgr.spec @@ -64,6 +64,7 @@ rm -rf $RPM_BUILD_ROOT /usr/bin/qubes-vm-boot-from-device /usr/bin/qubes-backup /usr/bin/qubes-backup-restore +/usr/bin/qubes-template-volume-manager /usr/libexec/qubes-manager/mount_for_backup.sh /usr/libexec/qubes-manager/qvm_about.sh @@ -87,6 +88,7 @@ rm -rf $RPM_BUILD_ROOT %{python3_sitelib}/qubesmanager/informationnotes.py %{python3_sitelib}/qubesmanager/create_new_vm.py %{python3_sitelib}/qubesmanager/thread_monitor.py +%{python3_sitelib}/qubesmanager/main.py %{python3_sitelib}/qubesmanager/utils.py %{python3_sitelib}/qubesmanager/bootfromdevice.py @@ -104,6 +106,7 @@ rm -rf $RPM_BUILD_ROOT %{python3_sitelib}/qubesmanager/ui_about.py %{python3_sitelib}/qubesmanager/ui_releasenotes.py %{python3_sitelib}/qubesmanager/ui_informationnotes.py +%{python3_sitelib}/qubesmanager/ui_vtmanager.py %{python3_sitelib}/qubesmanager/i18n/qubesmanager_*.qm %{python3_sitelib}/qubesmanager/i18n/qubesmanager_*.ts diff --git a/setup.py b/setup.py index c89c898..940e99b 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ if __name__ == '__main__': 'qubes-vm-create = qubesmanager.create_new_vm:main', 'qubes-vm-boot-from-device = qubesmanager.bootfromdevice:main', 'qubes-backup = qubesmanager.backup:main', - 'qubes-backup-restore = qubesmanager.restore:main' + 'qubes-backup-restore = qubesmanager.restore:main', + 'qubes-template-volume-manager = qubesmanager.main:main' ], }) diff --git a/ui/vtmanager.ui b/ui/vtmanager.ui index 4e89f45..03932fd 100644 --- a/ui/vtmanager.ui +++ b/ui/vtmanager.ui @@ -136,7 +136,7 @@ 10 - 15 + 11 false @@ -209,32 +209,6 @@ VM's netVM - - - CPU - - - - - CPU Graph - - - CPU usage graph - - - - - MEM - - - - - MEM Graph - - - Memory usage graph - - Size @@ -295,10 +269,6 @@ - - - -