block_utils/
nvme.rs

1use crate::{BlockResult, BlockUtilsError};
2use serde::{Deserialize, Serialize};
3use std::path::Path;
4use std::process::Command;
5
6#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
7#[serde(rename_all = "PascalCase")]
8pub struct NvmeDevice {
9    pub name_space: u64,
10    pub device_path: String,
11    pub index: Option<u64>,
12    pub model_number: String,
13    pub product_name: Option<String>,
14    pub firmware: Option<String>,
15    pub serial_number: String,
16    pub used_bytes: u64,
17    #[serde(rename = "MaximumLBA")]
18    pub maximum_lba: u64,
19    pub physical_size: u64,
20    pub sector_size: u32,
21}
22
23#[derive(Deserialize)]
24#[serde(rename_all = "PascalCase")]
25struct NvmeDeviceContainer {
26    devices: Vec<NvmeDevice>,
27}
28
29/// Retrieve the error logs from the nvme device
30pub fn get_error_log(dev: &Path) -> BlockResult<serde_json::Value> {
31    let out = Command::new("nvme")
32        .args(&["error-log", &dev.to_string_lossy(), "-o", "json"])
33        .output()?;
34    if !out.status.success() {
35        return Err(BlockUtilsError::new(
36            String::from_utf8_lossy(&out.stderr).into_owned(),
37        ));
38    }
39    let stdout = String::from_utf8_lossy(&out.stdout);
40    let deserialized: serde_json::Value = serde_json::from_str(&stdout)?;
41    Ok(deserialized)
42}
43
44/// Retrieve the firmware logs from the nvme device
45pub fn get_firmware_log(dev: &Path) -> BlockResult<serde_json::Value> {
46    let out = Command::new("nvme")
47        .args(&["fw-log", &dev.to_string_lossy(), "-o", "json"])
48        .output()?;
49    if !out.status.success() {
50        return Err(BlockUtilsError::new(
51            String::from_utf8_lossy(&out.stderr).into_owned(),
52        ));
53    }
54    let stdout = String::from_utf8_lossy(&out.stdout);
55    let deserialized: serde_json::Value = serde_json::from_str(&stdout)?;
56    Ok(deserialized)
57}
58
59/// Retrieve the smart logs from the nvme device
60pub fn get_smart_log(dev: &Path) -> BlockResult<serde_json::Value> {
61    let out = Command::new("nvme")
62        .args(&["smart-log", &dev.to_string_lossy(), "-o", "json"])
63        .output()?;
64    if !out.status.success() {
65        return Err(BlockUtilsError::new(
66            String::from_utf8_lossy(&out.stderr).into_owned(),
67        ));
68    }
69    let stdout = String::from_utf8_lossy(&out.stdout);
70    let deserialized: serde_json::Value = serde_json::from_str(&stdout)?;
71    Ok(deserialized)
72}
73
74/// Retrieve a log page from the nvme device
75pub fn get_log(dev: &Path, id: u8, len: u16) -> BlockResult<Vec<u8>> {
76    let out = Command::new("nvme")
77        .args(&[
78            "get-log",
79            &dev.to_string_lossy(),
80            "-i",
81            id.to_string().as_str(),
82            "-l",
83            len.to_string().as_str(),
84            "-b",
85        ])
86        .output()?;
87    if !out.status.success() {
88        return Err(BlockUtilsError::new(
89            String::from_utf8_lossy(&out.stderr).into_owned(),
90        ));
91    }
92    let stdout: Vec<u8> = out.stdout;
93    Ok(stdout)
94}
95
96// Format an nvme block device
97pub fn format(dev: &Path) -> BlockResult<()> {
98    let out = Command::new("nvme")
99        .args(&["format", &dev.to_string_lossy()])
100        .output()?;
101    if !out.status.success() {
102        return Err(BlockUtilsError::new(
103            String::from_utf8_lossy(&out.stderr).into_owned(),
104        ));
105    }
106    Ok(())
107}
108
109pub fn list_nvme_namespaces(dev: &Path) -> BlockResult<Vec<String>> {
110    let out = Command::new("nvme")
111        .args(&["list-ns", &dev.to_string_lossy(), "-o", "json"])
112        .output()?;
113    if !out.status.success() {
114        return Err(BlockUtilsError::new(
115            String::from_utf8_lossy(&out.stderr).into_owned(),
116        ));
117    }
118    let stdout = String::from_utf8_lossy(&out.stdout);
119    let deserialized: Vec<String> = serde_json::from_str(&stdout)?;
120    Ok(deserialized)
121}
122
123/// List the nvme controllers on the host
124pub fn list_nvme_controllers() -> BlockResult<Vec<String>> {
125    let out = Command::new("nvme-list").args(&["-o", "json"]).output()?;
126    if !out.status.success() {
127        return Err(BlockUtilsError::new(
128            String::from_utf8_lossy(&out.stderr).into_owned(),
129        ));
130    }
131    let stdout = String::from_utf8_lossy(&out.stdout);
132    let deserialized: Vec<String> = serde_json::from_str(&stdout)?;
133    Ok(deserialized)
134}
135
136/// List the nvme devices on the host
137pub fn list_nvme_devices() -> BlockResult<Vec<NvmeDevice>> {
138    let out = Command::new("nvme")
139        .args(&["list", "-o", "json"])
140        .output()?;
141    if !out.status.success() {
142        return Err(BlockUtilsError::new(
143            String::from_utf8_lossy(&out.stderr).into_owned(),
144        ));
145    }
146    let stdout = String::from_utf8_lossy(&out.stdout);
147    let deserialized: NvmeDeviceContainer = serde_json::from_str(&stdout)?;
148    Ok(deserialized.devices)
149}