]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/status.rs
move src/backup/datastore.rs into pbs_datastore crate
[proxmox-backup.git] / src / api2 / status.rs
1 //! Datastote status
2
3 use proxmox::list_subdirs_api_method;
4
5 use anyhow::{Error};
6 use serde_json::{json, Value};
7
8 use proxmox::api::{
9 api,
10 ApiMethod,
11 Permission,
12 Router,
13 RpcEnvironment,
14 SubdirMap,
15 };
16
17 use pbs_api_types::{
18 DATASTORE_SCHEMA, RRDMode, RRDTimeFrameResolution, Authid,
19 PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP,
20 };
21 use pbs_datastore::DataStore;
22 use pbs_config::CachedUserInfo;
23
24 use crate::tools::statistics::{linear_regression};
25
26 #[api(
27 returns: {
28 description: "Lists the Status of the Datastores.",
29 type: Array,
30 items: {
31 description: "Status of a Datastore",
32 type: Object,
33 properties: {
34 store: {
35 schema: DATASTORE_SCHEMA,
36 },
37 total: {
38 type: Integer,
39 description: "The Size of the underlying storage in bytes",
40 },
41 used: {
42 type: Integer,
43 description: "The used bytes of the underlying storage",
44 },
45 avail: {
46 type: Integer,
47 description: "The available bytes of the underlying storage",
48 },
49 history: {
50 type: Array,
51 optional: true,
52 description: "A list of usages of the past (last Month).",
53 items: {
54 type: Number,
55 description: "The usage of a time in the past. Either null or between 0.0 and 1.0.",
56 }
57 },
58 "estimated-full-date": {
59 type: Integer,
60 optional: true,
61 description: "Estimation of the UNIX epoch when the storage will be full.\
62 This is calculated via a simple Linear Regression (Least Squares)\
63 of RRD data of the last Month. Missing if there are not enough data points yet.\
64 If the estimate lies in the past, the usage is decreasing.",
65 },
66 "error": {
67 type: String,
68 optional: true,
69 description: "An error description, for example, when the datastore could not be looked up.",
70 },
71 },
72 },
73 },
74 access: {
75 permission: &Permission::Anybody,
76 },
77 )]
78 /// List Datastore usages and estimates
79 pub fn datastore_status(
80 _param: Value,
81 _info: &ApiMethod,
82 rpcenv: &mut dyn RpcEnvironment,
83 ) -> Result<Value, Error> {
84
85 let (config, _digest) = pbs_config::datastore::config()?;
86
87 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
88 let user_info = CachedUserInfo::new()?;
89
90 let mut list = Vec::new();
91
92 for (store, (_, _)) in &config.sections {
93 let user_privs = user_info.lookup_privs(&auth_id, &["datastore", &store]);
94 let allowed = (user_privs & (PRIV_DATASTORE_AUDIT| PRIV_DATASTORE_BACKUP)) != 0;
95 if !allowed {
96 continue;
97 }
98
99 let datastore = match DataStore::lookup_datastore(&store) {
100 Ok(datastore) => datastore,
101 Err(err) => {
102 list.push(json!({
103 "store": store,
104 "total": -1,
105 "used": -1,
106 "avail": -1,
107 "error": err.to_string()
108 }));
109 continue;
110 }
111 };
112 let status = crate::tools::disks::disk_usage(&datastore.base_path())?;
113
114 let mut entry = json!({
115 "store": store,
116 "total": status.total,
117 "used": status.used,
118 "avail": status.avail,
119 "gc-status": datastore.last_gc_status(),
120 });
121
122 let rrd_dir = format!("datastore/{}", store);
123 let now = proxmox::tools::time::epoch_f64();
124
125 let get_rrd = |what: &str| crate::rrd::extract_cached_data(
126 &rrd_dir,
127 what,
128 now,
129 RRDTimeFrameResolution::Month,
130 RRDMode::Average,
131 );
132
133 let total_res = get_rrd("total");
134 let used_res = get_rrd("used");
135
136 if let (Some((start, reso, total_list)), Some((_, _, used_list))) = (total_res, used_res) {
137 let mut usage_list: Vec<f64> = Vec::new();
138 let mut time_list: Vec<u64> = Vec::new();
139 let mut history = Vec::new();
140
141 for (idx, used) in used_list.iter().enumerate() {
142 let total = if idx < total_list.len() {
143 total_list[idx]
144 } else {
145 None
146 };
147
148 match (total, used) {
149 (Some(total), Some(used)) if total != 0.0 => {
150 time_list.push(start + (idx as u64)*reso);
151 let usage = used/total;
152 usage_list.push(usage);
153 history.push(json!(usage));
154 },
155 _ => {
156 history.push(json!(null))
157 }
158 }
159 }
160
161 entry["history-start"] = start.into();
162 entry["history-delta"] = reso.into();
163 entry["history"] = history.into();
164
165 // we skip the calculation for datastores with not enough data
166 if usage_list.len() >= 7 {
167 entry["estimated-full-date"] = match linear_regression(&time_list, &usage_list) {
168 Some((a, b)) if b != 0.0 => Value::from(((1.0 - a) / b).floor() as u64),
169 _ => Value::from(0),
170 };
171 }
172 }
173
174 list.push(entry);
175 }
176
177 Ok(list.into())
178 }
179
180 const SUBDIRS: SubdirMap = &[
181 ("datastore-usage", &Router::new().get(&API_METHOD_DATASTORE_STATUS)),
182 ];
183
184 pub const ROUTER: Router = Router::new()
185 .get(&list_subdirs_api_method!(SUBDIRS))
186 .subdirs(SUBDIRS);