1 use anyhow
::{bail, Error}
;
4 use proxmox_router
::{cli::*, ApiHandler, RpcEnvironment}
;
5 use proxmox_schema
::api
;
8 DISK_LIST_SCHEMA
, ZFS_ASHIFT_SCHEMA
, ZfsRaidLevel
, ZfsCompressionType
,
9 BLOCKDEVICE_NAME_SCHEMA
, DATASTORE_SCHEMA
,
11 use proxmox_backup
::tools
::disks
::{
17 use proxmox_backup
::api2
;
23 schema
: OUTPUT_FORMAT
,
30 fn list_disks(mut param
: Value
, rpcenv
: &mut dyn RpcEnvironment
) -> Result
<Value
, Error
> {
32 let output_format
= get_output_format(¶m
);
34 param
["node"] = "localhost".into();
36 let info
= &api2
::node
::disks
::API_METHOD_LIST_DISKS
;
37 let mut data
= match info
.handler
{
38 ApiHandler
::Sync(handler
) => (handler
)(param
, info
, rpcenv
)?
,
42 let render_wearout
= |value
: &Value
, _record
: &Value
| -> Result
<String
, Error
> {
43 match value
.as_f64() {
44 Some(value
) => Ok(format
!("{:.2} %", if value
<= 100.0 { 100.0 - value }
else { 0.0 }
)),
45 None
=> Ok(String
::from("-")),
49 let options
= default_table_format_options()
50 .column(ColumnConfig
::new("name"))
51 .column(ColumnConfig
::new("used"))
52 .column(ColumnConfig
::new("gpt"))
53 .column(ColumnConfig
::new("disk-type"))
54 .column(ColumnConfig
::new("size"))
55 .column(ColumnConfig
::new("model"))
56 .column(ColumnConfig
::new("wearout").renderer(render_wearout
))
57 .column(ColumnConfig
::new("status"))
60 format_and_print_result_full(&mut data
, &info
.returns
, &output_format
, &options
);
69 schema
: BLOCKDEVICE_NAME_SCHEMA
,
72 schema
: OUTPUT_FORMAT
,
78 description
: "SMART attributes.",
85 /// Show SMART attributes.
86 fn smart_attributes(mut param
: Value
, rpcenv
: &mut dyn RpcEnvironment
) -> Result
<Value
, Error
> {
88 let output_format
= get_output_format(¶m
);
90 param
["node"] = "localhost".into();
92 let info
= &api2
::node
::disks
::API_METHOD_SMART_STATUS
;
93 let mut data
= match info
.handler
{
94 ApiHandler
::Sync(handler
) => (handler
)(param
, info
, rpcenv
)?
,
98 let mut data
= data
["attributes"].take();
100 let options
= default_table_format_options();
101 format_and_print_result_full(&mut data
, &API_METHOD_SMART_ATTRIBUTES
.returns
, &output_format
, &options
);
110 schema
: BLOCKDEVICE_NAME_SCHEMA
,
113 description
: "UUID for the GPT table.",
121 /// Initialize empty Disk with GPT
122 async
fn initialize_disk(
124 rpcenv
: &mut dyn RpcEnvironment
,
125 ) -> Result
<Value
, Error
> {
127 param
["node"] = "localhost".into();
129 let info
= &api2
::node
::disks
::API_METHOD_INITIALIZE_DISK
;
130 let result
= match info
.handler
{
131 ApiHandler
::Sync(handler
) => (handler
)(param
, info
, rpcenv
)?
,
135 crate::wait_for_local_worker(result
.as_str().unwrap()).await?
;
144 schema
: DATASTORE_SCHEMA
,
147 schema
: DISK_LIST_SCHEMA
,
153 schema
: ZFS_ASHIFT_SCHEMA
,
157 type: ZfsCompressionType
,
161 description
: "Configure a datastore using the zpool.",
168 /// create a zfs pool
169 async
fn create_zpool(
171 rpcenv
: &mut dyn RpcEnvironment
,
172 ) -> Result
<Value
, Error
> {
174 param
["node"] = "localhost".into();
176 let info
= &api2
::node
::disks
::zfs
::API_METHOD_CREATE_ZPOOL
;
177 let result
= match info
.handler
{
178 ApiHandler
::Sync(handler
) => (handler
)(param
, info
, rpcenv
)?
,
182 crate::wait_for_local_worker(result
.as_str().unwrap()).await?
;
191 schema
: OUTPUT_FORMAT
,
198 fn list_zpools(mut param
: Value
, rpcenv
: &mut dyn RpcEnvironment
) -> Result
<Value
, Error
> {
200 let output_format
= get_output_format(¶m
);
202 param
["node"] = "localhost".into();
204 let info
= &api2
::node
::disks
::zfs
::API_METHOD_LIST_ZPOOLS
;
205 let mut data
= match info
.handler
{
206 ApiHandler
::Sync(handler
) => (handler
)(param
, info
, rpcenv
)?
,
210 let render_usage
= |value
: &Value
, record
: &Value
| -> Result
<String
, Error
> {
211 let value
= value
.as_u64().unwrap_or(0);
212 let size
= match record
["size"].as_u64() {
214 None
=> bail
!("missing size property"),
217 bail
!("got zero size");
219 Ok(format
!("{:.2} %", (value
as f64)/(size
as f64)))
222 let options
= default_table_format_options()
223 .column(ColumnConfig
::new("name"))
224 .column(ColumnConfig
::new("size"))
225 .column(ColumnConfig
::new("alloc").right_align(true).renderer(render_usage
))
226 .column(ColumnConfig
::new("health"));
228 format_and_print_result_full(&mut data
, &info
.returns
, &output_format
, &options
);
233 pub fn zpool_commands() -> CommandLineInterface
{
235 let cmd_def
= CliCommandMap
::new()
236 .insert("list", CliCommand
::new(&API_METHOD_LIST_ZPOOLS
))
238 CliCommand
::new(&API_METHOD_CREATE_ZPOOL
)
239 .arg_param(&["name"])
240 .completion_cb("devices", complete_disk_name
) // fixme: complete the list
250 schema
: OUTPUT_FORMAT
,
256 /// List systemd datastore mount units.
257 fn list_datastore_mounts(mut param
: Value
, rpcenv
: &mut dyn RpcEnvironment
) -> Result
<Value
, Error
> {
259 let output_format
= get_output_format(¶m
);
261 param
["node"] = "localhost".into();
263 let info
= &api2
::node
::disks
::directory
::API_METHOD_LIST_DATASTORE_MOUNTS
;
264 let mut data
= match info
.handler
{
265 ApiHandler
::Sync(handler
) => (handler
)(param
, info
, rpcenv
)?
,
269 let options
= default_table_format_options()
270 .column(ColumnConfig
::new("path"))
271 .column(ColumnConfig
::new("device"))
272 .column(ColumnConfig
::new("filesystem"))
273 .column(ColumnConfig
::new("options"));
275 format_and_print_result_full(&mut data
, &info
.returns
, &output_format
, &options
);
284 schema
: DATASTORE_SCHEMA
,
287 schema
: BLOCKDEVICE_NAME_SCHEMA
,
290 description
: "Configure a datastore using the directory.",
295 type: FileSystemType
,
301 /// Create a Filesystem on an unused disk. Will be mounted under '/mnt/datastore/<name>'.
302 async
fn create_datastore_disk(
304 rpcenv
: &mut dyn RpcEnvironment
,
305 ) -> Result
<Value
, Error
> {
307 param
["node"] = "localhost".into();
309 let info
= &api2
::node
::disks
::directory
::API_METHOD_CREATE_DATASTORE_DISK
;
310 let result
= match info
.handler
{
311 ApiHandler
::Sync(handler
) => (handler
)(param
, info
, rpcenv
)?
,
315 crate::wait_for_local_worker(result
.as_str().unwrap()).await?
;
320 pub fn filesystem_commands() -> CommandLineInterface
{
322 let cmd_def
= CliCommandMap
::new()
323 .insert("list", CliCommand
::new(&API_METHOD_LIST_DATASTORE_MOUNTS
))
325 CliCommand
::new(&API_METHOD_CREATE_DATASTORE_DISK
)
326 .arg_param(&["name"])
327 .completion_cb("disk", complete_disk_name
)
333 pub fn disk_commands() -> CommandLineInterface
{
335 let cmd_def
= CliCommandMap
::new()
336 .insert("list", CliCommand
::new(&API_METHOD_LIST_DISKS
))
337 .insert("smart-attributes",
338 CliCommand
::new(&API_METHOD_SMART_ATTRIBUTES
)
339 .arg_param(&["disk"])
340 .completion_cb("disk", complete_disk_name
)
342 .insert("fs", filesystem_commands())
343 .insert("zpool", zpool_commands())
344 .insert("initialize",
345 CliCommand
::new(&API_METHOD_INITIALIZE_DISK
)
346 .arg_param(&["disk"])
347 .completion_cb("disk", complete_disk_name
)