Skip to content

Commit

Permalink
Merge pull request #4843 from KKoukiou/custom-mount-point
Browse files Browse the repository at this point in the history
webui: mount point assignment support
  • Loading branch information
KKoukiou authored Jun 29, 2023
2 parents 934980c + bf0226b commit 56fcaf7
Show file tree
Hide file tree
Showing 17 changed files with 817 additions and 96 deletions.
25 changes: 25 additions & 0 deletions ui/webui/src/actions/storage-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
import cockpit from "cockpit";

import {
gatherRequests,
getAllDiskSelection,
getDeviceData,
getDevices,
getDiskFreeSpace,
getDiskTotalSpace,
getFormatData,
getPartitioningMethod,
getUsableDisks,
} from "../apis/storage.js";

Expand Down Expand Up @@ -82,3 +84,26 @@ export const getDiskSelectionAction = () => {
});
};
};

export const getPartitioningDataAction = ({ requests, partitioning, updateOnly }) => {
return async function fetchUserThunk (dispatch) {
const props = { path: partitioning };
const convertRequests = reqs => reqs.map(request => Object.entries(request).reduce((acc, [key, value]) => ({ ...acc, [key]: value.v }), {}));

if (!updateOnly) {
props.method = await getPartitioningMethod({ partitioning });
if (props.method === "MANUAL") {
const reqs = await gatherRequests({ partitioning });

props.requests = convertRequests(reqs[0]);
}
} else {
props.requests = convertRequests(requests);
}

return dispatch({
type: "GET_PARTITIONING_DATA",
payload: { path: partitioning, partitioningData: props }
});
};
};
106 changes: 104 additions & 2 deletions ui/webui/src/apis/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
*/
import cockpit from "cockpit";

import { getDiskSelectionAction } from "../actions/storage-actions.js";
import {
getDiskSelectionAction,
getPartitioningDataAction
} from "../actions/storage-actions.js";

export class StorageClient {
constructor (address) {
Expand Down Expand Up @@ -222,6 +225,26 @@ export const getPartitioningRequest = ({ partitioning }) => {
);
};

/**
* @param {string} partitioning DBus path to a partitioning
*
* @returns {Promise} The partitioning method
*/
export const getPartitioningMethod = ({ partitioning }) => {
return (
new StorageClient().client.call(
partitioning,
"org.freedesktop.DBus.Properties",
"Get",
[
"org.fedoraproject.Anaconda.Modules.Storage.Partitioning",
"PartitioningMethod",
]
)
.then(res => res[0].v)
);
};

/**
* @returns {Promise} The applied partitioning
*/
Expand Down Expand Up @@ -401,6 +424,37 @@ export const setSelectedDisks = ({ drives }) => {
);
};

/*
* @param {string} partitioning DBus path to a partitioning
* @param {Array.<Object>} requests An array of request objects
*/
export const setManualPartitioningRequests = ({ partitioning, requests }) => {
return new StorageClient().client.call(
partitioning,
"org.freedesktop.DBus.Properties",
"Set",
[
"org.fedoraproject.Anaconda.Modules.Storage.Partitioning.Manual",
"Requests",
cockpit.variant("aa{sv}", requests)
]
);
};

/**
* @param {string} partitioning DBus path to a partitioning
*
* @returns {Promise} The gathered requests for manual partitioning
*/
export const gatherRequests = ({ partitioning }) => {
return new StorageClient().client.call(
partitioning,
"org.fedoraproject.Anaconda.Modules.Storage.Partitioning.Manual",
"GatherRequests",
[]
);
};

export const startEventMonitorStorage = ({ dispatch }) => {
return new StorageClient().client.subscribe(
{ },
Expand All @@ -409,10 +463,58 @@ export const startEventMonitorStorage = ({ dispatch }) => {
case "PropertiesChanged":
if (args[0] === "org.fedoraproject.Anaconda.Modules.Storage.DiskSelection") {
dispatch(getDiskSelectionAction());
} else if (args[0] === "org.fedoraproject.Anaconda.Modules.Storage.Partitioning.Manual" && Object.hasOwn(args[1], "Requests")) {
dispatch(getPartitioningDataAction({ requests: args[1].Requests.v, partitioning: path, updateOnly: true }));
} else if (args[0] === "org.fedoraproject.Anaconda.Modules.Storage" && Object.hasOwn(args[1], "CreatedPartitioning")) {
const last = args[1].CreatedPartitioning.v.length - 1;
dispatch(getPartitioningDataAction({ partitioning: args[1].CreatedPartitioning.v[last] }));
} else {
console.debug(`Unhandled signal on ${path}: ${iface}.${signal} ${JSON.stringify(args)}`);
}
break;
default:
console.debug(`Unhandled signal on ${path}: ${iface}.${signal}`);
console.debug(`Unhandled signal on ${path}: ${iface}.${signal} ${JSON.stringify(args)}`);
}
});
};

export const initDataStorage = ({ dispatch }) => {
return new StorageClient().client.call(
"/org/fedoraproject/Anaconda/Modules/Storage",
"org.freedesktop.DBus.Properties",
"Get",
[
"org.fedoraproject.Anaconda.Modules.Storage",
"CreatedPartitioning",
]
)
.then(([res]) => {
if (res.v.length !== 0) {
return res.v.forEach(path => dispatch(getPartitioningDataAction({ partitioning: path })));
}
});
};

export const applyStorage = async ({ partitioning, encrypt, encryptPassword, onFail, onSuccess }) => {
await setInitializeLabelsEnabled({ enabled: true });
await setBootloaderDrive({ drive: "" });

const [part] = partitioning ? [partitioning] : await createPartitioning({ method: "AUTOMATIC" });

if (encrypt) {
await partitioningSetEncrypt({ partitioning: part, encrypt });
}
if (encryptPassword) {
await partitioningSetPassphrase({ partitioning: part, passphrase: encryptPassword });
}

const tasks = await partitioningConfigureWithTask({ partitioning: part });

runStorageTask({
task: tasks[0],
onFail,
onSuccess: () => applyPartitioning({ partitioning: part })
.then(onSuccess)
.catch(onFail)
});
};
56 changes: 47 additions & 9 deletions ui/webui/src/components/AnacondaWizard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* along with This program; If not, see <http://www.gnu.org/licenses/>.
*/
import cockpit from "cockpit";
import React, { useState } from "react";
import React, { useState, useMemo } from "react";

import {
ActionList,
Expand All @@ -32,16 +32,18 @@ import {
WizardContextConsumer,
} from "@patternfly/react-core";

import { InstallationDestination, applyDefaultStorage } from "./storage/InstallationDestination.jsx";
import { InstallationDestination } from "./storage/InstallationDestination.jsx";
import { StorageConfiguration, getScenario, getDefaultScenario } from "./storage/StorageConfiguration.jsx";
import { CustomMountPoint } from "./storage/CustomMountPoint.jsx";
import { DiskEncryption, StorageEncryptionState } from "./storage/DiskEncryption.jsx";
import { InstallationLanguage } from "./localization/InstallationLanguage.jsx";
import { InstallationProgress } from "./installation/InstallationProgress.jsx";
import { ReviewConfiguration, ReviewConfigurationConfirmModal } from "./review/ReviewConfiguration.jsx";
import { exitGui } from "../helpers/exit.js";
import { usePageLocation } from "hooks";
import {
resetPartitioning
applyStorage,
resetPartitioning,
} from "../apis/storage.js";

const _ = cockpit.gettext;
Expand All @@ -53,6 +55,11 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onAddE
const [storageEncryption, setStorageEncryption] = useState(new StorageEncryptionState());
const [showPassphraseScreen, setShowPassphraseScreen] = useState(false);
const [storageScenarioId, setStorageScenarioId] = useState(window.sessionStorage.getItem("storage-scenario-id") || getDefaultScenario().id);
const lastPartitioning = useMemo(() => {
const lastPartitioningKey = Object.keys(storageData.partitioning || {}).find(path => parseInt(path[path.length - 1]) === Object.keys(storageData.partitioning).length);

return storageData.partitioning?.[lastPartitioningKey];
}, [storageData.partitioning]);

// On live media rebooting the system will actually shut it off
const isBootIso = conf["Installation System"].type === "BOOT_ISO";
Expand All @@ -75,18 +82,26 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onAddE
label: _("Storage devices")
}, {
component: StorageConfiguration,
data: { selectedDisks: storageData.diskSelection.selectedDisks },
data: { deviceData: storageData.devices, diskSelection: storageData.diskSelection },
id: "storage-configuration",
label: _("Storage configuration")
}, {
component: CustomMountPoint,
data: { deviceData: storageData.devices, partitioningData: lastPartitioning, dispatch },
id: "custom-mountpoint",
label: _("Custom mount point"),
isHidden: storageScenarioId !== "custom-mount-point"

}, {
component: DiskEncryption,
id: "disk-encryption",
label: _("Disk encryption")
label: _("Disk encryption"),
isHidden: storageScenarioId === "custom-mount-point"
}]
},
{
component: ReviewConfiguration,
data: { deviceData: storageData.devices, diskSelection: storageData.diskSelection },
data: { deviceData: storageData.devices, diskSelection: storageData.diskSelection, requests: lastPartitioning ? lastPartitioning.requests : null },
id: "installation-review",
label: _("Review and install"),
},
Expand All @@ -101,7 +116,9 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onAddE
for (const step of steps) {
if (step.steps) {
for (const childStep of step.steps) {
stepIds.push(childStep.id);
if (childStep?.isHidden !== true) {
stepIds.push(childStep.id);
}
}
} else {
stepIds.push(step.id);
Expand All @@ -126,7 +143,7 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onAddE
};

const createSteps = (stepsOrder) => {
const steps = stepsOrder.map((s, idx) => {
const steps = stepsOrder.filter(s => !s.isHidden).map(s => {
let step = ({
id: s.id,
name: s.label,
Expand Down Expand Up @@ -181,6 +198,7 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onAddE
id="installation-wizard"
footer={<Footer
isFormValid={isFormValid}
partitioning={lastPartitioning?.path}
setIsFormValid={setIsFormValid}
setStepNotification={setStepNotification}
isInProgress={isInProgress}
Expand Down Expand Up @@ -209,6 +227,7 @@ const Footer = ({
setIsFormValid,
setStepNotification,
isInProgress,
partitioning,
setIsInProgress,
storageEncryption,
showPassphraseScreen,
Expand All @@ -230,7 +249,7 @@ const Footer = ({
}
setIsInProgress(true);

applyDefaultStorage({
applyStorage({
onFail: ex => {
console.error(ex);
setIsInProgress(false);
Expand All @@ -249,6 +268,25 @@ const Footer = ({
});
} else if (activeStep.id === "installation-review") {
setNextWaitsConfirmation(true);
} else if (activeStep.id === "custom-mountpoint") {
setIsInProgress(true);

applyStorage({
partitioning,
onFail: ex => {
console.error(ex);
setIsInProgress(false);
setStepNotification({ step: activeStep.id, ...ex });
},
onSuccess: () => {
onNext();

// Reset the state after the onNext call. Otherwise,
// React will try to render the current step again.
setIsInProgress(false);
setStepNotification();
},
});
} else {
onNext();
}
Expand Down
4 changes: 0 additions & 4 deletions ui/webui/src/components/Common.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ export const AddressContext = createContext("");
export const ConfContext = createContext();
export const LanguageContext = createContext("");

export const sleep = ({ seconds }) => {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
};

export const FormGroupHelpPopover = ({ helpContent }) => {
return (
<Popover
Expand Down
4 changes: 2 additions & 2 deletions ui/webui/src/components/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { HelpDrawer } from "./HelpDrawer.jsx";

import { BossClient } from "../apis/boss.js";
import { LocalizationClient } from "../apis/localization.js";
import { StorageClient, startEventMonitorStorage } from "../apis/storage.js";
import { StorageClient, initDataStorage, startEventMonitorStorage } from "../apis/storage.js";
import { PayloadsClient } from "../apis/payloads";

import { readBuildstamp, getIsFinal } from "../helpers/betanag.js";
Expand Down Expand Up @@ -62,6 +62,7 @@ export const Application = () => {

setAddress(address);

initDataStorage({ dispatch });
startEventMonitorStorage({ dispatch });
});

Expand Down Expand Up @@ -100,7 +101,6 @@ export const Application = () => {
if (!address || !conf) {
return null;
}
console.info("conf: ", conf);

const title = cockpit.format("$0 installation", prettyName);

Expand Down
Loading

0 comments on commit 56fcaf7

Please sign in to comment.