* Copyright (C) 2022 Red Hat, Inc.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
import cockpit from "cockpit";
import {
getDevicesAction,
getDiskSelectionAction,
getPartitioningDataAction,
setAppliedPartitioningAction,
setStorageScenarioAction,
} from "../actions/storage-actions.js";
import { debug, error } from "../helpers/log.js";
import { _callClient, _getProperty } from "./helpers.js";
const INTERFACE_NAME = "org.fedoraproject.Anaconda.Modules.Storage";
const OBJECT_PATH = "/org/fedoraproject/Anaconda/Modules/Storage";
const callClient = (...args) => {
return _callClient(StorageClient, OBJECT_PATH, INTERFACE_NAME, ...args);
};
const getProperty = (...args) => {
return _getProperty(StorageClient, OBJECT_PATH, INTERFACE_NAME, ...args);
};
export class StorageClient {
constructor (address, dispatch) {
if (StorageClient.instance && (!address || StorageClient.instance.address === address)) {
return StorageClient.instance;
}
StorageClient.instance?.client.close();
StorageClient.instance = this;
this.client = cockpit.dbus(
INTERFACE_NAME,
{ address, bus: "none", superuser: "try" }
);
this.address = address;
this.dispatch = dispatch;
}
async init (args = {}) {
this.client.addEventListener("close", () => error("Storage client closed"));
this.startEventMonitor();
await this.initData();
}
stopEventMonitor () {
this._subscription?.remove();
}
startEventMonitor () {
this._subscription = this.client.subscribe(
{ },
(path, iface, signal, args) => {
switch (signal) {
case "PropertiesChanged":
if (args[0] === "org.fedoraproject.Anaconda.Modules.Storage.DiskSelection") {
this.dispatch(getDiskSelectionAction());
} else if (args[0] === "org.fedoraproject.Anaconda.Modules.Storage.Partitioning.Manual" && Object.hasOwn(args[1], "Requests")) {
this.dispatch(getPartitioningDataAction({ partitioning: path, requests: args[1].Requests.v }));
} else if (args[0] === "org.fedoraproject.Anaconda.Modules.Storage.Partitioning.Automatic" && Object.hasOwn(args[1], "Request")) {
this.dispatch(getPartitioningDataAction({ partitioning: path, requests: [args[1].Request.v] }));
} else if (args[0] === INTERFACE_NAME && Object.hasOwn(args[1], "CreatedPartitioning")) {
const last = args[1].CreatedPartitioning.v.length - 1;
this.dispatch(getPartitioningDataAction({ partitioning: args[1].CreatedPartitioning.v[last] }));
} else if (args[0] === INTERFACE_NAME && Object.hasOwn(args[1], "AppliedPartitioning")) {
this.dispatch(setAppliedPartitioningAction({ appliedPartitioning: args[1].AppliedPartitioning.v }));
this.dispatch(getDevicesAction());
} else {
debug(`Unhandled signal on ${path}: ${iface}.${signal} ${JSON.stringify(args)}`);
}
break;
default:
debug(`Unhandled signal on ${path}: ${iface}.${signal} ${JSON.stringify(args)}`);
}
});
}
async initData () {
const partitioning = await getProperty("CreatedPartitioning");
if (partitioning.length === 1) {
this.dispatch(setStorageScenarioAction("use-configured-storage-kickstart"));
}
if (partitioning.length !== 0) {
const lastPartitioning = partitioning[partitioning.length - 1];
await this.dispatch(getPartitioningDataAction({ partitioning: lastPartitioning }));
}
const appliedPartitioning = await getProperty("AppliedPartitioning");
this.dispatch(setAppliedPartitioningAction({ appliedPartitioning }));
await this.dispatch(getDevicesAction());
await this.dispatch(getDiskSelectionAction());
}
}
* @param {string} task DBus path to a task
* @param {string} onSuccess Callback to run after Succeeded signal is received
* @param {string} onFail Callback to run as an error handler
*
* @returns {Promise} Resolves a DBus path to a task
*/
export const runStorageTask = ({ onFail, onSuccess, task }) => {
let succeededEmitted = false;
const taskProxy = new StorageClient().client.proxy(
"org.fedoraproject.Anaconda.Task",
task
);
const addEventListeners = () => {
taskProxy.addEventListener("Stopped", () => taskProxy.Finish().catch(onFail));
taskProxy.addEventListener("Succeeded", () => {
if (succeededEmitted) {
return;
}
succeededEmitted = true;
onSuccess();
});
};
taskProxy.wait(() => {
addEventListeners();
taskProxy.Start().catch(onFail);
});
};
export const runStorageTaskAsync = ({ onSuccess, task }) => {
return new Promise((resolve, reject) => {
runStorageTask({
onFail: reject,
onSuccess: () => {
Promise.resolve()
.then(() => (onSuccess !== undefined ? onSuccess() : undefined))
.then(resolve)
.catch(reject);
},
task,
});
});
};
* @returns {Promise} Resolves a DBus path to a task
*/
export const scanDevicesWithTask = () => {
return callClient("ScanDevicesWithTask", []);
};