use std::fs::{read_to_string, read_dir, DirEntry};
use regex::Regex;
use crate::ProcSysParserError;
#[derive(Debug, PartialEq, Default)]
pub struct SysBlock {
pub block_devices: Vec<BlockDevice>
}
#[derive(Default)]
pub struct Builder {
pub sys_path : String,
pub filter : String,
}
impl Builder {
pub fn new() -> Builder {
Builder {
sys_path: "/sys".to_string(),
filter: "^dm-".to_string(),
}
}
pub fn path(mut self, sys_path: &str) -> Builder {
self.sys_path = sys_path.to_string();
self
}
pub fn regex(mut self, filter: &str) -> Builder {
self.filter = filter.to_string();
self
}
pub fn read(self) -> Result<SysBlock, ProcSysParserError> {
SysBlock::read_sys_block_devices(format!("{}/block", self.sys_path).as_str(), self.filter.as_str())
}
}
pub fn read() -> Result<SysBlock, ProcSysParserError> {
Builder::new().read()
}
#[derive(Debug, PartialEq, Default)]
pub struct BlockDevice {
pub device_name: String,
pub alignment_offset: u64,
pub cache_type: Option<String>,
pub dev_block_major: u64,
pub dev_block_minor: u64,
pub discard_alignment: u64,
pub diskseq: Option<u64>,
pub hidden: u64,
pub inflight_reads: u64,
pub inflight_writes: u64,
pub queue_add_random: u64,
pub queue_chunk_sectors: Option<u64>,
pub queue_dax: u64,
pub queue_discard_granularity: u64,
pub queue_discard_max_bytes: u64,
pub queue_discard_max_hw_bytes: u64,
pub queue_hw_sector_size: u64,
pub queue_io_poll: u64,
pub queue_io_poll_delay: i64,
pub queue_logical_block_size: u64,
pub queue_max_discard_segments: u64,
pub queue_max_hw_sectors_kb: u64,
pub queue_max_sectors_kb: u64,
pub queue_max_integrity_segments: u64,
pub queue_max_segments: u64,
pub queue_max_segment_size: u64,
pub queue_minimum_io_size: u64,
pub queue_nomerges: u64,
pub queue_nr_requests: u64,
pub queue_nr_zones: Option<u64>,
pub queue_optimal_io_size: u64,
pub queue_physical_block_size: u64,
pub queue_read_ahead_kb: u64,
pub queue_rotational: u64,
pub queue_rq_affinity: u64,
pub queue_scheduler: String,
pub queue_write_cache: String,
pub queue_write_same_max_bytes: u64,
pub queue_zoned: Option<String>,
pub range: u64,
pub removable: u64,
pub ro: u64,
pub size: u64,
pub stat_reads_completed_success: u64,
pub stat_reads_merged: u64,
pub stat_reads_sectors: u64,
pub stat_reads_time_spent_ms: u64,
pub stat_writes_completed_success: u64,
pub stat_writes_merged: u64,
pub stat_writes_sectors: u64,
pub stat_writes_time_spent_ms: u64,
pub stat_ios_in_progress: u64,
pub stat_ios_time_spent_ms: u64,
pub stat_ios_weighted_time_spent_ms: u64,
pub stat_discards_completed_success: Option<u64>,
pub stat_discards_merged: Option<u64>,
pub stat_discards_sectors: Option<u64>,
pub stat_discards_time_spent_ms: Option<u64>,
pub stat_flush_requests_completed_success: Option<u64>,
pub stat_flush_requests_time_spent_ms: Option<u64>,
}
impl BlockDevice {
pub fn new() -> BlockDevice {
BlockDevice::default()
}
}
impl SysBlock {
pub fn new() -> SysBlock {
SysBlock::default()
}
fn parse_dev(
blockdevice_data: &mut BlockDevice,
blockdevice_dir: &DirEntry,
) -> Result<(), ProcSysParserError> {
let dev_contents = read_to_string(blockdevice_dir.path().join("dev"))
.map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join("dev").to_string_lossy().to_string(), error})?
.trim_end_matches('\n').to_string();
let mut fields = dev_contents.split(':');
blockdevice_data.dev_block_major = fields.next().ok_or(ProcSysParserError::IteratorItemError { item: "block parse_dev major".to_string() })?
.parse::<u64>().map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.dev_block_minor = fields.next().ok_or(ProcSysParserError::IteratorItemError { item: "block parse_dev minor".to_string() })?
.parse::<u64>().map_err(ProcSysParserError::ParseToIntegerError)?;
Ok(())
}
fn parse_inflight(
blockdevice_data: &mut BlockDevice,
blockdevice_dir: &DirEntry,
) -> Result<(), ProcSysParserError> {
let inflight_from_file = read_to_string(blockdevice_dir.path().join("inflight"))
.map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join("inflight").to_string_lossy().to_string(), error})?
.trim_end_matches('\n').to_string();
blockdevice_data.inflight_reads = inflight_from_file.split_whitespace()
.nth(0)
.ok_or(ProcSysParserError::IteratorItemError { item: "block parse_inflight reads".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.inflight_writes = inflight_from_file.split_whitespace()
.nth(1)
.ok_or(ProcSysParserError::IteratorItemError { item: "block parse_inflight writes".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
Ok(())
}
fn parse_queue_scheduler(
blockdevice_data: &mut BlockDevice,
blockdevice_dir: &DirEntry,
) -> Result<(), ProcSysParserError> {
let nr_requests = read_to_string(blockdevice_dir.path().join("queue").join("scheduler"))
.map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join("queue").join("scheduler").to_string_lossy().to_string(), error })?
.trim_end_matches('\n').to_string();
let left_bracket = nr_requests.find('[');
let right_bracket = nr_requests.find(']');
if left_bracket.is_some() && right_bracket.is_some() {
blockdevice_data.queue_scheduler = nr_requests[left_bracket.ok_or(ProcSysParserError::FindItemError { item: "block parse_queue_scheduler '['".to_string() })?+1..right_bracket.ok_or(ProcSysParserError::FindItemError { item: "block parse_queue_scheduler ']'".to_string() })?].to_string();
} else {
blockdevice_data.queue_scheduler = "?".to_string();
}
Ok(())
}
fn parse_stat(
blockdevice_data: &mut BlockDevice,
blockdevice_dir: &DirEntry,
) -> Result<(), ProcSysParserError> {
let parse_next_and_conversion_into_option_u64 = |result: Option<&str>| -> Option<u64> {
match result {
None => None,
Some(value) => {
match value.parse::<u64>() {
Err(_) => None,
Ok(number) => Some(number),
}
},
}
};
let stat_contents = read_to_string(blockdevice_dir.path().join("stat"))
.map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join("stat").to_string_lossy().to_string(), error })?
.trim_end_matches('\n')
.to_string();
let mut stat_contents_splitted = stat_contents
.split_whitespace();
blockdevice_data.stat_reads_completed_success = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat reads_completed_success".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_reads_merged = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat reads_merged".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_reads_sectors = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat reads_sectors".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_reads_time_spent_ms = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat reads_time_spent_ms".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_writes_completed_success = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat writes_completed_success".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_writes_merged = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat writes_completed_success".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_writes_sectors = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat writes_sectors".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_writes_time_spent_ms = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat writes_time_spent_ms".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_ios_in_progress = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat ios_in_progress".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_ios_time_spent_ms = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat ios_time_spent_ms".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_ios_weighted_time_spent_ms = stat_contents_splitted.next()
.ok_or(ProcSysParserError::FindItemError { item: "block parse_stat ios_weighted_time_spent_ms".to_string() })?
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?;
blockdevice_data.stat_discards_completed_success = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
blockdevice_data.stat_discards_merged = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
blockdevice_data.stat_discards_sectors = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
blockdevice_data.stat_discards_time_spent_ms = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
blockdevice_data.stat_flush_requests_completed_success = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
blockdevice_data.stat_flush_requests_time_spent_ms = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
Ok(())
}
fn parse_contents_file_u64(
file: &str,
blockdevice_dir: &DirEntry,
) -> Result<u64, ProcSysParserError> {
read_to_string(blockdevice_dir.path().join(file))
.map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join(file).to_string_lossy().to_string(), error })?
.trim_end_matches('\n')
.to_string()
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)
}
fn parse_contents_file_i64(
file: &str,
blockdevice_dir: &DirEntry,
) -> Result<i64, ProcSysParserError> {
read_to_string(blockdevice_dir.path().join(file))
.map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join(file).to_string_lossy().to_string(), error })?
.trim_end_matches('\n')
.to_string()
.parse::<i64>()
.map_err(ProcSysParserError::ParseToIntegerError)
}
fn parse_contents_file_option_u64(
file: &str,
blockdevice_dir: &DirEntry,
) -> Result<Option<u64>, ProcSysParserError>
{
match read_to_string(blockdevice_dir.path().join(file)) {
Ok(result) => {
Ok(
Some(result
.trim_end_matches('\n')
.to_string()
.parse::<u64>()
.map_err(ProcSysParserError::ParseToIntegerError)?)
)
},
Err(_) => Ok(None),
}
}
fn parse_contents_file_option_string(
file: &str,
blockdevice_dir: &DirEntry,
) -> Result<Option<String>, ProcSysParserError> {
Ok(match read_to_string(blockdevice_dir.path().join(file)) {
Ok(result) => Some(result.trim_end_matches('\n').to_string()),
Err(_) => None
})
}
fn parse_contents_file_string(
file: &str,
blockdevice_dir: &DirEntry,
) -> Result <String, ProcSysParserError> {
Ok(read_to_string(blockdevice_dir.path().join(file))
.map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join(file).to_string_lossy().to_string(), error })?
.trim_end_matches('\n')
.to_string())
}
pub fn read_sys_block_devices(
sys_block_path: &str,
filter: &str,
) -> Result<SysBlock, ProcSysParserError> {
let mut sysblock = SysBlock::new();
let blockdevice_directories = read_dir(sys_block_path)
.map_err(|error| ProcSysParserError::DirectoryReadError { directory: sys_block_path.to_string(), error })?;
let filter_regex = Regex::new(filter)
.map_err(|_| ProcSysParserError::RegexCompileError { regex: filter.to_string() })?;
for blockdevice in blockdevice_directories {
let directory_entry = blockdevice.unwrap_or_else(|error| panic!("Error {} reading block device sysfs entry", error));
if !filter_regex.as_str().is_empty() && filter_regex.is_match(&directory_entry.file_name().into_string().unwrap()) { continue };
let mut blockdevice_data = BlockDevice::new();
blockdevice_data.device_name = directory_entry.file_name().into_string().unwrap();
blockdevice_data.alignment_offset = SysBlock::parse_contents_file_u64("alignment_offset", &directory_entry)?;
blockdevice_data.cache_type = SysBlock::parse_contents_file_option_string("cache_type", &directory_entry)?;
SysBlock::parse_dev(&mut blockdevice_data, &directory_entry)?;
blockdevice_data.discard_alignment = SysBlock::parse_contents_file_u64("discard_alignment", &directory_entry)?;
blockdevice_data.diskseq = SysBlock::parse_contents_file_option_u64("diskseq", &directory_entry)?;
blockdevice_data.hidden = SysBlock::parse_contents_file_u64("hidden", &directory_entry)?;
SysBlock::parse_inflight(&mut blockdevice_data, &directory_entry)?;
blockdevice_data.queue_add_random = SysBlock::parse_contents_file_u64("queue/add_random", &directory_entry)?;
blockdevice_data.queue_chunk_sectors = SysBlock::parse_contents_file_option_u64("queue/chunk_sectors", &directory_entry)?;
blockdevice_data.queue_dax = SysBlock::parse_contents_file_u64("queue/dax", &directory_entry)?;
blockdevice_data.queue_discard_granularity = SysBlock::parse_contents_file_u64("queue/discard_granularity", &directory_entry)?;
blockdevice_data.queue_discard_max_bytes = SysBlock::parse_contents_file_u64("queue/discard_max_bytes", &directory_entry)?;
blockdevice_data.queue_discard_max_hw_bytes = SysBlock::parse_contents_file_u64("queue/discard_max_hw_bytes", &directory_entry)?;
blockdevice_data.queue_hw_sector_size = SysBlock::parse_contents_file_u64("queue/hw_sector_size", &directory_entry)?;
blockdevice_data.queue_io_poll = SysBlock::parse_contents_file_u64("queue/io_poll", &directory_entry)?;
blockdevice_data.queue_io_poll_delay = SysBlock::parse_contents_file_i64("queue/io_poll_delay", &directory_entry)?;
blockdevice_data.queue_logical_block_size = SysBlock::parse_contents_file_u64("queue/logical_block_size", &directory_entry)?;
blockdevice_data.queue_max_discard_segments = SysBlock::parse_contents_file_u64("queue/max_discard_segments", &directory_entry)?;
blockdevice_data.queue_max_hw_sectors_kb = SysBlock::parse_contents_file_u64("queue/max_hw_sectors_kb", &directory_entry)?;
blockdevice_data.queue_max_integrity_segments = SysBlock::parse_contents_file_u64("queue/max_integrity_segments", &directory_entry)?;
blockdevice_data.queue_max_sectors_kb = SysBlock::parse_contents_file_u64("queue/max_sectors_kb", &directory_entry)?;
blockdevice_data.queue_max_segment_size = SysBlock::parse_contents_file_u64("queue/max_segment_size", &directory_entry)?;
blockdevice_data.queue_max_segments = SysBlock::parse_contents_file_u64("queue/max_segments", &directory_entry)?;
blockdevice_data.queue_minimum_io_size = SysBlock::parse_contents_file_u64("queue/minimum_io_size", &directory_entry)?;
blockdevice_data.queue_nomerges = SysBlock::parse_contents_file_u64("queue/nomerges", &directory_entry)?;
blockdevice_data.queue_nr_requests = SysBlock::parse_contents_file_u64("queue/nr_requests", &directory_entry)?;
blockdevice_data.queue_nr_zones = SysBlock::parse_contents_file_option_u64("queue/nr_zones", &directory_entry)?;
blockdevice_data.queue_optimal_io_size = SysBlock::parse_contents_file_u64("queue/optimal_io_size", &directory_entry)?;
blockdevice_data.queue_physical_block_size = SysBlock::parse_contents_file_u64("queue/physical_block_size", &directory_entry)?;
blockdevice_data.queue_read_ahead_kb = SysBlock::parse_contents_file_u64("queue/read_ahead_kb", &directory_entry)?;
blockdevice_data.queue_rotational = SysBlock::parse_contents_file_u64("queue/rotational", &directory_entry)?;
blockdevice_data.queue_rq_affinity = SysBlock::parse_contents_file_u64("queue/rq_affinity", &directory_entry)?;
SysBlock::parse_queue_scheduler(&mut blockdevice_data, &directory_entry)?;
blockdevice_data.queue_write_cache = SysBlock::parse_contents_file_string("queue/write_cache", &directory_entry)?;
blockdevice_data.queue_write_same_max_bytes = SysBlock::parse_contents_file_u64("queue/write_same_max_bytes", &directory_entry)?;
blockdevice_data.queue_zoned = SysBlock::parse_contents_file_option_string("queue/zoned", &directory_entry)?;
blockdevice_data.range = SysBlock::parse_contents_file_u64("range", &directory_entry)?;
blockdevice_data.removable = SysBlock::parse_contents_file_u64("removable", &directory_entry)?;
blockdevice_data.ro = SysBlock::parse_contents_file_u64("ro", &directory_entry)?;
blockdevice_data.size = SysBlock::parse_contents_file_u64("size", &directory_entry)?;
SysBlock::parse_stat(&mut blockdevice_data, &directory_entry)?;
sysblock.block_devices.push(blockdevice_data);
}
Ok(sysblock)
}
}
#[cfg(test)]
mod tests {
use std::fs::{write, remove_dir_all, create_dir_all};
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
use super::*;
#[test]
fn create_sys_block_device_parse_files() {
let alignment_offset = format!("0\n");
let cache_type = format!("write back\n");
let dev= format!("253:0\n");
let discard_alignment = format!("0\n");
let diskseq = format!("9\n");
let hidden = format!("0\n");
let inflight = format!(" 1 2\n");
let queue_add_random= format!("0\n");
let queue_chunk_sectors = format!("0\n");
let queue_dax = format!("0\n");
let queue_discard_granularity = format!("512\n");
let queue_discard_max_bytes = format!("2147483136\n");
let queue_discard_max_hw_bytes = format!("2147483136\n");
let queue_hw_sector_size = format!("512\n");
let queue_io_poll = format!("0\n");
let queue_io_poll_delay = format!("-1\n");
let queue_logical_block_size = format!("512\n");
let queue_max_discard_segments = format!("1\n");
let queue_max_hw_sectors_kb = format!("2147483647\n");
let queue_max_integrity_segments = format!("0\n");
let queue_max_sectors_kb = format!("1280\n");
let queue_max_segment_size = format!("4294967295\n");
let queue_max_segments = format!("254\n");
let queue_minimum_io_size = format!("512\n");
let queue_nomerges = format!("0\n");
let queue_nr_requests = format!("256\n");
let queue_nr_zones = format!("0\n");
let queue_optimal_io_size = format!("0\n");
let queue_physical_block_size = format!("512\n");
let queue_read_ahead_kb = format!("128\n");
let queue_rotational = format!("1\n");
let queue_rq_affinity = format!("1\n");
let queue_scheduler = format!("[none] mq-deadline\n");
let queue_write_cache = format!("write back\n");
let queue_write_same_max_bytes = format!("0\n");
let queue_zoned = format!("none\n");
let range = format!("16\n");
let removable = format!("0\n");
let ro = format!("0\n");
let size = format!("125829120\n");
let stat = format!(" 9718 3826 1052371 3026 2856 2331 312397 1947 0 6004 5554 7141 0 88014755 276 591 304\n");
let directory_suffix: String = thread_rng().sample_iter(&Alphanumeric).take(8).map(char::from).collect();
let test_path = format!("/tmp/test.{}", directory_suffix);
create_dir_all(format!("{}/block/sda/queue", test_path)).expect("Error creating mock sysfs directories.");
write(format!("{}/block/sda/alignment_offset", test_path),alignment_offset).expect("error writing to mock sysfs alignment_offset file.");
write(format!("{}/block/sda/cache_type", test_path),cache_type).expect("error writing to mock sysfs cache_type file.");
write(format!("{}/block/sda/dev", test_path),dev).expect("error writing to mock sysfs dev file.");
write(format!("{}/block/sda/discard_alignment", test_path),discard_alignment).expect("error writing to mock sysfs discard_alginment file.");
write(format!("{}/block/sda/diskseq", test_path),diskseq).expect("error writing to mock sysfs diskseq file.");
write(format!("{}/block/sda/hidden", test_path),hidden).expect("error writing to mock sysfs hidden file.");
write(format!("{}/block/sda/inflight", test_path),inflight).expect("error writing to mock sysfs inflight file.");
write(format!("{}/block/sda/queue/add_random", test_path),queue_add_random).expect("error writing to mock sysfs queue/add_random file.");
write(format!("{}/block/sda/queue/chunk_sectors", test_path),queue_chunk_sectors).expect("error writing to mock sysfs queue/chunk_sectors file.");
write(format!("{}/block/sda/queue/dax", test_path),queue_dax).expect("error writing to mock sysfs queue/dax file.");
write(format!("{}/block/sda/queue/discard_granularity", test_path),queue_discard_granularity).expect("error writing to mock sysfs queue/discard_granularity file.");
write(format!("{}/block/sda/queue/discard_max_bytes", test_path),queue_discard_max_bytes).expect("error writing to mock sysfs queue/discard_max_bytes file.");
write(format!("{}/block/sda/queue/discard_max_hw_bytes", test_path),queue_discard_max_hw_bytes).expect("error writing to mock sysfs queue/discard_max_hw_bytes file.");
write(format!("{}/block/sda/queue/hw_sector_size", test_path),queue_hw_sector_size).expect("error writing to mock sysfs queue/hw_sector_size file.");
write(format!("{}/block/sda/queue/io_poll", test_path),queue_io_poll).expect("error writing to mock sysfs queue/io_poll file.");
write(format!("{}/block/sda/queue/io_poll_delay", test_path),queue_io_poll_delay).expect("error writing to mock sysfs queue/io_poll_delay file.");
write(format!("{}/block/sda/queue/logical_block_size", test_path),queue_logical_block_size).expect("error writing to mock sysfs queue/logical_block_size file.");
write(format!("{}/block/sda/queue/max_discard_segments", test_path),queue_max_discard_segments).expect("error writing to mock sysfs queue/max_discard_segments file.");
write(format!("{}/block/sda/queue/max_hw_sectors_kb", test_path),queue_max_hw_sectors_kb).expect("error writing to mock sysfs queue/max_hw_sectors_kb file.");
write(format!("{}/block/sda/queue/max_integrity_segments", test_path),queue_max_integrity_segments).expect("error writing to mock sysfs queue/max_integrity_segments file.");
write(format!("{}/block/sda/queue/max_sectors_kb", test_path),queue_max_sectors_kb).expect("error writing to mock sysfs queue/max_sectors_kb file.");
write(format!("{}/block/sda/queue/max_segment_size", test_path),queue_max_segment_size).expect("error writing to mock sysfs queue/max_segment_size file.");
write(format!("{}/block/sda/queue/max_segments", test_path),queue_max_segments).expect("error writing to mock sysfs queue/max_segments file.");
write(format!("{}/block/sda/queue/minimum_io_size", test_path),queue_minimum_io_size).expect("error writing to mock sysfs queue/minimum_io_size file.");
write(format!("{}/block/sda/queue/nomerges", test_path),queue_nomerges).expect("error writing to mock sysfs queue/nomerges file.");
write(format!("{}/block/sda/queue/nr_requests", test_path),queue_nr_requests).expect("error writing to mock sysfs queue/nr_requests file.");
write(format!("{}/block/sda/queue/nr_zones", test_path),queue_nr_zones).expect("error writing to mock sysfs queue/nr_zones file.");
write(format!("{}/block/sda/queue/optimal_io_size", test_path),queue_optimal_io_size).expect("error writing to mock sysfs queue/optimal_io_size file.");
write(format!("{}/block/sda/queue/physical_block_size", test_path),queue_physical_block_size).expect("error writing to mock sysfs queue/physical_block_size file.");
write(format!("{}/block/sda/queue/read_ahead_kb", test_path),queue_read_ahead_kb).expect("error writing to mock sysfs queue/read_ahead_kb file.");
write(format!("{}/block/sda/queue/rotational", test_path),queue_rotational).expect("error writing to mock sysfs queue/rotational file.");
write(format!("{}/block/sda/queue/rq_affinity", test_path),queue_rq_affinity).expect("error writing to mock sysfs queue/rq_affinity file.");
write(format!("{}/block/sda/queue/scheduler", test_path),queue_scheduler).expect("error writing to mock sysfs queue/scheduler file.");
write(format!("{}/block/sda/queue/write_cache", test_path),queue_write_cache).expect("error writing to mock sysfs queue/write_cache file.");
write(format!("{}/block/sda/queue/write_same_max_bytes", test_path),queue_write_same_max_bytes).expect("error writing to mock sysfs queue/write_same_max_bytes file.");
write(format!("{}/block/sda/queue/zoned", test_path),queue_zoned).expect("error writing to mock sysfs queue/zoned file.");
write(format!("{}/block/sda/range", test_path),range).expect("error writing to mock sysfs range file.");
write(format!("{}/block/sda/removable", test_path),removable).expect("error writing to mock sysfs removable file.");
write(format!("{}/block/sda/ro", test_path),ro).expect("error writing to mock sysfs ro file.");
write(format!("{}/block/sda/size", test_path),size).expect("error writing to mock sysfs size file.");
write(format!("{}/block/sda/stat", test_path),stat).expect("error writing to mock sysfs stat file.");
let result = Builder::new().path(&test_path).read().unwrap();
remove_dir_all(test_path).unwrap();
assert_eq!(result, SysBlock {
block_devices: vec![
BlockDevice {
dev_block_major: 253,
dev_block_minor: 0,
device_name: "sda".to_string(),
discard_alignment: 0,
stat_reads_completed_success: 9718,
stat_reads_merged: 3826,
stat_reads_sectors: 1052371,
stat_reads_time_spent_ms: 3026,
stat_writes_completed_success: 2856,
stat_writes_merged: 2331,
stat_writes_sectors: 312397,
stat_writes_time_spent_ms: 1947,
stat_ios_in_progress: 0,
stat_ios_time_spent_ms: 6004,
stat_ios_weighted_time_spent_ms: 5554,
stat_discards_completed_success: Some(
7141,
),
stat_discards_merged: Some(
0,
),
stat_discards_sectors: Some(
88014755,
),
stat_discards_time_spent_ms: Some(
276,
),
stat_flush_requests_completed_success: Some(
591,
),
stat_flush_requests_time_spent_ms: Some(
304,
),
alignment_offset: 0,
cache_type: Some("write back".to_string()),
diskseq: Some(9),
hidden: 0,
inflight_reads: 1,
inflight_writes: 2,
range: 16,
removable: 0,
ro: 0,
size: 125829120,
queue_max_hw_sectors_kb: 2147483647,
queue_max_sectors_kb: 1280,
queue_max_discard_segments: 1,
queue_nr_requests: 256,
queue_nr_zones: Some(
0,
),
queue_scheduler: "none".to_string(),
queue_rotational: 1,
queue_dax: 0,
queue_add_random: 0,
queue_discard_granularity: 512,
queue_discard_max_hw_bytes: 2147483136,
queue_discard_max_bytes: 2147483136,
queue_hw_sector_size: 512,
queue_io_poll: 0,
queue_io_poll_delay: -1,
queue_logical_block_size: 512,
queue_minimum_io_size: 512,
queue_max_integrity_segments: 0,
queue_max_segments: 254,
queue_max_segment_size: 4294967295,
queue_nomerges: 0,
queue_physical_block_size: 512,
queue_optimal_io_size: 0,
queue_read_ahead_kb: 128,
queue_rq_affinity: 1,
queue_write_cache: "write back".to_string(),
queue_write_same_max_bytes: 0,
queue_chunk_sectors: Some(
0,
),
queue_zoned: Some(
"none".to_string(),
),
},
],
}
);
}
#[test]
fn create_sys_block_device_parse_files_non_existent() {
let alignment_offset = "0";
let cache_type = "write back";
let dev = "253:0";
let discard_alignment = "0";
let diskseq = "9";
let hidden = "0";
let inflight = " 1 2";
let queue_add_random = "0";
let queue_dax = "0";
let queue_discard_granularity = "512";
let queue_discard_max_bytes = "2147483136";
let queue_discard_max_hw_bytes = "2147483136";
let queue_hw_sector_size = "512";
let queue_io_poll = "0";
let queue_io_poll_delay = "-1";
let queue_logical_block_size = "512";
let queue_max_discard_segments = "1";
let queue_max_hw_sectors_kb = "2147483647";
let queue_max_integrity_segments = "0";
let queue_max_sectors_kb = "1280";
let queue_max_segment_size = "4294967295";
let queue_max_segments = "254";
let queue_minimum_io_size = "512";
let queue_nomerges = "0";
let queue_nr_requests = "256";
let queue_optimal_io_size = "0";
let queue_physical_block_size = "512";
let queue_read_ahead_kb = "128";
let queue_rotational = "1";
let queue_rq_affinity = "1";
let queue_scheduler = "[none] mq-deadline";
let queue_write_cache = "write back";
let queue_write_same_max_bytes = "0";
let range = "16";
let removable = "0";
let ro = "0";
let size = "125829120";
let stat = " 9718 3826 1052371 3026 2856 2331 312397 1947 0 6004 5554";
let directory_suffix: String = thread_rng().sample_iter(&Alphanumeric).take(8).map(char::from).collect();
let test_path = format!("/tmp/test.{}", directory_suffix);
create_dir_all(format!("{}/block/sda/queue", test_path)).expect("Error creating mock sysfs directories.");
write(format!("{}/block/sda/alignment_offset", test_path), alignment_offset).expect("error writing to mock sysfs alignment_offset file.");
write(format!("{}/block/sda/cache_type", test_path), cache_type).expect("error writing to mock sysfs cache_type file.");
write(format!("{}/block/sda/dev", test_path), dev).expect("error writing to mock sysfs dev file.");
write(format!("{}/block/sda/discard_alignment", test_path), discard_alignment).expect("error writing to mock sysfs discard_alginment file.");
write(format!("{}/block/sda/diskseq", test_path), diskseq).expect("error writing to mock sysfs diskseq file.");
write(format!("{}/block/sda/hidden", test_path), hidden).expect("error writing to mock sysfs hidden file.");
write(format!("{}/block/sda/inflight", test_path), inflight).expect("error writing to mock sysfs inflight file.");
write(format!("{}/block/sda/queue/add_random", test_path), queue_add_random).expect("error writing to mock sysfs queue/add_random file.");
write(format!("{}/block/sda/queue/dax", test_path), queue_dax).expect("error writing to mock sysfs queue/dax file.");
write(format!("{}/block/sda/queue/discard_granularity", test_path), queue_discard_granularity).expect("error writing to mock sysfs queue/discard_granularity file.");
write(format!("{}/block/sda/queue/discard_max_bytes", test_path), queue_discard_max_bytes).expect("error writing to mock sysfs queue/discard_max_bytes file.");
write(format!("{}/block/sda/queue/discard_max_hw_bytes", test_path), queue_discard_max_hw_bytes).expect("error writing to mock sysfs queue/discard_max_hw_bytes file.");
write(format!("{}/block/sda/queue/hw_sector_size", test_path), queue_hw_sector_size).expect("error writing to mock sysfs queue/hw_sector_size file.");
write(format!("{}/block/sda/queue/io_poll", test_path), queue_io_poll).expect("error writing to mock sysfs queue/io_poll file.");
write(format!("{}/block/sda/queue/io_poll_delay", test_path), queue_io_poll_delay).expect("error writing to mock sysfs queue/io_poll_delay file.");
write(format!("{}/block/sda/queue/logical_block_size", test_path), queue_logical_block_size).expect("error writing to mock sysfs queue/logical_block_size file.");
write(format!("{}/block/sda/queue/max_discard_segments", test_path), queue_max_discard_segments).expect("error writing to mock sysfs queue/max_discard_segments file.");
write(format!("{}/block/sda/queue/max_hw_sectors_kb", test_path), queue_max_hw_sectors_kb).expect("error writing to mock sysfs queue/max_hw_sectors_kb file.");
write(format!("{}/block/sda/queue/max_integrity_segments", test_path), queue_max_integrity_segments).expect("error writing to mock sysfs queue/max_integrity_segments file.");
write(format!("{}/block/sda/queue/max_sectors_kb", test_path), queue_max_sectors_kb).expect("error writing to mock sysfs queue/max_sectors_kb file.");
write(format!("{}/block/sda/queue/max_segment_size", test_path), queue_max_segment_size).expect("error writing to mock sysfs queue/max_segment_size file.");
write(format!("{}/block/sda/queue/max_segments", test_path), queue_max_segments).expect("error writing to mock sysfs queue/max_segments file.");
write(format!("{}/block/sda/queue/minimum_io_size", test_path), queue_minimum_io_size).expect("error writing to mock sysfs queue/minimum_io_size file.");
write(format!("{}/block/sda/queue/nomerges", test_path), queue_nomerges).expect("error writing to mock sysfs queue/nomerges file.");
write(format!("{}/block/sda/queue/nr_requests", test_path), queue_nr_requests).expect("error writing to mock sysfs queue/nr_requests file.");
write(format!("{}/block/sda/queue/optimal_io_size", test_path), queue_optimal_io_size).expect("error writing to mock sysfs queue/optimal_io_size file.");
write(format!("{}/block/sda/queue/physical_block_size", test_path), queue_physical_block_size).expect("error writing to mock sysfs queue/physical_block_size file.");
write(format!("{}/block/sda/queue/read_ahead_kb", test_path), queue_read_ahead_kb).expect("error writing to mock sysfs queue/read_ahead_kb file.");
write(format!("{}/block/sda/queue/rotational", test_path), queue_rotational).expect("error writing to mock sysfs queue/rotational file.");
write(format!("{}/block/sda/queue/rq_affinity", test_path), queue_rq_affinity).expect("error writing to mock sysfs queue/rq_affinity file.");
write(format!("{}/block/sda/queue/scheduler", test_path), queue_scheduler).expect("error writing to mock sysfs queue/scheduler file.");
write(format!("{}/block/sda/queue/write_cache", test_path), queue_write_cache).expect("error writing to mock sysfs queue/write_cache file.");
write(format!("{}/block/sda/queue/write_same_max_bytes", test_path), queue_write_same_max_bytes).expect("error writing to mock sysfs queue/write_same_max_bytes file.");
write(format!("{}/block/sda/range", test_path), range).expect("error writing to mock sysfs range file.");
write(format!("{}/block/sda/removable", test_path), removable).expect("error writing to mock sysfs removable file.");
write(format!("{}/block/sda/ro", test_path), ro).expect("error writing to mock sysfs ro file.");
write(format!("{}/block/sda/size", test_path), size).expect("error writing to mock sysfs size file.");
write(format!("{}/block/sda/stat", test_path), stat).expect("error writing to mock sysfs stat file.");
let result = Builder::new().path(&test_path).read().unwrap();
remove_dir_all(test_path).unwrap();
assert_eq!(result,
SysBlock {
block_devices: vec![
BlockDevice {
dev_block_major: 253,
dev_block_minor: 0,
device_name: "sda".to_string(),
discard_alignment: 0,
stat_reads_completed_success: 9718,
stat_reads_merged: 3826,
stat_reads_sectors: 1052371,
stat_reads_time_spent_ms: 3026,
stat_writes_completed_success: 2856,
stat_writes_merged: 2331,
stat_writes_sectors: 312397,
stat_writes_time_spent_ms: 1947,
stat_ios_in_progress: 0,
stat_ios_time_spent_ms: 6004,
stat_ios_weighted_time_spent_ms: 5554,
stat_discards_completed_success: None,
stat_discards_merged: None,
stat_discards_sectors: None,
stat_discards_time_spent_ms: None,
stat_flush_requests_completed_success: None,
stat_flush_requests_time_spent_ms: None,
alignment_offset: 0,
cache_type: Some("write back".to_string()),
diskseq: Some(9),
hidden: 0,
inflight_reads: 1,
inflight_writes: 2,
range: 16,
removable: 0,
ro: 0,
size: 125829120,
queue_max_hw_sectors_kb: 2147483647,
queue_max_sectors_kb: 1280,
queue_max_discard_segments: 1,
queue_nr_requests: 256,
queue_nr_zones: None,
queue_scheduler: "none".to_string(),
queue_rotational: 1,
queue_dax: 0,
queue_add_random: 0,
queue_discard_granularity: 512,
queue_discard_max_hw_bytes: 2147483136,
queue_discard_max_bytes: 2147483136,
queue_hw_sector_size: 512,
queue_io_poll: 0,
queue_io_poll_delay: -1,
queue_logical_block_size: 512,
queue_minimum_io_size: 512,
queue_max_integrity_segments: 0,
queue_max_segments: 254,
queue_max_segment_size: 4294967295,
queue_nomerges: 0,
queue_physical_block_size: 512,
queue_optimal_io_size: 0,
queue_read_ahead_kb: 128,
queue_rq_affinity: 1,
queue_write_cache: "write back".to_string(),
queue_write_same_max_bytes: 0,
queue_chunk_sectors: None,
queue_zoned: None,
},
],
}
);
}
#[test]
fn create_sys_block_device_test_filter() {
let alignment_offset = "0";
let cache_type = "write back";
let dev = "253:0";
let discard_alignment = "0";
let diskseq = "9";
let hidden = "0";
let inflight = " 1 2";
let queue_add_random = "0";
let queue_dax = "0";
let queue_discard_granularity = "512";
let queue_discard_max_bytes = "2147483136";
let queue_discard_max_hw_bytes = "2147483136";
let queue_hw_sector_size = "512";
let queue_io_poll = "0";
let queue_io_poll_delay = "-1";
let queue_logical_block_size = "512";
let queue_max_discard_segments = "1";
let queue_max_hw_sectors_kb = "2147483647";
let queue_max_integrity_segments = "0";
let queue_max_sectors_kb = "1280";
let queue_max_segment_size = "4294967295";
let queue_max_segments = "254";
let queue_minimum_io_size = "512";
let queue_nomerges = "0";
let queue_nr_requests = "256";
let queue_optimal_io_size = "0";
let queue_physical_block_size = "512";
let queue_read_ahead_kb = "128";
let queue_rotational = "1";
let queue_rq_affinity = "1";
let queue_scheduler = "[none] mq-deadline";
let queue_write_cache = "write back";
let queue_write_same_max_bytes = "0";
let range = "16";
let removable = "0";
let ro = "0";
let size = "125829120";
let stat = " 9718 3826 1052371 3026 2856 2331 312397 1947 0 6004 5554";
let directory_suffix: String = thread_rng().sample_iter(&Alphanumeric).take(8).map(char::from).collect();
let test_path = format!("/tmp/test.{}", directory_suffix);
create_dir_all(format!("{}/block/dm-0/queue", test_path)).expect("Error creating mock sysfs directories.");
write(format!("{}/block/dm-0/alignment_offset", test_path), alignment_offset).expect("error writing to mock sysfs alignment_offset file.");
write(format!("{}/block/dm-0/cache_type", test_path), cache_type).expect("error writing to mock sysfs cache_type file.");
write(format!("{}/block/dm-0/dev", test_path), dev).expect("error writing to mock sysfs dev file.");
write(format!("{}/block/dm-0/discard_alignment", test_path), discard_alignment).expect("error writing to mock sysfs discard_alginment file.");
write(format!("{}/block/dm-0/diskseq", test_path), diskseq).expect("error writing to mock sysfs diskseq file.");
write(format!("{}/block/dm-0/hidden", test_path), hidden).expect("error writing to mock sysfs hidden file.");
write(format!("{}/block/dm-0/inflight", test_path), inflight).expect("error writing to mock sysfs inflight file.");
write(format!("{}/block/dm-0/queue/add_random", test_path), queue_add_random).expect("error writing to mock sysfs queue/add_random file.");
write(format!("{}/block/dm-0/queue/dax", test_path), queue_dax).expect("error writing to mock sysfs queue/dax file.");
write(format!("{}/block/dm-0/queue/discard_granularity", test_path), queue_discard_granularity).expect("error writing to mock sysfs queue/discard_granularity file.");
write(format!("{}/block/dm-0/queue/discard_max_bytes", test_path), queue_discard_max_bytes).expect("error writing to mock sysfs queue/discard_max_bytes file.");
write(format!("{}/block/dm-0/queue/discard_max_hw_bytes", test_path), queue_discard_max_hw_bytes).expect("error writing to mock sysfs queue/discard_max_hw_bytes file.");
write(format!("{}/block/dm-0/queue/hw_sector_size", test_path), queue_hw_sector_size).expect("error writing to mock sysfs queue/hw_sector_size file.");
write(format!("{}/block/dm-0/queue/io_poll", test_path), queue_io_poll).expect("error writing to mock sysfs queue/io_poll file.");
write(format!("{}/block/dm-0/queue/io_poll_delay", test_path), queue_io_poll_delay).expect("error writing to mock sysfs queue/io_poll_delay file.");
write(format!("{}/block/dm-0/queue/logical_block_size", test_path), queue_logical_block_size).expect("error writing to mock sysfs queue/logical_block_size file.");
write(format!("{}/block/dm-0/queue/max_discard_segments", test_path), queue_max_discard_segments).expect("error writing to mock sysfs queue/max_discard_segments file.");
write(format!("{}/block/dm-0/queue/max_hw_sectors_kb", test_path), queue_max_hw_sectors_kb).expect("error writing to mock sysfs queue/max_hw_sectors_kb file.");
write(format!("{}/block/dm-0/queue/max_integrity_segments", test_path), queue_max_integrity_segments).expect("error writing to mock sysfs queue/max_integrity_segments file.");
write(format!("{}/block/dm-0/queue/max_sectors_kb", test_path), queue_max_sectors_kb).expect("error writing to mock sysfs queue/max_sectors_kb file.");
write(format!("{}/block/dm-0/queue/max_segment_size", test_path), queue_max_segment_size).expect("error writing to mock sysfs queue/max_segment_size file.");
write(format!("{}/block/dm-0/queue/max_segments", test_path), queue_max_segments).expect("error writing to mock sysfs queue/max_segments file.");
write(format!("{}/block/dm-0/queue/minimum_io_size", test_path), queue_minimum_io_size).expect("error writing to mock sysfs queue/minimum_io_size file.");
write(format!("{}/block/dm-0/queue/nomerges", test_path), queue_nomerges).expect("error writing to mock sysfs queue/nomerges file.");
write(format!("{}/block/dm-0/queue/nr_requests", test_path), queue_nr_requests).expect("error writing to mock sysfs queue/nr_requests file.");
write(format!("{}/block/dm-0/queue/optimal_io_size", test_path), queue_optimal_io_size).expect("error writing to mock sysfs queue/optimal_io_size file.");
write(format!("{}/block/dm-0/queue/physical_block_size", test_path), queue_physical_block_size).expect("error writing to mock sysfs queue/physical_block_size file.");
write(format!("{}/block/dm-0/queue/read_ahead_kb", test_path), queue_read_ahead_kb).expect("error writing to mock sysfs queue/read_ahead_kb file.");
write(format!("{}/block/dm-0/queue/rotational", test_path), queue_rotational).expect("error writing to mock sysfs queue/rotational file.");
write(format!("{}/block/dm-0/queue/rq_affinity", test_path), queue_rq_affinity).expect("error writing to mock sysfs queue/rq_affinity file.");
write(format!("{}/block/dm-0/queue/scheduler", test_path), queue_scheduler).expect("error writing to mock sysfs queue/scheduler file.");
write(format!("{}/block/dm-0/queue/write_cache", test_path), queue_write_cache).expect("error writing to mock sysfs queue/write_cache file.");
write(format!("{}/block/dm-0/queue/write_same_max_bytes", test_path), queue_write_same_max_bytes).expect("error writing to mock sysfs queue/write_same_max_bytes file.");
write(format!("{}/block/dm-0/range", test_path), range).expect("error writing to mock sysfs range file.");
write(format!("{}/block/dm-0/removable", test_path), removable).expect("error writing to mock sysfs removable file.");
write(format!("{}/block/dm-0/ro", test_path), ro).expect("error writing to mock sysfs ro file.");
write(format!("{}/block/dm-0/size", test_path), size).expect("error writing to mock sysfs size file.");
write(format!("{}/block/dm-0/stat", test_path), stat).expect("error writing to mock sysfs stat file.");
let result = Builder::new().path(&test_path).read().unwrap();
remove_dir_all(test_path).unwrap();
assert_eq!(result, SysBlock { block_devices: vec![] });
}
}