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(
|
pub fn list_network_devices(
|
||||||
_param: Value,
|
_param: Value,
|
||||||
_info: &ApiMethod,
|
_info: &ApiMethod,
|
||||||
_rpcenv: &mut dyn RpcEnvironment,
|
rpcenv: &mut dyn RpcEnvironment,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
|
|
||||||
let (config, digest) = network::config()?;
|
let (config, digest) = network::config()?;
|
||||||
|
@ -41,6 +41,11 @@ pub fn list_network_devices(
|
||||||
list.push(item);
|
list.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let diff = network::changes()?;
|
||||||
|
if !diff.is_empty() {
|
||||||
|
rpcenv.set_result_attrib("changes", diff.into());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(list.into())
|
Ok(list.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,6 +317,23 @@ pub fn delete_interface(name: String, digest: Option<String>) -> Result<(), Erro
|
||||||
Ok(())
|
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()
|
const ITEM_ROUTER: Router = Router::new()
|
||||||
.get(&API_METHOD_READ_INTERFACE)
|
.get(&API_METHOD_READ_INTERFACE)
|
||||||
.put(&API_METHOD_UPDATE_INTERFACE)
|
.put(&API_METHOD_UPDATE_INTERFACE)
|
||||||
|
@ -319,4 +341,5 @@ const ITEM_ROUTER: Router = Router::new()
|
||||||
|
|
||||||
pub const ROUTER: Router = Router::new()
|
pub const ROUTER: Router = Router::new()
|
||||||
.get(&API_METHOD_LIST_NETWORK_DEVICES)
|
.get(&API_METHOD_LIST_NETWORK_DEVICES)
|
||||||
|
.put(&API_METHOD_RELOAD_NETWORK_CONFIG)
|
||||||
.match_all("name", &ITEM_ROUTER);
|
.match_all("name", &ITEM_ROUTER);
|
||||||
|
|
|
@ -254,6 +254,12 @@ fn list_network_devices(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result
|
||||||
_ => unreachable!(),
|
_ => 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> {
|
fn render_address(_value: &Value, record: &Value) -> Result<String, Error> {
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
|
|
||||||
|
@ -300,15 +306,21 @@ fn network_commands() -> CommandLineInterface {
|
||||||
|
|
||||||
let cmd_def = CliCommandMap::new()
|
let cmd_def = CliCommandMap::new()
|
||||||
.insert("list", CliCommand::new(&API_METHOD_LIST_NETWORK_DEVICES))
|
.insert("list", CliCommand::new(&API_METHOD_LIST_NETWORK_DEVICES))
|
||||||
.insert("update",
|
.insert(
|
||||||
|
"update",
|
||||||
CliCommand::new(&api2::config::network::API_METHOD_UPDATE_INTERFACE)
|
CliCommand::new(&api2::config::network::API_METHOD_UPDATE_INTERFACE)
|
||||||
.arg_param(&["name"])
|
.arg_param(&["name"])
|
||||||
.completion_cb("name", config::network::complete_interface_name)
|
.completion_cb("name", config::network::complete_interface_name)
|
||||||
)
|
)
|
||||||
.insert("remove",
|
.insert(
|
||||||
|
"remove",
|
||||||
CliCommand::new(&api2::config::network::API_METHOD_DELETE_INTERFACE)
|
CliCommand::new(&api2::config::network::API_METHOD_DELETE_INTERFACE)
|
||||||
.arg_param(&["name"])
|
.arg_param(&["name"])
|
||||||
.completion_cb("name", config::network::complete_interface_name)
|
.completion_cb("name", config::network::complete_interface_name)
|
||||||
|
)
|
||||||
|
.insert(
|
||||||
|
"reload",
|
||||||
|
CliCommand::new(&api2::config::network::API_METHOD_RELOAD_NETWORK_CONFIG)
|
||||||
);
|
);
|
||||||
|
|
||||||
cmd_def.into()
|
cmd_def.into()
|
||||||
|
|
|
@ -387,6 +387,15 @@ pub fn config() -> Result<(NetworkConfig, [u8;32]), Error> {
|
||||||
Ok((data, digest))
|
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> {
|
pub fn save_config(config: &NetworkConfig) -> Result<(), Error> {
|
||||||
|
|
||||||
let mut raw = Vec::new();
|
let mut raw = Vec::new();
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::{Error, bail, format_err};
|
use anyhow::{Error, bail, format_err};
|
||||||
|
@ -136,3 +138,59 @@ pub fn get_network_interfaces() -> Result<HashMap<String, bool>, Error> {
|
||||||
|
|
||||||
Ok(interface_list)
|
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