Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
joseivanlopez committed Jan 16, 2025
1 parent 0f537ea commit 522a071
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 55 deletions.
5 changes: 5 additions & 0 deletions rust/agama-lib/src/storage/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ impl<'a> StorageClient<'a> {
Ok(self.storage_proxy.probe().await?)
}

/// Runs the reprobing process
pub async fn reprobe(&self) -> Result<(), ServiceError> {
Ok(self.storage_proxy.reprobe().await?)
}

/// Set the storage config according to the JSON schema
pub async fn set_config(&self, settings: StorageSettings) -> Result<u32, ServiceError> {
Ok(self
Expand Down
3 changes: 3 additions & 0 deletions rust/agama-lib/src/storage/proxies/storage1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ pub trait Storage1 {
/// Probe method
fn probe(&self) -> zbus::Result<()>;

/// Reprobe method
fn reprobe(&self) -> zbus::Result<()>;

/// Set the storage config according to the JSON schema
fn set_config(&self, settings: &str) -> zbus::Result<u32>;

Expand Down
18 changes: 17 additions & 1 deletion rust/agama-server/src/storage/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ pub async fn storage_service(dbus: zbus::Connection) -> Result<Router, ServiceEr
.route("/config", put(set_config).get(get_config))
.route("/config_model", put(set_config_model).get(get_config_model))
.route("/probe", post(probe))
.route("/reprobe", post(reprobe))
.route("/devices/dirty", get(devices_dirty))
.route("/devices/system", get(system_devices))
.route("/devices/result", get(staging_devices))
Expand Down Expand Up @@ -240,7 +241,7 @@ async fn set_config_model(
path = "/probe",
context_path = "/api/storage",
responses(
(status = 200, description = "Devices were probed and an initial proposal were performed"),
(status = 200, description = "Devices were probed and an initial proposal was performed"),
(status = 400, description = "The D-Bus service could not perform the action")
),
operation_id = "storage_probe"
Expand All @@ -249,6 +250,21 @@ async fn probe(State(state): State<StorageState<'_>>) -> Result<Json<()>, Error>
Ok(Json(state.client.probe().await?))
}

/// Reprobes the storage devices.
#[utoipa::path(
post,
path = "/reprobe",
context_path = "/api/storage",
responses(
(status = 200, description = "Devices were probed and the proposal was recalculated"),
(status = 400, description = "The D-Bus service could not perform the action")
),
operation_id = "storage_reprobe"
)]
async fn reprobe(State(state): State<StorageState<'_>>) -> Result<Json<()>, Error> {
Ok(Json(state.client.reprobe().await?))
}

/// Gets whether the system is in a deprecated status.
///
/// The system is usually set as deprecated as effect of managing some kind of devices, for example,
Expand Down
5 changes: 3 additions & 2 deletions service/lib/agama/dbus/storage/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ def issues
STORAGE_INTERFACE = "org.opensuse.Agama.Storage1"
private_constant :STORAGE_INTERFACE

def probe
def probe(keep_config: false)
busy_while do
# Clean trees in advance to avoid having old objects exported in D-Bus.
system_devices_tree.clean
staging_devices_tree.clean

backend.probe
backend.probe(keep_config: keep_config)
end
end

Expand Down Expand Up @@ -162,6 +162,7 @@ def deprecated_system

dbus_interface STORAGE_INTERFACE do
dbus_method(:Probe) { probe }
dbus_method(:Reprobe) { probe(keep_config: true) }
dbus_method(:SetConfig, "in serialized_config:s, out result:u") do |serialized_config|
busy_while { apply_config(serialized_config) }
end
Expand Down
16 changes: 9 additions & 7 deletions service/lib/agama/storage/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,18 @@ def on_probe(&block)
end

# Probes storage devices and performs an initial proposal
def probe
def probe(keep_config: false)
start_progress_with_size(4)
product_config.pick_product(software.selected_product)
check_multipath
progress.step(_("Activating storage devices")) { activate_devices }
progress.step(_("Probing storage devices")) { probe_devices }
progress.step(_("Calculating the storage proposal")) { calculate_proposal }
progress.step(_("Calculating the storage proposal")) do
calculate_proposal(keep_config: keep_config)
end
progress.step(_("Selecting Linux Security Modules")) { security.probe }
# The system is not deprecated anymore
self.deprecated_system = false
update_issues
@on_probe_callbacks&.each(&:call)
end
Expand Down Expand Up @@ -216,14 +220,12 @@ def probe_devices

iscsi.probe
Y2Storage::StorageManager.instance.probe(callbacks)

# The system is not deprecated anymore
self.deprecated_system = false
end

# Calculates the proposal using the storage config from the product.
def calculate_proposal
config_json = ConfigJSONReader.new(product_config).read
def calculate_proposal(keep_config: false)
config_json = proposal.storage_json if keep_config
config_json ||= ConfigJSONReader.new(product_config).read
proposal.calculate_from_json(config_json)
end

Expand Down
5 changes: 4 additions & 1 deletion web/src/api/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ import { Action, config, configModel, ProductParams, Volume } from "~/api/storag
/**
* Starts the storage probing process.
*/

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const probe = (): Promise<any> => post("/api/storage/probe");

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const reprobe = (): Promise<any> => post("/api/storage/reprobe");

const fetchConfig = (): Promise<config.Config | undefined> =>
get("/api/storage/config").then((config) => config.storage);

Expand Down Expand Up @@ -80,6 +82,7 @@ const refresh = async (): Promise<void> => {

export {
probe,
reprobe,
fetchConfig,
fetchConfigModel,
setConfig,
Expand Down
16 changes: 1 addition & 15 deletions web/src/components/storage/DeviceSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,11 @@ import { Page } from "~/components/core";
import { DeviceSelectorTable } from "~/components/storage";
import DevicesTechMenu from "./DevicesTechMenu";
import { ProposalTarget, StorageDevice } from "~/types/storage";
import {
useAvailableDevices,
useProposalMutation,
useProposalResult,
useRefresh,
} from "~/queries/storage";
import { useAvailableDevices, useProposalMutation, useProposalResult } from "~/queries/storage";
import { deviceChildren } from "~/components/storage/utils";
import { compact } from "~/utils";
import a11y from "@patternfly/react-styles/css/utilities/Accessibility/accessibility";
import { _ } from "~/i18n";
import { Loading } from "~/components/layout";

const SELECT_DISK_ID = "select-disk";
const CREATE_LVM_ID = "create-lvm";
Expand All @@ -59,17 +53,11 @@ export default function DeviceSelection() {
const availableDevices = useAvailableDevices();
const updateProposal = useProposalMutation();
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [state, setState] = useState<DeviceSelectionState>({});

const isTargetDisk = state.target === ProposalTarget.DISK;
const isTargetNewLvmVg = state.target === ProposalTarget.NEW_LVM_VG;

useRefresh({
onStart: () => setIsLoading(true),
onFinish: () => setIsLoading(false),
});

useEffect(() => {
if (state.target !== undefined) return;

Expand Down Expand Up @@ -130,8 +118,6 @@ physical volumes will be created on demand as new partitions at the selected \
devices.",
).split(/[[\]]/);

if (isLoading) return <Loading />;

return (
<Page>
<Page.Header>
Expand Down
24 changes: 16 additions & 8 deletions web/src/components/storage/ProposalPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* find current contact information at www.suse.com.
*/

import React, { useState } from "react";
import React from "react";
import { Grid, GridItem, SplitItem } from "@patternfly/react-core";
import { Page } from "~/components/core/";
import { Loading } from "~/components/layout";
Expand All @@ -32,7 +32,13 @@ import ConfigEditorMenu from "./ConfigEditorMenu";
import { toValidationError } from "~/utils";
import { useIssues } from "~/queries/issues";
import { IssueSeverity } from "~/types/issues";
import { useDevices, useProposalResult, useRefresh } from "~/queries/storage";
import {
useDevices,
useDeprecated,
useDeprecatedChanges,
useProposalResult,
useReprobeMutation,
} from "~/queries/storage";
import { _ } from "~/i18n";

/**
Expand All @@ -59,21 +65,23 @@ export const NOT_AFFECTED = {
};

export default function ProposalPage() {
const [isLoading, setIsLoading] = useState(false);
const systemDevices = useDevices("system");
const stagingDevices = useDevices("result");
const isDeprecated = useDeprecated();
const { mutateAsync: reprobe } = useReprobeMutation();
const { actions } = useProposalResult();

useRefresh({
onStart: () => setIsLoading(true),
onFinish: () => setIsLoading(false),
});
useDeprecatedChanges();

React.useEffect(() => {
if (isDeprecated) reprobe().catch(console.log);
}, [isDeprecated, reprobe]);

const errors = useIssues("storage")
.filter((s) => s.severity === IssueSeverity.Error)
.map(toValidationError);

if (isLoading) {
if (isDeprecated) {
return (
<Page>
<Page.Header>
Expand Down
31 changes: 10 additions & 21 deletions web/src/queries/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
fetchDefaultVolume,
fetchProductParams,
fetchUsableDevices,
refresh,
reprobe,
} from "~/api/storage";
import { fetchDevices, fetchDevicesDirty } from "~/api/storage/devices";
import { calculate } from "~/api/storage/proposal";
Expand Down Expand Up @@ -316,30 +316,19 @@ const useDeprecatedChanges = () => {
});
};

type RefreshHandler = {
onStart?: () => void;
onFinish?: () => void;
};

/**
* Hook that reprobes the devices and recalculates the proposal using the current settings.
*/
const useRefresh = (handler?: RefreshHandler) => {
const useReprobeMutation = () => {
const queryClient = useQueryClient();
const isDeprecated = useDeprecated();

handler ||= {};
handler.onStart ||= () => undefined;
handler.onFinish ||= () => undefined;

React.useEffect(() => {
if (!isDeprecated) return;
const query = {
mutationFn: async () => {
await reprobe();
},
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["storage"] }),
};

handler.onStart();
refresh()
.then(() => queryClient.invalidateQueries({ queryKey: ["storage"] }))
.then(() => handler.onFinish());
}, [handler, isDeprecated, queryClient]);
return useMutation(query);
};

export {
Expand All @@ -354,7 +343,7 @@ export {
useProposalMutation,
useDeprecated,
useDeprecatedChanges,
useRefresh,
useReprobeMutation,
};

export * from "~/queries/storage/config-model";

0 comments on commit 522a071

Please sign in to comment.