3 use ::serde
::{Deserialize, Serialize}
;
5 use proxmox
::api
::{api, Permission, RpcEnvironment, RpcEnvironmentType}
;
6 use proxmox
::api
::section_config
::SectionConfigData
;
7 use proxmox
::api
::router
::Router
;
9 use crate::config
::acl
::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}
;
10 use crate::tools
::disks
::{
11 DiskManage
, FileSystemType
,
12 create_file_system
, create_single_linux_partition
, get_fs_uuid
,
14 use crate::tools
::systemd
::{self, types::*}
;
16 use crate::server
::WorkerTask
;
18 use crate::api2
::types
::*;
28 #[derive(Debug, Serialize, Deserialize)]
29 #[serde(rename_all="kebab-case")]
30 /// Datastore mount info.
31 pub struct DatastoreMountInfo
{
32 /// The path of the mount unit.
36 /// The mounted device.
39 pub filesystem
: Option
<String
>,
41 pub options
: Option
<String
>,
54 description
: "List of systemd datastore mount units.",
57 type: DatastoreMountInfo
,
61 permission
: &Permission
::Privilege(&["system", "disks"], PRIV_SYS_AUDIT
, false),
64 /// List systemd datastore mount units.
65 fn list_datastore_mounts() -> Result
<Vec
<DatastoreMountInfo
>, Error
> {
67 lazy_static
::lazy_static
! {
68 static ref MOUNT_NAME_REGEX
: regex
::Regex
= regex
::Regex
::new(r
"^mnt-datastore-(.+)\.mount$").unwrap();
71 let mut list
= Vec
::new();
73 let basedir
= "/etc/systemd/system";
74 for item
in crate::tools
::fs
::scan_subdir(libc
::AT_FDCWD
, basedir
, &MOUNT_NAME_REGEX
)?
{
76 let name
= item
.file_name().to_string_lossy().to_string();
78 let unitfile
= format
!("{}/{}", basedir
, name
);
79 let config
= systemd
::config
::parse_systemd_mount(&unitfile
)?
;
80 let data
: SystemdMountSection
= config
.lookup("Mount", "Mount")?
;
82 list
.push(DatastoreMountInfo
{
86 filesystem
: data
.Type
,
87 options
: data
.Options
,
102 schema
: DATASTORE_SCHEMA
,
105 schema
: BLOCKDEVICE_NAME_SCHEMA
,
108 description
: "Configure a datastore using the directory.",
113 type: FileSystemType
,
122 permission
: &Permission
::Privilege(&["system", "disks"], PRIV_SYS_MODIFY
, false),
125 /// Create a Filesystem on an unused disk. Will be mounted under '/mnt/datastore/<name>'.".
126 fn create_datastore_disk(
129 add_datastore
: Option
<bool
>,
130 filesystem
: Option
<FileSystemType
>,
131 rpcenv
: &mut dyn RpcEnvironment
,
132 ) -> Result
<String
, Error
> {
134 let to_stdout
= if rpcenv
.env_type() == RpcEnvironmentType
::CLI { true }
else { false }
;
136 let username
= rpcenv
.get_user().unwrap();
138 let upid_str
= WorkerTask
::new_thread(
139 "dircreate", Some(name
.clone()), &username
.clone(), to_stdout
, move |worker
|
141 worker
.log(format
!("create datastore '{}' on disk {}", name
, disk
));
143 let add_datastore
= add_datastore
.unwrap_or(false);
144 let filesystem
= filesystem
.unwrap_or(FileSystemType
::Ext4
);
146 let manager
= DiskManage
::new();
148 let disk
= manager
.clone().disk_by_name(&disk
)?
;
150 let partition
= create_single_linux_partition(&disk
)?
;
151 create_file_system(&partition
, filesystem
)?
;
153 let uuid
= get_fs_uuid(&partition
)?
;
154 let uuid_path
= format
!("/dev/disk/by-uuid/{}", uuid
);
156 let (mount_unit_name
, mount_point
) = create_datastore_mount_unit(&name
, filesystem
, &uuid_path
)?
;
158 systemd
::reload_daemon()?
;
159 systemd
::enable_unit(&mount_unit_name
)?
;
160 systemd
::start_unit(&mount_unit_name
)?
;
163 crate::api2
::config
::datastore
::create_datastore(json
!({ "name": name, "path": mount_point }
))?
172 pub const ROUTER
: Router
= Router
::new()
173 .get(&API_METHOD_LIST_DATASTORE_MOUNTS
)
174 .post(&API_METHOD_CREATE_DATASTORE_DISK
);
177 fn create_datastore_mount_unit(
178 datastore_name
: &str,
179 fs_type
: FileSystemType
,
181 ) -> Result
<(String
, String
), Error
> {
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");
187 let mount_unit_path
= format
!("/etc/systemd/system/{}", mount_unit_name
);
189 let unit
= SystemdUnitSection
{
190 Description
: format
!("Mount datatstore '{}' under '{}'", datastore_name
, mount_point
),
194 let install
= SystemdInstallSection
{
195 WantedBy
: Some(vec
!["multi-user.target".to_string()]),
199 let mount
= SystemdMountSection
{
200 What
: what
.to_string(),
201 Where
: mount_point
.clone(),
202 Type
: Some(fs_type
.to_string()),
203 Options
: Some(String
::from("defaults")),
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
)?
;
212 systemd
::config
::save_systemd_mount(&mount_unit_path
, &config
)?
;
214 Ok((mount_unit_name
, mount_point
))