]> git.proxmox.com Git - proxmox-backup.git/blob - src/bin/proxmox_tape/changer.rs
update to first proxmox crate split
[proxmox-backup.git] / src / bin / proxmox_tape / changer.rs
1 use anyhow::{bail, Error};
2 use serde_json::Value;
3
4 use proxmox_router::{cli::*, ApiHandler, RpcEnvironment};
5 use proxmox_schema::api;
6 use proxmox_section_config::SectionConfigData;
7
8 use pbs_config::drive::{
9 complete_drive_name,
10 complete_changer_name,
11 };
12
13 use pbs_api_types::CHANGER_NAME_SCHEMA;
14
15 use pbs_tape::linux_list_drives::{complete_changer_path};
16
17 use proxmox_backup::{api2, tape::drive::media_changer};
18
19 pub fn lookup_changer_name(
20 param: &Value,
21 config: &SectionConfigData,
22 ) -> Result<String, Error> {
23
24 if let Some(name) = param["name"].as_str() {
25 return Ok(String::from(name));
26 }
27
28 let mut empty = Value::Null;
29
30 if let Ok(drive) = crate::extract_drive_name(&mut empty, config) {
31 if let Ok(Some((_, name))) = media_changer(config, &drive) {
32 return Ok(name);
33 }
34 }
35
36 bail!("unable to get (default) changer name");
37 }
38
39 pub fn changer_commands() -> CommandLineInterface {
40
41 let cmd_def = CliCommandMap::new()
42 .insert("scan", CliCommand::new(&API_METHOD_SCAN_FOR_CHANGERS))
43 .insert("list", CliCommand::new(&API_METHOD_LIST_CHANGERS))
44 .insert("config",
45 CliCommand::new(&API_METHOD_GET_CONFIG)
46 .arg_param(&["name"])
47 .completion_cb("name", complete_changer_name)
48 )
49 .insert(
50 "remove",
51 CliCommand::new(&api2::config::changer::API_METHOD_DELETE_CHANGER)
52 .arg_param(&["name"])
53 .completion_cb("name", complete_changer_name)
54 )
55 .insert(
56 "create",
57 CliCommand::new(&api2::config::changer::API_METHOD_CREATE_CHANGER)
58 .arg_param(&["name"])
59 .completion_cb("name", complete_drive_name)
60 .completion_cb("path", complete_changer_path)
61 )
62 .insert(
63 "update",
64 CliCommand::new(&api2::config::changer::API_METHOD_UPDATE_CHANGER)
65 .arg_param(&["name"])
66 .completion_cb("name", complete_changer_name)
67 .completion_cb("path", complete_changer_path)
68 )
69 .insert("status",
70 CliCommand::new(&API_METHOD_GET_STATUS)
71 .arg_param(&["name"])
72 .completion_cb("name", complete_changer_name)
73 )
74 .insert("transfer",
75 CliCommand::new(&API_METHOD_TRANSFER)
76 .arg_param(&["name"])
77 .completion_cb("name", complete_changer_name)
78 )
79 ;
80
81 cmd_def.into()
82 }
83
84 #[api(
85 input: {
86 properties: {
87 "output-format": {
88 schema: OUTPUT_FORMAT,
89 optional: true,
90 },
91 },
92 },
93 )]
94 /// List changers
95 fn list_changers(
96 param: Value,
97 rpcenv: &mut dyn RpcEnvironment,
98 ) -> Result<(), Error> {
99
100 let output_format = get_output_format(&param);
101 let info = &api2::tape::changer::API_METHOD_LIST_CHANGERS;
102 let mut data = match info.handler {
103 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
104 _ => unreachable!(),
105 };
106
107 let options = default_table_format_options()
108 .column(ColumnConfig::new("name"))
109 .column(ColumnConfig::new("path"))
110 .column(ColumnConfig::new("vendor"))
111 .column(ColumnConfig::new("model"))
112 .column(ColumnConfig::new("serial"))
113 ;
114
115 format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
116
117 Ok(())
118 }
119
120 #[api(
121 input: {
122 properties: {
123 "output-format": {
124 schema: OUTPUT_FORMAT,
125 optional: true,
126 },
127 },
128 },
129 )]
130 /// Scan for SCSI tape changers
131 fn scan_for_changers(
132 param: Value,
133 rpcenv: &mut dyn RpcEnvironment,
134 ) -> Result<(), Error> {
135
136 let output_format = get_output_format(&param);
137 let info = &api2::tape::API_METHOD_SCAN_CHANGERS;
138 let mut data = match info.handler {
139 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
140 _ => unreachable!(),
141 };
142
143 let options = default_table_format_options()
144 .column(ColumnConfig::new("path"))
145 .column(ColumnConfig::new("vendor"))
146 .column(ColumnConfig::new("model"))
147 .column(ColumnConfig::new("serial"))
148 ;
149
150 format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
151
152 Ok(())
153 }
154
155 #[api(
156 input: {
157 properties: {
158 "output-format": {
159 schema: OUTPUT_FORMAT,
160 optional: true,
161 },
162 name: {
163 schema: CHANGER_NAME_SCHEMA,
164 },
165 },
166 },
167 )]
168 /// Get tape changer configuration
169 fn get_config(
170 param: Value,
171 rpcenv: &mut dyn RpcEnvironment,
172 ) -> Result<(), Error> {
173
174 let output_format = get_output_format(&param);
175 let info = &api2::config::changer::API_METHOD_GET_CONFIG;
176 let mut data = match info.handler {
177 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
178 _ => unreachable!(),
179 };
180
181 let options = default_table_format_options()
182 .column(ColumnConfig::new("name"))
183 .column(ColumnConfig::new("path"))
184 .column(ColumnConfig::new("export-slots"))
185 ;
186
187 format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
188
189 Ok(())
190 }
191
192 #[api(
193 input: {
194 properties: {
195 "output-format": {
196 schema: OUTPUT_FORMAT,
197 optional: true,
198 },
199 name: {
200 schema: CHANGER_NAME_SCHEMA,
201 optional: true,
202 },
203 cache: {
204 description: "Use cached value.",
205 type: bool,
206 optional: true,
207 default: true,
208 },
209 },
210 },
211 )]
212 /// Get tape changer status
213 async fn get_status(
214 mut param: Value,
215 rpcenv: &mut dyn RpcEnvironment,
216 ) -> Result<(), Error> {
217
218 let (config, _digest) = pbs_config::drive::config()?;
219
220 param["name"] = lookup_changer_name(&param, &config)?.into();
221
222 let output_format = get_output_format(&param);
223 let info = &api2::tape::changer::API_METHOD_GET_STATUS;
224 let mut data = match info.handler {
225 ApiHandler::Async(handler) => (handler)(param, info, rpcenv).await?,
226 _ => unreachable!(),
227 };
228
229 let render_label_text = |value: &Value, _record: &Value| -> Result<String, Error> {
230 if value.is_null() {
231 return Ok(String::new());
232 }
233 let text = value.as_str().unwrap().to_string();
234 if text.is_empty() {
235 Ok(String::from("--FULL--"))
236 } else {
237 Ok(text)
238 }
239 };
240
241 let options = default_table_format_options()
242 .sortby("entry-kind", false)
243 .sortby("entry-id", false)
244 .column(ColumnConfig::new("entry-kind"))
245 .column(ColumnConfig::new("entry-id"))
246 .column(ColumnConfig::new("label-text").renderer(render_label_text))
247 .column(ColumnConfig::new("loaded-slot"))
248 ;
249
250 format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
251
252 Ok(())
253 }
254
255 #[api(
256 input: {
257 properties: {
258 name: {
259 schema: CHANGER_NAME_SCHEMA,
260 optional: true,
261 },
262 from: {
263 description: "Source slot number",
264 type: u64,
265 minimum: 1,
266 },
267 to: {
268 description: "Destination slot number",
269 type: u64,
270 minimum: 1,
271 },
272 },
273 },
274 )]
275 /// Transfers media from one slot to another
276 pub async fn transfer(
277 mut param: Value,
278 rpcenv: &mut dyn RpcEnvironment,
279 ) -> Result<(), Error> {
280
281 let (config, _digest) = pbs_config::drive::config()?;
282
283 param["name"] = lookup_changer_name(&param, &config)?.into();
284
285 let info = &api2::tape::changer::API_METHOD_TRANSFER;
286 match info.handler {
287 ApiHandler::Async(handler) => (handler)(param, info, rpcenv).await?,
288 _ => unreachable!(),
289 };
290
291 Ok(())
292 }