]> git.proxmox.com Git - proxmox-backup.git/blame - src/tape/changer/online_status_map.rs
more api type cleanups: avoid re-exports
[proxmox-backup.git] / src / tape / changer / online_status_map.rs
CommitLineData
cafd51bf
DM
1use std::path::Path;
2use std::collections::{HashMap, HashSet};
3
4use anyhow::{bail, Error};
5
6use proxmox::tools::Uuid;
7use proxmox::api::section_config::SectionConfigData;
8
6227654a
DM
9use pbs_api_types::{VirtualTapeDrive, ScsiTapeChanger};
10
cafd51bf 11use crate::{
cafd51bf 12 tape::{
cafd51bf 13 Inventory,
37796ff7
DM
14 changer::{
15 MediaChange,
16 MtxStatus,
17 ElementStatus,
2b96a438 18 ScsiMediaChange,
37796ff7 19 },
cafd51bf
DM
20 },
21};
22
23/// Helper to update media online status
24///
25/// A tape media is considered online if it is accessible by a changer
26/// device. This class can store the list of available changes,
27/// together with the accessible media ids.
28pub struct OnlineStatusMap {
29 map: HashMap<String, Option<HashSet<Uuid>>>,
30 changer_map: HashMap<Uuid, String>,
31}
32
33impl OnlineStatusMap {
34
35 /// Creates a new instance with one map entry for each configured
36 /// changer (or 'VirtualTapeDrive', which has an internal
37 /// changer). The map entry is set to 'None' to indicate that we
38 /// do not have information about the online status.
39 pub fn new(config: &SectionConfigData) -> Result<Self, Error> {
40
41 let mut map = HashMap::new();
42
43 let changers: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
44 for changer in changers {
45 map.insert(changer.name.clone(), None);
46 }
47
48 let vtapes: Vec<VirtualTapeDrive> = config.convert_to_typed_array("virtual")?;
49 for vtape in vtapes {
50 map.insert(vtape.name.clone(), None);
51 }
52
53 Ok(Self { map, changer_map: HashMap::new() })
54 }
55
56 /// Returns the assiciated changer name for a media.
57 pub fn lookup_changer(&self, uuid: &Uuid) -> Option<&String> {
58 self.changer_map.get(uuid)
59 }
60
61 /// Returns the map which assiciates media uuids with changer names.
62 pub fn changer_map(&self) -> &HashMap<Uuid, String> {
63 &self.changer_map
64 }
65
66 /// Returns the set of online media for the specified changer.
67 pub fn online_map(&self, changer_name: &str) -> Option<&Option<HashSet<Uuid>>> {
68 self.map.get(changer_name)
69 }
70
71 /// Update the online set for the specified changer
72 pub fn update_online_status(&mut self, changer_name: &str, online_set: HashSet<Uuid>) -> Result<(), Error> {
73
74 match self.map.get(changer_name) {
75 None => bail!("no such changer '{}' device", changer_name),
76 Some(None) => { /* Ok */ },
77 Some(Some(_)) => {
78 // do not allow updates to keep self.changer_map consistent
79 bail!("update_online_status '{}' called twice", changer_name);
80 }
81 }
82
83 for uuid in online_set.iter() {
84 self.changer_map.insert(uuid.clone(), changer_name.to_string());
85 }
86
87 self.map.insert(changer_name.to_string(), Some(online_set));
88
89 Ok(())
90 }
91}
92
46a1863f
DM
93/// Extract the list of online media from MtxStatus
94///
95/// Returns a HashSet containing all found media Uuid. This only
96/// returns media found in Inventory.
97pub fn mtx_status_to_online_set(status: &MtxStatus, inventory: &Inventory) -> HashSet<Uuid> {
98
99 let mut online_set = HashSet::new();
100
101 for drive_status in status.drives.iter() {
8446fbca
DM
102 if let ElementStatus::VolumeTag(ref label_text) = drive_status.status {
103 if let Some(media_id) = inventory.find_media_by_label_text(label_text) {
46a1863f
DM
104 online_set.insert(media_id.label.uuid.clone());
105 }
106 }
107 }
108
697c41c5
DM
109 for slot_info in status.slots.iter() {
110 if slot_info.import_export { continue; }
111 if let ElementStatus::VolumeTag(ref label_text) = slot_info.status {
8446fbca 112 if let Some(media_id) = inventory.find_media_by_label_text(label_text) {
46a1863f
DM
113 online_set.insert(media_id.label.uuid.clone());
114 }
115 }
116 }
117
118 online_set
119}
120
cafd51bf
DM
121/// Update online media status
122///
9bbd83b1
DM
123/// For a single 'changer', or else simply ask all changer devices.
124pub fn update_online_status(state_path: &Path, changer: Option<&str>) -> Result<OnlineStatusMap, Error> {
cafd51bf 125
1ce8e905 126 let (config, _digest) = pbs_config::drive::config()?;
cafd51bf 127
cfae8f06 128 let mut inventory = Inventory::load(state_path)?;
cafd51bf
DM
129
130 let changers: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
131
132 let mut map = OnlineStatusMap::new(&config)?;
133
9bbd83b1
DM
134 let mut found_changer = false;
135
136 for mut changer_config in changers {
137 if let Some(changer) = changer {
138 if changer != &changer_config.name {
139 continue;
140 }
141 found_changer = true;
142 }
4188fd59 143 let status = match changer_config.status(false) {
cafd51bf
DM
144 Ok(status) => status,
145 Err(err) => {
9bbd83b1 146 eprintln!("unable to get changer '{}' status - {}", changer_config.name, err);
cafd51bf
DM
147 continue;
148 }
149 };
150
151 let online_set = mtx_status_to_online_set(&status, &inventory);
9bbd83b1 152 map.update_online_status(&changer_config.name, online_set)?;
cafd51bf
DM
153 }
154
155 let vtapes: Vec<VirtualTapeDrive> = config.convert_to_typed_array("virtual")?;
46a1863f 156 for mut vtape in vtapes {
9bbd83b1
DM
157 if let Some(changer) = changer {
158 if changer != &vtape.name {
159 continue;
160 }
161 found_changer = true;
162 }
163
8446fbca 164 let media_list = match vtape.online_media_label_texts() {
cafd51bf
DM
165 Ok(media_list) => media_list,
166 Err(err) => {
167 eprintln!("unable to get changer '{}' status - {}", vtape.name, err);
168 continue;
169 }
170 };
171
172 let mut online_set = HashSet::new();
8446fbca
DM
173 for label_text in media_list {
174 if let Some(media_id) = inventory.find_media_by_label_text(&label_text) {
cafd51bf
DM
175 online_set.insert(media_id.label.uuid.clone());
176 }
177 }
178 map.update_online_status(&vtape.name, online_set)?;
179 }
180
9bbd83b1
DM
181 if let Some(changer) = changer {
182 if !found_changer {
183 bail!("update_online_status failed - no such changer '{}'", changer);
184 }
185 }
186
cfae8f06 187 inventory.update_online_status(&map)?;
cafd51bf
DM
188
189 Ok(map)
190}
191
192/// Update online media status with data from a single changer device
193pub fn update_changer_online_status(
194 drive_config: &SectionConfigData,
195 inventory: &mut Inventory,
cafd51bf 196 changer_name: &str,
09faa9ee 197 label_text_list: &[String],
cafd51bf
DM
198) -> Result<(), Error> {
199
200 let mut online_map = OnlineStatusMap::new(drive_config)?;
201 let mut online_set = HashSet::new();
8446fbca
DM
202 for label_text in label_text_list.iter() {
203 if let Some(media_id) = inventory.find_media_by_label_text(&label_text) {
cafd51bf
DM
204 online_set.insert(media_id.label.uuid.clone());
205 }
206 }
207 online_map.update_online_status(&changer_name, online_set)?;
cfae8f06 208 inventory.update_online_status(&online_map)?;
cafd51bf
DM
209
210 Ok(())
211}