diff --git a/rust/agama-lib/src/storage/client.rs b/rust/agama-lib/src/storage/client.rs index b99626b13d..381c747209 100644 --- a/rust/agama-lib/src/storage/client.rs +++ b/rust/agama-lib/src/storage/client.rs @@ -159,7 +159,7 @@ impl<'a> StorageClient<'a> { Ok(settings) } - /// Set the storage config according to the JSON schema + /// Set the storage config model according to the JSON schema pub async fn set_config_model(&self, model: Box) -> Result { Ok(self .storage_proxy @@ -174,6 +174,13 @@ impl<'a> StorageClient<'a> { Ok(config_model) } + /// Solves the storage config model + pub async fn solve_config_model(&self, model: &str) -> Result, ServiceError> { + let serialized_solved_model = self.storage_proxy.solve_config_model(model).await?; + let solved_model = serde_json::from_str(serialized_solved_model.as_str()).unwrap(); + Ok(solved_model) + } + pub async fn calculate(&self, settings: ProposalSettingsPatch) -> Result { let map: HashMap<&str, zbus::zvariant::Value> = settings.into(); let options: HashMap<&str, &zbus::zvariant::Value> = diff --git a/rust/agama-lib/src/storage/proxies/storage1.rs b/rust/agama-lib/src/storage/proxies/storage1.rs index 563bbfd356..2dc37e05bb 100644 --- a/rust/agama-lib/src/storage/proxies/storage1.rs +++ b/rust/agama-lib/src/storage/proxies/storage1.rs @@ -71,6 +71,9 @@ pub trait Storage1 { /// Get the storage config model according to the JSON schema fn get_config_model(&self) -> zbus::Result; + /// Solve a storage config model + fn solve_config_model(&self, model: &str) -> zbus::Result; + /// DeprecatedSystem property #[zbus(property)] fn deprecated_system(&self) -> zbus::Result; diff --git a/rust/agama-server/src/software/web.rs b/rust/agama-server/src/software/web.rs index 99c7e95d22..3bbd61b2d5 100644 --- a/rust/agama-server/src/software/web.rs +++ b/rust/agama-server/src/software/web.rs @@ -57,7 +57,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use tokio_stream::{Stream, StreamExt}; -use super::license::{License, LicenseContent}; +use super::license::License; #[derive(Clone)] struct SoftwareState<'a> { diff --git a/rust/agama-server/src/storage/web.rs b/rust/agama-server/src/storage/web.rs index 260f2a9280..2018382180 100644 --- a/rust/agama-server/src/storage/web.rs +++ b/rust/agama-server/src/storage/web.rs @@ -115,6 +115,7 @@ pub async fn storage_service(dbus: zbus::Connection) -> Result>, + query: Query, +) -> Result>, Error> { + let solved_model = state + .client + .solve_config_model(query.model.as_str()) + .await + .map_err(Error::Service)?; + Ok(Json(solved_model)) +} + +#[derive(Deserialize, utoipa::IntoParams)] +struct SolveModelQuery { + /// Serialized config model. + model: String, +} + + /// Probes the storage devices. #[utoipa::path( post, diff --git a/service/lib/agama/dbus/storage/manager.rb b/service/lib/agama/dbus/storage/manager.rb index 918f43de6d..938e8fb244 100644 --- a/service/lib/agama/dbus/storage/manager.rb +++ b/service/lib/agama/dbus/storage/manager.rb @@ -147,6 +147,18 @@ def recover_model JSON.pretty_generate(json) end + # Solves the given serialized config model. + # + # @param serialized_model [String] Serialized storage config model. + # @return [String] Serialized solved model. + def solve_model(serialized_model) + logger.info("Solving storage config model from D-Bus: #{serialized_model}") + + model_json = JSON.parse(serialized_model, symbolize_names: true) + solved_model_json = proposal.solve_model(model_json) + JSON.pretty_generate(solved_model_json) + end + def install busy_while { backend.install } end @@ -173,6 +185,9 @@ def deprecated_system busy_while { apply_config_model(serialized_model) } end dbus_method(:GetConfigModel, "out serialized_model:s") { recover_model } + dbus_method(:SolveConfigModel, "in sparse_model:s, out solved_model:s") do |sparse_model| + solve_model(sparse_model) + end dbus_method(:Install) { install } dbus_method(:Finish) { finish } dbus_reader(:deprecated_system, "b") diff --git a/service/lib/agama/storage/proposal.rb b/service/lib/agama/storage/proposal.rb index 825194f085..d4365203da 100644 --- a/service/lib/agama/storage/proposal.rb +++ b/service/lib/agama/storage/proposal.rb @@ -22,6 +22,7 @@ require "agama/issue" require "agama/storage/actions_generator" require "agama/storage/config_conversions" +require "agama/storage/config_solver" require "agama/storage/proposal_settings" require "agama/storage/proposal_strategies" require "json" @@ -100,6 +101,24 @@ def model_json ConfigConversions::ToModel.new(config).convert end + # Solves a given model. + # + # @param model_json [Hash] Config model according to the JSON schema. + # @param [Hash, nil] Solved config model or nil if the model cannot be solved yet. + def solve_model(model_json) + return unless storage_manager.probed? + + config = ConfigConversions::FromModel + .new(model_json, product_config: product_config) + .convert + + ConfigSolver + .new(product_config, storage_manager.probed, disk_analyzer: disk_analyzer) + .solve(config) + + ConfigConversions::ToModel.new(config).convert + end + # Calculates a new proposal using the given JSON. # # @raise If the JSON is not valid.