]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/config/network.rs
src/api2/config/network.rs: implement update/delete comments
[proxmox-backup.git] / src / api2 / config / 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 },
15 returns: {
16 description: "List network devices (with config digest).",
17 type: Array,
18 items: {
19 type: Interface,
20 },
21 },
22 access: {
23 permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
24 },
25 )]
26 /// List all datastores
27 pub fn list_network_devices(
28 _param: Value,
29 _info: &ApiMethod,
30 _rpcenv: &mut dyn RpcEnvironment,
31 ) -> Result<Value, Error> {
32
33 let (config, digest) = network::config()?;
34 let digest = proxmox::tools::digest_to_hex(&digest);
35
36 let mut list = Vec::new();
37
38 for interface in config.interfaces.values() {
39 let mut item: Value = to_value(interface)?;
40 item["digest"] = digest.clone().into();
41 list.push(item);
42 }
43
44 Ok(list.into())
45 }
46
47 #[api(
48 input: {
49 properties: {
50 name: {
51 schema: NETWORK_INTERFACE_NAME_SCHEMA,
52 },
53 },
54 },
55 returns: {
56 description: "The network interface configuration (with config digest).",
57 type: Interface,
58 },
59 access: {
60 permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
61 },
62 )]
63 /// Read a network interface configuration.
64 pub fn read_interface(name: String) -> Result<Value, Error> {
65
66 let (config, digest) = network::config()?;
67
68 let interface = config.lookup(&name)?;
69
70 let mut data: Value = to_value(interface)?;
71 data["digest"] = proxmox::tools::digest_to_hex(&digest).into();
72
73 Ok(data)
74 }
75
76 #[api()]
77 #[derive(Serialize, Deserialize)]
78 #[allow(non_camel_case_types)]
79 /// Deletable property name
80 pub enum DeletableProperty {
81 /// Delete the IPv4 address property.
82 address_v4,
83 /// Delete the IPv6 address property.
84 address_v6,
85 /// Delete the IPv4 gateway property.
86 gateway_v4,
87 /// Delete the IPv6 gateway property.
88 gateway_v6,
89 /// Delete the whole IPv4 configuration entry.
90 method_v4,
91 /// Delete the whole IPv6 configuration entry.
92 method_v6,
93 /// Delete IPv4 comments
94 comments_v4,
95 /// Delete IPv6 comments
96 comments_v6,
97 /// Delete mtu.
98 mtu,
99 /// Delete auto flag
100 auto,
101 /// Delete bridge ports (set to 'none')
102 bridge_ports,
103 /// Delete bond-slaves (set to 'none')
104 bond_slaves,
105 }
106
107
108 #[api(
109 protected: true,
110 input: {
111 properties: {
112 name: {
113 schema: NETWORK_INTERFACE_NAME_SCHEMA,
114 },
115 auto: {
116 description: "Autostart interface.",
117 type: bool,
118 optional: true,
119 },
120 method_v4: {
121 type: NetworkConfigMethod,
122 optional: true,
123 },
124 method_v6: {
125 type: NetworkConfigMethod,
126 optional: true,
127 },
128 comments_v4: {
129 description: "Comments (inet)",
130 type: String,
131 optional: true,
132 },
133 comments_v6: {
134 description: "Comments (inet6)",
135 type: String,
136 optional: true,
137 },
138 address: {
139 schema: CIDR_SCHEMA,
140 optional: true,
141 },
142 gateway: {
143 schema: IP_SCHEMA,
144 optional: true,
145 },
146 mtu: {
147 description: "Maximum Transmission Unit.",
148 optional: true,
149 minimum: 46,
150 maximum: 65535,
151 default: 1500,
152 },
153 bridge_ports: {
154 schema: NETWORK_INTERFACE_LIST_SCHEMA,
155 optional: true,
156 },
157 bond_slaves: {
158 schema: NETWORK_INTERFACE_LIST_SCHEMA,
159 optional: true,
160 },
161 delete: {
162 description: "List of properties to delete.",
163 type: Array,
164 optional: true,
165 items: {
166 type: DeletableProperty,
167 }
168 },
169 digest: {
170 optional: true,
171 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
172 },
173 },
174 },
175 access: {
176 permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false),
177 },
178 )]
179 /// Update network interface config.
180 pub fn update_interface(
181 name: String,
182 auto: Option<bool>,
183 method_v4: Option<NetworkConfigMethod>,
184 method_v6: Option<NetworkConfigMethod>,
185 comments_v4: Option<String>,
186 comments_v6: Option<String>,
187 address: Option<String>,
188 gateway: Option<String>,
189 mtu: Option<u64>,
190 bridge_ports: Option<Vec<String>>,
191 bond_slaves: Option<Vec<String>>,
192 delete: Option<Vec<DeletableProperty>>,
193 digest: Option<String>,
194 ) -> Result<(), Error> {
195
196 let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
197
198 let (mut config, expected_digest) = network::config()?;
199
200 if let Some(ref digest) = digest {
201 let digest = proxmox::tools::hex_to_digest(digest)?;
202 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
203 }
204
205 let current_gateway_v4 = config.interfaces.iter()
206 .find(|(_, interface)| interface.gateway_v4.is_some())
207 .map(|(name, _)| name.to_string());
208
209 let current_gateway_v6 = config.interfaces.iter()
210 .find(|(_, interface)| interface.gateway_v4.is_some())
211 .map(|(name, _)| name.to_string());
212
213 let interface = config.lookup_mut(&name)?;
214
215 if let Some(delete) = delete {
216 for delete_prop in delete {
217 match delete_prop {
218 DeletableProperty::address_v4 => { interface.cidr_v4 = None; },
219 DeletableProperty::address_v6 => { interface.cidr_v6 = None; },
220 DeletableProperty::gateway_v4 => { interface.gateway_v4 = None; },
221 DeletableProperty::gateway_v6 => { interface.gateway_v6 = None; },
222 DeletableProperty::method_v4 => { interface.method_v4 = None; },
223 DeletableProperty::method_v6 => { interface.method_v6 = None; },
224 DeletableProperty::comments_v4 => { interface.comments_v4 = Vec::new(); },
225 DeletableProperty::comments_v6 => { interface.comments_v6 = Vec::new(); },
226 DeletableProperty::mtu => { interface.mtu = None; },
227 DeletableProperty::auto => { interface.auto = false; },
228 DeletableProperty::bridge_ports => { interface.set_bridge_ports(Vec::new())?; }
229 DeletableProperty::bond_slaves => { interface.set_bond_slaves(Vec::new())?; }
230 }
231 }
232 }
233
234 if let Some(auto) = auto { interface.auto = auto; }
235 if method_v4.is_some() { interface.method_v4 = method_v4; }
236 if method_v6.is_some() { interface.method_v6 = method_v6; }
237 if mtu.is_some() { interface.mtu = mtu; }
238 if let Some(ports) = bridge_ports { interface.set_bridge_ports(ports)?; }
239 if let Some(slaves) = bond_slaves { interface.set_bond_slaves(slaves)?; }
240
241 if let Some(address) = address {
242 let (_, _, is_v6) = network::parse_cidr(&address)?;
243 if is_v6 {
244 interface.cidr_v6 = Some(address);
245 } else {
246 interface.cidr_v4 = Some(address);
247 }
248 }
249
250 if let Some(gateway) = gateway {
251 let is_v6 = gateway.contains(':');
252 if is_v6 {
253 if let Some(current_gateway_v6) = current_gateway_v6 {
254 if current_gateway_v6 != name {
255 bail!("Default IPv6 gateway already exists on interface '{}'", current_gateway_v6);
256 }
257 }
258 interface.gateway_v6 = Some(gateway);
259 } else {
260 if let Some(current_gateway_v4) = current_gateway_v4 {
261 if current_gateway_v4 != name {
262 bail!("Default IPv4 gateway already exists on interface '{}'", current_gateway_v4);
263 }
264 }
265 interface.gateway_v4 = Some(gateway);
266 }
267 }
268
269 if let Some(comments) = comments_v4 {
270 interface.comments_v4 = comments.lines().map(String::from).collect();
271 }
272
273 if let Some(comments) = comments_v6 {
274 interface.comments_v6 = comments.lines().map(String::from).collect();
275 }
276
277 network::save_config(&config)?;
278
279 Ok(())
280 }
281
282 #[api(
283 protected: true,
284 input: {
285 properties: {
286 name: {
287 schema: NETWORK_INTERFACE_NAME_SCHEMA,
288 },
289 digest: {
290 optional: true,
291 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
292 },
293 },
294 },
295 access: {
296 permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false),
297 },
298 )]
299 /// Remove network interface configuration.
300 pub fn delete_interface(name: String, digest: Option<String>) -> Result<(), Error> {
301
302 let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
303
304 let (mut config, expected_digest) = network::config()?;
305
306 if let Some(ref digest) = digest {
307 let digest = proxmox::tools::hex_to_digest(digest)?;
308 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
309 }
310
311 let _interface = config.lookup(&name)?; // check if interface exists
312
313 config.interfaces.remove(&name);
314
315 network::save_config(&config)?;
316
317 Ok(())
318 }
319
320 const ITEM_ROUTER: Router = Router::new()
321 .get(&API_METHOD_READ_INTERFACE)
322 .put(&API_METHOD_UPDATE_INTERFACE)
323 .delete(&API_METHOD_DELETE_INTERFACE);
324
325 pub const ROUTER: Router = Router::new()
326 .get(&API_METHOD_LIST_NETWORK_DEVICES)
327 .match_all("name", &ITEM_ROUTER);