src/config/network.rs: implement network reload, set "changes" attribute
This commit is contained in:
parent
8a6b86b8a7
commit
2eefd9aee1
|
@ -27,7 +27,7 @@ use crate::api2::types::*;
|
|||
pub fn list_network_devices(
|
||||
_param: Value,
|
||||
_info: &ApiMethod,
|
||||
_rpcenv: &mut dyn RpcEnvironment,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<Value, Error> {
|
||||
|
||||
let (config, digest) = network::config()?;
|
||||
|
@ -41,6 +41,11 @@ pub fn list_network_devices(
|
|||
list.push(item);
|
||||
}
|
||||
|
||||
let diff = network::changes()?;
|
||||
if !diff.is_empty() {
|
||||
rpcenv.set_result_attrib("changes", diff.into());
|
||||
}
|
||||
|
||||
Ok(list.into())
|
||||
}
|
||||
|
||||
|
@ -312,6 +317,23 @@ pub fn delete_interface(name: String, digest: Option<String>) -> Result<(), Erro
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[api(
|
||||
access: {
|
||||
permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false),
|
||||
},
|
||||
)]
|
||||
/// Reload network configuration (requires ifupdown2).
|
||||
pub fn reload_network_config() -> Result<(), Error> {
|
||||
|
||||
network::assert_ifupdown2_installed()?;
|
||||
|
||||
let _ = std::fs::rename(network::NETWORK_INTERFACES_NEW_FILENAME, network::NETWORK_INTERFACES_FILENAME);
|
||||
|
||||
network::network_reload()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const ITEM_ROUTER: Router = Router::new()
|
||||
.get(&API_METHOD_READ_INTERFACE)
|
||||
.put(&API_METHOD_UPDATE_INTERFACE)
|
||||
|
@ -319,4 +341,5 @@ const ITEM_ROUTER: Router = Router::new()
|
|||
|
||||
pub const ROUTER: Router = Router::new()
|
||||
.get(&API_METHOD_LIST_NETWORK_DEVICES)
|
||||
.put(&API_METHOD_RELOAD_NETWORK_CONFIG)
|
||||
.match_all("name", &ITEM_ROUTER);
|
||||
|
|
|
@ -254,6 +254,12 @@ fn list_network_devices(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result
|
|||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if let Some(changes) = rpcenv.get_result_attrib("changes") {
|
||||
if let Some(diff) = changes.as_str() {
|
||||
eprintln!("pending changes:\n{}\n", diff);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_address(_value: &Value, record: &Value) -> Result<String, Error> {
|
||||
let mut text = String::new();
|
||||
|
||||
|
@ -300,15 +306,21 @@ fn network_commands() -> CommandLineInterface {
|
|||
|
||||
let cmd_def = CliCommandMap::new()
|
||||
.insert("list", CliCommand::new(&API_METHOD_LIST_NETWORK_DEVICES))
|
||||
.insert("update",
|
||||
CliCommand::new(&api2::config::network::API_METHOD_UPDATE_INTERFACE)
|
||||
.insert(
|
||||
"update",
|
||||
CliCommand::new(&api2::config::network::API_METHOD_UPDATE_INTERFACE)
|
||||
.arg_param(&["name"])
|
||||
.completion_cb("name", config::network::complete_interface_name)
|
||||
)
|
||||
.insert("remove",
|
||||
CliCommand::new(&api2::config::network::API_METHOD_DELETE_INTERFACE)
|
||||
.insert(
|
||||
"remove",
|
||||
CliCommand::new(&api2::config::network::API_METHOD_DELETE_INTERFACE)
|
||||
.arg_param(&["name"])
|
||||
.completion_cb("name", config::network::complete_interface_name)
|
||||
)
|
||||
.insert(
|
||||
"reload",
|
||||
CliCommand::new(&api2::config::network::API_METHOD_RELOAD_NETWORK_CONFIG)
|
||||
);
|
||||
|
||||
cmd_def.into()
|
||||
|
|
|
@ -387,6 +387,15 @@ pub fn config() -> Result<(NetworkConfig, [u8;32]), Error> {
|
|||
Ok((data, digest))
|
||||
}
|
||||
|
||||
pub fn changes() -> Result<String, Error> {
|
||||
|
||||
if !std::path::Path::new(NETWORK_INTERFACES_NEW_FILENAME).exists() {
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
||||
compute_file_diff(NETWORK_INTERFACES_FILENAME, NETWORK_INTERFACES_NEW_FILENAME)
|
||||
}
|
||||
|
||||
pub fn save_config(config: &NetworkConfig) -> Result<(), Error> {
|
||||
|
||||
let mut raw = Vec::new();
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::{Error, bail, format_err};
|
||||
|
@ -136,3 +138,59 @@ pub fn get_network_interfaces() -> Result<HashMap<String, bool>, Error> {
|
|||
|
||||
Ok(interface_list)
|
||||
}
|
||||
|
||||
pub fn compute_file_diff(filename: &str, shadow: &str) -> Result<String, Error> {
|
||||
|
||||
let output = Command::new("/usr/bin/diff")
|
||||
.arg("-b")
|
||||
.arg("-u")
|
||||
.arg(filename)
|
||||
.arg(shadow)
|
||||
.output()
|
||||
.map_err(|err| format_err!("failed to execute diff - {}", err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
match output.status.code() {
|
||||
Some(code) => {
|
||||
if code == 0 { return Ok(String::new()); }
|
||||
if code != 1 {
|
||||
let msg = String::from_utf8(output.stderr)
|
||||
.map(|m| if m.is_empty() { String::from("no error message") } else { m })
|
||||
.unwrap_or_else(|_| String::from("non utf8 error message (suppressed)"));
|
||||
|
||||
bail!("diff failed with status code: {} - {}", code, msg);
|
||||
}
|
||||
}
|
||||
None => bail!("diff terminated by signal"),
|
||||
}
|
||||
}
|
||||
|
||||
let diff = String::from_utf8(output.stdout)?;
|
||||
|
||||
Ok(diff)
|
||||
}
|
||||
|
||||
pub fn assert_ifupdown2_installed() -> Result<(), Error> {
|
||||
if !Path::new("/usr/share/ifupdown2").exists() {
|
||||
bail!("ifupdown2 is not installed.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn network_reload() -> Result<(), Error> {
|
||||
|
||||
let status = Command::new("/sbin/ifreload")
|
||||
.arg("-a")
|
||||
.status()
|
||||
.map_err(|err| format_err!("failed to execute ifreload: - {}", err))?;
|
||||
|
||||
if !status.success() {
|
||||
match status.code() {
|
||||
Some(code) => bail!("ifreload failed with status code: {}", code),
|
||||
None => bail!("ifreload terminated by signal")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue