]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/tape/changer.rs
update to first proxmox crate split
[proxmox-backup.git] / src / api2 / tape / changer.rs
CommitLineData
8be48ddf 1use std::collections::HashMap;
cafd51bf
DM
2use std::path::Path;
3
5d908606
DM
4use anyhow::Error;
5use serde_json::Value;
6
6ef1b649
WB
7use proxmox_schema::api;
8use proxmox_router::{list_subdirs_api_method, Permission, Router, RpcEnvironment, SubdirMap};
5d908606 9
8cc3760e
DM
10use pbs_api_types::{
11 Authid, ChangerListEntry, LtoTapeDrive, MtxEntryKind, MtxStatusEntry, ScsiTapeChanger,
12 CHANGER_NAME_SCHEMA, PRIV_TAPE_AUDIT, PRIV_TAPE_READ,
13};
ba3d7e19 14use pbs_config::CachedUserInfo;
048b43af
DM
15use pbs_tape::{
16 ElementStatus,
17 linux_list_drives::{lookup_device_identification, linux_tape_changer_list},
18};
8cc3760e 19
5d908606 20use crate::{
5d908606 21 tape::{
cafd51bf 22 TAPE_STATUS_DIR,
cafd51bf 23 Inventory,
37796ff7
DM
24 changer::{
25 OnlineStatusMap,
697c41c5 26 ScsiMediaChange,
37796ff7 27 mtx_status_to_online_set,
37796ff7 28 },
8be48ddf 29 drive::get_tape_device_state,
5d908606
DM
30 },
31};
32
33
34#[api(
35 input: {
36 properties: {
37 name: {
7e1d4712 38 schema: CHANGER_NAME_SCHEMA,
5d908606 39 },
4188fd59
DM
40 cache: {
41 description: "Use cached value.",
42 optional: true,
43 default: true,
44 },
5d908606
DM
45 },
46 },
47 returns: {
48 description: "A status entry for each drive and slot.",
49 type: Array,
50 items: {
51 type: MtxStatusEntry,
52 },
53 },
b4975d31
DM
54 access: {
55 permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_AUDIT, false),
56 },
5d908606
DM
57)]
58/// Get tape changer status
4188fd59
DM
59pub async fn get_status(
60 name: String,
61 cache: bool,
62) -> Result<Vec<MtxStatusEntry>, Error> {
5d908606 63
1ce8e905 64 let (config, _digest) = pbs_config::drive::config()?;
5d908606 65
697c41c5 66 let mut changer_config: ScsiTapeChanger = config.lookup("changer", &name)?;
5d908606 67
42cb9bd6 68 let status = tokio::task::spawn_blocking(move || {
4188fd59 69 changer_config.status(cache)
42cb9bd6 70 }).await??;
5d908606 71
cafd51bf 72 let state_path = Path::new(TAPE_STATUS_DIR);
cfae8f06 73 let mut inventory = Inventory::load(state_path)?;
5d908606
DM
74
75 let mut map = OnlineStatusMap::new(&config)?;
76 let online_set = mtx_status_to_online_set(&status, &inventory);
77 map.update_online_status(&name, online_set)?;
78
cfae8f06 79 inventory.update_online_status(&map)?;
5d908606 80
a79082a0 81 let drive_list: Vec<LtoTapeDrive> = config.convert_to_typed_array("lto")?;
8be48ddf
DC
82 let mut drive_map: HashMap<u64, String> = HashMap::new();
83
84 for drive in drive_list {
85 if let Some(changer) = drive.changer {
86 if changer != name {
87 continue;
88 }
89 let num = drive.changer_drivenum.unwrap_or(0);
90 drive_map.insert(num, drive.name.clone());
91 }
92 }
93
5d908606
DM
94 let mut list = Vec::new();
95
96 for (id, drive_status) in status.drives.iter().enumerate() {
8be48ddf
DC
97 let mut state = None;
98 if let Some(drive) = drive_map.get(&(id as u64)) {
99 state = get_tape_device_state(&config, &drive)?;
100 }
5d908606
DM
101 let entry = MtxStatusEntry {
102 entry_kind: MtxEntryKind::Drive,
103 entry_id: id as u64,
8446fbca 104 label_text: match &drive_status.status {
5d908606
DM
105 ElementStatus::Empty => None,
106 ElementStatus::Full => Some(String::new()),
107 ElementStatus::VolumeTag(tag) => Some(tag.to_string()),
108 },
109 loaded_slot: drive_status.loaded_slot,
8be48ddf 110 state,
5d908606
DM
111 };
112 list.push(entry);
113 }
114
697c41c5 115 for (id, slot_info) in status.slots.iter().enumerate() {
5d908606 116 let entry = MtxStatusEntry {
697c41c5 117 entry_kind: if slot_info.import_export {
e0362b0d
DM
118 MtxEntryKind::ImportExport
119 } else {
120 MtxEntryKind::Slot
121 },
5d908606 122 entry_id: id as u64 + 1,
697c41c5 123 label_text: match &slot_info.status {
5d908606
DM
124 ElementStatus::Empty => None,
125 ElementStatus::Full => Some(String::new()),
126 ElementStatus::VolumeTag(tag) => Some(tag.to_string()),
127 },
128 loaded_slot: None,
8be48ddf 129 state: None,
5d908606
DM
130 };
131 list.push(entry);
132 }
133
134 Ok(list)
135}
136
137#[api(
138 input: {
139 properties: {
140 name: {
7e1d4712 141 schema: CHANGER_NAME_SCHEMA,
5d908606
DM
142 },
143 from: {
144 description: "Source slot number",
145 minimum: 1,
146 },
147 to: {
148 description: "Destination slot number",
149 minimum: 1,
150 },
151 },
152 },
b4975d31
DM
153 access: {
154 permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_READ, false),
155 },
5d908606
DM
156)]
157/// Transfers media from one slot to another
42cb9bd6 158pub async fn transfer(
5d908606
DM
159 name: String,
160 from: u64,
161 to: u64,
162) -> Result<(), Error> {
163
1ce8e905 164 let (config, _digest) = pbs_config::drive::config()?;
5d908606 165
697c41c5 166 let mut changer_config: ScsiTapeChanger = config.lookup("changer", &name)?;
5d908606 167
42cb9bd6 168 tokio::task::spawn_blocking(move || {
cbd98993 169 changer_config.transfer(from, to)?;
cbd98993 170 Ok(())
42cb9bd6 171 }).await?
5d908606
DM
172}
173
740dc9d1
DC
174#[api(
175 input: {
176 properties: {},
177 },
178 returns: {
179 description: "The list of configured changers with model information.",
180 type: Array,
181 items: {
b5b99a52 182 type: ChangerListEntry,
740dc9d1
DC
183 },
184 },
8cd63df0
DM
185 access: {
186 description: "List configured tape changer filtered by Tape.Audit privileges",
187 permission: &Permission::Anybody,
188 },
740dc9d1
DC
189)]
190/// List changers
191pub fn list_changers(
192 _param: Value,
8cd63df0 193 rpcenv: &mut dyn RpcEnvironment,
b5b99a52 194) -> Result<Vec<ChangerListEntry>, Error> {
8cd63df0
DM
195 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
196 let user_info = CachedUserInfo::new()?;
740dc9d1 197
1ce8e905 198 let (config, _digest) = pbs_config::drive::config()?;
740dc9d1
DC
199
200 let linux_changers = linux_tape_changer_list();
201
202 let changer_list: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
203
204 let mut list = Vec::new();
205
206 for changer in changer_list {
8cd63df0
DM
207 let privs = user_info.lookup_privs(&auth_id, &["tape", "changer", &changer.name]);
208 if (privs & PRIV_TAPE_AUDIT) == 0 {
209 continue;
210 }
211
b5b99a52
DM
212 let info = lookup_device_identification(&linux_changers, &changer.path);
213 let entry = ChangerListEntry { config: changer, info };
740dc9d1
DC
214 list.push(entry);
215 }
216 Ok(list)
217}
218
5d908606
DM
219const SUBDIRS: SubdirMap = &[
220 (
740dc9d1
DC
221 "status",
222 &Router::new()
223 .get(&API_METHOD_GET_STATUS)
224 ),
225 (
226 "transfer",
5d908606 227 &Router::new()
740dc9d1 228 .post(&API_METHOD_TRANSFER)
5d908606
DM
229 ),
230];
231
740dc9d1 232const ITEM_ROUTER: Router = Router::new()
5d908606 233 .get(&list_subdirs_api_method!(SUBDIRS))
740dc9d1
DC
234 .subdirs(&SUBDIRS);
235
236pub const ROUTER: Router = Router::new()
237 .get(&API_METHOD_LIST_CHANGERS)
238 .match_all("name", &ITEM_ROUTER);