]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/node/network.rs
src/server/worker_task.rs: implement and use status command
[proxmox-backup.git] / src / api2 / node / network.rs
1 use anyhow::{Error, bail};
2 use serde_json::{Value, to_value};
3 use ::serde::{Deserialize, Serialize};
4
5 use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
6
7 use crate::config::network;
8 use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
9 use crate::api2::types::*;
10
11 #[api(
12 input: {
13 properties: {
14 node: {
15 schema: NODE_SCHEMA,
16 },
17 },
18 },
19 returns: {
20 description: "List network devices (with config digest).",
21 type: Array,
22 items: {
23 type: Interface,
24 },
25 },
26 access: {
27 permission: &Permission::Privilege(&["system", "network", "interfaces"], PRIV_SYS_AUDIT, false),
28 },
29 )]
30 /// List all datastores
31 pub fn list_network_devices(
32 _param: Value,
33 _info: &ApiMethod,
34 rpcenv: &mut dyn RpcEnvironment,
35 ) -> Result<Value, Error> {
36
37 let (config, digest) = network::config()?;
38 let digest = proxmox::tools::digest_to_hex(&digest);
39
40 let mut list = Vec::new();
41
42 for interface in config.interfaces.values() {
43 let mut item: Value = to_value(interface)?;
44 item["digest"] = digest.clone().into();
45 list.push(item);
46 }
47
48 let diff = network::changes()?;
49 if !diff.is_empty() {
50 rpcenv.set_result_attrib("changes", diff.into());
51 }
52
53 Ok(list.into())
54 }
55
56 #[api(
57 input: {
58 properties: {
59 node: {
60 schema: NODE_SCHEMA,
61 },
62 name: {
63 schema: NETWORK_INTERFACE_NAME_SCHEMA,
64 },
65 },
66 },
67 returns: {
68 description: "The network interface configuration (with config digest).",
69 type: Interface,
70 },
71 access: {
72 permission: &Permission::Privilege(&["system", "network", "interfaces", "{name}"], PRIV_SYS_AUDIT, false),
73 },
74 )]
75 /// Read a network interface configuration.
76 pub fn read_interface(name: String) -> Result<Value, Error> {
77
78 let (config, digest) = network::config()?;
79
80 let interface = config.lookup(&name)?;
81
82 let mut data: Value = to_value(interface)?;
83 data["digest"] = proxmox::tools::digest_to_hex(&digest).into();
84
85 Ok(data)
86 }
87
88 #[api()]
89 #[derive(Serialize, Deserialize)]
90 #[allow(non_camel_case_types)]
91 /// Deletable property name
92 pub enum DeletableProperty {
93 /// Delete the IPv4 address property.
94 address_v4,
95 /// Delete the IPv6 address property.
96 address_v6,
97 /// Delete the IPv4 gateway property.
98 gateway_v4,
99 /// Delete the IPv6 gateway property.
100 gateway_v6,
101 /// Delete the whole IPv4 configuration entry.
102 method_v4,
103 /// Delete the whole IPv6 configuration entry.
104 method_v6,
105 /// Delete IPv4 comments
106 comments_v4,
107 /// Delete IPv6 comments
108 comments_v6,
109 /// Delete mtu.
110 mtu,
111 /// Delete auto flag
112 auto,
113 /// Delete bridge ports (set to 'none')
114 bridge_ports,
115 /// Delete bond-slaves (set to 'none')
116 bond_slaves,
117 }
118
119
120 #[api(
121 protected: true,
122 input: {
123 properties: {
124 node: {
125 schema: NODE_SCHEMA,
126 },
127 name: {
128 schema: NETWORK_INTERFACE_NAME_SCHEMA,
129 },
130 auto: {
131 description: "Autostart interface.",
132 type: bool,
133 optional: true,
134 },
135 method_v4: {
136 type: NetworkConfigMethod,
137 optional: true,
138 },
139 method_v6: {
140 type: NetworkConfigMethod,
141 optional: true,
142 },
143 comments_v4: {
144 description: "Comments (inet, may span multiple lines)",
145 type: String,
146 optional: true,
147 },
148 comments_v6: {
149 description: "Comments (inet5, may span multiple lines)",
150 type: String,
151 optional: true,
152 },
153 address: {
154 schema: CIDR_SCHEMA,
155 optional: true,
156 },
157 gateway: {
158 schema: IP_SCHEMA,
159 optional: true,
160 },
161 mtu: {
162 description: "Maximum Transmission Unit.",
163 optional: true,
164 minimum: 46,
165 maximum: 65535,
166 default: 1500,
167 },
168 bridge_ports: {
169 schema: NETWORK_INTERFACE_LIST_SCHEMA,
170 optional: true,
171 },
172 bond_slaves: {
173 schema: NETWORK_INTERFACE_LIST_SCHEMA,
174 optional: true,
175 },
176 delete: {
177 description: "List of properties to delete.",
178 type: Array,
179 optional: true,
180 items: {
181 type: DeletableProperty,
182 }
183 },
184 digest: {
185 optional: true,
186 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
187 },
188 },
189 },
190 access: {
191 permission: &Permission::Privilege(&["system", "network", "interfaces", "{name}"], PRIV_SYS_MODIFY, false),
192 },
193 )]
194 /// Update network interface config.
195 pub fn update_interface(
196 name: String,
197 auto: Option<bool>,
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>,
204 mtu: Option<u64>,
205 bridge_ports: Option<Vec<String>>,
206 bond_slaves: Option<Vec<String>>,
207 delete: Option<Vec<DeletableProperty>>,
208 digest: Option<String>,
209 ) -> Result<(), Error> {
210
211 let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
212
213 let (mut config, expected_digest) = network::config()?;
214
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)?;
218 }
219
220 let current_gateway_v4 = config.interfaces.iter()
221 .find(|(_, interface)| interface.gateway_v4.is_some())
222 .map(|(name, _)| name.to_string());
223
224 let current_gateway_v6 = config.interfaces.iter()
225 .find(|(_, interface)| interface.gateway_v4.is_some())
226 .map(|(name, _)| name.to_string());
227
228 let interface = config.lookup_mut(&name)?;
229
230 if let Some(delete) = delete {
231 for delete_prop in delete {
232 match delete_prop {
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())?; }
245 }
246 }
247 }
248
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)?; }
255
256 if let Some(address) = address {
257 let (_, _, is_v6) = network::parse_cidr(&address)?;
258 if is_v6 {
259 interface.cidr_v6 = Some(address);
260 } else {
261 interface.cidr_v4 = Some(address);
262 }
263 }
264
265 if let Some(gateway) = gateway {
266 let is_v6 = gateway.contains(':');
267 if is_v6 {
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);
271 }
272 }
273 interface.gateway_v6 = Some(gateway);
274 } else {
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);
278 }
279 }
280 interface.gateway_v4 = Some(gateway);
281 }
282 }
283
284 if comments_v4.is_some() { interface.comments_v4 = comments_v4; }
285 if comments_v6.is_some() { interface.comments_v6 = comments_v6; }
286
287 network::save_config(&config)?;
288
289 Ok(())
290 }
291
292 #[api(
293 protected: true,
294 input: {
295 properties: {
296 node: {
297 schema: NODE_SCHEMA,
298 },
299 name: {
300 schema: NETWORK_INTERFACE_NAME_SCHEMA,
301 },
302 digest: {
303 optional: true,
304 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
305 },
306 },
307 },
308 access: {
309 permission: &Permission::Privilege(&["system", "network", "interfaces", "{name}"], PRIV_SYS_MODIFY, false),
310 },
311 )]
312 /// Remove network interface configuration.
313 pub fn delete_interface(name: String, digest: Option<String>) -> Result<(), Error> {
314
315 let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
316
317 let (mut config, expected_digest) = network::config()?;
318
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)?;
322 }
323
324 let _interface = config.lookup(&name)?; // check if interface exists
325
326 config.interfaces.remove(&name);
327
328 network::save_config(&config)?;
329
330 Ok(())
331 }
332
333 #[api(
334 input: {
335 properties: {
336 node: {
337 schema: NODE_SCHEMA,
338 },
339 },
340 },
341 access: {
342 permission: &Permission::Privilege(&["system", "network", "interfaces"], PRIV_SYS_MODIFY, false),
343 },
344 )]
345 /// Reload network configuration (requires ifupdown2).
346 pub fn reload_network_config() -> Result<(), Error> {
347
348 network::assert_ifupdown2_installed()?;
349
350 let _ = std::fs::rename(network::NETWORK_INTERFACES_NEW_FILENAME, network::NETWORK_INTERFACES_FILENAME);
351
352 network::network_reload()?;
353
354 Ok(())
355 }
356
357 #[api(
358 input: {
359 properties: {
360 node: {
361 schema: NODE_SCHEMA,
362 },
363 },
364 },
365 access: {
366 permission: &Permission::Privilege(&["system", "network", "interfaces"], PRIV_SYS_MODIFY, false),
367 },
368 )]
369 /// Revert network configuration (rm /etc/network/interfaces.new).
370 pub fn revert_network_config() -> Result<(), Error> {
371
372 let _ = std::fs::remove_file(network::NETWORK_INTERFACES_NEW_FILENAME);
373
374 Ok(())
375 }
376
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);
381
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);