Misc qubesmanager tools modified to be more resilient to insufficient permissions

Create New Qubes, Backup and Restore, Boot from Device and Template Manager
are now more resilient to insufficient permissions.
This commit is contained in:
Marta Marczykowska-Górecka 2020-08-04 22:50:20 +02:00
parent 7cbc7d9db1
commit 39129bd804
No known key found for this signature in database
GPG Key ID: 9A752C30B26FD04B
6 changed files with 79 additions and 29 deletions

View File

@ -49,6 +49,11 @@ class BackupThread(QtCore.QThread):
try:
if not self.vm.is_running():
self.vm.start()
except exc.QubesException:
# we may have insufficient exceptions to ensure the qube is running
pass
try:
self.vm.app.qubesd_call(
'dom0', 'admin.backup.Execute',
backup_utils.get_profile_name(True))
@ -103,8 +108,8 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard):
qubes_app=self.qubes_app,
filter_function=(lambda vm:
vm.klass != 'TemplateVM'
and vm.is_running()
and not vm.features.get('internal', False)),
and utils.is_running(vm, False)
and not utils.get_feature(vm, 'internal', False)),
allow_internal=True,
)
self.appvm_combobox.setCurrentIndex(
@ -215,7 +220,7 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard):
def __fill_vms_list__(self, selected=None):
for vm in self.qubes_app.domains:
if vm.features.get('internal', False):
if utils.get_feature(vm, 'internal', False):
continue
item = BackupVMsWindow.VmListItem(vm)
@ -298,13 +303,17 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard):
if self.currentPage() is self.confirm_page:
self.save_settings(use_temp=True)
backup_summary = self.qubes_app.qubesd_call(
'dom0', 'admin.backup.Info',
backup_utils.get_profile_name(True))
try:
backup_summary = self.qubes_app.qubesd_call(
'dom0', 'admin.backup.Info',
backup_utils.get_profile_name(True)).decode()
except exc.QubesDaemonCommunicationError:
backup_summary = "Failed to get backup summary: " \
"insufficient permissions"
self.textEdit.setReadOnly(True)
self.textEdit.setFontFamily("Monospace")
self.textEdit.setText(backup_summary.decode())
self.textEdit.setText(backup_summary)
elif self.currentPage() is self.commit_page:

View File

@ -43,10 +43,10 @@ def fill_appvms_list(dialog):
dialog.appvm_combobox.setCurrentIndex(0) # current selected is null ""
for vm in dialog.qubes_app.domains:
if vm.features.get('internal', False) or vm.klass == 'TemplateVM':
if utils.get_feature(vm, 'internal', False) or vm.klass == 'TemplateVM':
continue
if vm.is_running() and vm.qid != 0:
if utils.is_running(vm, False) and vm.qid != 0:
dialog.appvm_combobox.addItem(vm.name)
@ -101,6 +101,11 @@ def select_path_button_clicked(dialog, select_file=False, read_only=False):
dialog.tr("Unexpected characters in path!"),
dialog.tr("Backup path can only contain the following "
"special characters: /:.,_+=() -"))
except Exception as ex:
QtWidgets.QMessageBox.warning(
dialog,
dialog.tr("Failed to select path!"),
dialog.tr("Error {} occurred.".format(str(ex))))
except subprocess.CalledProcessError:
if not read_only:

View File

@ -24,6 +24,7 @@ from . import ui_bootfromdevice # pylint: disable=no-name-in-module
from PyQt5 import QtWidgets, QtGui # pylint: disable=import-error
from qubesadmin import tools
from qubesadmin.tools import qvm_start
from qubesadmin import exc
class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog,
@ -75,13 +76,20 @@ class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog,
self.done(0)
def __warn_if_running__(self):
if self.vm.is_running():
try:
if self.vm.is_running():
QtWidgets.QMessageBox.warning(
self,
self.tr("Warning!"),
self.tr("Qube must be turned off before booting it from "
"device. Please turn off the qube."))
except exc.QubesPropertyAccessError:
QtWidgets.QMessageBox.warning(
self,
self.tr("Warning!"),
self.tr("Qube must be turned off before booting it from "
"device. Please turn off the qube.")
)
self.tr("Insufficient permissions to determine if qube is "
"running. It must be turned off before booting it from "
"device."))
def __init_buttons__(self):
self.fileVM.setEnabled(False)
@ -98,8 +106,14 @@ class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog,
allow_internal=True
)
device_choice = [(str(device), device) for domain in self.vm.app.domains
for device in domain.devices["block"]]
device_choice = []
for domain in self.vm.app.domains:
try:
for device in domain.devices["block"]:
device_choice.append((str(device), device))
except exc.QubesException:
# insufficient permissions
pass
utils.initialize_widget(
widget=self.blockDeviceComboBox,

View File

@ -105,22 +105,27 @@ class NewVmDlg(QtWidgets.QDialog, Ui_NewVMDlg):
choices=[(vm.name, vm) for vm in self.app.domains
if not utils.is_internal(vm) and vm.klass == 'TemplateVM'],
mark_existing_as_default=True,
default_value=self.app.default_template)
default_value=getattr(self.app, 'default_template', None))
utils.initialize_widget_with_default(
widget=self.netvm,
choices=[(vm.name, vm) for vm in self.app.domains
if not utils.is_internal(vm) and vm.provides_network],
if not utils.is_internal(vm) and
getattr(vm, 'provides_network', False)],
add_none=True,
add_qubes_default=True,
default_value=self.app.default_netvm)
default_value=getattr(self.app, 'default_netvm', None))
utils.initialize_widget_with_default(
widget=self.storage_pool,
choices=[(str(pool), pool) for pool in self.app.pools.values()],
add_qubes_default=True,
mark_existing_as_default=True,
default_value=self.app.default_pool)
try:
utils.initialize_widget_with_default(
widget=self.storage_pool,
choices=[(str(pool), pool) for pool in self.app.pools.values()],
add_qubes_default=True,
mark_existing_as_default=True,
default_value=self.app.default_pool)
except qubesadmin.exc.QubesPropertyAccessError:
self.storage_pool.clear()
self.storage_pool.addItem("(default)", qubesadmin.DEFAULT)
self.name.setValidator(QtGui.QRegExpValidator(
QtCore.QRegExp("[a-zA-Z0-9_-]*", QtCore.Qt.CaseInsensitive), None))
@ -133,8 +138,6 @@ class NewVmDlg(QtWidgets.QDialog, Ui_NewVMDlg):
self.tr('No template available!'),
self.tr('Cannot create a qube when no template exists.'))
# Order of types is important and used elsewhere; if it's changed
# check for changes needed in self.type_change
type_list = [
(self.tr("Qube based on a template (AppVM)"), 'AppVM'),
(self.tr("Standalone qube copied from a template"),

View File

@ -345,7 +345,7 @@ class VMRow:
table_widget.setItem(row_no, columns.index('New template'),
self.dummy_new_item)
self.vm_state_change(self.vm.is_running(), row_no)
self.vm_state_change(is_vm_running(self.vm), row_no)
def vm_state_change(self, is_running, row=None):
self.state_item.set_state(is_running)
@ -385,6 +385,13 @@ class VMRow:
self.checkbox = None
def is_vm_running(vm):
try:
return vm.is_running()
except exc.QubesPropertyAccessError:
return False
def main():
utils.run_asynchronous(TemplateManagerWindow)

View File

@ -51,8 +51,20 @@ from PyQt5 import QtWidgets, QtCore, QtGui # pylint: disable=import-error
def is_internal(vm):
"""checks if the VM is either an AdminVM or has the 'internal' features"""
return (vm.klass == 'AdminVM'
or vm.features.get('internal', False))
try:
return (vm.klass == 'AdminVM'
or vm.features.get('internal', False))
except exc.QubesDaemonCommunicationError:
return False
def is_running(vm, default_state):
"""Checks if the VM is running, returns default_state if we have
insufficient permissions to deteremine that."""
try:
return vm.is_running()
except exc.QubesPropertyAccessError:
return default_state
def translate(string):