Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion rust/agama-lib/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ mod store;

pub use agama_network::{
error, model, types, Action, Adapter, NetworkAdapterError, NetworkManagerAdapter,
NetworkSystem, NetworkSystemClient, NetworkSystemError,
NetworkSystemClient, NetworkSystemError, Service,
};
pub use agama_utils::api::network::*;
pub use client::{NetworkClient, NetworkClientError};
Expand Down
15 changes: 14 additions & 1 deletion rust/agama-manager/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,11 @@ impl Starter {

let network = match self.network {
Some(network) => network,
None => network::start().await?,
None => {
network::Service::starter(self.events.clone(), progress.clone())
.start()
.await?
}
};

let hardware = match self.hardware {
Expand Down Expand Up @@ -291,6 +295,7 @@ impl Service {
///
/// If a default product is set, it asks the other services to initialize their configurations.
pub async fn setup(&mut self) -> Result<(), Error> {
self.network_default().await?;
self.read_system_info().await?;

if let Some(product) = self.products.default_product() {
Expand All @@ -303,6 +308,13 @@ impl Service {
Ok(())
}

// Configure the network according to defaults
async fn network_default(&mut self) -> Result<(), Error> {
self.network.propose_default().await?;
self.network.apply().await?;
Ok(())
}

async fn read_system_info(&mut self) -> Result<(), Error> {
self.licenses.read()?;
self.products.read()?;
Expand Down Expand Up @@ -719,6 +731,7 @@ impl InstallAction {
self.l10n.call(l10n::message::Install).await?;
self.software.call(software::message::Finish).await?;
self.files.call(files::message::WriteFiles).await?;
self.network.install().await?;
self.hostname.call(hostname::message::Install).await?;
self.storage.call(storage::message::Finish).await?;

Expand Down
2 changes: 1 addition & 1 deletion rust/agama-manager/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ pub async fn start_service(events: event::Sender, dbus: zbus::Connection) -> Han
.with_storage(
start_storage_service(events.clone(), issues.clone(), progress.clone(), dbus).await,
)
.with_network(start_network_service(events.clone(), progress.clone()).await)
.with_software(start_software_service(events, issues, progress, questions).await)
.with_network(start_network_service().await)
.with_hardware(hardware::Registry::new_from_file(
fixtures.join("lshw.json"),
))
Expand Down
2 changes: 2 additions & 0 deletions rust/agama-network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ utoipa = { version = "5.3.1", features = ["uuid"] }
uuid = { version = "1.16.0", features = ["v4", "serde"] }
zbus = { version = "5", default-features = false, features = ["tokio"] }
semver = "1.0.26"
serde_json = "1.0.148"
gettext-rs = "0.7.7"
5 changes: 5 additions & 0 deletions rust/agama-network/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ pub enum Action {
Box<Vec<String>>,
Responder<Result<(), NetworkStateError>>,
),
/// It persit existing connections if there is no one to be persisted and the copy of network
/// is not disabled
ProposeDefault(Responder<Result<(), NetworkStateError>>),
// Copies persistent connections to the target system
Install(Responder<Result<(), NetworkStateError>>),
/// Updates a connection (replacing the old one).
UpdateConnection(Box<Connection>, Responder<Result<(), NetworkStateError>>),
/// Updates the general network configuration
Expand Down
13 changes: 12 additions & 1 deletion rust/agama-network/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
// find current contact information at www.suse.com.

use crate::{model::StateConfig, Action, NetworkState};
use agama_utils::{api::event::Event, issue, progress};
use async_trait::async_trait;
use thiserror::Error;
use tokio::sync::mpsc::UnboundedSender;
use tokio::sync::{broadcast, mpsc::UnboundedSender};

#[derive(Error, Debug)]
pub enum NetworkAdapterError {
Expand All @@ -33,6 +34,16 @@ pub enum NetworkAdapterError {
Checkpoint(anyhow::Error), // only relevant for adapters that implement a checkpoint mechanism
#[error("The network watcher cannot run: {0}")]
Watcher(anyhow::Error),
#[error("Wrong signal arguments")]
ProgressChangedArgs,
#[error("Wrong signal data")]
ProgressChangedData,
#[error(transparent)]
Issue(#[from] issue::service::Error),
#[error(transparent)]
Progress(#[from] progress::service::Error),
#[error(transparent)]
Event(#[from] broadcast::error::SendError<Event>),
}

/// A trait for the ability to read/write from/to a network service.
Expand Down
2 changes: 2 additions & 0 deletions rust/agama-network/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub enum NetworkStateError {
InvalidWirelessBand(String),
#[error("Invalid bssid: '{0}'")]
InvalidBssid(String),
#[error("I/O error: '{0}'")]
IoError(String),
}

impl From<NetworkStateError> for zbus::fdo::Error {
Expand Down
6 changes: 2 additions & 4 deletions rust/agama-network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,13 @@ pub mod adapter;
pub mod error;
pub mod model;
mod nm;
pub mod start;
pub use start::start;
mod system;
mod service;
pub mod types;

pub use action::Action;
pub use adapter::{Adapter, NetworkAdapterError};
pub use model::NetworkState;
pub use nm::NetworkManagerAdapter;
pub use system::{NetworkSystem, NetworkSystemClient, NetworkSystemError};
pub use service::{NetworkSystemClient, NetworkSystemError, Service, Starter};

pub mod test_utils;
113 changes: 111 additions & 2 deletions rust/agama-network/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,19 @@
use crate::error::NetworkStateError;
use crate::types::*;

use agama_utils::openapi::schemas;
use agama_utils::{actor::Error, openapi::schemas};
use macaddr::MacAddr6;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, skip_serializing_none, DisplayFromStr};
use std::{
collections::HashMap,
default::Default,
fmt,
path::{Path, PathBuf},
str::{self, FromStr},
};
use thiserror::Error;
use tokio::process;
use uuid::Uuid;

#[derive(PartialEq)]
Expand Down Expand Up @@ -139,7 +141,7 @@ impl NetworkState {
self.devices.iter_mut().find(|c| c.name == name)
}

/// Returns the controller's connection for the givne connection Uuid.
/// Returns the controller's connection for the given connection Uuid.
pub fn get_controlled_by(&mut self, uuid: Uuid) -> Vec<&Connection> {
let uuid = Some(uuid);
self.connections
Expand All @@ -160,6 +162,82 @@ impl NetworkState {
Ok(())
}

// Persist the existing connections if there is no one to be persisted and the copy of the
// network is not disabled
pub fn propose_default(&mut self) -> Result<(), NetworkStateError> {
if !self.general_state.copy_network {
return Ok(());
}
if self.connections.is_empty() {
return Ok(());
}

let to_persist: Vec<&Connection> =
self.connections.iter().filter(|c| c.persistent).collect();
if !to_persist.is_empty() {
return Ok(());
}

for conn in self.connections.iter_mut() {
conn.persistent = true;
conn.keep_status();
}

Ok(())
}

pub async fn install(&self) -> Result<(), NetworkStateError> {
const CONNECTIONS_PATH: &str = "/etc/NetworkManager/system-connections";
let from = PathBuf::from(CONNECTIONS_PATH);
let to = PathBuf::from(self.target_dir()).join(CONNECTIONS_PATH.trim_start_matches('/'));

self.copy_connections(&from, &to)?;
self.enable_service("/").await
}

pub async fn enable_service(&self, path: &str) -> Result<(), NetworkStateError> {
let mut command = process::Command::new("chroot");
command.args([path, "systemctl", "enable", "NetworkManager.service"]);

if let Some(output) = command.output().await.ok() {
if output.status.success() {
return Ok(());
}
}

tracing::error!("Error enabling NetworkManager service");
Ok(())
}

fn copy_connections(&self, from: &Path, to: &Path) -> Result<(), NetworkStateError> {
if !to.exists() {
std::fs::create_dir_all(to).map_err(|e| NetworkStateError::IoError(e.to_string()))?;
}

for entry in
std::fs::read_dir(from).map_err(|e| NetworkStateError::IoError(e.to_string()))?
{
let entry = entry.map_err(|e| NetworkStateError::IoError(e.to_string()))?;
let path = entry.path();
if path.is_file() {
if let Some(file_name) = path.file_name() {
let dest = to.join(file_name);
if let Err(e) = std::fs::copy(&path, &dest) {
tracing::error!("It was not possible to copy {:?}: {:?}", &file_name, e);
}
} else {
tracing::error!("The path {:?} looks wrong, skipping it.", &path);
}
}
}

Ok(())
}

fn target_dir(&self) -> &str {
"/mnt"
}

/// Updates the current [NetworkState] with the configuration provided.
///
/// The config could contain a [NetworkConnectionsCollection] to be updated, in case of
Expand Down Expand Up @@ -453,6 +531,37 @@ mod tests {
NetworkStateError::NotControllerConnection(_),
));
}

#[test]
fn test_copy_connections() {
use std::fs;

let tmp_dir = std::env::temp_dir().join(format!("test_agama_network_{}", Uuid::new_v4()));
let source = tmp_dir.join("source");
let dest = tmp_dir.join("dest");

fs::create_dir_all(&source).unwrap();
fs::write(source.join("conn1.nmconnection"), "content1").unwrap();
fs::write(source.join("conn2.nmconnection"), "content2").unwrap();
fs::create_dir(source.join("ignored_dir")).unwrap();

let state = NetworkState::default();
state.copy_connections(&source, &dest).unwrap();

assert!(dest.join("conn1.nmconnection").exists());
assert_eq!(
fs::read_to_string(dest.join("conn1.nmconnection")).unwrap(),
"content1"
);
assert!(dest.join("conn2.nmconnection").exists());
assert_eq!(
fs::read_to_string(dest.join("conn2.nmconnection")).unwrap(),
"content2"
);
assert!(!dest.join("ignored_dir").exists());

fs::remove_dir_all(tmp_dir).unwrap();
}
}

pub const NOT_COPY_NETWORK_PATH: &str = "/run/agama/not_copy_network";
Expand Down
3 changes: 3 additions & 0 deletions rust/agama-network/src/nm/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ impl<'a> NetworkManagerAdapter<'a> {
pub async fn from_system() -> Result<NetworkManagerAdapter<'a>, NmError> {
let connection = zbus::Connection::system().await?;
let client = NetworkManagerClient::new(connection.clone()).await?;

Ok(Self { client, connection })
}
}
Expand Down Expand Up @@ -127,6 +128,7 @@ impl Adapter for NetworkManagerAdapter<'_> {
&network.general_state,
&e
);

return Err(NetworkAdapterError::Write(anyhow!(e)));
}

Expand Down Expand Up @@ -171,6 +173,7 @@ impl Adapter for NetworkManagerAdapter<'_> {
.await
.map_err(|e| NetworkAdapterError::Checkpoint(anyhow!(e)))?;
tracing::error!("Could not process the connection {}: {}", conn.id, &e);

return Err(NetworkAdapterError::Write(anyhow!(e)));
}
}
Expand Down
Loading
Loading