Add wip networkd Apply

This commit is contained in:
Samuel Lorch 2024-01-14 23:17:20 +01:00
parent 163c097d2d
commit ee013ec9ad
7 changed files with 237 additions and 8 deletions

173
src/apply/networkd/mod.rs Normal file
View file

@ -0,0 +1,173 @@
use super::ApplyError;
use crate::{
definitions::{config::Config, network::NetworkInterfaceType},
templates,
};
use std::error::Error;
use tera::Context;
use tracing::{error, info};
pub struct File {
pub name: String,
pub content: String,
}
pub fn apply_networkd(pending_config: Config, current_config: Config) -> Result<(), ApplyError> {
let files = generate_networkd_config_files(pending_config, current_config)?;
info!("Got Files");
for file in files {
info!("Conf File {}", file.name);
info!("{}", file.content);
}
Ok(())
}
pub fn generate_networkd_config_files(
pending_config: Config,
_current_config: Config,
) -> Result<Vec<File>, ApplyError> {
let mut files = Vec::new();
// Step 1 Generate vlan netdev files
for interface in &pending_config.network.interfaces {
if let NetworkInterfaceType::Vlan { id, .. } = &interface.interface_type {
let mut context = Context::new();
context.insert("name", &interface.name);
context.insert("vlan_id", &id);
files.push(generate_config_file(
context,
"networkd/create-vlan.netdev",
format!("10-create-vlan-{}.netdev", &interface.name),
)?);
}
}
// Step 2 Generate bond netdev files
for interface in &pending_config.network.interfaces {
if let NetworkInterfaceType::Bond { .. } = &interface.interface_type {
let mut context = Context::new();
context.insert("name", &interface.name);
files.push(generate_config_file(
context,
"networkd/create-bond.netdev",
format!("20-create-bond-{}.netdev", &interface.name),
)?);
// Create Membership files
for member in interface
.interface_type
.bond_members(pending_config.clone())
{
let mut context = Context::new();
context.insert("bond_name", &interface.name);
// if interface is a hardware interface then we want to use device instead
match member.interface_type {
NetworkInterfaceType::Hardware { device } => context.insert("name", &device),
_ => context.insert("name", &member.name),
};
files.push(generate_config_file(
context,
"networkd/bond-membership.network",
format!("50-bond-membership-{}.network", &member.name),
)?);
}
}
}
// Step 3 Generate bridge netdev files
for interface in &pending_config.network.interfaces {
if let NetworkInterfaceType::Bridge { .. } = &interface.interface_type {
let mut context = Context::new();
context.insert("name", &interface.name);
files.push(generate_config_file(
context,
"networkd/create-bridge.netdev",
format!("30-create-bridge-{}.netdev", &interface.name),
)?);
// Create Membership files
for member in interface
.interface_type
.bridge_members(pending_config.clone())
{
let mut context = Context::new();
context.insert("bridge_name", &interface.name);
// if interface is a hardware interface then we want to use device instead
match member.interface_type {
NetworkInterfaceType::Hardware { device } => context.insert("name", &device),
_ => context.insert("name", &member.name),
};
files.push(generate_config_file(
context,
"networkd/bridge-membership.network",
format!("60-bridge-membership-{}.network", &member.name),
)?);
}
}
}
// Step 4 Generate wireguard netdev files
/* TODO
for interface in &pending_config.network.interfaces {
if let NetworkInterfaceType::Bridge { .. } = interface.interface_type {
let mut context = Context::new();
context.insert("name", &interface.name);
files.push(generate_config_file(
context,
"networkd/create-wireguard.netdev",
format!("40-create-wireguard-{}.netdev", &interface.name),
)?);
}
}
*/
// Step 5 Generate Addressing network files
/*
for interface in &pending_config.network.interfaces {
if let NetworkInterfaceType::Vlan { id, .. } = &interface.interface_type {
let mut context = Context::new();
match &interface.interface_type {
NetworkInterfaceType::Hardware { device } => context.insert("name", &device),
_ => context.insert("name", &member.name),
};
files.push(generate_config_file(
context,
"networkd/config-addressing.network",
format!("70-config-addressing-{}.network", &interface.name),
)?);
}
}
*/
Ok(files)
}
fn generate_config_file(
context: Context,
template_name: &str,
file_name: String,
) -> Result<File, ApplyError> {
match templates::TEMPLATES.render(template_name, &context) {
Ok(s) => Ok(File {
name: file_name,
content: s,
}),
Err(e) => {
error!("Error: {}", e);
let mut cause = e.source();
while let Some(e) = cause {
error!("Reason: {}", e);
cause = e.source();
}
return Err(ApplyError::TemplateError(e));
}
}
}

View file

@ -1,13 +1,11 @@
use serde::Serialize;
use validator::Validate;
use super::definitions::config::Config;
use pwhash::sha512_crypt;
use serde::Serialize;
use std::fs;
use std::sync::{Arc, Mutex, MutexGuard};
use pwhash::sha512_crypt;
use thiserror::Error;
use tracing::{error, info};
use validator::Validate;
#[derive(Error, Debug)]
pub enum ConfigError {
@ -34,6 +32,11 @@ pub enum ConfigError {
pub const CURRENT_CONFIG_PATH: &str = "config.json";
pub const PENDING_CONFIG_PATH: &str = "pending.json";
static APPLY_FUNCTIONS: &'static [fn(
pending_config: Config,
current_config: Config,
) -> Result<(), super::apply::ApplyError>] = &[super::apply::networkd::apply_networkd];
#[derive(Clone)]
pub struct ConfigManager {
shared_data: Arc<Mutex<SharedData>>,
@ -93,8 +96,36 @@ impl ConfigManager {
pub fn apply_pending_changes(&mut self) -> Result<(), ConfigError> {
let mut data = self.shared_data.lock().unwrap();
// TODO run Apply functions
// TODO Revert on Apply Failure and Return
// TODO Improve Error Handling
for apply_function in APPLY_FUNCTIONS {
match (apply_function)(data.pending_config.clone(), data.current_config.clone()) {
Ok(_) => info!("Applied"),
Err(e) => {
error!("Applying function, Reverting to current config...");
for apply_function in APPLY_FUNCTIONS {
match (apply_function)(
// These are swapped for revert
data.current_config.clone(),
data.pending_config.clone(),
) {
Ok(_) => info!("Applied"),
Err(e) => {
error!("Reverting failed, giving up.");
return Err(ConfigError::ApplyError(e));
}
}
}
info!("Revert Done.");
return Err(ConfigError::ApplyError(e));
}
}
}
info!("Apply Done.");
write_config_to_file(CURRENT_CONFIG_PATH, data.pending_config.clone())?;
// TODO revert if config save fails
// TODO Remove Pending Config File

View file

@ -0,0 +1,5 @@
[Match]
Name={{ name }}
[Network]
Bond={{ bond_name }}

View file

@ -0,0 +1,5 @@
[Match]
Name={{ name }}
[Network]
Bridge={{ bridge_name }}

View file

@ -0,0 +1,6 @@
[NetDev]
Name={{ name }}
Kind=bond
[Bond]
Mode=active-backup

View file

@ -0,0 +1,3 @@
[NetDev]
Name={{ name }}
Kind=bridge

View file

@ -0,0 +1,6 @@
[NetDev]
Name={{ name }}
Kind=vlan
[VLAN]
Id={{ vlan_id }}