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