]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/config/drive.rs
update to proxmox-sys 0.2 crate
[proxmox-backup.git] / src / api2 / config / drive.rs
1 use anyhow::{bail, Error};
2 use ::serde::{Deserialize, Serialize};
3 use serde_json::Value;
4 use hex::FromHex;
5
6 use proxmox_router::{Router, RpcEnvironment, Permission};
7 use proxmox_schema::api;
8
9 use pbs_api_types::{
10 Authid, LtoTapeDrive, LtoTapeDriveUpdater, ScsiTapeChanger,
11 PROXMOX_CONFIG_DIGEST_SCHEMA, DRIVE_NAME_SCHEMA, PRIV_TAPE_AUDIT, PRIV_TAPE_MODIFY,
12 };
13 use pbs_config::CachedUserInfo;
14
15 use pbs_tape::linux_list_drives::{lto_tape_device_list, check_drive_path};
16
17 #[api(
18 protected: true,
19 input: {
20 properties: {
21 config: {
22 type: LtoTapeDrive,
23 flatten: true,
24 },
25 },
26 },
27 access: {
28 permission: &Permission::Privilege(&["tape", "device"], PRIV_TAPE_MODIFY, false),
29 },
30 )]
31 /// Create a new drive
32 pub fn create_drive(config: LtoTapeDrive) -> Result<(), Error> {
33
34 let _lock = pbs_config::drive::lock()?;
35
36 let (mut section_config, _digest) = pbs_config::drive::config()?;
37
38 let lto_drives = lto_tape_device_list();
39
40 check_drive_path(&lto_drives, &config.path)?;
41
42 let existing: Vec<LtoTapeDrive> = section_config.convert_to_typed_array("lto")?;
43
44 for drive in existing {
45 if drive.name == config.name {
46 bail!("Entry '{}' already exists", config.name);
47 }
48 if drive.path == config.path {
49 bail!("Path '{}' already used in drive '{}'", config.path, drive.name);
50 }
51 }
52
53 section_config.set_data(&config.name, "lto", &config)?;
54
55 pbs_config::drive::save_config(&section_config)?;
56
57 Ok(())
58 }
59
60 #[api(
61 input: {
62 properties: {
63 name: {
64 schema: DRIVE_NAME_SCHEMA,
65 },
66 },
67 },
68 returns: {
69 type: LtoTapeDrive,
70 },
71 access: {
72 permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_AUDIT, false),
73 },
74 )]
75 /// Get drive configuration
76 pub fn get_config(
77 name: String,
78 _param: Value,
79 mut rpcenv: &mut dyn RpcEnvironment,
80 ) -> Result<LtoTapeDrive, Error> {
81
82 let (config, digest) = pbs_config::drive::config()?;
83
84 let data: LtoTapeDrive = config.lookup("lto", &name)?;
85
86 rpcenv["digest"] = hex::encode(&digest).into();
87
88 Ok(data)
89 }
90
91 #[api(
92 input: {
93 properties: {},
94 },
95 returns: {
96 description: "The list of configured drives (with config digest).",
97 type: Array,
98 items: {
99 type: LtoTapeDrive,
100 },
101 },
102 access: {
103 description: "List configured tape drives filtered by Tape.Audit privileges",
104 permission: &Permission::Anybody,
105 },
106 )]
107 /// List drives
108 pub fn list_drives(
109 _param: Value,
110 mut rpcenv: &mut dyn RpcEnvironment,
111 ) -> Result<Vec<LtoTapeDrive>, Error> {
112 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
113 let user_info = CachedUserInfo::new()?;
114
115 let (config, digest) = pbs_config::drive::config()?;
116
117 let drive_list: Vec<LtoTapeDrive> = config.convert_to_typed_array("lto")?;
118
119 let drive_list = drive_list
120 .into_iter()
121 .filter(|drive| {
122 let privs = user_info.lookup_privs(&auth_id, &["tape", "device", &drive.name]);
123 privs & PRIV_TAPE_AUDIT != 0
124 })
125 .collect();
126
127 rpcenv["digest"] = hex::encode(&digest).into();
128
129 Ok(drive_list)
130 }
131
132 #[api()]
133 #[derive(Serialize, Deserialize)]
134 #[allow(non_camel_case_types)]
135 #[serde(rename_all = "kebab-case")]
136 /// Deletable property name
137 pub enum DeletableProperty {
138 /// Delete the changer property.
139 changer,
140 /// Delete the changer-drivenum property.
141 changer_drivenum,
142 }
143
144 #[api(
145 protected: true,
146 input: {
147 properties: {
148 name: {
149 schema: DRIVE_NAME_SCHEMA,
150 },
151 update: {
152 type: LtoTapeDriveUpdater,
153 flatten: true,
154 },
155 delete: {
156 description: "List of properties to delete.",
157 type: Array,
158 optional: true,
159 items: {
160 type: DeletableProperty,
161 }
162 },
163 digest: {
164 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
165 optional: true,
166 },
167 },
168 },
169 access: {
170 permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY, false),
171 },
172 )]
173 /// Update a drive configuration
174 pub fn update_drive(
175 name: String,
176 update: LtoTapeDriveUpdater,
177 delete: Option<Vec<DeletableProperty>>,
178 digest: Option<String>,
179 _param: Value,
180 ) -> Result<(), Error> {
181
182 let _lock = pbs_config::drive::lock()?;
183
184 let (mut config, expected_digest) = pbs_config::drive::config()?;
185
186 if let Some(ref digest) = digest {
187 let digest = <[u8; 32]>::from_hex(digest)?;
188 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
189 }
190
191 let mut data: LtoTapeDrive = config.lookup("lto", &name)?;
192
193 if let Some(delete) = delete {
194 for delete_prop in delete {
195 match delete_prop {
196 DeletableProperty::changer => {
197 data.changer = None;
198 data.changer_drivenum = None;
199 },
200 DeletableProperty::changer_drivenum => { data.changer_drivenum = None; },
201 }
202 }
203 }
204
205 if let Some(path) = update.path {
206 let lto_drives = lto_tape_device_list();
207 check_drive_path(&lto_drives, &path)?;
208 data.path = path;
209 }
210
211 if let Some(changer) = update.changer {
212 let _: ScsiTapeChanger = config.lookup("changer", &changer)?;
213 data.changer = Some(changer);
214 }
215
216 if let Some(changer_drivenum) = update.changer_drivenum {
217 if changer_drivenum == 0 {
218 data.changer_drivenum = None;
219 } else {
220 if data.changer.is_none() {
221 bail!("Option 'changer-drivenum' requires option 'changer'.");
222 }
223 data.changer_drivenum = Some(changer_drivenum);
224 }
225 }
226
227 config.set_data(&name, "lto", &data)?;
228
229 pbs_config::drive::save_config(&config)?;
230
231 Ok(())
232 }
233
234 #[api(
235 protected: true,
236 input: {
237 properties: {
238 name: {
239 schema: DRIVE_NAME_SCHEMA,
240 },
241 },
242 },
243 access: {
244 permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY, false),
245 },
246 )]
247 /// Delete a drive configuration
248 pub fn delete_drive(name: String, _param: Value) -> Result<(), Error> {
249
250 let _lock = pbs_config::drive::lock()?;
251
252 let (mut config, _digest) = pbs_config::drive::config()?;
253
254 match config.sections.get(&name) {
255 Some((section_type, _)) => {
256 if section_type != "lto" {
257 bail!("Entry '{}' exists, but is not a lto tape drive", name);
258 }
259 config.sections.remove(&name);
260 },
261 None => bail!("Delete drive '{}' failed - no such drive", name),
262 }
263
264 pbs_config::drive::save_config(&config)?;
265
266 Ok(())
267 }
268
269 const ITEM_ROUTER: Router = Router::new()
270 .get(&API_METHOD_GET_CONFIG)
271 .put(&API_METHOD_UPDATE_DRIVE)
272 .delete(&API_METHOD_DELETE_DRIVE);
273
274
275 pub const ROUTER: Router = Router::new()
276 .get(&API_METHOD_LIST_DRIVES)
277 .post(&API_METHOD_CREATE_DRIVE)
278 .match_all("name", &ITEM_ROUTER);