Fixed VM settings to be more tolerant of missing permissions

Settings unavailable due to permissions will be unavailable in the tool,
but the tool will start and attempt to show as much as possible.
This commit is contained in:
Marta Marczykowska-Górecka 2020-08-05 00:22:04 +02:00
parent 39129bd804
commit 8eb61b11ae
No known key found for this signature in database
GPG Key ID: 9A752C30B26FD04B
5 changed files with 307 additions and 180 deletions

View File

@ -21,6 +21,7 @@
import subprocess import subprocess
from PyQt5 import QtWidgets, QtCore # pylint: disable=import-error from PyQt5 import QtWidgets, QtCore # pylint: disable=import-error
from qubesadmin import exc
# TODO description in tooltip # TODO description in tooltip
# TODO icon # TODO icon
@ -60,9 +61,12 @@ class AppmenuSelectManager:
self.fill_apps_list(template=None) self.fill_apps_list(template=None)
def fill_apps_list(self, template=None): def fill_apps_list(self, template=None):
self.whitelisted = [line for line in subprocess.check_output( try:
['qvm-appmenus', '--get-whitelist', self.vm.name] self.whitelisted = [line for line in subprocess.check_output(
).decode().strip().split('\n') if line] ['qvm-appmenus', '--get-whitelist', self.vm.name]
).decode().strip().split('\n') if line]
except exc.QubesException:
self.whitelisted = []
currently_selected = [ currently_selected = [
self.app_list.selected_list.item(i).ident self.app_list.selected_list.item(i).ident
@ -84,9 +88,13 @@ class AppmenuSelectManager:
command.extend(['--template', template.name]) command.extend(['--template', template.name])
command.append(self.vm.name) command.append(self.vm.name)
available_appmenus = [ try:
AppListWidgetItem.from_line(line) available_appmenus = [
for line in subprocess.check_output(command).decode().splitlines()] AppListWidgetItem.from_line(line)
for line in subprocess.check_output(
command).decode().splitlines()]
except exc.QubesException:
available_appmenus = []
for app in available_appmenus: for app in available_appmenus:
if app.ident in whitelist: if app.ident in whitelist:

View File

@ -453,7 +453,7 @@ class QubesTableModel(QAbstractTableModel):
# Used for sorting # Used for sorting
if role == Qt.UserRole + 1: if role == Qt.UserRole + 1:
if vm.klass != 'AdminVM': if vm.klass == 'AdminVM':
return "" return ""
if col_name == "Type": if col_name == "Type":
return vm.klass return vm.klass
@ -572,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.klass != 'AdminVM': 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:

View File

@ -102,7 +102,7 @@ class RefreshAppsVMThread(common_threads.QubesThread):
self.tr('Refresh in progress (refreshing applications ' self.tr('Refresh in progress (refreshing applications '
'from {})').format(vm.name)) 'from {})').format(vm.name))
try: try:
if not vm.is_running(): if not utils.is_running(vm, True):
not_running = True not_running = True
vm.start() vm.start()
else: else:
@ -137,10 +137,6 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.threads_list = [] self.threads_list = []
self.progress = None self.progress = None
self.thread_closes = False self.thread_closes = False
try:
self.source_vm = self.vm.template
except AttributeError:
self.source_vm = self.vm
self.setupUi(self) self.setupUi(self)
self.setWindowTitle(self.tr("Settings: {vm}").format(vm=self.vm.name)) self.setWindowTitle(self.tr("Settings: {vm}").format(vm=self.vm.name))
@ -182,6 +178,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.firewall_modified_outside_label.setVisible(False) self.firewall_modified_outside_label.setVisible(False)
except firewall.FirewallModifiedOutsideError: except firewall.FirewallModifiedOutsideError:
self.disable_all_fw_conf() self.disable_all_fw_conf()
except qubesadmin.exc.QubesException:
self.tabWidget.setTabEnabled(
self.tabs_indices['firewall'], False)
self.new_rule_button.clicked.connect(self.new_rule_button_pressed) self.new_rule_button.clicked.connect(self.new_rule_button_pressed)
self.edit_rule_button.clicked.connect(self.edit_rule_button_pressed) self.edit_rule_button.clicked.connect(self.edit_rule_button_pressed)
@ -306,7 +305,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
ret.append(repr(ex)) ret.append(repr(ex))
try: try:
if self.policy_allow_radio_button.isEnabled(): if self.tabWidget.isTabEnabled(self.tabs_indices['firewall']) and \
self.policy_allow_radio_button.isEnabled():
self.fw_model.apply_rules( self.fw_model.apply_rules(
self.policy_allow_radio_button.isChecked(), self.policy_allow_radio_button.isChecked(),
self.temp_full_access.isChecked(), self.temp_full_access.isChecked(),
@ -328,15 +328,19 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
return ret return ret
def check_network_availability(self): def check_network_availability(self):
netvm = self.vm.netvm netvm = getattr(self.vm, 'netvm', None)
try: provides_network = getattr(self.vm, 'provides_network', False)
provides_network = self.vm.provides_network
except AttributeError:
provides_network = False
self.no_netvm_label.setVisible(netvm is None and not provides_network) self.no_netvm_label.setVisible(netvm is None and not provides_network)
self.netvm_no_firewall_label.setVisible(
netvm is not None and try:
not netvm.features.check_with_template('qubes-firewall', False)) no_firewall_state = \
netvm is not None and \
not netvm.features.check_with_template('qubes-firewall', False)
except qubesadmin.exc.QubesDaemonCommunicationError:
no_firewall_state = False
self.netvm_no_firewall_label.setVisible(no_firewall_state)
self.sysnet_warning_label.setVisible(netvm is None and provides_network) self.sysnet_warning_label.setVisible(netvm is None and provides_network)
def current_tab_changed(self, idx): def current_tab_changed(self, idx):
@ -364,57 +368,80 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.rename_vm_button.setEnabled(not self.vm.is_running()) self.rename_vm_button.setEnabled(not self.vm.is_running())
self.delete_vm_button.setEnabled(not self.vm.is_running()) self.delete_vm_button.setEnabled(not self.vm.is_running())
if self.vm.is_running(): if utils.is_running(self.vm, False):
self.delete_vm_button.setText( self.delete_vm_button.setText(
self.tr('Delete qube (cannot delete a running qube)')) self.tr('Delete qube (cannot delete a running qube)'))
if self.vm.qid == 0: if self.vm.qid == 0:
self.vmlabel.setVisible(False) self.vmlabel.setVisible(False)
else: else:
utils.initialize_widget_with_labels( try:
widget=self.vmlabel, utils.initialize_widget_with_labels(
qubes_app=self.qubesapp, widget=self.vmlabel,
holder=self.vm) qubes_app=self.qubesapp,
self.vmlabel.setVisible(True) holder=self.vm)
self.vmlabel.setEnabled(not self.vm.is_running()) self.vmlabel.setVisible(True)
self.vmlabel.setEnabled(not utils.is_running(self.vm, False))
except qubesadmin.exc.QubesPropertyAccessError:
self.vmlabel.setEnabled(False)
if self.vm.klass == 'AppVM': if self.vm.klass == 'AppVM':
utils.initialize_widget_with_vms( try:
widget=self.template_name, utils.initialize_widget_with_vms(
qubes_app=self.qubesapp, widget=self.template_name,
filter_function=(lambda vm: vm.klass == 'TemplateVM'), qubes_app=self.qubesapp,
holder=self.vm, filter_function=(lambda vm: vm.klass == 'TemplateVM'),
property_name='template') holder=self.vm,
property_name='template')
except qubesadmin.exc.QubesPropertyAccessError:
self.template_name.setCurrentIndex(-1)
self.template_name.setEnabled(False)
elif self.vm.klass == 'DispVM': elif self.vm.klass == 'DispVM':
utils.initialize_widget_with_vms( try:
widget=self.template_name, utils.initialize_widget_with_vms(
qubes_app=self.qubesapp, widget=self.template_name,
filter_function=(lambda vm: qubes_app=self.qubesapp,
getattr(vm, 'template_for_dispvms', False)), filter_function=(lambda vm:
holder=self.vm, getattr(vm, 'template_for_dispvms', False)),
property_name='template') holder=self.vm,
property_name='template')
except qubesadmin.exc.QubesPropertyAccessError:
self.template_name.setCurrentIndex(-1)
self.template_name.setEnabled(False)
else: else:
self.template_name.setEnabled(False) self.template_name.setEnabled(False)
if self.vm.is_running(): if utils.is_running(self.vm, False):
self.template_name.setEnabled(False) self.template_name.setEnabled(False)
utils.initialize_widget_with_vms( try:
widget=self.netVM, utils.initialize_widget_with_vms(
qubes_app=self.qubesapp, widget=self.netVM,
filter_function=(lambda vm: vm.provides_network), qubes_app=self.qubesapp,
holder=self.vm, filter_function=(lambda vm:
property_name='netvm', getattr(vm, 'provides_network', False)),
allow_default=True, holder=self.vm,
allow_none=True) property_name='netvm',
allow_default=True,
allow_none=True)
except qubesadmin.exc.QubesPropertyAccessError:
self.netVM.setEnabled(False)
self.netVM.setCurrentIndex(-1)
self.netVM.currentIndexChanged.connect(self.check_warn_dispvmnetvm) self.netVM.currentIndexChanged.connect(self.check_warn_dispvmnetvm)
self.include_in_backups.setChecked(self.vm.include_in_backups) try:
self.include_in_backups.setChecked(self.vm.include_in_backups)
except qubesadmin.exc.QubesPropertyAccessError:
self.include_in_backups.setEnabled(False)
try: try:
self.autostart_vm.setChecked(self.vm.autostart) self.autostart_vm.setChecked(self.vm.autostart)
self.autostart_vm.setVisible(True) self.autostart_vm.setVisible(True)
except qubesadmin.exc.QubesPropertyAccessError:
self.autostart_vm.setEnabled(False)
except AttributeError: except AttributeError:
self.autostart_vm.setVisible(False) self.autostart_vm.setVisible(False)
@ -422,41 +449,50 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.type_label.setText(self.vm.klass) self.type_label.setText(self.vm.klass)
# installed by rpm # installed by rpm
self.rpm_label.setText('Yes' if self.vm.installed_by_rpm else 'No') self.rpm_label.setText(
'Yes' if getattr(self.vm, 'installed_by_rpm', False) else 'No')
# networking info # networking info
if self.vm.netvm: if getattr(self.vm, 'netvm', None):
self.networking_groupbox.setEnabled(True) self.networking_groupbox.setEnabled(True)
self.ip_label.setText(self.vm.ip or "none") self.ip_label.setText(getattr(self.vm, 'ip', None) or "none")
self.netmask_label.setText(self.vm.visible_netmask or "none") self.netmask_label.setText(
self.gateway_label.setText(self.vm.visible_gateway or "none") getattr(self.vm, 'visible_netmask', None) or "none")
self.gateway_label.setText(
getattr(self.vm, 'visible_gateway', None) or "none")
dns_list = getattr(self.vm, 'dns', ['10.139.1.1', '10.139.1.2']) dns_list = getattr(self.vm, 'dns', ['10.139.1.1', '10.139.1.2'])
self.dns_label.setText(", ".join(dns_list)) self.dns_label.setText(", ".join(dns_list))
else: else:
self.networking_groupbox.setEnabled(False) self.networking_groupbox.setEnabled(False)
# max priv storage # max priv storage
self.priv_img_size = self.vm.volumes['private'].size // 1024**2 try:
self.max_priv_storage.setMinimum(self.priv_img_size) self.priv_img_size = self.vm.volumes['private'].size // 1024**2
self.max_priv_storage.setValue(self.priv_img_size) self.max_priv_storage.setMinimum(self.priv_img_size)
self.max_priv_storage.setMaximum( self.max_priv_storage.setValue(self.priv_img_size)
max(self.priv_img_size, self.max_priv_storage.setMaximum(
self.qubesapp.pools[self.vm.volumes['private'].pool].size max(self.priv_img_size,
// 1024**2)) self.qubesapp.pools[self.vm.volumes['private'].pool].size
// 1024**2))
except qubesadmin.exc.QubesException:
self.max_priv_storage.setEnabled(False)
self.root_img_size = self.vm.volumes['root'].size // 1024**2 try:
self.root_resize.setValue(self.root_img_size) self.root_img_size = self.vm.volumes['root'].size // 1024**2
self.root_resize.setMinimum(self.root_img_size) self.root_resize.setValue(self.root_img_size)
self.root_resize.setMaximum( self.root_resize.setMinimum(self.root_img_size)
max(self.root_img_size, self.root_resize.setMaximum(
self.qubesapp.pools[self.vm.volumes['root'].pool].size max(self.root_img_size,
// 1024**2)) self.qubesapp.pools[self.vm.volumes['root'].pool].size
self.root_resize.setEnabled(self.vm.volumes['root'].save_on_stop) // 1024**2))
if not self.root_resize.isEnabled(): self.root_resize.setEnabled(self.vm.volumes['root'].save_on_stop)
self.root_resize.setToolTip( if not self.root_resize.isEnabled():
self.tr("To change system storage size, change properties " self.root_resize.setToolTip(
"of the underlying template.")) self.tr("To change system storage size, change properties "
self.root_resize_label.setEnabled(self.root_resize.isEnabled()) "of the underlying template."))
self.root_resize_label.setEnabled(self.root_resize.isEnabled())
except qubesadmin.exc.QubesException:
self.root_resize.setEnabled(False)
self.warn_template_missing_apps.setVisible(False) self.warn_template_missing_apps.setVisible(False)
@ -487,7 +523,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
# include in backups # include in backups
try: try:
if self.vm.include_in_backups != \ if self.include_in_backups.isEnabled() and\
self.vm.include_in_backups != \
self.include_in_backups.isChecked(): self.include_in_backups.isChecked():
self.vm.include_in_backups = self.include_in_backups.isChecked() self.vm.include_in_backups = self.include_in_backups.isChecked()
except qubesadmin.exc.QubesException as ex: except qubesadmin.exc.QubesException as ex:
@ -502,22 +539,24 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
msg.append(str(ex)) msg.append(str(ex))
# max priv storage # max priv storage
priv_size = self.max_priv_storage.value() if self.max_priv_storage.isEnabled():
if self.priv_img_size != priv_size: priv_size = self.max_priv_storage.value()
try: if self.priv_img_size != priv_size:
self.vm.volumes['private'].resize(priv_size * 1024**2) try:
self.priv_img_size = priv_size self.vm.volumes['private'].resize(priv_size * 1024**2)
except qubesadmin.exc.QubesException as ex: self.priv_img_size = priv_size
msg.append(str(ex)) except qubesadmin.exc.QubesException as ex:
msg.append(str(ex))
# max sys storage # max sys storage
sys_size = self.root_resize.value() if self.root_resize.isEnabled():
if self.root_img_size != sys_size: sys_size = self.root_resize.value()
try: if self.root_img_size != sys_size:
self.vm.volumes['root'].resize(sys_size * 1024**2) try:
self.root_img_size = sys_size self.vm.volumes['root'].resize(sys_size * 1024**2)
except qubesadmin.exc.QubesException as ex: self.root_img_size = sys_size
msg.append(str(ex)) except qubesadmin.exc.QubesException as ex:
msg.append(str(ex))
return msg return msg
@ -526,6 +565,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
# do not interfere with settings if the VM is not included in memory # do not interfere with settings if the VM is not included in memory
# balancing # balancing
return return
if not self.max_mem_size.isEnabled() or not self.init_mem.isEnabled():
# do not interfere with settings if they are unavailable
return
if self.max_mem_size.value() < self.init_mem.value(): if self.max_mem_size.value() < self.init_mem.value():
QtWidgets.QMessageBox.warning( QtWidgets.QMessageBox.warning(
self, self,
@ -536,7 +578,13 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
# Linux specific limit: init memory must not be below # Linux specific limit: init memory must not be below
# max_mem_size/10.79 in order to allow scaling up to # max_mem_size/10.79 in order to allow scaling up to
# max_mem_size (or else "add_memory() failed: -17" problem) # max_mem_size (or else "add_memory() failed: -17" problem)
if self.vm.features.check_with_template('os', None) == 'Linux' and \ try:
is_linux = self.vm.features.check_with_template('os', None) == \
'Linux'
except qubesadmin.exc.QubesException:
is_linux = False
if is_linux and \
self.init_mem.value() * 10 < self.max_mem_size.value(): self.init_mem.value() * 10 < self.max_mem_size.value():
self.init_mem.setValue((self.max_mem_size.value() + 9) // 10) self.init_mem.setValue((self.max_mem_size.value() + 9) // 10)
QtWidgets.QMessageBox.warning( QtWidgets.QMessageBox.warning(
@ -563,7 +611,12 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
dispvm_netvm = getattr(dispvm, 'netvm', None) dispvm_netvm = getattr(dispvm, 'netvm', None)
if own_netvm == qubesadmin.DEFAULT: if own_netvm == qubesadmin.DEFAULT:
own_netvm = self.vm.property_get_default('netvm') try:
own_netvm = self.vm.property_get_default('netvm')
except qubesadmin.exc.QubesPropertyAccessError:
# no point in warning if we don't know what we're warning about
self.warn_netvm_dispvm.setVisible(False)
return
if dispvm_netvm and dispvm_netvm != own_netvm: if dispvm_netvm and dispvm_netvm != own_netvm:
self.warn_netvm_dispvm.setVisible(True) self.warn_netvm_dispvm.setVisible(True)
@ -576,7 +629,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
running_dependencies = [vm.name for (vm, prop) in dependencies running_dependencies = [vm.name for (vm, prop) in dependencies
if vm and prop == 'template' if vm and prop == 'template'
and vm.is_running()] and utils.is_running(vm, False)]
if running_dependencies: if running_dependencies:
QtWidgets.QMessageBox.warning( QtWidgets.QMessageBox.warning(
@ -663,64 +716,85 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
def __init_advanced_tab__(self): def __init_advanced_tab__(self):
self.init_mem.setValue(int(self.vm.memory)) vm_memory = getattr(self.vm, 'memory', None)
vm_maxmem = getattr(self.vm, 'maxmem', None)
if self.vm.maxmem > 0: if vm_memory is None:
self.max_mem_size.setValue(int(self.vm.maxmem)) self.init_mem.setEnabled(False)
else: else:
maxmem = self.vm.property_get_default('maxmem') self.init_mem.setValue(int(vm_memory))
if vm_maxmem is None:
self.max_mem_size.setEnabled(False)
elif vm_maxmem > 0:
self.max_mem_size.setValue(int(vm_maxmem))
else:
try:
maxmem = self.vm.property_get_default('maxmem')
except qubesadmin.exc.QubesPropertyAccessError:
maxmem = 0
if maxmem == 0: if maxmem == 0:
maxmem = self.vm.memory maxmem = vm_memory
self.max_mem_size.setValue(int( self.max_mem_size.setValue(
self.vm.features.get('qubesmanager.maxmem_value', maxmem))) int(utils.get_feature(
self.vm, 'qubesmanager.maxmem_value', maxmem)))
self.vcpus.setMinimum(1) self.vcpus.setMinimum(1)
self.vcpus.setValue(int(self.vm.vcpus)) self.vcpus.setValue(int(getattr(self.vm, 'vcpus', 1)))
self.include_in_balancing.setEnabled(True) self.include_in_balancing.setEnabled(True)
self.include_in_balancing.setChecked(int(self.vm.maxmem) > 0) self.include_in_balancing.setChecked(
int(getattr(self.vm, 'maxmem', 0)) > 0)
self.max_mem_size.setEnabled(self.include_in_balancing.isChecked()) self.max_mem_size.setEnabled(self.include_in_balancing.isChecked())
# in case VM is HVM # in case VM is HVM
if hasattr(self.vm, "kernel"): if hasattr(self.vm, "kernel"):
self.kernel_groupbox.setVisible(True) self.kernel_groupbox.setVisible(True)
utils.initialize_widget_with_kernels( try:
widget=self.kernel, utils.initialize_widget_with_kernels(
qubes_app=self.qubesapp, widget=self.kernel,
allow_none=True, qubes_app=self.qubesapp,
allow_default=True, allow_none=True,
holder=self.vm, allow_default=True,
property_name='kernel') holder=self.vm,
self.kernel.currentIndexChanged.connect(self.kernel_changed) property_name='kernel')
self.kernel_opts.setText(getattr(self.vm, 'kernelopts', '-')) self.kernel.currentIndexChanged.connect(self.kernel_changed)
self.kernel_opts.setText(getattr(self.vm, 'kernelopts', '-'))
except qubesadmin.exc.QubesPropertyAccessError:
self.kernel_groupbox.setVisible(False)
else: else:
self.kernel_groupbox.setVisible(False) self.kernel_groupbox.setVisible(False)
self.other_groupbox.setVisible(False)
if not hasattr(self.vm, 'default_dispvm'): if not hasattr(self.vm, 'default_dispvm'):
self.other_groupbox.setVisible(False) self.other_groupbox.setVisible(False)
else: else:
self.other_groupbox.setVisible(True) try:
utils.initialize_widget_with_vms( self.other_groupbox.setVisible(True)
widget=self.default_dispvm, utils.initialize_widget_with_vms(
qubes_app=self.qubesapp, widget=self.default_dispvm,
filter_function=(lambda vm: qubes_app=self.qubesapp,
getattr(vm, 'template_for_dispvms', False)), filter_function=(lambda vm:
allow_none=True, getattr(
allow_default=True, vm, 'template_for_dispvms', False)),
holder=self.vm, allow_none=True,
property_name='default_dispvm' allow_default=True,
) holder=self.vm,
self.default_dispvm.currentIndexChanged.connect( property_name='default_dispvm'
self.check_warn_dispvmnetvm) )
self.default_dispvm.currentIndexChanged.connect(
self.check_warn_dispvmnetvm)
except qubesadmin.exc.QubesPropertyAccessError:
self.other_groupbox.setVisible(False)
self.check_warn_dispvmnetvm() self.check_warn_dispvmnetvm()
self.update_virt_mode_list() self.update_virt_mode_list()
windows_running = \ try:
self.vm.features.check_with_template('os', None) == 'Windows' \ windows_running = \
and self.vm.is_running() self.vm.features.check_with_template('os', None) == 'Windows' \
and self.vm.is_running()
except qubesadmin.exc.QubesException:
windows_running = False
self.seamless_on_button.setEnabled(windows_running) self.seamless_on_button.setEnabled(windows_running)
self.seamless_off_button.setEnabled(windows_running) self.seamless_off_button.setEnabled(windows_running)
@ -728,15 +802,14 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.seamless_on_button.clicked.connect(self.enable_seamless) self.seamless_on_button.clicked.connect(self.enable_seamless)
self.seamless_off_button.clicked.connect(self.disable_seamless) self.seamless_off_button.clicked.connect(self.disable_seamless)
if hasattr(self.vm, "template_for_dispvms"): self.dvm_template_checkbox.setChecked(
self.dvm_template_checkbox.setChecked(self.vm.template_for_dispvms) getattr(self.vm, 'template_for_dispvms', False))
else:
self.dvm_template_checkbox.setVisible(False)
self.provides_network_checkbox.setChecked( self.provides_network_checkbox.setChecked(
getattr(self.vm, 'provides_network', False)) getattr(self.vm, 'provides_network', False))
if self.provides_network_checkbox.isChecked(): if self.provides_network_checkbox.isChecked():
domains_using = [vm.name for vm in self.vm.connected_vms] domains_using = [vm.name for vm
in getattr(self.vm, 'connected_vms', [])]
if domains_using: if domains_using:
self.provides_network_checkbox.setEnabled(False) self.provides_network_checkbox.setEnabled(False)
self.provides_network_checkbox.setToolTip(self.tr( self.provides_network_checkbox.setToolTip(self.tr(
@ -772,20 +845,34 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.allow_utf8_initial = self.allow_utf8.currentIndex() self.allow_utf8_initial = self.allow_utf8.currentIndex()
def enable_seamless(self): def enable_seamless(self):
self.vm.run_service_for_stdio("qubes.SetGuiMode", input=b'SEAMLESS') try:
self.vm.run_service_for_stdio("qubes.SetGuiMode", input=b'SEAMLESS')
except qubesadmin.exc.QubesException as ex:
QtWidgets.QMessageBox.warning(
self,
self.tr("Failed to set seamless mode"),
self.tr("Error occured: {}".format(str(ex))))
def disable_seamless(self): def disable_seamless(self):
self.vm.run_service_for_stdio("qubes.SetGuiMode", input=b'FULLSCREEN') try:
self.vm.run_service_for_stdio("qubes.SetGuiMode",
input=b'FULLSCREEN')
except qubesadmin.exc.QubesException as ex:
QtWidgets.QMessageBox.warning(
self,
self.tr("Failed to set fullscreen mode"),
self.tr("Error occured: {}".format(str(ex))))
def __apply_advanced_tab__(self): def __apply_advanced_tab__(self):
msg = [] msg = []
# mem/cpu # mem/cpu
try: try:
if self.init_mem.value() != int(self.vm.memory): if self.init_mem.isEnabled() and \
self.init_mem.value() != int(self.vm.memory):
self.vm.memory = self.init_mem.value() self.vm.memory = self.init_mem.value()
curr_maxmem = int(self.vm.maxmem) curr_maxmem = int(getattr(self.vm, 'maxmem', 0))
if not self.include_in_balancing.isChecked(): if not self.include_in_balancing.isChecked():
maxmem = 0 maxmem = 0
@ -795,9 +882,11 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
if maxmem != curr_maxmem: if maxmem != curr_maxmem:
if curr_maxmem > 0: if curr_maxmem > 0:
self.vm.features['qubesmanager.maxmem_value'] = curr_maxmem self.vm.features['qubesmanager.maxmem_value'] = curr_maxmem
self.vm.maxmem = maxmem if maxmem == 0 or self.max_mem_size.isEnabled():
self.vm.maxmem = maxmem
if self.vcpus.value() != int(self.vm.vcpus): if self.vcpus.isEnabled() and \
self.vcpus.value() != int(self.vm.vcpus):
self.vm.vcpus = self.vcpus.value() self.vm.vcpus = self.vcpus.value()
except qubesadmin.exc.QubesException as ex: except qubesadmin.exc.QubesException as ex:
@ -909,7 +998,10 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
if hasattr(self, "dev_list"): if hasattr(self, "dev_list"):
devs_attached = self.dev_list.selected_list.count() != 0 devs_attached = self.dev_list.selected_list.count() != 0
else: else:
devs_attached = bool(list(self.vm.devices['pci'].persistent())) try:
devs_attached = bool(list(self.vm.devices['pci'].persistent()))
except qubesadmin.exc.QubesException:
devs_attached = False
if devs_attached: if devs_attached:
self.pvh_mode_hidden.show() self.pvh_mode_hidden.show()
@ -923,18 +1015,25 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
# due to how virtualization mode has uniquely different displayed and # due to how virtualization mode has uniquely different displayed and
# actual name of the default value, I will add it manually # actual name of the default value, I will add it manually
choices.insert(0, ( try:
"default ({})".format( choices.insert(0, (
self.vm.property_get_default('virt_mode').upper()), "default ({})".format(
qubesadmin.DEFAULT)) self.vm.property_get_default('virt_mode').upper()),
qubesadmin.DEFAULT))
except qubesadmin.exc.QubesException:
choices.insert(0,
("default ({SYSTEM DEFAULT})", qubesadmin.DEFAULT))
utils.initialize_widget_for_property( try:
widget=self.virt_mode, utils.initialize_widget_for_property(
choices=choices, widget=self.virt_mode,
holder=self.vm, choices=choices,
property_name='virt_mode') holder=self.vm,
property_name='virt_mode')
except qubesadmin.exc.QubesPropertyAccessError:
self.virt_mode.setEnabled(False)
if old_mode is not None: if self.virt_mode.isEnabled() and old_mode is not None:
self.virt_mode.setCurrentIndex(self.virt_mode.findData(old_mode)) self.virt_mode.setCurrentIndex(self.virt_mode.findData(old_mode))
self.virt_mode.currentIndexChanged.connect(self.virt_mode_changed) self.virt_mode.currentIndexChanged.connect(self.virt_mode_changed)
@ -967,7 +1066,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
return False return False
if name is qubesadmin.DEFAULT: if name is qubesadmin.DEFAULT:
name = self.vm.app.default_kernel name = getattr(self.vm.app, 'default_kernel', None)
m = re.search(r'(\d+)\.(\d+)', name) m = re.search(r'(\d+)\.(\d+)', name)
@ -982,9 +1081,14 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.dev_list.add_all_button.setVisible(False) self.dev_list.add_all_button.setVisible(False)
self.devices_layout.addWidget(self.dev_list) self.devices_layout.addWidget(self.dev_list)
dom0_devs = list(self.vm.app.domains['dom0'].devices['pci'].available()) try:
dom0_devs = \
attached_devs = list(self.vm.devices['pci'].persistent()) list(self.vm.app.domains['dom0'].devices['pci'].available())
attached_devs = list(self.vm.devices['pci'].persistent())
except qubesadmin.exc.QubesException:
# no permission to access devices
self.tabWidget.setTabEnabled(self.tabs_indices['devices'], False)
return
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
class DevListWidgetItem(QtWidgets.QListWidgetItem): class DevListWidgetItem(QtWidgets.QListWidgetItem):
@ -1014,7 +1118,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.dmm_warning_adv.hide() self.dmm_warning_adv.hide()
self.dmm_warning_dev.hide() self.dmm_warning_dev.hide()
if self.vm.is_running(): if utils.is_running(self.vm, False):
self.dev_list.setEnabled(False) self.dev_list.setEnabled(False)
self.turn_off_vm_to_modify_devs.setVisible(True) self.turn_off_vm_to_modify_devs.setVisible(True)
self.no_strict_reset_button.setEnabled(False) self.no_strict_reset_button.setEnabled(False)
@ -1024,11 +1128,14 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.update_pvh_dont_support_devs() self.update_pvh_dont_support_devs()
self.dev_list.setEnabled(not self.vm.is_running()) self.dev_list.setEnabled(not utils.is_running(self.vm, False))
def __apply_devices_tab__(self): def __apply_devices_tab__(self):
msg = [] msg = []
if not self.tabWidget.isTabEnabled(self.tabs_indices['devices']):
return msg
try: try:
old_devs = list(self.vm.devices['pci'].persistent()) old_devs = list(self.vm.devices['pci'].persistent())
@ -1137,16 +1244,20 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
def __init_services_tab__(self): def __init_services_tab__(self):
self.new_srv_dict = {} self.new_srv_dict = {}
for feature in self.vm.features: try:
if not feature.startswith('service.'): for feature in self.vm.features:
continue if not feature.startswith('service.'):
service = feature[len('service.'):] continue
item = QtWidgets.QListWidgetItem(service) service = feature[len('service.'):]
item.setCheckState(ui_settingsdlg.QtCore.Qt.Checked item = QtWidgets.QListWidgetItem(service)
if self.vm.features[feature] item.setCheckState(ui_settingsdlg.QtCore.Qt.Checked
else ui_settingsdlg.QtCore.Qt.Unchecked) if self.vm.features[feature]
self.services_list.addItem(item) else ui_settingsdlg.QtCore.Qt.Unchecked)
self.new_srv_dict[service] = self.vm.features[feature] self.services_list.addItem(item)
self.new_srv_dict[service] = self.vm.features[feature]
except qubesadmin.exc.QubesDaemonCommunicationError:
self.tabWidget.setTabEnabled(self.tabs_indices["services"], False)
return
self.service_line_edit.addItem("") self.service_line_edit.addItem("")
@ -1157,9 +1268,12 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
if feature.startswith(service_prefix): if feature.startswith(service_prefix):
supported_services.add(feature[len(service_prefix):]) supported_services.add(feature[len(service_prefix):])
if getattr(self.vm, "template", None): if getattr(self.vm, "template", None):
for feature in self.vm.template.features: try:
if feature.startswith(service_prefix): for feature in self.vm.template.features:
supported_services.add(feature[len(service_prefix):]) if feature.startswith(service_prefix):
supported_services.add(feature[len(service_prefix):])
except qubesadmin.exc.QubesDaemonCommunicationError:
pass
for service in sorted(supported_services): for service in sorted(supported_services):
self.service_line_edit.addItem(service) self.service_line_edit.addItem(service)
@ -1205,6 +1319,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
def __apply_services_tab__(self): def __apply_services_tab__(self):
msg = [] msg = []
if not self.tabWidget.isTabEnabled(self.tabs_indices['services']):
return msg
try: try:
for i in range(self.services_list.count()): for i in range(self.services_list.count()):
item = self.services_list.item(i) item = self.services_list.item(i)

View File

@ -1313,8 +1313,8 @@ class QubeManagerThreadTest(unittest.TestCase):
@unittest.mock.patch('subprocess.check_call') @unittest.mock.patch('subprocess.check_call')
def test_20_update_vm_thread_dom0(self, check_call): def test_20_update_vm_thread_dom0(self, check_call):
vm = unittest.mock.Mock(spec=['qid']) vm = unittest.mock.Mock(spec=['klass'])
vm.qid = 0 vm.klass = 'AdminVM'
thread = qube_manager.UpdateVMThread(vm) thread = qube_manager.UpdateVMThread(vm)
thread.run() thread.run()
@ -1325,10 +1325,10 @@ class QubeManagerThreadTest(unittest.TestCase):
@unittest.mock.patch('subprocess.call') @unittest.mock.patch('subprocess.call')
def test_21_update_vm_thread_running(self, mock_call, mock_open): def test_21_update_vm_thread_running(self, mock_call, mock_open):
vm = unittest.mock.Mock( vm = unittest.mock.Mock(
spec=['qid', 'is_running', 'run_service_for_stdio', 'run_service'], spec=['klass', 'is_running', 'run_service_for_stdio', 'run_service'],
**{'is_running.return_value': True}) **{'is_running.return_value': True})
vm.qid = 1 vm.klass = 'AppVM'
vm.run_service_for_stdio.return_value = (b'changed=no\n', None) vm.run_service_for_stdio.return_value = (b'changed=no\n', None)
thread = qube_manager.UpdateVMThread(vm) thread = qube_manager.UpdateVMThread(vm)
@ -1350,11 +1350,11 @@ class QubeManagerThreadTest(unittest.TestCase):
@unittest.mock.patch('subprocess.call') @unittest.mock.patch('subprocess.call')
def test_22_update_vm_thread_not_running(self, mock_call, mock_open): def test_22_update_vm_thread_not_running(self, mock_call, mock_open):
vm = unittest.mock.Mock( vm = unittest.mock.Mock(
spec=['qid', 'is_running', 'run_service_for_stdio', spec=['klass', 'is_running', 'run_service_for_stdio',
'run_service', 'start', 'name'], 'run_service', 'start', 'name'],
**{'is_running.return_value': False}) **{'is_running.return_value': False})
vm.qid = 1 vm.klass = 'AppVM'
vm.run_service_for_stdio.return_value = (b'changed=yes\n', None) vm.run_service_for_stdio.return_value = (b'changed=yes\n', None)
thread = qube_manager.UpdateVMThread(vm) thread = qube_manager.UpdateVMThread(vm)
@ -1377,10 +1377,10 @@ class QubeManagerThreadTest(unittest.TestCase):
@unittest.mock.patch('subprocess.check_call') @unittest.mock.patch('subprocess.check_call')
def test_23_update_vm_thread_error(self, *_args): def test_23_update_vm_thread_error(self, *_args):
vm = unittest.mock.Mock( vm = unittest.mock.Mock(
spec=['qid', 'is_running'], spec=['klass', 'is_running'],
**{'is_running.side_effect': ChildProcessError}) **{'is_running.side_effect': ChildProcessError})
vm.qid = 1 vm.klass = 'AppVM'
thread = qube_manager.UpdateVMThread(vm) thread = qube_manager.UpdateVMThread(vm)
thread.run() thread.run()

View File

@ -124,6 +124,8 @@ def get_boolean_feature(vm, feature_name):
def did_widget_selection_change(widget): def did_widget_selection_change(widget):
"""a simple heuristic to check if the widget text contains appropriately """a simple heuristic to check if the widget text contains appropriately
translated 'current'""" translated 'current'"""
if not widget.isEnabled():
return False
return not translate(" (current)") in widget.currentText() return not translate(" (current)") in widget.currentText()