#!/usr/bin/python3 # # Copyright (C) 2024 Red Hat, Inc. # SPDX-License-Identifier: LGPL-2.1-or-later from anacondalib import VirtInstallMachineCase, disk_images, pixel_tests_ignore, run_boot from installer import Installer from review import Review from storage import EFI_PARTITION_DEFAULT_SIZE_MB, Storage from storagelib import StorageCase # pylint: disable=import-error from testlib import nondestructive, skipImage, test_main # pylint: disable=import-error from utils import pretend_live_workstation_iso @nondestructive class TestStorageCockpitIntegration(VirtInstallMachineCase, StorageCase): def reachCockpitStorage(self): b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) i.open() i.reach(i.steps.INSTALLATION_METHOD) s.wait_scenario_visible("use-configured-storage", False) s.check_disk_selected("vda") s.enter_cockpit_storage() def cockpitCreateBootloaderPartitions(self): self.click_dropdown(self.card_row("Storage", 1), "Create partition table") self.confirm() self.click_dropdown(self.card_row("Storage", 2), "Create partition") self.dialog({"size": 524, "type": "efi", "mount_point": "/boot/efi"}) self.click_dropdown(self.card_row("Storage", 3), "Create partition") self.dialog({"size": 1070, "type": "ext4", "mount_point": "/boot"}) def testBasicLiveISO(self): """ Description: Test that the 'Modify storage' button is available on the first page of the installer when using a live ISO. References: - https://bugzilla.redhat.com/show_bug.cgi?id=2336488 """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) pretend_live_workstation_iso(self, i, m) i.open() i.reach(i.steps.INSTALLATION_METHOD) s.check_disk_selected("vda") s.enter_cockpit_storage() def testEncryptedUnlock(self): """ Description: Test installation on an encrypted partition created in Cockpit storage Expected results: - An encrypted partition can be created and used as the root mount point - An encrypted partition can be reformatted in Cockpit storage and reused - The partition can be deleted in Cockpit storage References: - https://bugzilla.redhat.com/show_bug.cgi?id=2354497 """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) dev = "vda" self.reachCockpitStorage() self.cockpitCreateBootloaderPartitions() self.click_dropdown(self.card_row("Storage", 4), "Create partition") self.dialog({ "type": "ext4", "mount_point": "/", "crypto": self.default_crypto_type, "passphrase": "redhat", "passphrase2": "redhat", }) s.exit_cockpit_storage() s.check_scenario_selected("use-configured-storage") # Check that choosing another storage scenario without diving into that, # preserves the visibility of "Use configured storage" s.set_scenario("erase-all") s.wait_scenario_available("use-configured-storage") s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) r.check_disk_row(dev, "/", "vda3", "14.5 GB", False, None, True) # Exit the cockpit-storage iframe i.reach_on_sidebar(i.steps.INSTALLATION_METHOD) s.enter_cockpit_storage() # Format the partition again with 'Reuse existing encryption' # Check that cockpit sends us the information also after re-formatting (rhbz#2354497) self.click_dropdown(self.card_row("Storage", 4), "Format") self.dialog_wait_open() self.dialog_set_val("type", "ext4") self.dialog_set_val("mount_point", "/") self.dialog_set_val("old_passphrase", "redhat") b.set_checked("#dialog-confirm", val=True) self.dialog_apply() self.dialog_wait_close() s.exit_cockpit_storage() # Do not crash if encrypted device got deleted s.enter_cockpit_storage() self.click_dropdown(self.card_row("Storage", 4), "Delete") b.set_checked("#dialog-confirm", val=True) self.dialog_apply() b.wait_not_present(self.card_row("Storage", name="vda3")) s.exit_cockpit_storage() def _createLVMVolumes(self, browser, dev, vgname, root_size=6010): self.reachCockpitStorage() self.cockpitCreateBootloaderPartitions() self.click_devices_dropdown("Create LVM2 volume group") self.dialog({"name": vgname, "disks": {dev: True}}) self.click_card_row("Storage", name=vgname) browser.click(self.card_button("LVM2 logical volumes", "Create new logical volume")) self.dialog({"name": "root", "size": root_size}) self.click_card_row("LVM2 logical volumes", 1) self.click_card_dropdown("Unformatted data", "Format") self.dialog({"type": "ext4", "mount_point": "/"}) browser.click(self.card_parent_link()) browser.click(self.card_button("LVM2 logical volumes", "Create new logical volume")) self.dialog({"name": "home", "size": 8120}) self.click_card_row("LVM2 logical volumes", 1) self.click_card_dropdown("Unformatted data", "Format") self.dialog({"type": "ext4", "mount_point": "/home"}) browser.click(self.card_parent_link()) browser.click(self.card_button("LVM2 logical volumes", "Create new logical volume")) self.dialog({"name": "swap", "size": 377}) self.click_card_row("LVM2 logical volumes", 3) self.click_card_dropdown("Unformatted data", "Format") self.dialog({"type": "swap"}) def testLVM(self): """ Description: Test installation on an LVM volume created in Cockpit storage Expected results: - An LVM volume can be created and used for the root mount point """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) self._createLVMVolumes(b, "/dev/vda", "fedoravg") # Exit the cockpit-storage iframe b.switch_to_top() def checkStorageReview(prefix=""): disk = "vda" r.check_disk(disk, "16.1 GB vda (Virtio Block Device)", prefix=prefix) r.check_disk_row(disk, "/boot/efi", "vda1", EFI_PARTITION_DEFAULT_SIZE_MB, False, "efi", prefix=prefix) r.check_disk_row(disk, "/boot", "vda2", "1.07 GB", False, prefix=prefix) r.check_disk_row(disk, "/", "vda3, LVM", "6.01 GB", False, prefix=prefix) r.check_disk_row(disk, "/home", "vda3, LVM", "8.12 GB", False, prefix=prefix) r.check_disk_row(disk, "swap", "vda3, LVM", "377 MB", False, prefix=prefix) s.return_to_installation() # verify storage layout review in Cockpit storage exit dialog checkStorageReview(prefix="#cockpit-storage-integration-check-storage-dialog") b.assert_pixels( "#cockpit-storage-integration-check-storage-dialog", "cockpit-storage-integration-check-storage-dialog-LVM", ignore=pixel_tests_ignore, ) s.return_to_installation_confirm() s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) # verify review screen checkStorageReview() def _testStandardPartition(self, rootfs_format): b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) dev = "vda" i.open() i.reach(i.steps.INSTALLATION_METHOD) s.wait_scenario_visible("use-configured-storage", False) s.check_disk_selected("vda") s.enter_cockpit_storage() self.click_dropdown(self.card_row("Storage", 1), "Create partition table") self.confirm() self.click_dropdown(self.card_row("Storage", 2), "Create partition") self.dialog({"size": 100, "type": "efi", "mount_point": "/boot/efi"}) self.click_dropdown(self.card_row("Storage", 3), "Create partition") self.dialog({"size": 1070, "type": "ext4", "mount_point": "/boot"}) self.click_dropdown(self.card_row("Storage", 4), "Create partition") self.dialog({"type": rootfs_format, "mount_point": "/"}) b.switch_to_top() def checkStorageReview(prefix=""): r.check_disk(dev, "16.1 GB vda (Virtio Block Device)", prefix=prefix) r.check_disk_row(dev, "/boot/efi", "vda1", "99.6 MB", False, "efi", prefix=prefix) r.check_disk_row(dev, "/boot", "vda2", "1.07 GB", False, prefix=prefix) r.check_disk_row(dev, "/", "vda3", "14.9 GB", False, prefix=prefix) s.return_to_installation() checkStorageReview(prefix="#cockpit-storage-integration-check-storage-dialog") s.return_to_installation_confirm() i.reach(i.steps.REVIEW) checkStorageReview() # Check fstab fstab = m.execute("cat /etc/fstab") self.assertTrue("/mnt/sysroot/boot/efi auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot/boot auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot auto noauto 0 0" in fstab) def testStandardPartitionExt4(self): """ Description: Test installation on an ext4 partition created in Cockpit storage Expected results: - An ext4 partition can be created and used for the root mount point """ self._testStandardPartition("ext4") def testStandardPartitionXFS(self): """ Description: Test installation on an XFS partition created in Cockpit storage" Expected results: - An xfs partition can be created and used for the root mount point """ self._testStandardPartition("xfs") def testMissingBootEfiPartition(self): """ Description: Test that a storage layout without /boot/efi shows a backend validation error Expected results: - The check storage dialog shows backend validation errors References: - https://bugzilla.redhat.com/show_bug.cgi?id=2406021 """ b = self.browser s = Storage(b, self.machine) self.reachCockpitStorage() self.click_dropdown(self.card_row("Storage", 1), "Create partition table") self.confirm() self.click_dropdown(self.card_row("Storage", 2), "Create partition") self.dialog({"size": 1070, "type": "ext4", "mount_point": "/boot"}) self.click_dropdown(self.card_row("Storage", 3), "Create partition") self.dialog({"type": "ext4", "mount_point": "/"}) b.switch_to_top() s.return_to_installation("EFI System Partition must be mounted on one of /boot/efi") b.wait_in_text("#cockpit-storage-integration-check-storage-dialog", "Storage requirements not met") def testPayloadSizeCheckExt4(self): """ Description: Test free space check on partitions with XFS filesystem" Expected results: - A warning about insufficient space is showed to the user on the review screen - The warning prevents continuing with the installation - There are hints beside the checked mount points in case of failed check - The link for changing partitioning layout moves user to the right page """ self._testPayloadSizeCheck("ext4") def testPayloadSizeCheckXFS(self): """ Description: Test free space check on partitions with ext4 filesystem" Expected results: - A warning about insufficient space is showed to the user on the review screen - The warning prevents continuing with the installation - There are hints beside the checked mount points in case of failed check - The link for changing partitioning layout moves user to the right page """ self._testPayloadSizeCheck("xfs") def _testPayloadSizeCheck(self, rootfs_format): b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) dev = "vda" i.open() i.reach(i.steps.INSTALLATION_METHOD) s.wait_scenario_visible("use-configured-storage", False) s.check_disk_selected(dev) s.enter_cockpit_storage() self.click_dropdown(self.card_row("Storage", 1), "Create partition table") self.confirm() self.click_dropdown(self.card_row("Storage", 2), "Create partition") self.dialog({"size": 100, "type": "efi", "mount_point": "/boot/efi"}) self.click_dropdown(self.card_row("Storage", 3), "Create partition") self.dialog({"size": 1000, "type": "ext4", "mount_point": "/boot"}) self.click_dropdown(self.card_row("Storage", 4), "Create partition") self.dialog({"size": 1300, "type": rootfs_format, "mount_point": "/"}) s.exit_cockpit_storage() s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) r.check_size_error() # The value starts with 1 (is non-zero) r.check_available_size_error_value("1") # check that there is a hint with required size beside the device r.check_disk(dev, f"16.1 GB {dev} (Virtio Block Device)") r.check_disk_row(dev, "/", f"{dev}3", "1.30 GB", False) r.check_disk_mount_point_helper_text(dev, "/", "Needs at least") i.check_next_disabled() # Test going back from the alert to the proper page b.click(f"#{i.steps.REVIEW}-change-partition-layout") s.wait_scenario_available("use-configured-storage") def testPayloadSizeCheckBtrfs(self): """ Description: Test free space check on partitions with btrfs filesystem" Expected results: - A warning about insufficient space is showed to the user on the review screen - The warning prevents continuing with the installation - There are hints beside the checked mount points in case of failed check - The link for changing partitioning layout moves user to the right page """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) self._createBtrfsSubvolumes(b, vol_size=1700) s.exit_cockpit_storage() s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) r.check_size_error() # The value starts with 1 (is non-zero) r.check_available_size_error_value("1") # check that there is a hint with required size beside the device dev = "vda" r.check_disk(dev, "16.1 GB vda (Virtio Block Device)") r.check_disk_row(dev, "/", "vda3", "1.70 GB", False) r.check_disk_mount_point_helper_text(dev, "/", "Needs at least") i.check_next_disabled() def testPayloadSizeCheckLVM(self): """ Description: Test free space check on partitions on LVM logical volume" Expected results: - A warning about insufficient space is showed to the user on the review screen - The warning prevents continuing with the installation - There are hints beside the checked mount points in case of failed check - The link for changing partitioning layout moves user to the right page """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) self._createLVMVolumes(b, "/dev/vda", "fedoravg", root_size=1700) s.exit_cockpit_storage() s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) r.check_size_error() # The value starts with 1 (is non-zero) r.check_available_size_error_value("1") # check that there is a hint with required size beside the device dev = "vda" r.check_disk(dev, "16.1 GB vda (Virtio Block Device)") r.check_disk_row(dev, "/", "vda3, LVM", "1.70 GB", False) r.check_disk_mount_point_helper_text(dev, "/", "Needs at least") i.check_next_disabled() def testBtrfsTopLevelVolume(self): """ Description: Test installation on a btrfs top-level volume created in Cockpit storage Expected results: - A btrfs top-level volume can be created and used for the root mount point - The review screen shows the correct mount points - The fstab file is updated correctly """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) i.open() i.reach(i.steps.INSTALLATION_METHOD) s.wait_scenario_visible("use-configured-storage", False) s.check_disk_selected("vda") s.enter_cockpit_storage() self.cockpitCreateBootloaderPartitions() self.click_dropdown(self.card_row("Storage", 4), "Create partition") self.dialog({"type": "btrfs", "mount_point": "/"}) s.exit_cockpit_storage() s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) # verify review screen dev = "vda" r.check_disk(dev, "16.1 GB vda (Virtio Block Device)") r.check_disk_row(dev, "/boot/efi", "vda1", EFI_PARTITION_DEFAULT_SIZE_MB, False) r.check_disk_row(dev, "/boot", "vda2", "1.07 GB", False) r.check_disk_row(dev, "/", "vda3", "14.5 GB", False) # Check fstab fstab = m.execute("cat /etc/fstab") self.assertTrue("/mnt/sysroot/boot/efi auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot/boot auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot auto noauto,subvol=/ 0 0" in fstab) def _createBtrfsSubvolumes(self, browser, vol_size=None): self.reachCockpitStorage() self.cockpitCreateBootloaderPartitions() self.click_dropdown(self.card_row("Storage", 4), "Create partition") if vol_size: self.dialog({"type": "btrfs", "size": vol_size}) else: self.dialog({"type": "btrfs"}) self.click_card_row("Storage", name="top-level") browser.click(self.card_button("btrfs subvolume", "Create subvolume")) self.dialog({"name": "root", "mount_point": "/"}) browser.click(self.card_button("btrfs subvolume", "Create subvolume")) self.dialog({"name": "unused"}) browser.click(self.card_button("btrfs subvolume", "Create subvolume")) self.dialog({"name": "home", "mount_point": "/home"}) def testBtrfsSubvolumes(self): """ Description: Test installation on a btrfs subvolume created in Cockpit storage Expected results: - A btrfs subvolume can be created and used for the root and home mount points - The review screen shows the correct mount points - The fstab file is updated correctly """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) self._createBtrfsSubvolumes(b) s.exit_cockpit_storage() s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) # verify review screen dev = "vda" r.check_disk(dev, "16.1 GB vda (Virtio Block Device)") r.check_disk_row(dev, "/boot/efi", "vda1", EFI_PARTITION_DEFAULT_SIZE_MB, False) r.check_disk_row(dev, "/boot", "vda2", "1.07 GB", False) r.check_disk_row(dev, "/", "vda3", "14.5 GB", False) r.check_disk_row(dev, "/home", "vda3", "14.5 GB", False) # Check fstab fstab = m.execute("cat /etc/fstab") self.assertTrue("/mnt/sysroot/boot/efi auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot/boot auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot btrfs noauto,subvol=root 0 0" in fstab) self.assertTrue("/mnt/sysroot/home btrfs noauto,subvol=home 0 0" in fstab) def testBtrfsAndMountPointAssignment(self): """ Description: Test installation on a btrfs subvolume created in Cockpit storage by selecting these in the Mount Point Assignment screen Expected results: - A btrfs subvolume can be created and used through mount point assignment screen for the root and home mount points - The review screen shows the correct mount points - The fstab file is updated correctly """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) self._createBtrfsSubvolumes(b) s.exit_cockpit_storage() s.set_scenario("mount-point-mapping") i.next(next_page=i.steps.CUSTOM_MOUNT_POINT) s.check_mountpoint_row(1, "/", "root", False, "btrfs") s.check_mountpoint_row(2, "/boot/efi", "vda1", False, "EFI System Partition") s.check_mountpoint_row(3, "/boot", "vda2", False, "ext4") s.check_mountpoint_row(4, "/home", "home", False, "btrfs") s.select_mountpoint_row_reformat(1) s.select_mountpoint_row_reformat(3) s.select_mountpoint_row_reformat(4, False) s.check_mountpoint_row(1, "/", "root", True, "btrfs") s.check_mountpoint_row(2, "/boot/efi", "vda1", False, "EFI System Partition") s.check_mountpoint_row(3, "/boot", "vda2", True, "ext4") s.check_mountpoint_row(4, "/home", "home", False, "btrfs") i.next(should_fail=True) i.reach(i.steps.REVIEW) # verify review screen dev = "vda" r.check_disk(dev, "16.1 GB vda (Virtio Block Device)") r.check_disk_row(dev, "/boot/efi", "vda1", EFI_PARTITION_DEFAULT_SIZE_MB, False) r.check_disk_row(dev, "/boot", "vda2", "1.07 GB", True, "ext4") r.check_disk_row(dev, "/", "vda3", "14.5 GB", True, "btrfs subvolume") r.check_disk_row(dev, "/home", "vda3", "14.5 GB", False) # Check fstab fstab = m.execute("cat /etc/fstab") self.assertTrue("/mnt/sysroot/boot/efi auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot/boot auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot btrfs noauto,subvol=root 0 0" in fstab) self.assertTrue("/mnt/sysroot/home btrfs noauto,subvol=home 0 0" in fstab) def _createStratisPool(self, browser, poolname="fedorapool", passphrase=None): self.reachCockpitStorage() self.cockpitCreateBootloaderPartitions() self.click_devices_dropdown("Create Stratis pool") pool_dialog = { "name": poolname, "disks": {"/dev/vda": True}, "overprov.on": False, } if passphrase: pool_dialog.update({ "encrypt_pass.on": True, "passphrase": passphrase, "passphrase2": passphrase, }) self.dialog(pool_dialog) self.click_card_row("Storage", name=poolname) browser.click(self.card_button("Stratis filesystems", "Create new filesystem")) self.dialog({"name": "root", "size": 5000, "mount_point": "/"}) browser.click(self.card_button("Stratis filesystems", "Create new filesystem")) self.dialog({"name": "home", "size": 5000, "mount_point": "/home"}) def testStratis(self): """ Description: Test installation on a Stratis pool and filesystems created in Cockpit storage Expected results: - A Stratis pool and filesystems can be created and used for the root and home mount points - The review screen shows the correct mount points and devices """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) self._createStratisPool(b) s.exit_cockpit_storage() s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) # verify review screen dev = "vda" r.check_disk(dev, "16.1 GB vda (Virtio Block Device)") r.check_disk_row(dev, "/boot/efi", "vda1", EFI_PARTITION_DEFAULT_SIZE_MB, False) r.check_disk_row(dev, "/boot", "vda2", "1.07 GB", False) r.check_disk_row(dev, "/", "vda3, Stratis", fs_type="stratis xfs") r.check_disk_row(dev, "/home", "vda3, Stratis", fs_type="stratis xfs") def testStratisEncrypted(self): """ Description: Test installation on an encrypted Stratis pool created in Cockpit storage Expected results: - An encrypted Stratis pool and filesystems can be created and used for the root and home mount points - Cockpit passes the passphrase to the installer so that the pool can be used without an explicit unlock step - The review screen shows the devices as encrypted """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) self._createStratisPool(b, passphrase="redhat") s.exit_cockpit_storage() s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) # verify review screen dev = "vda" r.check_disk(dev, "16.1 GB vda (Virtio Block Device)") r.check_disk_row(dev, "/boot/efi", "vda1", EFI_PARTITION_DEFAULT_SIZE_MB, False) r.check_disk_row(dev, "/boot", "vda2", "1.07 GB", False) r.check_disk_row(dev, "/", "vda3, Stratis", fs_type="stratis xfs", is_encrypted=True) r.check_disk_row(dev, "/home", "vda3, Stratis", fs_type="stratis xfs", is_encrypted=True) @disk_images([("", 15), ("", 15), ("", 15)]) def testRAIDUnsupportedNesting(self): """ Description: Check that partitions created on a RAID device which is on top of partitions are considered unsupported References: - https://issues.redhat.com/browse/INSTALLER-4141 TODO: This logic is currently implemented in the frontend, but it should be moved to the backend Once this is moved to backend we should implement a unit test for this and we can remove the Web UI test """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) i.open() i.reach(i.steps.INSTALLATION_METHOD) s.select_disks([("vdb", True), ("vda", True), ("vdc", True)]) s.enter_cockpit_storage() # Create ESP and boot partitions on vda self.click_dropdown(self.card_row("Storage", 1), "Create partition table") self.confirm() self.click_dropdown(self.card_row("Storage", 2), "Create partition") self.dialog({"size": 100, "type": "efi", "mount_point": "/boot/efi"}) self.click_dropdown(self.card_row("Storage", 3), "Create partition") self.dialog({"size": 1070, "type": "ext4", "mount_point": "/boot"}) # Create partitions for the RAID device on vdb and vdc self.click_dropdown(self.card_row("Storage", 5), "Create partition table") self.dialog({"type": "gpt"}) self.click_dropdown(self.card_row("Storage", 6), "Create partition") self.dialog({"type": "empty"}) self.click_dropdown(self.card_row("Storage", 7), "Create partition table") self.dialog({"type": "gpt"}) self.click_dropdown(self.card_row("Storage", 8), "Create partition") self.dialog({"type": "empty"}) # Create RAID0 on vda3, vdb1, and vdc1 self.click_dropdown(self.card_header("Storage"), "Create MDRAID device") self.dialog_wait_open() self.dialog_set_val("level", "raid0") self.dialog_set_val("disks", {"/dev/vdb1": True, "/dev/vdc1": True}) self.dialog_set_val("name", "SOMERAID") self.dialog_apply() self.dialog_wait_close() b.wait_visible(self.card_row("Storage", name="/dev/md/SOMERAID")) # Create / partition on the RAID device self.click_dropdown(self.card_row("Storage", name="/dev/md/SOMERAID"), "Create partition table") self.dialog({"type": "gpt"}) self.click_dropdown(self.card_row("Storage", 10), "Create partition") self.dialog({"type": "ext4", "mount_point": "/"}) # Exit the cockpit-storage iframe and return to installation b.switch_to_top() s.return_to_installation("Invalid RAID configuration detected.") @skipImage("Flaky: FIXME: INSTALLER-4917", "*") @disk_images([("", 15), ("", 15), ("", 15)]) @run_boot("bios", "efi") def testUnsupportedBootloaderOnMDRAID(self): """ Description: Check that the installer does not support bootloader partitions on MDRAID devices Expected results: - The installer should show an error message when trying to use a bootloader partition on a MDRAID device - The installer should block the user from proceeding with the installation """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) self.addCleanup(m.execute, "mdadm --zero-superblock /dev/vda /dev/vdb /dev/vdc") self.addCleanup(m.execute, "mdadm --stop /dev/md/SOMERAID") i.open() i.reach(i.steps.INSTALLATION_METHOD) s.select_disks([("vdb", True), ("vda", True), ("vdc", True)]) s.enter_cockpit_storage() # Create RAID device on vda, vdb, and vdc self.click_dropdown(self.card_header("Storage"), "Create MDRAID device") self.dialog_wait_open() self.dialog_set_val("level", "raid0") self.dialog_set_val("disks", {"/dev/vda": True, "/dev/vdb": True, "/dev/vdc": True}) self.dialog_set_val("name", "SOMERAID") self.dialog_apply() self.dialog_wait_close() # Create bootloader and /boot partitions on vda # Create ESP, /boot and / partitions on the RAID device self.click_dropdown(self.card_row("Storage", 4), "Create partition table") self.dialog({"type": "gpt"}) self.click_dropdown(self.card_row("Storage", 5), "Create partition") if self.is_efi: self.dialog({"size": 100, "type": "efi", "mount_point": "/boot/efi"}) else: self.dialog({"size": 1, "type": "biosboot"}) self.click_dropdown(self.card_row("Storage", 6), "Create partition") self.dialog({"size": 1070, "type": "ext4", "mount_point": "/boot"}) self.click_dropdown(self.card_row("Storage", 7), "Create partition") self.dialog({"type": "ext4", "mount_point": "/"}) # Exit the cockpit-storage iframe and return to installation b.switch_to_top() bootloaderType = "efi" if self.is_efi else "biosboot" s.return_to_installation( f"'{bootloaderType}' partition on MDRAID device SOMERAID found. Bootloader partitions on MDRAID devices are not supported." ) def _createRAID(self, disks, level, name): """ Create a RAID device with the given disks, level and name """ b = self.browser self.click_dropdown(self.card_header("Storage"), "Create MDRAID device") self.dialog_wait_open() self.dialog_set_val("level", level) self.dialog_set_val("disks", disks) self.dialog_set_val("name", name) self.dialog_apply() self.dialog_wait_close() b.wait_visible(self.card_row("Storage", name=f"md/{name}")) def _delete_storage(self, index): """ Delete the storage device with the given index """ b = self.browser self.click_dropdown(self.card_row("Storage", index), "Delete") self.dialog_apply() self.dialog_wait_close() b.wait_not_present(self.card_row("Storage", index)) @disk_images([("", 15), ("", 15), ("", 15)]) def testCalculateSelectedDisksRAID(self): """ Description: Verify that the disk selection changes when creating a RAID device Expected results: - The disk selection should be updated to include the RAID device and exclude the member disks when the RAID device is created - The disk selection should be updated to include the member disks and exclude the RAID device when the RAID device is deleted """ b = self.browser m = self.machine i = Installer(b, m) s = Storage(b, m) i.open() i.reach(i.steps.INSTALLATION_METHOD) s.select_disks([("vdb", True), ("vda", True), ("vdc", True)]) s.enter_cockpit_storage() # Create RAID device on vda, vdb, and vdc self._createRAID({"/dev/vda": True, "/dev/vdb": True, "/dev/vdc": True}, "raid0", "SOMERAID") s.exit_cockpit_storage() s.check_disk_selected("MDRAID-SOMERAID") s.enter_cockpit_storage() # Delete RAID device and expect the disk selection to be set back to the parent disks self._delete_storage(4) s.exit_cockpit_storage() for disk in ["vda", "vdb", "vdc"]: s.check_disk_selected(disk) # Do the same for RAID device with 2 disks s.enter_cockpit_storage() # Create RAID device on vda, vdb self._createRAID({"/dev/vda": True, "/dev/vdb": True}, "raid0", "SOMERAID") s.exit_cockpit_storage() s.check_disk_selected("MDRAID-SOMERAID") s.check_disk_selected("vdc") # Delete RAID device and expect the disk selection to be set back to the parent disks s.enter_cockpit_storage() self._delete_storage(4) s.exit_cockpit_storage() for disk in ["vda", "vdb", "vdc"]: s.check_disk_selected(disk) s.enter_cockpit_storage() # Create RAID device on vda, vdb, and vdc, then delete it and create a new one with another name # before returning to installation self._createRAID({"/dev/vda": True, "/dev/vdb": True, "/dev/vdc": True}, "raid0", "SOMERAID") b.switch_to_top() s.return_to_installation() s.return_to_installation_cancel() b.switch_to_frame("cockpit-storage") self._delete_storage(4) self._createRAID({"/dev/vda": True, "/dev/vdb": True, "/dev/vdc": True}, "raid0", "SOMERAID2") s.exit_cockpit_storage() s.check_disk_selected("MDRAID-SOMERAID2") s.enter_cockpit_storage() # Delete it for the test cleanup self._delete_storage(4) @disk_images([("", 15), ("", 15)]) def testFormattedDiskRootfs(self): """ Description: Test that a formatted disk can be used as the root filesystem Expected results: - The disk can be formatted and used as the root filesystem - The review screen should show the correct mount points - The fstab file should be updated correctly """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) i.open() i.reach(i.steps.INSTALLATION_METHOD) s.select_disks([("vdb", True), ("vda", True)]) s.enter_cockpit_storage() self.click_dropdown(self.card_row("Storage", 1), "Create partition table") self.confirm() self.click_dropdown(self.card_row("Storage", 2), "Create partition") self.dialog({"size": 100, "type": "efi", "mount_point": "/boot/efi"}) self.click_dropdown(self.card_row("Storage", 3), "Create partition") self.dialog({"size": 1070, "type": "ext4", "mount_point": "/boot"}) self.click_dropdown(self.card_row("Storage", 4), "Create partition") self.dialog({"type": "ext4", "mount_point": "/"}) self.click_dropdown(self.card_row("Storage", 5), "Format") self.dialog({"type": "ext4", "mount_point": "/home"}) s.exit_cockpit_storage() s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) # verify review screen dev = "vda" r.check_disk(dev, "16.1 GB vda (Virtio Block Device)") r.check_disk_row(dev, "/boot/efi", "vda1", "99.6 MB", False) r.check_disk_row(dev, "/boot", "vda2", "1.07 GB", False) r.check_disk_row(dev, "/", "vda3", "14.9 GB", False) dev = "vdb" r.check_disk(dev, "16.1 GB vdb (Virtio Block Device)") r.check_disk_row(dev, "/home", "", "16.1 GB", False) # Check fstab fstab = m.execute("cat /etc/fstab") self.assertTrue("/mnt/sysroot/boot/efi auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot/boot auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot/home auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot auto noauto 0 0" in fstab) # Check that mount point assignment also works i.reach_on_sidebar(i.steps.INSTALLATION_METHOD) s.select_mountpoint([("vda", True), ("vdb", True)]) s.select_mountpoint_row_device(1, "vda3") s.select_mountpoint_row_device(2, "vda1") s.select_mountpoint_row_device(3, "vda2") s.add_mountpoint_row() s.select_mountpoint_row_device(4, "vdb") s.select_mountpoint_row_mountpoint(4, "/home") # https://fedoraproject.org/wiki/Changes/Anaconda_Drop_Support_UEFI_on_MBR # Fedora installer has phased out support for booting in UEFI mode on MBR-partitioned disks. @run_boot("bios") def testMBRParttable(self): """ Description: Test that a MBR formatted disk can be used for the installation Expected results: - The 'Use configured storage' scenario should be available when using a MBR formatted disk - The review screen shows the correct mount points - The fstab file should be updated correctly """ b = self.browser m = self.machine i = Installer(b, m, scenario="use-configured-storage") s = Storage(b, m) r = Review(b, m) dev = "vda" i.open() i.reach(i.steps.INSTALLATION_METHOD) s.wait_scenario_visible("use-configured-storage", False) s.check_disk_selected(dev) s.enter_cockpit_storage() self.click_dropdown(self.card_row("Storage", 1), "Create partition table") self.dialog_set_val("type", "dos") self.dialog_apply() self.dialog_wait_close() self.click_dropdown(self.card_row("Storage", 2), "Create partition") self.dialog({"size": 1070, "type": "ext4", "mount_point": "/boot"}) self.click_dropdown(self.card_row("Storage", 3), "Create partition") self.dialog({"type": "btrfs", "mount_point": "/"}) s.exit_cockpit_storage() s.set_scenario("use-configured-storage") i.reach(i.steps.REVIEW) # verify review screen r.check_disk(dev, "16.1 GB vda (Virtio Block Device)") r.check_disk_row(dev, "/boot", "vda1", "1.07 GB", False) r.check_disk_row(dev, "/", "vda2", "15.0 GB", False) # Check fstab fstab = m.execute("cat /etc/fstab") self.assertTrue("/mnt/sysroot/boot auto noauto 0 0" in fstab) self.assertTrue("/mnt/sysroot auto noauto,subvol=/ 0 0" in fstab) if __name__ == '__main__': test_main()