]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/node/disks/directory.rs
avoid compiler warning
[proxmox-backup.git] / src / api2 / node / disks / directory.rs
CommitLineData
d4f2397d 1use anyhow::{Error};
fbbcd858 2use serde_json::json;
d4f2397d
DM
3use ::serde::{Deserialize, Serialize};
4
5use proxmox::api::{api, Permission, RpcEnvironment, RpcEnvironmentType};
6use proxmox::api::section_config::SectionConfigData;
7use proxmox::api::router::Router;
8
9use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
10use crate::tools::disks::{
11 DiskManage, FileSystemType,
12 create_file_system, create_single_linux_partition, get_fs_uuid,
13};
14use crate::tools::systemd::{self, types::*};
15
16use crate::server::WorkerTask;
17
18use crate::api2::types::*;
19
20#[api(
21 properties: {
22 "filesystem": {
23 type: FileSystemType,
24 optional: true,
25 },
26 },
27)]
28#[derive(Debug, Serialize, Deserialize)]
29#[serde(rename_all="kebab-case")]
30/// Datastore mount info.
31pub struct DatastoreMountInfo {
32 /// The path of the mount unit.
33 pub unitfile: String,
34 /// The mount path.
35 pub path: String,
36 /// The mounted device.
37 pub device: String,
38 /// File system type
39 pub filesystem: Option<String>,
40 /// Mount options
41 pub options: Option<String>,
42}
43
44#[api(
45 protected: true,
46 input: {
47 properties: {
48 node: {
49 schema: NODE_SCHEMA,
50 },
51 }
52 },
53 returns: {
54 description: "List of systemd datastore mount units.",
55 type: Array,
56 items: {
57 type: DatastoreMountInfo,
58 },
59 },
60 access: {
61 permission: &Permission::Privilege(&["system", "disks"], PRIV_SYS_AUDIT, false),
62 },
63)]
64/// List systemd datastore mount units.
65fn list_datastore_mounts() -> Result<Vec<DatastoreMountInfo>, Error> {
66
67 lazy_static::lazy_static! {
68 static ref MOUNT_NAME_REGEX: regex::Regex = regex::Regex::new(r"^mnt-datastore-(.+)\.mount$").unwrap();
69 }
70
71 let mut list = Vec::new();
72
73 let basedir = "/etc/systemd/system";
74 for item in crate::tools::fs::scan_subdir(libc::AT_FDCWD, basedir, &MOUNT_NAME_REGEX)? {
75 let item = item?;
76 let name = item.file_name().to_string_lossy().to_string();
77
78 let unitfile = format!("{}/{}", basedir, name);
79 let config = systemd::config::parse_systemd_mount(&unitfile)?;
80 let data: SystemdMountSection = config.lookup("Mount", "Mount")?;
81
82 list.push(DatastoreMountInfo {
83 unitfile,
84 device: data.What,
85 path: data.Where,
86 filesystem: data.Type,
87 options: data.Options,
88 });
89 }
90
91 Ok(list)
92}
93
94#[api(
95 protected: true,
96 input: {
97 properties: {
98 node: {
99 schema: NODE_SCHEMA,
100 },
101 name: {
102 schema: DATASTORE_SCHEMA,
103 },
104 disk: {
105 schema: BLOCKDEVICE_NAME_SCHEMA,
106 },
107 "add-datastore": {
108 description: "Configure a datastore using the directory.",
109 type: bool,
110 optional: true,
111 },
112 filesystem: {
113 type: FileSystemType,
114 optional: true,
115 },
116 }
117 },
118 returns: {
119 schema: UPID_SCHEMA,
120 },
121 access: {
122 permission: &Permission::Privilege(&["system", "disks"], PRIV_SYS_MODIFY, false),
123 },
124)]
125/// Create a Filesystem on an unused disk. Will be mounted under '/mnt/datastore/<name>'.".
126fn create_datastore_disk(
127 name: String,
128 disk: String,
129 add_datastore: Option<bool>,
130 filesystem: Option<FileSystemType>,
131 rpcenv: &mut dyn RpcEnvironment,
132) -> Result<String, Error> {
133
134 let to_stdout = if rpcenv.env_type() == RpcEnvironmentType::CLI { true } else { false };
135
136 let username = rpcenv.get_user().unwrap();
137
138 let upid_str = WorkerTask::new_thread(
139 "dircreate", Some(name.clone()), &username.clone(), to_stdout, move |worker|
140 {
141 worker.log(format!("create datastore '{}' on disk {}", name, disk));
142
143 let add_datastore = add_datastore.unwrap_or(false);
144 let filesystem = filesystem.unwrap_or(FileSystemType::Ext4);
145
146 let manager = DiskManage::new();
147
148 let disk = manager.clone().disk_by_name(&disk)?;
149
150 let partition = create_single_linux_partition(&disk)?;
151 create_file_system(&partition, filesystem)?;
152
153 let uuid = get_fs_uuid(&partition)?;
154 let uuid_path = format!("/dev/disk/by-uuid/{}", uuid);
155
fbbcd858 156 let (mount_unit_name, mount_point) = create_datastore_mount_unit(&name, filesystem, &uuid_path)?;
d4f2397d
DM
157
158 systemd::reload_daemon()?;
159 systemd::enable_unit(&mount_unit_name)?;
160 systemd::start_unit(&mount_unit_name)?;
161
fbbcd858
DM
162 if add_datastore {
163 crate::api2::config::datastore::create_datastore(json!({ "name": name, "path": mount_point }))?
164 }
165
d4f2397d
DM
166 Ok(())
167 })?;
168
169 Ok(upid_str)
170}
171
172pub const ROUTER: Router = Router::new()
173 .get(&API_METHOD_LIST_DATASTORE_MOUNTS)
174 .post(&API_METHOD_CREATE_DATASTORE_DISK);
175
176
177fn create_datastore_mount_unit(
178 datastore_name: &str,
179 fs_type: FileSystemType,
180 what: &str,
fbbcd858 181) -> Result<(String, String), Error> {
d4f2397d
DM
182
183 let mount_point = format!("/mnt/datastore/{}", datastore_name);
184 let mut mount_unit_name = systemd::escape_unit(&mount_point, true);
185 mount_unit_name.push_str(".mount");
186
187 let mount_unit_path = format!("/etc/systemd/system/{}", mount_unit_name);
188
189 let unit = SystemdUnitSection {
190 Description: format!("Mount datatstore '{}' under '{}'", datastore_name, mount_point),
191 ..Default::default()
192 };
193
194 let install = SystemdInstallSection {
195 WantedBy: Some(vec!["multi-user.target".to_string()]),
196 ..Default::default()
197 };
198
199 let mount = SystemdMountSection {
200 What: what.to_string(),
fbbcd858 201 Where: mount_point.clone(),
d4f2397d
DM
202 Type: Some(fs_type.to_string()),
203 Options: Some(String::from("defaults")),
204 ..Default::default()
205 };
206
207 let mut config = SectionConfigData::new();
208 config.set_data("Unit", "Unit", unit)?;
209 config.set_data("Install", "Install", install)?;
210 config.set_data("Mount", "Mount", mount)?;
211
212 systemd::config::save_systemd_mount(&mount_unit_path, &config)?;
213
fbbcd858 214 Ok((mount_unit_name, mount_point))
d4f2397d 215}