mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2026-10-24 22:07:40 -05:00
changes
This commit is contained in:
1 parent
2a4478de34
commit
db56ab135f
5 files changed
+130
-34
No files matched your search
@@ -10771,6 +10771,10 @@ mod tests {
|
||||
let dir = std::env::temp_dir().join(format!("lqos-bakery-{name}-{ts}"));
|
||||
std::fs::create_dir_all(&dir).expect("temp runtime dir");
|
||||
cfg.lqos_directory = dir.display().to_string();
|
||||
// Keep Bakery qdisc-handle state under the per-test runtime directory so
|
||||
// these allocator tests do not share `/opt/libreqos/state` across
|
||||
// parallel test threads.
|
||||
cfg.state_directory = None;
|
||||
Arc::new(cfg)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
@@ -24,10 +26,12 @@ pub struct ConfigBuilder {
|
||||
pub mbps_to_network: u64,
|
||||
pub allow_subnets: Vec<String>,
|
||||
pub node_name: String,
|
||||
pub config_load_error: Option<String>,
|
||||
}
|
||||
|
||||
impl ConfigBuilder {
|
||||
pub fn new() -> Self {
|
||||
let config_path = current_config_path();
|
||||
if let Ok(cfg) = lqos_config::load_config() {
|
||||
let mut to_internet = String::new();
|
||||
let mut to_network = String::new();
|
||||
@@ -61,8 +65,10 @@ impl ConfigBuilder {
|
||||
mbps_to_network: cfg.queues.uplink_bandwidth_mbps,
|
||||
allow_subnets: cfg.ip_ranges.allow_subnets.clone(),
|
||||
node_name: cfg.node_name.clone(),
|
||||
config_load_error: None,
|
||||
}
|
||||
} else {
|
||||
let config_load_error = existing_config_load_error_for_path(&config_path);
|
||||
// Default configuration if no config is loaded
|
||||
ConfigBuilder {
|
||||
bridge_mode: BridgeMode::Linux,
|
||||
@@ -79,14 +85,75 @@ impl ConfigBuilder {
|
||||
"192.168.0.0/16".to_string(),
|
||||
],
|
||||
node_name: "LibreQoS".to_string(),
|
||||
config_load_error,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_config_path() -> PathBuf {
|
||||
std::env::var_os("LQOS_CONFIG")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| PathBuf::from("/etc/lqos.conf"))
|
||||
}
|
||||
|
||||
fn existing_config_load_error_for_path(config_path: &Path) -> Option<String> {
|
||||
config_path.exists().then(|| {
|
||||
format!(
|
||||
"Existing LibreQoS configuration at {} could not be loaded. Fix or replace it before setup can continue.",
|
||||
config_path.display()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn existing_config_load_error() -> Option<String> {
|
||||
existing_config_load_error_for_path(¤t_config_path())
|
||||
}
|
||||
|
||||
pub fn existing_config_uses_xdp() -> bool {
|
||||
lqos_config::load_config()
|
||||
.ok()
|
||||
.and_then(|config| config.bridge.as_ref().map(|bridge| bridge.use_xdp_bridge))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ConfigBuilder;
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
use std::fs;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
static CONFIG_ENV_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
||||
|
||||
#[test]
|
||||
fn invalid_existing_config_sets_blocking_load_error() {
|
||||
let _guard = CONFIG_ENV_LOCK.lock();
|
||||
let unique = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("clock before epoch")
|
||||
.as_nanos();
|
||||
let path = std::env::temp_dir().join(format!(
|
||||
"libreqos-setup-invalid-config-{}-{unique}.toml",
|
||||
std::process::id()
|
||||
));
|
||||
fs::write(&path, "not valid toml = [\n").expect("write invalid config");
|
||||
let old_lqos_config = std::env::var_os("LQOS_CONFIG");
|
||||
unsafe {
|
||||
std::env::set_var("LQOS_CONFIG", &path);
|
||||
}
|
||||
lqos_config::clear_cached_config();
|
||||
|
||||
let builder = ConfigBuilder::new();
|
||||
|
||||
assert!(builder.config_load_error.is_some());
|
||||
|
||||
match old_lqos_config {
|
||||
Some(value) => unsafe { std::env::set_var("LQOS_CONFIG", value) },
|
||||
None => unsafe { std::env::remove_var("LQOS_CONFIG") },
|
||||
}
|
||||
lqos_config::clear_cached_config();
|
||||
fs::remove_file(path).expect("remove temp config");
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ use lqos_setup::{bootstrap, hotfix};
|
||||
use std::path::Path;
|
||||
|
||||
use bandwidth::bandwidth_view;
|
||||
use config_builder::CURRENT_CONFIG;
|
||||
use config_builder::{CURRENT_CONFIG, existing_config_load_error};
|
||||
use cursive::{
|
||||
Rect, Vec2, View,
|
||||
view::{Margins, Nameable, Resizable, Scrollable},
|
||||
@@ -353,26 +353,11 @@ fn finalize(ui: &mut cursive::Cursive) {
|
||||
}
|
||||
}
|
||||
|
||||
// If we cannot load the config but a file exists, warn the user that
|
||||
// continuing will replace it after saving a backup.
|
||||
let config_path = Path::new("/etc/lqos.conf");
|
||||
let load_result = lqos_config::load_config();
|
||||
if load_result.is_err() && config_path.exists() {
|
||||
let msg = "An existing configuration file was found at /etc/lqos.conf,\n\
|
||||
but it could not be read or parsed.\n\n\
|
||||
Press Continue to replace it with a new configuration using defaults.\n\
|
||||
LibreQoS will first try to save a backup to /etc/lqos.conf.setupbackup.\n\n\
|
||||
Press Cancel to exit and investigate."
|
||||
.to_string();
|
||||
|
||||
if let Some(message) = existing_config_load_error() {
|
||||
ui.add_layer(
|
||||
Dialog::around(TextView::new(msg))
|
||||
Dialog::around(TextView::new(message))
|
||||
.title("Existing Config Unreadable")
|
||||
.button("Continue", |s| {
|
||||
s.pop_layer();
|
||||
continue_finalize(s);
|
||||
})
|
||||
.button("Cancel", |s| {
|
||||
.button("OK", |s| {
|
||||
s.pop_layer();
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Shared setup commit/apply helpers used by both Cursive and the setup WebUI.
|
||||
|
||||
use crate::config_builder::{BridgeMode, CURRENT_CONFIG};
|
||||
use crate::config_builder::{BridgeMode, CURRENT_CONFIG, existing_config_load_error};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use lqos_netplan_helper::protocol::{ApplyMode, ApplyRequest};
|
||||
use lqos_netplan_helper::transaction::{
|
||||
@@ -11,7 +11,6 @@ use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write as _;
|
||||
use std::path::Path;
|
||||
|
||||
static PENDING_CHILDREN: Lazy<Mutex<PendingChildren>> =
|
||||
Lazy::new(|| Mutex::new(PendingChildren::default()));
|
||||
@@ -236,20 +235,51 @@ fn load_existing_or_default(event_log: &mut Vec<String>) -> Result<lqos_config::
|
||||
return Ok((*config).clone());
|
||||
}
|
||||
|
||||
let config_path = Path::new("/etc/lqos.conf");
|
||||
if config_path.exists() {
|
||||
let backup_path = "/etc/lqos.conf.setupbackup";
|
||||
std::fs::copy(config_path, backup_path).with_context(|| {
|
||||
format!(
|
||||
"Existing /etc/lqos.conf could not be loaded and could not be backed up to {backup_path}"
|
||||
)
|
||||
})?;
|
||||
event_log.push(format!(
|
||||
"Existing /etc/lqos.conf could not be loaded. Backup saved to {backup_path}."
|
||||
));
|
||||
} else {
|
||||
event_log.push("Creating new configuration".to_string());
|
||||
if let Some(message) = existing_config_load_error() {
|
||||
bail!("{message}");
|
||||
}
|
||||
|
||||
event_log.push("Creating new configuration".to_string());
|
||||
Ok(lqos_config::Config::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::load_existing_or_default;
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
use std::fs;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
static CONFIG_ENV_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
||||
|
||||
#[test]
|
||||
fn existing_unreadable_config_blocks_default_fallback() {
|
||||
let _guard = CONFIG_ENV_LOCK.lock();
|
||||
let unique = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("clock before epoch")
|
||||
.as_nanos();
|
||||
let path = std::env::temp_dir().join(format!(
|
||||
"libreqos-setup-actions-invalid-config-{}-{unique}.toml",
|
||||
std::process::id()
|
||||
));
|
||||
fs::write(&path, "not valid toml = [\n").expect("write invalid config");
|
||||
let old_lqos_config = std::env::var_os("LQOS_CONFIG");
|
||||
unsafe {
|
||||
std::env::set_var("LQOS_CONFIG", &path);
|
||||
}
|
||||
lqos_config::clear_cached_config();
|
||||
|
||||
let error = load_existing_or_default(&mut Vec::new()).expect_err("should block");
|
||||
|
||||
assert!(error.to_string().contains("could not be loaded"));
|
||||
|
||||
match old_lqos_config {
|
||||
Some(value) => unsafe { std::env::set_var("LQOS_CONFIG", value) },
|
||||
None => unsafe { std::env::remove_var("LQOS_CONFIG") },
|
||||
}
|
||||
lqos_config::clear_cached_config();
|
||||
fs::remove_file(path).expect("remove temp config");
|
||||
}
|
||||
}
|
||||
@@ -183,6 +183,9 @@ async fn save_setup(Form(form): Form<SaveForm>) -> Response {
|
||||
"Create the first admin account before saving setup settings.",
|
||||
);
|
||||
}
|
||||
if let Some(message) = CURRENT_CONFIG.lock().config_load_error.clone() {
|
||||
return error_response(StatusCode::BAD_REQUEST, &message);
|
||||
}
|
||||
if let Err(err) = apply_form_to_current_config(&form) {
|
||||
return error_response(StatusCode::BAD_REQUEST, &err.to_string());
|
||||
}
|
||||
@@ -313,6 +316,13 @@ fn render_setup_page(
|
||||
);
|
||||
html.push_str("<label>Confirm Password<input type=\"password\" name=\"confirm_password\" required></label>");
|
||||
html.push_str("<button type=\"submit\">Create Admin</button></form></section>");
|
||||
} else if let Some(config_error) = config.config_load_error.as_ref() {
|
||||
html.push_str("<section class=\"card\"><h2>Existing Config Needs Repair</h2>");
|
||||
html.push_str(&format!(
|
||||
"<p class=\"notice\">{}</p>",
|
||||
escape_html(config_error)
|
||||
));
|
||||
html.push_str("<p class=\"muted\">LibreQoS will not save setup changes until the existing configuration file can be loaded again.</p></section>");
|
||||
} else if !snapshot.hotfix_required {
|
||||
html.push_str("<section class=\"card\"><h2>Core Setup</h2>");
|
||||
html.push_str("<form id=\"coreSetupForm\" method=\"post\" action=\"/setup/save\" onsubmit=\"return validateAndSubmitSetup(this);\">");
|
||||
|
||||
Reference in new issue
Block a user