]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/tape/drive.rs
tape: implement eject
[proxmox-backup.git] / src / api2 / tape / drive.rs
1 use anyhow::{bail, Error};
2 use serde_json::Value;
3
4 use proxmox::api::{api, Router, SubdirMap};
5 use proxmox::list_subdirs_api_method;
6
7 use crate::{
8 config,
9 api2::types::{
10 DRIVE_ID_SCHEMA,
11 LinuxTapeDrive,
12 ScsiTapeChanger,
13 TapeDeviceInfo,
14 },
15 tape::{
16 MediaChange,
17 mtx_load,
18 mtx_unload,
19 linux_tape_device_list,
20 open_drive,
21 media_changer,
22 },
23 };
24
25 #[api(
26 input: {
27 properties: {
28 name: {
29 schema: DRIVE_ID_SCHEMA,
30 },
31 slot: {
32 description: "Source slot number",
33 minimum: 1,
34 },
35 },
36 },
37 )]
38 /// Load media via changer from slot
39 pub fn load_slot(
40 name: String,
41 slot: u64,
42 _param: Value,
43 ) -> Result<(), Error> {
44
45 let (config, _digest) = config::drive::config()?;
46
47 let drive: LinuxTapeDrive = config.lookup("linux", &name)?;
48
49 let changer: ScsiTapeChanger = match drive.changer {
50 Some(ref changer) => config.lookup("changer", changer)?,
51 None => bail!("drive '{}' has no associated changer", name),
52 };
53
54 let drivenum = 0;
55
56 mtx_load(&changer.path, slot, drivenum)
57 }
58
59 #[api(
60 input: {
61 properties: {
62 name: {
63 schema: DRIVE_ID_SCHEMA,
64 },
65 slot: {
66 description: "Target slot number. If omitted, defaults to the slot that the drive was loaded from.",
67 minimum: 1,
68 optional: true,
69 },
70 },
71 },
72 )]
73 /// Unload media via changer
74 pub fn unload(
75 name: String,
76 slot: Option<u64>,
77 _param: Value,
78 ) -> Result<(), Error> {
79
80 let (config, _digest) = config::drive::config()?;
81
82 let mut drive: LinuxTapeDrive = config.lookup("linux", &name)?;
83
84 let changer: ScsiTapeChanger = match drive.changer {
85 Some(ref changer) => config.lookup("changer", changer)?,
86 None => bail!("drive '{}' has no associated changer", name),
87 };
88
89 let drivenum: u64 = 0;
90
91 if let Some(slot) = slot {
92 mtx_unload(&changer.path, slot, drivenum)
93 } else {
94 drive.unload_media()
95 }
96 }
97
98 #[api(
99 input: {
100 properties: {},
101 },
102 returns: {
103 description: "The list of autodetected tape drives.",
104 type: Array,
105 items: {
106 type: TapeDeviceInfo,
107 },
108 },
109 )]
110 /// Scan tape drives
111 pub fn scan_drives(_param: Value) -> Result<Vec<TapeDeviceInfo>, Error> {
112
113 let list = linux_tape_device_list();
114
115 Ok(list)
116 }
117
118 #[api(
119 input: {
120 properties: {
121 drive: {
122 schema: DRIVE_ID_SCHEMA,
123 },
124 fast: {
125 description: "Use fast erase.",
126 type: bool,
127 optional: true,
128 default: true,
129 },
130 },
131 },
132 )]
133 /// Erase media
134 pub fn erase_media(drive: String, fast: Option<bool>) -> Result<(), Error> {
135
136 let (config, _digest) = config::drive::config()?;
137
138 let mut drive = open_drive(&config, &drive)?;
139
140 drive.erase_media(fast.unwrap_or(true))?;
141
142 Ok(())
143 }
144
145 #[api(
146 input: {
147 properties: {
148 drive: {
149 schema: DRIVE_ID_SCHEMA,
150 },
151 },
152 },
153 )]
154 /// Rewind tape
155 pub fn rewind(drive: String) -> Result<(), Error> {
156
157 let (config, _digest) = config::drive::config()?;
158
159 let mut drive = open_drive(&config, &drive)?;
160
161 drive.rewind()?;
162
163 Ok(())
164 }
165
166 #[api(
167 input: {
168 properties: {
169 drive: {
170 schema: DRIVE_ID_SCHEMA,
171 },
172 },
173 },
174 )]
175 /// Eject/Unload drive media
176 pub fn eject_media(drive: String) -> Result<(), Error> {
177
178 let (config, _digest) = config::drive::config()?;
179
180 let (mut changer, _) = media_changer(&config, &drive, false)?;
181
182 if !changer.eject_on_unload() {
183 let mut drive = open_drive(&config, &drive)?;
184 drive.eject_media()?;
185 }
186
187 changer.unload_media()?;
188
189 Ok(())
190 }
191
192 pub const SUBDIRS: SubdirMap = &[
193 (
194 "rewind",
195 &Router::new()
196 .put(&API_METHOD_REWIND)
197 ),
198 (
199 "erase-media",
200 &Router::new()
201 .put(&API_METHOD_ERASE_MEDIA)
202 ),
203 (
204 "eject-media",
205 &Router::new()
206 .put(&API_METHOD_EJECT_MEDIA)
207 ),
208 (
209 "load-slot",
210 &Router::new()
211 .put(&API_METHOD_LOAD_SLOT)
212 ),
213 (
214 "scan",
215 &Router::new()
216 .get(&API_METHOD_SCAN_DRIVES)
217 ),
218 (
219 "unload",
220 &Router::new()
221 .put(&API_METHOD_UNLOAD)
222 ),
223 ];
224
225 pub const ROUTER: Router = Router::new()
226 .get(&list_subdirs_api_method!(SUBDIRS))
227 .subdirs(SUBDIRS);