diff --git a/Makefile b/Makefile
index 8186587e9..c8281990d 100644
--- a/Makefile
+++ b/Makefile
@@ -188,7 +188,7 @@ payload: bots
.PHONY: images
images: bots
- bots/image-download $(TEST_OS) debian-stable ubuntu-stable
+ bots/image-download $(TEST_OS) debian-stable ubuntu-stable fedora-41
$(UPDATES_IMG): prepare-test-deps
test/prepare-updates-img
diff --git a/src/apis/storage_partitioning.js b/src/apis/storage_partitioning.js
index 1b32e9e87..873f1f0f8 100644
--- a/src/apis/storage_partitioning.js
+++ b/src/apis/storage_partitioning.js
@@ -95,6 +95,35 @@ export const partitioningSetEncrypt = ({ encrypt, partitioning }) => {
});
};
+/**
+ * @param {string} partitioning DBus path to a partitioning
+ * @param {string} scheme autopartitioning scheme
+ */
+export const partitioningSetHomeReuse = async ({ partitioning, scheme }) => {
+ const request = await getPartitioningRequest({ partitioning });
+
+ const configurationSchemeToDBus = {
+ BTRFS: cockpit.variant("i", 1),
+ LVM: cockpit.variant("i", 2),
+ LVM_THINP: cockpit.variant("i", 3),
+ PLAIN: cockpit.variant("i", 0),
+ };
+ request["partitioning-scheme"] = configurationSchemeToDBus?.[scheme];
+
+ request["reused-mount-points"] = cockpit.variant("as", ["/home"]);
+ if (scheme === "PLAIN") {
+ // "/" will be reallocated by autopartitioning
+ request["removed-mount-points"] = cockpit.variant("as", ["/", "/boot", "bootloader"]);
+ } else {
+ // "LVM", "BTRFS", "LVM_THINP"
+ // "/" can't be reallocated by autopartitioing as it is sharing container device with /home
+ request["removed-mount-points"] = cockpit.variant("as", ["/boot", "bootloader"]);
+ request["reformatted-mount-points"] = cockpit.variant("as", ["/"]);
+ }
+
+ await setPartitioningRequest({ partitioning, request });
+};
+
/**
* @returns {Promise} The request of automatic partitioning
*/
diff --git a/src/components/Common.jsx b/src/components/Common.jsx
index 8567dc0fb..2ab6117a0 100644
--- a/src/components/Common.jsx
+++ b/src/components/Common.jsx
@@ -25,6 +25,7 @@ export const LanguageContext = createContext(null);
export const OsReleaseContext = createContext(null);
export const RuntimeContext = createContext(null);
export const StorageContext = createContext(null);
+export const StorageDefaultsContext = createContext(null);
export const SystemTypeContext = createContext(null);
export const TargetSystemRootContext = createContext(null);
export const UsersContext = createContext(null);
@@ -66,13 +67,16 @@ const ModuleContextWrapper = ({ children, state }) => {
const SystemInfoContextWrapper = ({ children, conf, osRelease }) => {
const systemType = conf?.["Installation System"].type;
+ const defaultScheme = conf?.Storage.default_scheme;
return (
-
- {children}
-
+
+
+ {children}
+
+
);
diff --git a/src/components/review/StorageReview.jsx b/src/components/review/StorageReview.jsx
index 4f8dd8fb0..07d3ab5d7 100644
--- a/src/components/review/StorageReview.jsx
+++ b/src/components/review/StorageReview.jsx
@@ -77,6 +77,7 @@ const DeviceRow = ({ disk }) => {
const requests = partitioning.requests;
const deviceData = devices?.[disk];
+ const reuseHomeRequest = requests.find(request => request["reused-mount-points"]);
if (!deviceData) {
return null;
@@ -86,11 +87,18 @@ const DeviceRow = ({ disk }) => {
const size = cockpit.format_bytes(devices[device].size.v);
const request = requests.find(request => request["device-spec"] === device);
const format = devices[device].formatData.type.v;
- const action = (
- request === undefined || request.reformat
- ? (format ? cockpit.format(_("format as $0"), format) : null)
- : ((format === "biosboot") ? format : _("mount"))
- );
+
+ let action = null;
+ if (reuseHomeRequest?.["reused-mount-points"].includes(mount)) {
+ action = _("mount");
+ } else if ((request === undefined || request.reformat) && format) {
+ action = cockpit.format(_("format as $0"), format);
+ } else if (format === "biosboot") {
+ action = format;
+ } else {
+ action = _("mount");
+ }
+
const parents = getParentPartitions(devices, device);
const showMaybeType = () => {
if (checkDeviceOnStorageType(devices, device, "lvmvg")) {
diff --git a/src/components/steps.js b/src/components/steps.js
index 82cb3984e..127384fe3 100644
--- a/src/components/steps.js
+++ b/src/components/steps.js
@@ -11,16 +11,18 @@ import { Page as PageAccounts } from "./users/Accounts.jsx";
const _ = cockpit.gettext;
export const getSteps = (...args) => {
+ const diskConfigurationSteps = [
+ new PageMountPointMapping(...args),
+ new PageDiskEncryption(...args),
+ ];
const stepsOrder = [
new PageInstallationLanguage(...args),
new PageInstallationMethod(...args),
{
id: "disk-configuration",
+ isHidden: diskConfigurationSteps.every(step => step.isHidden),
label: _("Disk configuration"),
- steps: [
- new PageMountPointMapping(...args),
- new PageDiskEncryption(...args),
- ]
+ steps: diskConfigurationSteps,
},
new PageAccounts(...args),
new PageReviewConfiguration(...args),
diff --git a/src/components/storage/Common.jsx b/src/components/storage/Common.jsx
index 0ff1c3c8c..a6bc27f8f 100644
--- a/src/components/storage/Common.jsx
+++ b/src/components/storage/Common.jsx
@@ -37,6 +37,7 @@ import {
createPartitioning,
getDeviceTree,
partitioningSetEncrypt,
+ partitioningSetHomeReuse,
partitioningSetPassphrase,
} from "../../apis/storage_partitioning.js";
@@ -129,6 +130,7 @@ export const useMountPointConstraints = () => {
};
export const getNewPartitioning = async ({
+ autopartScheme,
currentPartitioning,
method = "AUTOMATIC",
storageScenarioId,
@@ -143,7 +145,14 @@ export const getNewPartitioning = async ({
const part = await createPartitioning({ method });
- if (currentPartitioning?.method === method && method === "AUTOMATIC" && currentPartitioning.requests[0].encrypted) {
+ if (storageScenarioId === "home-reuse") {
+ await partitioningSetHomeReuse({ partitioning: part, scheme: autopartScheme });
+ }
+
+ if (currentPartitioning?.method === method &&
+ method === "AUTOMATIC" &&
+ storageScenarioId !== "home-reuse" &&
+ currentPartitioning.requests[0].encrypted) {
await partitioningSetEncrypt({ encrypt: true, partitioning: part });
await partitioningSetPassphrase({ partitioning: part, passphrase: currentPartitioning.requests[0].passphrase });
}
diff --git a/src/components/storage/DiskEncryption.jsx b/src/components/storage/DiskEncryption.jsx
index a634abc27..ef38acf47 100644
--- a/src/components/storage/DiskEncryption.jsx
+++ b/src/components/storage/DiskEncryption.jsx
@@ -194,7 +194,7 @@ export class Page {
constructor (isBootIso, storageScenarioId) {
this.component = DiskEncryption;
this.id = "disk-encryption";
- this.isHidden = ["mount-point-mapping", "use-configured-storage"].includes(storageScenarioId);
+ this.isHidden = ["mount-point-mapping", "use-configured-storage", "home-reuse"].includes(storageScenarioId);
this.label = _("Disk encryption");
this.title = _("Encrypt the selected devices?");
this.usePageInit = usePageInit;
diff --git a/src/components/storage/HelpAutopartOptions.jsx b/src/components/storage/HelpAutopartOptions.jsx
index ee778c66c..da5c29074 100644
--- a/src/components/storage/HelpAutopartOptions.jsx
+++ b/src/components/storage/HelpAutopartOptions.jsx
@@ -8,4 +8,6 @@ export const helpUseFreeSpace = _("Keep current disk layout and use available sp
export const helpMountPointMapping = _("Assign partitions to mount points. Useful for pre-configured custom layouts.");
+export const helpHomeReuse = _("Replace current installation, but keep files in home.");
+
export const helpConfiguredStorage = _("Storage is based on the configuration from 'Modify storage'.");
diff --git a/src/components/storage/InstallationMethod.jsx b/src/components/storage/InstallationMethod.jsx
index 58798f292..27e5bcce0 100644
--- a/src/components/storage/InstallationMethod.jsx
+++ b/src/components/storage/InstallationMethod.jsx
@@ -25,10 +25,19 @@ import {
useWizardFooter,
} from "@patternfly/react-core";
-import { resetPartitioning } from "../../apis/storage_partitioning.js";
+import {
+ applyStorage,
+ resetPartitioning,
+} from "../../apis/storage_partitioning.js";
import { AnacondaWizardFooter } from "../AnacondaWizardFooter.jsx";
-import { DialogsContext, FooterContext, OsReleaseContext, StorageContext } from "../Common.jsx";
+import {
+ DialogsContext,
+ FooterContext,
+ OsReleaseContext,
+ StorageContext,
+ StorageDefaultsContext,
+} from "../Common.jsx";
import { getNewPartitioning } from "./Common.jsx";
import { InstallationDestination } from "./InstallationDestination.jsx";
import { InstallationScenario, scenarios } from "./InstallationScenario.jsx";
@@ -44,6 +53,7 @@ const InstallationMethod = ({
onCritFail,
setIsFormDisabled,
setIsFormValid,
+ setStepNotification,
showStorage,
}) => {
const [isReclaimSpaceCheckboxChecked, setIsReclaimSpaceCheckboxChecked] = useState();
@@ -53,8 +63,9 @@ const InstallationMethod = ({
- ), [isFormDisabled, isReclaimSpaceCheckboxChecked]);
+ ), [isFormDisabled, isReclaimSpaceCheckboxChecked, setStepNotification]);
useWizardFooter(getFooter);
return (
@@ -86,13 +97,14 @@ const InstallationMethod = ({
);
};
-const CustomFooter = ({ isFormDisabled, isReclaimSpaceCheckboxChecked }) => {
+const CustomFooter = ({ isFormDisabled, isReclaimSpaceCheckboxChecked, setStepNotification }) => {
const [isReclaimSpaceModalOpen, setIsReclaimSpaceModalOpen] = useState(false);
const [isNextClicked, setIsNextClicked] = useState(false);
const { goToNextStep } = useWizardContext();
const [newPartitioning, setNewPartitioning] = useState(-1);
const nextRef = useRef();
const { partitioning, storageScenarioId } = useContext(StorageContext);
+ const { defaultScheme } = useContext(StorageDefaultsContext);
const method = ["mount-point-mapping", "use-configured-storage"].includes(storageScenarioId) ? "MANUAL" : "AUTOMATIC";
useEffect(() => {
@@ -102,12 +114,17 @@ const CustomFooter = ({ isFormDisabled, isReclaimSpaceCheckboxChecked }) => {
}
}, [isNextClicked, goToNextStep, newPartitioning, partitioning.path]);
- const onNext = async () => {
+ const onNext = async ({ setIsFormDisabled }) => {
if (method === "MANUAL") {
setNewPartitioning(partitioning.path);
setIsNextClicked(true);
} else {
- const part = await getNewPartitioning({ currentPartitioning: partitioning, method, storageScenarioId });
+ const part = await getNewPartitioning({
+ autopartScheme: defaultScheme,
+ currentPartitioning: partitioning,
+ method,
+ storageScenarioId,
+ });
setNewPartitioning(part);
const scenarioSupportsReclaimSpace = scenarios.find(sc => sc.id === storageScenarioId)?.canReclaimSpace;
@@ -115,8 +132,27 @@ const CustomFooter = ({ isFormDisabled, isReclaimSpaceCheckboxChecked }) => {
if (willShowReclaimSpaceModal) {
setIsReclaimSpaceModalOpen(true);
- } else {
+ } else if (storageScenarioId !== "home-reuse") {
setIsNextClicked(true);
+ } else {
+ setIsFormDisabled(true);
+ const step = new Page().id;
+ await applyStorage({
+ onFail: ex => {
+ console.error(ex);
+ setIsFormDisabled(false);
+ setStepNotification({ step, ...ex });
+ },
+ onSuccess: () => {
+ goToNextStep();
+
+ // Reset the state after the onNext call. Otherwise,
+ // React will try to render the current step again.
+ setIsFormDisabled(false);
+ setStepNotification();
+ },
+ partitioning: part,
+ });
}
}
};
@@ -165,12 +201,16 @@ const InstallationMethodFooterHelper = () => {
const usePageInit = () => {
const { setIsFormDisabled } = useContext(FooterContext);
const { appliedPartitioning, partitioning } = useContext(StorageContext);
+ const pageHasMounted = useRef(false);
+ // Always reset the partitioning when entering the installation destination page
// If the last partitioning applied was from the cockpit storage integration
// we should not reset it, as this option does apply the partitioning onNext
- const needsReset = partitioning.storageScenarioId !== "use-configured-storage" && appliedPartitioning;
+ const needsReset = partitioning.storageScenarioId !== "use-configured-storage" &&
+ appliedPartitioning &&
+ pageHasMounted.current !== true;
useEffect(() => {
- // Always reset the partitioning when entering the installation destination page
+ pageHasMounted.current = true;
if (needsReset) {
resetPartitioning();
} else {
diff --git a/src/components/storage/InstallationScenario.jsx b/src/components/storage/InstallationScenario.jsx
index aa8838362..67bff466b 100644
--- a/src/components/storage/InstallationScenario.jsx
+++ b/src/components/storage/InstallationScenario.jsx
@@ -29,18 +29,27 @@ import {
import { setStorageScenarioAction } from "../../actions/storage-actions.js";
import { debug } from "../../helpers/log.js";
+import {
+ getDeviceAncestors,
+} from "../../helpers/storage.js";
-import { DialogsContext, StorageContext, SystemTypeContext } from "../Common.jsx";
+import {
+ DialogsContext,
+ StorageContext,
+ StorageDefaultsContext,
+ SystemTypeContext
+} from "../Common.jsx";
import { StorageReview } from "../review/StorageReview.jsx";
import {
useDiskFreeSpace,
useDiskTotalSpace,
useMountPointConstraints,
useOriginalDeviceTree,
+ useOriginalExistingSystems,
useRequiredSize,
useUsablePartitions,
} from "./Common.jsx";
-import { helpConfiguredStorage, helpEraseAll, helpMountPointMapping, helpUseFreeSpace } from "./HelpAutopartOptions.jsx";
+import { helpConfiguredStorage, helpEraseAll, helpHomeReuse, helpMountPointMapping, helpUseFreeSpace } from "./HelpAutopartOptions.jsx";
import "./InstallationScenario.scss";
@@ -126,6 +135,69 @@ const checkMountPointMapping = ({ mountPointConstraints, selectedDisks, usablePa
return availability;
};
+const checkHomeReuse = ({ autopartScheme, devices, originalExistingSystems, selectedDisks }) => {
+ const availability = new AvailabilityState();
+ let reusedOS = null;
+
+ availability.hidden = false;
+ availability.available = !!selectedDisks.length;
+
+ const isCompleteOSOnDisks = (osData, disks) => {
+ const osDisks = osData.devices.v.map(deviceId => getDeviceAncestors(devices, deviceId))
+ .reduce((disks, ancestors) => disks.concat(ancestors))
+ .filter(dev => devices[dev].type.v === "disk")
+ .reduce((uniqueDisks, disk) => uniqueDisks.includes(disk) ? uniqueDisks : [...uniqueDisks, disk], []);
+ const missingDisks = osDisks.filter(disk => !disks.includes(disk));
+ return missingDisks.length === 0;
+ };
+
+ // Check that exactly one Linux OS is present and it is Fedora Linux
+ // (Stronger check for mountpoints uniqueness is in the backend
+ const linuxSystems = originalExistingSystems.filter(osdata => osdata["os-name"].v.includes("Linux"))
+ .filter(osdata => isCompleteOSOnDisks(osdata, selectedDisks));
+ if (linuxSystems.length === 0) {
+ availability.available = false;
+ availability.hidden = true;
+ debug("home reuse: No existing Linux system found.");
+ } else if (linuxSystems.length > 1) {
+ availability.available = false;
+ availability.hidden = true;
+ debug("home reuse: Multiple existing Linux systems found.");
+ } else {
+ reusedOS = linuxSystems[0];
+ if (!linuxSystems.some(osdata => osdata["os-name"].v.includes("Fedora"))) {
+ availability.available = false;
+ availability.hidden = true;
+ debug("home reuse: No existing Fedora Linux system found.");
+ }
+ }
+
+ if (reusedOS) {
+ // Check that required autopartitioning scheme matches reused OS.
+ // Check just "/home". To be more generic we could check all reused devices (as the backend).
+ const homeDevice = reusedOS["mount-points"].v["/home"];
+ const homeDeviceType = devices[homeDevice]?.type.v;
+ const requiredSchemeTypes = {
+ BTRFS: "btrfs subvolume",
+ LVM: "lvmlv",
+ LVM_THINP: "lvmthinlv",
+ PLAIN: "partition",
+ };
+ if (homeDeviceType !== requiredSchemeTypes[autopartScheme]) {
+ availability.available = false;
+ availability.hidden = true;
+ debug(`home reuse: No reusable existing Linux system found, reused devices must have ${requiredSchemeTypes[autopartScheme]} type`);
+ }
+ }
+
+ // TODO checks:
+ // - luks - partitions are unlocked - enforce? allow opt-out?
+ // - size ?
+ // - Windows system along (forbidden for now?)
+
+ return availability;
+};
+
export const checkConfiguredStorage = ({
devices,
mountPointConstraints,
@@ -204,6 +276,16 @@ const ReclaimSpace = ({ availability }) => {
};
export const scenarios = [{
+ buttonLabel: _("Reinstall Fedora"),
+ buttonVariant: "danger",
+ check: checkHomeReuse,
+ default: false,
+ detail: helpHomeReuse,
+ id: "home-reuse",
+ // CLEAR_PARTITIONS_NONE = 0
+ initializationMode: 0,
+ label: _("Reinstall Fedora"),
+}, {
buttonLabel: _("Erase data and install"),
buttonVariant: "danger",
check: checkEraseAll,
@@ -282,6 +364,8 @@ const InstallationScenarioSelector = ({
const usablePartitions = useUsablePartitions({ devices, selectedDisks });
const requiredSize = useRequiredSize();
const { storageScenarioId } = useContext(StorageContext);
+ const originalExistingSystems = useOriginalExistingSystems();
+ const { defaultScheme } = useContext(StorageDefaultsContext);
useEffect(() => {
if ([diskTotalSpace, diskFreeSpace, mountPointConstraints, requiredSize, usablePartitions].some(itm => itm === undefined)) {
@@ -293,10 +377,12 @@ const InstallationScenarioSelector = ({
for (const scenario of scenarios) {
const availability = scenario.check({
+ autopartScheme: defaultScheme,
devices,
diskFreeSpace,
diskTotalSpace,
mountPointConstraints,
+ originalExistingSystems,
partitioning: partitioning.path,
requiredSize,
selectedDisks,
@@ -308,10 +394,12 @@ const InstallationScenarioSelector = ({
return newAvailability;
});
}, [
+ defaultScheme,
devices,
diskFreeSpace,
diskTotalSpace,
mountPointConstraints,
+ originalExistingSystems,
partitioning.path,
partitioning.storageScenarioId,
requiredSize,
diff --git a/test/check-storage-home-reuse b/test/check-storage-home-reuse
new file mode 100755
index 000000000..00283bff0
--- /dev/null
+++ b/test/check-storage-home-reuse
@@ -0,0 +1,152 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2024 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 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
+# Lesser 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 .
+
+import os
+
+from anacondalib import VirtInstallMachineCase
+from installer import Installer
+from review import Review
+from storage import Storage
+from storagelib import StorageCase # pylint: disable=import-error
+from testlib import nondestructive, skipImage, test_main # pylint: disable=import-error
+from utils import pretend_default_scheme
+
+TEST_DIR = os.path.dirname(__file__)
+ROOT_DIR = os.path.dirname(TEST_DIR)
+BOTS_DIR = f'{ROOT_DIR}/bots'
+
+
+@nondestructive
+class TestHomeReuseFedora(VirtInstallMachineCase, StorageCase):
+ disk_image = "fedora-rawhide"
+ efi = False
+
+ def testBasic(self):
+ b = self.browser
+ m = self.machine
+ i = Installer(b, m, scenario="home-reuse")
+ s = Storage(b, m)
+ r = Review(b, m)
+
+ pretend_default_scheme(self, "BTRFS")
+
+ dev="vda"
+
+ i.open()
+ i.reach(i.steps.INSTALLATION_METHOD)
+ s.rescan_disks()
+
+ s.set_partitioning("home-reuse")
+ i.reach(i.steps.REVIEW)
+
+
+ # check selected disks are shown
+ r.check_disk(dev, f"16.1 GB {dev} (0x1af4)")
+ r.check_disk_row(dev, parent=f"{dev}3", action="delete")
+ r.check_disk_row(dev, parent=f"{dev}1", action="delete")
+ r.check_disk_row(dev, "/boot", f"{dev}3", "1.07 GB", True, "xfs", is_encrypted=False)
+ r.check_disk_row(dev, "/", f"{dev}4", "12.8 GB", True, "btrfs", is_encrypted=False)
+ r.check_disk_row(dev, "/home", f"{dev}4", "12.8 GB", False, "btrfs", is_encrypted=False,
+ action="mount")
+
+
+ @skipImage("btrfs support missing on fedora-eln image", "fedora-eln-boot")
+ def testMultipleRoots(self):
+ b = self.browser
+ m = self.machine
+ i = Installer(b, m, scenario="home-reuse")
+ s = Storage(b, m)
+ r = Review(b, m)
+
+ self.add_disk(15, os.path.join(BOTS_DIR, "./images/fedora-41"))
+ self.add_disk(15, os.path.join(BOTS_DIR, "./images/ubuntu-stable"))
+
+ dev_fedora1 = "vda"
+ dev_fedora2 = "vdb"
+ dev_ubuntu = "vdc"
+
+ s.udevadm_settle()
+
+ pretend_default_scheme(self, "BTRFS")
+
+ i.open()
+ i.reach(i.steps.INSTALLATION_METHOD)
+
+ # Select both Fedora disks and verify that home reuse is not available
+ s.select_disks([(dev_fedora1, True), (dev_fedora2, True), (dev_ubuntu, False)])
+ s.wait_scenario_visible("home-reuse", False)
+
+ # Select only single Ubuntu disk and verify that home reuse is not available
+ s.select_disks([(dev_fedora1, False), (dev_fedora2, False), (dev_ubuntu, True)])
+ s.wait_scenario_visible("home-reuse", False)
+
+ # Select Ubuntu disk and Fedora disk and verify that home reuse is not available
+ s.select_disks([(dev_fedora1, True), (dev_fedora2, False), (dev_ubuntu, True)])
+ s.wait_scenario_visible("home-reuse", False)
+
+ # Select only single Fedora disk and verify that home reuse is available
+ s.select_disks([(dev_fedora1, True), (dev_fedora2, False), (dev_ubuntu, False)])
+ s.wait_scenario_visible("home-reuse", True)
+ s.wait_scenario_available("home-reuse", True)
+
+ s.set_partitioning("home-reuse")
+ i.reach(i.steps.REVIEW)
+
+ # check selected disks are shown
+ r.check_disk(dev_fedora1, f"16.1 GB {dev_fedora1} (0x1af4)")
+ r.check_disk_row(dev_fedora1, parent=f"{dev_fedora1}3", action="delete")
+ r.check_disk_row(dev_fedora1, parent=f"{dev_fedora1}1", action="delete")
+ r.check_disk_row(dev_fedora1, "/boot", f"{dev_fedora1}3", "1.07 GB", True, "xfs", is_encrypted=False)
+ r.check_disk_row(dev_fedora1, "/", f"{dev_fedora1}4", "12.8 GB", True, "btrfs", is_encrypted=False)
+ r.check_disk_row(dev_fedora1, "/home", f"{dev_fedora1}4", "12.8 GB", False, "btrfs", is_encrypted=False,
+ action="mount")
+
+
+class TestHomeReuseFedoraEFI(VirtInstallMachineCase, StorageCase):
+ disk_image = "fedora-rawhide"
+ efi = True
+
+ def testBasic(self):
+ b = self.browser
+ m = self.machine
+ i = Installer(b, m, scenario="home-reuse")
+ s = Storage(b, m)
+ r = Review(b, m)
+
+ pretend_default_scheme(self, "BTRFS")
+
+ dev="vda"
+
+ i.open()
+ i.reach(i.steps.INSTALLATION_METHOD)
+ s.rescan_disks()
+
+ s.set_partitioning("home-reuse")
+ i.reach(i.steps.REVIEW)
+
+ # check selected disks are shown
+ r.check_disk_row(dev, parent=f"{dev}2", action="delete")
+ r.check_disk_row(dev, parent=f"{dev}3", action="delete")
+ r.check_disk(dev, f"16.1 GB {dev} (0x1af4)")
+ r.check_disk_row(dev, "/boot", f"{dev}3", "1.07 GB", True, "xfs", is_encrypted=False)
+ r.check_disk_row(dev, "/boot/efi", f"{dev}2", "629 MB", True, "efi", is_encrypted=False)
+ r.check_disk_row(dev, "/", f"{dev}4", "12.8 GB", True, "btrfs", is_encrypted=False)
+ r.check_disk_row(dev, "/home", f"{dev}4", "12.8 GB", False, "btrfs", is_encrypted=False,
+ action="mount")
+
+if __name__ == '__main__':
+ test_main()
diff --git a/test/helpers/installer.py b/test/helpers/installer.py
index f9e2f1527..fe57c7b82 100644
--- a/test/helpers/installer.py
+++ b/test/helpers/installer.py
@@ -57,6 +57,9 @@ def __init__(self, browser, machine, hidden_steps=None, scenario=None):
if (scenario == 'use-configured-storage'):
self.steps._steps_jump[self.steps.INSTALLATION_METHOD] = [self.steps.ACCOUNTS]
self.hidden_steps.extend([self.steps.CUSTOM_MOUNT_POINT, self.steps.DISK_ENCRYPTION])
+ elif (scenario == 'home-reuse'):
+ self.steps._steps_jump[self.steps.INSTALLATION_METHOD] = [self.steps.ACCOUNTS]
+ self.hidden_steps.extend([self.steps.CUSTOM_MOUNT_POINT, self.steps.DISK_ENCRYPTION])
else:
self.steps._steps_jump[self.steps.INSTALLATION_METHOD] = [self.steps.DISK_ENCRYPTION, self.steps.CUSTOM_MOUNT_POINT]
diff --git a/test/helpers/utils.py b/test/helpers/utils.py
index ee6166ce4..ba8f82b18 100644
--- a/test/helpers/utils.py
+++ b/test/helpers/utils.py
@@ -31,6 +31,9 @@ def pretend_live_iso(test, installer):
test.restore_file('/run/anaconda/anaconda.conf')
test.machine.execute("sed -i 's/type = BOOT_ISO/type = LIVE_OS/g' /run/anaconda/anaconda.conf")
+def pretend_default_scheme(test, scheme):
+ test.restore_file('/run/anaconda/anaconda.conf')
+ test.machine.execute(f"sed -i 's/default_scheme =.*/default_scheme = {scheme}/g' /run/anaconda/anaconda.conf")
def get_pretty_name(machine):
return machine.execute("cat /etc/os-release | grep PRETTY_NAME | cut -d '\"' -f 2 | tr -d '\n'")
diff --git a/test/reference b/test/reference
index f613f73b9..6dd33cda3 160000
--- a/test/reference
+++ b/test/reference
@@ -1 +1 @@
-Subproject commit f613f73b9d21c6a75cda1db68e81b271aa4e5a0c
+Subproject commit 6dd33cda3786b64deed1335f2a2b4060337fad06