src/api2/config/network.rs: improve network api
This commit is contained in:
parent
e2d940b949
commit
df6bb03d0e
|
@ -5,8 +5,8 @@ use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
|
||||||
|
|
||||||
//use crate::api2::types::*;
|
//use crate::api2::types::*;
|
||||||
use crate::config::network;
|
use crate::config::network;
|
||||||
use crate::config::acl::{PRIV_SYS_AUDIT};
|
use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
|
||||||
use crate::api2::types::Interface;
|
use crate::api2::types::*;
|
||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
|
@ -44,5 +44,145 @@ pub fn list_network_devices(
|
||||||
Ok(list.into())
|
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()
|
pub const ROUTER: Router = Router::new()
|
||||||
.get(&API_METHOD_LIST_NETWORK_DEVICES);
|
.get(&API_METHOD_LIST_NETWORK_DEVICES)
|
||||||
|
.match_all("name", &ITEM_ROUTER);
|
||||||
|
|
|
@ -236,7 +236,17 @@ fn acl_commands() -> CommandLineInterface {
|
||||||
fn network_commands() -> CommandLineInterface {
|
fn network_commands() -> CommandLineInterface {
|
||||||
|
|
||||||
let cmd_def = CliCommandMap::new()
|
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()
|
cmd_def.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::io::{Write};
|
use std::io::{Write};
|
||||||
use std::collections::{HashSet, HashMap};
|
use std::collections::{HashSet, HashMap};
|
||||||
|
|
||||||
use anyhow::{Error, bail};
|
use anyhow::{Error, format_err, bail};
|
||||||
|
|
||||||
use proxmox::tools::{fs::replace_file, fs::CreateOptions};
|
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> {
|
pub fn write_config(&self, w: &mut dyn Write) -> Result<(), Error> {
|
||||||
|
|
||||||
let mut done = HashSet::new();
|
let mut done = HashSet::new();
|
||||||
|
@ -267,3 +281,11 @@ pub fn save_config(config: &NetworkConfig) -> Result<(), Error> {
|
||||||
|
|
||||||
Ok(())
|
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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue