]>
Commit | Line | Data |
---|---|---|
bda48e04 DC |
1 | use proxmox::list_subdirs_api_method; |
2 | ||
3 | use anyhow::{Error}; | |
4 | use serde_json::{json, Value}; | |
5 | ||
20b3094b DC |
6 | use proxmox::api::{ |
7 | api, | |
8 | ApiMethod, | |
9 | Permission, | |
10 | Router, | |
11 | RpcEnvironment, | |
12 | SubdirMap, | |
20b3094b DC |
13 | }; |
14 | ||
15 | use crate::api2::types::{ | |
16 | DATASTORE_SCHEMA, | |
17 | RRDMode, | |
18 | RRDTimeFrameResolution, | |
e7cb4dc5 WB |
19 | TaskListItem, |
20 | Userid, | |
20b3094b | 21 | }; |
bda48e04 | 22 | |
20b3094b | 23 | use crate::server; |
bda48e04 DC |
24 | use crate::backup::{DataStore}; |
25 | use crate::config::datastore; | |
26 | use crate::tools::statistics::{linear_regression}; | |
27 | use crate::config::cached_user_info::CachedUserInfo; | |
28 | use crate::config::acl::{ | |
20b3094b | 29 | PRIV_SYS_AUDIT, |
bda48e04 DC |
30 | PRIV_DATASTORE_AUDIT, |
31 | PRIV_DATASTORE_BACKUP, | |
32 | }; | |
33 | ||
34 | #[api( | |
35 | returns: { | |
36 | description: "Lists the Status of the Datastores.", | |
37 | type: Array, | |
38 | items: { | |
39 | description: "Status of a Datastore", | |
40 | type: Object, | |
41 | properties: { | |
42 | store: { | |
43 | schema: DATASTORE_SCHEMA, | |
44 | }, | |
45 | total: { | |
46 | type: Integer, | |
47 | description: "The Size of the underlying storage in bytes", | |
48 | }, | |
49 | used: { | |
50 | type: Integer, | |
51 | description: "The used bytes of the underlying storage", | |
52 | }, | |
53 | avail: { | |
54 | type: Integer, | |
55 | description: "The available bytes of the underlying storage", | |
56 | }, | |
57 | history: { | |
58 | type: Array, | |
59 | description: "A list of usages of the past (last Month).", | |
60 | items: { | |
61 | type: Number, | |
62 | description: "The usage of a time in the past. Either null or between 0.0 and 1.0.", | |
63 | } | |
64 | }, | |
65 | "estimated-full-date": { | |
66 | type: Integer, | |
67 | optional: true, | |
68 | description: "Estimation of the UNIX epoch when the storage will be full.\ | |
69 | This is calculated via a simple Linear Regression (Least Squares)\ | |
70 | of RRD data of the last Month. Missing if there are not enough data points yet.\ | |
71 | If the estimate lies in the past, the usage is decreasing.", | |
72 | }, | |
73 | }, | |
74 | }, | |
75 | }, | |
ecd55041 FG |
76 | access: { |
77 | permission: &Permission::Anybody, | |
78 | }, | |
bda48e04 DC |
79 | )] |
80 | /// List Datastore usages and estimates | |
81 | fn datastore_status( | |
82 | _param: Value, | |
83 | _info: &ApiMethod, | |
84 | rpcenv: &mut dyn RpcEnvironment, | |
85 | ) -> Result<Value, Error> { | |
86 | ||
87 | let (config, _digest) = datastore::config()?; | |
88 | ||
e7cb4dc5 | 89 | let userid: Userid = rpcenv.get_user().unwrap().parse()?; |
bda48e04 DC |
90 | let user_info = CachedUserInfo::new()?; |
91 | ||
92 | let mut list = Vec::new(); | |
93 | ||
94 | for (store, (_, _)) in &config.sections { | |
e7cb4dc5 | 95 | let user_privs = user_info.lookup_privs(&userid, &["datastore", &store]); |
bda48e04 DC |
96 | let allowed = (user_privs & (PRIV_DATASTORE_AUDIT| PRIV_DATASTORE_BACKUP)) != 0; |
97 | if !allowed { | |
98 | continue; | |
99 | } | |
100 | ||
101 | let datastore = DataStore::lookup_datastore(&store)?; | |
102 | let status = crate::tools::disks::disk_usage(&datastore.base_path())?; | |
103 | ||
104 | let mut entry = json!({ | |
105 | "store": store, | |
106 | "total": status.total, | |
107 | "used": status.used, | |
108 | "avail": status.avail, | |
109 | }); | |
110 | ||
111 | let rrd_dir = format!("datastore/{}", store); | |
6a7be83e | 112 | let now = proxmox::tools::time::epoch_f64(); |
ec8f0424 DC |
113 | let rrd_resolution = RRDTimeFrameResolution::Month; |
114 | let rrd_mode = RRDMode::Average; | |
bda48e04 | 115 | |
ec8f0424 | 116 | let total_res = crate::rrd::extract_cached_data( |
bda48e04 | 117 | &rrd_dir, |
ec8f0424 DC |
118 | "total", |
119 | now, | |
120 | rrd_resolution, | |
121 | rrd_mode, | |
122 | ); | |
bda48e04 | 123 | |
ec8f0424 DC |
124 | let used_res = crate::rrd::extract_cached_data( |
125 | &rrd_dir, | |
126 | "used", | |
127 | now, | |
128 | rrd_resolution, | |
129 | rrd_mode, | |
130 | ); | |
131 | ||
132 | match (total_res, used_res) { | |
133 | (Some((start, reso, total_list)), Some((_, _, used_list))) => { | |
134 | let mut usage_list: Vec<f64> = Vec::new(); | |
135 | let mut time_list: Vec<u64> = Vec::new(); | |
136 | let mut history = Vec::new(); | |
137 | ||
138 | for (idx, used) in used_list.iter().enumerate() { | |
139 | let total = if idx < total_list.len() { | |
140 | total_list[idx] | |
141 | } else { | |
142 | None | |
143 | }; | |
144 | ||
145 | match (total, used) { | |
146 | (Some(total), Some(used)) if total != 0.0 => { | |
147 | time_list.push(start + (idx as u64)*reso); | |
148 | let usage = used/total; | |
149 | usage_list.push(usage); | |
150 | history.push(json!(usage)); | |
151 | }, | |
152 | _ => { | |
153 | history.push(json!(null)) | |
154 | } | |
155 | } | |
bda48e04 | 156 | } |
bda48e04 | 157 | |
ec8f0424 | 158 | entry["history"] = history.into(); |
bda48e04 | 159 | |
ec8f0424 DC |
160 | // we skip the calculation for datastores with not enough data |
161 | if usage_list.len() >= 7 { | |
162 | if let Some((a,b)) = linear_regression(&time_list, &usage_list) { | |
163 | if b != 0.0 { | |
164 | let estimate = (1.0 - a) / b; | |
165 | entry["estimated-full-date"] = Value::from(estimate.floor() as u64); | |
a26c27c8 DC |
166 | } else { |
167 | entry["estimated-full-date"] = Value::from(0); | |
ec8f0424 DC |
168 | } |
169 | } | |
bda48e04 | 170 | } |
ec8f0424 DC |
171 | }, |
172 | _ => {}, | |
bda48e04 DC |
173 | } |
174 | ||
175 | list.push(entry); | |
176 | } | |
177 | ||
bda48e04 DC |
178 | Ok(list.into()) |
179 | } | |
180 | ||
20b3094b DC |
181 | #[api( |
182 | input: { | |
183 | properties: { | |
184 | since: { | |
185 | type: u64, | |
186 | description: "Only list tasks since this UNIX epoch.", | |
187 | optional: true, | |
188 | }, | |
189 | }, | |
190 | }, | |
191 | returns: { | |
192 | description: "A list of tasks.", | |
193 | type: Array, | |
194 | items: { type: TaskListItem }, | |
195 | }, | |
196 | access: { | |
197 | description: "Users can only see there own tasks, unless the have Sys.Audit on /system/tasks.", | |
198 | permission: &Permission::Anybody, | |
199 | }, | |
200 | )] | |
201 | /// List tasks. | |
202 | pub fn list_tasks( | |
203 | _param: Value, | |
204 | rpcenv: &mut dyn RpcEnvironment, | |
205 | ) -> Result<Vec<TaskListItem>, Error> { | |
206 | ||
e7cb4dc5 | 207 | let userid: Userid = rpcenv.get_user().unwrap().parse()?; |
20b3094b | 208 | let user_info = CachedUserInfo::new()?; |
e7cb4dc5 | 209 | let user_privs = user_info.lookup_privs(&userid, &["system", "tasks"]); |
20b3094b DC |
210 | |
211 | let list_all = (user_privs & PRIV_SYS_AUDIT) != 0; | |
212 | ||
213 | // TODO: replace with call that gets all task since 'since' epoch | |
214 | let list: Vec<TaskListItem> = server::read_task_list()? | |
215 | .into_iter() | |
216 | .map(TaskListItem::from) | |
e7cb4dc5 | 217 | .filter(|entry| list_all || entry.user == userid) |
20b3094b DC |
218 | .collect(); |
219 | ||
220 | Ok(list.into()) | |
221 | } | |
222 | ||
bda48e04 DC |
223 | const SUBDIRS: SubdirMap = &[ |
224 | ("datastore-usage", &Router::new().get(&API_METHOD_DATASTORE_STATUS)), | |
20b3094b | 225 | ("tasks", &Router::new().get(&API_METHOD_LIST_TASKS)), |
bda48e04 DC |
226 | ]; |
227 | ||
228 | pub const ROUTER: Router = Router::new() | |
229 | .get(&list_subdirs_api_method!(SUBDIRS)) | |
230 | .subdirs(SUBDIRS); |