2 use ::serde
::{Deserialize, Serialize}
;
4 use proxmox
::api
::{api, Permission, RpcEnvironment, RpcEnvironmentType}
;
5 use proxmox
::api
::section_config
::SectionConfigData
;
6 use proxmox
::api
::router
::Router
;
8 use crate::config
::acl
::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}
;
9 use crate::tools
::disks
::{
10 DiskManage
, FileSystemType
,
11 create_file_system
, create_single_linux_partition
, get_fs_uuid
,
13 use crate::tools
::systemd
::{self, types::*}
;
15 use crate::server
::WorkerTask
;
17 use crate::api2
::types
::*;
27 #[derive(Debug, Serialize, Deserialize)]
28 #[serde(rename_all="kebab-case")]
29 /// Datastore mount info.
30 pub struct DatastoreMountInfo
{
31 /// The path of the mount unit.
35 /// The mounted device.
38 pub filesystem
: Option
<String
>,
40 pub options
: Option
<String
>,
53 description
: "List of systemd datastore mount units.",
56 type: DatastoreMountInfo
,
60 permission
: &Permission
::Privilege(&["system", "disks"], PRIV_SYS_AUDIT
, false),
63 /// List systemd datastore mount units.
64 fn list_datastore_mounts() -> Result
<Vec
<DatastoreMountInfo
>, Error
> {
66 lazy_static
::lazy_static
! {
67 static ref MOUNT_NAME_REGEX
: regex
::Regex
= regex
::Regex
::new(r
"^mnt-datastore-(.+)\.mount$").unwrap();
70 let mut list
= Vec
::new();
72 let basedir
= "/etc/systemd/system";
73 for item
in crate::tools
::fs
::scan_subdir(libc
::AT_FDCWD
, basedir
, &MOUNT_NAME_REGEX
)?
{
75 let name
= item
.file_name().to_string_lossy().to_string();
77 let unitfile
= format
!("{}/{}", basedir
, name
);
78 let config
= systemd
::config
::parse_systemd_mount(&unitfile
)?
;
79 let data
: SystemdMountSection
= config
.lookup("Mount", "Mount")?
;
81 list
.push(DatastoreMountInfo
{
85 filesystem
: data
.Type
,
86 options
: data
.Options
,
101 schema
: DATASTORE_SCHEMA
,
104 schema
: BLOCKDEVICE_NAME_SCHEMA
,
107 description
: "Configure a datastore using the directory.",
112 type: FileSystemType
,
121 permission
: &Permission
::Privilege(&["system", "disks"], PRIV_SYS_MODIFY
, false),
124 /// Create a Filesystem on an unused disk. Will be mounted under '/mnt/datastore/<name>'.".
125 fn create_datastore_disk(
128 add_datastore
: Option
<bool
>,
129 filesystem
: Option
<FileSystemType
>,
130 rpcenv
: &mut dyn RpcEnvironment
,
131 ) -> Result
<String
, Error
> {
133 let to_stdout
= if rpcenv
.env_type() == RpcEnvironmentType
::CLI { true }
else { false }
;
135 let username
= rpcenv
.get_user().unwrap();
137 let upid_str
= WorkerTask
::new_thread(
138 "dircreate", Some(name
.clone()), &username
.clone(), to_stdout
, move |worker
|
140 worker
.log(format
!("create datastore '{}' on disk {}", name
, disk
));
142 let add_datastore
= add_datastore
.unwrap_or(false);
143 let filesystem
= filesystem
.unwrap_or(FileSystemType
::Ext4
);
145 let manager
= DiskManage
::new();
147 let disk
= manager
.clone().disk_by_name(&disk
)?
;
149 let partition
= create_single_linux_partition(&disk
)?
;
150 create_file_system(&partition
, filesystem
)?
;
152 let uuid
= get_fs_uuid(&partition
)?
;
153 let uuid_path
= format
!("/dev/disk/by-uuid/{}", uuid
);
155 let mount_unit_name
= create_datastore_mount_unit(&name
, filesystem
, &uuid_path
)?
;
158 unimplemented
!(); // fixme
161 systemd
::reload_daemon()?
;
162 systemd
::enable_unit(&mount_unit_name
)?
;
163 systemd
::start_unit(&mount_unit_name
)?
;
171 pub const ROUTER
: Router
= Router
::new()
172 .get(&API_METHOD_LIST_DATASTORE_MOUNTS
)
173 .post(&API_METHOD_CREATE_DATASTORE_DISK
);
176 fn create_datastore_mount_unit(
177 datastore_name
: &str,
178 fs_type
: FileSystemType
,
180 ) -> Result
<String
, Error
> {
182 let mount_point
= format
!("/mnt/datastore/{}", datastore_name
);
183 let mut mount_unit_name
= systemd
::escape_unit(&mount_point
, true);
184 mount_unit_name
.push_str(".mount");
186 let mount_unit_path
= format
!("/etc/systemd/system/{}", mount_unit_name
);
188 let unit
= SystemdUnitSection
{
189 Description
: format
!("Mount datatstore '{}' under '{}'", datastore_name
, mount_point
),
193 let install
= SystemdInstallSection
{
194 WantedBy
: Some(vec
!["multi-user.target".to_string()]),
198 let mount
= SystemdMountSection
{
199 What
: what
.to_string(),
201 Type
: Some(fs_type
.to_string()),
202 Options
: Some(String
::from("defaults")),
206 let mut config
= SectionConfigData
::new();
207 config
.set_data("Unit", "Unit", unit
)?
;
208 config
.set_data("Install", "Install", install
)?
;
209 config
.set_data("Mount", "Mount", mount
)?
;
211 systemd
::config
::save_systemd_mount(&mount_unit_path
, &config
)?
;