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