1 use anyhow
::{Error, bail}
;
2 use serde_json
::{Value, to_value}
;
3 use ::serde
::{Deserialize, Serialize}
;
5 use proxmox
::api
::{api, ApiMethod, Router, RpcEnvironment, Permission}
;
7 use crate::config
::network
;
8 use crate::config
::acl
::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}
;
9 use crate::api2
::types
::*;
20 description
: "List network devices (with config digest).",
27 permission
: &Permission
::Privilege(&["system", "network", "interfaces"], PRIV_SYS_AUDIT
, false),
30 /// List all datastores
31 pub fn list_network_devices(
34 rpcenv
: &mut dyn RpcEnvironment
,
35 ) -> Result
<Value
, Error
> {
37 let (config
, digest
) = network
::config()?
;
38 let digest
= proxmox
::tools
::digest_to_hex(&digest
);
40 let mut list
= Vec
::new();
42 for interface
in config
.interfaces
.values() {
43 let mut item
: Value
= to_value(interface
)?
;
44 item
["digest"] = digest
.clone().into();
48 let diff
= network
::changes()?
;
50 rpcenv
.set_result_attrib("changes", diff
.into());
63 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
68 description
: "The network interface configuration (with config digest).",
72 permission
: &Permission
::Privilege(&["system", "network", "interfaces", "{name}"], PRIV_SYS_AUDIT
, false),
75 /// Read a network interface configuration.
76 pub fn read_interface(name
: String
) -> Result
<Value
, Error
> {
78 let (config
, digest
) = network
::config()?
;
80 let interface
= config
.lookup(&name
)?
;
82 let mut data
: Value
= to_value(interface
)?
;
83 data
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
89 #[derive(Serialize, Deserialize)]
90 #[allow(non_camel_case_types)]
91 /// Deletable property name
92 pub enum DeletableProperty
{
93 /// Delete the IPv4 address property.
95 /// Delete the IPv6 address property.
97 /// Delete the IPv4 gateway property.
99 /// Delete the IPv6 gateway property.
101 /// Delete the whole IPv4 configuration entry.
103 /// Delete the whole IPv6 configuration entry.
105 /// Delete IPv4 comments
107 /// Delete IPv6 comments
113 /// Delete bridge ports (set to 'none')
115 /// Delete bond-slaves (set to 'none')
128 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
131 description
: "Autostart interface.",
136 type: NetworkConfigMethod
,
140 type: NetworkConfigMethod
,
144 description
: "Comments (inet, may span multiple lines)",
149 description
: "Comments (inet5, may span multiple lines)",
162 description
: "Maximum Transmission Unit.",
169 schema
: NETWORK_INTERFACE_LIST_SCHEMA
,
173 schema
: NETWORK_INTERFACE_LIST_SCHEMA
,
177 description
: "List of properties to delete.",
181 type: DeletableProperty
,
186 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
191 permission
: &Permission
::Privilege(&["system", "network", "interfaces", "{name}"], PRIV_SYS_MODIFY
, false),
194 /// Update network interface config.
195 pub fn update_interface(
198 method_v4
: Option
<NetworkConfigMethod
>,
199 method_v6
: Option
<NetworkConfigMethod
>,
200 comments_v4
: Option
<String
>,
201 comments_v6
: Option
<String
>,
202 address
: Option
<String
>,
203 gateway
: Option
<String
>,
205 bridge_ports
: Option
<Vec
<String
>>,
206 bond_slaves
: Option
<Vec
<String
>>,
207 delete
: Option
<Vec
<DeletableProperty
>>,
208 digest
: Option
<String
>,
209 ) -> Result
<(), Error
> {
211 let _lock
= crate::tools
::open_file_locked(network
::NETWORK_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
213 let (mut config
, expected_digest
) = network
::config()?
;
215 if let Some(ref digest
) = digest
{
216 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
217 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
220 let current_gateway_v4
= config
.interfaces
.iter()
221 .find(|(_
, interface
)| interface
.gateway_v4
.is_some())
222 .map(|(name
, _
)| name
.to_string());
224 let current_gateway_v6
= config
.interfaces
.iter()
225 .find(|(_
, interface
)| interface
.gateway_v4
.is_some())
226 .map(|(name
, _
)| name
.to_string());
228 let interface
= config
.lookup_mut(&name
)?
;
230 if let Some(delete
) = delete
{
231 for delete_prop
in delete
{
233 DeletableProperty
::address_v4
=> { interface.cidr_v4 = None; }
,
234 DeletableProperty
::address_v6
=> { interface.cidr_v6 = None; }
,
235 DeletableProperty
::gateway_v4
=> { interface.gateway_v4 = None; }
,
236 DeletableProperty
::gateway_v6
=> { interface.gateway_v6 = None; }
,
237 DeletableProperty
::method_v4
=> { interface.method_v4 = None; }
,
238 DeletableProperty
::method_v6
=> { interface.method_v6 = None; }
,
239 DeletableProperty
::comments_v4
=> { interface.comments_v4 = None; }
,
240 DeletableProperty
::comments_v6
=> { interface.comments_v6 = None; }
,
241 DeletableProperty
::mtu
=> { interface.mtu = None; }
,
242 DeletableProperty
::auto => { interface.auto = false; }
,
243 DeletableProperty
::bridge_ports
=> { interface.set_bridge_ports(Vec::new())?; }
244 DeletableProperty
::bond_slaves
=> { interface.set_bond_slaves(Vec::new())?; }
249 if let Some(auto) = auto { interface.auto = auto; }
250 if method_v4
.is_some() { interface.method_v4 = method_v4; }
251 if method_v6
.is_some() { interface.method_v6 = method_v6; }
252 if mtu
.is_some() { interface.mtu = mtu; }
253 if let Some(ports
) = bridge_ports { interface.set_bridge_ports(ports)?; }
254 if let Some(slaves
) = bond_slaves { interface.set_bond_slaves(slaves)?; }
256 if let Some(address
) = address
{
257 let (_
, _
, is_v6
) = network
::parse_cidr(&address
)?
;
259 interface
.cidr_v6
= Some(address
);
261 interface
.cidr_v4
= Some(address
);
265 if let Some(gateway
) = gateway
{
266 let is_v6
= gateway
.contains('
:'
);
268 if let Some(current_gateway_v6
) = current_gateway_v6
{
269 if current_gateway_v6
!= name
{
270 bail
!("Default IPv6 gateway already exists on interface '{}'", current_gateway_v6
);
273 interface
.gateway_v6
= Some(gateway
);
275 if let Some(current_gateway_v4
) = current_gateway_v4
{
276 if current_gateway_v4
!= name
{
277 bail
!("Default IPv4 gateway already exists on interface '{}'", current_gateway_v4
);
280 interface
.gateway_v4
= Some(gateway
);
284 if comments_v4
.is_some() { interface.comments_v4 = comments_v4; }
285 if comments_v6
.is_some() { interface.comments_v6 = comments_v6; }
287 network
::save_config(&config
)?
;
300 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
304 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
309 permission
: &Permission
::Privilege(&["system", "network", "interfaces", "{name}"], PRIV_SYS_MODIFY
, false),
312 /// Remove network interface configuration.
313 pub fn delete_interface(name
: String
, digest
: Option
<String
>) -> Result
<(), Error
> {
315 let _lock
= crate::tools
::open_file_locked(network
::NETWORK_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
317 let (mut config
, expected_digest
) = network
::config()?
;
319 if let Some(ref digest
) = digest
{
320 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
321 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
324 let _interface
= config
.lookup(&name
)?
; // check if interface exists
326 config
.interfaces
.remove(&name
);
328 network
::save_config(&config
)?
;
342 permission
: &Permission
::Privilege(&["system", "network", "interfaces"], PRIV_SYS_MODIFY
, false),
345 /// Reload network configuration (requires ifupdown2).
346 pub fn reload_network_config() -> Result
<(), Error
> {
348 network
::assert_ifupdown2_installed()?
;
350 let _
= std
::fs
::rename(network
::NETWORK_INTERFACES_NEW_FILENAME
, network
::NETWORK_INTERFACES_FILENAME
);
352 network
::network_reload()?
;
366 permission
: &Permission
::Privilege(&["system", "network", "interfaces"], PRIV_SYS_MODIFY
, false),
369 /// Revert network configuration (rm /etc/network/interfaces.new).
370 pub fn revert_network_config() -> Result
<(), Error
> {
372 let _
= std
::fs
::remove_file(network
::NETWORK_INTERFACES_NEW_FILENAME
);
377 const ITEM_ROUTER
: Router
= Router
::new()
378 .get(&API_METHOD_READ_INTERFACE
)
379 .put(&API_METHOD_UPDATE_INTERFACE
)
380 .delete(&API_METHOD_DELETE_INTERFACE
);
382 pub const ROUTER
: Router
= Router
::new()
383 .get(&API_METHOD_LIST_NETWORK_DEVICES
)
384 .put(&API_METHOD_RELOAD_NETWORK_CONFIG
)
385 .delete(&API_METHOD_REVERT_NETWORK_CONFIG
)
386 .match_all("name", &ITEM_ROUTER
);