]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/status.rs
config: acme: use latest proxmox_sys::fs::ensure_dir_exists
[proxmox-backup.git] / src / api2 / status.rs
CommitLineData
bf78f708
DM
1//! Datastote status
2
762f7d15
DM
3use anyhow::Error;
4use serde_json::Value;
bda48e04 5
6ef1b649 6use proxmox_router::list_subdirs_api_method;
dc7a5b34
TL
7use proxmox_router::{ApiMethod, Permission, Router, RpcEnvironment, SubdirMap};
8use proxmox_schema::api;
20b3094b 9
8cc3760e 10use pbs_api_types::{
dc7a5b34
TL
11 Authid, DataStoreStatusListItem, Operation, RRDMode, RRDTimeFrame, PRIV_DATASTORE_AUDIT,
12 PRIV_DATASTORE_BACKUP,
20b3094b 13};
09340f28 14
6d5d305d 15use pbs_config::CachedUserInfo;
dc7a5b34 16use pbs_datastore::DataStore;
bda48e04 17
fae4f6c5 18use crate::rrd_cache::extract_rrd_data;
dc7a5b34 19use crate::tools::statistics::linear_regression;
bda48e04 20
84de1012
TL
21use crate::backup::can_access_any_namespace;
22
bda48e04
DC
23#[api(
24 returns: {
25 description: "Lists the Status of the Datastores.",
26 type: Array,
27 items: {
762f7d15 28 type: DataStoreStatusListItem,
bda48e04
DC
29 },
30 },
ecd55041
FG
31 access: {
32 permission: &Permission::Anybody,
33 },
bda48e04
DC
34)]
35/// List Datastore usages and estimates
143ac7e6 36pub async fn datastore_status(
bda48e04
DC
37 _param: Value,
38 _info: &ApiMethod,
39 rpcenv: &mut dyn RpcEnvironment,
dc7a5b34 40) -> Result<Vec<DataStoreStatusListItem>, Error> {
e7d4be9d 41 let (config, _digest) = pbs_config::datastore::config()?;
bda48e04 42
e6dc35ac 43 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
bda48e04
DC
44 let user_info = CachedUserInfo::new()?;
45
46 let mut list = Vec::new();
47
48 for (store, (_, _)) in &config.sections {
9a37bd6c 49 let user_privs = user_info.lookup_privs(&auth_id, &["datastore", store]);
dc7a5b34 50 let allowed = (user_privs & (PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP)) != 0;
bda48e04 51 if !allowed {
e1db0670 52 if let Ok(datastore) = DataStore::lookup_datastore(store, Some(Operation::Lookup)) {
84de1012
TL
53 if can_access_any_namespace(datastore, &auth_id, &user_info) {
54 list.push(DataStoreStatusListItem::empty(store, None));
55 }
56 }
bda48e04
DC
57 continue;
58 }
59
e1db0670 60 let datastore = match DataStore::lookup_datastore(store, Some(Operation::Read)) {
64591e73
TL
61 Ok(datastore) => datastore,
62 Err(err) => {
997c96d6 63 list.push(DataStoreStatusListItem::empty(store, Some(err.to_string())));
64591e73
TL
64 continue;
65 }
66 };
143ac7e6 67 let status = crate::tools::fs::fs_info(datastore.base_path()).await?;
bda48e04 68
762f7d15
DM
69 let mut entry = DataStoreStatusListItem {
70 store: store.clone(),
cbb478fa
GG
71 total: Some(status.total),
72 used: Some(status.used),
73 avail: Some(status.available),
762f7d15
DM
74 history: None,
75 history_start: None,
76 history_delta: None,
77 estimated_full_date: None,
78 error: None,
8550de74 79 gc_status: Some(datastore.last_gc_status()),
762f7d15 80 };
bda48e04
DC
81
82 let rrd_dir = format!("datastore/{}", store);
fa49d0fd 83
dc7a5b34
TL
84 let get_rrd =
85 |what: &str| extract_rrd_data(&rrd_dir, what, RRDTimeFrame::Month, RRDMode::Average);
bda48e04 86
1198f8d4
DM
87 let total_res = get_rrd("total")?;
88 let used_res = get_rrd("used")?;
f362f8f0 89 let avail_res = get_rrd("available")?;
ec8f0424 90
f362f8f0 91 if let Some(((total_entry, used), avail)) = total_res.zip(used_res).zip(avail_res) {
b92cad09
FG
92 let mut usage_list: Vec<f64> = Vec::new();
93 let mut time_list: Vec<u64> = Vec::new();
94 let mut history = Vec::new();
95
f362f8f0
DT
96 for (idx, used) in used.data.iter().enumerate() {
97 let used = match used {
98 Some(used) => used,
99 _ => {
100 history.push(None);
101 continue;
102 }
103 };
104
6d1f8b4b
WB
105 let total = if let Some(avail) = avail.get(idx) {
106 avail + used
107 } else if let Some(total) = total_entry.get(idx) {
108 total
b92cad09 109 } else {
f362f8f0
DT
110 history.push(None);
111 continue;
b92cad09
FG
112 };
113
f362f8f0
DT
114 let usage = used / total;
115 time_list.push(total_entry.start + (idx as u64) * total_entry.resolution);
116 usage_list.push(usage);
117 history.push(Some(usage));
b92cad09
FG
118 }
119
f362f8f0
DT
120 entry.history_start = Some(total_entry.start);
121 entry.history_delta = Some(total_entry.resolution);
762f7d15 122 entry.history = Some(history);
b92cad09
FG
123
124 // we skip the calculation for datastores with not enough data
125 if usage_list.len() >= 7 {
762f7d15
DM
126 entry.estimated_full_date = match linear_regression(&time_list, &usage_list) {
127 Some((a, b)) if b != 0.0 => Some(((1.0 - a) / b).floor() as i64),
39ffb75d 128 Some((_, b)) if b == 0.0 => Some(0), // infinite estimate, set to past for gui to detect
762f7d15 129 _ => None,
90761f0f 130 };
b92cad09 131 }
bda48e04
DC
132 }
133
134 list.push(entry);
135 }
136
e1db0670 137 Ok(list)
bda48e04
DC
138}
139
dc7a5b34
TL
140const SUBDIRS: SubdirMap = &[(
141 "datastore-usage",
142 &Router::new().get(&API_METHOD_DATASTORE_STATUS),
143)];
bda48e04
DC
144
145pub const ROUTER: Router = Router::new()
146 .get(&list_subdirs_api_method!(SUBDIRS))
147 .subdirs(SUBDIRS);