1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
mod system;
mod types;

pub use types::*;

#[cfg(test)]
use super::mocks::SYS_BLOCK_DEV_STAT;
use super::SysPath;
use crate::Result;
use system::blk_bsz_get;

// Parses out major and minor number from str like '8:1'
// and returns a tuple (8, 1)
fn parse_maj_min(dev: &str) -> Option<(u32, u32)> {
    let mut elems = dev.split(':');

    if let Some(maj) = elems.next() {
        if let Some(min) = elems.next() {
            if let Ok(maj) = maj.trim().parse::<u32>() {
                if let Ok(min) = min.trim().parse::<u32>() {
                    return Some((maj, min));
                }
            }
        }
    }

    None
}

/// Returns block size of device in bytes
/// device argument must be a path to block device file descriptor
pub fn block_size(device: &str) -> Result<i64> {
    blk_bsz_get(SysPath::Dev(device).path().to_string_lossy().as_ref())
}

/// Parses a StorageDevice object from system. If the provided name
/// doesn't start with `sd` returns an error.
pub fn stat_block_device(name: &str) -> Result<StorageDevice> {
    StorageDevice::from_sys(name)
}

/// Parses a DeviceMapper object from system. If the provided name
/// doesn't start with `dm` returns an error.
pub fn stat_device_mapper(name: &str) -> Result<DeviceMapper> {
    DeviceMapper::from_sys(name)
}

/// Parses a ScsiCdrom object from system. If the provided name
/// doesn't start with `sr` returns an error.
pub fn stat_scsi_cdrom(name: &str) -> Result<ScsiCdrom> {
    ScsiCdrom::from_sys(name)
}

/// Parses a MultipleDeviceStorage object from system. If the provided name
/// doesn't start with `md` returns an error.
pub fn stat_multiple_device_storage(name: &str) -> Result<MultipleDeviceStorage> {
    MultipleDeviceStorage::from_sys(name)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn parses_block_device_stat_from_sys_block_dev_stat() {
        let dev = StorageDevice {
            model: "ST2000DM008-2FR1".to_string(),
            vendor: "ATA".to_string(),
            state: "running".to_string(),
            info: BlockStorageInfo {
                stat: BlockStorageStat {
                    read_ios: 327,
                    read_merges: 72,
                    read_sectors: 8832,
                    read_ticks: 957,
                    write_ios: 31,
                    write_merges: 1,
                    write_sectors: 206,
                    write_ticks: 775,
                    in_flight: 0,
                    io_ticks: 1620,
                    time_in_queue: 2427,
                    discard_ios: 0,
                    discard_merges: 0,
                    discard_sectors: 0,
                    discard_ticks: 0,
                },

                dev: "sda".to_string(),
                size: 3907029168,
                maj: 8,
                min: 1,
                block_size: 4096,
            },
            partitions: vec![],
        };

        assert_eq!(BlockStorageStat::from_stat(SYS_BLOCK_DEV_STAT), Ok(dev.info.stat))
    }
    #[test]
    fn parses_maj_min() {
        assert_eq!(parse_maj_min("X:5"), None);
        assert_eq!(parse_maj_min("1:Y"), None);
        assert_eq!(parse_maj_min("rand:"), None);
        assert_eq!(parse_maj_min(":xx"), None);
        assert_eq!(parse_maj_min("xxx"), None);
        assert_eq!(parse_maj_min("8:1"), Some((8, 1)))
    }
}