bsudlib/
lvm.rs

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}