fix(sysctl_tuning): complete fix36 production reconcile and bbr modules-load lifecycle
This commit is contained in:
@@ -289,6 +289,7 @@ impl Config {
|
||||
|
||||
pub fn validate_sysctl_tuning_runtime(&self) -> anyhow::Result<()> {
|
||||
self.validate_config()?;
|
||||
self.check_supported_host()?;
|
||||
|
||||
if !self.sysctl_tuning.enabled {
|
||||
return Ok(());
|
||||
@@ -742,7 +743,13 @@ swappiness = 1
|
||||
let mut cfg = base_config();
|
||||
cfg.sysctl_tuning.enabled = false;
|
||||
cfg.sysctl_tuning.tcp_bbr = "auto".into();
|
||||
assert!(cfg.validate_sysctl_tuning_runtime().is_ok());
|
||||
if let Err(e) = cfg.validate_sysctl_tuning_runtime() {
|
||||
let msg = e.to_string();
|
||||
assert!(
|
||||
!msg.contains("modprobe"),
|
||||
"validation must not require modprobe when sysctl_tuning is disabled, got error: {msg}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -28,7 +28,7 @@ pub fn run(cfg: Config) -> anyhow::Result<()> {
|
||||
info!("starting daemon");
|
||||
let state = Arc::new(Mutex::new(LastActionState::default()));
|
||||
|
||||
if cfg.sysctl_tuning.enabled && cfg.sysctl_tuning.apply_on_daemon_start {
|
||||
if cfg.sysctl_tuning.apply_on_daemon_start {
|
||||
if let Err(e) = sysctl_tuning::ensure_sysctl_tuning(&cfg) {
|
||||
set_err(&state, format!("startup network tuning reconcile: {e}"));
|
||||
append_event(&format!("startup_network_tuning error=\"{e}\""));
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::os::unix::fs::PermissionsExt;
|
||||
use crate::{config::Config, lock::FileLock};
|
||||
|
||||
const SYSCTL_CONF_PATH: &str = "/etc/sysctl.d/90-awg2-harness-vpn.conf";
|
||||
const BBR_MODULES_LOAD_PATH: &str = "/etc/modules-load.d/90-awg2-harness-bbr.conf";
|
||||
const MANAGED_MARKER: &str = "# Managed by awg2-harness sysctl_tuning";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -14,8 +15,11 @@ pub struct SysctlTuningStatus {
|
||||
pub apply_on_daemon_start: bool,
|
||||
pub tcp_bbr_mode: String,
|
||||
pub conf_path: String,
|
||||
pub modules_load_path: String,
|
||||
pub persisted_present: bool,
|
||||
pub persisted_matches: bool,
|
||||
pub modules_load_present: bool,
|
||||
pub modules_load_matches: bool,
|
||||
pub bbr_available: Option<bool>,
|
||||
pub bbr_active: Option<bool>,
|
||||
pub current: Vec<SysctlValue>,
|
||||
@@ -38,6 +42,7 @@ pub struct SysctlValue {
|
||||
struct RenderPlan {
|
||||
desired: BTreeMap<String, String>,
|
||||
persisted: String,
|
||||
modules_load_persisted: Option<String>,
|
||||
bbr_available: Option<bool>,
|
||||
bbr_active: Option<bool>,
|
||||
warnings: Vec<String>,
|
||||
@@ -45,7 +50,7 @@ struct RenderPlan {
|
||||
|
||||
pub fn ensure_sysctl_tuning(cfg: &Config) -> anyhow::Result<()> {
|
||||
if !cfg.sysctl_tuning.enabled {
|
||||
remove_managed_sysctl_file_if_disabled(cfg)?;
|
||||
remove_managed_sysctl_files_if_disabled(cfg)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -58,6 +63,9 @@ pub fn ensure_sysctl_tuning(cfg: &Config) -> anyhow::Result<()> {
|
||||
for w in &plan.warnings {
|
||||
tracing::warn!(target: "sysctl_tuning", "{w}");
|
||||
}
|
||||
|
||||
reconcile_modules_load_file(&plan)?;
|
||||
|
||||
ensure_managed_or_absent(SYSCTL_CONF_PATH)?;
|
||||
write_atomic(SYSCTL_CONF_PATH, &plan.persisted)?;
|
||||
|
||||
@@ -97,8 +105,11 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result<SysctlTuningStatus>
|
||||
apply_on_daemon_start: cfg.sysctl_tuning.apply_on_daemon_start,
|
||||
tcp_bbr_mode: cfg.sysctl_tuning.tcp_bbr.clone(),
|
||||
conf_path: SYSCTL_CONF_PATH.to_string(),
|
||||
modules_load_path: BBR_MODULES_LOAD_PATH.to_string(),
|
||||
persisted_present: Path::new(SYSCTL_CONF_PATH).exists(),
|
||||
persisted_matches: false,
|
||||
modules_load_present: Path::new(BBR_MODULES_LOAD_PATH).exists(),
|
||||
modules_load_matches: false,
|
||||
bbr_available: None,
|
||||
bbr_active: None,
|
||||
current: Vec::new(),
|
||||
@@ -114,6 +125,14 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result<SysctlTuningStatus>
|
||||
.as_deref()
|
||||
.map(|v| normalize(v) == normalize(&plan.persisted))
|
||||
.unwrap_or(false);
|
||||
let modules_load_raw = fs::read_to_string(BBR_MODULES_LOAD_PATH).ok();
|
||||
let modules_load_present = modules_load_raw.is_some();
|
||||
let modules_load_expected = plan.modules_load_persisted.as_deref();
|
||||
let modules_load_matches = match (modules_load_raw.as_deref(), modules_load_expected) {
|
||||
(Some(actual), Some(expected)) => normalize(actual) == normalize(expected),
|
||||
(None, None) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let mut values = Vec::<SysctlValue>::new();
|
||||
for (k, v) in &plan.desired {
|
||||
@@ -131,6 +150,9 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result<SysctlTuningStatus>
|
||||
drift_reason = Some("managed sysctl file is missing".to_string());
|
||||
} else if !persisted_matches {
|
||||
drift_reason = Some("persisted sysctl file differs from rendered config".to_string());
|
||||
} else if !modules_load_matches {
|
||||
drift_reason =
|
||||
Some("persisted modules-load file differs from rendered config".to_string());
|
||||
} else if let Some(v) = values
|
||||
.iter()
|
||||
.find(|v| v.desired.as_deref() != v.current.as_deref())
|
||||
@@ -140,6 +162,35 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result<SysctlTuningStatus>
|
||||
v.key, v.current, v.desired
|
||||
));
|
||||
}
|
||||
} else if persisted_present {
|
||||
let managed = persisted_raw
|
||||
.as_deref()
|
||||
.and_then(|raw| raw.lines().next())
|
||||
.map(|first| first.contains(MANAGED_MARKER))
|
||||
.unwrap_or(false);
|
||||
|
||||
if managed {
|
||||
drift_reason =
|
||||
Some("managed sysctl file exists while sysctl_tuning is disabled".to_string());
|
||||
} else {
|
||||
drift_reason =
|
||||
Some("unmanaged sysctl file exists at awg2-harness managed path".to_string());
|
||||
}
|
||||
} else if modules_load_present {
|
||||
let managed = modules_load_raw
|
||||
.as_deref()
|
||||
.and_then(|raw| raw.lines().next())
|
||||
.map(|first| first.contains(MANAGED_MARKER))
|
||||
.unwrap_or(false);
|
||||
|
||||
if managed {
|
||||
drift_reason = Some(
|
||||
"managed modules-load file exists while sysctl_tuning is disabled".to_string(),
|
||||
);
|
||||
} else {
|
||||
drift_reason =
|
||||
Some("unmanaged modules-load file exists at awg2-harness managed path".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SysctlTuningStatus {
|
||||
@@ -147,8 +198,11 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result<SysctlTuningStatus>
|
||||
apply_on_daemon_start: cfg.sysctl_tuning.apply_on_daemon_start,
|
||||
tcp_bbr_mode: cfg.sysctl_tuning.tcp_bbr.clone(),
|
||||
conf_path: SYSCTL_CONF_PATH.to_string(),
|
||||
modules_load_path: BBR_MODULES_LOAD_PATH.to_string(),
|
||||
persisted_present,
|
||||
persisted_matches,
|
||||
modules_load_present,
|
||||
modules_load_matches,
|
||||
bbr_available: plan.bbr_available,
|
||||
bbr_active: plan.bbr_active,
|
||||
current: values.clone(),
|
||||
@@ -164,6 +218,7 @@ pub fn print_sysctl_tuning_status(status: &SysctlTuningStatus) {
|
||||
println!(" apply_on_daemon_start: {}", status.apply_on_daemon_start);
|
||||
println!(" tcp_bbr_mode: {}", status.tcp_bbr_mode);
|
||||
println!(" conf_path: {}", status.conf_path);
|
||||
println!(" modules_load_path: {}", status.modules_load_path);
|
||||
println!(
|
||||
" persisted_present: {}",
|
||||
if status.persisted_present {
|
||||
@@ -180,6 +235,22 @@ pub fn print_sysctl_tuning_status(status: &SysctlTuningStatus) {
|
||||
"no"
|
||||
}
|
||||
);
|
||||
println!(
|
||||
" modules_load_present: {}",
|
||||
if status.modules_load_present {
|
||||
"yes"
|
||||
} else {
|
||||
"no"
|
||||
}
|
||||
);
|
||||
println!(
|
||||
" modules_load_matches: {}",
|
||||
if status.modules_load_matches {
|
||||
"yes"
|
||||
} else {
|
||||
"no"
|
||||
}
|
||||
);
|
||||
if let Some(v) = status.bbr_available {
|
||||
println!(" bbr_available: {}", if v { "yes" } else { "no" });
|
||||
}
|
||||
@@ -207,12 +278,23 @@ pub fn print_sysctl_tuning_status(status: &SysctlTuningStatus) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_managed_sysctl_file_if_disabled(cfg: &Config) -> anyhow::Result<()> {
|
||||
pub fn remove_managed_sysctl_files_if_disabled(cfg: &Config) -> anyhow::Result<()> {
|
||||
if cfg.sysctl_tuning.enabled {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
remove_managed_sysctl_file_at(Path::new(SYSCTL_CONF_PATH))
|
||||
remove_managed_sysctl_file_at(Path::new(SYSCTL_CONF_PATH))?;
|
||||
remove_managed_sysctl_file_at(Path::new(BBR_MODULES_LOAD_PATH))
|
||||
}
|
||||
|
||||
fn reconcile_modules_load_file(plan: &RenderPlan) -> anyhow::Result<()> {
|
||||
if let Some(content) = &plan.modules_load_persisted {
|
||||
ensure_managed_or_absent(BBR_MODULES_LOAD_PATH)?;
|
||||
write_atomic(BBR_MODULES_LOAD_PATH, content)?;
|
||||
} else {
|
||||
remove_managed_sysctl_file_at(Path::new(BBR_MODULES_LOAD_PATH))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_managed_sysctl_file_at(path: &Path) -> anyhow::Result<()> {
|
||||
@@ -256,6 +338,7 @@ fn build_render_plan(cfg: &Config, allow_modprobe: bool) -> anyhow::Result<Rende
|
||||
return Ok(RenderPlan {
|
||||
desired,
|
||||
persisted,
|
||||
modules_load_persisted: None,
|
||||
bbr_available: None,
|
||||
bbr_active: None,
|
||||
warnings,
|
||||
@@ -318,9 +401,15 @@ fn build_render_plan(cfg: &Config, allow_modprobe: bool) -> anyhow::Result<Rende
|
||||
}
|
||||
|
||||
let persisted = render_persisted(&desired);
|
||||
let modules_load_persisted = if bbr_active == Some(true) {
|
||||
Some(render_modules_load_persisted("tcp_bbr"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(RenderPlan {
|
||||
desired,
|
||||
persisted,
|
||||
modules_load_persisted,
|
||||
bbr_available: bbr_available_opt,
|
||||
bbr_active,
|
||||
warnings,
|
||||
@@ -344,6 +433,17 @@ fn render_persisted(desired: &BTreeMap<String, String>) -> String {
|
||||
out
|
||||
}
|
||||
|
||||
fn render_modules_load_persisted(module: &str) -> String {
|
||||
let mut out = String::new();
|
||||
out.push_str(MANAGED_MARKER);
|
||||
out.push('\n');
|
||||
out.push_str("# Source: /etc/awg2-harness/config.toml\n");
|
||||
out.push_str("# Do not edit this file manually; use [sysctl_tuning].\n\n");
|
||||
out.push_str(module);
|
||||
out.push('\n');
|
||||
out
|
||||
}
|
||||
|
||||
fn read_sysctl(key: &str) -> anyhow::Result<String> {
|
||||
let out = Command::new("sysctl").args(["-n", key]).output()?;
|
||||
if !out.status.success() {
|
||||
@@ -494,6 +594,13 @@ mod tests {
|
||||
assert!(!path.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_modules_load_contains_marker_and_module() {
|
||||
let s = render_modules_load_persisted("tcp_bbr");
|
||||
assert!(s.starts_with(MANAGED_MARKER));
|
||||
assert!(s.contains("tcp_bbr"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ensure_refuses_overwrite_unmanaged_file() {
|
||||
let path = std::env::temp_dir().join(format!(
|
||||
@@ -544,6 +651,7 @@ mod tests {
|
||||
let plan = build_render_plan(&c, false).expect("render plan");
|
||||
assert!(!plan.desired.contains_key("net.ipv4.tcp_congestion_control"));
|
||||
assert!(!plan.desired.contains_key("net.core.default_qdisc"));
|
||||
assert_eq!(plan.modules_load_persisted, None);
|
||||
assert_eq!(plan.bbr_available, None);
|
||||
assert_eq!(plan.bbr_active, None);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user