]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/tape/changer.rs
cleanup: move scan changers API implementation
[proxmox-backup.git] / src / api2 / tape / changer.rs
CommitLineData
cafd51bf
DM
1use std::path::Path;
2
5d908606
DM
3use anyhow::Error;
4use serde_json::Value;
5
6use proxmox::api::{api, Router, SubdirMap};
7use proxmox::list_subdirs_api_method;
8
9use crate::{
10 config,
11 api2::types::{
7e1d4712 12 CHANGER_NAME_SCHEMA,
740dc9d1 13 DriveListEntry,
5d908606 14 ScsiTapeChanger,
5d908606
DM
15 MtxStatusEntry,
16 MtxEntryKind,
17 },
18 tape::{
cafd51bf 19 TAPE_STATUS_DIR,
cafd51bf 20 Inventory,
645a044b 21 linux_tape_changer_list,
37796ff7
DM
22 changer::{
23 OnlineStatusMap,
24 ElementStatus,
697c41c5 25 ScsiMediaChange,
37796ff7 26 mtx_status_to_online_set,
37796ff7 27 },
740dc9d1 28 lookup_drive,
5d908606
DM
29 },
30};
31
32
33#[api(
34 input: {
35 properties: {
36 name: {
7e1d4712 37 schema: CHANGER_NAME_SCHEMA,
5d908606
DM
38 },
39 },
40 },
41 returns: {
42 description: "A status entry for each drive and slot.",
43 type: Array,
44 items: {
45 type: MtxStatusEntry,
46 },
47 },
48)]
49/// Get tape changer status
42cb9bd6 50pub async fn get_status(name: String) -> Result<Vec<MtxStatusEntry>, Error> {
5d908606
DM
51
52 let (config, _digest) = config::drive::config()?;
53
697c41c5 54 let mut changer_config: ScsiTapeChanger = config.lookup("changer", &name)?;
5d908606 55
42cb9bd6 56 let status = tokio::task::spawn_blocking(move || {
697c41c5 57 changer_config.status()
42cb9bd6 58 }).await??;
5d908606 59
cafd51bf 60 let state_path = Path::new(TAPE_STATUS_DIR);
cfae8f06 61 let mut inventory = Inventory::load(state_path)?;
5d908606
DM
62
63 let mut map = OnlineStatusMap::new(&config)?;
64 let online_set = mtx_status_to_online_set(&status, &inventory);
65 map.update_online_status(&name, online_set)?;
66
cfae8f06 67 inventory.update_online_status(&map)?;
5d908606
DM
68
69 let mut list = Vec::new();
70
71 for (id, drive_status) in status.drives.iter().enumerate() {
72 let entry = MtxStatusEntry {
73 entry_kind: MtxEntryKind::Drive,
74 entry_id: id as u64,
8446fbca 75 label_text: match &drive_status.status {
5d908606
DM
76 ElementStatus::Empty => None,
77 ElementStatus::Full => Some(String::new()),
78 ElementStatus::VolumeTag(tag) => Some(tag.to_string()),
79 },
80 loaded_slot: drive_status.loaded_slot,
81 };
82 list.push(entry);
83 }
84
697c41c5 85 for (id, slot_info) in status.slots.iter().enumerate() {
5d908606 86 let entry = MtxStatusEntry {
697c41c5 87 entry_kind: if slot_info.import_export {
e0362b0d
DM
88 MtxEntryKind::ImportExport
89 } else {
90 MtxEntryKind::Slot
91 },
5d908606 92 entry_id: id as u64 + 1,
697c41c5 93 label_text: match &slot_info.status {
5d908606
DM
94 ElementStatus::Empty => None,
95 ElementStatus::Full => Some(String::new()),
96 ElementStatus::VolumeTag(tag) => Some(tag.to_string()),
97 },
98 loaded_slot: None,
99 };
100 list.push(entry);
101 }
102
103 Ok(list)
104}
105
106#[api(
107 input: {
108 properties: {
109 name: {
7e1d4712 110 schema: CHANGER_NAME_SCHEMA,
5d908606
DM
111 },
112 from: {
113 description: "Source slot number",
114 minimum: 1,
115 },
116 to: {
117 description: "Destination slot number",
118 minimum: 1,
119 },
120 },
121 },
122)]
123/// Transfers media from one slot to another
42cb9bd6 124pub async fn transfer(
5d908606
DM
125 name: String,
126 from: u64,
127 to: u64,
128) -> Result<(), Error> {
129
130 let (config, _digest) = config::drive::config()?;
131
697c41c5 132 let mut changer_config: ScsiTapeChanger = config.lookup("changer", &name)?;
5d908606 133
42cb9bd6 134 tokio::task::spawn_blocking(move || {
697c41c5 135 changer_config.transfer(from, to)
42cb9bd6 136 }).await?
5d908606
DM
137}
138
740dc9d1
DC
139#[api(
140 input: {
141 properties: {},
142 },
143 returns: {
144 description: "The list of configured changers with model information.",
145 type: Array,
146 items: {
147 type: DriveListEntry,
148 },
149 },
150)]
151/// List changers
152pub fn list_changers(
153 _param: Value,
154) -> Result<Vec<DriveListEntry>, Error> {
155
156 let (config, _digest) = config::drive::config()?;
157
158 let linux_changers = linux_tape_changer_list();
159
160 let changer_list: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
161
162 let mut list = Vec::new();
163
164 for changer in changer_list {
165 let mut entry = DriveListEntry {
166 name: changer.name,
167 path: changer.path.clone(),
168 changer: None,
169 vendor: None,
170 model: None,
171 serial: None,
172 };
173 if let Some(info) = lookup_drive(&linux_changers, &changer.path) {
174 entry.vendor = Some(info.vendor.clone());
175 entry.model = Some(info.model.clone());
176 entry.serial = Some(info.serial.clone());
177 }
178
179 list.push(entry);
180 }
181 Ok(list)
182}
183
5d908606
DM
184const SUBDIRS: SubdirMap = &[
185 (
740dc9d1
DC
186 "status",
187 &Router::new()
188 .get(&API_METHOD_GET_STATUS)
189 ),
190 (
191 "transfer",
5d908606 192 &Router::new()
740dc9d1 193 .post(&API_METHOD_TRANSFER)
5d908606
DM
194 ),
195];
196
740dc9d1 197const ITEM_ROUTER: Router = Router::new()
5d908606 198 .get(&list_subdirs_api_method!(SUBDIRS))
740dc9d1
DC
199 .subdirs(&SUBDIRS);
200
201pub const ROUTER: Router = Router::new()
202 .get(&API_METHOD_LIST_CHANGERS)
203 .match_all("name", &ITEM_ROUTER);