proc_sys_parser/
block.rs

1/*!
2Read `/sys/block` for block devices into the struct [`SysBlock`].
3
4The documentation for `/sys/block` is found here:
5- `/sys/block`: <https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block>
6- `/sys/block/<disk>/stat`: <https://docs.kernel.org/block/stat.html>
7- `/sys/block/<disk>/queue`: <https://www.kernel.org/doc/Documentation/block/queue-sysfs.txt>
8
9The main disk IO information is found in `/sys/block/<dev>/stat`, which is mostly equal to `/proc/diskstats`.
10
11Here is an example obtaining the data from `/proc/sys_block`:
12```no_run
13use proc_sys_parser::{block, block::SysBlock};
14
15let proc_block = block::read();
16
17println!("{:#?}", proc_block);
18```
19Example output:
20```text
21SysBlock {
22    block_devices: [
23        BlockDevice {
24            dev_block_major: 253,
25            dev_block_minor: 0,
26            device_name: "sda",
27            discard_alignment: 0,
28            stat_reads_completed_success: 9718,
29            stat_reads_merged: 3826,
30            stat_reads_sectors: 1052371,
31            stat_reads_time_spent_ms: 3026,
32            stat_writes_completed_success: 2856,
33            stat_writes_merged: 2331,
34            stat_writes_sectors: 312397,
35            stat_writes_time_spent_ms: 1947,
36            stat_ios_in_progress: 0,
37            stat_ios_time_spent_ms: 6004,
38            stat_ios_weighted_time_spent_ms: 5554,
39            stat_discards_completed_success: Some(
40                7141,
41            ),
42            stat_discards_merged: Some(
43                0,
44            ),
45            stat_discards_sectors: Some(
46                88014755,
47            ),
48            stat_discards_time_spent_ms: Some(
49                276,
50            ),
51            stat_flush_requests_completed_success: Some(
52                591,
53            ),
54            stat_flush_requests_time_spent_ms: Some(
55                304,
56            ),
57            alignment_offset: 0,
58            cache_type: "write back",
59            diskseq: 9,
60            hidden: 0,
61            inflight_reads: 1,
62            inflight_writes: 2,
63            range: 16,
64            removable: 0,
65            ro: 0,
66            size: 125829120,
67            queue_max_hw_sectors_kb: 2147483647,
68            queue_max_sectors_kb: 1280,
69            queue_max_discard_segments: 1,
70            queue_nr_requests: 256,
71            queue_nr_zones: Some(
72                0,
73            ),
74            queue_scheduler: "none",
75            queue_rotational: 1,
76            queue_dax: 0,
77            queue_add_random: 0,
78            queue_discard_granularity: 512,
79            queue_discard_max_hw_bytes: 2147483136,
80            queue_discard_max_bytes: 2147483136,
81            queue_hw_sector_size: 512,
82            queue_io_poll: 0,
83            queue_io_poll_delay: -1,
84            queue_logical_block_size: 512,
85            queue_minimum_io_size: 512,
86            queue_max_integrity_segments: 0,
87            queue_max_segments: 254,
88            queue_max_segment_size: 4294967295,
89            queue_nomerges: 0,
90            queue_physical_block_size: 512,
91            queue_optimal_io_size: 0,
92            queue_read_ahead_kb: 128,
93            queue_rq_affinity: 1,
94            queue_write_cache: "write back",
95            queue_write_same_max_bytes: 0,
96            queue_chunk_sectors: Some(
97                0,
98            ),
99            queue_zoned: Some(
100                "none",
101            ),
102        },
103    ],
104}
105```
106(edited for readability)
107
108If you want to change the directory that is read for the blockdevices for [`SysBlock`], which is `/sys/block`
109by default, use:
110```no_run
111use proc_sys_parser::{block, block::{SysBlock, Builder}};
112
113let proc_block = Builder::new().path("/my-sys/block").read();
114```
115*/
116use std::fs::{read_to_string, read_dir, DirEntry};
117use regex::Regex;
118use crate::ProcSysParserError;
119
120/// Struct for holding `/sys/block` block device statistics and information
121#[derive(Debug, PartialEq, Default)]
122pub struct SysBlock {
123    pub block_devices: Vec<BlockDevice>
124}
125
126/// Builder pattern for [`SysBlock`]
127#[derive(Default)]
128pub struct Builder {
129    pub sys_path : String,
130    pub filter : String,
131}
132
133impl Builder {
134    pub fn new() -> Builder {
135        Builder { 
136            sys_path: "/sys".to_string(), 
137            filter: "^dm-".to_string(),
138        }
139    }
140    pub fn path(mut self, sys_path: &str) -> Builder {
141        self.sys_path = sys_path.to_string();
142        self
143    }
144    pub fn regex(mut self, filter: &str) -> Builder {
145        self.filter = filter.to_string();
146        self
147    }
148    pub fn read(self) -> Result<SysBlock, ProcSysParserError> {
149        SysBlock::read_sys_block_devices(format!("{}/block", self.sys_path).as_str(), self.filter.as_str())
150    }
151}
152
153/// The main function for building a [`SysBlock`] struct with current data.
154/// This uses the Builder pattern, which allows settings such as the filename to specified.
155pub fn read() -> Result<SysBlock, ProcSysParserError> {
156   Builder::new().read()
157}
158
159/// Struct for holding `/sys/block/<device>` statistics and information
160#[derive(Debug, PartialEq, Default)]
161pub struct BlockDevice {
162    /// `/sys/block/<device>` name.
163    pub device_name: String,
164    //----------------------------------------------------------------------------------------------------------------//
165    /// `/sys/block/<device>/alignment_offset`
166    /// Number of bytes at the beginning of the device is offset from the disks natural alignment.
167    pub alignment_offset: u64,
168    /// `/sys/block/<device>/cache_type`
169    /// | cache_type STRING     | write cache | read cache |
170    /// |-----------------------|-------------|------------|
171    /// |"write through"        | off         | on         |
172    /// |"none"                 | off         | off        |
173    /// |"write back"           | on          | on         |
174    /// |"write back, no read"  | on          | off        |
175    ///
176    /// <https://docs.kernel.org/scsi/sd-parameters.html>
177    pub cache_type: Option<String>,
178    /// From the `/sys/block/<device>/dev` file: block major number.
179    pub dev_block_major: u64,
180    /// From the `/sys/block/<device>/dev` file: block major number.
181    pub dev_block_minor: u64,
182    /// `/sys/block/<device>/discard_alignment`
183    /// Devices that support discard functionality may internally allocate space in units that are bigger than the exported
184    /// logical block size.
185    /// This parameter indicates how many bytes the beginning of the device is offset from the internal allocation unit's
186    /// natural assignment.
187    pub discard_alignment: u64,
188    /// `/sys/block/<device>/diskseq`
189    /// Disk sequence number, which is a monotonically increasing number assigned to every drive.
190    /// This file does not exist on EL7.
191    pub diskseq: Option<u64>,
192    /// `/sys/block/<device>/hidden`
193    /// The block device is hidden. It doesn't produce events, and can't be openend from userspace.
194    /// Used for the underlying components of multipath devices.
195    pub hidden: u64,
196    /// `/sys/block/<device>/inflight`
197    /// Reports the number of pending IO requests in a device driver.
198    /// The inflight file contains two fields: reads and writes.
199    /// Number of read requests.
200    pub inflight_reads: u64,
201    /// `/sys/block/<device>/inflight`
202    /// Reports the number of pending IO requests in a device driver.
203    /// The inflight file contains two fields: reads and writes.
204    /// Number of write requests.
205    pub inflight_writes: u64,
206    /// `/sys/block/<device>/queue/add_random`
207    /// Disk entropy contribution.
208    pub queue_add_random: u64,
209    /// `/sys/block/<device>/queue/chunk_sectors`
210    /// Kernel 4.10+
211    /// For a RAID device (dm-raid), this is the size in 512 bytes sectors of the RAID volume stripe segment.
212    /// For a zoned block device, this is the size in 512 bytes sectors of the zones of the device.
213    pub queue_chunk_sectors: Option<u64>,
214    /// `/sys/block/<device>/queue/dax`
215    /// Does the device support direct access (DAX)? 0/no, 1/yes.
216    /// DAX is used by CPU-addressable storage to bypass the pagecache.
217    pub queue_dax: u64,
218    /// `/sys/block/<device>/queue/discard_granularity`
219    /// The size of the internal allocation of the device in bytes.
220    /// A value of '0' means the device does not support the discard functionality.
221    pub queue_discard_granularity: u64,
222    /// `/sys/block/<device>/queue/discard_max_bytes`
223    /// This is the current set maximum bytes as limit for the device.
224    /// Some devices might exhibit large latencies when large discards are issued, for which this setting can reduce the amount
225    /// of bytes discarded in a single operation, potentially reducing latency.
226    pub queue_discard_max_bytes: u64,
227    /// `/sys/block/<device>/queue/discard_max_hw_bytes`
228    /// Devices that have discard functionality may have internal limits on the number of bytes that can be trimmed or unmapped.
229    /// This value is set by the driver to indicate the maximum amount that can be discarded in a single operation.
230    /// A value of '0' means the device does not support the discard functionality.
231    pub queue_discard_max_hw_bytes: u64,
232    /// `/sys/block/<device>/queue/hw_sector_size`
233    /// The hardware sector size of the device, in bytes.
234    pub queue_hw_sector_size: u64,
235    /// `/sys/block/<device>/queue/io_poll`
236    /// Is polling enabled? 0/no, 1/yes.
237    pub queue_io_poll: u64,
238    /// `/sys/block/<device>/queue/io_poll_delay`
239    /// If polling is enabled, this controls what kind of polling will be performed.
240    /// The default is -1, classic polling.
241    /// Other modes:
242    /// 0: hybrid polling: kernel makes an educated guess when the IO will be complete. This might be somewhat
243    /// slower than classic polling, but is more efficient.
244    /// >0: number of microseconds before classic polling.
245    pub queue_io_poll_delay: i64,
246    /// `/sys/block/<device>/queue/logical_block_size`
247    /// The logical block size of the device, in bytes.
248    pub queue_logical_block_size: u64,
249    /// `/sys/block/<device>/queue/max_discard_segments`
250    /// The maximum number of DMA scatter/gather entries in a discard request.
251    pub queue_max_discard_segments: u64,
252    /// `/sys/block/<device>/queue/max_hw_sectors_kb`
253    /// The maximum IO size allowed by the driver.
254    /// Size is in kilobytes.
255    pub queue_max_hw_sectors_kb: u64,
256    /// `/sys/block/<device>/queue/max_sectors_kb`
257    /// The current set maximum IO size. (limited to max_hw_sectors_kb)
258    /// Size is in kilobytes.
259    pub queue_max_sectors_kb: u64,
260    /// `/sys/block/<device>/queue/max_integrity_segments`
261    /// The maximum number of elements in a DMA scatter/gather list with integrity data that will be submitted
262    /// by the block layer core to the associated driver.
263    pub queue_max_integrity_segments: u64,
264    /// `/sys/block/<device>/queue/max_segments`
265    pub queue_max_segments: u64,
266    /// `/sys/block/<device>/queue/max_segment_size`
267    pub queue_max_segment_size: u64,
268    /// `/sys/block/<device>/queue/minimum_io_size`
269    /// The smallest preferred IO size reported by the device
270    pub queue_minimum_io_size: u64,
271    /// `/sys/block/<device>/queue/nomerges`
272    /// Setting for disabling the lookup logic involved with IO merging.
273    /// Settings:
274    /// 0: all merges enabled (default)
275    /// 1: only simple one-hit merges will be tried.
276    /// 2: no merge algorithms will be tried.
277    pub queue_nomerges: u64,
278    /// `/sys/block/<device>/queue/nr_requests`
279    /// The current set maximum queue size independently for reads and writes.
280    /// This means the actual queue size can be potentialy nr_requests*2!
281    pub queue_nr_requests: u64,
282    /// `/sys/block/<device>/queue/nr_zones`
283    /// Kernel 4.20+
284    /// Total number of zones
285    pub queue_nr_zones: Option<u64>,
286    /// `/sys/block/<device>/queue/optimal_io_size`
287    /// The optimal io size reported by the device, in bytes.
288    pub queue_optimal_io_size: u64,
289    /// `/sys/block/<device>/queue/physical_block_size`
290    /// The physical block size of the device, in bytes.
291    pub queue_physical_block_size: u64,
292    /// `/sys/block/<device>/queue/read_ahead_kb`
293    /// The maximum number of kilobytes to read-ahead for filesystems on this block device.
294    pub queue_read_ahead_kb: u64,
295    /// `/sys/block/<device>/queue/rotational`
296    /// Is the device of rotating type? 0/no, 1/yes.
297    pub queue_rotational: u64,
298    /// `/sys/block/<device>/queue/rq_affinity`
299    /// - 1: the block layer will migrate req. completions to the cpu group that originally submitted
300    ///  the request. Some workloads can reduce cpu cycles due to caching effects.
301    /// - 2: force completion to run on the requesting cpu (bypassing the group aggregate function)
302    ///  this maximizes distribution.
303    pub queue_rq_affinity: u64,
304    /// `/sys/block/<device>/queue/scheduler`
305    /// The scheduler file contains all available IO schedulers, and the current set IO scheduler is enclosed in '[]' brackets.
306    /// When the file is parsed, it takes the current scheduler enclosed in the brackets.
307    pub queue_scheduler: String,
308    /// `/sys/block/<device>/queue/write_cache`
309    /// Whether the device has:
310    /// - "write back": write back caching enabled.
311    /// - "write through": no write back caching.
312    pub queue_write_cache: String,
313    /// `/sys/block/<device>/queue/write_write_same_max_bytes`
314    /// The number of bytes the device can write in a single write-same command.
315    /// A value of '0' means write-same is not supported by the device.
316    pub queue_write_same_max_bytes: u64,
317    /// `/sys/block/<device>/queue/zoned`
318    /// Kernel 4.10+
319    /// Indicates whether the device is a zoned blockdevice, and the zone model:
320    /// - "none": not zoned
321    /// - "host-aware"
322    /// - "host-managed"
323    /// - "drive-managed": shows as "none".
324    pub queue_zoned: Option<String>,
325    /// `/sys/block/<device>/range`
326    /// ?? No documentation found.
327    pub range: u64,
328    /// `/sys/block/<device>/removable`
329    /// Is the device removable? 0/no, 1/yes.
330    pub removable: u64,
331    /// `/sys/block/<device>/ro`
332    /// Is the device readonly? 0/no, 1/yes.
333    pub ro: u64,
334    /// `/sys/block/<device>/size`
335    /// The size of the block device in sectors.
336    /// Sector size is 512 bytes.
337    pub size: u64,
338    /// The stat file contents are in order of the fields:
339    /// From the `/sys/block/<device>/stat` file: number of read requests IOs processed.
340    pub stat_reads_completed_success: u64,
341    /// From the `/sys/block/<device>/stat` file: number of read requests IOs merged with in-queue IO.
342    pub stat_reads_merged: u64,
343    /// From the `/sys/block/<device>/stat` file: number of sectors read.
344    pub stat_reads_sectors: u64,
345    /// From the `/sys/block/<device>/stat` file: total time waited for read requests.
346    /// Sector size is 512 bytes.
347    pub stat_reads_time_spent_ms: u64,
348    /// From the `/sys/block/<device>/stat` file: number of write requests IOs processed.
349    /// Time is in milliseconds.
350    pub stat_writes_completed_success: u64,
351    /// From the `/sys/block/<device>/stat` file: number of write requests IOs merged with in-queue IO.
352    pub stat_writes_merged: u64,
353    /// From the `/sys/block/<device>/stat` file: number of sectors written.
354    pub stat_writes_sectors: u64,
355    /// From the `/sys/block/<device>/stat` file: total time waited for write requests.
356    /// Sector size is 512 bytes.
357    pub stat_writes_time_spent_ms: u64,
358    /// From the `/sys/block/<device>/stat` file: number of current IOs.
359    /// Time is in milliseconds.
360    pub stat_ios_in_progress: u64,
361    /// From the `/sys/block/<device>/stat` file: total time this device has been active.
362    pub stat_ios_time_spent_ms: u64,
363    /// From the `/sys/block/<device>/stat` file: total wait time for all requests.
364    /// Time is in milliseconds.
365    pub stat_ios_weighted_time_spent_ms: u64,
366    /// kernel 4.18+, returns none if field not found.
367    /// From the `/sys/block/<device>/stat` file: number of discard request IOs processed.
368    /// Time is in milliseconds.
369    pub stat_discards_completed_success: Option<u64>,
370    /// kernel 4.18+, returns none if field not found.
371    /// From the `/sys/block/<device>/stat` file: number of discard request IOs merged with in-queue IO.
372    pub stat_discards_merged: Option<u64>,
373    /// kernel 4.18+, returns none if field not found.
374    /// From the `/sys/block/<device>/stat` file: number of sectors discarded.
375    pub stat_discards_sectors: Option<u64>,
376    /// kernel 4.18+, returns none if field not found.
377    /// From the `/sys/block/<device>/stat` file: number of sectors discarded.
378    /// Sector size is 512 bytes.
379    pub stat_discards_time_spent_ms: Option<u64>,
380    /// kernel 5.5+, returns none if field not found.
381    /// From the `/sys/block/<device>/stat` file: number of flush IOs processed.
382    /// The block layer combines flush requests and executes at most one at a time.
383    /// Not tracked for partitions. <https://docs.kernel.org/block/stat.html>
384    pub stat_flush_requests_completed_success: Option<u64>,
385    /// kernel 5.5+, returns none if field not found.
386    /// From the `/sys/block/<device>/stat` file: total wit time for flush requests.
387    /// Time is in milliseconds.
388    pub stat_flush_requests_time_spent_ms: Option<u64>,
389}
390
391impl BlockDevice {
392    pub fn new() -> BlockDevice {
393        BlockDevice::default()
394    }
395}
396
397impl SysBlock {
398    pub fn new() -> SysBlock {
399        SysBlock::default() 
400    }
401    fn parse_dev(
402        blockdevice_data: &mut BlockDevice,
403        blockdevice_dir: &DirEntry,
404    ) -> Result<(), ProcSysParserError> {
405        //let dev_contents = read_to_string(blockdevice_dir.path().join("dev")).unwrap_or_else(|error| panic!("Error {} reading block device dev sysfs entry", error)).trim_end_matches('\n').to_string();
406        let dev_contents = read_to_string(blockdevice_dir.path().join("dev"))
407            .map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join("dev").to_string_lossy().to_string(), error})?
408            .trim_end_matches('\n').to_string();
409        let mut fields = dev_contents.split(':');
410        blockdevice_data.dev_block_major = fields.next().ok_or(ProcSysParserError::IteratorItemError { item: "block parse_dev major".to_string() })?
411                        .parse::<u64>().map_err(ProcSysParserError::ParseToIntegerError)?;
412        blockdevice_data.dev_block_minor = fields.next().ok_or(ProcSysParserError::IteratorItemError { item: "block parse_dev minor".to_string() })?
413                        .parse::<u64>().map_err(ProcSysParserError::ParseToIntegerError)?;
414        Ok(())
415    }
416    fn parse_inflight(
417        blockdevice_data: &mut BlockDevice,
418        blockdevice_dir: &DirEntry,
419    ) -> Result<(), ProcSysParserError> {
420        let inflight_from_file = read_to_string(blockdevice_dir.path().join("inflight"))
421            .map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join("inflight").to_string_lossy().to_string(), error})?
422            .trim_end_matches('\n').to_string();
423        blockdevice_data.inflight_reads = inflight_from_file.split_whitespace()
424            .nth(0)
425            .ok_or(ProcSysParserError::IteratorItemError { item: "block parse_inflight reads".to_string() })?
426            .parse::<u64>()
427            .map_err(ProcSysParserError::ParseToIntegerError)?;
428        blockdevice_data.inflight_writes = inflight_from_file.split_whitespace()
429            .nth(1)
430            .ok_or(ProcSysParserError::IteratorItemError { item: "block parse_inflight writes".to_string() })?
431            .parse::<u64>()
432            .map_err(ProcSysParserError::ParseToIntegerError)?;
433        Ok(())
434    }
435    fn parse_queue_scheduler(
436        blockdevice_data: &mut BlockDevice,
437        blockdevice_dir: &DirEntry,
438    ) -> Result<(), ProcSysParserError> {
439        let nr_requests = read_to_string(blockdevice_dir.path().join("queue").join("scheduler"))
440            .map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join("queue").join("scheduler").to_string_lossy().to_string(), error })?
441            .trim_end_matches('\n').to_string();
442        let left_bracket = nr_requests.find('[');
443        let right_bracket = nr_requests.find(']');
444
445        if left_bracket.is_some() && right_bracket.is_some() {
446            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();
447        } else {
448            blockdevice_data.queue_scheduler = "?".to_string();
449        }
450        Ok(())
451    }
452    fn parse_stat(
453        blockdevice_data: &mut BlockDevice,
454        blockdevice_dir: &DirEntry,
455    ) -> Result<(), ProcSysParserError> {
456        let parse_next_and_conversion_into_option_u64 = |result: Option<&str>| -> Option<u64> {
457            match result {
458                None => None,
459                Some(value) => {
460                    match value.parse::<u64>() {
461                        Err(_) => None,
462                        Ok(number) => Some(number),
463                    }
464                },
465            }
466        };
467
468        let stat_contents = read_to_string(blockdevice_dir.path().join("stat"))
469            .map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join("stat").to_string_lossy().to_string(), error })?
470            .trim_end_matches('\n')
471            .to_string();
472        let mut stat_contents_splitted = stat_contents
473            .split_whitespace();
474
475        blockdevice_data.stat_reads_completed_success = stat_contents_splitted.next()
476            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat reads_completed_success".to_string() })?
477            .parse::<u64>()
478            .map_err(ProcSysParserError::ParseToIntegerError)?;
479        blockdevice_data.stat_reads_merged = stat_contents_splitted.next()
480            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat reads_merged".to_string() })?
481            .parse::<u64>()
482            .map_err(ProcSysParserError::ParseToIntegerError)?;
483        blockdevice_data.stat_reads_sectors = stat_contents_splitted.next()
484            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat reads_sectors".to_string() })?
485            .parse::<u64>()
486            .map_err(ProcSysParserError::ParseToIntegerError)?;
487        blockdevice_data.stat_reads_time_spent_ms = stat_contents_splitted.next()
488            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat reads_time_spent_ms".to_string() })?
489            .parse::<u64>()
490            .map_err(ProcSysParserError::ParseToIntegerError)?;
491        blockdevice_data.stat_writes_completed_success = stat_contents_splitted.next()
492            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat writes_completed_success".to_string() })?
493            .parse::<u64>()
494            .map_err(ProcSysParserError::ParseToIntegerError)?;
495        blockdevice_data.stat_writes_merged = stat_contents_splitted.next()
496            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat writes_completed_success".to_string() })?
497            .parse::<u64>()
498            .map_err(ProcSysParserError::ParseToIntegerError)?;
499        blockdevice_data.stat_writes_sectors = stat_contents_splitted.next()
500            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat writes_sectors".to_string() })?
501            .parse::<u64>()
502            .map_err(ProcSysParserError::ParseToIntegerError)?;
503        blockdevice_data.stat_writes_time_spent_ms = stat_contents_splitted.next()
504            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat writes_time_spent_ms".to_string() })?
505            .parse::<u64>()
506            .map_err(ProcSysParserError::ParseToIntegerError)?;
507        blockdevice_data.stat_ios_in_progress = stat_contents_splitted.next()
508            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat ios_in_progress".to_string() })?
509            .parse::<u64>()
510            .map_err(ProcSysParserError::ParseToIntegerError)?;
511        blockdevice_data.stat_ios_time_spent_ms = stat_contents_splitted.next()
512            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat ios_time_spent_ms".to_string() })?
513            .parse::<u64>()
514            .map_err(ProcSysParserError::ParseToIntegerError)?;
515        blockdevice_data.stat_ios_weighted_time_spent_ms = stat_contents_splitted.next()
516            .ok_or(ProcSysParserError::FindItemError { item: "block parse_stat ios_weighted_time_spent_ms".to_string() })?
517            .parse::<u64>()
518            .map_err(ProcSysParserError::ParseToIntegerError)?;
519        blockdevice_data.stat_discards_completed_success = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
520        blockdevice_data.stat_discards_merged = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
521        blockdevice_data.stat_discards_sectors = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
522        blockdevice_data.stat_discards_time_spent_ms = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
523        blockdevice_data.stat_flush_requests_completed_success = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
524        blockdevice_data.stat_flush_requests_time_spent_ms = parse_next_and_conversion_into_option_u64(stat_contents_splitted.next());
525        Ok(())
526    }
527    fn parse_contents_file_u64(
528        file: &str,
529        blockdevice_dir: &DirEntry,
530    ) -> Result<u64, ProcSysParserError> {
531        read_to_string(blockdevice_dir.path().join(file))
532                .map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join(file).to_string_lossy().to_string(), error })?
533                .trim_end_matches('\n')
534                .to_string()
535                .parse::<u64>()
536                .map_err(ProcSysParserError::ParseToIntegerError)
537    }
538    fn parse_contents_file_i64(
539        file: &str,
540        blockdevice_dir: &DirEntry,
541    ) -> Result<i64, ProcSysParserError> {
542        read_to_string(blockdevice_dir.path().join(file))
543                .map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join(file).to_string_lossy().to_string(), error })?
544                .trim_end_matches('\n')
545                .to_string()
546                .parse::<i64>()
547                .map_err(ProcSysParserError::ParseToIntegerError)
548    }
549    fn parse_contents_file_option_u64(
550        file: &str,
551        blockdevice_dir: &DirEntry,
552    ) -> Result<Option<u64>, ProcSysParserError>
553    {
554        match read_to_string(blockdevice_dir.path().join(file)) {
555            Ok(result) => {
556                Ok(
557                    Some(result
558                        .trim_end_matches('\n')
559                        .to_string()
560                        .parse::<u64>()
561                        .map_err(ProcSysParserError::ParseToIntegerError)?)
562                )
563            },
564            Err(_) => Ok(None),
565        }
566    }
567    fn parse_contents_file_option_string(
568        file: &str,
569        blockdevice_dir: &DirEntry,
570    ) -> Result<Option<String>, ProcSysParserError> {
571        Ok(match read_to_string(blockdevice_dir.path().join(file)) {
572            Ok(result) => Some(result.trim_end_matches('\n').to_string()),
573            Err(_) => None
574        })
575    }
576    fn parse_contents_file_string(
577        file: &str,
578        blockdevice_dir: &DirEntry,
579    ) -> Result <String, ProcSysParserError> {
580        Ok(read_to_string(blockdevice_dir.path().join(file))
581            .map_err(|error| ProcSysParserError::FileReadError { file: blockdevice_dir.path().join(file).to_string_lossy().to_string(), error })?
582            .trim_end_matches('\n')
583            .to_string())
584    }
585    pub fn read_sys_block_devices(
586        sys_block_path: &str,
587        filter: &str,
588    ) -> Result<SysBlock, ProcSysParserError> {
589        let mut sysblock = SysBlock::new();
590
591        let blockdevice_directories = read_dir(sys_block_path)
592            .map_err(|error| ProcSysParserError::DirectoryReadError { directory: sys_block_path.to_string(), error })?;
593        let filter_regex = Regex::new(filter)
594            .map_err(|_| ProcSysParserError::RegexCompileError { regex: filter.to_string() })?;
595
596        for blockdevice in blockdevice_directories {
597            let directory_entry = blockdevice.unwrap_or_else(|error| panic!("Error {} reading block device sysfs entry", error));
598
599            // apply filter
600            if !filter_regex.as_str().is_empty() && filter_regex.is_match(&directory_entry.file_name().into_string().unwrap()) { continue };
601
602            let mut blockdevice_data = BlockDevice::new();
603
604            blockdevice_data.device_name = directory_entry.file_name().into_string().unwrap();
605            blockdevice_data.alignment_offset = SysBlock::parse_contents_file_u64("alignment_offset", &directory_entry)?;
606            blockdevice_data.cache_type = SysBlock::parse_contents_file_option_string("cache_type", &directory_entry)?;
607            SysBlock::parse_dev(&mut blockdevice_data, &directory_entry)?;
608            blockdevice_data.discard_alignment = SysBlock::parse_contents_file_u64("discard_alignment", &directory_entry)?;
609            blockdevice_data.diskseq = SysBlock::parse_contents_file_option_u64("diskseq", &directory_entry)?;
610            blockdevice_data.hidden = SysBlock::parse_contents_file_u64("hidden", &directory_entry)?;
611            SysBlock::parse_inflight(&mut blockdevice_data, &directory_entry)?;
612            blockdevice_data.queue_add_random = SysBlock::parse_contents_file_u64("queue/add_random", &directory_entry)?;
613            blockdevice_data.queue_chunk_sectors = SysBlock::parse_contents_file_option_u64("queue/chunk_sectors", &directory_entry)?;
614            blockdevice_data.queue_dax = SysBlock::parse_contents_file_u64("queue/dax", &directory_entry)?;
615            blockdevice_data.queue_discard_granularity = SysBlock::parse_contents_file_u64("queue/discard_granularity", &directory_entry)?;
616            blockdevice_data.queue_discard_max_bytes = SysBlock::parse_contents_file_u64("queue/discard_max_bytes", &directory_entry)?;
617            blockdevice_data.queue_discard_max_hw_bytes = SysBlock::parse_contents_file_u64("queue/discard_max_hw_bytes", &directory_entry)?;
618            blockdevice_data.queue_hw_sector_size = SysBlock::parse_contents_file_u64("queue/hw_sector_size", &directory_entry)?;
619            blockdevice_data.queue_io_poll = SysBlock::parse_contents_file_u64("queue/io_poll", &directory_entry)?;
620            blockdevice_data.queue_io_poll_delay = SysBlock::parse_contents_file_i64("queue/io_poll_delay", &directory_entry)?;
621            blockdevice_data.queue_logical_block_size = SysBlock::parse_contents_file_u64("queue/logical_block_size", &directory_entry)?;
622            blockdevice_data.queue_max_discard_segments = SysBlock::parse_contents_file_u64("queue/max_discard_segments", &directory_entry)?;
623            blockdevice_data.queue_max_hw_sectors_kb = SysBlock::parse_contents_file_u64("queue/max_hw_sectors_kb", &directory_entry)?;
624            blockdevice_data.queue_max_integrity_segments = SysBlock::parse_contents_file_u64("queue/max_integrity_segments", &directory_entry)?;
625            blockdevice_data.queue_max_sectors_kb = SysBlock::parse_contents_file_u64("queue/max_sectors_kb", &directory_entry)?;
626            blockdevice_data.queue_max_segment_size = SysBlock::parse_contents_file_u64("queue/max_segment_size", &directory_entry)?;
627            blockdevice_data.queue_max_segments = SysBlock::parse_contents_file_u64("queue/max_segments", &directory_entry)?;
628            blockdevice_data.queue_minimum_io_size = SysBlock::parse_contents_file_u64("queue/minimum_io_size", &directory_entry)?;
629            blockdevice_data.queue_nomerges = SysBlock::parse_contents_file_u64("queue/nomerges", &directory_entry)?;
630            blockdevice_data.queue_nr_requests = SysBlock::parse_contents_file_u64("queue/nr_requests", &directory_entry)?;
631            blockdevice_data.queue_nr_zones = SysBlock::parse_contents_file_option_u64("queue/nr_zones", &directory_entry)?;
632            blockdevice_data.queue_optimal_io_size = SysBlock::parse_contents_file_u64("queue/optimal_io_size", &directory_entry)?;
633            blockdevice_data.queue_physical_block_size = SysBlock::parse_contents_file_u64("queue/physical_block_size", &directory_entry)?;
634            blockdevice_data.queue_read_ahead_kb = SysBlock::parse_contents_file_u64("queue/read_ahead_kb", &directory_entry)?;
635            blockdevice_data.queue_rotational = SysBlock::parse_contents_file_u64("queue/rotational", &directory_entry)?;
636            blockdevice_data.queue_rq_affinity = SysBlock::parse_contents_file_u64("queue/rq_affinity", &directory_entry)?;
637            SysBlock::parse_queue_scheduler(&mut blockdevice_data, &directory_entry)?;
638            blockdevice_data.queue_write_cache = SysBlock::parse_contents_file_string("queue/write_cache", &directory_entry)?;
639            blockdevice_data.queue_write_same_max_bytes = SysBlock::parse_contents_file_u64("queue/write_same_max_bytes", &directory_entry)?;
640            blockdevice_data.queue_zoned = SysBlock::parse_contents_file_option_string("queue/zoned", &directory_entry)?;
641            blockdevice_data.range = SysBlock::parse_contents_file_u64("range", &directory_entry)?;
642            blockdevice_data.removable = SysBlock::parse_contents_file_u64("removable", &directory_entry)?;
643            blockdevice_data.ro = SysBlock::parse_contents_file_u64("ro", &directory_entry)?;
644            blockdevice_data.size = SysBlock::parse_contents_file_u64("size", &directory_entry)?;
645
646            SysBlock::parse_stat(&mut blockdevice_data, &directory_entry)?;
647
648            sysblock.block_devices.push(blockdevice_data);
649        }
650
651        Ok(sysblock)
652    }
653}
654
655#[cfg(test)]
656mod tests {
657    use std::fs::{write, remove_dir_all, create_dir_all};
658    use rand::{thread_rng, Rng};
659    use rand::distributions::Alphanumeric;
660    use super::*;
661
662    #[test]
663    fn create_sys_block_device_parse_files() {
664        let alignment_offset = format!("0\n");
665        let cache_type = format!("write back\n");
666        let dev= format!("253:0\n");
667        let discard_alignment = format!("0\n");
668        let diskseq = format!("9\n");
669        let hidden = format!("0\n");
670        let inflight = format!("       1        2\n");
671        let queue_add_random= format!("0\n");
672        let queue_chunk_sectors = format!("0\n");
673        let queue_dax = format!("0\n");
674        let queue_discard_granularity = format!("512\n");
675        let queue_discard_max_bytes = format!("2147483136\n");
676        let queue_discard_max_hw_bytes = format!("2147483136\n");
677        let queue_hw_sector_size = format!("512\n");
678        let queue_io_poll = format!("0\n");
679        let queue_io_poll_delay = format!("-1\n");
680        let queue_logical_block_size = format!("512\n");
681        let queue_max_discard_segments = format!("1\n");
682        let queue_max_hw_sectors_kb = format!("2147483647\n");
683        let queue_max_integrity_segments = format!("0\n");
684        let queue_max_sectors_kb = format!("1280\n");
685        let queue_max_segment_size = format!("4294967295\n");
686        let queue_max_segments = format!("254\n");
687        let queue_minimum_io_size = format!("512\n");
688        let queue_nomerges = format!("0\n");
689        let queue_nr_requests = format!("256\n");
690        let queue_nr_zones = format!("0\n");
691        let queue_optimal_io_size = format!("0\n");
692        let queue_physical_block_size = format!("512\n");
693        let queue_read_ahead_kb = format!("128\n");
694        let queue_rotational = format!("1\n");
695        let queue_rq_affinity = format!("1\n");
696        let queue_scheduler = format!("[none] mq-deadline\n");
697        let queue_write_cache = format!("write back\n");
698        let queue_write_same_max_bytes = format!("0\n");
699        let queue_zoned = format!("none\n");
700        let range = format!("16\n");
701        let removable = format!("0\n");
702        let ro = format!("0\n");
703        let size = format!("125829120\n");
704        let stat = format!("    9718     3826  1052371     3026     2856     2331   312397     1947        0     6004     5554     7141        0 88014755      276      591      304\n");
705
706        let directory_suffix: String = thread_rng().sample_iter(&Alphanumeric).take(8).map(char::from).collect();
707        let test_path = format!("/tmp/test.{}", directory_suffix);
708        create_dir_all(format!("{}/block/sda/queue", test_path)).expect("Error creating mock sysfs directories.");
709        write(format!("{}/block/sda/alignment_offset", test_path),alignment_offset).expect("error writing to mock sysfs alignment_offset file.");
710        write(format!("{}/block/sda/cache_type", test_path),cache_type).expect("error writing to mock sysfs cache_type file.");
711        write(format!("{}/block/sda/dev", test_path),dev).expect("error writing to mock sysfs dev file.");
712        write(format!("{}/block/sda/discard_alignment", test_path),discard_alignment).expect("error writing to mock sysfs discard_alginment file.");
713        write(format!("{}/block/sda/diskseq", test_path),diskseq).expect("error writing to mock sysfs diskseq file.");
714        write(format!("{}/block/sda/hidden", test_path),hidden).expect("error writing to mock sysfs hidden file.");
715        write(format!("{}/block/sda/inflight", test_path),inflight).expect("error writing to mock sysfs inflight file.");
716        write(format!("{}/block/sda/queue/add_random", test_path),queue_add_random).expect("error writing to mock sysfs queue/add_random file.");
717        write(format!("{}/block/sda/queue/chunk_sectors", test_path),queue_chunk_sectors).expect("error writing to mock sysfs queue/chunk_sectors file.");
718        write(format!("{}/block/sda/queue/dax", test_path),queue_dax).expect("error writing to mock sysfs queue/dax file.");
719        write(format!("{}/block/sda/queue/discard_granularity", test_path),queue_discard_granularity).expect("error writing to mock sysfs queue/discard_granularity file.");
720        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.");
721        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.");
722        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.");
723        write(format!("{}/block/sda/queue/io_poll", test_path),queue_io_poll).expect("error writing to mock sysfs queue/io_poll file.");
724        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.");
725        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.");
726        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.");
727        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.");
728        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.");
729        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.");
730        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.");
731        write(format!("{}/block/sda/queue/max_segments", test_path),queue_max_segments).expect("error writing to mock sysfs queue/max_segments file.");
732        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.");
733        write(format!("{}/block/sda/queue/nomerges", test_path),queue_nomerges).expect("error writing to mock sysfs queue/nomerges file.");
734        write(format!("{}/block/sda/queue/nr_requests", test_path),queue_nr_requests).expect("error writing to mock sysfs queue/nr_requests file.");
735        write(format!("{}/block/sda/queue/nr_zones", test_path),queue_nr_zones).expect("error writing to mock sysfs queue/nr_zones file.");
736        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.");
737        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.");
738        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.");
739        write(format!("{}/block/sda/queue/rotational", test_path),queue_rotational).expect("error writing to mock sysfs queue/rotational file.");
740        write(format!("{}/block/sda/queue/rq_affinity", test_path),queue_rq_affinity).expect("error writing to mock sysfs queue/rq_affinity file.");
741        write(format!("{}/block/sda/queue/scheduler", test_path),queue_scheduler).expect("error writing to mock sysfs queue/scheduler file.");
742        write(format!("{}/block/sda/queue/write_cache", test_path),queue_write_cache).expect("error writing to mock sysfs queue/write_cache file.");
743        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.");
744        write(format!("{}/block/sda/queue/zoned", test_path),queue_zoned).expect("error writing to mock sysfs queue/zoned file.");
745        write(format!("{}/block/sda/range", test_path),range).expect("error writing to mock sysfs range file.");
746        write(format!("{}/block/sda/removable", test_path),removable).expect("error writing to mock sysfs removable file.");
747        write(format!("{}/block/sda/ro", test_path),ro).expect("error writing to mock sysfs ro file.");
748        write(format!("{}/block/sda/size", test_path),size).expect("error writing to mock sysfs size file.");
749        write(format!("{}/block/sda/stat", test_path),stat).expect("error writing to mock sysfs stat file.");
750
751        let result = Builder::new().path(&test_path).read().unwrap();
752
753        remove_dir_all(test_path).unwrap();
754
755        assert_eq!(result, SysBlock {
756            block_devices: vec![
757                BlockDevice {
758                    dev_block_major: 253,
759                    dev_block_minor: 0,
760                    device_name: "sda".to_string(),
761                    discard_alignment: 0,
762                    stat_reads_completed_success: 9718,
763                    stat_reads_merged: 3826,
764                    stat_reads_sectors: 1052371,
765                    stat_reads_time_spent_ms: 3026,
766                    stat_writes_completed_success: 2856,
767                    stat_writes_merged: 2331,
768                    stat_writes_sectors: 312397,
769                    stat_writes_time_spent_ms: 1947,
770                    stat_ios_in_progress: 0,
771                    stat_ios_time_spent_ms: 6004,
772                    stat_ios_weighted_time_spent_ms: 5554,
773                    stat_discards_completed_success: Some(
774                        7141,
775                    ),
776                    stat_discards_merged: Some(
777                        0,
778                    ),
779                    stat_discards_sectors: Some(
780                        88014755,
781                    ),
782                    stat_discards_time_spent_ms: Some(
783                        276,
784                    ),
785                    stat_flush_requests_completed_success: Some(
786                        591,
787                    ),
788                    stat_flush_requests_time_spent_ms: Some(
789                        304,
790                    ),
791                    alignment_offset: 0,
792                    cache_type: Some("write back".to_string()),
793                    diskseq: Some(9),
794                    hidden: 0,
795                    inflight_reads: 1,
796                    inflight_writes: 2,
797                    range: 16,
798                    removable: 0,
799                    ro: 0,
800                    size: 125829120,
801                    queue_max_hw_sectors_kb: 2147483647,
802                    queue_max_sectors_kb: 1280,
803                    queue_max_discard_segments: 1,
804                    queue_nr_requests: 256,
805                    queue_nr_zones: Some(
806                        0,
807                    ),
808                    queue_scheduler: "none".to_string(),
809                    queue_rotational: 1,
810                    queue_dax: 0,
811                    queue_add_random: 0,
812                    queue_discard_granularity: 512,
813                    queue_discard_max_hw_bytes: 2147483136,
814                    queue_discard_max_bytes: 2147483136,
815                    queue_hw_sector_size: 512,
816                    queue_io_poll: 0,
817                    queue_io_poll_delay: -1,
818                    queue_logical_block_size: 512,
819                    queue_minimum_io_size: 512,
820                    queue_max_integrity_segments: 0,
821                    queue_max_segments: 254,
822                    queue_max_segment_size: 4294967295,
823                    queue_nomerges: 0,
824                    queue_physical_block_size: 512,
825                    queue_optimal_io_size: 0,
826                    queue_read_ahead_kb: 128,
827                    queue_rq_affinity: 1,
828                    queue_write_cache: "write back".to_string(),
829                    queue_write_same_max_bytes: 0,
830                    queue_chunk_sectors: Some(
831                        0,
832                    ),
833                    queue_zoned: Some(
834                        "none".to_string(),
835                    ),
836                },
837            ],
838        }
839        );
840    }
841    #[test]
842    fn create_sys_block_device_parse_files_non_existent() {
843        let alignment_offset = "0";
844        let cache_type = "write back";
845        let dev = "253:0";
846        let discard_alignment = "0";
847        let diskseq = "9";
848        let hidden = "0";
849        let inflight = "       1        2";
850        let queue_add_random = "0";
851        let queue_dax = "0";
852        let queue_discard_granularity = "512";
853        let queue_discard_max_bytes = "2147483136";
854        let queue_discard_max_hw_bytes = "2147483136";
855        let queue_hw_sector_size = "512";
856        let queue_io_poll = "0";
857        let queue_io_poll_delay = "-1";
858        let queue_logical_block_size = "512";
859        let queue_max_discard_segments = "1";
860        let queue_max_hw_sectors_kb = "2147483647";
861        let queue_max_integrity_segments = "0";
862        let queue_max_sectors_kb = "1280";
863        let queue_max_segment_size = "4294967295";
864        let queue_max_segments = "254";
865        let queue_minimum_io_size = "512";
866        let queue_nomerges = "0";
867        let queue_nr_requests = "256";
868        let queue_optimal_io_size = "0";
869        let queue_physical_block_size = "512";
870        let queue_read_ahead_kb = "128";
871        let queue_rotational = "1";
872        let queue_rq_affinity = "1";
873        let queue_scheduler = "[none] mq-deadline";
874        let queue_write_cache = "write back";
875        let queue_write_same_max_bytes = "0";
876        let range = "16";
877        let removable = "0";
878        let ro = "0";
879        let size = "125829120";
880        let stat = "    9718     3826  1052371     3026     2856     2331   312397     1947        0     6004     5554";
881
882        let directory_suffix: String = thread_rng().sample_iter(&Alphanumeric).take(8).map(char::from).collect();
883        let test_path = format!("/tmp/test.{}", directory_suffix);
884        
885        create_dir_all(format!("{}/block/sda/queue", test_path)).expect("Error creating mock sysfs directories.");
886        write(format!("{}/block/sda/alignment_offset", test_path), alignment_offset).expect("error writing to mock sysfs alignment_offset file.");
887        write(format!("{}/block/sda/cache_type", test_path), cache_type).expect("error writing to mock sysfs cache_type file.");
888        write(format!("{}/block/sda/dev", test_path), dev).expect("error writing to mock sysfs dev file.");
889        write(format!("{}/block/sda/discard_alignment", test_path), discard_alignment).expect("error writing to mock sysfs discard_alginment file.");
890        write(format!("{}/block/sda/diskseq", test_path), diskseq).expect("error writing to mock sysfs diskseq file.");
891        write(format!("{}/block/sda/hidden", test_path), hidden).expect("error writing to mock sysfs hidden file.");
892        write(format!("{}/block/sda/inflight", test_path), inflight).expect("error writing to mock sysfs inflight file.");
893        write(format!("{}/block/sda/queue/add_random", test_path), queue_add_random).expect("error writing to mock sysfs queue/add_random file.");
894        write(format!("{}/block/sda/queue/dax", test_path), queue_dax).expect("error writing to mock sysfs queue/dax file.");
895        write(format!("{}/block/sda/queue/discard_granularity", test_path), queue_discard_granularity).expect("error writing to mock sysfs queue/discard_granularity file.");
896        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.");
897        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.");
898        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.");
899        write(format!("{}/block/sda/queue/io_poll", test_path), queue_io_poll).expect("error writing to mock sysfs queue/io_poll file.");
900        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.");
901        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.");
902        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.");
903        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.");
904        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.");
905        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.");
906        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.");
907        write(format!("{}/block/sda/queue/max_segments", test_path), queue_max_segments).expect("error writing to mock sysfs queue/max_segments file.");
908        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.");
909        write(format!("{}/block/sda/queue/nomerges", test_path), queue_nomerges).expect("error writing to mock sysfs queue/nomerges file.");
910        write(format!("{}/block/sda/queue/nr_requests", test_path), queue_nr_requests).expect("error writing to mock sysfs queue/nr_requests file.");
911        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.");
912        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.");
913        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.");
914        write(format!("{}/block/sda/queue/rotational", test_path), queue_rotational).expect("error writing to mock sysfs queue/rotational file.");
915        write(format!("{}/block/sda/queue/rq_affinity", test_path), queue_rq_affinity).expect("error writing to mock sysfs queue/rq_affinity file.");
916        write(format!("{}/block/sda/queue/scheduler", test_path), queue_scheduler).expect("error writing to mock sysfs queue/scheduler file.");
917        write(format!("{}/block/sda/queue/write_cache", test_path), queue_write_cache).expect("error writing to mock sysfs queue/write_cache file.");
918        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.");
919        write(format!("{}/block/sda/range", test_path), range).expect("error writing to mock sysfs range file.");
920        write(format!("{}/block/sda/removable", test_path), removable).expect("error writing to mock sysfs removable file.");
921        write(format!("{}/block/sda/ro", test_path), ro).expect("error writing to mock sysfs ro file.");
922        write(format!("{}/block/sda/size", test_path), size).expect("error writing to mock sysfs size file.");
923        write(format!("{}/block/sda/stat", test_path), stat).expect("error writing to mock sysfs stat file.");
924
925        let result = Builder::new().path(&test_path).read().unwrap();
926
927        remove_dir_all(test_path).unwrap();
928
929        assert_eq!(result,
930                   SysBlock {
931                       block_devices: vec![
932                           BlockDevice {
933                               dev_block_major: 253,
934                               dev_block_minor: 0,
935                               device_name: "sda".to_string(),
936                               discard_alignment: 0,
937                               stat_reads_completed_success: 9718,
938                               stat_reads_merged: 3826,
939                               stat_reads_sectors: 1052371,
940                               stat_reads_time_spent_ms: 3026,
941                               stat_writes_completed_success: 2856,
942                               stat_writes_merged: 2331,
943                               stat_writes_sectors: 312397,
944                               stat_writes_time_spent_ms: 1947,
945                               stat_ios_in_progress: 0,
946                               stat_ios_time_spent_ms: 6004,
947                               stat_ios_weighted_time_spent_ms: 5554,
948                               stat_discards_completed_success: None,
949                               stat_discards_merged: None,
950                               stat_discards_sectors: None,
951                               stat_discards_time_spent_ms: None,
952                               stat_flush_requests_completed_success: None,
953                               stat_flush_requests_time_spent_ms: None,
954                               alignment_offset: 0,
955                               cache_type: Some("write back".to_string()),
956                               diskseq: Some(9),
957                               hidden: 0,
958                               inflight_reads: 1,
959                               inflight_writes: 2,
960                               range: 16,
961                               removable: 0,
962                               ro: 0,
963                               size: 125829120,
964                               queue_max_hw_sectors_kb: 2147483647,
965                               queue_max_sectors_kb: 1280,
966                               queue_max_discard_segments: 1,
967                               queue_nr_requests: 256,
968                               queue_nr_zones: None,
969                               queue_scheduler: "none".to_string(),
970                               queue_rotational: 1,
971                               queue_dax: 0,
972                               queue_add_random: 0,
973                               queue_discard_granularity: 512,
974                               queue_discard_max_hw_bytes: 2147483136,
975                               queue_discard_max_bytes: 2147483136,
976                               queue_hw_sector_size: 512,
977                               queue_io_poll: 0,
978                               queue_io_poll_delay: -1,
979                               queue_logical_block_size: 512,
980                               queue_minimum_io_size: 512,
981                               queue_max_integrity_segments: 0,
982                               queue_max_segments: 254,
983                               queue_max_segment_size: 4294967295,
984                               queue_nomerges: 0,
985                               queue_physical_block_size: 512,
986                               queue_optimal_io_size: 0,
987                               queue_read_ahead_kb: 128,
988                               queue_rq_affinity: 1,
989                               queue_write_cache: "write back".to_string(),
990                               queue_write_same_max_bytes: 0,
991                               queue_chunk_sectors: None,
992                               queue_zoned: None,
993                           },
994                       ],
995                   }
996        );
997    }
998    #[test]
999    fn create_sys_block_device_test_filter() {
1000        let alignment_offset = "0";
1001        let cache_type = "write back";
1002        let dev = "253:0";
1003        let discard_alignment = "0";
1004        let diskseq = "9";
1005        let hidden = "0";
1006        let inflight = "       1        2";
1007        let queue_add_random = "0";
1008        let queue_dax = "0";
1009        let queue_discard_granularity = "512";
1010        let queue_discard_max_bytes = "2147483136";
1011        let queue_discard_max_hw_bytes = "2147483136";
1012        let queue_hw_sector_size = "512";
1013        let queue_io_poll = "0";
1014        let queue_io_poll_delay = "-1";
1015        let queue_logical_block_size = "512";
1016        let queue_max_discard_segments = "1";
1017        let queue_max_hw_sectors_kb = "2147483647";
1018        let queue_max_integrity_segments = "0";
1019        let queue_max_sectors_kb = "1280";
1020        let queue_max_segment_size = "4294967295";
1021        let queue_max_segments = "254";
1022        let queue_minimum_io_size = "512";
1023        let queue_nomerges = "0";
1024        let queue_nr_requests = "256";
1025        let queue_optimal_io_size = "0";
1026        let queue_physical_block_size = "512";
1027        let queue_read_ahead_kb = "128";
1028        let queue_rotational = "1";
1029        let queue_rq_affinity = "1";
1030        let queue_scheduler = "[none] mq-deadline";
1031        let queue_write_cache = "write back";
1032        let queue_write_same_max_bytes = "0";
1033        let range = "16";
1034        let removable = "0";
1035        let ro = "0";
1036        let size = "125829120";
1037        let stat = "    9718     3826  1052371     3026     2856     2331   312397     1947        0     6004     5554";
1038
1039        let directory_suffix: String = thread_rng().sample_iter(&Alphanumeric).take(8).map(char::from).collect();
1040        let test_path = format!("/tmp/test.{}", directory_suffix);
1041        
1042        create_dir_all(format!("{}/block/dm-0/queue", test_path)).expect("Error creating mock sysfs directories.");
1043        write(format!("{}/block/dm-0/alignment_offset", test_path), alignment_offset).expect("error writing to mock sysfs alignment_offset file.");
1044        write(format!("{}/block/dm-0/cache_type", test_path), cache_type).expect("error writing to mock sysfs cache_type file.");
1045        write(format!("{}/block/dm-0/dev", test_path), dev).expect("error writing to mock sysfs dev file.");
1046        write(format!("{}/block/dm-0/discard_alignment", test_path), discard_alignment).expect("error writing to mock sysfs discard_alginment file.");
1047        write(format!("{}/block/dm-0/diskseq", test_path), diskseq).expect("error writing to mock sysfs diskseq file.");
1048        write(format!("{}/block/dm-0/hidden", test_path), hidden).expect("error writing to mock sysfs hidden file.");
1049        write(format!("{}/block/dm-0/inflight", test_path), inflight).expect("error writing to mock sysfs inflight file.");
1050        write(format!("{}/block/dm-0/queue/add_random", test_path), queue_add_random).expect("error writing to mock sysfs queue/add_random file.");
1051        write(format!("{}/block/dm-0/queue/dax", test_path), queue_dax).expect("error writing to mock sysfs queue/dax file.");
1052        write(format!("{}/block/dm-0/queue/discard_granularity", test_path), queue_discard_granularity).expect("error writing to mock sysfs queue/discard_granularity file.");
1053        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.");
1054        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.");
1055        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.");
1056        write(format!("{}/block/dm-0/queue/io_poll", test_path), queue_io_poll).expect("error writing to mock sysfs queue/io_poll file.");
1057        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.");
1058        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.");
1059        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.");
1060        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.");
1061        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.");
1062        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.");
1063        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.");
1064        write(format!("{}/block/dm-0/queue/max_segments", test_path), queue_max_segments).expect("error writing to mock sysfs queue/max_segments file.");
1065        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.");
1066        write(format!("{}/block/dm-0/queue/nomerges", test_path), queue_nomerges).expect("error writing to mock sysfs queue/nomerges file.");
1067        write(format!("{}/block/dm-0/queue/nr_requests", test_path), queue_nr_requests).expect("error writing to mock sysfs queue/nr_requests file.");
1068        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.");
1069        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.");
1070        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.");
1071        write(format!("{}/block/dm-0/queue/rotational", test_path), queue_rotational).expect("error writing to mock sysfs queue/rotational file.");
1072        write(format!("{}/block/dm-0/queue/rq_affinity", test_path), queue_rq_affinity).expect("error writing to mock sysfs queue/rq_affinity file.");
1073        write(format!("{}/block/dm-0/queue/scheduler", test_path), queue_scheduler).expect("error writing to mock sysfs queue/scheduler file.");
1074        write(format!("{}/block/dm-0/queue/write_cache", test_path), queue_write_cache).expect("error writing to mock sysfs queue/write_cache file.");
1075        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.");
1076        write(format!("{}/block/dm-0/range", test_path), range).expect("error writing to mock sysfs range file.");
1077        write(format!("{}/block/dm-0/removable", test_path), removable).expect("error writing to mock sysfs removable file.");
1078        write(format!("{}/block/dm-0/ro", test_path), ro).expect("error writing to mock sysfs ro file.");
1079        write(format!("{}/block/dm-0/size", test_path), size).expect("error writing to mock sysfs size file.");
1080        write(format!("{}/block/dm-0/stat", test_path), stat).expect("error writing to mock sysfs stat file.");
1081
1082        let result = Builder::new().path(&test_path).read().unwrap();
1083
1084        remove_dir_all(test_path).unwrap();
1085
1086        assert_eq!(result, SysBlock { block_devices: vec![] });
1087    }
1088}