diff --git a/rust/agama-lib/src/storage/client/zfcp.rs b/rust/agama-lib/src/storage/client/zfcp.rs index 1e16aef86d..dd8b285912 100644 --- a/rust/agama-lib/src/storage/client/zfcp.rs +++ b/rust/agama-lib/src/storage/client/zfcp.rs @@ -34,8 +34,71 @@ use crate::{ }, }; +pub async fn get_wwpns(controller_id: &str) -> Result, ServiceError> { + let output = std::process::Command::new("zfcp_san_disc") + .arg("-b") + .arg(controller_id) + .arg("-W") + .output() + .expect("zfcp_san_disc failed"); // TODO: real error handling + Ok(String::from_utf8_lossy(&output.stdout) + .split_whitespace() + .map(|s| s.to_string()) + .collect()) +} + +pub async fn get_luns(controller_id: String, wwpn: String) -> Result, ServiceError> { + let output = std::process::Command::new("zfcp_san_disc") + .arg("-b") + .arg(controller_id) + .arg("-p") + .arg(wwpn) + .arg("-L") + .output() + .expect("zfcp_san_disc failed"); // TODO: real error handling + Ok(String::from_utf8_lossy(&output.stdout) + .split_whitespace() + .map(|s| s.to_string()) + .collect()) +} + +/// Obtains a LUNs map for the given controller +/// +/// Given a controller id it returns a HashMap with each of its WWPNs as keys and the list of +/// LUNS corresponding to that specific WWPN as values. +/// +/// Arguments: +/// +/// `controller_id`: controller id +pub async fn get_luns_map( + controller_id: String, +) -> Result>, ServiceError> { + let wwpns = get_wwpns(controller_id.as_str()).await?; + let mut tasks = vec![]; + for wwpn in wwpns { + tasks.push(( + wwpn.clone(), + tokio::spawn(get_luns(controller_id.to_string(), wwpn.clone())), + )); + } + let mut result = HashMap::with_capacity(tasks.len()); + for (wwpn, task) in tasks { + result.insert(wwpn.clone(), task.await.expect("thread join failed")?); + } + Ok(result) +} + const ZFCP_CONTROLLER_PREFIX: &'static str = "/org/opensuse/Agama/Storage1/zfcp_controllers"; +/// Partial zfcp controller data used for thread processing +/// TODO: for final version move all those thread related methods to this struct to procude final result +struct PartialZFCPController { + pub id: String, + pub channel: String, + pub lun_scan: bool, + pub active: bool +} + /// Client to connect to Agama's D-Bus API for zFCP management. #[derive(Clone)] pub struct ZFCPClient<'a> { @@ -99,23 +162,40 @@ impl<'a> ZFCPClient<'a> { ) -> Result, ServiceError> { let managed_objects = self.object_manager_proxy.get_managed_objects().await?; - let mut devices: Vec<(OwnedObjectPath, ZFCPController)> = vec![]; + let mut devices: Vec<(OwnedObjectPath, PartialZFCPController)> = vec![]; for (path, ifaces) in managed_objects { if let Some(properties) = ifaces.get("org.opensuse.Agama.Storage1.ZFCP.Controller") { let id = extract_id_from_path(&path)?.to_string(); + let channel: String = get_property(properties, "Channel")?; devices.push(( path, - ZFCPController { - id: id.clone(), - channel: get_property(properties, "Channel")?, + PartialZFCPController { + id: id, + channel: channel, lun_scan: get_property(properties, "LUNScan")?, active: get_property(properties, "Active")?, - luns_map: self.get_luns_map(id.as_str()).await?, }, )) } } - Ok(devices) + let mut tasks = vec![]; + for (_path, partial_controller) in &devices { + tasks.push( + tokio::spawn(get_luns_map(partial_controller.channel.clone())) + ) + } + let mut result = Vec::with_capacity(devices.len()); + for (index, task) in tasks.into_iter().enumerate() { + let (path, partial) = devices.get(index).unwrap(); + result.push((path.clone(), ZFCPController { + id: partial.id.clone(), + channel: partial.channel.clone(), + lun_scan: partial.lun_scan, + active: partial.active, + luns_map: task.await.expect("thread join failed")?, + })); + } + Ok(result) } async fn get_controller_proxy( @@ -129,12 +209,6 @@ impl<'a> ZFCPClient<'a> { Ok(dbus) } - pub async fn activate_controller(&self, controller_id: &str) -> Result<(), ServiceError> { - let controller = self.get_controller_proxy(controller_id).await?; - controller.activate().await?; - Ok(()) - } - pub async fn get_wwpns(&self, controller_id: &str) -> Result, ServiceError> { let controller = self.get_controller_proxy(controller_id).await?; let result = controller.get_wwpns().await?; @@ -176,6 +250,12 @@ impl<'a> ZFCPClient<'a> { .collect::>, _>>() } + pub async fn activate_controller(&self, controller_id: &str) -> Result<(), ServiceError> { + let controller = self.get_controller_proxy(controller_id).await?; + controller.activate().await?; + Ok(()) + } + pub async fn activate_disk( &self, controller_id: &str,