#!/usr/bin/python3 # # Copyright (C) 2022 Red Hat, Inc. # SPDX-License-Identifier: LGPL-2.1-or-later import os from anacondalib import VirtInstallMachineCase, disk_images, pixel_tests_ignore from installer import Installer from storage import Storage from testlib import nondestructive, test_main # pylint: disable=import-error from utils import check_cockpit_module_js_error TEST_DIR = os.path.dirname(__file__) ROOT_DIR = os.path.dirname(TEST_DIR) BOTS_DIR = f'{ROOT_DIR}/bots' @nondestructive class TestStorageBasic(VirtInstallMachineCase): def testLocalStandardDisks(self): """ Description: Test that the installer can detect and select local standard disks Expected results: - The installer should detect the local standard disks - The installer should allow the user to select the disks - Without any disks selected, the installer should not allow the user to continue - When the selected disk[s] does not have enough space for the installation, the installer should not allow the user to continue - The disk selection should be preserved when moving back and forth between screens - Removing the second disk and rescanning should reflect only the remaining disk (rhbz#2456923) """ b = self.browser i = Installer(b, self.machine) s = Storage(b, self.machine) i.open() i.reach(i.steps.INSTALLATION_METHOD) # Check the auto-selected disk's details s.check_disk_selected("vda", selected=True, size="16.1 GB") # Wait for the form to be ready before taking a screenshot i.check_next_disabled(False) b.assert_pixels( "#app", "storage-step-basic", ignore=pixel_tests_ignore, ) # Unselect the only available disk s.select_disks([("vda", False)]) s.wait_no_disks() # Check the next buton is disabled if no disks are selected i.check_next_disabled() # This attaches a disk to the running VM # However, since the storage module initialization is long completed # the newly added disk, will not be visible in the UI, # until the test clicks on the re-scan button dev = "vdb" vdb_image = self.add_disk(2, target=dev) s.rescan_disks(["vda", dev]) # Check the next button is disabled when not sufficient disk space is available s.select_disks([("vda", False), (dev, True)]) i.check_next_disabled() # Check that the disk selection persists when moving next and back s.select_disks([("vda", True), (dev, False)]) s.check_disk_selected("vda", True) s.check_disk_selected(dev, False) i.next() i.back() s.check_disk_selected("vda", True) s.check_disk_selected(dev, False) # Try unselecting both disks and expect and error s.select_disks([("vda", False), (dev, False)]) s.wait_no_disks() # Check the next button is disabled if no disks are selected i.check_next_disabled() # Pixel test the storage step when no disks are selected b.assert_pixels( "#app", "storage-step-no-disks", ignore=pixel_tests_ignore, ) # Check that disk selection is kept on Next and Back s.select_disks([(dev, True), ("vda", True)]) i.next() i.back() for disk in ["vda", dev]: s.check_disk_selected(disk) # rhbz#2456923: unplug second disk while it remains in the installer's selection, then rescan self.rem_disk(vdb_image) s.rescan_disks(["vda"]) s.check_disk_selected("vda", selected=True) s.check_disk_selected(dev, selected=False) i.check_next_disabled(False) def testScenarioSelection(self): """ Description: Test that the use-free-space scenario is conditionally available and the user selection for scenario is kept when moving back and forth between screens """ b = self.browser i = Installer(b, self.machine) s = Storage(b, self.machine) i.open() i.reach(i.steps.INSTALLATION_METHOD) # Check the default mode s.check_scenario_selected("erase-all") # Check that when an unpartitioned disk is selected the "Use free space" scenario is hidden s.wait_scenario_visible("use-free-space", False) # Check that when a partitioned disk is selected the "Use free space" scenario is visible s.partition_disk("/dev/vda", [("10GiB", "lvmpv")]) s.rescan_disks() s.wait_scenario_visible("use-free-space") # Storage Configuration # The choice is preserved (stored in the backend). # The choice is available only if the partitioning was reset # (there is enough free space) s.set_scenario("use-free-space") i.next() i.back() s.check_scenario_selected("use-free-space") def testPartitioningObject(self): """ Description: Test which partitioning object ends up being the AppliedPartitioning when we go back and forward the storage steps """ b = self.browser i = Installer(b, self.machine) s = Storage(b, self.machine) # Read partitioning data before we do anything created_partitioning = s.dbus_get_created_partitioning() # Go to Review step i.open() i.reach(i.steps.REVIEW) # Read partitioning data after we went to Review step new_applied_partitioning = s.dbus_get_applied_partitioning() new_created_partitioning = s.dbus_get_created_partitioning() # A new AUTOMATIC partitioning object should be created each time the user enters the review page self.assertEqual(len(created_partitioning) + 1, len(new_created_partitioning)) # The applied partitioning object should be the last one created self.assertEqual(new_applied_partitioning, new_created_partitioning[-1]) created_partitioning = new_created_partitioning # Create a few partitioning objects and ensure that these will not mess up with the user's # configuration for _ in range(10): s.dbus_create_partitioning("AUTOMATIC") # Go back to the Disk Configuration page and re-enter the review screen. # This should create again a new partitioning object and apply it # no matter how many partitioning objects were created before i.reach_on_sidebar(i.steps.INSTALLATION_METHOD) i.reach(i.steps.REVIEW) new_applied_partitioning = s.dbus_get_applied_partitioning() new_created_partitioning = s.dbus_get_created_partitioning() self.assertEqual(len(created_partitioning) + 11, len(new_created_partitioning)) self.assertEqual(new_applied_partitioning, new_created_partitioning[-1]) # The applied partitioning should be reset also when going back to installation method i.click_step_on_sidebar(i.steps.INSTALLATION_METHOD) new_applied_partitioning = s.dbus_get_applied_partitioning() self.assertEqual(new_applied_partitioning, "") def testCockpitJsErrorHandling(self): """ Description: Test that the installer can handle JS errors in the storage module and that it does not crash the installer Setup: - Generate a JS error in the storage module Expected results: - The installer should not crash - The installer should show a critical error dialog """ b = self.browser m = self.machine i = Installer(b, m) s = Storage(b, m) i.open() i.reach(i.steps.INSTALLATION_METHOD) s.enter_cockpit_storage() check_cockpit_module_js_error(b, "Storage") def _testDiskSelectionLVM_partition_disk(self): b = self.browser m = self.machine s = Storage(b, m) s.partition_disk("/dev/vda", [("10GiB", "lvmpv")]) s.partition_disk("/dev/vdb", [("10GiB", "lvmpv")]) # Create VG on LUKS with the two available disks m.execute(""" vgcreate -y vg /dev/vda1 /dev/vdb1 lvcreate -y -n lv -l 100%FREE vg mkfs.ext4 -F /dev/vg/lv vgchange -a n vg """) @disk_images([("", 15), ("", 15)]) def testDiskSelectionLVM(self): """ Description: Test that the user can't select one PV when VG group spans across multiple disks Setup: - Create a VG group on LVM with two disks - Create a LV on the VG group Expected results: - The installer should not allow the user to select one of the disks and proceed with the installation """ b = self.browser m = self.machine i = Installer(b, m) s = Storage(b, m) i.open() i.reach(i.steps.INSTALLATION_METHOD) s.set_scenario("erase-all") i.reach(i.steps.STORAGE_CONFIGURATION) i.next(should_fail=True) b.wait_in_text( "#anaconda-screen-storage-configuration-step-notification", "Selected disks vda contain volume group 'vg' that also uses further unselected disks" ) if __name__ == '__main__': test_main()