diff --git a/mgmt/src/frr/frr_reload/mod.rs b/mgmt/src/frr/frr_reload/mod.rs new file mode 100644 index 00000000..72a8be56 --- /dev/null +++ b/mgmt/src/frr/frr_reload/mod.rs @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Open Network Fabric Authors + +//! Wrapper for the FRR reload utility (frr_reload.py). + +use std::process::{Command, Stdio}; +use tracing::info; + +#[allow(dead_code)] +pub fn reload_frr( + frr_reload_bin: &str, + frr_config_file: &str, +) -> Result<(), Box> { + frr_test_config(frr_reload_bin, frr_config_file)?; + frr_do_reload(frr_reload_bin, frr_config_file)?; + + info!("FRR successfully reloaded"); + Ok(()) +} + +fn frr_test_config( + frr_reload_bin: &str, + frr_config_file: &str, +) -> Result<(), Box> { + run_frr_reload_script(frr_reload_bin, frr_config_file, &["--test"]) +} + +fn frr_do_reload( + frr_reload_bin: &str, + frr_config_file: &str, +) -> Result<(), Box> { + run_frr_reload_script( + frr_reload_bin, + frr_config_file, + &["--reload", "--overwrite"], + ) +} + +fn run_frr_reload_script( + frr_reload_bin: &str, + frr_config_file: &str, + action_args: &[&str], +) -> Result<(), Box> { + let output = Command::new(frr_reload_bin) + .args(action_args) + .arg(frr_config_file) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .map_err(|e| format!("Failed to spawn command: {}", e))? + .wait_with_output() + .map_err(|e| format!("Failed to wait for command: {}", e))?; + + if !output.stderr.is_empty() { + return Err(format!( + "Command printed an error message. Command status: {}, stdout: {}, stderr: {}", + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) + .into()); + } + + if !output.status.success() { + return Err(format!( + "Command exited with non-zero status. Command status: {}, stdout: {}, stderr: {}", + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) + .into()); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + const FRR_CONFIG: &str = "/etc/frr/frr.conf"; + + fn binpath(name: &str) -> String { + let test_path: &str = "mgmt/src/processor/frr_reload_test"; + format!("{test_path}/{name}") + } + + #[test] + fn test_reload_frr() { + let bin = binpath("pass"); + let result = reload_frr(bin.as_str(), FRR_CONFIG); + assert!( + result.is_ok(), + "FRR reload test failed: {:?} (bin: {bin})", + result + ); + } + + #[test] + fn test_reload_frr_fail_errcode() { + let result = frr_do_reload(binpath("fail-errcode").as_str(), FRR_CONFIG); + assert!( + result.is_err(), + "FRR config test succeeded unexpectedly: {:?}", + result + ); + assert_eq!( + result.map_err(|e| e.to_string()), + Err("Command exited with non-zero status. Command status: exit status: 1, stdout: , stderr: " + .to_string()) + ); + } + + #[test] + fn test_reload_frr_fail_stderr() { + let result = frr_do_reload(binpath("fail-stderr").as_str(), FRR_CONFIG); + assert!( + result.is_err(), + "FRR cofig test succeeded unexpectedly: {:?}", + result + ); + assert_eq!( + result.map_err(|e| e.to_string()), + Err("Command printed an error message. Command status: exit status: 0, stdout: , stderr: failure\n" + .to_string()) + ); + } + + #[test] + fn test_reload_frr_errcode_stderr() { + let result = frr_do_reload(binpath("fail-errcode-stderr").as_str(), FRR_CONFIG); + assert!( + result.is_err(), + "FRR cofig test succeeded unexpectedly: {:?}", + result + ); + assert_eq!( + result.map_err(|e| e.to_string()), + Err("Command printed an error message. Command status: exit status: 1, stdout: , stderr: failure\n" + .to_string()) + ); + } +} diff --git a/mgmt/src/frr/frr_reload/test/fail-errcode b/mgmt/src/frr/frr_reload/test/fail-errcode new file mode 100755 index 00000000..9b8cad1d Binary files /dev/null and b/mgmt/src/frr/frr_reload/test/fail-errcode differ diff --git a/mgmt/src/frr/frr_reload/test/fail-errcode-stderr b/mgmt/src/frr/frr_reload/test/fail-errcode-stderr new file mode 100755 index 00000000..7f673d25 Binary files /dev/null and b/mgmt/src/frr/frr_reload/test/fail-errcode-stderr differ diff --git a/mgmt/src/frr/frr_reload/test/fail-errcode-stderr.c b/mgmt/src/frr/frr_reload/test/fail-errcode-stderr.c new file mode 100644 index 00000000..2b0c3f3a --- /dev/null +++ b/mgmt/src/frr/frr_reload/test/fail-errcode-stderr.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + fprintf(stderr, "failure\n"); + return 1; +} diff --git a/mgmt/src/frr/frr_reload/test/fail-errcode.c b/mgmt/src/frr/frr_reload/test/fail-errcode.c new file mode 100644 index 00000000..5015b2b5 --- /dev/null +++ b/mgmt/src/frr/frr_reload/test/fail-errcode.c @@ -0,0 +1,3 @@ +int main(void) { + return 1; +} diff --git a/mgmt/src/frr/frr_reload/test/fail-stderr b/mgmt/src/frr/frr_reload/test/fail-stderr new file mode 100755 index 00000000..d039e6c0 Binary files /dev/null and b/mgmt/src/frr/frr_reload/test/fail-stderr differ diff --git a/mgmt/src/frr/frr_reload/test/fail-stderr.c b/mgmt/src/frr/frr_reload/test/fail-stderr.c new file mode 100644 index 00000000..ccb6668c --- /dev/null +++ b/mgmt/src/frr/frr_reload/test/fail-stderr.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + fprintf(stderr, "failure\n"); + return 0; +} diff --git a/mgmt/src/frr/frr_reload/test/pass b/mgmt/src/frr/frr_reload/test/pass new file mode 100755 index 00000000..2f8f5b38 Binary files /dev/null and b/mgmt/src/frr/frr_reload/test/pass differ diff --git a/mgmt/src/frr/frr_reload/test/pass.c b/mgmt/src/frr/frr_reload/test/pass.c new file mode 100644 index 00000000..a4928b5f --- /dev/null +++ b/mgmt/src/frr/frr_reload/test/pass.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + fprintf(stdout, "success\n"); + return 0; +} diff --git a/mgmt/src/frr/mod.rs b/mgmt/src/frr/mod.rs index f771a8c8..ce5d8672 100644 --- a/mgmt/src/frr/mod.rs +++ b/mgmt/src/frr/mod.rs @@ -4,4 +4,6 @@ //! FRR drivers: the logic to drive FRR. //! Currently only one driver exists, leveraging frr-reload.py. +pub mod frr_reload; pub mod renderer; + diff --git a/mgmt/src/processor/mod.rs b/mgmt/src/processor/mod.rs index 60483cc7..1aa9e05b 100644 --- a/mgmt/src/processor/mod.rs +++ b/mgmt/src/processor/mod.rs @@ -8,3 +8,4 @@ mod confbuild; mod namegen; pub mod proc; mod tests; +