diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9ecc48e..f48bd00 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,6 +31,10 @@ rust-latest: paths: - "./raid_manager_static" - "./raid_manager" + cache: + key: shared-rust-latest-cache + paths: + - lib/target/ rust-nightly: stage: build_backend @@ -42,3 +46,7 @@ rust-nightly: - cargo build -r --manifest-path=lib/Cargo.toml - cargo test -r --manifest-path=lib/Cargo.toml allow_failure: true + cache: + key: shared-rust-nightly-cache + paths: + - lib/target/ diff --git a/lib/Cargo.lock b/lib/Cargo.lock index a5bf553..3c1d6f8 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -573,6 +573,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.4" @@ -646,6 +655,12 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "mio" version = "0.8.5" @@ -678,6 +693,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -838,6 +863,7 @@ dependencies = [ "regex", "rocket", "rust-embed", + "versions", ] [[package]] @@ -1456,6 +1482,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "versions" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee97e1d97bd593fb513912a07691b742361b3dd64ad56f2c694ea2dbfe0665d3" +dependencies = [ + "itertools", + "nom", +] + [[package]] name = "walkdir" version = "2.3.2" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 32628a4..d1938a0 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -9,6 +9,7 @@ regex = "1" lazy_static = "1.4.0" include_dir = "0.7.3" rust-embed = "6.4.2" +versions = "4.1.0" [features] static = [] diff --git a/lib/src/parser/lsblk_parser.rs b/lib/src/parser/lsblk_parser.rs index 0831413..0fd12a1 100644 --- a/lib/src/parser/lsblk_parser.rs +++ b/lib/src/parser/lsblk_parser.rs @@ -1,12 +1,17 @@ use std::process::Command; +use lazy_static::lazy_static; +use regex::{Regex}; use rocket::serde::json::{serde_json}; use rocket::serde::{Serialize, Deserialize}; +use versions::Versioning; + #[derive(Serialize)] #[serde(crate = "rocket::serde")] pub struct Disk { name: String, size: u64, + mountpoints: Vec } #[derive(Serialize, Deserialize)] @@ -20,7 +25,10 @@ struct ChildDevice { ro: bool, #[serde(rename = "type")] kind: String, - mountpoints: Vec>, + // new version is with vec of mountpoints + mountpoints: Option>>, + // old version is with one single mountpoint + mountpoint: Option, } #[derive(Serialize, Deserialize)] @@ -34,7 +42,10 @@ struct Blockdevice { majmin: String, rm: bool, ro: bool, - mountpoints: Vec>, + // new version is with vec of mountpoints + mountpoints: Option>>, + // old version is with one single mountpoint + mountpoint: Option, children: Option>, } @@ -44,6 +55,63 @@ struct LsblkT { blockdevices: Vec, } +fn lsblk_version() -> Versioning { + let output = Command::new("lsblk") + .arg("--version") + .output() + .expect("failed to execute process"); + + let out = output.stdout; + let out = String::from_utf8(out).unwrap(); + + lazy_static! { + static ref RE: Regex = Regex::new(r"([0-9]+\.[0-9]+\.[0-9]+)$").unwrap(); + } + + match RE.captures(&out) { + None => Versioning::default(), + Some(res) => { + if res.len() == 2 { + match Versioning::new(&res[1]){ + None => Versioning::default(), + Some(v) => v + } + }else{ + Versioning::default() + } + } + } +} + +fn parse_lsblk_str(out: String) -> Vec { + let v: LsblkT = serde_json::from_str(out.as_str()).unwrap(); + let version = lsblk_version(); + + let mut disks: Vec = Vec::new(); + for d in v.blockdevices { + if d.kind != "disk" { continue; }; + + let mut mountpoints: Vec = Vec::new(); + if version < Versioning::new("2.37.0").unwrap() { + if d.mountpoint.is_some() { + mountpoints.push(d.mountpoint.unwrap()) + } + } else{ + if d.mountpoints.is_some() { + let mntpts = d.mountpoints.unwrap(); + for p in mntpts { + if p.is_some() { + mountpoints.push(p.unwrap()) + } + } + } + } + + disks.push(Disk { name: d.name, size: d.size, mountpoints }); + } + return disks; +} + pub fn parse_lsblk() -> Vec { let output = Command::new("lsblk") .arg("-J") @@ -54,13 +122,70 @@ pub fn parse_lsblk() -> Vec { let out = output.stdout; let out = String::from_utf8(out).unwrap(); - - let v: LsblkT = serde_json::from_str(out.as_str()).unwrap(); - - let mut disks: Vec = Vec::new(); - for d in v.blockdevices { - if d.kind != "disk" { continue; }; - disks.push(Disk { name: d.name, size: d.size }); - } - return disks; + parse_lsblk_str(out) +} + +#[cfg(test)] +mod tests { + use rocket::serde::json::serde_json::json; + // Note this useful idiom: importing names from outer (for mod tests) scope. + use super::*; + + #[test] + fn test_empty() { + assert_eq!(json!(parse_lsblk_str(r#" +{ + "blockdevices": [ + {"name":"/dev/loop0", "maj:min":"7:0", "rm":false, "size":8589934592, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop1", "maj:min":"7:1", "rm":false, "size":8589934592, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop2", "maj:min":"7:2", "rm":false, "size":107374182400, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop3", "maj:min":"7:3", "rm":false, "size":10737418240, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop4", "maj:min":"7:4", "rm":false, "size":34359738368, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop5", "maj:min":"7:5", "rm":false, "size":8589934592, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop6", "maj:min":"7:6", "rm":false, "size":8589934592, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop7", "maj:min":"7:7", "rm":false, "size":8589934592, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop8", "maj:min":"7:8", "rm":false, "size":32212254720, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop9", "maj:min":"7:9", "rm":false, "size":8589934592, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop10", "maj:min":"7:10", "rm":false, "size":8589934592, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop11", "maj:min":"7:11", "rm":false, "size":12884901888, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/loop12", "maj:min":"7:12", "rm":false, "size":8589934592, "ro":false, "type":"loop", "mountpoint":null}, + {"name":"/dev/sda", "maj:min":"8:0", "rm":false, "size":120034123776, "ro":false, "type":"disk", "mountpoint":null, + "children": [ + {"name":"/dev/sda1", "maj:min":"8:1", "rm":false, "size":1031168, "ro":false, "type":"part", "mountpoint":null}, + {"name":"/dev/sda2", "maj:min":"8:2", "rm":false, "size":536870912, "ro":false, "type":"part", "mountpoint":null}, + {"name":"/dev/sda3", "maj:min":"8:3", "rm":false, "size":119496187392, "ro":false, "type":"part", "mountpoint":null, + "children": [ + {"name":"/dev/mapper/pve-swap", "maj:min":"253:0", "rm":false, "size":4294967296, "ro":false, "type":"lvm", "mountpoint":"[SWAP]"}, + {"name":"/dev/mapper/pve-root", "maj:min":"253:1", "rm":false, "size":115196559360, "ro":false, "type":"lvm", "mountpoint":"/"} + ] + } + ] + }, + {"name":"/dev/sdb", "maj:min":"8:16", "rm":false, "size":3000592982016, "ro":false, "type":"disk", "mountpoint":null, + "children": [ + {"name":"/dev/md127", "maj:min":"9:127", "rm":false, "size":9001376219136, "ro":false, "type":"raid5", "mountpoint":"/media/3TBRaid"} + ] + }, + {"name":"/dev/sdc", "maj:min":"8:32", "rm":false, "size":3000592982016, "ro":false, "type":"disk", "mountpoint":null, + "children": [ + {"name":"/dev/md127", "maj:min":"9:127", "rm":false, "size":9001376219136, "ro":false, "type":"raid5", "mountpoint":"/media/3TBRaid"} + ] + }, + {"name":"/dev/sdd", "maj:min":"8:48", "rm":false, "size":4000787030016, "ro":false, "type":"disk", "mountpoint":null, + "children": [ + {"name":"/dev/md127", "maj:min":"9:127", "rm":false, "size":9001376219136, "ro":false, "type":"raid5", "mountpoint":"/media/3TBRaid"} + ] + }, + {"name":"/dev/sde", "maj:min":"8:64", "rm":false, "size":240057409536, "ro":false, "type":"disk", "mountpoint":"/media/hdd1"}, + {"name":"/dev/sdf", "maj:min":"8:80", "rm":false, "size":120034123776, "ro":false, "type":"disk", "mountpoint":"/media/hdd2"}, + {"name":"/dev/sdg", "maj:min":"8:96", "rm":false, "size":3000592982016, "ro":false, "type":"disk", "mountpoint":null, + "children": [ + {"name":"/dev/md127", "maj:min":"9:127", "rm":false, "size":9001376219136, "ro":false, "type":"raid5", "mountpoint":"/media/3TBRaid"} + ] + }, + {"name":"/dev/nvme0n1", "maj:min":"259:0", "rm":false, "size":1000204886016, "ro":false, "type":"disk", "mountpoint":"/media/hdd3"} + ] +} + "#.to_string())).to_string(), r#"[{"name":"/dev/sda","size":120034123776},{"name":"/dev/sdb","size":3000592982016},{"name":"/dev/sdc","size":3000592982016},{"name":"/dev/sdd","size":4000787030016},{"name":"/dev/sde","size":240057409536},{"name":"/dev/sdf","size":120034123776},{"name":"/dev/sdg","size":3000592982016},{"name":"/dev/nvme0n1","size":1000204886016}]"#); + } }