diff --git a/tests/uitests/data/capabilities/dac-caps-template.xml b/tests/uitests/data/capabilities/dac-caps-template.xml new file mode 100644 index 000000000..5e2091b4e --- /dev/null +++ b/tests/uitests/data/capabilities/dac-caps-template.xml @@ -0,0 +1,89 @@ + + + + + i686 + + + + + + + + + + + + + + + 2097152 + 524288 + 0 + 0 + + + + + + + + + + + + + 4194304 + 524288 + 0 + 0 + + + + + + + + + + + + + + + dac + 0 + +%(UID)s:+%(GID)s + +%(UID)s:+%(GID)s + + + + + hvm + + 32 + /usr/bin/test-hv + + + + + + + + + + xen + + 32 + /usr/bin/test-hv + + + + + + + + + + + diff --git a/tests/uitests/test_addhardware.py b/tests/uitests/test_addhardware.py index 5212432fc..cb75ded76 100644 --- a/tests/uitests/test_addhardware.py +++ b/tests/uitests/test_addhardware.py @@ -2,11 +2,44 @@ # See the COPYING file in the top-level directory. import os +import tempfile import tests from tests.uitests import utils as uiutils +def _search_permissions_decorator(fn): + """ + Decorator to set up necessary bits to test disk permission search + """ + def wrapper(self, *args, **kwargs): + # Generate capabilities XML from a template, with out + # UID/GID inserted as the intended emulator permissions + capsfile = (os.path.dirname(__file__) + + "/data/capabilities/dac-caps-template.xml") + capsdata = open(capsfile).read() % { + "UID": os.getuid(), "GID": os.getgid()} + tmpcaps = tempfile.NamedTemporaryFile( + prefix="virt-manager-uitests-caps") + tmpcapspath = tmpcaps.name + open(tmpcapspath, "w").write(capsdata) + + # We mock a qemu URI to trigger the permissions check + uri = (tests.utils.URIs.test_full + + ",fakeuri=qemu:///system,caps=%s" % tmpcapspath) + + # Create a temporary directory that we can manipulate perms + tmpobj = tempfile.TemporaryDirectory( + prefix="virtinst-test-search") + tmpdir = tmpobj.name + try: + os.chmod(tmpdir, 0o000) + fn(self, uri, tmpdir, *args, **kwargs) + finally: + os.chmod(tmpdir, 0o777) + return wrapper + + class AddHardware(uiutils.UITestCase): """ UI tests for virt-manager's VM addhardware window @@ -122,12 +155,16 @@ class AddHardware(uiutils.UITestCase): # Disk with some tweaks addhw = self._open_addhw_window(details) tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.find("GiB", "spin button").text = "1.5" tab.find("Bus type:", "combo box").click() tab.find("VirtIO", "menu item").click() tab.find("Advanced options", "toggle button").click_expander() tab.find("Cache mode:", "combo box").click() tab.find("none", "menu item").click() + # Size too big + tab.find("GiB", "spin button").text = "2000" + finish.click() + self._click_alert_button("not enough free space", "Close") + tab.find("GiB", "spin button").text = "1.5" finish.click() uiutils.check_in_loop(lambda: details.active) @@ -135,6 +172,8 @@ class AddHardware(uiutils.UITestCase): addhw = self._open_addhw_window(details) tab = self._select_hw(addhw, "Storage", "storage-tab") tab.find_fuzzy("Select or create", "radio").click() + finish.click() + self._click_alert_button("storage path must be specified", "OK") tab.find("storage-browse", "push button").click() browse = self.app.root.find("vmm-storage-browser") @@ -179,6 +218,8 @@ class AddHardware(uiutils.UITestCase): browse.find("Choose Volume", "push button").click() self.assertTrue("/diskvol1" in tab.find("storage-entry").text) finish.click() + self._click_alert_button("already in use by", "No") + finish.click() self._click_alert_button("already in use by", "Yes") uiutils.check_in_loop(lambda: details.active) @@ -204,6 +245,106 @@ class AddHardware(uiutils.UITestCase): finish.click() uiutils.check_in_loop(lambda: details.active) + @_search_permissions_decorator + def testAddDiskSearchPermsCheckbox(self, uri, tmpdir): + """ + Test search permissions 'no' and checkbox case + """ + self.app.uri = uri + details = self._open_details_window() + + # Say 'No' but path should still work due to test driver + addhw = self._open_addhw_window(details) + finish = addhw.find("Finish", "push button") + tab = self._select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo1.img" + tab.find("storage-entry").text = path + finish.click() + self._click_alert_button("emulator may not have", "No") + uiutils.check_in_loop(lambda: details.active) + + # Say 'don't ask again' + addhw = self._open_addhw_window(details) + tab = self._select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo2.img" + tab.find("storage-entry").text = path + finish.click() + alert = self.app.root.find_fuzzy("vmm dialog", "alert") + alert.find_fuzzy("Don't ask", "check box").click() + self._click_alert_button("emulator may not have", "No") + uiutils.check_in_loop(lambda: details.active) + + # Confirm it doesn't ask about path again + addhw = self._open_addhw_window(details) + tab = self._select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo3.img" + tab.find("storage-entry").text = path + finish.click() + uiutils.check_in_loop(lambda: details.active) + + @_search_permissions_decorator + def testAddDiskSearchPermsSuccess(self, uri, tmpdir): + """ + Select 'Yes' for search perms fixing + """ + self.app.uri = uri + details = self._open_details_window() + + # Say 'Yes' + addhw = self._open_addhw_window(details) + finish = addhw.find("Finish", "push button") + tab = self._select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo1.img" + tab.find("storage-entry").text = path + finish.click() + self._click_alert_button("emulator may not have", "Yes") + uiutils.check_in_loop(lambda: details.active) + + # Confirm it doesn't ask about path again + addhw = self._open_addhw_window(details) + tab = self._select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo3.img" + tab.find("storage-entry").text = path + finish.click() + uiutils.check_in_loop(lambda: details.active) + + @_search_permissions_decorator + def testAddDiskSearchPermsFail(self, uri, tmpdir): + """ + Force perms fixing to fail + """ + self.app.uri = uri + self.app.open(break_setfacl=True) + details = self._open_details_window() + + # Say 'Yes' and it should fail, then blacklist the paths + addhw = self._open_addhw_window(details) + finish = addhw.find("Finish", "push button") + tab = self._select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo1.img" + tab.find("storage-entry").text = path + finish.click() + self._click_alert_button("emulator may not have", "Yes") + alert = self.app.root.find("vmm dialog", "alert") + alert.find_fuzzy("Errors were encountered", "label") + alert.find_fuzzy("Don't ask", "check box").click() + alert.find_fuzzy("OK", "push button").click() + uiutils.check_in_loop(lambda: details.active) + + # Confirm it doesn't ask about path again + addhw = self._open_addhw_window(details) + tab = self._select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo2.img" + tab.find("storage-entry").text = path + finish.click() + uiutils.check_in_loop(lambda: details.active) def testAddNetworks(self): """ diff --git a/tests/uitests/utils.py b/tests/uitests/utils.py index 9a0ab8c08..495ffb662 100644 --- a/tests/uitests/utils.py +++ b/tests/uitests/utils.py @@ -455,7 +455,8 @@ class VMMDogtailApp(object): return bool(self._proc and self._proc.poll() is None) def open(self, extra_opts=None, check_already_running=True, use_uri=True, - window_name=None, xmleditor_enabled=False, keyfile=None): + window_name=None, xmleditor_enabled=False, keyfile=None, + break_setfacl=False): extra_opts = extra_opts or [] if tests.utils.TESTCONFIG.debug: @@ -476,6 +477,8 @@ class VMMDogtailApp(object): testoptions = [] if xmleditor_enabled: testoptions.append("xmleditor-enabled") + if break_setfacl: + testoptions.append("break-setfacl") if keyfile: import atexit import tempfile diff --git a/virtManager/addhardware.py b/virtManager/addhardware.py index 1179837f8..f04c82f63 100644 --- a/virtManager/addhardware.py +++ b/virtManager/addhardware.py @@ -1428,8 +1428,6 @@ class vmmAddHardware(vmmGObjectUI): return False def _validate_device(self, dev): - dev.validate() - if dev.DEVICE_TYPE == "disk": if self.addstorage.validate_device(dev) is False: return False @@ -1440,6 +1438,8 @@ class vmmAddHardware(vmmGObjectUI): if dev.DEVICE_TYPE == "hostdev": self._validate_hostdev_collision(dev) + dev.validate() + def _build_xmleditor_device(self, srcdev): xml = self._xmleditor.get_xml() log.debug("Using XML from xmleditor:\n%s", xml) diff --git a/virtManager/device/addstorage.py b/virtManager/device/addstorage.py index 87f9f59cb..2a5253a20 100644 --- a/virtManager/device/addstorage.py +++ b/virtManager/device/addstorage.py @@ -49,7 +49,7 @@ class vmmAddStorage(vmmGObjectUI): pool.refresh() avail = int(pool.get_available()) return float(avail / 1024.0 / 1024.0 / 1024.0) - except Exception: + except Exception: # pragma: no cover log.exception("Error determining host disk space") return -1 @@ -108,9 +108,8 @@ class vmmAddStorage(vmmGObjectUI): "following directories:") details = "" for p, error in errors.items(): - if p not in broken_paths: - continue - details += "%s : %s\n" % (p, error) + if p in broken_paths: + details += "%s : %s\n" % (p, error) details += "\nIt is very likely the VM will fail to start up." log.debug("Permission errors:\n%s", details) @@ -136,7 +135,7 @@ class vmmAddStorage(vmmGObjectUI): storage_area = self.widget("storage-box") storage_area.set_sensitive(can_storage) - if not can_storage: + if not can_storage: # pragma: no cover storage_tooltip = _("Connection does not support storage" " management.") use_storage.set_sensitive(True) @@ -145,7 +144,7 @@ class vmmAddStorage(vmmGObjectUI): def get_default_path(self, name, collideguest=None): pool = self.conn.get_default_pool() if not pool: - return + return # pragma: no cover fmt = self.conn.get_default_storage_format() suffix = virtinst.StorageVolume.get_file_extension_for_format(fmt) @@ -189,7 +188,7 @@ class vmmAddStorage(vmmGObjectUI): disk.get_vol_install().format = fmt else: log.debug("path=%s can not use default prefs format=%s, " - "not setting it", disk.path, fmt) + "not setting it", disk.path, fmt) # pragma: no cover return disk @@ -199,13 +198,6 @@ class vmmAddStorage(vmmGObjectUI): disk.validate() - isfatal, errmsg = disk.is_size_conflict() - if not isfatal and errmsg: - # Fatal errors are reported when setting 'size' - res = self.err.ok_cancel(_("Not Enough Free Space"), errmsg) - if not res: - return False - # Disk collision names = disk.is_conflict_disk() if names: diff --git a/virtManager/virtmanager.py b/virtManager/virtmanager.py index a77fa57e7..68c97b379 100755 --- a/virtManager/virtmanager.py +++ b/virtManager/virtmanager.py @@ -211,6 +211,7 @@ class CLITestOptionsClass: self.no_events = _get("no-events") self.xmleditor_enabled = _get("xmleditor-enabled") self.gsettings_keyfile = _get_value("gsettings-keyfile") + self.break_setfacl = _get("break-setfacl") if opts: print("Unknown --test-options keys: %s" % opts) @@ -251,6 +252,14 @@ def main(): if options.test_no_events: CLITestOptions.no_events = True + if CLITestOptions.break_setfacl: + import virtinst.diskbackend + def fake_search(*args, **kwargs): + raise RuntimeError("Fake search fix fail from test suite") + virtinst.diskbackend.SETFACL = "getfacl" + # pylint: disable=protected-access + virtinst.diskbackend._fix_perms_chmod = fake_search + # With F27 gnome+wayland we need to set these before GTK import os.environ["GSETTINGS_SCHEMA_DIR"] = BuildConfig.gsettings_dir if CLITestOptions.first_run: