diff --git a/qubesmanager/device_list.py b/qubesmanager/device_list.py new file mode 100644 index 0000000..fb1fbae --- /dev/null +++ b/qubesmanager/device_list.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with this program; if not, see . +# +# + +from . import ui_devicelist # pylint: disable=no-name-in-module +from PyQt4 import QtGui, QtCore # pylint: disable=import-error + + +class PCIDeviceListWindow(ui_devicelist.Ui_Dialog, QtGui.QDialog): + def __init__(self, vm, qapp, dev_list, no_strict_reset_list, parent=None): + super(PCIDeviceListWindow, self).__init__(parent) + + self.vm = vm + self.qapp = qapp + self.dev_list = dev_list + self.no_strict_reset_list = no_strict_reset_list + + self.setupUi(self) + + self.connect( + self.buttonBox, QtCore.SIGNAL("accepted()"), self.save_and_apply) + self.connect( + self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) + + self.ident_list = {} + self.fill_device_list() + + def fill_device_list(self): + self.device_list.clear() + + for i in range(self.dev_list.selected_list.count()): + text = self.dev_list.selected_list.item(i).text() + ident = self.dev_list.selected_list.item(i).ident + self.device_list.addItem(text) + self.ident_list[text] = ident + if ident in self.no_strict_reset_list: + self.device_list.setItemSelected( + self.device_list.item(i), True) + + def reject(self): + self.done(0) + + def save_and_apply(self): + self.no_strict_reset_list.clear() + self.no_strict_reset_list.extend([self.ident_list[item.text()] for item + in self.device_list.selectedItems()]) + self.done(0) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 0ac5985..30eed29 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python3 # # The Qubes OS Project, http://www.qubes-os.org # @@ -39,12 +39,14 @@ import qubesadmin.exc from . import utils from . import multiselectwidget from . import thread_monitor +from . import device_list from .appmenu_select import AppmenuSelectManager from . import firewall from PyQt4 import QtCore, QtGui # pylint: disable=import-error -from . import ui_settingsdlg #pylint: disable=no-name-in-module +from . import ui_settingsdlg # pylint: disable=no-name-in-module + # pylint: disable=too-many-instance-attributes class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): @@ -124,6 +126,11 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.connect(self.dev_list, QtCore.SIGNAL("selected_changed()"), self.devices_selection_changed) + self.no_strict_reset_button.clicked.connect( + self.strict_reset_button_pressed) + self.current_strict_reset_list = [] + self.new_strict_reset_list = [] + self.define_strict_reset_devices() ####### services tab self.__init_services_tab__() @@ -141,7 +148,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): def reject(self): self.done(0) - #needed not to close the dialog before applying changes + # needed not to close the dialog before applying changes def accept(self): pass @@ -279,8 +286,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.delete_vm_button.setEnabled(not self.vm.is_running()) if self.vm.is_running(): - self.delete_vm_button.setText(self.tr('Delete VM ' - '(cannot delete a running VM)')) + self.delete_vm_button.setText( + self.tr('Delete VM (cannot delete a running VM)')) if self.vm.qid == 0: self.vmlabel.setVisible(False) @@ -328,13 +335,13 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): except AttributeError: self.autostart_vm.setVisible(False) - #type + # type 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') - #networking info + # networking info if self.vm.netvm: self.networking_groupbox.setEnabled(True) self.ip_label.setText(self.vm.ip or "none") @@ -343,7 +350,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): else: self.networking_groupbox.setEnabled(False) - #max priv storage + # max priv storage 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) @@ -357,7 +364,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): def __apply_basic_tab__(self): msg = [] - #vm label changed + # vm label changed try: if self.vmlabel.isVisible(): if self.vmlabel.currentIndex() != self.label_idx: @@ -366,7 +373,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) - #vm template changed + # vm template changed try: if self.template_name.currentIndex() != self.template_idx: self.vm.template = \ @@ -374,14 +381,14 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) - #vm netvm changed + # vm netvm changed try: if self.netVM.currentIndex() != self.netvm_idx: self.vm.netvm = self.netvm_list[self.netVM.currentIndex()] except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) - #include in backups + # include in backups try: if self.vm.include_in_backups != \ self.include_in_backups.isChecked(): @@ -389,7 +396,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) - #run_in_debug_mode + # run_in_debug_mode try: if self.run_in_debug_mode.isVisible(): if self.vm.debug != self.run_in_debug_mode.isChecked(): @@ -397,7 +404,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) - #autostart_vm + # autostart_vm try: if self.autostart_vm.isVisible(): if self.vm.autostart != self.autostart_vm.isChecked(): @@ -405,7 +412,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) - #max priv storage + # max priv storage priv_size = self.max_priv_storage.value() if self.priv_img_size != priv_size: try: @@ -413,7 +420,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): 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_img_size != sys_size: try: @@ -423,7 +430,6 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): return msg - def check_mem_changes(self): if self.max_mem_size.value() < self.init_mem.value(): QtGui.QMessageBox.warning( @@ -456,10 +462,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if not t_monitor.success: QtGui.QMessageBox.warning(None, - self.tr("Error!"), - self.tr("ERROR: {}").format( - t_monitor.error_msg)) - + self.tr("Error!"), + self.tr("ERROR: {}").format( + t_monitor.error_msg)) def _rename_vm(self, t_monitor, name): try: @@ -473,7 +478,6 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): t_monitor.set_finished() - def rename_vm(self): new_vm_name, ok = QtGui.QInputDialog.getText( @@ -502,10 +506,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self, self.tr('Delete VM'), self.tr('Are you absolutely sure you want to delete this VM? ' - '
All VM settings and data will be irrevocably' - ' deleted.
If you are sure, please enter this ' - 'VM\'s name below.')) - + '
All VM settings and data will be irrevocably' + ' deleted.
If you are sure, please enter this ' + 'VM\'s name below.')) if ok and answer == self.vm.name: self._run_in_thread(self._remove_vm) @@ -546,7 +549,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): def __init_advanced_tab__(self): - #mem/cpu + # mem/cpu # qubes_memory = QubesHost().memory_total/1024 self.init_mem.setValue(int(self.vm.memory)) @@ -564,7 +567,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): bool(self.vm.features.get('service.meminfo-writer', True))) 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"): self.kernel_groupbox.setVisible(True) self.kernel_list, self.kernel_idx = utils.prepare_kernel_choice( @@ -594,7 +597,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): def __apply_advanced_tab__(self): msg = [] - #mem/cpu + # mem/cpu try: if self.init_mem.value() != int(self.vm.memory): self.vm.memory = self.init_mem.value() @@ -607,9 +610,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) - #include_in_memory_balancing applied in services tab + # include_in_memory_balancing applied in services tab - #in case VM is not Linux + # in case VM is not Linux if hasattr(self.vm, "kernel") and self.kernel_groupbox.isVisible(): try: if self.kernel.currentIndex() != self.kernel_idx: @@ -618,7 +621,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): except qubesadmin.exc.QubesException as ex: msg.append(str(ex)) - #vm default_dispvm changed + # vm default_dispvm changed try: if self.default_dispvm.currentIndex() != self.default_dispvm_idx: self.vm.default_dispvm = \ @@ -739,7 +742,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.ident = ident persistent = [ass.ident.replace('_', ':') - for ass in self.vm.devices['pci'].persistent()] + for ass in self.vm.devices['pci'].persistent()] for name, ident in devs: if ident in persistent: @@ -760,29 +763,53 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.vm.is_running(): self.dev_list.setEnabled(False) self.turn_off_vm_to_modify_devs.setVisible(True) + self.no_strict_reset_button.setEnabled(False) else: self.dev_list.setEnabled(True) self.turn_off_vm_to_modify_devs.setVisible(False) self.update_pvh_dont_support_devs() - def __apply_devices_tab__(self): msg = [] try: old = [ass.ident.replace('_', ':') - for ass in self.vm.devices['pci'].persistent()] + for ass in self.vm.devices['pci'].persistent()] new = [self.dev_list.selected_list.item(i).ident - for i in range(self.dev_list.selected_list.count())] + for i in range(self.dev_list.selected_list.count())] for ident in new: if ident not in old: + options = {} + if ident in self.new_strict_reset_list: + options['no-strict-reset'] = True ass = devices.DeviceAssignment( self.vm.app.domains['dom0'], ident.replace(':', '_'), - persistent=True) + persistent=True, options=options) self.vm.devices['pci'].attach(ass) + elif (ident in self.current_strict_reset_list) != \ + (ident in self.new_strict_reset_list): + current_assignment = None + for assignment in self.vm.devices['pci'].assignments( + persistent=True): + if assignment.ident.replace("_", ":") == ident: + current_assignment = assignment + break + if current_assignment is None: + # it would be very weird if this happened + msg.append(self.tr("Error re-assigning device ") + + ident) + continue + + self.vm.devices['pci'].detach(current_assignment) + + current_assignment.options['no-strict-reset'] = \ + (ident in self.new_strict_reset_list) + + self.vm.devices['pci'].attach(current_assignment) + for ass in self.vm.devices['pci'].assignments(persistent=True): if ass.ident.replace('_', ':') not in new: self.vm.devices['pci'].detach(ass) @@ -829,6 +856,18 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): self.dev_list.setEnabled(True) self.pvh_dont_support_devs.setVisible(False) + def define_strict_reset_devices(self): + for assignment in self.vm.devices['pci'].assignments(): + if assignment.options.get('no-strict-reset', False): + self.current_strict_reset_list.append( + assignment.ident.replace('_', ':')) + self.new_strict_reset_list = self.current_strict_reset_list.copy() + + def strict_reset_button_pressed(self): + device_list_window = device_list.PCIDeviceListWindow( + self.vm, self.qapp, self.dev_list, self.new_strict_reset_list, self) + device_list_window.exec() + ######## applications tab def refresh_apps_in_vm(self, t_monitor): @@ -881,7 +920,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): service = feature[len('service.'):] item = QtGui.QListWidgetItem(service) item.setCheckState(ui_settingsdlg.QtCore.Qt.Checked - if self.vm.features[feature] + if self.vm.features[feature] else ui_settingsdlg.QtCore.Qt.Unchecked) self.services_list.addItem(item) self.new_srv_dict[service] = self.vm.features[feature] @@ -922,7 +961,6 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): item = self.services_list.takeItem(row) del self.new_srv_dict[str(item.text())] - def services_item_clicked(self, item): if str(item.text()) == 'meminfo-writer': if item.checkState() == ui_settingsdlg.QtCore.Qt.Checked: @@ -932,7 +970,6 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): if self.include_in_balancing.isChecked(): self.include_in_balancing.setChecked(False) - def __apply_services_tab__(self): msg = [] @@ -968,9 +1005,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): return msg - ######### firewall tab related - def set_fw_model(self, model): self.fw_model = model self.rulesTreeView.setModel(model) @@ -1049,10 +1084,10 @@ def handle_exception(exc_type, exc_value, exc_traceback): while stacktrace: (filename, line, func, txt) = stacktrace.pop() strace += "----\n" - strace += "line: %s\n" %txt - strace += "func: %s\n" %func - strace += "line no.: %d\n" %line - strace += "file: %s\n" %filename + strace += "line: %s\n" % txt + strace += "func: %s\n" % func + strace += "line no.: %d\n" % line + strace += "file: %s\n" % filename msg_box = QtGui.QMessageBox() msg_box.setDetailedText(strace) @@ -1070,13 +1105,14 @@ def handle_exception(exc_type, exc_value, exc_traceback): parser = QubesArgumentParser(vmname_nargs=1) parser.add_argument('--tab', metavar='TAB', - action='store', - choices=VMSettingsWindow.tabs_indices.keys()) + action='store', + choices=VMSettingsWindow.tabs_indices.keys()) parser.set_defaults( tab='basic', ) + def main(args=None): args = parser.parse_args(args) vm = args.domains.pop() diff --git a/rpm_spec/qmgr.spec b/rpm_spec/qmgr.spec index f41b8cf..0b3a28e 100644 --- a/rpm_spec/qmgr.spec +++ b/rpm_spec/qmgr.spec @@ -91,6 +91,7 @@ rm -rf $RPM_BUILD_ROOT %{python3_sitelib}/qubesmanager/qube_manager.py %{python3_sitelib}/qubesmanager/utils.py %{python3_sitelib}/qubesmanager/bootfromdevice.py +%{python3_sitelib}/qubesmanager/device_list.py %{python3_sitelib}/qubesmanager/resources_rc.py @@ -107,6 +108,7 @@ rm -rf $RPM_BUILD_ROOT %{python3_sitelib}/qubesmanager/ui_releasenotes.py %{python3_sitelib}/qubesmanager/ui_informationnotes.py %{python3_sitelib}/qubesmanager/ui_qubemanager.py +%{python3_sitelib}/qubesmanager/ui_devicelist.py %{python3_sitelib}/qubesmanager/i18n/qubesmanager_*.qm %{python3_sitelib}/qubesmanager/i18n/qubesmanager_*.ts diff --git a/ui/devicelist.ui b/ui/devicelist.ui new file mode 100644 index 0000000..502b049 --- /dev/null +++ b/ui/devicelist.ui @@ -0,0 +1,115 @@ + + + Dialog + + + + 0 + 0 + 400 + 300 + + + + Select devices + + + + + 10 + 260 + 391 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 0 + 40 + 391 + 171 + + + + QAbstractItemView::MultiSelection + + + + + + 0 + 0 + 391 + 31 + + + + Which PCI devices should use the no strict reset option? + + + + + + 0 + 220 + 391 + 41 + + + + + true + + + + Note: use this option only if "unable to reset PCI device" error occurs. + + + true + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/settingsdlg.ui b/ui/settingsdlg.ui index 8593eea..9f0d40f 100644 --- a/ui/settingsdlg.ui +++ b/ui/settingsdlg.ui @@ -29,7 +29,7 @@ - 2 + 3 @@ -1216,6 +1216,13 @@ border-width: 1px; + + + + Configure strict reset for PCI devices + + +