ceph_async/
ceph_volume.rs

1extern crate serde_json;
2
3use crate::ceph::Rados;
4use crate::cmd;
5use crate::error::{RadosError, RadosResult};
6use crate::json::*;
7use crate::CephVersion;
8use crate::JsonData;
9use std::collections::HashMap;
10use std::path::PathBuf;
11use std::process::Command;
12
13/// ceph_volume is a wrapper around the ceph-volume commands
14/// ceph-volume is a command line tool included in ceph versions Luminous+
15/// it used to deploy and inspect OSDs using logical volumes
16
17#[derive(Serialize, Deserialize, Clone, Debug)]
18pub struct LvmTags {
19    #[serde(rename = "ceph.block_device")]
20    pub block_device: Option<String>,
21    #[serde(rename = "ceph.block_uuid")]
22    pub block_uuid: Option<String>,
23    #[serde(rename = "ceph.cephx_lockbox_secret")]
24    pub cephx_lockbox_secret: Option<String>,
25    #[serde(rename = "ceph.cluster_fsid")]
26    pub cluster_fsid: Option<String>,
27    #[serde(rename = "ceph.cluster_name")]
28    pub cluster_name: Option<String>,
29    #[serde(rename = "ceph.crush_device_class")]
30    pub crush_device_class: Option<String>,
31    #[serde(rename = "ceph.data_device")]
32    pub data_device: Option<String>,
33    #[serde(rename = "ceph.data_uuid")]
34    pub data_uuid: Option<String>,
35    #[serde(rename = "ceph.db_device")]
36    pub db_device: Option<String>,
37    #[serde(rename = "ceph.db_uuid")]
38    pub db_uuid: Option<String>,
39    #[serde(rename = "ceph.encrypted")]
40    pub encrypted: Option<String>,
41    #[serde(rename = "ceph.journal_device")]
42    pub journal_device: Option<String>,
43    #[serde(rename = "ceph.journal_uuid")]
44    pub journal_uuid: Option<String>,
45    #[serde(rename = "ceph.osd_fsid")]
46    pub osd_fsid: Option<String>,
47    #[serde(rename = "ceph.osd_id")]
48    pub osd_id: Option<String>,
49    #[serde(rename = "ceph.type")]
50    pub c_type: Option<String>,
51    #[serde(rename = "ceph.vdo")]
52    pub vdo: Option<String>,
53    #[serde(rename = "ceph.wal_device")]
54    pub wal_device: Option<String>,
55    #[serde(rename = "ceph.wal_uuid")]
56    pub wal_uuid: Option<String>,
57    //Other tags that are not listed here
58    #[serde(flatten)]
59    pub other_tags: Option<HashMap<String, String>>,
60}
61
62#[derive(Serialize, Deserialize, Clone, Debug)]
63pub struct LvmMeta {
64    pub devices: Vec<String>,
65    pub lv_name: String,
66    pub lv_path: String,
67    pub lv_tags: String,
68    pub lv_uuid: String,
69    pub name: String,
70    pub path: String,
71    pub tags: LvmTags,
72    #[serde(rename = "type")]
73    pub lv_type: String,
74    pub vg_name: String,
75    // other metadata not captured through the above attributes
76    #[serde(flatten)]
77    pub other_meta: Option<HashMap<String, String>>,
78}
79#[derive(Serialize, Deserialize, Clone, Debug)]
80#[serde(untagged)]
81pub enum LvmData {
82    Osd(LvmMeta),
83    Journal {
84        path: Option<String>,
85        tags: Option<HashMap<String, String>>,
86        #[serde(rename = "type")]
87        j_type: Option<String>,
88        // other metadata not captured through the above attributes
89        #[serde(flatten)]
90        other_meta: Option<HashMap<String, String>>,
91    },
92    // unknown type of ceph-volume lvm list output
93    Unknown {
94        //unknown metadata not captured through the above attributes
95        #[serde(flatten)]
96        unknown_meta: Option<HashMap<String, String>>,
97    },
98}
99
100#[derive(Serialize, Deserialize, Clone, Debug)]
101pub struct Lvm {
102    #[serde(flatten)]
103    pub metadata: LvmData,
104}
105
106// Check the cluster version. If version < Luminous, error out
107fn check_version(cluster_handle: &Rados) -> RadosResult<()> {
108    let version: CephVersion = cmd::version(cluster_handle)?.parse()?;
109    if version < CephVersion::Luminous {
110        return Err(RadosError::MinVersion(CephVersion::Luminous, version));
111    }
112    Ok(())
113}
114
115/// List all LVM devices (logical and physical) that may be associated with a
116/// ceph cluster assuming they contain enough metadata to allow for discovery
117/// Does not show devices that aren't associated with Ceph.
118/// NOTE: This requires Ceph version Luminous+
119pub fn ceph_volume_list(cluster_handle: &Rados) -> RadosResult<HashMap<String, Vec<Lvm>>> {
120    check_version(cluster_handle)?;
121    let output = Command::new("ceph-volume")
122        .args(&["lvm", "list", "--format=json"])
123        .output()?;
124    let lvms: HashMap<String, Vec<Lvm>> =
125        serde_json::from_str(&String::from_utf8_lossy(&output.stdout))?;
126    Ok(lvms)
127}
128
129/// Scan and capture important details on deployed OSDs
130/// Input path, if given, must be the path to the ceph data partition,
131/// so /var/lib/ceph/osd/ceph-{osd_id}
132pub fn ceph_volume_scan(
133    cluster_handle: &Rados,
134    osd_path: Option<PathBuf>,
135) -> RadosResult<JsonData> {
136    check_version(cluster_handle)?;
137    let output;
138    if let Some(p) = osd_path {
139        let path = format!("{}", p.display());
140        output = Command::new("ceph-volume")
141            .args(&["simple", "scan", "--stdout", &path])
142            .output()?;
143    } else {
144        output = Command::new("ceph-volume")
145            .args(&["simple", "scan", "--stdout"])
146            .output()?;
147    }
148    let json = String::from_utf8_lossy(&output.stdout);
149    let index: usize = match json.find("{") {
150        Some(i) => i,
151        None => 0,
152    };
153    // Skip stderr's.  The last output is Json
154    let json = json.split_at(index);
155    match json_data(&json.1) {
156        Some(jsondata) => Ok(jsondata),
157        _ => Err(RadosError::new("JSON data not found.".to_string())),
158    }
159}