]> git.proxmox.com Git - proxmox-backup.git/blame - src/bin/proxmox_backup_manager/disk.rs
move acl to pbs_config workspaces, pbs_api_types cleanups
[proxmox-backup.git] / src / bin / proxmox_backup_manager / disk.rs
CommitLineData
929a13b3 1use anyhow::{bail, Error};
8e40aa63
DM
2use serde_json::Value;
3
4use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler};
5
8cc3760e
DM
6use pbs_api_types::{
7 DISK_LIST_SCHEMA, ZFS_ASHIFT_SCHEMA, ZfsRaidLevel, ZfsCompressionType,
8 BLOCKDEVICE_NAME_SCHEMA, DATASTORE_SCHEMA,
9};
929a13b3 10use proxmox_backup::tools::disks::{
1aef491e 11 FileSystemType,
929a13b3
DM
12 SmartAttribute,
13 complete_disk_name,
14};
15
8cc3760e 16use proxmox_backup::api2;
8e40aa63
DM
17
18#[api(
19 input: {
20 properties: {
21 "output-format": {
22 schema: OUTPUT_FORMAT,
23 optional: true,
24 },
25 }
26 }
27)]
28/// Local disk list.
29fn list_disks(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
30
31 let output_format = get_output_format(&param);
32
33 param["node"] = "localhost".into();
34
35 let info = &api2::node::disks::API_METHOD_LIST_DISKS;
36 let mut data = match info.handler {
37 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
38 _ => unreachable!(),
39 };
40
c8137518
DM
41 let render_wearout = |value: &Value, _record: &Value| -> Result<String, Error> {
42 match value.as_f64() {
43 Some(value) => Ok(format!("{:.2} %", if value <= 100.0 { 100.0 - value } else { 0.0 })),
44 None => Ok(String::from("-")),
45 }
46 };
47
8e40aa63
DM
48 let options = default_table_format_options()
49 .column(ColumnConfig::new("name"))
50 .column(ColumnConfig::new("used"))
dfb31de8 51 .column(ColumnConfig::new("gpt"))
8e40aa63
DM
52 .column(ColumnConfig::new("disk-type"))
53 .column(ColumnConfig::new("size"))
54 .column(ColumnConfig::new("model"))
c8137518 55 .column(ColumnConfig::new("wearout").renderer(render_wearout))
8e40aa63
DM
56 .column(ColumnConfig::new("status"))
57 ;
58
b2362a12 59 format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
8e40aa63
DM
60
61 Ok(Value::Null)
62}
63
64#[api(
65 input: {
66 properties: {
67 disk: {
9069debc 68 schema: BLOCKDEVICE_NAME_SCHEMA,
8e40aa63
DM
69 },
70 "output-format": {
71 schema: OUTPUT_FORMAT,
72 optional: true,
73 },
74 }
75 },
76 returns: {
77 description: "SMART attributes.",
78 type: Array,
79 items: {
80 type: SmartAttribute,
81 },
82 }
83)]
84/// Show SMART attributes.
85fn smart_attributes(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
86
87 let output_format = get_output_format(&param);
88
89 param["node"] = "localhost".into();
90
91 let info = &api2::node::disks::API_METHOD_SMART_STATUS;
92 let mut data = match info.handler {
93 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
94 _ => unreachable!(),
95 };
96
97 let mut data = data["attributes"].take();
98
99 let options = default_table_format_options();
b2362a12 100 format_and_print_result_full(&mut data, &API_METHOD_SMART_ATTRIBUTES.returns, &output_format, &options);
8e40aa63
DM
101
102 Ok(Value::Null)
103}
104
707974fd
DM
105#[api(
106 input: {
107 properties: {
108 disk: {
109 schema: BLOCKDEVICE_NAME_SCHEMA,
110 },
111 uuid: {
112 description: "UUID for the GPT table.",
113 type: String,
114 optional: true,
115 max_length: 36,
116 },
117 },
118 },
119)]
120/// Initialize empty Disk with GPT
121async fn initialize_disk(
122 mut param: Value,
123 rpcenv: &mut dyn RpcEnvironment,
124) -> Result<Value, Error> {
125
126 param["node"] = "localhost".into();
127
128 let info = &api2::node::disks::API_METHOD_INITIALIZE_DISK;
129 let result = match info.handler {
130 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
131 _ => unreachable!(),
132 };
133
134 crate::wait_for_local_worker(result.as_str().unwrap()).await?;
135
136 Ok(Value::Null)
137}
138
929a13b3
DM
139#[api(
140 input: {
141 properties: {
142 name: {
143 schema: DATASTORE_SCHEMA,
144 },
145 devices: {
146 schema: DISK_LIST_SCHEMA,
147 },
148 raidlevel: {
149 type: ZfsRaidLevel,
150 },
151 ashift: {
152 schema: ZFS_ASHIFT_SCHEMA,
153 optional: true,
154 },
155 compression: {
156 type: ZfsCompressionType,
157 optional: true,
158 },
159 "add-datastore": {
160 description: "Configure a datastore using the zpool.",
161 type: bool,
162 optional: true,
163 },
164 },
165 },
166)]
167/// create a zfs pool
168async fn create_zpool(
169 mut param: Value,
170 rpcenv: &mut dyn RpcEnvironment,
171) -> Result<Value, Error> {
172
173 param["node"] = "localhost".into();
174
175 let info = &api2::node::disks::zfs::API_METHOD_CREATE_ZPOOL;
176 let result = match info.handler {
177 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
178 _ => unreachable!(),
179 };
180
181 crate::wait_for_local_worker(result.as_str().unwrap()).await?;
182
183 Ok(Value::Null)
184}
185
186#[api(
187 input: {
188 properties: {
189 "output-format": {
190 schema: OUTPUT_FORMAT,
191 optional: true,
192 },
193 }
194 }
195)]
196/// Local zfs pools.
197fn list_zpools(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
198
199 let output_format = get_output_format(&param);
200
201 param["node"] = "localhost".into();
202
203 let info = &api2::node::disks::zfs::API_METHOD_LIST_ZPOOLS;
204 let mut data = match info.handler {
205 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
206 _ => unreachable!(),
207 };
208
209 let render_usage = |value: &Value, record: &Value| -> Result<String, Error> {
210 let value = value.as_u64().unwrap_or(0);
211 let size = match record["size"].as_u64() {
212 Some(size) => size,
213 None => bail!("missing size property"),
214 };
215 if size == 0 {
216 bail!("got zero size");
217 }
218 Ok(format!("{:.2} %", (value as f64)/(size as f64)))
219 };
220
221 let options = default_table_format_options()
222 .column(ColumnConfig::new("name"))
223 .column(ColumnConfig::new("size"))
224 .column(ColumnConfig::new("alloc").right_align(true).renderer(render_usage))
225 .column(ColumnConfig::new("health"));
226
b2362a12 227 format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
929a13b3
DM
228
229 Ok(Value::Null)
230}
231
232pub fn zpool_commands() -> CommandLineInterface {
233
234 let cmd_def = CliCommandMap::new()
235 .insert("list", CliCommand::new(&API_METHOD_LIST_ZPOOLS))
236 .insert("create",
237 CliCommand::new(&API_METHOD_CREATE_ZPOOL)
238 .arg_param(&["name"])
1ffe0301 239 .completion_cb("devices", complete_disk_name) // fixme: complete the list
929a13b3
DM
240 );
241
242 cmd_def.into()
243}
244
1aef491e
DM
245#[api(
246 input: {
247 properties: {
248 "output-format": {
249 schema: OUTPUT_FORMAT,
250 optional: true,
251 },
252 }
253 }
254)]
255/// List systemd datastore mount units.
256fn list_datastore_mounts(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
257
258 let output_format = get_output_format(&param);
259
260 param["node"] = "localhost".into();
261
262 let info = &api2::node::disks::directory::API_METHOD_LIST_DATASTORE_MOUNTS;
263 let mut data = match info.handler {
264 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
265 _ => unreachable!(),
266 };
267
268 let options = default_table_format_options()
269 .column(ColumnConfig::new("path"))
270 .column(ColumnConfig::new("device"))
271 .column(ColumnConfig::new("filesystem"))
272 .column(ColumnConfig::new("options"));
273
b2362a12 274 format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
1aef491e
DM
275
276 Ok(Value::Null)
277}
278
279#[api(
280 input: {
281 properties: {
282 name: {
283 schema: DATASTORE_SCHEMA,
284 },
285 disk: {
286 schema: BLOCKDEVICE_NAME_SCHEMA,
287 },
288 "add-datastore": {
289 description: "Configure a datastore using the directory.",
290 type: bool,
291 optional: true,
292 },
293 filesystem: {
294 type: FileSystemType,
295 optional: true,
296 },
297 },
298 },
299)]
300/// Create a Filesystem on an unused disk. Will be mounted under '/mnt/datastore/<name>'.
301async fn create_datastore_disk(
302 mut param: Value,
303 rpcenv: &mut dyn RpcEnvironment,
304) -> Result<Value, Error> {
305
306 param["node"] = "localhost".into();
307
308 let info = &api2::node::disks::directory::API_METHOD_CREATE_DATASTORE_DISK;
309 let result = match info.handler {
310 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
311 _ => unreachable!(),
312 };
313
314 crate::wait_for_local_worker(result.as_str().unwrap()).await?;
315
316 Ok(Value::Null)
317}
318
319pub fn filesystem_commands() -> CommandLineInterface {
320
321 let cmd_def = CliCommandMap::new()
322 .insert("list", CliCommand::new(&API_METHOD_LIST_DATASTORE_MOUNTS))
323 .insert("create",
324 CliCommand::new(&API_METHOD_CREATE_DATASTORE_DISK)
325 .arg_param(&["name"])
326 .completion_cb("disk", complete_disk_name)
327 );
328
329 cmd_def.into()
330}
929a13b3 331
8e40aa63
DM
332pub fn disk_commands() -> CommandLineInterface {
333
334 let cmd_def = CliCommandMap::new()
335 .insert("list", CliCommand::new(&API_METHOD_LIST_DISKS))
336 .insert("smart-attributes",
337 CliCommand::new(&API_METHOD_SMART_ATTRIBUTES)
338 .arg_param(&["disk"])
339 .completion_cb("disk", complete_disk_name)
707974fd 340 )
1aef491e 341 .insert("fs", filesystem_commands())
929a13b3 342 .insert("zpool", zpool_commands())
707974fd
DM
343 .insert("initialize",
344 CliCommand::new(&API_METHOD_INITIALIZE_DISK)
345 .arg_param(&["disk"])
346 .completion_cb("disk", complete_disk_name)
8e40aa63
DM
347 );
348
349 cmd_def.into()
350}