init
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
/target
 | 
			
		||||
							
								
								
									
										17
									
								
								.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
stages:
 | 
			
		||||
  - build
 | 
			
		||||
 | 
			
		||||
rust-latest:
 | 
			
		||||
  stage: build
 | 
			
		||||
  image: rust:latest
 | 
			
		||||
  script:
 | 
			
		||||
    - cargo build --verbose
 | 
			
		||||
    - cargo test --verbose
 | 
			
		||||
 | 
			
		||||
rust-nightly:
 | 
			
		||||
  stage: build
 | 
			
		||||
  image: rustlang/rust:nightly
 | 
			
		||||
  script:
 | 
			
		||||
    - cargo build --verbose
 | 
			
		||||
    - cargo test --verbose
 | 
			
		||||
  allow_failure: true
 | 
			
		||||
							
								
								
									
										1536
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1536
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "raid_manager"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
rocket = {version = "0.5.0-rc.2", features = ["json"]}
 | 
			
		||||
regex = "1"
 | 
			
		||||
							
								
								
									
										22
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
mod mdstat_parser;
 | 
			
		||||
 | 
			
		||||
#[macro_use]
 | 
			
		||||
extern crate rocket;
 | 
			
		||||
 | 
			
		||||
use rocket::serde::json::Json;
 | 
			
		||||
use crate::mdstat_parser::{MdRaidSystem, parse_mdstat};
 | 
			
		||||
 | 
			
		||||
#[get("/raiddevices")]
 | 
			
		||||
fn get_raid_devices() -> Json<MdRaidSystem> {
 | 
			
		||||
    Json(parse_mdstat())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[rocket::main]
 | 
			
		||||
async fn main() -> Result<(), rocket::Error> {
 | 
			
		||||
    let _rocket = rocket::build()
 | 
			
		||||
        .mount("/api", routes![get_raid_devices])
 | 
			
		||||
        .launch()
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										162
									
								
								src/mdstat_parser.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/mdstat_parser.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
			
		||||
use std::fs;
 | 
			
		||||
use regex::{Regex};
 | 
			
		||||
use rocket::serde::{Serialize};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
#[serde(crate = "rocket::serde")]
 | 
			
		||||
pub struct MdRaidSystem {
 | 
			
		||||
    raids: Vec<MdRaid>,
 | 
			
		||||
    supported_levels: Vec<String>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
#[serde(crate = "rocket::serde")]
 | 
			
		||||
pub struct MdRaid {
 | 
			
		||||
    name: String,
 | 
			
		||||
    faulty: bool,
 | 
			
		||||
    level: String,
 | 
			
		||||
    devices: Vec<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn parse_mdstat() -> MdRaidSystem {
 | 
			
		||||
    // let mdstat_path = "/proc/mdstat";
 | 
			
		||||
    let mdstat_path = "testmdstat.txt";
 | 
			
		||||
    return match fs::read_to_string(mdstat_path) {
 | 
			
		||||
        Ok(contents) => parse_mdstat_str(contents),
 | 
			
		||||
        Err(_) => MdRaidSystem{
 | 
			
		||||
            raids: vec![],
 | 
			
		||||
            supported_levels: vec![],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_mdstat_str(contents: String) -> MdRaidSystem {
 | 
			
		||||
    let mut md_raids = Vec::new();
 | 
			
		||||
 | 
			
		||||
    // todo avoid recompiling regex every time
 | 
			
		||||
    let re = Regex::new(r"([a-z_]{2,5}[0-9]{1,3})\s:\s(active|started|faulty)\s(raid[0-9])\s((?:\s?[a-z]{1,10}[0-9]{0,2}\[[0-9]])+)").unwrap();
 | 
			
		||||
    let re_personalities = Regex::new(r"Personalities\s:\s((?:\s?\[(?:raid[0-9]|linear|multipath|faulty)])+)").unwrap();
 | 
			
		||||
 | 
			
		||||
    for cap in re.captures_iter(&contents) {
 | 
			
		||||
        if cap.len() != 5 {
 | 
			
		||||
            // todo error handling
 | 
			
		||||
            continue
 | 
			
		||||
        } else {
 | 
			
		||||
            let devicesplit = cap[4].split(" ");
 | 
			
		||||
            let mut devices = Vec::new();
 | 
			
		||||
            for i in devicesplit {
 | 
			
		||||
                // remove brackets
 | 
			
		||||
                match i.split("[").next() {
 | 
			
		||||
                    None => println!("no [ in device string!?"),
 | 
			
		||||
                    Some(name) => devices.push(name.to_string())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let raid = MdRaid {
 | 
			
		||||
                name: cap[1].to_string(),
 | 
			
		||||
                faulty: &cap[2] == "faulty",
 | 
			
		||||
                level: cap[3].to_string(),
 | 
			
		||||
                devices,
 | 
			
		||||
            };
 | 
			
		||||
            md_raids.push(raid);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut supp_levels: Vec<String> = Vec::new();
 | 
			
		||||
    match re_personalities.captures(&contents) {
 | 
			
		||||
        None => {}
 | 
			
		||||
        Some(res) => {
 | 
			
		||||
            if res.len() == 2 {
 | 
			
		||||
                let splits = res[1].split(" ");
 | 
			
		||||
                for i in splits {
 | 
			
		||||
                    let mut chars = i.chars();
 | 
			
		||||
                    // remove brackets
 | 
			
		||||
                    chars.next();
 | 
			
		||||
                    chars.next_back();
 | 
			
		||||
                    supp_levels.push(chars.as_str().to_string());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MdRaidSystem{
 | 
			
		||||
        raids: md_raids,
 | 
			
		||||
        supported_levels: supp_levels
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[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_mdstat_str(r#"
 | 
			
		||||
Personalities :
 | 
			
		||||
unused devices: <none>
 | 
			
		||||
        "#.to_string())).to_string(), r#"{"raids":[],"supported_levels":[]}"#);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn single_special_name() {
 | 
			
		||||
        assert_eq!(json!(parse_mdstat_str(r#"
 | 
			
		||||
Personalities : [raid1] [raid6] [raid5] [raid4]
 | 
			
		||||
md_d0 : active raid5 sde1[0] sdf1[4] sdb1[5] sdd1[2] sdc1[1]
 | 
			
		||||
      1250241792 blocks super 1.2 level 5, 64k chunk, algorithm 2 [5/5] [UUUUU]
 | 
			
		||||
      bitmap: 0/10 pages [0KB], 16384KB chunk
 | 
			
		||||
 | 
			
		||||
unused devices: <none>
 | 
			
		||||
        "#.to_string())).to_string(), r#"{"raids":[{"devices":["sde1","sdf1","sdb1","sdd1","sdc1"],"faulty":false,"level":"raid5","name":"md_d0"}],"supported_levels":["raid1","raid6","raid5","raid4"]}"#);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn rebuilding_array() {
 | 
			
		||||
        assert_eq!(json!(parse_mdstat_str(r#"
 | 
			
		||||
Personalities : [raid1] [raid6] [raid5] [raid4]
 | 
			
		||||
md127 : active raid5 sdh1[6] sdg1[4] sdf1[3] sde1[2] sdd1[1] sdc1[0]
 | 
			
		||||
      1464725760 blocks level 5, 64k chunk, algorithm 2 [6/5] [UUUUU_]
 | 
			
		||||
      [==>..................]  recovery = 12.6% (37043392/292945152) finish=127.5min speed=33440K/sec
 | 
			
		||||
 | 
			
		||||
unused devices: <none>
 | 
			
		||||
        "#.to_string())).to_string(), r#"{"raids":[{"devices":["sdh1","sdg1","sdf1","sde1","sdd1","sdc1"],"faulty":false,"level":"raid5","name":"md127"}],"supported_levels":["raid1","raid6","raid5","raid4"]}"#);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test3() {
 | 
			
		||||
        assert_eq!(json!(parse_mdstat_str(r#"
 | 
			
		||||
Personalities : [linear] [raid0] [raid1] [raid10] [raid6] [raid5] [raid4]
 | 
			
		||||
md2 : active raid0 sda3[0] sdb3[1]
 | 
			
		||||
      4874137088 blocks super 1.2 64k chunks [2/2] [UU]
 | 
			
		||||
 | 
			
		||||
md1 : active raid1 sda2[0] sdb2[1]
 | 
			
		||||
      2097088 blocks [2/2] [UU]
 | 
			
		||||
 | 
			
		||||
md0 : active raid1 sda1[0] sdb1[1]
 | 
			
		||||
      2490176 blocks [2/2] [UU]
 | 
			
		||||
        "#.to_string())).to_string(), r#"{"raids":[{"devices":["sda3","sdb3"],"faulty":false,"level":"raid0","name":"md2"},{"devices":["sda2","sdb2"],"faulty":false,"level":"raid1","name":"md1"},{"devices":["sda1","sdb1"],"faulty":false,"level":"raid1","name":"md0"}],"supported_levels":["linear","raid0","raid1"]}"#);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn lots_of_devices() {
 | 
			
		||||
        assert_eq!(json!(parse_mdstat_str(r#"
 | 
			
		||||
Personalities : [raid1] [raid6] [raid5] [raid4]
 | 
			
		||||
md1 : active raid1 sdb2[1] sda2[0]
 | 
			
		||||
      136448 blocks [2/2] [UU]
 | 
			
		||||
 | 
			
		||||
md2 : active raid1 sdb3[1] sda3[0]
 | 
			
		||||
      129596288 blocks [2/2] [UU]
 | 
			
		||||
 | 
			
		||||
md3 : active raid5 sdl1[9] sdk1[8] sdj1[7] sdi1[6] sdh1[5] sdg1[4] sdf1[3] sde1[2] sdd1[1] sdc1[0]
 | 
			
		||||
      1318680576 blocks level 5, 1024k chunk, algorithm 2 [10/10] [UUUUUUUUUU]
 | 
			
		||||
 | 
			
		||||
md0 : active raid1 sdb1[1] sda1[0]
 | 
			
		||||
      16787776 blocks [2/2] [UU]
 | 
			
		||||
 | 
			
		||||
unused devices: <none>
 | 
			
		||||
        "#.to_string())).to_string(), r#"{"raids":[{"devices":["sdb2","sda2"],"faulty":false,"level":"raid1","name":"md1"},{"devices":["sdb3","sda3"],"faulty":false,"level":"raid1","name":"md2"},{"devices":["sdl1","sdk1","sdj1","sdi1","sdh1","sdg1","sdf1","sde1","sdd1","sdc1"],"faulty":false,"level":"raid5","name":"md3"},{"devices":["sdb1","sda1"],"faulty":false,"level":"raid1","name":"md0"}],"supported_levels":["raid1","raid6","raid5","raid4"]}"#);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user