device_types/
devices.rs

1// Copyright (c) 2020 DDN. All rights reserved.
2// Use of this source code is governed by a MIT-style
3// license that can be found in the LICENSE file.
4
5use crate::{mount, DevicePath};
6use im::{ordset, OrdSet};
7use std::path::PathBuf;
8
9type Children = OrdSet<Device>;
10pub type Paths = OrdSet<DevicePath>;
11
12#[derive(
13    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
14)]
15pub struct Root {
16    pub children: Children,
17}
18
19impl Default for Root {
20    fn default() -> Self {
21        Self {
22            children: ordset![],
23        }
24    }
25}
26
27#[derive(
28    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
29)]
30pub struct ScsiDevice {
31    pub serial: Option<String>,
32    pub scsi80: Option<String>,
33    pub major: String,
34    pub minor: String,
35    pub devpath: PathBuf,
36    pub size: u64,
37    pub filesystem_type: Option<String>,
38    pub fs_uuid: Option<String>,
39    pub fs_label: Option<String>,
40    pub paths: Paths,
41    pub mount: Option<mount::Mount>,
42    pub children: Children,
43}
44
45#[derive(
46    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
47)]
48pub struct Partition {
49    pub serial: Option<String>,
50    pub scsi80: Option<String>,
51    pub partition_number: u64,
52    pub size: u64,
53    pub major: String,
54    pub minor: String,
55    pub devpath: PathBuf,
56    pub filesystem_type: Option<String>,
57    pub fs_uuid: Option<String>,
58    pub fs_label: Option<String>,
59    pub paths: Paths,
60    pub mount: Option<mount::Mount>,
61    pub children: Children,
62}
63
64#[derive(
65    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
66)]
67pub struct MdRaid {
68    pub size: u64,
69    pub major: String,
70    pub minor: String,
71    pub filesystem_type: Option<String>,
72    pub fs_uuid: Option<String>,
73    pub fs_label: Option<String>,
74    pub paths: Paths,
75    pub mount: Option<mount::Mount>,
76    pub uuid: String,
77    pub children: Children,
78}
79
80#[derive(
81    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
82)]
83pub struct Mpath {
84    pub devpath: PathBuf,
85    pub serial: Option<String>,
86    pub scsi80: Option<String>,
87    pub dm_name: String,
88    pub size: u64,
89    pub major: String,
90    pub minor: String,
91    pub filesystem_type: Option<String>,
92    pub fs_uuid: Option<String>,
93    pub fs_label: Option<String>,
94    pub paths: Paths,
95    pub children: Children,
96    pub mount: Option<mount::Mount>,
97}
98
99#[derive(
100    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
101)]
102pub struct VolumeGroup {
103    pub name: String,
104    pub uuid: String,
105    pub size: u64,
106    pub children: Children,
107}
108
109#[derive(
110    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
111)]
112pub struct LogicalVolume {
113    pub name: String,
114    pub uuid: String,
115    pub major: String,
116    pub minor: String,
117    pub size: u64,
118    pub children: Children,
119    pub devpath: PathBuf,
120    pub paths: Paths,
121    pub filesystem_type: Option<String>,
122    pub fs_uuid: Option<String>,
123    pub fs_label: Option<String>,
124    pub mount: Option<mount::Mount>,
125}
126
127#[derive(
128    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
129)]
130pub struct Zpool {
131    pub guid: u64,
132    pub name: String,
133    pub health: String,
134    pub state: String,
135    pub size: u64,
136    pub vdev: libzfs_types::VDev,
137    pub props: Vec<libzfs_types::ZProp>,
138    pub children: Children,
139    pub mount: Option<mount::Mount>,
140}
141
142#[derive(
143    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
144)]
145pub struct Dataset {
146    pub guid: u64,
147    pub name: String,
148    pub kind: String,
149    pub props: Vec<libzfs_types::ZProp>,
150    pub mount: Option<mount::Mount>,
151}
152
153#[derive(
154    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
155)]
156pub enum Device {
157    Root(Root),
158    ScsiDevice(ScsiDevice),
159    Partition(Partition),
160    MdRaid(MdRaid),
161    Mpath(Mpath),
162    VolumeGroup(VolumeGroup),
163    LogicalVolume(LogicalVolume),
164    Zpool(Zpool),
165    Dataset(Dataset),
166}
167
168#[derive(Debug, serde::Serialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
169pub struct DeviceId(pub String);
170
171impl Device {
172    pub fn find_device_by_devpath(&self, dev_path: &DevicePath) -> Option<&Device> {
173        match self {
174            Self::Root(x) => x
175                .children
176                .iter()
177                .find_map(|c| c.find_device_by_devpath(dev_path)),
178            Self::ScsiDevice(x) => {
179                if x.paths.contains(dev_path) {
180                    return Some(self);
181                }
182
183                x.children
184                    .iter()
185                    .find_map(|c| c.find_device_by_devpath(dev_path))
186            }
187            Self::Partition(x) => {
188                if x.paths.contains(dev_path) {
189                    return Some(self);
190                }
191
192                x.children
193                    .iter()
194                    .find_map(|c| c.find_device_by_devpath(dev_path))
195            }
196            Self::MdRaid(x) => {
197                if x.paths.contains(dev_path) {
198                    return Some(self);
199                }
200
201                x.children
202                    .iter()
203                    .find_map(|c| c.find_device_by_devpath(dev_path))
204            }
205            Self::Mpath(x) => {
206                if x.paths.contains(dev_path) {
207                    return Some(self);
208                }
209
210                x.children
211                    .iter()
212                    .find_map(|c| c.find_device_by_devpath(dev_path))
213            }
214            Self::VolumeGroup(x) => x
215                .children
216                .iter()
217                .find_map(|c| c.find_device_by_devpath(dev_path)),
218            Self::LogicalVolume(x) => {
219                if x.paths.contains(dev_path) {
220                    return Some(self);
221                }
222
223                x.children
224                    .iter()
225                    .find_map(|c| c.find_device_by_devpath(dev_path))
226            }
227            Self::Zpool(x) => x
228                .children
229                .iter()
230                .find_map(|c| c.find_device_by_devpath(dev_path)),
231            Self::Dataset(x) => {
232                if x.name == dev_path.0.to_string_lossy() {
233                    Some(self)
234                } else {
235                    None
236                }
237            }
238        }
239    }
240    pub fn get_fs_uuid(&self) -> Option<&str> {
241        match self {
242            Self::Root(_) => None,
243            Self::ScsiDevice(x) => x.fs_uuid.as_deref(),
244            Self::Partition(x) => x.fs_uuid.as_deref(),
245            Self::MdRaid(x) => x.fs_uuid.as_deref(),
246            Self::Mpath(x) => x.fs_uuid.as_deref(),
247            Self::VolumeGroup(_) => None,
248            Self::LogicalVolume(x) => x.fs_uuid.as_deref(),
249            Self::Zpool(_) => None,
250            Self::Dataset(x) => {
251                let guid = x.props.iter().find(|x| x.name == "guid")?;
252
253                Some(&guid.value)
254            }
255        }
256    }
257    pub fn find_device_by_id(&self, id: &DeviceId) -> Option<&Device> {
258        match self {
259            Self::Root(x) => x.children.iter().find_map(|c| c.find_device_by_id(id)),
260            Self::ScsiDevice(x) => {
261                if self.get_id().as_ref()? == id {
262                    return Some(self);
263                }
264
265                x.children.iter().find_map(|c| c.find_device_by_id(id))
266            }
267            Self::Partition(x) => {
268                if self.get_id().as_ref()? == id {
269                    return Some(self);
270                }
271
272                x.children.iter().find_map(|c| c.find_device_by_id(id))
273            }
274            Self::MdRaid(x) => {
275                if self.get_id().as_ref()? == id {
276                    return Some(self);
277                }
278
279                x.children.iter().find_map(|c| c.find_device_by_id(id))
280            }
281            Self::Mpath(x) => {
282                if self.get_id().as_ref()? == id {
283                    return Some(self);
284                }
285
286                x.children.iter().find_map(|c| c.find_device_by_id(id))
287            }
288            Self::VolumeGroup(x) => {
289                if self.get_id().as_ref()? == id {
290                    return Some(self);
291                }
292
293                x.children.iter().find_map(|c| c.find_device_by_id(id))
294            }
295            Self::LogicalVolume(x) => {
296                if self.get_id().as_ref()? == id {
297                    return Some(self);
298                }
299
300                x.children.iter().find_map(|c| c.find_device_by_id(id))
301            }
302            Self::Zpool(x) => {
303                if self.get_id().as_ref()? == id {
304                    return Some(self);
305                }
306
307                x.children.iter().find_map(|c| c.find_device_by_id(id))
308            }
309            Self::Dataset(_) => {
310                if self.get_id().as_ref()? == id {
311                    Some(self)
312                } else {
313                    None
314                }
315            }
316        }
317    }
318    pub fn get_id(&self) -> Option<DeviceId> {
319        match self {
320            Self::Root(_) => Some(DeviceId("root".into())),
321            Self::ScsiDevice(x) => Some(DeviceId(format!("scsi_{}", x.serial.as_ref()?))),
322            Self::Partition(x) => Some(DeviceId(format!(
323                "partition{}_{}",
324                x.partition_number,
325                x.serial.as_ref()?
326            ))),
327            Self::MdRaid(x) => Some(DeviceId(format!("mdraid_{}", x.uuid))),
328            Self::Mpath(x) => Some(DeviceId(format!("mpath_{}", x.serial.as_ref()?))),
329            Self::VolumeGroup(x) => Some(DeviceId(format!("vg_{}", x.uuid))),
330            Self::LogicalVolume(x) => Some(DeviceId(format!("lv_{}", x.uuid))),
331            Self::Zpool(x) => Some(DeviceId(format!("zpool_{}", x.guid))),
332            Self::Dataset(x) => Some(DeviceId(format!("dataset_{}", x.guid))),
333        }
334    }
335    pub fn children(&self) -> Option<&OrdSet<Device>> {
336        match self {
337            Self::Root(x) => Some(&x.children),
338            Self::ScsiDevice(x) => Some(&x.children),
339            Self::Partition(x) => Some(&x.children),
340            Self::MdRaid(x) => Some(&x.children),
341            Self::Mpath(x) => Some(&x.children),
342            Self::VolumeGroup(x) => Some(&x.children),
343            Self::LogicalVolume(x) => Some(&x.children),
344            Self::Zpool(x) => Some(&x.children),
345            Self::Dataset(_) => None,
346        }
347    }
348}