From 8545d6055ed2fd97724de4591f28881ef2bac70f Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Wed, 2 Aug 2023 10:32:27 +0100 Subject: [PATCH 01/12] Add support to assign an interface or match settings to a connection --- .../src/network/dbus/interfaces.rs | 90 ++++++++++++++++++- .../src/network/dbus/tree.rs | 5 ++ rust/agama-dbus-server/src/network/model.rs | 26 ++++++ rust/agama-dbus-server/src/network/nm/dbus.rs | 62 ++++++++++++- rust/agama-lib/src/network/client.rs | 56 +++++++++++- rust/agama-lib/src/network/proxies.rs | 29 ++++++ rust/agama-lib/src/network/settings.rs | 25 ++++++ 7 files changed, 288 insertions(+), 5 deletions(-) diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces.rs b/rust/agama-dbus-server/src/network/dbus/interfaces.rs index fb92b711de..e0f48043f7 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces.rs @@ -7,7 +7,8 @@ use crate::network::{ action::Action, error::NetworkStateError, model::{ - Connection as NetworkConnection, Device as NetworkDevice, IpAddress, WirelessConnection, + Connection as NetworkConnection, Device as NetworkDevice, IpAddress, MatchConfig, + WirelessConnection, }, }; use log; @@ -205,6 +206,93 @@ impl Connection { pub async fn id(&self) -> String { self.get_connection().await.id().to_string() } + + #[dbus_interface(property)] + pub fn interface(&self) -> String { + self.get_connection().interface().to_string() + } +} + +/// D-Bus interface for Match settings +pub struct Match { + actions: Arc>>, + connection: Arc>, +} + +impl Match { + /// Creates a Match Settings interface object. + /// + /// * `actions`: sending-half of a channel to send actions. + /// * `connection`: connection to expose over D-Bus. + pub fn new(actions: Sender, connection: Arc>) -> Self { + Self { + actions: Arc::new(Mutex::new(actions)), + connection, + } + } + + /// Returns the underlying connection. + fn get_connection(&self) -> MutexGuard { + self.connection.lock() + } + + /// Updates the connection data in the NetworkSystem. + /// + /// * `connection`: Updated connection. + fn update_connection( + &self, + connection: MutexGuard, + ) -> zbus::fdo::Result<()> { + let actions = self.actions.lock(); + actions + .send(Action::UpdateConnection(connection.clone())) + .unwrap(); + Ok(()) + } +} + +#[dbus_interface(name = "org.opensuse.Agama.Network1.Connection.Match")] +impl Match { + /// List of driver + #[dbus_interface(property)] + pub fn driver(&self) -> Vec { + let connection = self.get_connection(); + connection.match_config().driver.clone() + } + + pub fn set_driver(&mut self, driver: Vec) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection(); + let config = connection.match_config_mut(); + config.driver = driver; + self.update_connection(connection) + } + + /// List of paths + #[dbus_interface(property)] + pub fn path(&self) -> Vec { + let connection = self.get_connection(); + connection.match_config().path.clone() + } + + pub fn set_path(&mut self, path: Vec) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection(); + let config = connection.match_config_mut(); + config.path = path; + self.update_connection(connection) + } + /// List of driver + #[dbus_interface(property)] + pub fn interface(&self) -> Vec { + let connection = self.get_connection(); + connection.match_config().interface.clone() + } + + /// List of kernel options + #[dbus_interface(property)] + pub fn kernel(&self) -> Vec { + let connection = self.get_connection(); + connection.match_config().kernel.clone() + } } /// D-Bus interface for IPv4 settings diff --git a/rust/agama-dbus-server/src/network/dbus/tree.rs b/rust/agama-dbus-server/src/network/dbus/tree.rs index e299b3a78f..2a357e7b7c 100644 --- a/rust/agama-dbus-server/src/network/dbus/tree.rs +++ b/rust/agama-dbus-server/src/network/dbus/tree.rs @@ -104,6 +104,11 @@ impl Tree { ) .await?; + self.add_interface( + &path, + interfaces::Match::new(self.actions.clone(), Arc::clone(&cloned)), + ) + .await?; if let Connection::Wireless(_) = conn { self.add_interface( &path, diff --git a/rust/agama-dbus-server/src/network/model.rs b/rust/agama-dbus-server/src/network/model.rs index 43c6ef25c7..1962ab67a0 100644 --- a/rust/agama-dbus-server/src/network/model.rs +++ b/rust/agama-dbus-server/src/network/model.rs @@ -262,6 +262,14 @@ impl Connection { self.base_mut().id = id.to_string() } + pub fn interface(&self) -> &str { + self.base().interface.as_str() + } + + pub fn set_interface(&mut self, interface: &str) { + self.base_mut().interface = interface.to_string() + } + pub fn uuid(&self) -> Uuid { self.base().uuid } @@ -274,6 +282,14 @@ impl Connection { &mut self.base_mut().ipv4 } + pub fn match_config(&self) -> &MatchConfig { + &self.base().match_config + } + + pub fn match_config_mut(&mut self) -> &mut MatchConfig { + &mut self.base_mut().match_config + } + pub fn remove(&mut self) { self.base_mut().status = Status::Removed; } @@ -294,6 +310,8 @@ pub struct BaseConnection { pub uuid: Uuid, pub ipv4: Ipv4Config, pub status: Status, + pub interface: String, + pub match_config: MatchConfig, } impl PartialEq for BaseConnection { @@ -317,6 +335,14 @@ pub struct Ipv4Config { pub gateway: Option, } +#[derive(Debug, Default, Clone)] +pub struct MatchConfig { + pub driver: Vec, + pub interface: Vec, + pub path: Vec, + pub kernel: Vec, +} + #[derive(Debug, Error)] #[error("Unknown IP configuration method name: {0}")] pub struct UnknownIpMethod(String); diff --git a/rust/agama-dbus-server/src/network/nm/dbus.rs b/rust/agama-dbus-server/src/network/nm/dbus.rs index 7511b44399..80d501ce31 100644 --- a/rust/agama-dbus-server/src/network/nm/dbus.rs +++ b/rust/agama-dbus-server/src/network/nm/dbus.rs @@ -173,6 +173,14 @@ fn base_connection_from_dbus(conn: &OwnedNestedHash) -> Option { ..Default::default() }; + if let Some(interface) = connection.get("interface-name") { + let interface: &str = interface.downcast_ref()?; + base_connection.interface = interface.parse().unwrap(); + } + if let Some(match_config) = conn.get("match") { + base_connection.match_config = match_config_from_dbus(match_config)?; + } + if let Some(ipv4) = conn.get("ipv4") { base_connection.ipv4 = ipv4_config_from_dbus(ipv4)?; } @@ -180,6 +188,48 @@ fn base_connection_from_dbus(conn: &OwnedNestedHash) -> Option { Some(base_connection) } +fn match_config_from_dbus( + match_config: &HashMap, +) -> Option { + let mut match_conf = MatchConfig { + ..Default::default() + }; + + if let Some(drivers) = match_config.get("driver") { + let drivers = drivers.downcast_ref::()?; + for driver in drivers.get() { + let driver: &str = driver.downcast_ref()?; + match_conf.driver.push(driver.parse().unwrap()); + } + } + + if let Some(interface_names) = match_config.get("interface-name") { + let interface_names = interface_names.downcast_ref::()?; + for name in interface_names.get() { + let name: &str = name.downcast_ref()?; + match_conf.interface.push(name.parse().unwrap()); + } + } + + if let Some(paths) = match_config.get("path") { + let paths = paths.downcast_ref::()?; + for path in paths.get() { + let path: &str = path.downcast_ref()?; + match_conf.path.push(path.parse().unwrap()); + } + } + + if let Some(kernel_options) = match_config.get("kernel-command-line") { + let options = kernel_options.downcast_ref::()?; + for option in options.get() { + let option: &str = option.downcast_ref()?; + match_conf.kernel.push(option.parse().unwrap()); + } + } + + Some(match_conf) +} + fn ipv4_config_from_dbus(ipv4: &HashMap) -> Option { let method: &str = ipv4.get("method")?.downcast_ref()?; let address_data = ipv4.get("address-data")?; @@ -244,8 +294,8 @@ fn wireless_config_from_dbus(conn: &OwnedNestedHash) -> Option { #[cfg(test)] mod test { use super::{ - connection_from_dbus, connection_to_dbus, merge_dbus_connections, NestedHash, - OwnedNestedHash, + connection_from_dbus, connection_to_dbus, match_config_from_dbus, merge_dbus_connections, + NestedHash, OwnedNestedHash, }; use crate::network::{model::*, nm::dbus::ETHERNET_KEY}; use agama_lib::network::types::SSID; @@ -279,9 +329,15 @@ mod test { ), ]); + let match_section = HashMap::from([( + "kernel-command-line".to_string(), + Value::new(vec!["pci-0000:00:19.0"]).to_owned(), + )]); + let dbus_conn = HashMap::from([ ("connection".to_string(), connection_section), ("ipv4".to_string(), ipv4_section), + ("match".to_string(), match_section), (ETHERNET_KEY.to_string(), build_ethernet_section_from_dbus()), ]); @@ -289,6 +345,8 @@ mod test { assert_eq!(connection.id(), "eth0"); let ipv4 = connection.ipv4(); + let match_config = connection.match_config().as_ref().unwrap(); + assert_eq!(match_config.kernel, vec!["pci-0000:00:19.0"]); assert_eq!(ipv4.addresses, vec!["192.168.0.10/24".parse().unwrap()]); assert_eq!(ipv4.nameservers, vec![Ipv4Addr::new(192, 168, 0, 2)]); assert_eq!(ipv4.method, IpMethod::Auto); diff --git a/rust/agama-lib/src/network/client.rs b/rust/agama-lib/src/network/client.rs index 240d0359f0..7cb2a49cb7 100644 --- a/rust/agama-lib/src/network/client.rs +++ b/rust/agama-lib/src/network/client.rs @@ -1,8 +1,8 @@ -use super::settings::{NetworkConnection, WirelessSettings}; +use super::settings::{MatchSettings, NetworkConnection, WirelessSettings}; use super::types::SSID; use crate::error::ServiceError; -use super::proxies::{ConnectionProxy, ConnectionsProxy, IPv4Proxy, WirelessProxy}; +use super::proxies::{ConnectionProxy, ConnectionsProxy, IPv4Proxy, MatchProxy, WirelessProxy}; use async_std::stream::StreamExt; use zbus::zvariant::OwnedObjectPath; use zbus::Connection; @@ -33,6 +33,11 @@ impl<'a> NetworkClient<'a> { connection.wireless = Some(wireless); } + let match_settings = self.match_settings_from(path.as_str()).await?; + if !match_settings.is_empty() { + connection.match_settings = Some(match_settings); + } + connections.push(connection); } @@ -54,6 +59,10 @@ impl<'a> NetworkClient<'a> { .build() .await?; let id = connection_proxy.id().await?; + let interface = match connection_proxy.interface().await?.as_str() { + "" => None, + value => Some(value.to_string()), + }; let ipv4_proxy = IPv4Proxy::builder(&self.connection) .path(path)? @@ -74,6 +83,7 @@ impl<'a> NetworkClient<'a> { gateway, addresses, nameservers, + interface, ..Default::default() }) } @@ -96,6 +106,23 @@ impl<'a> NetworkClient<'a> { Ok(wireless) } + /// Returns the [match settings][MatchSettings] for the given connection + /// + /// * `path`: the connections path to get the match settings from + async fn match_settings_from(&self, path: &str) -> Result { + let match_proxy = MatchProxy::builder(&self.connection) + .path(path)? + .build() + .await?; + let match_settings = MatchSettings { + paths: match_proxy.path().await?, + kernel: match_proxy.kernel().await?, + interface: match_proxy.interface().await?, + driver: match_proxy.driver().await?, + }; + Ok(match_settings) + } + /// Adds or updates a network connection. /// /// If a network connection with the same name exists, it updates its settings. Otherwise, it @@ -168,6 +195,11 @@ impl<'a> NetworkClient<'a> { if let Some(ref wireless) = conn.wireless { self.update_wireless_settings(path, wireless).await?; } + + if let Some(ref match_settings) = conn.match_settings { + self.update_match_settings(path, match_settings).await?; + } + Ok(()) } @@ -193,4 +225,24 @@ impl<'a> NetworkClient<'a> { proxy.set_password(&wireless.password).await?; Ok(()) } + + /// Updates the match settings for network connection. + /// + /// * `path`: connection D-Bus path. + /// * `match_settings`: match settings of the network connection. + async fn update_match_settings( + &self, + path: &OwnedObjectPath, + match_settings: &MatchSettings, + ) -> Result<(), ServiceError> { + let proxy = MatchProxy::builder(&self.connection) + .path(path)? + .build() + .await?; + + let paths: Vec<_> = match_settings.path.iter().map(String::as_ref).collect(); + proxy.set_path(paths.as_slice()).await?; + + Ok(()) + } } diff --git a/rust/agama-lib/src/network/proxies.rs b/rust/agama-lib/src/network/proxies.rs index 1456e286c5..47a8be26bc 100644 --- a/rust/agama-lib/src/network/proxies.rs +++ b/rust/agama-lib/src/network/proxies.rs @@ -79,6 +79,10 @@ trait Connection { /// Id property #[dbus_proxy(property)] fn id(&self) -> zbus::Result; + #[dbus_proxy(property)] + fn interface(&self) -> zbus::Result; + #[dbus_proxy(property)] + fn set_interface(&self, interface: &str) -> zbus::Result<()>; } #[dbus_proxy( @@ -115,3 +119,28 @@ trait IPv4 { #[dbus_proxy(property)] fn set_nameservers(&self, value: &[&str]) -> zbus::Result<()>; } + +#[dbus_proxy( + interface = "org.opensuse.Agama.Network1.Connection.Match", + default_service = "org.opensuse.Agama.Network1", + default_path = "/org/opensuse/Agama/Network1" +)] +trait Match { + /// Driver property + #[dbus_proxy(property)] + fn driver(&self) -> zbus::Result>; + + /// Interface property + #[dbus_proxy(property)] + fn interface(&self) -> zbus::Result>; + + /// Path property + #[dbus_proxy(property)] + fn path(&self) -> zbus::Result>; + #[dbus_proxy(property)] + fn set_path(&self, value: &[&str]) -> zbus::Result<()>; + + /// Path property + #[dbus_proxy(property)] + fn kernel(&self) -> zbus::Result>; +} diff --git a/rust/agama-lib/src/network/settings.rs b/rust/agama-lib/src/network/settings.rs index 04316d95ba..c3c1ce5f7e 100644 --- a/rust/agama-lib/src/network/settings.rs +++ b/rust/agama-lib/src/network/settings.rs @@ -16,6 +16,27 @@ pub struct NetworkSettings { pub connections: Vec, } +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct MatchSettings { + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub driver: Vec, + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub paths: Vec, + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub kernel: Vec, + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub interface: Vec, +} + +impl MatchSettings { + pub fn is_empty(&self) -> bool { + self.paths.is_empty() + && self.driver.is_empty() + && self.kernel.is_empty() + && self.interface.is_empty() + } +} + #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct WirelessSettings { #[serde(skip_serializing_if = "String::is_empty")] @@ -38,6 +59,10 @@ pub struct NetworkConnection { pub nameservers: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub wireless: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub interface: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub match_settings: Option, } impl NetworkConnection { From d1e92cc91a74ce3e0c035386be25d75de34a5710 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Tue, 5 Sep 2023 09:59:39 +0100 Subject: [PATCH 02/12] Adapt the code usinc async-aware mutex --- .../src/network/dbus/interfaces.rs | 48 +++++++++---------- rust/agama-lib/src/network/client.rs | 2 +- rust/agama-lib/src/network/proxies.rs | 6 +-- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces.rs b/rust/agama-dbus-server/src/network/dbus/interfaces.rs index e0f48043f7..560034c6d0 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces.rs @@ -7,8 +7,7 @@ use crate::network::{ action::Action, error::NetworkStateError, model::{ - Connection as NetworkConnection, Device as NetworkDevice, IpAddress, MatchConfig, - WirelessConnection, + Connection as NetworkConnection, Device as NetworkDevice, IpAddress, WirelessConnection, }, }; use log; @@ -208,8 +207,8 @@ impl Connection { } #[dbus_interface(property)] - pub fn interface(&self) -> String { - self.get_connection().interface().to_string() + pub async fn interface(&self) -> String { + self.get_connection().await.interface().to_string() } } @@ -232,65 +231,66 @@ impl Match { } /// Returns the underlying connection. - fn get_connection(&self) -> MutexGuard { - self.connection.lock() + async fn get_connection(&self) -> MutexGuard { + self.connection.lock().await } /// Updates the connection data in the NetworkSystem. /// /// * `connection`: Updated connection. - fn update_connection( + async fn update_connection<'a>( &self, - connection: MutexGuard, + connection: MutexGuard<'a, NetworkConnection>, ) -> zbus::fdo::Result<()> { - let actions = self.actions.lock(); + let actions = self.actions.lock().await; actions .send(Action::UpdateConnection(connection.clone())) + .await .unwrap(); Ok(()) } } -#[dbus_interface(name = "org.opensuse.Agama.Network1.Connection.Match")] +#[dbus_interface(name = "org.opensuse.Agama1.Network.Connection.Match")] impl Match { /// List of driver #[dbus_interface(property)] - pub fn driver(&self) -> Vec { - let connection = self.get_connection(); + pub async fn driver(&self) -> Vec { + let connection = self.get_connection().await; connection.match_config().driver.clone() } - pub fn set_driver(&mut self, driver: Vec) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection(); + pub async fn set_driver(&mut self, driver: Vec) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection().await; let config = connection.match_config_mut(); config.driver = driver; - self.update_connection(connection) + self.update_connection(connection).await } /// List of paths #[dbus_interface(property)] - pub fn path(&self) -> Vec { - let connection = self.get_connection(); + pub async fn path(&self) -> Vec { + let connection = self.get_connection().await; connection.match_config().path.clone() } - pub fn set_path(&mut self, path: Vec) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection(); + pub async fn set_path(&mut self, path: Vec) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection().await; let config = connection.match_config_mut(); config.path = path; - self.update_connection(connection) + self.update_connection(connection).await } /// List of driver #[dbus_interface(property)] - pub fn interface(&self) -> Vec { - let connection = self.get_connection(); + pub async fn interface(&self) -> Vec { + let connection = self.get_connection().await; connection.match_config().interface.clone() } /// List of kernel options #[dbus_interface(property)] - pub fn kernel(&self) -> Vec { - let connection = self.get_connection(); + pub async fn kernel(&self) -> Vec { + let connection = self.get_connection().await; connection.match_config().kernel.clone() } } diff --git a/rust/agama-lib/src/network/client.rs b/rust/agama-lib/src/network/client.rs index 7cb2a49cb7..7977469c6f 100644 --- a/rust/agama-lib/src/network/client.rs +++ b/rust/agama-lib/src/network/client.rs @@ -240,7 +240,7 @@ impl<'a> NetworkClient<'a> { .build() .await?; - let paths: Vec<_> = match_settings.path.iter().map(String::as_ref).collect(); + let paths: Vec<_> = match_settings.paths.iter().map(String::as_ref).collect(); proxy.set_path(paths.as_slice()).await?; Ok(()) diff --git a/rust/agama-lib/src/network/proxies.rs b/rust/agama-lib/src/network/proxies.rs index 47a8be26bc..d07f853ecf 100644 --- a/rust/agama-lib/src/network/proxies.rs +++ b/rust/agama-lib/src/network/proxies.rs @@ -121,9 +121,9 @@ trait IPv4 { } #[dbus_proxy( - interface = "org.opensuse.Agama.Network1.Connection.Match", - default_service = "org.opensuse.Agama.Network1", - default_path = "/org/opensuse/Agama/Network1" + interface = "org.opensuse.Agama1.Network.Connection.Match", + default_service = "org.opensuse.Agama1.Network", + default_path = "/org/opensuse/Agama1/Network" )] trait Match { /// Driver property From f5d504e2aa5da2131b6566a5ae4a2d13434e1a6f Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Thu, 7 Sep 2023 08:27:33 +0100 Subject: [PATCH 03/12] Send match settings to D-BUS when updating a connection --- .../src/network/dbus/interfaces.rs | 18 +++++++ rust/agama-dbus-server/src/network/model.rs | 2 +- rust/agama-dbus-server/src/network/nm/dbus.rs | 53 +++++++++++++++++-- rust/agama-lib/src/network/proxies.rs | 2 +- 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces.rs b/rust/agama-dbus-server/src/network/dbus/interfaces.rs index 560034c6d0..0db35be010 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces.rs @@ -260,6 +260,7 @@ impl Match { connection.match_config().driver.clone() } + #[dbus_interface(property)] pub async fn set_driver(&mut self, driver: Vec) -> zbus::fdo::Result<()> { let mut connection = self.get_connection().await; let config = connection.match_config_mut(); @@ -274,6 +275,7 @@ impl Match { connection.match_config().path.clone() } + #[dbus_interface(property)] pub async fn set_path(&mut self, path: Vec) -> zbus::fdo::Result<()> { let mut connection = self.get_connection().await; let config = connection.match_config_mut(); @@ -287,12 +289,28 @@ impl Match { connection.match_config().interface.clone() } + #[dbus_interface(property)] + pub async fn set_interface(&mut self, interface: Vec) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection().await; + let config = connection.match_config_mut(); + config.interface = interface; + self.update_connection(connection).await + } + /// List of kernel options #[dbus_interface(property)] pub async fn kernel(&self) -> Vec { let connection = self.get_connection().await; connection.match_config().kernel.clone() } + + #[dbus_interface(property)] + pub async fn set_kernel(&mut self, kernel: Vec) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection().await; + let config = connection.match_config_mut(); + config.kernel = kernel; + self.update_connection(connection).await + } } /// D-Bus interface for IPv4 settings diff --git a/rust/agama-dbus-server/src/network/model.rs b/rust/agama-dbus-server/src/network/model.rs index 1962ab67a0..7478e546de 100644 --- a/rust/agama-dbus-server/src/network/model.rs +++ b/rust/agama-dbus-server/src/network/model.rs @@ -335,7 +335,7 @@ pub struct Ipv4Config { pub gateway: Option, } -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, PartialEq, Clone)] pub struct MatchConfig { pub driver: Vec, pub interface: Vec, diff --git a/rust/agama-dbus-server/src/network/nm/dbus.rs b/rust/agama-dbus-server/src/network/nm/dbus.rs index 80d501ce31..7a18e01641 100644 --- a/rust/agama-dbus-server/src/network/nm/dbus.rs +++ b/rust/agama-dbus-server/src/network/nm/dbus.rs @@ -19,12 +19,13 @@ const LOOPBACK_KEY: &str = "loopback"; /// Converts a connection struct into a HashMap that can be sent over D-Bus. /// -/// * `conn`: Connection to cnvert. +/// * `conn`: Connection to convert. pub fn connection_to_dbus(conn: &Connection) -> NestedHash { let mut result = NestedHash::new(); let mut connection_dbus = HashMap::from([("id", conn.id().into()), ("type", ETHERNET_KEY.into())]); result.insert("ipv4", ipv4_to_dbus(conn.ipv4())); + result.insert("match", match_config_to_dbus(conn.match_config())); if let Connection::Wireless(wireless) = conn { connection_dbus.insert("type", "802-11-wireless".into()); @@ -33,6 +34,7 @@ pub fn connection_to_dbus(conn: &Connection) -> NestedHash { result.insert(k, v); } } + result.insert("connection", connection_dbus); result } @@ -159,6 +161,48 @@ fn wireless_config_to_dbus(conn: &WirelessConnection) -> NestedHash { ]) } +/// Converts a MatchConfig struct into a HashMap that can be sent over D-Bus. +/// +/// * `match_config`: MatchConfig to convert. +fn match_config_to_dbus(match_config: &MatchConfig) -> HashMap<&str, zvariant::Value> { + let mut match_config_dbus = HashMap::new(); + + let drivers: Value = match_config + .driver + .iter() + .map(|dr| dr.to_string()) + .collect::>() + .into(); + match_config_dbus.insert("driver", drivers); + + let kernels: Value = match_config + .kernel + .iter() + .map(|dr| dr.to_string()) + .collect::>() + .into(); + match_config_dbus.insert("kernel-command-line", kernels); + + let paths: Value = match_config + .path + .iter() + .map(|dr| dr.to_string()) + .collect::>() + .into(); + match_config_dbus.insert("path", paths); + + let interfaces: Value = match_config + .interface + .iter() + .map(|dr| dr.to_string()) + .collect::>() + .into(); + + match_config_dbus.insert("interface-name", interfaces); + + match_config_dbus +} + fn base_connection_from_dbus(conn: &OwnedNestedHash) -> Option { let Some(connection) = conn.get("connection") else { return None; @@ -177,6 +221,7 @@ fn base_connection_from_dbus(conn: &OwnedNestedHash) -> Option { let interface: &str = interface.downcast_ref()?; base_connection.interface = interface.parse().unwrap(); } + if let Some(match_config) = conn.get("match") { base_connection.match_config = match_config_from_dbus(match_config)?; } @@ -294,8 +339,8 @@ fn wireless_config_from_dbus(conn: &OwnedNestedHash) -> Option { #[cfg(test)] mod test { use super::{ - connection_from_dbus, connection_to_dbus, match_config_from_dbus, merge_dbus_connections, - NestedHash, OwnedNestedHash, + connection_from_dbus, connection_to_dbus, merge_dbus_connections, NestedHash, + OwnedNestedHash, }; use crate::network::{model::*, nm::dbus::ETHERNET_KEY}; use agama_lib::network::types::SSID; @@ -345,7 +390,7 @@ mod test { assert_eq!(connection.id(), "eth0"); let ipv4 = connection.ipv4(); - let match_config = connection.match_config().as_ref().unwrap(); + let match_config = connection.match_config().clone(); assert_eq!(match_config.kernel, vec!["pci-0000:00:19.0"]); assert_eq!(ipv4.addresses, vec!["192.168.0.10/24".parse().unwrap()]); assert_eq!(ipv4.nameservers, vec![Ipv4Addr::new(192, 168, 0, 2)]); diff --git a/rust/agama-lib/src/network/proxies.rs b/rust/agama-lib/src/network/proxies.rs index d07f853ecf..4de94c5560 100644 --- a/rust/agama-lib/src/network/proxies.rs +++ b/rust/agama-lib/src/network/proxies.rs @@ -122,7 +122,7 @@ trait IPv4 { #[dbus_proxy( interface = "org.opensuse.Agama1.Network.Connection.Match", - default_service = "org.opensuse.Agama1.Network", + default_service = "org.opensuse.Agama1", default_path = "/org/opensuse/Agama1/Network" )] trait Match { From 237e93d263d3738ad76e5c4bc591c317ead0159b Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Thu, 7 Sep 2023 11:20:50 +0100 Subject: [PATCH 04/12] Allow to modify the connection interface name --- .../src/network/dbus/interfaces.rs | 31 ++++++++++++- .../src/network/dbus/tree.rs | 7 ++- rust/agama-dbus-server/tests/network.rs | 26 +++++++++++ rust/agama-lib/src/network/client.rs | 43 +++++++++++++++---- 4 files changed, 95 insertions(+), 12 deletions(-) diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces.rs b/rust/agama-dbus-server/src/network/dbus/interfaces.rs index 0db35be010..4dbc4a9d54 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces.rs @@ -177,21 +177,41 @@ impl Connections { /// /// It offers an API to query a connection. pub struct Connection { + actions: Arc>>, connection: Arc>, } impl Connection { /// Creates a Connection interface object. /// + /// * `actions`: sending-half of a channel to send actions. /// * `connection`: connection to expose over D-Bus. - pub fn new(connection: Arc>) -> Self { - Self { connection } + pub fn new(actions: Sender, connection: Arc>) -> Self { + Self { + actions: Arc::new(Mutex::new(actions)), + connection, + } } /// Returns the underlying connection. async fn get_connection(&self) -> MutexGuard { self.connection.lock().await } + + /// Updates the connection data in the NetworkSystem. + /// + /// * `connection`: Updated connection. + async fn update_connection<'a>( + &self, + connection: MutexGuard<'a, NetworkConnection>, + ) -> zbus::fdo::Result<()> { + let actions = self.actions.lock().await; + actions + .send(Action::UpdateConnection(connection.clone())) + .await + .unwrap(); + Ok(()) + } } #[dbus_interface(name = "org.opensuse.Agama1.Network.Connection")] @@ -210,6 +230,13 @@ impl Connection { pub async fn interface(&self) -> String { self.get_connection().await.interface().to_string() } + + #[dbus_interface(property)] + pub async fn set_interface(&mut self, name: &str) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection().await; + connection.set_interface(name); + self.update_connection(connection).await + } } /// D-Bus interface for Match settings diff --git a/rust/agama-dbus-server/src/network/dbus/tree.rs b/rust/agama-dbus-server/src/network/dbus/tree.rs index 2a357e7b7c..bcf5f097b3 100644 --- a/rust/agama-dbus-server/src/network/dbus/tree.rs +++ b/rust/agama-dbus-server/src/network/dbus/tree.rs @@ -95,8 +95,11 @@ impl Tree { log::info!("Publishing network connection '{}'", id); let cloned = Arc::new(Mutex::new(conn.clone())); - self.add_interface(&path, interfaces::Connection::new(Arc::clone(&cloned))) - .await?; + self.add_interface( + &path, + interfaces::Connection::new(self.actions.clone(), Arc::clone(&cloned)), + ) + .await?; self.add_interface( &path, diff --git a/rust/agama-dbus-server/tests/network.rs b/rust/agama-dbus-server/tests/network.rs index cef72b000f..e473fbfc7d 100644 --- a/rust/agama-dbus-server/tests/network.rs +++ b/rust/agama-dbus-server/tests/network.rs @@ -78,3 +78,29 @@ async fn test_add_connection() { assert_eq!(conn.id, "wlan0"); assert_eq!(conn.device_type(), DeviceType::Wireless); } + +#[test] +async fn test_update_connection() { + let mut server = DBusServer::new().start().await; + + let device = model::Device { + name: String::from("eth0"), + type_: DeviceType::Ethernet, + }; + let eth0 = model::Connection::new("eth0".to_string(), DeviceType::Ethernet); + let state = NetworkState::new(vec![device], vec![eth0]); + let adapter = NetworkTestAdapter(state); + + let _service = NetworkService::start(&server.connection(), adapter) + .await + .unwrap(); + + server.request_name().await.unwrap(); + + let client = NetworkClient::new(server.connection()).await.unwrap(); + let mut dbus_eth0 = client.get_connection("eth0").await.unwrap(); + dbus_eth0.interface = Some("eth0".to_string()); + client.add_or_update_connection(&dbus_eth0).await.unwrap(); + let dbus_eth0 = client.get_connection("eth0").await.unwrap(); + assert_eq!(dbus_eth0.interface, Some("eth0".to_string())); +} diff --git a/rust/agama-lib/src/network/client.rs b/rust/agama-lib/src/network/client.rs index 7977469c6f..c196a1676a 100644 --- a/rust/agama-lib/src/network/client.rs +++ b/rust/agama-lib/src/network/client.rs @@ -21,6 +21,11 @@ impl<'a> NetworkClient<'a> { }) } + pub async fn get_connection(&self, id: &str) -> Result { + let path = self.connections_proxy.get_connection(id).await?; + Ok(self.connection_from(path.as_str()).await?) + } + /// Returns an array of network connections pub async fn connections(&self) -> Result, ServiceError> { let connection_paths = self.connections_proxy.get_connections().await?; @@ -173,6 +178,36 @@ impl<'a> NetworkClient<'a> { &self, path: &OwnedObjectPath, conn: &NetworkConnection, + ) -> Result<(), ServiceError> { + let proxy = ConnectionProxy::builder(&self.connection) + .path(path)? + .build() + .await?; + + let interface = conn.interface.as_deref().unwrap_or(""); + proxy.set_interface(interface).await?; + + self.update_ipv4_settings(path, conn).await?; + + if let Some(ref wireless) = conn.wireless { + self.update_wireless_settings(path, wireless).await?; + } + + if let Some(ref match_settings) = conn.match_settings { + self.update_match_settings(path, match_settings).await?; + } + + Ok(()) + } + + /// Updates the IPv4 setttings for the network connection. + /// + /// * `path`: connection D-Bus path. + /// * `conn`: network connection. + async fn update_ipv4_settings( + &self, + path: &OwnedObjectPath, + conn: &NetworkConnection, ) -> Result<(), ServiceError> { let proxy = IPv4Proxy::builder(&self.connection) .path(path)? @@ -192,14 +227,6 @@ impl<'a> NetworkClient<'a> { let gateway = conn.gateway.as_deref().unwrap_or(""); proxy.set_gateway(gateway).await?; - if let Some(ref wireless) = conn.wireless { - self.update_wireless_settings(path, wireless).await?; - } - - if let Some(ref match_settings) = conn.match_settings { - self.update_match_settings(path, match_settings).await?; - } - Ok(()) } From c030a9406ff8eb5c0c33188071f43f091211e146 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Thu, 7 Sep 2023 12:32:59 +0100 Subject: [PATCH 05/12] WIP: remove interface-name if empty --- rust/agama-dbus-server/src/network/nm/dbus.rs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/rust/agama-dbus-server/src/network/nm/dbus.rs b/rust/agama-dbus-server/src/network/nm/dbus.rs index 7a18e01641..e2c91f7ea7 100644 --- a/rust/agama-dbus-server/src/network/nm/dbus.rs +++ b/rust/agama-dbus-server/src/network/nm/dbus.rs @@ -22,8 +22,11 @@ const LOOPBACK_KEY: &str = "loopback"; /// * `conn`: Connection to convert. pub fn connection_to_dbus(conn: &Connection) -> NestedHash { let mut result = NestedHash::new(); - let mut connection_dbus = - HashMap::from([("id", conn.id().into()), ("type", ETHERNET_KEY.into())]); + let mut connection_dbus = HashMap::from([ + ("id", conn.id().into()), + ("type", ETHERNET_KEY.into()), + ("interface-name", conn.interface().into()), + ]); result.insert("ipv4", ipv4_to_dbus(conn.ipv4())); result.insert("match", match_config_to_dbus(conn.match_config())); @@ -91,6 +94,14 @@ pub fn merge_dbus_connections<'a>( merged } +fn is_empty_value(value: &zvariant::Value) -> bool { + let value: Result = value.try_into(); + if let Ok(v) = value { + return v.is_empty(); + } + false +} + /// Cleans up the NestedHash that represents a connection. /// /// By now it just removes the "addresses" key from the "ipv4" and "ipv6" objects, which is @@ -98,6 +109,15 @@ pub fn merge_dbus_connections<'a>( /// /// * `conn`: connection represented as a NestedHash. fn cleanup_dbus_connection(conn: &mut NestedHash) { + if let Some(connection) = conn.get_mut("connection") { + if connection + .get("interface-name") + .is_some_and(|v| is_empty_value(&v)) + { + connection.remove("interface-name"); + } + } + if let Some(ipv4) = conn.get_mut("ipv4") { ipv4.remove("addresses"); ipv4.remove("dns"); From 53acd3861845118605dba8d7903786ed2b7f62fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 7 Sep 2023 13:05:20 +0100 Subject: [PATCH 06/12] Test whether 'interface-name' is dropped if needed --- rust/agama-dbus-server/src/network/nm/dbus.rs | 54 +++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/rust/agama-dbus-server/src/network/nm/dbus.rs b/rust/agama-dbus-server/src/network/nm/dbus.rs index e2c91f7ea7..f4a46fc258 100644 --- a/rust/agama-dbus-server/src/network/nm/dbus.rs +++ b/rust/agama-dbus-server/src/network/nm/dbus.rs @@ -94,14 +94,6 @@ pub fn merge_dbus_connections<'a>( merged } -fn is_empty_value(value: &zvariant::Value) -> bool { - let value: Result = value.try_into(); - if let Ok(v) = value { - return v.is_empty(); - } - false -} - /// Cleans up the NestedHash that represents a connection. /// /// By now it just removes the "addresses" key from the "ipv4" and "ipv6" objects, which is @@ -356,6 +348,19 @@ fn wireless_config_from_dbus(conn: &OwnedNestedHash) -> Option { Some(wireless_config) } +/// Determines whether a value is empty. +/// +/// TODO: Generalize for other kind of values, like dicts or arrays. +/// +/// * `value`: value to analyze +fn is_empty_value(value: &zvariant::Value) -> bool { + if let Some(value) = value.downcast_ref::() { + return value.is_empty(); + } + + false +} + #[cfg(test)] mod test { use super::{ @@ -410,7 +415,7 @@ mod test { assert_eq!(connection.id(), "eth0"); let ipv4 = connection.ipv4(); - let match_config = connection.match_config().clone(); + let match_config = connection.match_config(); assert_eq!(match_config.kernel, vec!["pci-0000:00:19.0"]); assert_eq!(ipv4.addresses, vec!["192.168.0.10/24".parse().unwrap()]); assert_eq!(ipv4.nameservers, vec![Ipv4Addr::new(192, 168, 0, 2)]); @@ -527,6 +532,7 @@ mod test { let base = BaseConnection { id: "agama".to_string(), + interface: "eth0".to_string(), ..Default::default() }; let ethernet = EthernetConnection { @@ -543,6 +549,11 @@ mod test { Value::new("agama".to_string()) ); + assert_eq!( + *connection.get("interface-name").unwrap(), + Value::new("eth0".to_string()) + ); + let ipv4 = merged.get("ipv4").unwrap(); assert_eq!( *ipv4.get("method").unwrap(), @@ -555,6 +566,31 @@ mod test { assert!(ipv4.get("addresses").is_none()); } + #[test] + fn test_merged_connections_are_clean() { + let mut original = OwnedNestedHash::new(); + let connection = HashMap::from([ + ("id".to_string(), Value::new("conn0".to_string()).to_owned()), + ( + "type".to_string(), + Value::new(ETHERNET_KEY.to_string()).to_owned(), + ), + ( + "interface-name".to_string(), + Value::new("eth0".to_string()).to_owned(), + ), + ]); + original.insert("connection".to_string(), connection); + + let mut updated = Connection::Ethernet(EthernetConnection::default()); + updated.set_interface(""); + let updated = connection_to_dbus(&updated); + + let merged = merge_dbus_connections(&original, &updated); + let connection = merged.get("connection").unwrap(); + assert_eq!(connection.get("interface-name"), None); + } + fn build_ethernet_section_from_dbus() -> HashMap { HashMap::from([("auto-negotiate".to_string(), true.into())]) } From 07a98cdc30304043a0ce959f8d6d16264a8df5e9 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Mon, 11 Sep 2023 21:27:23 +0100 Subject: [PATCH 07/12] Extend the schema supporting match settings --- rust/agama-lib/share/examples/profile.json | 1 + rust/agama-lib/share/profile.schema.json | 43 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/rust/agama-lib/share/examples/profile.json b/rust/agama-lib/share/examples/profile.json index 12dad32cde..bc379d9153 100644 --- a/rust/agama-lib/share/examples/profile.json +++ b/rust/agama-lib/share/examples/profile.json @@ -27,6 +27,7 @@ { "id": "Ethernet network device 1", "method": "manual", + "interface": "eth0", "addresses": [ "192.168.122.100/24" ], diff --git a/rust/agama-lib/share/profile.schema.json b/rust/agama-lib/share/profile.schema.json index e49ea4e43f..842dd0f13b 100644 --- a/rust/agama-lib/share/profile.schema.json +++ b/rust/agama-lib/share/profile.schema.json @@ -32,6 +32,10 @@ "description": "Connection ID", "type": "string" }, + "interface": { + "description": "The name of the network interface bound to this connection", + "type": "string" + }, "method": { "description": "IPv4 configuration method (e.g., 'auto')", "type": "string", @@ -86,6 +90,45 @@ ] } } + }, + "match": { + "type": "object", + "description": "Match settings", + "additionalProperties": false, + "properties": { + "kernel": { + "type": "array", + "items": { + "description": "A list of kernel command line arguments to match", + "type": "string", + "additionalProperties": false + } + }, + "interface": { + "type": "array", + "items": { + "description": "A list of interface names to match", + "type": "string", + "additionalProperties": false + } + }, + "driver": { + "type": "array", + "items": { + "description": "A list of driver names to match", + "type": "string", + "additionalProperties": false + } + }, + "path": { + "type": "array", + "items": { + "description": "A list of paths to match against the ID_PATH udev property of devices", + "type": "string", + "additionalProperties": false + } + } + } } }, "required": [ From 20763292dfaa8cc9d6c3a71f2bdb0199a95167d9 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Tue, 12 Sep 2023 07:07:47 +0100 Subject: [PATCH 08/12] Use path in singular for unification --- rust/agama-lib/src/network/client.rs | 4 ++-- rust/agama-lib/src/network/settings.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/agama-lib/src/network/client.rs b/rust/agama-lib/src/network/client.rs index c196a1676a..61be850280 100644 --- a/rust/agama-lib/src/network/client.rs +++ b/rust/agama-lib/src/network/client.rs @@ -120,7 +120,7 @@ impl<'a> NetworkClient<'a> { .build() .await?; let match_settings = MatchSettings { - paths: match_proxy.path().await?, + path: match_proxy.path().await?, kernel: match_proxy.kernel().await?, interface: match_proxy.interface().await?, driver: match_proxy.driver().await?, @@ -267,7 +267,7 @@ impl<'a> NetworkClient<'a> { .build() .await?; - let paths: Vec<_> = match_settings.paths.iter().map(String::as_ref).collect(); + let paths: Vec<_> = match_settings.path.iter().map(String::as_ref).collect(); proxy.set_path(paths.as_slice()).await?; Ok(()) diff --git a/rust/agama-lib/src/network/settings.rs b/rust/agama-lib/src/network/settings.rs index c3c1ce5f7e..0ff53aa7cb 100644 --- a/rust/agama-lib/src/network/settings.rs +++ b/rust/agama-lib/src/network/settings.rs @@ -21,7 +21,7 @@ pub struct MatchSettings { #[serde(skip_serializing_if = "Vec::is_empty", default)] pub driver: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub paths: Vec, + pub path: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] pub kernel: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] @@ -30,7 +30,7 @@ pub struct MatchSettings { impl MatchSettings { pub fn is_empty(&self) -> bool { - self.paths.is_empty() + self.path.is_empty() && self.driver.is_empty() && self.kernel.is_empty() && self.interface.is_empty() From 23f6085e8f2af2285a25d4fcdbf8a6296390b587 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Tue, 12 Sep 2023 07:11:53 +0100 Subject: [PATCH 09/12] Added a match settings example --- rust/agama-lib/share/examples/profile.jsonnet | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rust/agama-lib/share/examples/profile.jsonnet b/rust/agama-lib/share/examples/profile.jsonnet index 96095a0697..b893150f27 100644 --- a/rust/agama-lib/share/examples/profile.jsonnet +++ b/rust/agama-lib/share/examples/profile.jsonnet @@ -63,7 +63,10 @@ local findBiggestDisk(disks) = ], nameservers: [ '1.2.3.4' - ] + ], + match: { + path: ["pci-0000:00:19.0"] + } } ] } From 5d7ad051cdbfef2a16f4c1ed97200d1ef2dd1d13 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Tue, 12 Sep 2023 07:14:35 +0100 Subject: [PATCH 10/12] Changes based on CR --- rust/agama-dbus-server/src/network/nm/dbus.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rust/agama-dbus-server/src/network/nm/dbus.rs b/rust/agama-dbus-server/src/network/nm/dbus.rs index f4a46fc258..cfebabc236 100644 --- a/rust/agama-dbus-server/src/network/nm/dbus.rs +++ b/rust/agama-dbus-server/src/network/nm/dbus.rs @@ -248,9 +248,7 @@ fn base_connection_from_dbus(conn: &OwnedNestedHash) -> Option { fn match_config_from_dbus( match_config: &HashMap, ) -> Option { - let mut match_conf = MatchConfig { - ..Default::default() - }; + let mut match_conf = MatchConfig::default(); if let Some(drivers) = match_config.get("driver") { let drivers = drivers.downcast_ref::()?; From 3de5288026eddda8a6cda344fceb0fea5d46a850 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Wed, 13 Sep 2023 10:29:16 +0100 Subject: [PATCH 11/12] Added changelog --- rust/package/agama-cli.changes | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/package/agama-cli.changes b/rust/package/agama-cli.changes index cde2101d02..9999eb7733 100644 --- a/rust/package/agama-cli.changes +++ b/rust/package/agama-cli.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Sep 13 09:27:22 UTC 2023 - Knut Anderssen + +- Allow to bind a connection to an specific interface through its + name or through a set of match settings (gh#opensSUSE/agama#723). + ------------------------------------------------------------------- Thu Aug 31 10:30:28 UTC 2023 - Imobach Gonzalez Sosa From c3f1bd9ee0de8663149024ae01169ccb053330cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Wed, 13 Sep 2023 11:59:45 +0100 Subject: [PATCH 12/12] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Imobach González Sosa --- rust/agama-dbus-server/src/network/nm/dbus.rs | 43 ++++++++----------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/rust/agama-dbus-server/src/network/nm/dbus.rs b/rust/agama-dbus-server/src/network/nm/dbus.rs index cfebabc236..9e513c0898 100644 --- a/rust/agama-dbus-server/src/network/nm/dbus.rs +++ b/rust/agama-dbus-server/src/network/nm/dbus.rs @@ -177,42 +177,35 @@ fn wireless_config_to_dbus(conn: &WirelessConnection) -> NestedHash { /// /// * `match_config`: MatchConfig to convert. fn match_config_to_dbus(match_config: &MatchConfig) -> HashMap<&str, zvariant::Value> { - let mut match_config_dbus = HashMap::new(); - let drivers: Value = match_config .driver .iter() - .map(|dr| dr.to_string()) - .collect::>() + .cloned() + .collect::>() .into(); - match_config_dbus.insert("driver", drivers); let kernels: Value = match_config .kernel .iter() - .map(|dr| dr.to_string()) - .collect::>() + .cloned() + .collect::>() .into(); - match_config_dbus.insert("kernel-command-line", kernels); - let paths: Value = match_config - .path - .iter() - .map(|dr| dr.to_string()) - .collect::>() - .into(); - match_config_dbus.insert("path", paths); + let paths: Value = match_config.path.iter().cloned().collect::>().into(); let interfaces: Value = match_config .interface .iter() - .map(|dr| dr.to_string()) - .collect::>() + .cloned() + .collect::>() .into(); - match_config_dbus.insert("interface-name", interfaces); - - match_config_dbus + HashMap::from([ + ("driver", drivers), + ("kernel-command-line", kernels), + ("path", paths), + ("interface-name", interfaces), + ]) } fn base_connection_from_dbus(conn: &OwnedNestedHash) -> Option { @@ -231,7 +224,7 @@ fn base_connection_from_dbus(conn: &OwnedNestedHash) -> Option { if let Some(interface) = connection.get("interface-name") { let interface: &str = interface.downcast_ref()?; - base_connection.interface = interface.parse().unwrap(); + base_connection.interface = interface.to_string(); } if let Some(match_config) = conn.get("match") { @@ -254,7 +247,7 @@ fn match_config_from_dbus( let drivers = drivers.downcast_ref::()?; for driver in drivers.get() { let driver: &str = driver.downcast_ref()?; - match_conf.driver.push(driver.parse().unwrap()); + match_conf.driver.push(driver.to_string()); } } @@ -262,7 +255,7 @@ fn match_config_from_dbus( let interface_names = interface_names.downcast_ref::()?; for name in interface_names.get() { let name: &str = name.downcast_ref()?; - match_conf.interface.push(name.parse().unwrap()); + match_conf.interface.push(name.to_string()); } } @@ -270,7 +263,7 @@ fn match_config_from_dbus( let paths = paths.downcast_ref::()?; for path in paths.get() { let path: &str = path.downcast_ref()?; - match_conf.path.push(path.parse().unwrap()); + match_conf.path.push(path.to_string()); } } @@ -278,7 +271,7 @@ fn match_config_from_dbus( let options = kernel_options.downcast_ref::()?; for option in options.get() { let option: &str = option.downcast_ref()?; - match_conf.kernel.push(option.parse().unwrap()); + match_conf.kernel.push(option.to_string()); } }