2 use std
::collections
::{HashMap, HashSet}
;
4 use anyhow
::{bail, Error}
;
6 use proxmox_section_config
::SectionConfigData
;
7 use proxmox_uuid
::Uuid
;
9 use pbs_api_types
::{VirtualTapeDrive, ScsiTapeChanger}
;
10 use pbs_tape
::{ElementStatus, MtxStatus}
;
12 use crate::tape
::Inventory
;
13 use crate::tape
::changer
::{MediaChange, ScsiMediaChange}
;
15 /// Helper to update media online status
17 /// A tape media is considered online if it is accessible by a changer
18 /// device. This class can store the list of available changes,
19 /// together with the accessible media ids.
20 pub struct OnlineStatusMap
{
21 map
: HashMap
<String
, Option
<HashSet
<Uuid
>>>,
22 changer_map
: HashMap
<Uuid
, String
>,
25 impl OnlineStatusMap
{
27 /// Creates a new instance with one map entry for each configured
28 /// changer (or 'VirtualTapeDrive', which has an internal
29 /// changer). The map entry is set to 'None' to indicate that we
30 /// do not have information about the online status.
31 pub fn new(config
: &SectionConfigData
) -> Result
<Self, Error
> {
33 let mut map
= HashMap
::new();
35 let changers
: Vec
<ScsiTapeChanger
> = config
.convert_to_typed_array("changer")?
;
36 for changer
in changers
{
37 map
.insert(changer
.name
.clone(), None
);
40 let vtapes
: Vec
<VirtualTapeDrive
> = config
.convert_to_typed_array("virtual")?
;
42 map
.insert(vtape
.name
.clone(), None
);
45 Ok(Self { map, changer_map: HashMap::new() }
)
48 /// Returns the assiciated changer name for a media.
49 pub fn lookup_changer(&self, uuid
: &Uuid
) -> Option
<&String
> {
50 self.changer_map
.get(uuid
)
53 /// Returns the map which assiciates media uuids with changer names.
54 pub fn changer_map(&self) -> &HashMap
<Uuid
, String
> {
58 /// Returns the set of online media for the specified changer.
59 pub fn online_map(&self, changer_name
: &str) -> Option
<&Option
<HashSet
<Uuid
>>> {
60 self.map
.get(changer_name
)
63 /// Update the online set for the specified changer
64 pub fn update_online_status(&mut self, changer_name
: &str, online_set
: HashSet
<Uuid
>) -> Result
<(), Error
> {
66 match self.map
.get(changer_name
) {
67 None
=> bail
!("no such changer '{}' device", changer_name
),
68 Some(None
) => { /* Ok */ }
,
70 // do not allow updates to keep self.changer_map consistent
71 bail
!("update_online_status '{}' called twice", changer_name
);
75 for uuid
in online_set
.iter() {
76 self.changer_map
.insert(uuid
.clone(), changer_name
.to_string());
79 self.map
.insert(changer_name
.to_string(), Some(online_set
));
85 /// Extract the list of online media from MtxStatus
87 /// Returns a HashSet containing all found media Uuid. This only
88 /// returns media found in Inventory.
89 pub fn mtx_status_to_online_set(status
: &MtxStatus
, inventory
: &Inventory
) -> HashSet
<Uuid
> {
91 let mut online_set
= HashSet
::new();
93 for drive_status
in status
.drives
.iter() {
94 if let ElementStatus
::VolumeTag(ref label_text
) = drive_status
.status
{
95 if let Some(media_id
) = inventory
.find_media_by_label_text(label_text
) {
96 online_set
.insert(media_id
.label
.uuid
.clone());
101 for slot_info
in status
.slots
.iter() {
102 if slot_info
.import_export { continue; }
103 if let ElementStatus
::VolumeTag(ref label_text
) = slot_info
.status
{
104 if let Some(media_id
) = inventory
.find_media_by_label_text(label_text
) {
105 online_set
.insert(media_id
.label
.uuid
.clone());
113 /// Update online media status
115 /// For a single 'changer', or else simply ask all changer devices.
116 pub fn update_online_status(state_path
: &Path
, changer
: Option
<&str>) -> Result
<OnlineStatusMap
, Error
> {
118 let (config
, _digest
) = pbs_config
::drive
::config()?
;
120 let mut inventory
= Inventory
::load(state_path
)?
;
122 let changers
: Vec
<ScsiTapeChanger
> = config
.convert_to_typed_array("changer")?
;
124 let mut map
= OnlineStatusMap
::new(&config
)?
;
126 let mut found_changer
= false;
128 for mut changer_config
in changers
{
129 if let Some(changer
) = changer
{
130 if changer
!= &changer_config
.name
{
133 found_changer
= true;
135 let status
= match changer_config
.status(false) {
136 Ok(status
) => status
,
138 eprintln
!("unable to get changer '{}' status - {}", changer_config
.name
, err
);
143 let online_set
= mtx_status_to_online_set(&status
, &inventory
);
144 map
.update_online_status(&changer_config
.name
, online_set
)?
;
147 let vtapes
: Vec
<VirtualTapeDrive
> = config
.convert_to_typed_array("virtual")?
;
148 for mut vtape
in vtapes
{
149 if let Some(changer
) = changer
{
150 if changer
!= &vtape
.name
{
153 found_changer
= true;
156 let media_list
= match vtape
.online_media_label_texts() {
157 Ok(media_list
) => media_list
,
159 eprintln
!("unable to get changer '{}' status - {}", vtape
.name
, err
);
164 let mut online_set
= HashSet
::new();
165 for label_text
in media_list
{
166 if let Some(media_id
) = inventory
.find_media_by_label_text(&label_text
) {
167 online_set
.insert(media_id
.label
.uuid
.clone());
170 map
.update_online_status(&vtape
.name
, online_set
)?
;
173 if let Some(changer
) = changer
{
175 bail
!("update_online_status failed - no such changer '{}'", changer
);
179 inventory
.update_online_status(&map
)?
;
184 /// Update online media status with data from a single changer device
185 pub fn update_changer_online_status(
186 drive_config
: &SectionConfigData
,
187 inventory
: &mut Inventory
,
189 label_text_list
: &[String
],
190 ) -> Result
<(), Error
> {
192 let mut online_map
= OnlineStatusMap
::new(drive_config
)?
;
193 let mut online_set
= HashSet
::new();
194 for label_text
in label_text_list
.iter() {
195 if let Some(media_id
) = inventory
.find_media_by_label_text(label_text
) {
196 online_set
.insert(media_id
.label
.uuid
.clone());
199 online_map
.update_online_status(changer_name
, online_set
)?
;
200 inventory
.update_online_status(&online_map
)?
;