Make Qube Manager resistant to missing permissions
It should no longer crash if policy denies it access to some information. The only required information is vm name, qid and class. references QubesOS/qubes-issues#5811
This commit is contained in:
parent
7fc8c2e9e0
commit
1f933b775a
@ -199,23 +199,21 @@ class VmInfo():
|
|||||||
self.vm = vm
|
self.vm = vm
|
||||||
self.qid = vm.qid
|
self.qid = vm.qid
|
||||||
self.name = self.vm.name
|
self.name = self.vm.name
|
||||||
self.label = self.vm.label
|
|
||||||
self.klass = self.vm.klass
|
self.label = getattr(self.vm, 'label', None)
|
||||||
|
self.klass = getattr(self.vm, 'klass', None)
|
||||||
self.state = {'power': "", 'outdated': ""}
|
self.state = {'power': "", 'outdated': ""}
|
||||||
self.updateable = getattr(vm, 'updateable', False)
|
self.updateable = getattr(vm, 'updateable', False)
|
||||||
self.update(True)
|
self.update(True)
|
||||||
|
|
||||||
def update(self, update_size_on_disk=False, event=None):
|
def update_power_state(self):
|
||||||
"""
|
|
||||||
Update VmInfo
|
|
||||||
:param update_size_on_disk: should disk utilization be updated?
|
|
||||||
:param event: name of the event that caused the update, to avoid
|
|
||||||
updating unnecessary properties; if event is none, update everything
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
self.state['power'] = self.vm.get_power_state()
|
self.state['power'] = self.vm.get_power_state()
|
||||||
|
except exc.QubesPropertyAccessError:
|
||||||
|
self.state['power'] = ""
|
||||||
|
|
||||||
|
self.state['outdated'] = ""
|
||||||
|
try:
|
||||||
if self.vm.is_running():
|
if self.vm.is_running():
|
||||||
if hasattr(self.vm, 'template') and \
|
if hasattr(self.vm, 'template') and \
|
||||||
self.vm.template.is_running():
|
self.vm.template.is_running():
|
||||||
@ -225,64 +223,89 @@ class VmInfo():
|
|||||||
if vol.is_outdated():
|
if vol.is_outdated():
|
||||||
self.state['outdated'] = "outdated"
|
self.state['outdated'] = "outdated"
|
||||||
break
|
break
|
||||||
else:
|
|
||||||
self.state['outdated'] = ""
|
|
||||||
|
|
||||||
if self.vm.klass in {'TemplateVM', 'StandaloneVM'} and \
|
if self.vm.klass in {'TemplateVM', 'StandaloneVM'} and \
|
||||||
self.vm.features.get('updates-available', False):
|
self.vm.features.get('updates-available', False):
|
||||||
self.state['outdated'] = 'update'
|
self.state['outdated'] = 'update'
|
||||||
|
except exc.QubesPropertyAccessError:
|
||||||
|
pass
|
||||||
|
|
||||||
if not event or event.endswith(':label'):
|
def update(self, update_size_on_disk=False, event=None):
|
||||||
self.label = self.vm.label
|
"""
|
||||||
if not event or event.endswith(':template'):
|
Update VmInfo
|
||||||
try:
|
:param update_size_on_disk: should disk utilization be updated?
|
||||||
self.template = self.vm.template.name
|
:param event: name of the event that caused the update, to avoid
|
||||||
except AttributeError:
|
updating unnecessary properties; if event is none, update everything
|
||||||
self.template = None
|
:return: None
|
||||||
if not event or event.endswith(':netvm'):
|
"""
|
||||||
self.netvm = getattr(self.vm, 'netvm', None)
|
self.update_power_state()
|
||||||
if self.netvm:
|
|
||||||
self.netvm = self.netvm.name
|
if not event or event.endswith(':label'):
|
||||||
else:
|
self.label = getattr(self.vm, 'label', None)
|
||||||
self.netvm = "n/a"
|
|
||||||
if self.qid != 0 and self.vm.property_is_default("netvm"):
|
if not event or event.endswith(':template'):
|
||||||
|
try:
|
||||||
|
self.template = self.vm.template.name
|
||||||
|
except AttributeError:
|
||||||
|
self.template = None
|
||||||
|
|
||||||
|
if not event or event.endswith(':netvm'):
|
||||||
|
self.netvm = getattr(self.vm, 'netvm', None)
|
||||||
|
if self.netvm:
|
||||||
|
self.netvm = str(self.netvm)
|
||||||
|
else:
|
||||||
|
self.netvm = "n/a"
|
||||||
|
try:
|
||||||
|
if hasattr(self.vm, 'netvm') \
|
||||||
|
and self.vm.property_is_default("netvm"):
|
||||||
self.netvm = "default (" + self.netvm + ")"
|
self.netvm = "default (" + self.netvm + ")"
|
||||||
if not event or event.endswith(':internal'):
|
except exc.QubesPropertyAccessError:
|
||||||
# this is a feature, not a property; TODO: fix event handling
|
pass
|
||||||
|
|
||||||
|
if not event or event.endswith(':internal'):
|
||||||
|
try:
|
||||||
self.internal = self.vm.features.get('internal', False)
|
self.internal = self.vm.features.get('internal', False)
|
||||||
if not event or event.endswith(':ip'):
|
except exc.QubesPropertyAccessError:
|
||||||
self.ip = getattr(self.vm, 'ip', "n/a")
|
self.internal = False
|
||||||
if not event or event.endswith(':include_in_backups'):
|
|
||||||
self.inc_backup = getattr(self.vm, 'include_in_backups', None)
|
if not event or event.endswith(':ip'):
|
||||||
if not event or event.endswith(':backup_timestamp'):
|
self.ip = getattr(self.vm, 'ip', "n/a")
|
||||||
self.last_backup = getattr(self.vm, 'backup_timestamp', None)
|
|
||||||
if self.last_backup:
|
if not event or event.endswith(':include_in_backups'):
|
||||||
self.last_backup = str(datetime.fromtimestamp(
|
self.inc_backup = getattr(self.vm, 'include_in_backups', None)
|
||||||
self.last_backup))
|
|
||||||
if not event or event.endswith(':default_dispvm'):
|
if not event or event.endswith(':backup_timestamp'):
|
||||||
self.dvm = getattr(self.vm, 'default_dispvm', None)
|
self.last_backup = getattr(self.vm, 'backup_timestamp', None)
|
||||||
|
if self.last_backup:
|
||||||
|
self.last_backup = str(datetime.fromtimestamp(self.last_backup))
|
||||||
|
|
||||||
|
if not event or event.endswith(':default_dispvm'):
|
||||||
|
self.dvm = getattr(self.vm, 'default_dispvm', None)
|
||||||
|
try:
|
||||||
if self.vm.property_is_default("default_dispvm"):
|
if self.vm.property_is_default("default_dispvm"):
|
||||||
self.dvm = "default (" + str(self.dvm) + ")"
|
self.dvm = "default (" + str(self.dvm) + ")"
|
||||||
elif self.dvm is not None:
|
elif self.dvm is not None:
|
||||||
self.dvm = self.dvm.name
|
self.dvm = str(self.dvm)
|
||||||
if not event or event.endswith(':template_for_dispvms'):
|
except exc.QubesPropertyAccessError:
|
||||||
self.dvm_template = getattr(self.vm, 'template_for_dispvms',
|
self.dvm = None
|
||||||
None)
|
|
||||||
if self.qid != 0 and update_size_on_disk:
|
if not event or event.endswith(':template_for_dispvms'):
|
||||||
|
self.dvm_template = getattr(self.vm, 'template_for_dispvms', None)
|
||||||
|
|
||||||
|
if self.vm.klass != 'AdminVM' and update_size_on_disk:
|
||||||
|
try:
|
||||||
self.disk_float = float(self.vm.get_disk_utilization())
|
self.disk_float = float(self.vm.get_disk_utilization())
|
||||||
self.disk = str(round(self.disk_float/(1024*1024), 2)) + " MiB"
|
self.disk = str(round(self.disk_float/(1024*1024), 2)) + " MiB"
|
||||||
|
except exc.QubesPropertyAccessError:
|
||||||
|
self.disk_float = None
|
||||||
|
self.disk = None
|
||||||
|
|
||||||
|
if self.vm.klass != 'AdminVM':
|
||||||
|
self.virt_mode = getattr(self.vm, 'virt_mode', None)
|
||||||
|
else:
|
||||||
|
self.virt_mode = None
|
||||||
|
self.disk = "n/a"
|
||||||
|
|
||||||
if self.qid != 0:
|
|
||||||
self.virt_mode = self.vm.virt_mode
|
|
||||||
else:
|
|
||||||
self.virt_mode = None
|
|
||||||
self.disk = "n/a"
|
|
||||||
except exc.QubesPropertyAccessError:
|
|
||||||
pass
|
|
||||||
except exc.QubesDaemonNoResponseError:
|
|
||||||
# TODO: this will be fixed by a rewrite moving the event system to
|
|
||||||
# AdminAPI
|
|
||||||
pass
|
|
||||||
|
|
||||||
class QubesCache(QAbstractTableModel):
|
class QubesCache(QAbstractTableModel):
|
||||||
def __init__(self, qubes_app):
|
def __init__(self, qubes_app):
|
||||||
@ -314,6 +337,7 @@ class QubesCache(QAbstractTableModel):
|
|||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self._info_list)
|
return iter(self._info_list)
|
||||||
|
|
||||||
|
|
||||||
class QubesTableModel(QAbstractTableModel):
|
class QubesTableModel(QAbstractTableModel):
|
||||||
def __init__(self, qubes_cache):
|
def __init__(self, qubes_cache):
|
||||||
QAbstractTableModel.__init__(self)
|
QAbstractTableModel.__init__(self)
|
||||||
@ -398,6 +422,8 @@ class QubesTableModel(QAbstractTableModel):
|
|||||||
pixmap.load(icon_name)
|
pixmap.load(icon_name)
|
||||||
self.klass_pixmap[vm.klass] = pixmap.scaled(icon_size)
|
self.klass_pixmap[vm.klass] = pixmap.scaled(icon_size)
|
||||||
return self.klass_pixmap[vm.klass]
|
return self.klass_pixmap[vm.klass]
|
||||||
|
except exc.QubesPropertyAccessError:
|
||||||
|
return None
|
||||||
|
|
||||||
if col_name == "Label":
|
if col_name == "Label":
|
||||||
try:
|
try:
|
||||||
@ -406,6 +432,8 @@ class QubesTableModel(QAbstractTableModel):
|
|||||||
icon = QIcon.fromTheme(vm.label.icon)
|
icon = QIcon.fromTheme(vm.label.icon)
|
||||||
self.label_pixmap[vm.label] = icon.pixmap(icon_size)
|
self.label_pixmap[vm.label] = icon.pixmap(icon_size)
|
||||||
return self.label_pixmap[vm.label]
|
return self.label_pixmap[vm.label]
|
||||||
|
except exc.QubesPropertyAccessError:
|
||||||
|
return None
|
||||||
|
|
||||||
if role == Qt.FontRole:
|
if role == Qt.FontRole:
|
||||||
if col_name == "Template":
|
if col_name == "Template":
|
||||||
@ -425,12 +453,12 @@ class QubesTableModel(QAbstractTableModel):
|
|||||||
|
|
||||||
# Used for sorting
|
# Used for sorting
|
||||||
if role == Qt.UserRole + 1:
|
if role == Qt.UserRole + 1:
|
||||||
if vm.qid == 0:
|
if vm.klass != 'AdminVM':
|
||||||
return ""
|
return ""
|
||||||
if col_name == "Type":
|
if col_name == "Type":
|
||||||
return vm.klass
|
return vm.klass
|
||||||
if col_name == "Label":
|
if col_name == "Label":
|
||||||
return vm.label.name
|
return str(vm.label)
|
||||||
if col_name == "State":
|
if col_name == "State":
|
||||||
return str(vm.state)
|
return str(vm.state)
|
||||||
if col_name == "Disk Usage":
|
if col_name == "Disk Usage":
|
||||||
@ -447,7 +475,6 @@ class QubesTableModel(QAbstractTableModel):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
vm_shutdown_timeout = 20000 # in msec
|
vm_shutdown_timeout = 20000 # in msec
|
||||||
vm_restart_check_timeout = 1000 # in msec
|
vm_restart_check_timeout = 1000 # in msec
|
||||||
|
|
||||||
@ -545,7 +572,7 @@ class StartVMThread(common_threads.QubesThread):
|
|||||||
class UpdateVMThread(common_threads.QubesThread):
|
class UpdateVMThread(common_threads.QubesThread):
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
if self.vm.qid == 0:
|
if self.vm.klass != 'AdminVM':
|
||||||
subprocess.check_call(
|
subprocess.check_call(
|
||||||
["/usr/bin/qubes-dom0-update", "--clean", "--gui"])
|
["/usr/bin/qubes-dom0-update", "--clean", "--gui"])
|
||||||
else:
|
else:
|
||||||
@ -587,6 +614,7 @@ class RunCommandThread(common_threads.QubesThread):
|
|||||||
except (ChildProcessError, exc.QubesException) as ex:
|
except (ChildProcessError, exc.QubesException) as ex:
|
||||||
self.msg = (self.tr("Error while running command!"), str(ex))
|
self.msg = (self.tr("Error while running command!"), str(ex))
|
||||||
|
|
||||||
|
|
||||||
class QubesProxyModel(QSortFilterProxyModel):
|
class QubesProxyModel(QSortFilterProxyModel):
|
||||||
def lessThan(self, left, right):
|
def lessThan(self, left, right):
|
||||||
if left.data(self.sortRole()) != right.data(self.sortRole()):
|
if left.data(self.sortRole()) != right.data(self.sortRole()):
|
||||||
@ -597,6 +625,7 @@ class QubesProxyModel(QSortFilterProxyModel):
|
|||||||
|
|
||||||
return left_vm.name.lower() < right_vm.name.lower()
|
return left_vm.name.lower() < right_vm.name.lower()
|
||||||
|
|
||||||
|
|
||||||
class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||||
# suppress saving settings while initializing widgets
|
# suppress saving settings while initializing widgets
|
||||||
settings_loaded = False
|
settings_loaded = False
|
||||||
@ -733,6 +762,10 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
|||||||
self.on_domain_changed)
|
self.on_domain_changed)
|
||||||
dispatcher.add_handler('property-load',
|
dispatcher.add_handler('property-load',
|
||||||
self.on_domain_changed)
|
self.on_domain_changed)
|
||||||
|
dispatcher.add_handler('domain-feature-set:internal',
|
||||||
|
self.on_domain_changed)
|
||||||
|
dispatcher.add_handler('domain-feature-delete:internal',
|
||||||
|
self.on_domain_changed)
|
||||||
|
|
||||||
dispatcher.add_handler('domain-feature-set:updates-available',
|
dispatcher.add_handler('domain-feature-set:updates-available',
|
||||||
self.on_domain_updates_available)
|
self.on_domain_updates_available)
|
||||||
@ -813,9 +846,12 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
|||||||
self.check_updates(info_iter)
|
self.check_updates(info_iter)
|
||||||
return
|
return
|
||||||
|
|
||||||
if info.vm.klass in {'TemplateVM', 'StandaloneVM'} and \
|
try:
|
||||||
info.vm.features.get('updates-available', False):
|
if info.vm.klass in {'TemplateVM', 'StandaloneVM'} and \
|
||||||
info.state['outdated'] = 'update'
|
info.vm.features.get('updates-available', False):
|
||||||
|
info.state['outdated'] = 'update'
|
||||||
|
except exc.QubesPropertyAccessError:
|
||||||
|
return
|
||||||
|
|
||||||
def on_domain_added(self, _submitter, _event, vm, **_kwargs):
|
def on_domain_added(self, _submitter, _event, vm, **_kwargs):
|
||||||
try:
|
try:
|
||||||
@ -974,7 +1010,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
|||||||
if vm.vm.features.get('internal', False):
|
if vm.vm.features.get('internal', False):
|
||||||
self.action_appmenus.setEnabled(False)
|
self.action_appmenus.setEnabled(False)
|
||||||
|
|
||||||
if not vm.updateable and vm.qid != 0:
|
if not vm.updateable and vm.klass != 'AdminVM':
|
||||||
self.action_updatevm.setEnabled(False)
|
self.action_updatevm.setEnabled(False)
|
||||||
|
|
||||||
self.update_logs_menu()
|
self.update_logs_menu()
|
||||||
@ -1363,7 +1399,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
|||||||
if len(vm_info) == 1:
|
if len(vm_info) == 1:
|
||||||
vm = vm_info[0].vm
|
vm = vm_info[0].vm
|
||||||
|
|
||||||
if vm.qid == 0:
|
if vm.klass == 'AdminVM':
|
||||||
logfiles = ["/var/log/xen/console/hypervisor.log"]
|
logfiles = ["/var/log/xen/console/hypervisor.log"]
|
||||||
else:
|
else:
|
||||||
logfiles = [
|
logfiles = [
|
||||||
|
Loading…
Reference in New Issue
Block a user