src/api2/config/network.rs: improve network api

This commit is contained in:
Dietmar Maurer 2020-04-22 10:54:07 +02:00
parent e2d940b949
commit df6bb03d0e
3 changed files with 177 additions and 5 deletions

View File

@ -5,8 +5,8 @@ use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
//use crate::api2::types::*;
use crate::config::network;
use crate::config::acl::{PRIV_SYS_AUDIT};
use crate::api2::types::Interface;
use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
use crate::api2::types::*;
#[api(
input: {
@ -44,5 +44,145 @@ pub fn list_network_devices(
Ok(list.into())
}
#[api(
input: {
properties: {
name: {
schema: NETWORK_INTERFACE_NAME_SCHEMA,
},
},
},
returns: {
description: "The network interface configuration (with config digest).",
type: Interface,
},
access: {
permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
},
)]
/// Read a network interface configuration.
pub fn read_interface(name: String) -> Result<Value, Error> {
let (config, digest) = network::config()?;
let interface = config.lookup(&name)?;
let mut data: Value = to_value(interface)?;
data["digest"] = proxmox::tools::digest_to_hex(&digest).into();
Ok(data)
}
#[api(
protected: true,
input: {
properties: {
name: {
schema: NETWORK_INTERFACE_NAME_SCHEMA,
},
address: {
schema: CIDR_SCHEMA,
optional: true,
},
gateway: {
schema: IP_SCHEMA,
optional: true,
},
digest: {
optional: true,
schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
},
},
},
access: {
permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false),
},
)]
/// Update network interface config.
pub fn update_interface(
name: String,
address: Option<String>,
gateway: Option<String>,
digest: Option<String>,
) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, expected_digest) = network::config()?;
if let Some(ref digest) = digest {
let digest = proxmox::tools::hex_to_digest(digest)?;
crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
}
let interface = config.lookup_mut(&name)?;
if let Some(address) = address {
let (_, _, is_v6) = network::parse_cidr(&address)?;
if is_v6 {
interface.cidr_v6 = Some(address);
} else {
interface.cidr_v4 = Some(address);
}
}
if let Some(gateway) = gateway {
let is_v6 = gateway.contains(':');
if is_v6 {
interface.gateway_v6 = Some(gateway);
} else {
interface.gateway_v4 = Some(gateway);
}
}
network::save_config(&config)?;
Ok(())
}
#[api(
protected: true,
input: {
properties: {
name: {
schema: NETWORK_INTERFACE_NAME_SCHEMA,
},
digest: {
optional: true,
schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
},
},
},
access: {
permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false),
},
)]
/// Remove network interface configuration.
pub fn delete_interface(name: String, digest: Option<String>) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, expected_digest) = network::config()?;
if let Some(ref digest) = digest {
let digest = proxmox::tools::hex_to_digest(digest)?;
crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
}
let _interface = config.lookup(&name)?; // check if interface exists
config.interfaces.remove(&name);
network::save_config(&config)?;
Ok(())
}
const ITEM_ROUTER: Router = Router::new()
.get(&API_METHOD_READ_INTERFACE)
.put(&API_METHOD_UPDATE_INTERFACE)
.delete(&API_METHOD_DELETE_INTERFACE);
pub const ROUTER: Router = Router::new()
.get(&API_METHOD_LIST_NETWORK_DEVICES);
.get(&API_METHOD_LIST_NETWORK_DEVICES)
.match_all("name", &ITEM_ROUTER);

View File

@ -236,7 +236,17 @@ fn acl_commands() -> CommandLineInterface {
fn network_commands() -> CommandLineInterface {
let cmd_def = CliCommandMap::new()
.insert("list", CliCommand::new(&api2::config::network::API_METHOD_LIST_NETWORK_DEVICES));
.insert("list", CliCommand::new(&api2::config::network::API_METHOD_LIST_NETWORK_DEVICES))
.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)
.arg_param(&["name"])
.completion_cb("name", config::network::complete_interface_name)
);
cmd_def.into()
}

View File

@ -1,7 +1,7 @@
use std::io::{Write};
use std::collections::{HashSet, HashMap};
use anyhow::{Error, bail};
use anyhow::{Error, format_err, bail};
use proxmox::tools::{fs::replace_file, fs::CreateOptions};
@ -184,6 +184,20 @@ impl NetworkConfig {
}
}
pub fn lookup(&self, name: &str) -> Result<&Interface, Error> {
let interface = self.interfaces.get(name).ok_or_else(|| {
format_err!("interface '{}' does not exist.", name)
})?;
Ok(interface)
}
pub fn lookup_mut(&mut self, name: &str) -> Result<&mut Interface, Error> {
let interface = self.interfaces.get_mut(name).ok_or_else(|| {
format_err!("interface '{}' does not exist.", name)
})?;
Ok(interface)
}
pub fn write_config(&self, w: &mut dyn Write) -> Result<(), Error> {
let mut done = HashSet::new();
@ -267,3 +281,11 @@ pub fn save_config(config: &NetworkConfig) -> Result<(), Error> {
Ok(())
}
// shell completion helper
pub fn complete_interface_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
match config() {
Ok((data, _digest)) => data.interfaces.keys().map(|id| id.to_string()).collect(),
Err(_) => return vec![],
}
}