diff --git a/rust/agama-lib/share/examples/dasd.json b/rust/agama-lib/share/examples/dasd.json new file mode 100644 index 0000000000..70353aa140 --- /dev/null +++ b/rust/agama-lib/share/examples/dasd.json @@ -0,0 +1,16 @@ +{ + "dasd": { + "devices": [ + { + "channel": "0.0.0200", + "format": true, + "diag": true, + "state": "active" + }, + { + "channel": "0.0.0201", + "state": "offline" + } + ] + } +} \ No newline at end of file diff --git a/rust/agama-lib/share/profile.schema.json b/rust/agama-lib/share/profile.schema.json index b0376704c6..4192040698 100644 --- a/rust/agama-lib/share/profile.schema.json +++ b/rust/agama-lib/share/profile.schema.json @@ -84,6 +84,45 @@ ] }, + "dasd": { + "title": "DASD device actiation (s390x only)", + "type": "object", + "properties": { + "devices": { + "title": "List of DASD devices", + "type": "array", + "items": { + "type": "object", + "properties": { + "channel": { + "title": "DASD device channel", + "type": "string" + }, + "state": { + "title": "Specify target state of device. Either activate it or deactivate it.", + "type": "string", + "enum": [ + "active", + "offline" + ], + "default": "active" + }, + "format": { + "title": "If device should be formatted. If not specified then it format device only if not already formatted.", + "type": "boolean" + }, + "diag": { + "title": "If device have set diag flag. If not specified then it keep what device has before.", + "type": "boolean" + } + }, + "required": [ + "channel" + ] + } + } + } + }, "hostname": { "title": "Hostname settings", "type": "object", diff --git a/rust/agama-lib/src/error.rs b/rust/agama-lib/src/error.rs index a500ad2568..04f06ff8e9 100644 --- a/rust/agama-lib/src/error.rs +++ b/rust/agama-lib/src/error.rs @@ -61,6 +61,8 @@ pub enum ServiceError { Profile(#[from] ProfileError), #[error("Unsupported SSL Fingerprint algorithm '#{0}'.")] UnsupportedSSLFingerprintAlgorithm(String), + #[error("DASD with channel '#{0}' not found.")] + DASDChannelNotFound(String), } #[derive(Error, Debug)] diff --git a/rust/agama-lib/src/install_settings.rs b/rust/agama-lib/src/install_settings.rs index a4cf324113..361821bac2 100644 --- a/rust/agama-lib/src/install_settings.rs +++ b/rust/agama-lib/src/install_settings.rs @@ -29,7 +29,8 @@ use crate::hostname::model::HostnameSettings; use crate::security::settings::SecuritySettings; use crate::{ localization::LocalizationSettings, network::NetworkSettings, product::ProductSettings, - scripts::ScriptsConfig, software::SoftwareSettings, users::UserSettings, + scripts::ScriptsConfig, software::SoftwareSettings, storage::settings::dasd::DASDConfig, + users::UserSettings, }; use fluent_uri::Uri; use serde::{Deserialize, Serialize}; @@ -59,6 +60,9 @@ pub struct InstallSettings { pub bootloader: Option, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] + pub dasd: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] pub files: Option>, #[serde(default)] pub hostname: Option, diff --git a/rust/agama-lib/src/storage.rs b/rust/agama-lib/src/storage.rs index 5a6b849824..4ad2acd7fb 100644 --- a/rust/agama-lib/src/storage.rs +++ b/rust/agama-lib/src/storage.rs @@ -24,8 +24,8 @@ pub mod client; pub mod http_client; pub mod model; pub mod proxies; -mod settings; -mod store; +pub mod settings; +pub(crate) mod store; pub use client::{ iscsi::{ISCSIAuth, ISCSIClient, ISCSIInitiator, ISCSINode}, diff --git a/rust/agama-lib/src/storage/client/dasd.rs b/rust/agama-lib/src/storage/client/dasd.rs index d61d549fb3..940e45d2b1 100644 --- a/rust/agama-lib/src/storage/client/dasd.rs +++ b/rust/agama-lib/src/storage/client/dasd.rs @@ -20,6 +20,9 @@ //! Implements a client to access Agama's D-Bus API related to DASD management. +use std::{collections::HashMap, time::Duration}; + +use tokio::time::sleep; use zbus::{ fdo::{IntrospectableProxy, ObjectManagerProxy}, zvariant::{ObjectPath, OwnedObjectPath}, @@ -28,7 +31,12 @@ use zbus::{ use crate::{ error::ServiceError, - storage::{model::dasd::DASDDevice, proxies::dasd::ManagerProxy}, + jobs::client::JobsClient, + storage::{ + model::dasd::DASDDevice, + proxies::dasd::ManagerProxy, + settings::dasd::{DASDConfig, DASDDeviceConfig, DASDDeviceState}, + }, }; /// Client to connect to Agama's D-Bus API for DASD management. @@ -37,6 +45,7 @@ pub struct DASDClient<'a> { manager_proxy: ManagerProxy<'a>, object_manager_proxy: ObjectManagerProxy<'a>, introspectable_proxy: IntrospectableProxy<'a>, + connection: Connection, } impl<'a> DASDClient<'a> { @@ -57,6 +66,7 @@ impl<'a> DASDClient<'a> { manager_proxy, object_manager_proxy, introspectable_proxy, + connection, }) } @@ -66,6 +76,155 @@ impl<'a> DASDClient<'a> { Ok(introspect.contains("org.opensuse.Agama.Storage1.DASD.Manager")) } + /// Returns DASD configuration that can be used by set_config call + pub async fn get_config(&self) -> Result { + // TODO: implement + Ok(DASDConfig::default()) + } + + /// applies DASD configuration + /// + /// Note that it do actions immediatelly. So there is no write method there that apply it. + pub async fn set_config(&self, config: DASDConfig) -> Result<(), ServiceError> { + // at first probe to ensure we work on real system info + self.probe().await?; + self.config_activate(&config).await?; + self.config_format(&config).await?; + self.config_set_diag(&config).await?; + + Ok(()) + } + + /// Apply activation or deactivation from config + async fn config_activate(&self, config: &DASDConfig) -> Result<(), ServiceError> { + let pairs = self.config_pairs(config).await?; + let to_activate: Vec<&str> = pairs + .iter() + .filter(|(system, _config)| system.enabled == false) + .filter(|(_system, config)| config.state.unwrap_or_default() == DASDDeviceState::Active) + .map(|(system, _config)| system.id.as_str()) + .collect(); + self.enable(&to_activate).await?; + + let to_deactivate: Vec<&str> = pairs + .iter() + .filter(|(system, _config)| system.enabled == true) + .filter(|(_system, config)| config.state == Some(DASDDeviceState::Offline)) + .map(|(system, _config)| system.id.as_str()) + .collect(); + self.disable(&to_deactivate).await?; + + if !to_activate.is_empty() || !to_deactivate.is_empty() { + // reprobe after calling enable. TODO: check if it is needed or callbacks take into action and update it automatically + self.probe().await?; + } + Ok(()) + } + + /// Apply request for device formatting from config + async fn config_format(&self, config: &DASDConfig) -> Result<(), ServiceError> { + let pairs = self.config_pairs(config).await?; + let to_format: Vec<&str> = pairs + .iter() + .filter(|(system, config)| { + if config.format == Some(true) { + true + } else if config.format == None { + !system.formatted + } else { + false + } + }) + .map(|(system, _config)| system.id.as_str()) + .collect(); + + if !to_format.is_empty() { + let job_path = self.format(&to_format).await?; + self.wait_for_format(job_path).await?; + // reprobe after calling format. TODO: check if it is needed or callbacks take into action and update it automatically + // also do we need to wait here for finish of format progress? + self.probe().await?; + } + Ok(()) + } + + /// Wait for format operation to finish + async fn wait_for_format(&self, job_path: String) -> Result<(), ServiceError> { + let mut finished = false; + let jobs_client = JobsClient::new( + self.connection.clone(), + "org.opensuse.Agama.Storage1", + "/org/opensuse/Agama/Storage1", + ) + .await?; + while !finished { + // active polling with 1 sec sleep for jobs + sleep(Duration::from_secs(1)).await; + let jobs = jobs_client.jobs().await?; + let job_pair = jobs + .iter() + .find(|(path, _job)| path.to_string() == job_path); + + if let Some((_, job)) = job_pair { + finished = !job.running; + } else { + // job does not exist, so probably finished + finished = true; + } + } + + Ok(()) + } + + /// Apply diag flag from config + async fn config_set_diag(&self, config: &DASDConfig) -> Result<(), ServiceError> { + let pairs = self.config_pairs(config).await?; + let to_enable: Vec<&str> = pairs + .iter() + .filter(|(_system, config)| config.diag == Some(true)) + .map(|(system, _config)| system.id.as_str()) + .collect(); + self.set_diag(&to_enable, true).await?; + + let to_disable: Vec<&str> = pairs + .iter() + .filter(|(_system, config)| config.diag == Some(false)) + .map(|(system, _config)| system.id.as_str()) + .collect(); + self.set_diag(&to_disable, false).await?; + + if !to_enable.is_empty() || !to_disable.is_empty() { + // reprobe after calling format. TODO: check if it is needed or callbacks take into action and update it automatically + // also do we need to wait here for finish of format progress? + self.probe().await?; + } + Ok(()) + } + + /// create vector with pairs of devices on system and devices specification in config + async fn config_pairs( + &self, + config: &DASDConfig, + ) -> Result, ServiceError> { + let devices = self.devices().await?; + let mut devices_map: HashMap<&str, DASDDevice> = devices + .iter() + .map(|d| (d.1.id.as_str(), d.1.clone())) + .collect(); + config + .devices + .iter() + .map(|c| { + Ok(( + devices_map + .remove(c.channel.as_str()) + .ok_or(ServiceError::DASDChannelNotFound(c.channel.clone()))?, + c.clone(), + )) + }) + .collect() + } + pub async fn probe(&self) -> Result<(), ServiceError> { Ok(self.manager_proxy.probe().await?) } diff --git a/rust/agama-lib/src/storage/http_client.rs b/rust/agama-lib/src/storage/http_client.rs index 8942d13746..74527cf342 100644 --- a/rust/agama-lib/src/storage/http_client.rs +++ b/rust/agama-lib/src/storage/http_client.rs @@ -19,7 +19,9 @@ // find current contact information at www.suse.com. //! Implements a client to access Agama's storage service. -use serde_json::value::RawValue; + +pub mod dasd; +pub mod iscsi; use crate::{ base_http_client::{BaseHTTPClient, BaseHTTPClientError}, @@ -49,29 +51,3 @@ impl StorageHTTPClient { Ok(self.client.put_void("/storage/config", config).await?) } } - -#[derive(Debug, thiserror::Error)] -pub enum ISCSIHTTPClientError { - #[error(transparent)] - ISCSI(#[from] BaseHTTPClientError), -} - -pub struct ISCSIHTTPClient { - client: BaseHTTPClient, -} - -impl ISCSIHTTPClient { - pub fn new(base: BaseHTTPClient) -> Self { - Self { client: base } - } - - pub async fn get_config(&self) -> Result, ISCSIHTTPClientError> { - // TODO: implement it as part of next step - //self.client.get("/storage/config").await - Ok(None) - } - - pub async fn set_config(&self, config: &Box) -> Result<(), ISCSIHTTPClientError> { - Ok(self.client.post_void("/iscsi/config", config).await?) - } -} diff --git a/rust/agama-lib/src/storage/http_client/dasd.rs b/rust/agama-lib/src/storage/http_client/dasd.rs new file mode 100644 index 0000000000..e42974a88b --- /dev/null +++ b/rust/agama-lib/src/storage/http_client/dasd.rs @@ -0,0 +1,50 @@ +// Copyright (c) [2025] SUSE LLC +// +// All Rights Reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, contact SUSE LLC. +// +// To contact SUSE LLC about this file by physical or electronic mail, you may +// find current contact information at www.suse.com. + +//! Implements a client to access Agama's iscsi service. + +use crate::{ + base_http_client::{BaseHTTPClient, BaseHTTPClientError}, + storage::settings::dasd::DASDConfig, +}; + +#[derive(Debug, thiserror::Error)] +pub enum DASDHTTPClientError { + #[error(transparent)] + DASD(#[from] BaseHTTPClientError), +} + +pub struct DASDHTTPClient { + client: BaseHTTPClient, +} + +impl DASDHTTPClient { + pub fn new(base: BaseHTTPClient) -> Self { + Self { client: base } + } + + pub async fn get_config(&self) -> Result, DASDHTTPClientError> { + Ok(self.client.get("/storage/dasd/config").await?) + } + + pub async fn set_config(&self, config: &DASDConfig) -> Result<(), DASDHTTPClientError> { + Ok(self.client.put_void("/storage/dasd/config", config).await?) + } +} diff --git a/rust/agama-lib/src/storage/http_client/iscsi.rs b/rust/agama-lib/src/storage/http_client/iscsi.rs new file mode 100644 index 0000000000..152b00ea3a --- /dev/null +++ b/rust/agama-lib/src/storage/http_client/iscsi.rs @@ -0,0 +1,54 @@ +// Copyright (c) [2025] SUSE LLC +// +// All Rights Reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, contact SUSE LLC. +// +// To contact SUSE LLC about this file by physical or electronic mail, you may +// find current contact information at www.suse.com. + +//! Implements a client to access Agama's iscsi service. + +use serde_json::value::RawValue; + +use crate::{ + base_http_client::{BaseHTTPClient, BaseHTTPClientError}, + storage::StorageSettings, +}; + +#[derive(Debug, thiserror::Error)] +pub enum ISCSIHTTPClientError { + #[error(transparent)] + ISCSI(#[from] BaseHTTPClientError), +} + +pub struct ISCSIHTTPClient { + client: BaseHTTPClient, +} + +impl ISCSIHTTPClient { + pub fn new(base: BaseHTTPClient) -> Self { + Self { client: base } + } + + pub async fn get_config(&self) -> Result, ISCSIHTTPClientError> { + // TODO: implement it as part of next step + //self.client.get("/storage/config").await + Ok(None) + } + + pub async fn set_config(&self, config: &Box) -> Result<(), ISCSIHTTPClientError> { + Ok(self.client.post_void("/iscsi/config", config).await?) + } +} diff --git a/rust/agama-lib/src/storage/settings.rs b/rust/agama-lib/src/storage/settings.rs index 67068e2a9c..0e432c8037 100644 --- a/rust/agama-lib/src/storage/settings.rs +++ b/rust/agama-lib/src/storage/settings.rs @@ -20,6 +20,8 @@ //! Representation of the storage settings +pub mod dasd; + use crate::install_settings::InstallSettings; use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; diff --git a/rust/agama-lib/src/storage/settings/dasd.rs b/rust/agama-lib/src/storage/settings/dasd.rs new file mode 100644 index 0000000000..a310220e8e --- /dev/null +++ b/rust/agama-lib/src/storage/settings/dasd.rs @@ -0,0 +1,52 @@ +// Copyright (c) [2025] SUSE LLC +// +// All Rights Reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, contact SUSE LLC. +// +// To contact SUSE LLC about this file by physical or electronic mail, you may +// find current contact information at www.suse.com. + +//! Representation of the DASD settings used in set/get config + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Default, Serialize, Deserialize, utoipa::ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct DASDConfig { + pub devices: Vec, +} + +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, utoipa::ToSchema)] +#[serde(rename_all = "camelCase")] +pub enum DASDDeviceState { + #[default] + Active, + Offline, +} + +/// Representation of single DASD device in settings used in set/get config +#[derive(Clone, Debug, Default, Serialize, Deserialize, utoipa::ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct DASDDeviceConfig { + /// DASD device Channel. Mandatory part of device config. + pub channel: String, + /// State of device. Optional, if missing then default is active. + pub state: Option, + /// explicit request to format device. If missing then it will format only if not already formatted. + /// false means never format. + pub format: Option, + /// Set diag flag for device. If missing, then do not change what device already has set. + pub diag: Option, +} diff --git a/rust/agama-lib/src/storage/store.rs b/rust/agama-lib/src/storage/store.rs index 0fbbf87d1a..00580b975c 100644 --- a/rust/agama-lib/src/storage/store.rs +++ b/rust/agama-lib/src/storage/store.rs @@ -20,6 +20,8 @@ //! Implements the store for the storage settings. +pub mod dasd; + use super::{http_client::StorageHTTPClientError, StorageSettings}; use crate::{base_http_client::BaseHTTPClient, storage::http_client::StorageHTTPClient}; diff --git a/rust/agama-lib/src/storage/store/dasd.rs b/rust/agama-lib/src/storage/store/dasd.rs new file mode 100644 index 0000000000..07c21278d6 --- /dev/null +++ b/rust/agama-lib/src/storage/store/dasd.rs @@ -0,0 +1,57 @@ +// Copyright (c) [2025] SUSE LLC +// +// All Rights Reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, contact SUSE LLC. +// +// To contact SUSE LLC about this file by physical or electronic mail, you may +// find current contact information at www.suse.com. + +//! Implements the store for the storage settings. + +use crate::{ + base_http_client::BaseHTTPClient, + storage::{ + http_client::dasd::{DASDHTTPClient, DASDHTTPClientError}, + settings::dasd::DASDConfig, + }, +}; + +#[derive(Debug, thiserror::Error)] +#[error("Error processing DASD settings: {0}")] +pub struct DASDStoreError(#[from] DASDHTTPClientError); + +type DASDStoreResult = Result; + +/// Loads and stores the storage settings from/to the HTTP service. +pub struct DASDStore { + client: DASDHTTPClient, +} + +impl DASDStore { + pub fn new(client: BaseHTTPClient) -> Self { + Self { + client: DASDHTTPClient::new(client), + } + } + + pub async fn load(&self) -> DASDStoreResult> { + Ok(self.client.get_config().await?) + } + + pub async fn store(&self, settings: &DASDConfig) -> DASDStoreResult<()> { + self.client.set_config(settings).await?; + Ok(()) + } +} diff --git a/rust/agama-lib/src/store.rs b/rust/agama-lib/src/store.rs index d09e9d7f83..1177b9b9d1 100644 --- a/rust/agama-lib/src/store.rs +++ b/rust/agama-lib/src/store.rs @@ -35,7 +35,8 @@ use crate::{ security::store::{SecurityStore, SecurityStoreError}, software::{SoftwareStore, SoftwareStoreError}, storage::{ - http_client::{ISCSIHTTPClient, ISCSIHTTPClientError}, + http_client::iscsi::{ISCSIHTTPClient, ISCSIHTTPClientError}, + store::dasd::{DASDStore, DASDStoreError}, StorageStore, StorageStoreError, }, users::{UsersStore, UsersStoreError}, @@ -46,6 +47,8 @@ pub enum StoreError { #[error(transparent)] Bootloader(#[from] BootloaderStoreError), #[error(transparent)] + DASD(#[from] DASDStoreError), + #[error(transparent)] Files(#[from] FilesStoreError), #[error(transparent)] Hostname(#[from] HostnameStoreError), @@ -84,6 +87,7 @@ pub enum StoreError { /// This struct uses the default connection built by [connection function](super::connection). pub struct Store { bootloader: BootloaderStore, + dasd: DASDStore, files: FilesStore, hostname: HostnameStore, users: UsersStore, @@ -103,6 +107,7 @@ impl Store { pub async fn new(http_client: BaseHTTPClient) -> Result { Ok(Self { bootloader: BootloaderStore::new(http_client.clone()), + dasd: DASDStore::new(http_client.clone()), files: FilesStore::new(http_client.clone()), hostname: HostnameStore::new(http_client.clone()), localization: LocalizationStore::new(http_client.clone()), @@ -123,6 +128,7 @@ impl Store { pub async fn load(&self) -> Result { let mut settings = InstallSettings { bootloader: self.bootloader.load().await?, + dasd: self.dasd.load().await?, files: self.files.load().await?, hostname: Some(self.hostname.load().await?), network: Some(self.network.load().await?), @@ -193,6 +199,10 @@ impl Store { if let Some(iscsi) = &settings.iscsi { self.iscsi_client.set_config(iscsi).await? } + // dasd devices has to be activated before storage + if let Some(dasd) = &settings.dasd { + self.dasd.store(dasd).await? + } if settings.storage.is_some() || settings.storage_autoyast.is_some() { self.storage.store(&settings.into()).await? } diff --git a/rust/agama-server/src/storage/web/dasd.rs b/rust/agama-server/src/storage/web/dasd.rs index c681f3489d..443837e1da 100644 --- a/rust/agama-server/src/storage/web/dasd.rs +++ b/rust/agama-server/src/storage/web/dasd.rs @@ -27,7 +27,7 @@ use agama_lib::{ error::ServiceError, - storage::{client::dasd::DASDClient, model::dasd::DASDDevice}, + storage::{client::dasd::DASDClient, model::dasd::DASDDevice, settings::dasd::DASDConfig}, }; use axum::{ extract::State, @@ -70,6 +70,7 @@ pub async fn dasd_service(dbus: &zbus::Connection) -> Result, Servi let state = DASDState { client }; let router = Router::new() .route("/supported", get(supported)) + .route("/config", get(get_config).put(set_config)) .route("/devices", get(devices)) .route("/probe", post(probe)) .route("/format", post(format)) @@ -94,6 +95,37 @@ async fn supported(State(state): State>) -> Result, Err Ok(Json(state.client.supported().await?)) } +/// Returns DASD config +#[utoipa::path( + get, + path="/config", + context_path="/api/storage/dasd", + operation_id = "dasd_get_config", + responses( + (status = OK, description = "Returns DASD config", body=DASDConfig) + ) +)] +async fn get_config(State(state): State>) -> Result, Error> { + Ok(Json(state.client.get_config().await?)) +} + +/// Returns DASD config +#[utoipa::path( + put, + path="/config", + context_path="/api/storage/dasd", + operation_id = "dasd_set_config", + responses( + (status = OK, description = "Sets DASD config") + ) +)] +async fn set_config( + State(state): State>, + Json(config): Json, +) -> Result<(), Error> { + Ok(state.client.set_config(config).await?) +} + /// Returns the list of known DASD devices. #[utoipa::path( get, diff --git a/rust/agama-server/src/web/docs/storage.rs b/rust/agama-server/src/web/docs/storage.rs index 02062f5a25..08c781f72c 100644 --- a/rust/agama-server/src/web/docs/storage.rs +++ b/rust/agama-server/src/web/docs/storage.rs @@ -51,6 +51,8 @@ impl ApiDocBuilder for StorageApiDocBuilder { .path_from::() .path_from::() .path_from::() + .path_from::() + .path_from::() .path_from::() .path_from::() .path_from::() @@ -108,6 +110,9 @@ impl ApiDocBuilder for StorageApiDocBuilder { .schema_from::() .schema_from::() .schema_from::() + .schema_from::() + .schema_from::() + .schema_from::() .schema_from::() .schema_from::() .schema_from::() diff --git a/service/lib/agama/dbus/storage/interfaces/dasd_manager.rb b/service/lib/agama/dbus/storage/interfaces/dasd_manager.rb index 16dd67e210..08c13cb14a 100644 --- a/service/lib/agama/dbus/storage/interfaces/dasd_manager.rb +++ b/service/lib/agama/dbus/storage/interfaces/dasd_manager.rb @@ -178,6 +178,7 @@ def find_dasds(paths) def register_dasd_callbacks dasd_backend.on_probe do |dasds| dasds_tree.populate(dasds) + deprecate_system end dasd_backend.on_refresh do |dasds|