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

View File

@ -453,7 +453,7 @@ class QubesTableModel(QAbstractTableModel):
# Used for sorting
if role == Qt.UserRole + 1:
if vm.klass != 'AdminVM':
if vm.klass == 'AdminVM':
return ""
if col_name == "Type":
return vm.klass
@ -572,7 +572,7 @@ class StartVMThread(common_threads.QubesThread):
class UpdateVMThread(common_threads.QubesThread):
def run(self):
try:
if self.vm.klass != 'AdminVM':
if self.vm.klass == 'AdminVM':
subprocess.check_call(
["/usr/bin/qubes-dom0-update", "--clean", "--gui"])
else:

View File

@ -102,7 +102,7 @@ class RefreshAppsVMThread(common_threads.QubesThread):
self.tr('Refresh in progress (refreshing applications '
'from {})').format(vm.name))
try:
if not vm.is_running():
if not utils.is_running(vm, True):
not_running = True
vm.start()
else:
@ -137,10 +137,6 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.threads_list = []
self.progress = None
self.thread_closes = False
try:
self.source_vm = self.vm.template
except AttributeError:
self.source_vm = self.vm
self.setupUi(self)
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)
except firewall.FirewallModifiedOutsideError:
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.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))
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.policy_allow_radio_button.isChecked(),
self.temp_full_access.isChecked(),
@ -328,15 +328,19 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
return ret
def check_network_availability(self):
netvm = self.vm.netvm
try:
provides_network = self.vm.provides_network
except AttributeError:
provides_network = False
netvm = getattr(self.vm, 'netvm', None)
provides_network = getattr(self.vm, 'provides_network', False)
self.no_netvm_label.setVisible(netvm is None and not provides_network)
self.netvm_no_firewall_label.setVisible(
netvm is not None and
not netvm.features.check_with_template('qubes-firewall', False))
try:
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)
def current_tab_changed(self, idx):
@ -364,28 +368,37 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.rename_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.tr('Delete qube (cannot delete a running qube)'))
if self.vm.qid == 0:
self.vmlabel.setVisible(False)
else:
try:
utils.initialize_widget_with_labels(
widget=self.vmlabel,
qubes_app=self.qubesapp,
holder=self.vm)
self.vmlabel.setVisible(True)
self.vmlabel.setEnabled(not self.vm.is_running())
self.vmlabel.setEnabled(not utils.is_running(self.vm, False))
except qubesadmin.exc.QubesPropertyAccessError:
self.vmlabel.setEnabled(False)
if self.vm.klass == 'AppVM':
try:
utils.initialize_widget_with_vms(
widget=self.template_name,
qubes_app=self.qubesapp,
filter_function=(lambda vm: vm.klass == 'TemplateVM'),
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':
try:
utils.initialize_widget_with_vms(
widget=self.template_name,
qubes_app=self.qubesapp,
@ -393,28 +406,42 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
getattr(vm, 'template_for_dispvms', False)),
holder=self.vm,
property_name='template')
except qubesadmin.exc.QubesPropertyAccessError:
self.template_name.setCurrentIndex(-1)
self.template_name.setEnabled(False)
else:
self.template_name.setEnabled(False)
if self.vm.is_running():
if utils.is_running(self.vm, False):
self.template_name.setEnabled(False)
try:
utils.initialize_widget_with_vms(
widget=self.netVM,
qubes_app=self.qubesapp,
filter_function=(lambda vm: vm.provides_network),
filter_function=(lambda vm:
getattr(vm, 'provides_network', False)),
holder=self.vm,
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)
try:
self.include_in_backups.setChecked(self.vm.include_in_backups)
except qubesadmin.exc.QubesPropertyAccessError:
self.include_in_backups.setEnabled(False)
try:
self.autostart_vm.setChecked(self.vm.autostart)
self.autostart_vm.setVisible(True)
except qubesadmin.exc.QubesPropertyAccessError:
self.autostart_vm.setEnabled(False)
except AttributeError:
self.autostart_vm.setVisible(False)
@ -422,20 +449,24 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.type_label.setText(self.vm.klass)
# 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
if self.vm.netvm:
if getattr(self.vm, 'netvm', None):
self.networking_groupbox.setEnabled(True)
self.ip_label.setText(self.vm.ip or "none")
self.netmask_label.setText(self.vm.visible_netmask or "none")
self.gateway_label.setText(self.vm.visible_gateway or "none")
self.ip_label.setText(getattr(self.vm, 'ip', None) or "none")
self.netmask_label.setText(
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'])
self.dns_label.setText(", ".join(dns_list))
else:
self.networking_groupbox.setEnabled(False)
# max priv storage
try:
self.priv_img_size = self.vm.volumes['private'].size // 1024**2
self.max_priv_storage.setMinimum(self.priv_img_size)
self.max_priv_storage.setValue(self.priv_img_size)
@ -443,7 +474,10 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
max(self.priv_img_size,
self.qubesapp.pools[self.vm.volumes['private'].pool].size
// 1024**2))
except qubesadmin.exc.QubesException:
self.max_priv_storage.setEnabled(False)
try:
self.root_img_size = self.vm.volumes['root'].size // 1024**2
self.root_resize.setValue(self.root_img_size)
self.root_resize.setMinimum(self.root_img_size)
@ -457,6 +491,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.tr("To change system storage size, change properties "
"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)
@ -487,7 +523,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
# include in backups
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.vm.include_in_backups = self.include_in_backups.isChecked()
except qubesadmin.exc.QubesException as ex:
@ -502,6 +539,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
msg.append(str(ex))
# max priv storage
if self.max_priv_storage.isEnabled():
priv_size = self.max_priv_storage.value()
if self.priv_img_size != priv_size:
try:
@ -511,6 +549,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
msg.append(str(ex))
# max sys storage
if self.root_resize.isEnabled():
sys_size = self.root_resize.value()
if self.root_img_size != sys_size:
try:
@ -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
# balancing
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():
QtWidgets.QMessageBox.warning(
self,
@ -536,7 +578,13 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
# Linux specific limit: init memory must not be below
# max_mem_size/10.79 in order to allow scaling up to
# 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.setValue((self.max_mem_size.value() + 9) // 10)
QtWidgets.QMessageBox.warning(
@ -563,7 +611,12 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
dispvm_netvm = getattr(dispvm, 'netvm', None)
if own_netvm == qubesadmin.DEFAULT:
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:
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
if vm and prop == 'template'
and vm.is_running()]
and utils.is_running(vm, False)]
if running_dependencies:
QtWidgets.QMessageBox.warning(
@ -663,27 +716,41 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
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:
self.max_mem_size.setValue(int(self.vm.maxmem))
if vm_memory is None:
self.init_mem.setEnabled(False)
else:
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:
maxmem = self.vm.memory
self.max_mem_size.setValue(int(
self.vm.features.get('qubesmanager.maxmem_value', maxmem)))
maxmem = vm_memory
self.max_mem_size.setValue(
int(utils.get_feature(
self.vm, 'qubesmanager.maxmem_value', maxmem)))
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.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())
# in case VM is HVM
if hasattr(self.vm, "kernel"):
self.kernel_groupbox.setVisible(True)
try:
utils.initialize_widget_with_kernels(
widget=self.kernel,
qubes_app=self.qubesapp,
@ -693,20 +760,22 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
property_name='kernel')
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:
self.kernel_groupbox.setVisible(False)
self.other_groupbox.setVisible(False)
if not hasattr(self.vm, 'default_dispvm'):
self.other_groupbox.setVisible(False)
else:
try:
self.other_groupbox.setVisible(True)
utils.initialize_widget_with_vms(
widget=self.default_dispvm,
qubes_app=self.qubesapp,
filter_function=(lambda vm:
getattr(vm, 'template_for_dispvms', False)),
getattr(
vm, 'template_for_dispvms', False)),
allow_none=True,
allow_default=True,
holder=self.vm,
@ -714,13 +783,18 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
)
self.default_dispvm.currentIndexChanged.connect(
self.check_warn_dispvmnetvm)
except qubesadmin.exc.QubesPropertyAccessError:
self.other_groupbox.setVisible(False)
self.check_warn_dispvmnetvm()
self.update_virt_mode_list()
try:
windows_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_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_off_button.clicked.connect(self.disable_seamless)
if hasattr(self.vm, "template_for_dispvms"):
self.dvm_template_checkbox.setChecked(self.vm.template_for_dispvms)
else:
self.dvm_template_checkbox.setVisible(False)
self.dvm_template_checkbox.setChecked(
getattr(self.vm, 'template_for_dispvms', False))
self.provides_network_checkbox.setChecked(
getattr(self.vm, 'provides_network', False))
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:
self.provides_network_checkbox.setEnabled(False)
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()
def enable_seamless(self):
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):
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):
msg = []
# mem/cpu
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()
curr_maxmem = int(self.vm.maxmem)
curr_maxmem = int(getattr(self.vm, 'maxmem', 0))
if not self.include_in_balancing.isChecked():
maxmem = 0
@ -795,9 +882,11 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
if maxmem != curr_maxmem:
if curr_maxmem > 0:
self.vm.features['qubesmanager.maxmem_value'] = curr_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()
except qubesadmin.exc.QubesException as ex:
@ -909,7 +998,10 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
if hasattr(self, "dev_list"):
devs_attached = self.dev_list.selected_list.count() != 0
else:
try:
devs_attached = bool(list(self.vm.devices['pci'].persistent()))
except qubesadmin.exc.QubesException:
devs_attached = False
if devs_attached:
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
# actual name of the default value, I will add it manually
try:
choices.insert(0, (
"default ({})".format(
self.vm.property_get_default('virt_mode').upper()),
qubesadmin.DEFAULT))
except qubesadmin.exc.QubesException:
choices.insert(0,
("default ({SYSTEM DEFAULT})", qubesadmin.DEFAULT))
try:
utils.initialize_widget_for_property(
widget=self.virt_mode,
choices=choices,
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.currentIndexChanged.connect(self.virt_mode_changed)
@ -967,7 +1066,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
return False
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)
@ -982,9 +1081,14 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.dev_list.add_all_button.setVisible(False)
self.devices_layout.addWidget(self.dev_list)
dom0_devs = list(self.vm.app.domains['dom0'].devices['pci'].available())
try:
dom0_devs = \
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
class DevListWidgetItem(QtWidgets.QListWidgetItem):
@ -1014,7 +1118,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.dmm_warning_adv.hide()
self.dmm_warning_dev.hide()
if self.vm.is_running():
if utils.is_running(self.vm, False):
self.dev_list.setEnabled(False)
self.turn_off_vm_to_modify_devs.setVisible(True)
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.dev_list.setEnabled(not self.vm.is_running())
self.dev_list.setEnabled(not utils.is_running(self.vm, False))
def __apply_devices_tab__(self):
msg = []
if not self.tabWidget.isTabEnabled(self.tabs_indices['devices']):
return msg
try:
old_devs = list(self.vm.devices['pci'].persistent())
@ -1137,6 +1244,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
def __init_services_tab__(self):
self.new_srv_dict = {}
try:
for feature in self.vm.features:
if not feature.startswith('service.'):
continue
@ -1147,6 +1255,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
else ui_settingsdlg.QtCore.Qt.Unchecked)
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("")
@ -1157,9 +1268,12 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
if feature.startswith(service_prefix):
supported_services.add(feature[len(service_prefix):])
if getattr(self.vm, "template", None):
try:
for feature in self.vm.template.features:
if feature.startswith(service_prefix):
supported_services.add(feature[len(service_prefix):])
except qubesadmin.exc.QubesDaemonCommunicationError:
pass
for service in sorted(supported_services):
self.service_line_edit.addItem(service)
@ -1205,6 +1319,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
def __apply_services_tab__(self):
msg = []
if not self.tabWidget.isTabEnabled(self.tabs_indices['services']):
return msg
try:
for i in range(self.services_list.count()):
item = self.services_list.item(i)

View File

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

View File

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