]>
Commit | Line | Data |
---|---|---|
d4ab4070 DM |
1 | //! Tape drive/changer configuration |
2 | //! | |
3 | //! This configuration module is based on [`SectionConfig`], and | |
a79082a0 | 4 | //! provides a type safe interface to store [`LtoTapeDrive`], |
d4ab4070 DM |
5 | //! [`VirtualTapeDrive`] and [`ScsiTapeChanger`] configurations. |
6 | //! | |
7 | //! Drive type [`VirtualTapeDrive`] is only useful for debugging. | |
8 | //! | |
a79082a0 | 9 | //! [LtoTapeDrive]: crate::api2::types::LtoTapeDrive |
d4ab4070 DM |
10 | //! [VirtualTapeDrive]: crate::api2::types::VirtualTapeDrive |
11 | //! [ScsiTapeChanger]: crate::api2::types::ScsiTapeChanger | |
12 | //! [SectionConfig]: proxmox::api::section_config::SectionConfig | |
13 | ||
a0765714 DM |
14 | use std::collections::HashMap; |
15 | ||
16 | use anyhow::{bail, Error}; | |
17 | use lazy_static::lazy_static; | |
18 | ||
6ef1b649 WB |
19 | use proxmox_schema::*; |
20 | use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin}; | |
a0765714 | 21 | |
1ce8e905 DM |
22 | use crate::{open_backup_lockfile, replace_backup_config, BackupLockGuard}; |
23 | ||
24 | use pbs_api_types::{ | |
25 | DRIVE_NAME_SCHEMA, VirtualTapeDrive, LtoTapeDrive, ScsiTapeChanger, | |
a0765714 DM |
26 | }; |
27 | ||
1ce8e905 | 28 | |
a0765714 | 29 | lazy_static! { |
d4ab4070 | 30 | /// Static [`SectionConfig`] to access parser/writer functions. |
a0765714 DM |
31 | pub static ref CONFIG: SectionConfig = init(); |
32 | } | |
33 | ||
34 | ||
35 | fn init() -> SectionConfig { | |
49c965a4 | 36 | let mut config = SectionConfig::new(&DRIVE_NAME_SCHEMA); |
a0765714 DM |
37 | |
38 | let obj_schema = match VirtualTapeDrive::API_SCHEMA { | |
39 | Schema::Object(ref obj_schema) => obj_schema, | |
40 | _ => unreachable!(), | |
41 | }; | |
42 | let plugin = SectionConfigPlugin::new("virtual".to_string(), Some("name".to_string()), obj_schema); | |
43 | config.register_plugin(plugin); | |
44 | ||
a79082a0 | 45 | let obj_schema = match LtoTapeDrive::API_SCHEMA { |
a0765714 DM |
46 | Schema::Object(ref obj_schema) => obj_schema, |
47 | _ => unreachable!(), | |
48 | }; | |
a79082a0 | 49 | let plugin = SectionConfigPlugin::new("lto".to_string(), Some("name".to_string()), obj_schema); |
a0765714 DM |
50 | config.register_plugin(plugin); |
51 | ||
52 | let obj_schema = match ScsiTapeChanger::API_SCHEMA { | |
53 | Schema::Object(ref obj_schema) => obj_schema, | |
54 | _ => unreachable!(), | |
55 | }; | |
56 | let plugin = SectionConfigPlugin::new("changer".to_string(), Some("name".to_string()), obj_schema); | |
57 | config.register_plugin(plugin); | |
58 | config | |
59 | } | |
60 | ||
d4ab4070 | 61 | /// Configuration file name |
a0765714 | 62 | pub const DRIVE_CFG_FILENAME: &str = "/etc/proxmox-backup/tape.cfg"; |
d4ab4070 | 63 | /// Lock file name (used to prevent concurrent access) |
a0765714 DM |
64 | pub const DRIVE_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.tape.lck"; |
65 | ||
d4ab4070 | 66 | /// Get exclusive lock |
7526d864 DM |
67 | pub fn lock() -> Result<BackupLockGuard, Error> { |
68 | open_backup_lockfile(DRIVE_CFG_LOCKFILE, None, true) | |
a0765714 DM |
69 | } |
70 | ||
d4ab4070 | 71 | /// Read and parse the configuration file |
a0765714 DM |
72 | pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> { |
73 | ||
25877d05 | 74 | let content = proxmox_sys::fs::file_read_optional_string(DRIVE_CFG_FILENAME)? |
e062ebbc | 75 | .unwrap_or_else(|| "".to_string()); |
a0765714 DM |
76 | |
77 | let digest = openssl::sha::sha256(content.as_bytes()); | |
78 | let data = CONFIG.parse(DRIVE_CFG_FILENAME, &content)?; | |
79 | Ok((data, digest)) | |
80 | } | |
81 | ||
d4ab4070 | 82 | /// Save the configuration file |
a0765714 | 83 | pub fn save_config(config: &SectionConfigData) -> Result<(), Error> { |
9a37bd6c | 84 | let raw = CONFIG.write(DRIVE_CFG_FILENAME, config)?; |
1ce8e905 | 85 | replace_backup_config(DRIVE_CFG_FILENAME, raw.as_bytes()) |
a0765714 DM |
86 | } |
87 | ||
d4ab4070 | 88 | /// Check if the specified drive name exists in the config. |
a0765714 DM |
89 | pub fn check_drive_exists(config: &SectionConfigData, drive: &str) -> Result<(), Error> { |
90 | match config.sections.get(drive) { | |
91 | Some((section_type, _)) => { | |
a79082a0 | 92 | if !(section_type == "lto" || section_type == "virtual") { |
a0765714 DM |
93 | bail!("Entry '{}' exists, but is not a tape drive", drive); |
94 | } | |
95 | } | |
96 | None => bail!("Drive '{}' does not exist", drive), | |
97 | } | |
98 | Ok(()) | |
99 | } | |
100 | ||
101 | ||
102 | // shell completion helper | |
103 | ||
104 | /// List all drive names | |
105 | pub fn complete_drive_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> { | |
106 | match config() { | |
107 | Ok((data, _digest)) => data.sections.iter() | |
108 | .map(|(id, _)| id.to_string()) | |
109 | .collect(), | |
110 | Err(_) => return vec![], | |
111 | } | |
112 | } | |
113 | ||
a79082a0 DM |
114 | /// List Lto tape drives |
115 | pub fn complete_lto_drive_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> { | |
a0765714 DM |
116 | match config() { |
117 | Ok((data, _digest)) => data.sections.iter() | |
118 | .filter(|(_id, (section_type, _))| { | |
a79082a0 | 119 | section_type == "lto" |
a0765714 DM |
120 | }) |
121 | .map(|(id, _)| id.to_string()) | |
122 | .collect(), | |
123 | Err(_) => return vec![], | |
124 | } | |
125 | } | |
126 | ||
127 | /// List Scsi tape changer names | |
128 | pub fn complete_changer_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> { | |
129 | match config() { | |
130 | Ok((data, _digest)) => data.sections.iter() | |
131 | .filter(|(_id, (section_type, _))| { | |
132 | section_type == "changer" | |
133 | }) | |
134 | .map(|(id, _)| id.to_string()) | |
135 | .collect(), | |
136 | Err(_) => return vec![], | |
137 | } | |
138 | } |