diff --git a/rust/agama-lib/share/examples/profile.json b/rust/agama-lib/share/examples/profile.json index d4bfd4ff8e..82ec4cb0db 100644 --- a/rust/agama-lib/share/examples/profile.json +++ b/rust/agama-lib/share/examples/profile.json @@ -21,5 +21,20 @@ "root": { "password": "nots3cr3t", "sshKey": "..." + }, + "network": { + "connections": [ + { + "name": "Ethernet network device 1", + "method": "manual", + "addresses": [ + "192.168.122.100/24" + ], + "gateway": "192.168.122.1", + "nameservers": [ + "192.168.122.1" + ] + } + ] } } diff --git a/rust/agama-lib/share/examples/profile.jsonnet b/rust/agama-lib/share/examples/profile.jsonnet index 28b6a5262b..b862c4435b 100644 --- a/rust/agama-lib/share/examples/profile.jsonnet +++ b/rust/agama-lib/share/examples/profile.jsonnet @@ -29,4 +29,27 @@ local findBiggestDisk(disks) = }, ], }, + network: { + connections: [ + { + name: 'AgamaNetwork', + wireless: { + password: 'agama.test', + security: 'wpa-psk', + ssid: 'AgamaNetwork' + } + }, + { + name: 'Etherned device 1', + method: 'manual', + gateway: '192.168.122.1', + addresses: [ + '192.168.122.100/24,' + ], + nameservers: [ + '1.2.3.4' + ] + ] + } + ] } diff --git a/rust/agama-lib/share/profile.schema.json b/rust/agama-lib/share/profile.schema.json index 9c76884f2d..b4ba50babc 100644 --- a/rust/agama-lib/share/profile.schema.json +++ b/rust/agama-lib/share/profile.schema.json @@ -16,6 +16,84 @@ } } }, + "network": { + "description": "Network settings", + "type": "object", + "properties": { + "connections": { + "description": "Network connections to be defined", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "Connection ID", + "type": "string" + }, + "method": { + "description": "IPv4 configuration method (e.g., 'auto')", + "type": "string", + "enum": [ + "auto", + "manual", + "link-local", + "disabled" + ] + }, + "gateway": { + "description": "Connection gateway address (e.g. 192.168.122.1)", + "type": "string" + }, + "addresses": { + "type": "array", + "items": { + "description": "Connection addresses", + "type": "string", + "additionalProperties": false + } + }, + "nameservers": { + "type": "array", + "items": { + "description": "Connection nameservers", + "type": "string", + "additionalProperties": false + } + }, + "wireless": { + "type": "object", + "description": "Wireless configuration", + "additionalProperties": false, + "properties": { + "password": { + "type": "string" + }, + "security": { + "type": "string" + }, + "ssid": { + "type": "string" + }, + "mode": { + "type": "string", + "enum": [ + "infrastructure", + "adhoc", + "mesh", + "ap" + ] + } + } + } + }, + "required": [ + "name" + ] + } + } + } + }, "user": { "description": "First user settings", "type": "object", diff --git a/rust/agama-lib/src/install_settings.rs b/rust/agama-lib/src/install_settings.rs index 3bc16347ce..c343e28a38 100644 --- a/rust/agama-lib/src/install_settings.rs +++ b/rust/agama-lib/src/install_settings.rs @@ -1,6 +1,7 @@ //! Configuration settings handling //! //! This module implements the mechanisms to load and store the installation settings. +use crate::network::NetworkSettings; use crate::settings::{SettingObject, SettingValue, Settings}; use agama_derive::Settings; use serde::{Deserialize, Serialize}; @@ -20,14 +21,21 @@ pub enum Scope { Software, /// Storage settings Storage, + /// Network settings + Network, } impl Scope { /// Returns known scopes /// // TODO: we can rely on strum so we do not forget to add them - pub fn all() -> [Scope; 3] { - [Scope::Software, Scope::Storage, Scope::Users] + pub fn all() -> [Scope; 4] { + [ + Scope::Network, + Scope::Software, + Scope::Storage, + Scope::Users, + ] } } @@ -39,6 +47,7 @@ impl FromStr for Scope { "users" => Ok(Self::Users), "software" => Ok(Self::Software), "storage" => Ok(Self::Storage), + "network" => Ok(Self::Network), _ => Err("Unknown section"), } } @@ -57,6 +66,8 @@ pub struct InstallSettings { pub software: Option, #[serde(default)] pub storage: Option, + #[serde(default)] + pub network: Option, } impl InstallSettings { @@ -73,6 +84,9 @@ impl InstallSettings { if self.software.is_some() { scopes.push(Scope::Software); } + if self.network.is_some() { + scopes.push(Scope::Network); + } scopes } } @@ -81,6 +95,10 @@ impl Settings for InstallSettings { fn add(&mut self, attr: &str, value: SettingObject) -> Result<(), &'static str> { if let Some((ns, id)) = attr.split_once('.') { match ns { + "network" => { + let network = self.network.get_or_insert(Default::default()); + network.add(id, value)? + } "software" => { let software = self.software.get_or_insert(Default::default()); software.add(id, value)? @@ -102,6 +120,10 @@ impl Settings for InstallSettings { fn set(&mut self, attr: &str, value: SettingValue) -> Result<(), &'static str> { if let Some((ns, id)) = attr.split_once('.') { match ns { + "network" => { + let network = self.network.get_or_insert(Default::default()); + network.set(id, value)? + } "software" => { let software = self.software.get_or_insert(Default::default()); software.set(id, value)? @@ -127,6 +149,11 @@ impl Settings for InstallSettings { } fn merge(&mut self, other: &Self) { + if let Some(other_network) = &other.network { + let network = self.network.get_or_insert(Default::default()); + network.merge(other_network); + } + if let Some(other_software) = &other.software { let software = self.software.get_or_insert(Default::default()); software.merge(other_software); diff --git a/rust/agama-lib/src/lib.rs b/rust/agama-lib/src/lib.rs index 8240beba0e..4ae2bd00d2 100644 --- a/rust/agama-lib/src/lib.rs +++ b/rust/agama-lib/src/lib.rs @@ -1,6 +1,7 @@ pub mod error; pub mod install_settings; pub mod manager; +pub mod network; pub mod profile; pub mod settings; pub mod software; diff --git a/rust/agama-lib/src/network.rs b/rust/agama-lib/src/network.rs new file mode 100644 index 0000000000..523d7b820d --- /dev/null +++ b/rust/agama-lib/src/network.rs @@ -0,0 +1,9 @@ +mod client; +mod model; +mod proxies; +mod store; +pub mod types; + +pub use client::NetworkClient; +pub use model::NetworkSettings; +pub use store::NetworkStore; diff --git a/rust/agama-lib/src/network/client.rs b/rust/agama-lib/src/network/client.rs new file mode 100644 index 0000000000..f13e250c4f --- /dev/null +++ b/rust/agama-lib/src/network/client.rs @@ -0,0 +1,104 @@ +use super::model::{NetworkConnection, WirelessSettings}; +use super::types::SSID; +use crate::error::ServiceError; + +use super::proxies::ConnectionProxy; +use super::proxies::ConnectionsProxy; +use super::proxies::IPv4Proxy; +use super::proxies::WirelessProxy; +use zbus::Connection; + +/// D-BUS client for the network service +pub struct NetworkClient<'a> { + pub connection: Connection, + connections_proxy: ConnectionsProxy<'a>, +} + +impl<'a> NetworkClient<'a> { + pub async fn new(connection: Connection) -> Result, ServiceError> { + Ok(Self { + connections_proxy: ConnectionsProxy::new(&connection).await?, + connection, + }) + } + + /// Returns an array of network connections + pub async fn connections(&self) -> Result, ServiceError> { + let connection_paths = self.connections_proxy.get_connections().await?; + let mut connections = vec![]; + + for path in connection_paths { + let mut connection = self.connection_from(path.as_str()).await?; + + if let Ok(wireless) = self.wireless_from(path.as_str()).await { + connection.wireless = Some(wireless); + } + + connections.push(connection); + } + + Ok(connections) + } + + /// Returns the NetworkConnection for the given connection path + /// + /// * `path`: the connections path to get the config from + async fn connection_from(&self, path: &str) -> Result { + let connection_proxy = ConnectionProxy::builder(&self.connection) + .path(path)? + .build() + .await?; + let name = connection_proxy.id().await?; + + let ipv4_proxy = IPv4Proxy::builder(&self.connection) + .path(path)? + .build() + .await?; + + /// TODO: consider using the `IPMethod` struct from `agama-network`. + let method = match ipv4_proxy.method().await? { + 0 => "auto", + 1 => "manual", + 2 => "link-local", + 3 => "disable", + _ => "auto", + }; + let gateway = match ipv4_proxy.gateway().await?.as_str() { + "" => None, + value => Some(value.to_string()), + }; + let nameservers = ipv4_proxy.nameservers().await?; + let addresses = ipv4_proxy.addresses().await?; + let addresses = addresses + .into_iter() + .map(|(ip, prefix)| format!("{ip}/{prefix}")) + .collect(); + + Ok(NetworkConnection { + name, + method: method.to_string(), + gateway, + addresses, + nameservers, + ..Default::default() + }) + } + + /// Returns the [wireless settings][WirelessSettings] for the given connection + /// + /// * `path`: the connections path to get the wireless config from + async fn wireless_from(&self, path: &str) -> Result { + let wireless_proxy = WirelessProxy::builder(&self.connection) + .path(path)? + .build() + .await?; + let wireless = WirelessSettings { + mode: wireless_proxy.mode().await?, + password: wireless_proxy.password().await?, + security: wireless_proxy.security().await?, + ssid: SSID(wireless_proxy.ssid().await?).to_string(), + }; + + Ok(wireless) + } +} diff --git a/rust/agama-lib/src/network/model.rs b/rust/agama-lib/src/network/model.rs new file mode 100644 index 0000000000..c98317ec6a --- /dev/null +++ b/rust/agama-lib/src/network/model.rs @@ -0,0 +1,54 @@ +//! Configuration network settings handling +//! +//! This module implements the mechanisms to load and store the installation settings. +use crate::settings::{SettingObject, Settings}; +use agama_derive::Settings; +use serde::{Deserialize, Serialize}; +use std::convert::TryFrom; +use std::default::Default; + +/// Network settings for installation +#[derive(Debug, Default, Settings, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NetworkSettings { + /// Connections to use in the installation + #[collection_setting] + pub connections: Vec, +} + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct WirelessSettings { + #[serde(skip_serializing_if = "String::is_empty")] + pub password: String, + pub security: String, + pub ssid: String, + pub mode: String, +} + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct NetworkConnection { + pub name: String, + pub method: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub gateway: Option, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub addresses: Vec, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub nameservers: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub wireless: Option, +} + +impl TryFrom for NetworkConnection { + type Error = &'static str; + + fn try_from(value: SettingObject) -> Result { + match value.0.get("name") { + Some(name) => Ok(NetworkConnection { + name: name.clone().try_into()?, + ..Default::default() + }), + None => Err("'name' key not found"), + } + } +} diff --git a/rust/agama-lib/src/network/proxies.rs b/rust/agama-lib/src/network/proxies.rs new file mode 100644 index 0000000000..1f975393a7 --- /dev/null +++ b/rust/agama-lib/src/network/proxies.rs @@ -0,0 +1,104 @@ +//! D-Bus interface proxies for: `org.opensuse.Agama*.**.*` +//! +//! This code was generated by `zbus-xmlgen` `3.1.0` from DBus introspection data.`. +use zbus::dbus_proxy; + +#[dbus_proxy( + interface = "org.opensuse.Agama.Network1.Connections", + default_service = "org.opensuse.Agama.Network1", + default_path = "/org/opensuse/Agama/Network1/connections" +)] +trait Connections { + /// Add a new network connection. + /// + /// `name`: connection name. + /// `ty`: connection type. + fn add_connection(&self, name: &str, ty: u8) -> zbus::Result<()>; + + /// Apply method + fn apply(&self) -> zbus::Result<()>; + + /// GetConnections method + fn get_connections(&self) -> zbus::Result>; + + /// RemoveConnection method + fn remove_connection(&self, uuid: &str) -> zbus::Result<()>; +} + +#[dbus_proxy( + interface = "org.opensuse.Agama.Network1.Connection.Wireless", + default_service = "org.opensuse.Agama.Network1", + default_path = "/org/opensuse/Agama/Network1" +)] +trait Wireless { + /// Returns the operating mode of the Wireless device + /// + /// Possible values are 'unknown', 'adhoc', 'infrastructure', 'ap' or 'mesh' + #[dbus_proxy(property)] + fn mode(&self) -> zbus::Result; + fn set_mode(&self, value: String) -> zbus::Result<()>; + + /// Password property + #[dbus_proxy(property)] + fn password(&self) -> zbus::Result; + fn set_password(&self, value: &str) -> zbus::Result<()>; + + /// SSID property + #[dbus_proxy(property, name = "SSID")] + fn ssid(&self) -> zbus::Result>; + fn set_ssid(&self, value: Vec) -> zbus::Result<()>; + + /// Wireless Security property + /// + /// Possible values are 'none', 'owe', 'ieee8021x', 'wpa-psk', 'sae', + /// 'wpa-eap', 'wpa-eap-suite-b192' + #[dbus_proxy(property)] + fn security(&self) -> zbus::Result; + fn set_security(&self, value: String) -> zbus::Result<()>; +} + +#[dbus_proxy( + interface = "org.opensuse.Agama.Network1.Connection", + default_service = "org.opensuse.Agama.Network1", + default_path = "/org/opensuse/Agama/Network1" +)] +trait Connection { + /// Id property + #[dbus_proxy(property)] + fn id(&self) -> zbus::Result; + + /// UUID property + #[dbus_proxy(property, name = "UUID")] + fn uuid(&self) -> zbus::Result; +} + +#[dbus_proxy( + interface = "org.opensuse.Agama.Network1.Connection.IPv4", + default_service = "org.opensuse.Agama.Network1", + default_path = "/org/opensuse/Agama/Network1" +)] +trait IPv4 { + /// Addresses property + /// + /// By now just an array of IPv4 addresses in string format + #[dbus_proxy(property)] + fn addresses(&self) -> zbus::Result>; + fn set_addresses(&self, value: &[(&str, u32)]) -> zbus::Result<()>; + + /// Gateway property + #[dbus_proxy(property)] + fn gateway(&self) -> zbus::Result; + fn set_gateway(&self, value: &str) -> zbus::Result<()>; + + /// Method property + #[dbus_proxy(property)] + fn method(&self) -> zbus::Result; + fn set_method(&self, value: u8) -> zbus::Result<()>; + + /// Nameservers property + /// + /// By now just an array of IPv4 addresses in string format + #[dbus_proxy(property)] + fn nameservers(&self) -> zbus::Result>; + fn set_nameservers(&self, value: &[&str]) -> zbus::Result<()>; +} diff --git a/rust/agama-lib/src/network/store.rs b/rust/agama-lib/src/network/store.rs new file mode 100644 index 0000000000..6853976a25 --- /dev/null +++ b/rust/agama-lib/src/network/store.rs @@ -0,0 +1,28 @@ +use crate::error::ServiceError; +use crate::network::{NetworkClient, NetworkSettings}; +use std::error::Error; +use zbus::Connection; + +/// Loads and stores the network settings from/to the D-Bus service. +pub struct NetworkStore<'a> { + network_client: NetworkClient<'a>, +} + +impl<'a> NetworkStore<'a> { + pub async fn new(connection: Connection) -> Result, ServiceError> { + Ok(Self { + network_client: NetworkClient::new(connection).await?, + }) + } + + // TODO: read the settings from the service + pub async fn load(&self) -> Result> { + let connections = self.network_client.connections().await?; + + Ok(NetworkSettings { connections }) + } + + pub async fn store(&self, settings: &NetworkSettings) -> Result<(), Box> { + Ok(()) + } +} diff --git a/rust/agama-lib/src/network/types.rs b/rust/agama-lib/src/network/types.rs new file mode 100644 index 0000000000..537427838b --- /dev/null +++ b/rust/agama-lib/src/network/types.rs @@ -0,0 +1,24 @@ +use std::{fmt, str}; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)] +pub struct SSID(pub Vec); + +impl SSID { + pub fn to_vec(&self) -> &Vec { + &self.0 + } +} + +impl fmt::Display for SSID { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", str::from_utf8(&self.0).unwrap()) + } +} + +impl From for Vec { + fn from(value: SSID) -> Self { + value.0.clone() + } +} diff --git a/rust/agama-lib/src/store.rs b/rust/agama-lib/src/store.rs index 31d8867e86..04a69fc9bf 100644 --- a/rust/agama-lib/src/store.rs +++ b/rust/agama-lib/src/store.rs @@ -4,6 +4,7 @@ mod users; use crate::error::ServiceError; use crate::install_settings::{InstallSettings, Scope}; +use crate::network::NetworkStore; use crate::store::software::SoftwareStore; use crate::store::storage::StorageStore; use crate::store::users::UsersStore; @@ -15,6 +16,7 @@ use zbus::Connection; /// This struct uses the default connection built by [connection function](super::connection). pub struct Store<'a> { users: UsersStore<'a>, + network: NetworkStore<'a>, software: SoftwareStore<'a>, storage: StorageStore<'a>, } @@ -23,6 +25,7 @@ impl<'a> Store<'a> { pub async fn new(connection: Connection) -> Result, ServiceError> { Ok(Self { users: UsersStore::new(connection.clone()).await?, + network: NetworkStore::new(connection.clone()).await?, software: SoftwareStore::new(connection.clone()).await?, storage: StorageStore::new(connection).await?, }) @@ -36,6 +39,9 @@ impl<'a> Store<'a> { }; let mut settings: InstallSettings = Default::default(); + if scopes.contains(&Scope::Network) { + settings.network = Some(self.network.load().await?); + } if scopes.contains(&Scope::Storage) { settings.storage = Some(self.storage.load().await?); } @@ -54,6 +60,9 @@ impl<'a> Store<'a> { /// Stores the given installation settings in the D-Bus service pub async fn store(&self, settings: &InstallSettings) -> Result<(), Box> { + if let Some(network) = &settings.network { + self.network.store(network).await?; + } if let Some(software) = &settings.software { self.software.store(software).await?; } diff --git a/rust/agama-network/src/dbus/interfaces.rs b/rust/agama-network/src/dbus/interfaces.rs index 45de5f4146..a209c5ea61 100644 --- a/rust/agama-network/src/dbus/interfaces.rs +++ b/rust/agama-network/src/dbus/interfaces.rs @@ -8,6 +8,8 @@ use crate::{ error::NetworkStateError, model::{Connection as NetworkConnection, Device as NetworkDevice, WirelessConnection}, }; + +use agama_lib::network::types::SSID; use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; use std::{ net::{AddrParseError, Ipv4Addr}, @@ -344,24 +346,24 @@ impl Wireless { #[dbus_interface(property, name = "SSID")] pub fn ssid(&self) -> Vec { let connection = self.get_wireless(); - connection.wireless.ssid.clone() + connection.wireless.ssid.clone().into() } #[dbus_interface(property, name = "SSID")] pub fn set_ssid(&mut self, ssid: Vec) -> zbus::fdo::Result<()> { let mut connection = self.get_wireless(); - connection.wireless.ssid = ssid; + connection.wireless.ssid = SSID(ssid); self.update_connection(connection) } #[dbus_interface(property)] - pub fn mode(&self) -> u8 { + pub fn mode(&self) -> String { let connection = self.get_wireless(); - connection.wireless.mode as u8 + connection.wireless.mode.to_string() } #[dbus_interface(property)] - pub fn set_mode(&mut self, mode: u8) -> zbus::fdo::Result<()> { + pub fn set_mode(&mut self, mode: &str) -> zbus::fdo::Result<()> { let mut connection = self.get_wireless(); connection.wireless.mode = mode.try_into()?; self.update_connection(connection) @@ -389,9 +391,9 @@ impl Wireless { } #[dbus_interface(property)] - pub fn security(&self) -> u8 { + pub fn security(&self) -> String { let connection = self.get_wireless(); - connection.wireless.security as u8 + connection.wireless.security.to_string() } #[dbus_interface(property)] diff --git a/rust/agama-network/src/error.rs b/rust/agama-network/src/error.rs index baa173fba0..c20dd0918d 100644 --- a/rust/agama-network/src/error.rs +++ b/rust/agama-network/src/error.rs @@ -15,7 +15,7 @@ pub enum NetworkStateError { #[error("Invalid IP method: '{0}'")] InvalidIpMethod(u8), #[error("Invalid wireless mode: '{0}'")] - InvalidWirelessMode(u8), + InvalidWirelessMode(String), #[error("Connection '{0}' already exists")] ConnectionExists(Uuid), #[error("Invalid device type: '{0}'")] diff --git a/rust/agama-network/src/model.rs b/rust/agama-network/src/model.rs index 1c124193ce..ecc1f53aa2 100644 --- a/rust/agama-network/src/model.rs +++ b/rust/agama-network/src/model.rs @@ -5,7 +5,8 @@ use uuid::Uuid; use crate::error::NetworkStateError; -use std::{fmt, net::Ipv4Addr}; +use agama_lib::network::types::SSID; +use std::{fmt, net::Ipv4Addr, str}; #[derive(Default)] pub struct NetworkState { @@ -354,30 +355,32 @@ pub struct LoopbackConnection { #[derive(Debug, Default, PartialEq, Clone)] pub struct WirelessConfig { pub mode: WirelessMode, - pub ssid: Vec, + pub ssid: SSID, pub password: Option, pub security: SecurityProtocol, } #[derive(Debug, Default, Clone, Copy, PartialEq)] pub enum WirelessMode { - #[default] - Infra = 0, + Unknown = 0, AdHoc = 1, - Mesh = 2, + #[default] + Infra = 2, AP = 3, + Mesh = 4, } -impl TryFrom for WirelessMode { +impl TryFrom<&str> for WirelessMode { type Error = NetworkStateError; - fn try_from(value: u8) -> Result { + fn try_from(value: &str) -> Result { match value { - 1 => Ok(WirelessMode::AdHoc), - 2 => Ok(WirelessMode::Infra), - 3 => Ok(WirelessMode::AP), - 4 => Ok(WirelessMode::Mesh), - _ => Err(NetworkStateError::InvalidWirelessMode(value)), + "unknown" => Ok(WirelessMode::Unknown), + "adhoc" => Ok(WirelessMode::AdHoc), + "infrastructure" => Ok(WirelessMode::Infra), + "ap" => Ok(WirelessMode::AP), + "mesh" => Ok(WirelessMode::Mesh), + _ => Err(NetworkStateError::InvalidWirelessMode(value.to_string())), } } } @@ -385,6 +388,7 @@ impl TryFrom for WirelessMode { impl fmt::Display for WirelessMode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let name = match &self { + WirelessMode::Unknown => "unknown", WirelessMode::AdHoc => "adhoc", WirelessMode::Infra => "infrastructure", WirelessMode::AP => "ap", diff --git a/rust/agama-network/src/nm/dbus.rs b/rust/agama-network/src/nm/dbus.rs index 79a3fbdf50..d398ee8a50 100644 --- a/rust/agama-network/src/nm/dbus.rs +++ b/rust/agama-network/src/nm/dbus.rs @@ -4,7 +4,10 @@ //! with nested hash maps (see [NestedHash] and [OwnedNestedHash]). use super::model::*; use crate::model::*; -use agama_lib::dbus::{NestedHash, OwnedNestedHash}; +use agama_lib::{ + dbus::{NestedHash, OwnedNestedHash}, + network::types::SSID, +}; use std::collections::HashMap; use std::net::Ipv4Addr; use uuid::Uuid; @@ -139,7 +142,7 @@ fn wireless_config_to_dbus(conn: &WirelessConnection) -> NestedHash { let config = &conn.wireless; let wireless: HashMap<&str, zvariant::Value> = HashMap::from([ ("mode", Value::new(config.mode.to_string())), - ("ssid", Value::new(&config.ssid)), + ("ssid", Value::new(config.ssid.to_vec())), ]); let mut security: HashMap<&str, zvariant::Value> = @@ -225,7 +228,7 @@ fn wireless_config_from_dbus(conn: &OwnedNestedHash) -> Option { .collect(); let mut wireless_config = WirelessConfig { mode: NmWirelessMode(mode.to_string()).try_into().ok()?, - ssid, + ssid: SSID(ssid), ..Default::default() }; @@ -244,6 +247,7 @@ mod test { OwnedNestedHash, }; use crate::{model::*, nm::dbus::ETHERNET_KEY}; + use agama_lib::network::types::SSID; use std::{collections::HashMap, net::Ipv4Addr}; use uuid::Uuid; use zbus::zvariant::{self, OwnedValue, Value}; @@ -324,7 +328,7 @@ mod test { let connection = connection_from_dbus(dbus_conn).unwrap(); assert!(matches!(connection, Connection::Wireless(_))); if let Connection::Wireless(connection) = connection { - assert_eq!(connection.wireless.ssid, vec![97, 103, 97, 109, 97]); + assert_eq!(connection.wireless.ssid, SSID(vec![97, 103, 97, 109, 97])); assert_eq!(connection.wireless.mode, WirelessMode::Infra); assert_eq!(connection.wireless.security, SecurityProtocol::WPA2) } @@ -335,7 +339,7 @@ mod test { let config = WirelessConfig { mode: WirelessMode::Infra, security: SecurityProtocol::WPA2, - ssid: "agama".as_bytes().into(), + ssid: SSID(vec![97, 103, 97, 109, 97]), ..Default::default() }; let wireless = WirelessConnection {