]>
Commit | Line | Data |
---|---|---|
cafd51bf DM |
1 | use std::path::Path; |
2 | ||
5d908606 DM |
3 | use anyhow::Error; |
4 | use serde_json::Value; | |
5 | ||
6 | use proxmox::api::{api, Router, SubdirMap}; | |
7 | use proxmox::list_subdirs_api_method; | |
8 | ||
9 | use 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 | 50 | pub 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 | 124 | pub 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 | |
152 | pub 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 |
184 | const 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 | 197 | const ITEM_ROUTER: Router = Router::new() |
5d908606 | 198 | .get(&list_subdirs_api_method!(SUBDIRS)) |
740dc9d1 DC |
199 | .subdirs(&SUBDIRS); |
200 | ||
201 | pub const ROUTER: Router = Router::new() | |
202 | .get(&API_METHOD_LIST_CHANGERS) | |
203 | .match_all("name", &ITEM_ROUTER); |