Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
9d6053e
fix(rust): filter duplicated device state changes
imobachgs Apr 1, 2025
939bb90
refactor(web): rename WifiNetworksListPage to WifiNetworksList
imobachgs Apr 2, 2025
78ccf81
feat(web): reorganize wireless configuration
imobachgs Apr 2, 2025
c57c740
feat(web): begin modeling new connection details page
dgdavid Apr 4, 2025
2d2a2d8
fix(web): use camelCase for network GeneralState
imobachgs Apr 3, 2025
55fac2d
refactor(web): break useNetwork into smaller hooks
imobachgs Apr 3, 2025
ed32d17
fix(web): avoid fetching all network devices on each change
imobachgs Apr 3, 2025
8875de0
fix(rust): expose the password for wpa-psk
imobachgs Apr 4, 2025
ef12ab6
fix(rust): fix network state test
imobachgs Apr 4, 2025
7cf50f0
fix(web): drop the feature to connect to a hidden network
imobachgs Apr 4, 2025
badedfa
refactor(web): drop unused code related to Wi-Fi selection
imobachgs Apr 4, 2025
61a31d8
fix(rust): do not crash if cannot read secrets
imobachgs Apr 4, 2025
da542ed
fix(web): fix and rename useNetworkConfigChanges
imobachgs Apr 7, 2025
bc2136e
fix(web): display an error when cannot add/update a connection
imobachgs Apr 7, 2025
aedb70d
web: simplify list of visible Wi-Fi networks
dgdavid Apr 7, 2025
b7a117a
web: minor adjustments for connection details
dgdavid Apr 7, 2025
14ca2ec
fix(web): fix error when creating a connection
imobachgs Apr 7, 2025
2c13172
feat(rust): simplify network DeviceState values
imobachgs Apr 7, 2025
007294b
refactor(web): adapt network types to the new DeviceState
imobachgs Apr 7, 2025
06687d7
fix(web): correctly detect non-empty arrays
dgdavid Apr 7, 2025
62bfdf3
web: Wi-Fi network list layout improvements
dgdavid Apr 7, 2025
5485267
web: improve Wi-Fi networks interface
dgdavid Apr 8, 2025
90666ab
web: add new route and components for wired connections
dgdavid Apr 8, 2025
bcf1be1
web: adapt network page
dgdavid Apr 8, 2025
4985d2e
fix(web): render IP settings form in a single column
dgdavid Apr 8, 2025
7acb5ae
web: change hover color for clickable data list items
dgdavid Apr 8, 2025
b769cdd
fix(web): add missing component
dgdavid Apr 8, 2025
3e1e5e9
fix(web): adapt DeviceState to the new values
imobachgs Apr 8, 2025
280ae20
fix(web): drop the unused ConnectionState enum
imobachgs Apr 8, 2025
675e42e
fix(web): drop an unused mock
imobachgs Apr 8, 2025
eba6f5c
fix(Web): fix NetworkPage test
imobachgs Apr 8, 2025
e923262
fix(web): web the unused WifiSelectorPage component
imobachgs Apr 8, 2025
10425be
fix(web): drop an unused plainRender import
imobachgs Apr 8, 2025
dcd3a77
fix(web): update cancel action on connection edit
dgdavid Apr 8, 2025
472c07c
fix(web): ignore aria-label errors in the WifiNetworksList tests
imobachgs Apr 9, 2025
fbba1c3
web: connection details adjustments
dgdavid Apr 9, 2025
be5492f
web: add empty state for Wi-Fi network not found
dgdavid Apr 9, 2025
416867f
web: use empty state for wired connection not found
dgdavid Apr 9, 2025
7f611a9
web: attemp to improve wifi connection form behavior
dgdavid Apr 9, 2025
4e6020d
fix(web): drop reference to not used variant
dgdavid Apr 10, 2025
224bfe7
fix(web): update WifiConnectionForm unit test
dgdavid Apr 10, 2025
229ba5d
Merge branch 'master' into better-network
imobachgs Apr 10, 2025
e54db5f
refactor(rust): move DevicesChangedStream to its own module
imobachgs Apr 10, 2025
0060f53
refactor(rust): add a common module for NM streams
imobachgs Apr 10, 2025
e2c90dc
feat(rust): expose the connection state
imobachgs Apr 10, 2025
5bb1c60
feat(web): add network connections state
imobachgs Apr 10, 2025
37c7682
fix(web): add missing property to network connection type
dgdavid Apr 11, 2025
70db700
fix(web): avoid crashing because malformed connection
dgdavid Apr 11, 2025
487ba67
web: make WifiConnectionForm less fragile
dgdavid Apr 11, 2025
3920799
web: fix broken unit test
dgdavid Apr 11, 2025
3c9d77a
fix(web): fix connection device detection
dgdavid Apr 15, 2025
5a42d49
fix(web): minor adjustments for Wi-Fi networks list
dgdavid Apr 15, 2025
f0ef57a
web: show spinner in Wi-Fi network when connecting
dgdavid Apr 15, 2025
9e0c003
feat(rust): do not write wireless security settings when not used
imobachgs Apr 16, 2025
cf1625e
fix(web): use "none" as security for public networks
dgdavid Apr 16, 2025
df6c7ce
fix(web): clarify signal strength in Wi-Fi connection details
dgdavid Apr 16, 2025
4dfe2f5
fix(rust): add ConnectionState to the OpenAPI spec
imobachgs Apr 18, 2025
3301cf1
Fix sorting of access points in the original query
teclator Apr 1, 2025
97c9459
Document the sorting of access points
teclator Apr 1, 2025
e2d318d
docs: update changes files
imobachgs Apr 21, 2025
0fe86f4
Merge branch 'master' into better-network
imobachgs Apr 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 69 additions & 60 deletions rust/agama-lib/src/network/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ use std::{
use thiserror::Error;
use zbus;

use super::settings::NetworkConnection;

/// Network device
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
Expand Down Expand Up @@ -78,71 +80,67 @@ pub enum DeviceType {
Bridge = 6,
}

// For now this mirrors NetworkManager, because it was less mental work than coming up with
// what exactly Agama needs. Expected to be adapted.
#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize, utoipa::ToSchema)]
/// Network device state.
#[derive(
Default,
Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Clone,
Copy,
strum::Display,
strum::EnumString,
utoipa::ToSchema,
)]
#[strum(serialize_all = "camelCase")]
#[serde(rename_all = "camelCase")]
pub enum DeviceState {
#[default]
Unknown = 0,
Unmanaged = 10,
Unavailable = 20,
Disconnected = 30,
Prepare = 40,
Config = 50,
NeedAuth = 60,
IpConfig = 70,
IpCheck = 80,
Secondaries = 90,
Activated = 100,
Deactivating = 110,
Failed = 120,
/// The device's state is unknown.
Unknown,
/// The device is recognized but not managed by Agama.
Unmanaged,
/// The device is detected but it cannot be used (wireless switched off, missing firmware, etc.).
Unavailable,
/// The device is connecting to the network.
Connecting,
/// The device is successfully connected to the network.
Connected,
/// The device is disconnecting from the network.
Disconnecting,
/// The device is disconnected from the network.
Disconnected,
/// The device failed to connect to a network.
Failed,
}
#[derive(Debug, Error, PartialEq)]
#[error("Invalid state: {0}")]
pub struct InvalidDeviceState(String);

impl TryFrom<u8> for DeviceState {
type Error = InvalidDeviceState;

fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(DeviceState::Unknown),
10 => Ok(DeviceState::Unmanaged),
20 => Ok(DeviceState::Unavailable),
30 => Ok(DeviceState::Disconnected),
40 => Ok(DeviceState::Prepare),
50 => Ok(DeviceState::Config),
60 => Ok(DeviceState::NeedAuth),
70 => Ok(DeviceState::IpConfig),
80 => Ok(DeviceState::IpCheck),
90 => Ok(DeviceState::Secondaries),
100 => Ok(DeviceState::Activated),
110 => Ok(DeviceState::Deactivating),
120 => Ok(DeviceState::Failed),
_ => Err(InvalidDeviceState(value.to_string())),
}
}
}
impl fmt::Display for DeviceState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match &self {
DeviceState::Unknown => "unknown",
DeviceState::Unmanaged => "unmanaged",
DeviceState::Unavailable => "unavailable",
DeviceState::Disconnected => "disconnected",
DeviceState::Prepare => "prepare",
DeviceState::Config => "config",
DeviceState::NeedAuth => "need_auth",
DeviceState::IpConfig => "ip_config",
DeviceState::IpCheck => "ip_check",
DeviceState::Secondaries => "secondaries",
DeviceState::Activated => "activated",
DeviceState::Deactivating => "deactivating",
DeviceState::Failed => "failed",
};
write!(f, "{}", name)
}
#[derive(
Default,
Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Clone,
Copy,
strum::Display,
strum::EnumString,
utoipa::ToSchema,
)]
#[strum(serialize_all = "camelCase")]
#[serde(rename_all = "camelCase")]
pub enum ConnectionState {
/// The connection is getting activated.
Activating,
/// The connection is activated.
Activated,
/// The connection is getting deactivated.
Deactivating,
#[default]
/// The connection is deactivated.
Deactivated,
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize, utoipa::ToSchema)]
Expand Down Expand Up @@ -294,6 +292,17 @@ impl From<InvalidDeviceType> for zbus::fdo::Error {
}
}

// FIXME: found a better place for the HTTP types.
//
// TODO: If the client ignores the additional "state" field, this struct
// does not need to be here.
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct NetworkConnectionWithState {
#[serde(flatten)]
pub connection: NetworkConnection,
pub state: ConnectionState,
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
4 changes: 3 additions & 1 deletion rust/agama-server/src/network/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
// find current contact information at www.suse.com.

use crate::network::model::{AccessPoint, Connection, Device};
use agama_lib::network::types::DeviceType;
use agama_lib::network::types::{ConnectionState, DeviceType};
use tokio::sync::oneshot;
use uuid::Uuid;

Expand Down Expand Up @@ -62,6 +62,8 @@ pub enum Action {
/// Gets all the existent devices
GetDevices(Responder<Vec<Device>>),
GetGeneralState(Responder<GeneralState>),
/// Connection state changed
ChangeConnectionState(String, ConnectionState),
/// Sets a controller's ports. It uses the Uuid of the controller and the IDs or interface names
/// of the ports.
SetPorts(
Expand Down
10 changes: 7 additions & 3 deletions rust/agama-server/src/network/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::network::error::NetworkStateError;
use agama_lib::network::settings::{
BondSettings, IEEE8021XSettings, NetworkConnection, WirelessSettings,
};
use agama_lib::network::types::{BondMode, DeviceState, DeviceType, Status, SSID};
use agama_lib::network::types::{BondMode, ConnectionState, DeviceState, DeviceType, Status, SSID};
use agama_lib::openapi::schemas;
use cidr::IpInet;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -455,6 +455,7 @@ mod tests {
/// Network state
#[serde_as]
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, utoipa::ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct GeneralState {
pub hostname: String,
pub connectivity: bool,
Expand All @@ -479,7 +480,7 @@ pub struct AccessPoint {
/// Network device
#[serde_as]
#[skip_serializing_none]
#[derive(Default, Debug, Clone, Serialize, utoipa::ToSchema)]
#[derive(Default, Debug, Clone, PartialEq, Serialize, utoipa::ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct Device {
pub name: String,
Expand All @@ -491,7 +492,6 @@ pub struct Device {
// Connection.id
pub connection: Option<String>,
pub state: DeviceState,
pub state_reason: u8,
}

/// Represents a known network connection.
Expand All @@ -515,6 +515,7 @@ pub struct Connection {
pub config: ConnectionConfig,
pub ieee_8021x_config: Option<IEEE8021XConfig>,
pub autoconnect: bool,
pub state: ConnectionState,
}

impl Connection {
Expand Down Expand Up @@ -587,6 +588,7 @@ impl Default for Connection {
config: Default::default(),
ieee_8021x_config: Default::default(),
autoconnect: true,
state: Default::default(),
}
}
}
Expand Down Expand Up @@ -1783,6 +1785,8 @@ pub enum NetworkChange {
/// original device name, which is especially useful if the
/// device gets renamed.
DeviceUpdated(String, Device),
/// A connection state has changed.
ConnectionStateChanged { id: String, state: ConnectionState },
}

#[derive(Default, Debug, PartialEq, Clone, Serialize, utoipa::ToSchema)]
Expand Down
1 change: 1 addition & 0 deletions rust/agama-server/src/network/nm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod dbus;
mod error;
mod model;
mod proxies;
mod streams;
mod watcher;

pub use adapter::NetworkManagerAdapter;
Expand Down
53 changes: 44 additions & 9 deletions rust/agama-server/src/network/nm/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ use anyhow::Context;
use cidr::IpInet;
use std::{collections::HashMap, net::IpAddr, str::FromStr};

use super::model::NmDeviceState;

/// Builder to create a [Device] from its corresponding NetworkManager D-Bus representation.
pub struct DeviceFromProxyBuilder<'a> {
connection: zbus::Connection,
Expand All @@ -57,21 +59,14 @@ impl<'a> DeviceFromProxyBuilder<'a> {
.try_into()
.context("Unsupported device type: {device_type}")?;

let state = self.proxy.state().await? as u8;
let (_, state_reason) = self.proxy.state_reason().await?;
let state: DeviceState = state
.try_into()
.context("Unsupported device state: {state}")?;

let mut device = Device {
name: self.proxy.interface().await?,
state: self.device_state_from_proxy().await?,
type_,
state,
state_reason: state_reason as u8,
..Default::default()
};

if state == DeviceState::Activated {
if device.state == DeviceState::Connected {
device.ip_config = self.build_ip_config().await?;
}

Expand Down Expand Up @@ -249,4 +244,44 @@ impl<'a> DeviceFromProxyBuilder<'a> {

Some(id.to_string())
}

/// Map the combination of state + reason to the Agama set of states.
///
/// See https://www.networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceState
/// and https://www.networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceStateReason
/// for further information.
async fn device_state_from_proxy(&self) -> Result<DeviceState, ServiceError> {
const USER_REQUESTED: u32 = 39;

let (state, reason) = self.proxy.state_reason().await?;
let state: NmDeviceState = (state as u8)
.try_into()
.context("Unsupported device state: {state}")?;

let device_state = match state {
NmDeviceState::Unknown => DeviceState::Unknown,
NmDeviceState::Unmanaged => DeviceState::Unmanaged,
NmDeviceState::Unavailable => DeviceState::Unavailable,
NmDeviceState::Prepare
| NmDeviceState::IpConfig
| NmDeviceState::NeedAuth
| NmDeviceState::Config
| NmDeviceState::Secondaries
| NmDeviceState::IpCheck => DeviceState::Connecting,
NmDeviceState::Activated => DeviceState::Connected,
NmDeviceState::Deactivating => DeviceState::Disconnecting,
NmDeviceState::Disconnected => {
// If the connection failed, NetworkManager sets the state to "disconnected".
// Let's consider it a problem unless it was requested by the user.
if reason == USER_REQUESTED {
DeviceState::Disconnected
} else {
DeviceState::Failed
}
}
NmDeviceState::Failed => DeviceState::Failed,
};

Ok(device_state)
}
}
Loading
Loading