Browse Source

Merge remote-tracking branch 'qubesos/pr/55'

* qubesos/pr/55:
  Fixed according to @marmarek request
  Fixed according to @marmarek request
  Added 'configure no strict reset for PCI devices' button
  Adding a button to list PCI devices and enable no-strict-reset
  Fixed some indents and whitespaces
  Fixed python2 in settings.py
Marek Marczykowski-Górecki 6 years ago
parent
commit
81413907bf
5 changed files with 272 additions and 50 deletions
  1. 62 0
      qubesmanager/device_list.py
  2. 85 49
      qubesmanager/settings.py
  3. 2 0
      rpm_spec/qmgr.spec
  4. 115 0
      ui/devicelist.ui
  5. 8 1
      ui/settingsdlg.ui

+ 62 - 0
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 <http://www.gnu.org/licenses/>.
+#
+#
+
+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)

+ 85 - 49
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? '
-                      '<br/> All VM settings and data will be irrevocably'
-                      ' deleted. <br/> If you are sure, please enter this '
-                      'VM\'s name below.'))
-
+                    '<br/> All VM settings and data will be irrevocably'
+                    ' deleted. <br/> 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()

+ 2 - 0
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
 

+ 115 - 0
ui/devicelist.ui

@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Dialog</class>
+ <widget class="QDialog" name="Dialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Select devices</string>
+  </property>
+  <widget class="QDialogButtonBox" name="buttonBox">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>260</y>
+     <width>391</width>
+     <height>32</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+   <property name="standardButtons">
+    <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+   </property>
+  </widget>
+  <widget class="QListWidget" name="device_list">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>40</y>
+     <width>391</width>
+     <height>171</height>
+    </rect>
+   </property>
+   <property name="selectionMode">
+    <enum>QAbstractItemView::MultiSelection</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>391</width>
+     <height>31</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Which PCI devices should use the no strict reset option?</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_2">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>220</y>
+     <width>391</width>
+     <height>41</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <italic>true</italic>
+    </font>
+   </property>
+   <property name="text">
+    <string>Note: use this option only if &quot;unable to reset PCI device&quot; error occurs. </string>
+   </property>
+   <property name="wordWrap">
+    <bool>true</bool>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>Dialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>Dialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

+ 8 - 1
ui/settingsdlg.ui

@@ -29,7 +29,7 @@
         <locale language="English" country="UnitedStates"/>
        </property>
        <property name="currentIndex">
-        <number>2</number>
+        <number>3</number>
        </property>
        <widget class="QWidget" name="basic_tab">
         <property name="locale">
@@ -1216,6 +1216,13 @@ border-width: 1px;</string>
            </item>
           </layout>
          </item>
+         <item>
+          <widget class="QPushButton" name="no_strict_reset_button">
+           <property name="text">
+            <string>Configure strict reset for PCI devices</string>
+           </property>
+          </widget>
+         </item>
         </layout>
        </widget>
        <widget class="QWidget" name="apps_tab">