Skip to content

Commit

Permalink
Merge 686a23d into b43af77
Browse files Browse the repository at this point in the history
  • Loading branch information
imobachgs committed Apr 26, 2024
2 parents b43af77 + 686a23d commit b664f13
Show file tree
Hide file tree
Showing 37 changed files with 1,572 additions and 217 deletions.
6 changes: 4 additions & 2 deletions rust/agama-lib/src/network/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use super::proxies::{
WirelessProxy,
};
use super::settings::{BondSettings, MatchSettings, NetworkConnection, WirelessSettings};
use super::types::{Device, DeviceType, SSID};
use super::types::{Device, DeviceState, DeviceType, SSID};
use crate::error::ServiceError;
use tokio_stream::StreamExt;
use zbus::zvariant::OwnedObjectPath;
Expand Down Expand Up @@ -86,10 +86,12 @@ impl<'a> NetworkClient<'a> {
.await?;
let name = device_proxy.name().await?;
let device_type = device_proxy.type_().await?;
let state = DeviceState::try_from(device_proxy.state().await?).expect("Unknown state");

Ok(Device {
name,
type_: DeviceType::try_from(device_type).unwrap(),
type_: DeviceType::try_from(device_type).expect("Unknown type"),
state,
})
}

Expand Down
3 changes: 3 additions & 0 deletions rust/agama-lib/src/network/proxies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ trait Device {
/// Type property
#[dbus_proxy(property)]
fn type_(&self) -> zbus::Result<u8>;
/// State property
#[dbus_proxy(property)]
fn state(&self) -> zbus::Result<u8>;
}

#[dbus_proxy(
Expand Down
3 changes: 2 additions & 1 deletion rust/agama-lib/src/network/settings.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Representation of the network settings

use super::types::{DeviceType, Status};
use super::types::{DeviceState, DeviceType, Status};
use agama_settings::error::ConversionError;
use agama_settings::{SettingObject, SettingValue, Settings};
use cidr::IpInet;
Expand Down Expand Up @@ -71,6 +71,7 @@ impl Default for BondSettings {
pub struct NetworkDevice {
pub id: String,
pub type_: DeviceType,
pub state: DeviceState,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
Expand Down
70 changes: 69 additions & 1 deletion rust/agama-lib/src/network/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use zbus;
pub struct Device {
pub name: String,
pub type_: DeviceType,
pub state: DeviceState,
}

#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -44,9 +45,11 @@ impl From<SSID> for Vec<u8> {
}
}

#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Default, Debug, PartialEq, Copy, Clone, Serialize, Deserialize, utoipa::ToSchema)]
#[serde(rename_all = "camelCase")]
pub enum DeviceType {
Loopback = 0,
#[default]
Ethernet = 1,
Wireless = 2,
Dummy = 3,
Expand All @@ -55,6 +58,71 @@ pub enum DeviceType {
Bridge = 6,
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[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,
}
#[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(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Status {
Expand Down
4 changes: 1 addition & 3 deletions rust/agama-server/src/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ use openssl::error::ErrorStack;
use openssl::hash::MessageDigest;
use openssl::pkey::{PKey, Private};
use openssl::rsa::Rsa;
use openssl::x509::extension::{
BasicConstraints, KeyUsage, SubjectAlternativeName, SubjectKeyIdentifier,
};
use openssl::x509::extension::{BasicConstraints, SubjectAlternativeName, SubjectKeyIdentifier};
use openssl::x509::{X509NameBuilder, X509};

// TODO: move the certificate related functions into a struct
Expand Down
6 changes: 6 additions & 0 deletions rust/agama-server/src/network/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ pub enum Action {
),
/// Gets all scanned access points
GetAccessPoints(Responder<Vec<AccessPoint>>),
/// Adds a new device.
AddDevice(Box<Device>),
/// Updates a device by its `name`.
UpdateDevice(String, Box<Device>),
/// Removes a device by its `name`.
RemoveDevice(String),
/// Gets a device by its name
GetDevice(String, Responder<Option<Device>>),
/// Gets all the existent devices
Expand Down
24 changes: 21 additions & 3 deletions rust/agama-server/src/network/adapter.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::network::model::StateConfig;
use crate::network::NetworkState;
use crate::network::{model::StateConfig, Action, NetworkState};
use agama_lib::error::ServiceError;
use async_trait::async_trait;
use thiserror::Error;
use tokio::sync::mpsc::UnboundedSender;

#[derive(Error, Debug)]
pub enum NetworkAdapterError {
Expand All @@ -12,17 +12,35 @@ pub enum NetworkAdapterError {
Write(ServiceError),
#[error("Checkpoint handling error: {0}")]
Checkpoint(ServiceError), // only relevant for adapters that implement a checkpoint mechanism
#[error("The network watcher cannot run: {0}")]
Watcher(ServiceError),
}

/// A trait for the ability to read/write from/to a network service
/// A trait for the ability to read/write from/to a network service.
#[async_trait]
pub trait Adapter {
async fn read(&self, config: StateConfig) -> Result<NetworkState, NetworkAdapterError>;
async fn write(&self, network: &NetworkState) -> Result<(), NetworkAdapterError>;
/// Returns the watcher, which is responsible for listening for network changes.
fn watcher(&self) -> Option<Box<dyn Watcher + Send>> {
None
}
}

impl From<NetworkAdapterError> for zbus::fdo::Error {
fn from(value: NetworkAdapterError) -> zbus::fdo::Error {
zbus::fdo::Error::Failed(value.to_string())
}
}

#[async_trait]
/// A trait for the ability to listen for network changes.
pub trait Watcher {
/// Listens for network changes and emit actions to update the state.
///
/// * `actions`: channel to emit the actions.
async fn run(
self: Box<Self>,
actions: UnboundedSender<Action>,
) -> Result<(), NetworkAdapterError>;
}
2 changes: 1 addition & 1 deletion rust/agama-server/src/network/dbus/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub struct NetworkService;

impl NetworkService {
/// Starts listening and dispatching events on the D-Bus connection.
pub async fn start<T: Adapter + std::marker::Send + 'static>(
pub async fn start<T: Adapter + Send + Sync + 'static>(
connection: &Connection,
adapter: T,
) -> Result<(), Box<dyn Error>> {
Expand Down
2 changes: 2 additions & 0 deletions rust/agama-server/src/network/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub enum NetworkStateError {
UnknownConnection(String),
#[error("Cannot update connection '{0}'")]
CannotUpdateConnection(String),
#[error("Unknown device '{0}'")]
UnknownDevice(String),
#[error("Invalid connection UUID: '{0}'")]
InvalidUuid(String),
#[error("Invalid IP address: '{0}'")]
Expand Down
58 changes: 55 additions & 3 deletions rust/agama-server/src/network/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! agnostic from the real network service (e.g., NetworkManager).
use crate::network::error::NetworkStateError;
use agama_lib::network::settings::{BondSettings, NetworkConnection, WirelessSettings};
use agama_lib::network::types::{BondMode, DeviceType, Status, SSID};
use agama_lib::network::types::{BondMode, DeviceState, DeviceType, Status, SSID};
use cidr::IpInet;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, skip_serializing_none, DisplayFromStr};
Expand Down Expand Up @@ -112,6 +112,13 @@ impl NetworkState {
self.connections.iter_mut().find(|c| c.id == id)
}

/// Get a device by name as mutable
///
/// * `name`: device name
pub fn get_device_mut(&mut self, name: &str) -> Option<&mut Device> {
self.devices.iter_mut().find(|c| c.name == name)
}

pub fn get_controlled_by(&mut self, uuid: Uuid) -> Vec<&Connection> {
let uuid = Some(uuid);
self.connections
Expand Down Expand Up @@ -158,6 +165,28 @@ impl NetworkState {
Ok(())
}

pub fn add_device(&mut self, device: Device) -> Result<(), NetworkStateError> {
self.devices.push(device);
Ok(())
}

pub fn update_device(&mut self, name: &str, device: Device) -> Result<(), NetworkStateError> {
let Some(old_device) = self.get_device_mut(name) else {
return Err(NetworkStateError::UnknownDevice(device.name.clone()));
};
*old_device = device;

Ok(())
}

pub fn remove_device(&mut self, name: &str) -> Result<(), NetworkStateError> {
// TODO: return an error if the device does not exist
if let Some(position) = self.devices.iter().position(|d| &d.name == name) {
self.devices.remove(position);
}
Ok(())
}

/// Sets a controller's ports.
///
/// If the connection is not a controller, returns an error.
Expand Down Expand Up @@ -309,7 +338,6 @@ mod tests {
fn test_remove_connection() {
let mut state = NetworkState::default();
let conn0 = Connection::new("eth0".to_string(), DeviceType::Ethernet);
let uuid = conn0.uuid;
state.add_connection(conn0).unwrap();
state.remove_connection("eth0".as_ref()).unwrap();
let found = state.get_connection("eth0").unwrap();
Expand Down Expand Up @@ -413,6 +441,7 @@ pub struct GeneralState {
/// Access Point
#[serde_as]
#[derive(Default, Debug, Clone, Serialize, utoipa::ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct AccessPoint {
#[serde_as(as = "DisplayFromStr")]
pub ssid: SSID,
Expand All @@ -424,17 +453,27 @@ pub struct AccessPoint {
}

/// Network device
#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
#[serde_as]
#[skip_serializing_none]
#[derive(Default, Debug, Clone, Serialize, utoipa::ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct Device {
pub name: String,
#[serde(rename = "type")]
pub type_: DeviceType,
#[serde_as(as = "DisplayFromStr")]
pub mac_address: MacAddress,
pub ip_config: Option<IpConfig>,
// Connection.id
pub connection: Option<String>,
pub state: DeviceState,
}

/// Represents a known network connection.
#[serde_as]
#[skip_serializing_none]
#[derive(Debug, Clone, PartialEq, Serialize, utoipa::ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct Connection {
pub id: String,
pub uuid: Uuid,
Expand Down Expand Up @@ -702,6 +741,7 @@ impl From<InvalidMacAddress> for zbus::fdo::Error {

#[skip_serializing_none]
#[derive(Default, Debug, PartialEq, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct IpConfig {
pub method4: Ipv4Method,
pub method6: Ipv6Method,
Expand Down Expand Up @@ -733,6 +773,7 @@ pub struct MatchConfig {
pub struct UnknownIpMethod(String);

#[derive(Debug, Default, Copy, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum Ipv4Method {
#[default]
Disabled = 0,
Expand Down Expand Up @@ -768,6 +809,7 @@ impl FromStr for Ipv4Method {
}

#[derive(Debug, Default, Copy, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum Ipv6Method {
#[default]
Disabled = 0,
Expand Down Expand Up @@ -815,6 +857,7 @@ impl From<UnknownIpMethod> for zbus::fdo::Error {
}

#[derive(Debug, PartialEq, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct IpRoute {
pub destination: IpInet,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -884,6 +927,7 @@ pub struct VlanConfig {

#[serde_as]
#[derive(Debug, Default, PartialEq, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WirelessConfig {
pub mode: WirelessMode,
#[serde_as(as = "DisplayFromStr")]
Expand Down Expand Up @@ -1259,3 +1303,11 @@ impl fmt::Display for InfinibandTransportMode {
write!(f, "{}", name)
}
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum NetworkChange {
DeviceAdded(Device),
DeviceRemoved(String),
DeviceUpdated(String, Device),
}
3 changes: 3 additions & 0 deletions rust/agama-server/src/network/nm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
//! internally, so the API is focused on Agama's use cases.

mod adapter;
mod builder;
mod client;
mod dbus;
mod error;
mod model;
mod proxies;
mod watcher;

pub use adapter::NetworkManagerAdapter;
pub use client::NetworkManagerClient;
pub use watcher::NetworkManagerWatcher;
Loading

0 comments on commit b664f13

Please sign in to comment.