From e2e22ea7ae6cb701dbfc002cbe6ab96eb32a4cd0 Mon Sep 17 00:00:00 2001 From: Herbert Wolverson Date: Wed, 1 May 2024 12:36:47 -0500 Subject: [PATCH] Round-trip load/edit/save cycle is basically working for lqos.conf files. More testing needed, passed the easy cases I tried. --- src/rust/lqos_bus/src/bus/request.rs | 5 ++- src/rust/lqos_config/src/etc/mod.rs | 36 +++++++++++++++++++ .../src/etc/v15/anonymous_stats.rs | 2 +- src/rust/lqos_config/src/etc/v15/bridge.rs | 4 +-- src/rust/lqos_config/src/etc/v15/flows.rs | 2 +- src/rust/lqos_config/src/etc/v15/influxdb.rs | 2 +- .../src/etc/v15/integration_common.rs | 2 +- src/rust/lqos_config/src/etc/v15/ip_ranges.rs | 2 +- .../src/etc/v15/long_term_stats.rs | 2 +- .../src/etc/v15/powercode_integration.rs | 2 +- src/rust/lqos_config/src/etc/v15/queues.rs | 2 +- .../src/etc/v15/sonar_integration.rs | 2 +- .../src/etc/v15/spylnx_integration.rs | 2 +- .../lqos_config/src/etc/v15/top_config.rs | 2 +- src/rust/lqos_config/src/etc/v15/tuning.rs | 2 +- .../src/etc/v15/uisp_integration.rs | 4 +-- src/rust/lqos_config/src/lib.rs | 2 +- .../lqos_node_manager/src/config_control.rs | 12 +++++++ src/rust/lqos_node_manager/src/main.rs | 1 + src/rust/lqos_node_manager/static/config.html | 13 ++++++- src/rust/lqosd/src/main.rs | 7 ++++ 21 files changed, 89 insertions(+), 19 deletions(-) diff --git a/src/rust/lqos_bus/src/bus/request.rs b/src/rust/lqos_bus/src/bus/request.rs index 29b44ffd..dffd7b11 100644 --- a/src/rust/lqos_bus/src/bus/request.rs +++ b/src/rust/lqos_bus/src/bus/request.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; /// One or more `BusRequest` objects must be included in a `BusSession` /// request. Each `BusRequest` represents a single request for action /// or data. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub enum BusRequest { /// A generic "is it alive?" test. Returns an `Ack`. Ping, @@ -113,6 +113,9 @@ pub enum BusRequest { /// Requests a real-time adjustment of the `lqosd` tuning settings UpdateLqosDTuning(u64, Tunables), + /// Requests that the configuration be updated + UpdateLqosdConfig(Box), + /// Request that we start watching a circuit's queue WatchQueue(String), diff --git a/src/rust/lqos_config/src/etc/mod.rs b/src/rust/lqos_config/src/etc/mod.rs index 74af9730..0c5e48e9 100644 --- a/src/rust/lqos_config/src/etc/mod.rs +++ b/src/rust/lqos_config/src/etc/mod.rs @@ -1,6 +1,8 @@ //! Manages the `/etc/lqos.conf` file. mod etclqos_migration; + +use std::path::Path; use self::migration::migrate_if_needed; pub use self::v15::Config; pub use etclqos_migration::*; @@ -70,6 +72,38 @@ pub fn enable_long_term_stats(license_key: String) -> Result<(), LibreQoSConfigE Ok(()) } +/// Update the configuration on disk +pub fn update_config(new_config: &Config) -> Result<(), LibreQoSConfigError> { + log::info!("Updating stored configuration"); + let mut lock = CONFIG.lock().unwrap(); + *lock = Some(new_config.clone()); + + // Does the configuration exist? + let config_path = Path::new("/etc/lqos.conf"); + if config_path.exists() { + let backup_path = Path::new("/etc/lqos.conf.webbackup"); + std::fs::copy(config_path, backup_path) + .map_err(|e| { + log::error!("Unable to create backup configuration: {e:?}"); + LibreQoSConfigError::CannotCopy + })?; + } + + // Serialize the new one + let serialized = toml::to_string_pretty(new_config) + .map_err(|e| { + log::error!("Unable to serialize new configuration to TOML: {e:?}"); + LibreQoSConfigError::SerializeError + })?; + std::fs::write(config_path, serialized) + .map_err(|e| { + log::error!("Unable to write new configuration: {e:?}"); + LibreQoSConfigError::CannotWrite + })?; + + Ok(()) +} + #[derive(Debug, Error)] pub enum LibreQoSConfigError { #[error("Unable to read /etc/lqos.conf. See other errors for details.")] @@ -90,4 +124,6 @@ pub enum LibreQoSConfigError { CannotWrite, #[error("Unable to read IP")] CannotReadIP, + #[error("Unable to serialize config")] + SerializeError, } diff --git a/src/rust/lqos_config/src/etc/v15/anonymous_stats.rs b/src/rust/lqos_config/src/etc/v15/anonymous_stats.rs index 2fe73e90..ad145a2c 100644 --- a/src/rust/lqos_config/src/etc/v15/anonymous_stats.rs +++ b/src/rust/lqos_config/src/etc/v15/anonymous_stats.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UsageStats { /// Are we allowed to send stats at all? pub send_anonymous: bool, diff --git a/src/rust/lqos_config/src/etc/v15/bridge.rs b/src/rust/lqos_config/src/etc/v15/bridge.rs index 89618728..7ba82872 100644 --- a/src/rust/lqos_config/src/etc/v15/bridge.rs +++ b/src/rust/lqos_config/src/etc/v15/bridge.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; /// Represents a two-interface bridge configuration. -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BridgeConfig { /// Use the XDP-accelerated bridge? pub use_xdp_bridge: bool, @@ -27,7 +27,7 @@ impl Default for BridgeConfig { } } -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SingleInterfaceConfig { /// The name of the interface pub interface: String, diff --git a/src/rust/lqos_config/src/etc/v15/flows.rs b/src/rust/lqos_config/src/etc/v15/flows.rs index 8b518b47..2c584c7d 100644 --- a/src/rust/lqos_config/src/etc/v15/flows.rs +++ b/src/rust/lqos_config/src/etc/v15/flows.rs @@ -4,7 +4,7 @@ use serde::{Serialize, Deserialize}; -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct FlowConfig { pub flow_timeout_seconds: u64, pub netflow_enabled: bool, diff --git a/src/rust/lqos_config/src/etc/v15/influxdb.rs b/src/rust/lqos_config/src/etc/v15/influxdb.rs index eb7788e3..a0703348 100644 --- a/src/rust/lqos_config/src/etc/v15/influxdb.rs +++ b/src/rust/lqos_config/src/etc/v15/influxdb.rs @@ -1,6 +1,6 @@ use serde::{Serialize, Deserialize}; -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InfluxDbConfig { pub enable_influxdb: bool, pub url: String, diff --git a/src/rust/lqos_config/src/etc/v15/integration_common.rs b/src/rust/lqos_config/src/etc/v15/integration_common.rs index f0cc9f3d..c3a3257e 100644 --- a/src/rust/lqos_config/src/etc/v15/integration_common.rs +++ b/src/rust/lqos_config/src/etc/v15/integration_common.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct IntegrationConfig { /// Replace names with addresses? pub circuit_name_as_address: bool, diff --git a/src/rust/lqos_config/src/etc/v15/ip_ranges.rs b/src/rust/lqos_config/src/etc/v15/ip_ranges.rs index a8a681c5..4dbb40aa 100644 --- a/src/rust/lqos_config/src/etc/v15/ip_ranges.rs +++ b/src/rust/lqos_config/src/etc/v15/ip_ranges.rs @@ -1,6 +1,6 @@ use serde::{Serialize, Deserialize}; -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct IpRanges { pub ignore_subnets: Vec, pub allow_subnets: Vec, diff --git a/src/rust/lqos_config/src/etc/v15/long_term_stats.rs b/src/rust/lqos_config/src/etc/v15/long_term_stats.rs index 30724d09..3d4ba89b 100644 --- a/src/rust/lqos_config/src/etc/v15/long_term_stats.rs +++ b/src/rust/lqos_config/src/etc/v15/long_term_stats.rs @@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize}; -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct LongTermStats { /// Should we store long-term stats at all? pub gather_stats: bool, diff --git a/src/rust/lqos_config/src/etc/v15/powercode_integration.rs b/src/rust/lqos_config/src/etc/v15/powercode_integration.rs index 9f032cc4..ed52241f 100644 --- a/src/rust/lqos_config/src/etc/v15/powercode_integration.rs +++ b/src/rust/lqos_config/src/etc/v15/powercode_integration.rs @@ -1,6 +1,6 @@ use serde::{Serialize, Deserialize}; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct PowercodeIntegration { pub enable_powercode: bool, pub powercode_api_key: String, diff --git a/src/rust/lqos_config/src/etc/v15/queues.rs b/src/rust/lqos_config/src/etc/v15/queues.rs index e3560453..3df28710 100644 --- a/src/rust/lqos_config/src/etc/v15/queues.rs +++ b/src/rust/lqos_config/src/etc/v15/queues.rs @@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize}; -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct QueueConfig { /// Which SQM to use by default pub default_sqm: String, diff --git a/src/rust/lqos_config/src/etc/v15/sonar_integration.rs b/src/rust/lqos_config/src/etc/v15/sonar_integration.rs index 1dfb5ca4..c8415a63 100644 --- a/src/rust/lqos_config/src/etc/v15/sonar_integration.rs +++ b/src/rust/lqos_config/src/etc/v15/sonar_integration.rs @@ -1,6 +1,6 @@ use serde::{Serialize, Deserialize}; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct SonarIntegration { pub enable_sonar: bool, pub sonar_api_url: String, diff --git a/src/rust/lqos_config/src/etc/v15/spylnx_integration.rs b/src/rust/lqos_config/src/etc/v15/spylnx_integration.rs index 00c14e14..26fd17ae 100644 --- a/src/rust/lqos_config/src/etc/v15/spylnx_integration.rs +++ b/src/rust/lqos_config/src/etc/v15/spylnx_integration.rs @@ -1,6 +1,6 @@ use serde::{Serialize, Deserialize}; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct SplynxIntegration { pub enable_spylnx: bool, pub api_key: String, diff --git a/src/rust/lqos_config/src/etc/v15/top_config.rs b/src/rust/lqos_config/src/etc/v15/top_config.rs index f96f3d56..94cfdbe3 100644 --- a/src/rust/lqos_config/src/etc/v15/top_config.rs +++ b/src/rust/lqos_config/src/etc/v15/top_config.rs @@ -8,7 +8,7 @@ use sha2::Digest; use uuid::Uuid; /// Top-level configuration file for LibreQoS. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Config { /// Version number for the configuration file. /// This will be set to "1.5". Versioning will make diff --git a/src/rust/lqos_config/src/etc/v15/tuning.rs b/src/rust/lqos_config/src/etc/v15/tuning.rs index 9ca9bb00..b2e18756 100644 --- a/src/rust/lqos_config/src/etc/v15/tuning.rs +++ b/src/rust/lqos_config/src/etc/v15/tuning.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; /// Represents a set of `sysctl` and `ethtool` tweaks that may be /// applied (in place of the previous version's offload service) -#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Tunables { /// Should the `irq_balance` system service be stopped? pub stop_irq_balance: bool, diff --git a/src/rust/lqos_config/src/etc/v15/uisp_integration.rs b/src/rust/lqos_config/src/etc/v15/uisp_integration.rs index cda3bcde..2d653cc4 100644 --- a/src/rust/lqos_config/src/etc/v15/uisp_integration.rs +++ b/src/rust/lqos_config/src/etc/v15/uisp_integration.rs @@ -1,6 +1,6 @@ use serde::{Serialize, Deserialize}; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct UispIntegration { pub enable_uisp: bool, pub token: String, @@ -18,7 +18,7 @@ pub struct UispIntegration { pub use_ptmp_as_parent: bool, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct ExceptionCpe { pub cpe: String, pub parent: String, diff --git a/src/rust/lqos_config/src/lib.rs b/src/rust/lqos_config/src/lib.rs index eb905e9c..e7db6b9e 100644 --- a/src/rust/lqos_config/src/lib.rs +++ b/src/rust/lqos_config/src/lib.rs @@ -13,7 +13,7 @@ mod program_control; mod shaped_devices; pub use authentication::{UserRole, WebUsers}; -pub use etc::{load_config, Config, enable_long_term_stats, Tunables, BridgeConfig}; +pub use etc::{load_config, Config, enable_long_term_stats, Tunables, BridgeConfig, update_config}; pub use network_json::{NetworkJson, NetworkJsonNode, NetworkJsonTransport}; pub use program_control::load_libreqos; pub use shaped_devices::{ConfigShapedDevices, ShapedDevice}; diff --git a/src/rust/lqos_node_manager/src/config_control.rs b/src/rust/lqos_node_manager/src/config_control.rs index 5f25549d..9c3b2ac7 100644 --- a/src/rust/lqos_node_manager/src/config_control.rs +++ b/src/rust/lqos_node_manager/src/config_control.rs @@ -1,5 +1,6 @@ use crate::{auth_guard::AuthGuard, cache_control::NoCache}; use default_net::get_interfaces; +use reqwest::StatusCode; use lqos_bus::{bus_request, BusRequest, BusResponse}; use lqos_config::{Tunables, Config}; use rocket::{fs::NamedFile, serde::{json::Json, Serialize}}; @@ -39,6 +40,17 @@ pub async fn get_current_lqosd_config( NoCache::new(Json(config)) } +#[post("/api/update_config", data = "")] +pub async fn update_lqosd_config( + data: Json +) -> String { + let config: Config = (*data).clone(); + bus_request(vec![BusRequest::UpdateLqosdConfig(Box::new(config))]) + .await + .unwrap(); + "Ok".to_string() +} + #[post("/api/lqos_tuning/", data = "")] pub async fn update_lqos_tuning( auth: AuthGuard, diff --git a/src/rust/lqos_node_manager/src/main.rs b/src/rust/lqos_node_manager/src/main.rs index a7565bd9..b4a327f9 100644 --- a/src/rust/lqos_node_manager/src/main.rs +++ b/src/rust/lqos_node_manager/src/main.rs @@ -87,6 +87,7 @@ fn rocket() -> _ { config_control::get_current_lqosd_config, //config_control::update_python_config, config_control::update_lqos_tuning, + config_control::update_lqosd_config, auth_guard::create_first_user, auth_guard::login, auth_guard::admin_check, diff --git a/src/rust/lqos_node_manager/static/config.html b/src/rust/lqos_node_manager/static/config.html index 1290b400..09813e14 100644 --- a/src/rust/lqos_node_manager/static/config.html +++ b/src/rust/lqos_node_manager/static/config.html @@ -975,7 +975,18 @@ if (!validationResult.valid) return; updateSavedConfig(validationResult.changes); - console.log(lqosd_config); + $.ajax({ + type: "POST", + url: "/api/update_config", + data: JSON.stringify(lqosd_config), + success: (data) => { + if (data === "Ok") { + alert("Configuration saved"); + } else { + alert("Configuration not saved: " + data); + } + } + }) } function start() { diff --git a/src/rust/lqosd/src/main.rs b/src/rust/lqosd/src/main.rs index dcb9f19d..d705b964 100644 --- a/src/rust/lqosd/src/main.rs +++ b/src/rust/lqosd/src/main.rs @@ -178,6 +178,13 @@ fn handle_bus_requests( lqos_bus::BusResponse::Ack } BusRequest::UpdateLqosDTuning(..) => tuning::tune_lqosd_from_bus(req), + BusRequest::UpdateLqosdConfig(config) => { + let result = lqos_config::update_config(config); + if result.is_err() { + log::error!("Error updating config: {:?}", result); + } + BusResponse::Ack + }, #[cfg(feature = "equinix_tests")] BusRequest::RequestLqosEquinixTest => lqos_daht_test::lqos_daht_test(), BusRequest::ValidateShapedDevicesCsv => {