Skip to content

Commit

Permalink
Added support for modifying the network configuration through http
Browse files Browse the repository at this point in the history
  • Loading branch information
teclator committed Mar 20, 2024
1 parent e58deb8 commit f35ef04
Show file tree
Hide file tree
Showing 10 changed files with 715 additions and 34 deletions.
14 changes: 13 additions & 1 deletion rust/agama-lib/src/network/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use cidr::errors::NetworkParseError;
use serde::{Deserialize, Serialize};
use std::{fmt, str};
use std::{
fmt,
str::{self, FromStr},
};
use thiserror::Error;
use zbus;

Expand All @@ -26,6 +30,14 @@ impl fmt::Display for SSID {
}
}

impl FromStr for SSID {
type Err = NetworkParseError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(SSID(s.as_bytes().into()))
}
}

impl From<SSID> for Vec<u8> {
fn from(value: SSID) -> Self {
value.0
Expand Down
20 changes: 16 additions & 4 deletions rust/agama-server/src/network/action.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::network::model::{Connection, Device};
use crate::network::model::{AccessPoint, Connection, Device};
use agama_lib::network::types::DeviceType;
use tokio::sync::oneshot;
use uuid::Uuid;
use zbus::zvariant::OwnedObjectPath;

use super::{error::NetworkStateError, NetworkAdapterError};
use super::{error::NetworkStateError, model::GeneralState, NetworkAdapterError};

pub type Responder<T> = oneshot::Sender<T>;
pub type ControllerConnection = (Connection, Vec<String>);
Expand All @@ -21,6 +21,11 @@ pub enum Action {
DeviceType,
Responder<Result<OwnedObjectPath, NetworkStateError>>,
),
/// Add a new connection
NewConnection(
Connection,
Responder<Result<OwnedObjectPath, NetworkStateError>>,
),
/// Gets a connection by its Uuid
GetConnection(Uuid, Responder<Option<Connection>>),
/// Gets a connection
Expand All @@ -36,6 +41,8 @@ pub enum Action {
Uuid,
Responder<Result<ControllerConnection, NetworkStateError>>,
),
/// Gets all scanned access points
GetAccessPoints(Responder<Vec<AccessPoint>>),
/// Gets a device by its name
GetDevice(String, Responder<Option<Device>>),
/// Gets all the existent devices
Expand All @@ -44,17 +51,22 @@ pub enum Action {
GetDevicePath(String, Responder<Option<OwnedObjectPath>>),
/// Get devices paths
GetDevicesPaths(Responder<Vec<OwnedObjectPath>>),
GetGeneralState(Responder<GeneralState>),
/// Sets a controller's ports. It uses the Uuid of the controller and the IDs or interface names
/// of the ports.
SetPorts(
Uuid,
Box<Vec<String>>,
Responder<Result<(), NetworkStateError>>,
),
/// Update a connection (replacing the old one).
/// Updates a connection (replacing the old one).
UpdateConnection(Box<Connection>),
/// Updates the general network configuration
UpdateGeneralState(GeneralState),
/// Forces a wireless networks scan refresh
RefreshScan(Responder<Result<(), NetworkAdapterError>>),
/// Remove the connection with the given Uuid.
RemoveConnection(Uuid),
RemoveConnection(Uuid, Responder<Result<(), NetworkStateError>>),
/// Apply the current configuration.
Apply(Responder<Result<(), NetworkAdapterError>>),
}
6 changes: 5 additions & 1 deletion rust/agama-server/src/network/adapter.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::network::model::NetworkStateItems;
use crate::network::NetworkState;
use agama_lib::error::ServiceError;
use async_trait::async_trait;
Expand All @@ -16,7 +17,10 @@ pub enum NetworkAdapterError {
/// A trait for the ability to read/write from/to a network service
#[async_trait]
pub trait Adapter {
async fn read(&self) -> Result<NetworkState, NetworkAdapterError>;
async fn read(
&self,
items: Vec<NetworkStateItems>,
) -> Result<NetworkState, NetworkAdapterError>;
async fn write(&self, network: &NetworkState) -> Result<(), NetworkAdapterError>;
}

Expand Down
5 changes: 4 additions & 1 deletion rust/agama-server/src/network/dbus/interfaces/connections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ impl Connections {
.parse()
.map_err(|_| NetworkStateError::InvalidUuid(uuid.to_string()))?;
let actions = self.actions.lock().await;
actions.send(Action::RemoveConnection(uuid)).unwrap();
let (tx, rx) = oneshot::channel();
actions.send(Action::RemoveConnection(uuid, tx)).unwrap();

rx.await.unwrap()?;
Ok(())
}

Expand Down
188 changes: 186 additions & 2 deletions rust/agama-server/src/network/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
//! * This module contains the types that represent the network concepts. They are supposed to be
//! 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, SSID};
use cidr::IpInet;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, skip_serializing_none, DisplayFromStr};
use std::{
collections::HashMap,
Expand All @@ -18,19 +19,38 @@ use thiserror::Error;
use uuid::Uuid;
use zbus::zvariant::Value;

#[derive(PartialEq)]
pub enum NetworkStateItems {
AccessPoints,
Devices,
Connections,
GeneralState,
}

#[derive(Default, Clone, Debug, utoipa::ToSchema)]
pub struct NetworkState {
pub general_state: GeneralState,
pub access_points: Vec<AccessPoint>,
pub devices: Vec<Device>,
pub connections: Vec<Connection>,
}

impl NetworkState {
/// Returns a NetworkState struct with the given devices and connections.
///
/// * `general_state`: General network configuration
/// * `access_points`: Access points to include in the state.
/// * `devices`: devices to include in the state.
/// * `connections`: connections to include in the state.
pub fn new(devices: Vec<Device>, connections: Vec<Connection>) -> Self {
pub fn new(
general_state: GeneralState,
access_points: Vec<AccessPoint>,
devices: Vec<Device>,
connections: Vec<Connection>,
) -> Self {
Self {
general_state,
access_points,
devices,
connections,
}
Expand Down Expand Up @@ -369,6 +389,35 @@ mod tests {
}
}

/// Network state
#[serde_as]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, utoipa::ToSchema)]
pub struct GeneralState {
pub connectivity: bool,
pub wireless_enabled: bool,
pub networking_enabled: bool, // pub network_state: NMSTATE
// pub dns: GlobalDnsConfiguration <HashMap>
}

impl Default for GeneralState {
fn default() -> Self {
Self {
connectivity: false,
wireless_enabled: false,
networking_enabled: false,
}
}
}

/// Access Point
#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
pub struct AccessPoint {
pub ssid: SSID,
pub hw_address: String,
pub strength: u8,
pub security_protocols: Vec<SecurityProtocol>,
}

/// Network device
#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
pub struct Device {
Expand Down Expand Up @@ -465,6 +514,84 @@ impl Default for Connection {
}
}

impl TryFrom<NetworkConnection> for Connection {
type Error = NetworkStateError;

fn try_from(conn: NetworkConnection) -> Result<Self, Self::Error> {
let id = conn.clone().id;
let mut connection = Connection::new(id, conn.device_type());

if let Some(method) = conn.clone().method4 {
let method: Ipv4Method = method.parse().unwrap();
connection.ip_config.method4 = method;
}

if let Some(method) = conn.method6 {
let method: Ipv6Method = method.parse().unwrap();
connection.ip_config.method6 = method;
}

if let Some(wireless_config) = conn.wireless {
let config = WirelessConfig::try_from(wireless_config)?;
connection.config = config.into();
}

if let Some(bond_config) = conn.bond {
let config = BondConfig::try_from(bond_config)?;
connection.config = config.into();
}

connection.ip_config.nameservers = conn.nameservers;
connection.ip_config.gateway4 = conn.gateway4;
connection.ip_config.gateway6 = conn.gateway6;
connection.interface = conn.interface;

Ok(connection)
}
}

impl TryFrom<Connection> for NetworkConnection {
type Error = NetworkStateError;

fn try_from(conn: Connection) -> Result<Self, Self::Error> {
let id = conn.clone().id;
let mac = conn.mac_address.to_string();
let method4 = Some(conn.ip_config.method4.to_string());
let method6 = Some(conn.ip_config.method6.to_string());
let mac_address = (!mac.is_empty()).then(|| mac);
let nameservers = conn.ip_config.nameservers.into();
let addresses = conn.ip_config.addresses.into();
let gateway4 = conn.ip_config.gateway4.into();
let gateway6 = conn.ip_config.gateway6.into();
let interface = conn.interface.into();

let mut connection = NetworkConnection {
id,
method4,
method6,
gateway4,
gateway6,
nameservers,
mac_address,
interface,
addresses,
..Default::default()
};

match conn.config {
ConnectionConfig::Wireless(config) => {
connection.wireless = Some(WirelessSettings::try_from(config)?);
}
ConnectionConfig::Bond(config) => {
connection.bond = Some(BondSettings::try_from(config)?);
}
_ => {}
}

Ok(connection)
}
}

#[derive(Default, Debug, PartialEq, Clone, Serialize)]
pub enum ConnectionConfig {
#[default]
Expand Down Expand Up @@ -781,6 +908,36 @@ impl TryFrom<ConnectionConfig> for WirelessConfig {
}
}

impl TryFrom<WirelessSettings> for WirelessConfig {
type Error = NetworkStateError;

fn try_from(settings: WirelessSettings) -> Result<Self, Self::Error> {
let ssid = SSID(settings.ssid.as_bytes().into());
let mode = WirelessMode::try_from(settings.mode.as_str())?;
let security = SecurityProtocol::try_from(settings.security.as_str())?;
Ok(WirelessConfig {
ssid,
mode,
security,
password: Some(settings.password),
..Default::default()
})
}
}

impl TryFrom<WirelessConfig> for WirelessSettings {
type Error = NetworkStateError;

fn try_from(wireless: WirelessConfig) -> Result<Self, Self::Error> {
Ok(WirelessSettings {
ssid: wireless.ssid.to_string(),
mode: wireless.mode.to_string(),
security: wireless.security.to_string(),
password: wireless.password.unwrap_or_default(),
})
}
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize)]
pub enum WirelessMode {
Unknown = 0,
Expand Down Expand Up @@ -1008,6 +1165,33 @@ impl TryFrom<ConnectionConfig> for BondConfig {
}
}

impl TryFrom<BondSettings> for BondConfig {
type Error = NetworkStateError;

fn try_from(settings: BondSettings) -> Result<Self, Self::Error> {
let mode = BondMode::try_from(settings.mode.as_str())
.map_err(|_| NetworkStateError::InvalidBondMode(settings.mode))?;
let mut options = BondOptions::default();
if let Some(setting_options) = settings.options {
options = BondOptions::try_from(setting_options.as_str())?;
}

Ok(BondConfig { mode, options })
}
}

impl TryFrom<BondConfig> for BondSettings {
type Error = NetworkStateError;

fn try_from(bond: BondConfig) -> Result<Self, Self::Error> {
Ok(BondSettings {
mode: bond.mode.to_string(),
options: Some(bond.options.to_string()),
..Default::default()
})
}
}

#[derive(Debug, Default, PartialEq, Clone, Serialize)]
pub struct BridgeConfig {
pub stp: bool,
Expand Down
Loading

0 comments on commit f35ef04

Please sign in to comment.