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<()> {
|
pub fn validate_sysctl_tuning_runtime(&self) -> anyhow::Result<()> {
|
||||||
self.validate_config()?;
|
self.validate_config()?;
|
||||||
|
self.check_supported_host()?;
|
||||||
|
|
||||||
if !self.sysctl_tuning.enabled {
|
if !self.sysctl_tuning.enabled {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -742,7 +743,13 @@ swappiness = 1
|
|||||||
let mut cfg = base_config();
|
let mut cfg = base_config();
|
||||||
cfg.sysctl_tuning.enabled = false;
|
cfg.sysctl_tuning.enabled = false;
|
||||||
cfg.sysctl_tuning.tcp_bbr = "auto".into();
|
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]
|
#[test]
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ pub fn run(cfg: Config) -> anyhow::Result<()> {
|
|||||||
info!("starting daemon");
|
info!("starting daemon");
|
||||||
let state = Arc::new(Mutex::new(LastActionState::default()));
|
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) {
|
if let Err(e) = sysctl_tuning::ensure_sysctl_tuning(&cfg) {
|
||||||
set_err(&state, format!("startup network tuning reconcile: {e}"));
|
set_err(&state, format!("startup network tuning reconcile: {e}"));
|
||||||
append_event(&format!("startup_network_tuning error=\"{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};
|
use crate::{config::Config, lock::FileLock};
|
||||||
|
|
||||||
const SYSCTL_CONF_PATH: &str = "/etc/sysctl.d/90-awg2-harness-vpn.conf";
|
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";
|
const MANAGED_MARKER: &str = "# Managed by awg2-harness sysctl_tuning";
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -14,8 +15,11 @@ pub struct SysctlTuningStatus {
|
|||||||
pub apply_on_daemon_start: bool,
|
pub apply_on_daemon_start: bool,
|
||||||
pub tcp_bbr_mode: String,
|
pub tcp_bbr_mode: String,
|
||||||
pub conf_path: String,
|
pub conf_path: String,
|
||||||
|
pub modules_load_path: String,
|
||||||
pub persisted_present: bool,
|
pub persisted_present: bool,
|
||||||
pub persisted_matches: bool,
|
pub persisted_matches: bool,
|
||||||
|
pub modules_load_present: bool,
|
||||||
|
pub modules_load_matches: bool,
|
||||||
pub bbr_available: Option<bool>,
|
pub bbr_available: Option<bool>,
|
||||||
pub bbr_active: Option<bool>,
|
pub bbr_active: Option<bool>,
|
||||||
pub current: Vec<SysctlValue>,
|
pub current: Vec<SysctlValue>,
|
||||||
@@ -38,6 +42,7 @@ pub struct SysctlValue {
|
|||||||
struct RenderPlan {
|
struct RenderPlan {
|
||||||
desired: BTreeMap<String, String>,
|
desired: BTreeMap<String, String>,
|
||||||
persisted: String,
|
persisted: String,
|
||||||
|
modules_load_persisted: Option<String>,
|
||||||
bbr_available: Option<bool>,
|
bbr_available: Option<bool>,
|
||||||
bbr_active: Option<bool>,
|
bbr_active: Option<bool>,
|
||||||
warnings: Vec<String>,
|
warnings: Vec<String>,
|
||||||
@@ -45,7 +50,7 @@ struct RenderPlan {
|
|||||||
|
|
||||||
pub fn ensure_sysctl_tuning(cfg: &Config) -> anyhow::Result<()> {
|
pub fn ensure_sysctl_tuning(cfg: &Config) -> anyhow::Result<()> {
|
||||||
if !cfg.sysctl_tuning.enabled {
|
if !cfg.sysctl_tuning.enabled {
|
||||||
remove_managed_sysctl_file_if_disabled(cfg)?;
|
remove_managed_sysctl_files_if_disabled(cfg)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +63,9 @@ pub fn ensure_sysctl_tuning(cfg: &Config) -> anyhow::Result<()> {
|
|||||||
for w in &plan.warnings {
|
for w in &plan.warnings {
|
||||||
tracing::warn!(target: "sysctl_tuning", "{w}");
|
tracing::warn!(target: "sysctl_tuning", "{w}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reconcile_modules_load_file(&plan)?;
|
||||||
|
|
||||||
ensure_managed_or_absent(SYSCTL_CONF_PATH)?;
|
ensure_managed_or_absent(SYSCTL_CONF_PATH)?;
|
||||||
write_atomic(SYSCTL_CONF_PATH, &plan.persisted)?;
|
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,
|
apply_on_daemon_start: cfg.sysctl_tuning.apply_on_daemon_start,
|
||||||
tcp_bbr_mode: cfg.sysctl_tuning.tcp_bbr.clone(),
|
tcp_bbr_mode: cfg.sysctl_tuning.tcp_bbr.clone(),
|
||||||
conf_path: SYSCTL_CONF_PATH.to_string(),
|
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_present: Path::new(SYSCTL_CONF_PATH).exists(),
|
||||||
persisted_matches: false,
|
persisted_matches: false,
|
||||||
|
modules_load_present: Path::new(BBR_MODULES_LOAD_PATH).exists(),
|
||||||
|
modules_load_matches: false,
|
||||||
bbr_available: None,
|
bbr_available: None,
|
||||||
bbr_active: None,
|
bbr_active: None,
|
||||||
current: Vec::new(),
|
current: Vec::new(),
|
||||||
@@ -114,6 +125,14 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result<SysctlTuningStatus>
|
|||||||
.as_deref()
|
.as_deref()
|
||||||
.map(|v| normalize(v) == normalize(&plan.persisted))
|
.map(|v| normalize(v) == normalize(&plan.persisted))
|
||||||
.unwrap_or(false);
|
.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();
|
let mut values = Vec::<SysctlValue>::new();
|
||||||
for (k, v) in &plan.desired {
|
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());
|
drift_reason = Some("managed sysctl file is missing".to_string());
|
||||||
} else if !persisted_matches {
|
} else if !persisted_matches {
|
||||||
drift_reason = Some("persisted sysctl file differs from rendered config".to_string());
|
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
|
} else if let Some(v) = values
|
||||||
.iter()
|
.iter()
|
||||||
.find(|v| v.desired.as_deref() != v.current.as_deref())
|
.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
|
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 {
|
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,
|
apply_on_daemon_start: cfg.sysctl_tuning.apply_on_daemon_start,
|
||||||
tcp_bbr_mode: cfg.sysctl_tuning.tcp_bbr.clone(),
|
tcp_bbr_mode: cfg.sysctl_tuning.tcp_bbr.clone(),
|
||||||
conf_path: SYSCTL_CONF_PATH.to_string(),
|
conf_path: SYSCTL_CONF_PATH.to_string(),
|
||||||
|
modules_load_path: BBR_MODULES_LOAD_PATH.to_string(),
|
||||||
persisted_present,
|
persisted_present,
|
||||||
persisted_matches,
|
persisted_matches,
|
||||||
|
modules_load_present,
|
||||||
|
modules_load_matches,
|
||||||
bbr_available: plan.bbr_available,
|
bbr_available: plan.bbr_available,
|
||||||
bbr_active: plan.bbr_active,
|
bbr_active: plan.bbr_active,
|
||||||
current: values.clone(),
|
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!(" apply_on_daemon_start: {}", status.apply_on_daemon_start);
|
||||||
println!(" tcp_bbr_mode: {}", status.tcp_bbr_mode);
|
println!(" tcp_bbr_mode: {}", status.tcp_bbr_mode);
|
||||||
println!(" conf_path: {}", status.conf_path);
|
println!(" conf_path: {}", status.conf_path);
|
||||||
|
println!(" modules_load_path: {}", status.modules_load_path);
|
||||||
println!(
|
println!(
|
||||||
" persisted_present: {}",
|
" persisted_present: {}",
|
||||||
if status.persisted_present {
|
if status.persisted_present {
|
||||||
@@ -180,6 +235,22 @@ pub fn print_sysctl_tuning_status(status: &SysctlTuningStatus) {
|
|||||||
"no"
|
"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 {
|
if let Some(v) = status.bbr_available {
|
||||||
println!(" bbr_available: {}", if v { "yes" } else { "no" });
|
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 {
|
if cfg.sysctl_tuning.enabled {
|
||||||
return Ok(());
|
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<()> {
|
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 {
|
return Ok(RenderPlan {
|
||||||
desired,
|
desired,
|
||||||
persisted,
|
persisted,
|
||||||
|
modules_load_persisted: None,
|
||||||
bbr_available: None,
|
bbr_available: None,
|
||||||
bbr_active: None,
|
bbr_active: None,
|
||||||
warnings,
|
warnings,
|
||||||
@@ -318,9 +401,15 @@ fn build_render_plan(cfg: &Config, allow_modprobe: bool) -> anyhow::Result<Rende
|
|||||||
}
|
}
|
||||||
|
|
||||||
let persisted = render_persisted(&desired);
|
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 {
|
Ok(RenderPlan {
|
||||||
desired,
|
desired,
|
||||||
persisted,
|
persisted,
|
||||||
|
modules_load_persisted,
|
||||||
bbr_available: bbr_available_opt,
|
bbr_available: bbr_available_opt,
|
||||||
bbr_active,
|
bbr_active,
|
||||||
warnings,
|
warnings,
|
||||||
@@ -344,6 +433,17 @@ fn render_persisted(desired: &BTreeMap<String, String>) -> String {
|
|||||||
out
|
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> {
|
fn read_sysctl(key: &str) -> anyhow::Result<String> {
|
||||||
let out = Command::new("sysctl").args(["-n", key]).output()?;
|
let out = Command::new("sysctl").args(["-n", key]).output()?;
|
||||||
if !out.status.success() {
|
if !out.status.success() {
|
||||||
@@ -494,6 +594,13 @@ mod tests {
|
|||||||
assert!(!path.exists());
|
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]
|
#[test]
|
||||||
fn ensure_refuses_overwrite_unmanaged_file() {
|
fn ensure_refuses_overwrite_unmanaged_file() {
|
||||||
let path = std::env::temp_dir().join(format!(
|
let path = std::env::temp_dir().join(format!(
|
||||||
@@ -544,6 +651,7 @@ mod tests {
|
|||||||
let plan = build_render_plan(&c, false).expect("render plan");
|
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.ipv4.tcp_congestion_control"));
|
||||||
assert!(!plan.desired.contains_key("net.core.default_qdisc"));
|
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_available, None);
|
||||||
assert_eq!(plan.bbr_active, None);
|
assert_eq!(plan.bbr_active, None);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user