1use crate::utils::bytes_to_gib;
2use crate::utils::exec;
3use crate::utils::exec_bool;
4use easy_error::format_err;
5use log::debug;
6use serde_derive::Deserialize;
7use serde_derive::Serialize;
8use std::error::Error;
9
10const LV_NAME: &str = "bsud";
11
12pub fn lv_path(drive_name: &str) -> String {
13 let drive_name = drive_name.replace('-', "--");
14 format!("/dev/mapper/{}-{}", drive_name, LV_NAME)
15}
16
17pub fn get_reports() -> Result<Vec<Lvm>, Box<dyn Error>> {
18 let output = exec(
19 "lvm",
20 &[
21 "fullreport",
22 "--all",
23 "--units",
24 "B",
25 "--reportformat",
26 "json",
27 ],
28 )?;
29 let desc: JsonDesc = serde_json::from_str(output.stdout.as_str())?;
30 Ok(desc.report)
31}
32
33pub fn get_report(name: &String) -> Result<Option<Lvm>, Box<dyn Error>> {
34 let all_lvm = get_reports()?;
35 for lvm in all_lvm {
36 let Some(vg) = lvm.vg.first() else {
37 continue;
38 };
39 if vg.vg_name == *name {
40 return Ok(Some(lvm));
41 }
42 }
43 Ok(None)
44}
45
46pub fn get_report_with_no_vg() -> Result<Option<Lvm>, Box<dyn Error>> {
47 let all_lvm = get_reports()?;
48 for lvm in all_lvm {
49 if lvm.vg.is_empty() {
50 return Ok(Some(lvm));
51 }
52 }
53 Ok(None)
54}
55
56pub fn get_vg(name: &String) -> Result<Vg, Box<dyn Error>> {
57 let Some(lvm) = get_report(name)? else {
58 return Err(Box::new(format_err!(
59 "\"{}\" drive: Cannot get LVM description",
60 name
61 )));
62 };
63 let Some(vg) = lvm.vg.into_iter().next() else {
64 return Err(Box::new(format_err!(
65 "\"{}\" drive: Cannot get VG description",
66 name
67 )));
68 };
69 Ok(vg)
70}
71
72pub fn get_lv(name: &String) -> Result<Lv, Box<dyn Error>> {
73 let Some(lvm) = get_report(name)? else {
74 return Err(Box::new(format_err!(
75 "\"{}\" drive: Cannot get LVM description",
76 name
77 )));
78 };
79 let Some(lv) = lvm.lv.into_iter().next() else {
80 return Err(Box::new(format_err!(
81 "\"{}\" drive: Cannot get LV description",
82 name
83 )));
84 };
85 Ok(lv)
86}
87
88pub fn init_pv(path: &String) -> Result<(), Box<dyn Error>> {
89 exec("lvm", &["pvcreate", path])?;
90 Ok(())
91}
92
93pub fn vg_create(vg_name: &String, initial_pv_path: &String) -> Result<(), Box<dyn Error>> {
94 exec(
95 "lvm",
96 &["vgcreate", "--alloc", "normal", vg_name, initial_pv_path],
97 )?;
98 Ok(())
99}
100
101pub fn vg_activate(activate: bool, vg_name: &String) -> Result<(), Box<dyn Error>> {
102 if activate {
103 exec("vgchange", &["-ay", vg_name])?;
104 } else {
105 exec("vgchange", &["-an", vg_name])?;
106 }
107 Ok(())
108}
109
110pub fn extend_vg(vg_name: &String, pv_device_path: &String) -> Result<(), Box<dyn Error>> {
111 exec("lvm", &["vgextend", vg_name, pv_device_path])?;
112 Ok(())
113}
114
115pub fn create_lv(vg_name: &String) -> Result<(), Box<dyn Error>> {
116 exec(
117 "lvm",
118 &["lvcreate", "--extents", "100%FREE", "-n", LV_NAME, vg_name],
119 )?;
120 Ok(())
121}
122
123pub fn get_vg_size_bytes(vg_name: &String) -> Result<usize, Box<dyn Error>> {
124 let mut vg = get_vg(vg_name)?;
125 vg.vg_size.pop();
126 let vg_size_bytes = vg.vg_size.parse::<usize>()?;
127 Ok(vg_size_bytes)
128}
129
130pub fn get_lv_size_bytes(vg_name: &String) -> Result<usize, Box<dyn Error>> {
131 let mut lv = get_lv(vg_name)?;
132 lv.lv_size.pop();
133 let lv_size_bytes = lv.lv_size.parse::<usize>()?;
134 Ok(lv_size_bytes)
135}
136
137pub fn lv_extend_full(lv_path: &String) -> Result<(), Box<dyn Error>> {
138 exec("lvm", &["lvextend", "--extents", "+100%FREE", lv_path])?;
139 Ok(())
140}
141
142pub fn lv_activate(activate: bool, lv_name: &String) -> Result<(), Box<dyn Error>> {
143 if activate {
144 exec("lvchange", &["-ay", lv_name])?;
145 } else {
146 exec("lvchange", &["-an", lv_name])?;
147 }
148 Ok(())
149}
150
151pub fn vg_scan() -> Result<(), Box<dyn Error>> {
152 exec("vgscan", &[])?;
153 Ok(())
154}
155
156pub fn pv_move(pv_path: &String) -> Result<(), Box<dyn Error>> {
157 exec_bool("lvm", &["pvmove", pv_path])?;
158 Ok(())
159}
160
161pub fn pv_move_no_arg() -> Result<(), Box<dyn Error>> {
162 exec_bool("lvm", &["pvmove"])?;
163 Ok(())
164}
165
166pub fn lv_reduce(lv_path: &String, new_fs_size_bytes: usize) -> Result<(), Box<dyn Error>> {
167 debug!(
168 "lv_reduce {} of size {}B ({}GiB)",
169 lv_path,
170 new_fs_size_bytes,
171 bytes_to_gib(new_fs_size_bytes)
172 );
173 exec(
174 "lvm",
175 &[
176 "lvreduce",
177 "--yes",
178 "--size",
179 format!("{}B", new_fs_size_bytes).as_str(),
180 lv_path,
181 ],
182 )?;
183 Ok(())
184}
185
186pub fn vg_reduce(name: &str, device_path: &str) -> Result<(), Box<dyn Error>> {
187 exec("lvm", &["vgreduce", name, device_path])?;
188 Ok(())
189}
190
191pub fn pv_remove(device_path: &str) -> Result<(), Box<dyn Error>> {
192 exec("lvm", &["pvremove", device_path])?;
193 Ok(())
194}
195
196#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
197#[serde(default)]
198pub struct JsonDesc {
199 pub report: Vec<Lvm>,
200}
201
202#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
203#[serde(default)]
204pub struct Lvm {
205 pub vg: Vec<Vg>,
206 pub pv: Vec<Pv>,
207 pub lv: Vec<Lv>,
208 pub pvseg: Vec<Pvseg>,
209 pub seg: Vec<Seg>,
210}
211
212impl Lvm {
213 pub fn devices(&self) -> Vec<String> {
214 let mut all_devices = Vec::new();
215 for pv in self.pv.iter() {
216 all_devices.push(pv.pv_name.clone());
217 }
218 all_devices
219 }
220}
221
222#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
223#[serde(default)]
224pub struct Vg {
225 pub vg_fmt: String,
226 pub vg_uuid: String,
227 pub vg_name: String,
228 pub vg_attr: String,
229 pub vg_permissions: String,
230 pub vg_extendable: String,
231 pub vg_exported: String,
232 pub vg_autoactivation: String,
233 pub vg_partial: String,
234 pub vg_allocation_policy: String,
235 pub vg_clustered: String,
236 pub vg_shared: String,
237 pub vg_size: String,
238 pub vg_free: String,
239 pub vg_sysid: String,
240 pub vg_systemid: String,
241 pub vg_lock_type: String,
242 pub vg_lock_args: String,
243 pub vg_extent_size: String,
244 pub vg_extent_count: String,
245 pub vg_free_count: String,
246 pub max_lv: String,
247 pub max_pv: String,
248 pub pv_count: String,
249 pub vg_missing_pv_count: String,
250 pub lv_count: String,
251 pub snap_count: String,
252 pub vg_seqno: String,
253 pub vg_tags: String,
254 pub vg_profile: String,
255 pub vg_mda_count: String,
256 pub vg_mda_used_count: String,
257 pub vg_mda_free: String,
258 pub vg_mda_size: String,
259 pub vg_mda_copies: String,
260}
261
262#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
263#[serde(default)]
264pub struct Pv {
265 pub pv_fmt: String,
266 pub pv_uuid: String,
267 pub dev_size: String,
268 pub pv_name: String,
269 pub pv_major: String,
270 pub pv_minor: String,
271 pub pv_mda_free: String,
272 pub pv_mda_size: String,
273 pub pv_ext_vsn: String,
274 pub pe_start: String,
275 pub pv_size: String,
276 pub pv_free: String,
277 pub pv_used: String,
278 pub pv_attr: String,
279 pub pv_allocatable: String,
280 pub pv_exported: String,
281 pub pv_missing: String,
282 pub pv_pe_count: String,
283 pub pv_pe_alloc_count: String,
284 pub pv_tags: String,
285 pub pv_mda_count: String,
286 pub pv_mda_used_count: String,
287 pub pv_ba_start: String,
288 pub pv_ba_size: String,
289 pub pv_in_use: String,
290 pub pv_duplicate: String,
291 pub pv_device_id: String,
292 pub pv_device_id_type: String,
293}
294
295#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
296#[serde(default)]
297pub struct Lv {
298 pub lv_uuid: String,
299 pub lv_name: String,
300 pub lv_full_name: String,
301 pub lv_path: String,
302 pub lv_dm_path: String,
303 pub lv_parent: String,
304 pub lv_layout: String,
305 pub lv_role: String,
306 pub lv_initial_image_sync: String,
307 pub lv_image_synced: String,
308 pub lv_merging: String,
309 pub lv_converting: String,
310 pub lv_allocation_policy: String,
311 pub lv_allocation_locked: String,
312 pub lv_fixed_minor: String,
313 pub lv_skip_activation: String,
314 pub lv_autoactivation: String,
315 pub lv_when_full: String,
316 pub lv_active: String,
317 pub lv_active_locally: String,
318 pub lv_active_remotely: String,
319 pub lv_active_exclusively: String,
320 pub lv_major: String,
321 pub lv_minor: String,
322 pub lv_read_ahead: String,
323 pub lv_size: String,
324 pub lv_metadata_size: String,
325 pub seg_count: String,
326 pub origin: String,
327 pub origin_uuid: String,
328 pub origin_size: String,
329 pub lv_ancestors: String,
330 pub lv_full_ancestors: String,
331 pub lv_descendants: String,
332 pub lv_full_descendants: String,
333 pub raid_mismatch_count: String,
334 pub raid_sync_action: String,
335 pub raid_write_behind: String,
336 pub raid_min_recovery_rate: String,
337 pub raid_max_recovery_rate: String,
338 pub raidintegritymode: String,
339 pub raidintegrityblocksize: String,
340 pub integritymismatches: String,
341 pub move_pv: String,
342 pub move_pv_uuid: String,
343 pub convert_lv: String,
344 pub convert_lv_uuid: String,
345 pub mirror_log: String,
346 pub mirror_log_uuid: String,
347 pub data_lv: String,
348 pub data_lv_uuid: String,
349 pub metadata_lv: String,
350 pub metadata_lv_uuid: String,
351 pub pool_lv: String,
352 pub pool_lv_uuid: String,
353 pub lv_tags: String,
354 pub lv_profile: String,
355 pub lv_lockargs: String,
356 pub lv_time: String,
357 pub lv_time_removed: String,
358 pub lv_host: String,
359 pub lv_modules: String,
360 pub lv_historical: String,
361 pub writecache_block_size: String,
362 pub lv_kernel_major: String,
363 pub lv_kernel_minor: String,
364 pub lv_kernel_read_ahead: String,
365 pub lv_permissions: String,
366 pub lv_suspended: String,
367 pub lv_live_table: String,
368 pub lv_inactive_table: String,
369 pub lv_device_open: String,
370 pub data_percent: String,
371 pub snap_percent: String,
372 pub metadata_percent: String,
373 pub copy_percent: String,
374 pub sync_percent: String,
375 pub cache_total_blocks: String,
376 pub cache_used_blocks: String,
377 pub cache_dirty_blocks: String,
378 pub cache_read_hits: String,
379 pub cache_read_misses: String,
380 pub cache_write_hits: String,
381 pub cache_write_misses: String,
382 pub kernel_cache_settings: String,
383 pub kernel_cache_policy: String,
384 pub kernel_metadata_format: String,
385 pub lv_health_status: String,
386 pub kernel_discards: String,
387 pub lv_check_needed: String,
388 pub lv_merge_failed: String,
389 pub lv_snapshot_invalid: String,
390 pub vdo_operating_mode: String,
391 pub vdo_compression_state: String,
392 pub vdo_index_state: String,
393 pub vdo_used_size: String,
394 pub vdo_saving_percent: String,
395 pub writecache_total_blocks: String,
396 pub writecache_free_blocks: String,
397 pub writecache_writeback_blocks: String,
398 pub writecache_error: String,
399 pub lv_attr: String,
400}
401
402#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
403#[serde(default)]
404pub struct Pvseg {
405 pub pvseg_start: String,
406 pub pvseg_size: String,
407 pub pv_uuid: String,
408 pub lv_uuid: String,
409}
410
411#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
412#[serde(default)]
413pub struct Seg {
414 pub segtype: String,
415 pub stripes: String,
416 pub data_stripes: String,
417 pub reshape_len: String,
418 pub reshape_len_le: String,
419 pub data_copies: String,
420 pub data_offset: String,
421 pub new_data_offset: String,
422 pub parity_chunks: String,
423 pub stripe_size: String,
424 pub region_size: String,
425 pub chunk_size: String,
426 pub thin_count: String,
427 pub discards: String,
428 pub cache_metadata_format: String,
429 pub cache_mode: String,
430 pub zero: String,
431 pub transaction_id: String,
432 pub thin_id: String,
433 pub seg_start: String,
434 pub seg_start_pe: String,
435 pub seg_size: String,
436 pub seg_size_pe: String,
437 pub seg_tags: String,
438 pub seg_pe_ranges: String,
439 pub seg_le_ranges: String,
440 pub seg_metadata_le_ranges: String,
441 pub devices: String,
442 pub metadata_devices: String,
443 pub seg_monitor: String,
444 pub cache_policy: String,
445 pub cache_settings: String,
446 pub vdo_compression: String,
447 pub vdo_deduplication: String,
448 pub vdo_use_metadata_hints: String,
449 pub vdo_minimum_io_size: String,
450 pub vdo_block_map_cache_size: String,
451 pub vdo_block_map_era_length: String,
452 pub vdo_use_sparse_index: String,
453 pub vdo_index_memory_size: String,
454 pub vdo_slab_size: String,
455 pub vdo_ack_threads: String,
456 pub vdo_bio_threads: String,
457 pub vdo_bio_rotation: String,
458 pub vdo_cpu_threads: String,
459 pub vdo_hash_zone_threads: String,
460 pub vdo_logical_threads: String,
461 pub vdo_physical_threads: String,
462 pub vdo_max_discard: String,
463 pub vdo_write_policy: String,
464 pub vdo_header_size: String,
465 pub lv_uuid: String,
466}