Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 45 additions & 11 deletions rust/agama-lib/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::{
localization::{LocalizationStore, LocalizationStoreError},
manager::{http_client::ManagerHTTPClientError, InstallationPhase, ManagerHTTPClient},
network::{NetworkStore, NetworkStoreError},
product::{ProductStore, ProductStoreError},
product::{ProductHTTPClient, ProductStore, ProductStoreError},
scripts::{ScriptsClient, ScriptsClientError, ScriptsGroup, ScriptsStore, ScriptsStoreError},
security::store::{SecurityStore, SecurityStoreError},
software::{SoftwareStore, SoftwareStoreError},
Expand Down Expand Up @@ -85,6 +85,8 @@ pub enum StoreError {
ZFCP(#[from] ZFCPStoreError),
#[error("Could not calculate the context")]
InvalidStoreContext,
#[error("Cannot proceed with profile without specified product")]
MissingProduct,
}

/// Struct that loads/stores the settings from/to the D-Bus services.
Expand Down Expand Up @@ -177,15 +179,6 @@ impl Store {
}
}

if let Some(files) = &settings.files {
self.files.store(files).await?;
}

// import the users (esp. the root password) before initializing software,
// if software fails the Web UI would be stuck in the root password dialog
if let Some(user) = &settings.user {
self.users.store(user).await?;
}
if let Some(network) = &settings.network {
self.network.store(network).await?;
}
Expand All @@ -199,40 +192,67 @@ impl Store {
if let Some(product) = &settings.product {
self.product.store(product).await?;
}
// here detect if product is properly selected, so later it can be checked
let is_product_selected = self.detect_selected_product().await?;
// ordering: localization after product as some product may miss some locales
if let Some(localization) = &settings.localization {
Store::ensure_selected_product(is_product_selected)?;
self.localization.store(localization).await?;
}
// import the users (esp. the root password) before initializing software,
// if software fails the Web UI would be stuck in the root password dialog
if let Some(user) = &settings.user {
Store::ensure_selected_product(is_product_selected)?;
self.users.store(user).await?;
}
if let Some(software) = &settings.software {
Store::ensure_selected_product(is_product_selected)?;
self.software.store(software).await?;
}
let mut dirty_flag_set = false;
// iscsi has to be done before storage
if let Some(iscsi) = &settings.iscsi {
Store::ensure_selected_product(is_product_selected)?;
dirty_flag_set = true;
self.iscsi_client.set_config(iscsi).await?
}
// dasd devices has to be activated before storage
if let Some(dasd) = &settings.dasd {
Store::ensure_selected_product(is_product_selected)?;
dirty_flag_set = true;
self.dasd.store(dasd).await?
}
// zfcp devices has to be activated before storage
if let Some(zfcp) = &settings.zfcp {
Store::ensure_selected_product(is_product_selected)?;
dirty_flag_set = true;
self.zfcp.store(zfcp).await?
}
// Reprobing storage is not directly done by zFCP, DASD or iSCSI services for a matter of
// efficiency. For now, clients are expected to explicitly reprobe. It is important to
// reprobe here before loading the storage settings. Otherwise, the new storage devices are
// not used.
self.reprobe_storage().await?;
if dirty_flag_set {
Store::ensure_selected_product(is_product_selected)?;
self.reprobe_storage().await?;
}

if settings.storage.is_some() || settings.storage_autoyast.is_some() {
Store::ensure_selected_product(is_product_selected)?;
self.storage.store(&settings.into()).await?
}
if let Some(bootloader) = &settings.bootloader {
Store::ensure_selected_product(is_product_selected)?;
self.bootloader.store(bootloader).await?;
}
if let Some(hostname) = &settings.hostname {
Store::ensure_selected_product(is_product_selected)?;
self.hostname.store(hostname).await?;
}
if let Some(files) = &settings.files {
Store::ensure_selected_product(is_product_selected)?;
self.files.store(files).await?;
}

Ok(())
}
Expand All @@ -246,6 +266,20 @@ impl Store {
Ok(())
}

async fn detect_selected_product(&self) -> Result<bool, ProductStoreError> {
let product_client = ProductHTTPClient::new(self.http_client.clone());
let product = product_client.product().await?;
Ok(!product.is_empty())
}

fn ensure_selected_product(selected: bool) -> Result<(), StoreError> {
if selected {
Ok(())
} else {
Err(StoreError::MissingProduct)
}
}

/// Runs the pre-installation scripts and forces a probe if the installation phase is "config".
async fn run_pre_scripts(&self) -> Result<(), StoreError> {
let scripts_client = ScriptsClient::new(self.http_client.clone());
Expand Down
9 changes: 9 additions & 0 deletions rust/package/agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@

-------------------------------------------------------------------
Wed Jul 16 19:05:48 UTC 2025 - Josef Reidinger <[email protected]>

- Fix crash when an Agama profile contains only the zfcp section by
providing better error report when product selection have to be
done before or in the same profile (bsc#1246601)


-------------------------------------------------------------------
Wed Jul 16 15:37:34 UTC 2025 - Imobach Gonzalez Sosa <[email protected]>

Expand Down