src/api2/config/network.rs: improve network api
This commit is contained in:
		@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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![],
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user