]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/status.rs
d/control: add ',' after qrencode dependency
[proxmox-backup.git] / src / api2 / status.rs
1 use proxmox::list_subdirs_api_method;
2
3 use anyhow::{Error};
4 use serde_json::{json, Value};
5
6 use proxmox::api::{
7 api,
8 ApiMethod,
9 Permission,
10 Router,
11 RpcEnvironment,
12 SubdirMap,
13 };
14
15 use crate::api2::types::{
16 DATASTORE_SCHEMA,
17 RRDMode,
18 RRDTimeFrameResolution,
19 TaskListItem,
20 Userid,
21 };
22
23 use crate::server;
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::{
29 PRIV_SYS_AUDIT,
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 },
76 access: {
77 permission: &Permission::Anybody,
78 },
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
89 let userid: Userid = rpcenv.get_user().unwrap().parse()?;
90 let user_info = CachedUserInfo::new()?;
91
92 let mut list = Vec::new();
93
94 for (store, (_, _)) in &config.sections {
95 let user_privs = user_info.lookup_privs(&userid, &["datastore", &store]);
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);
112 let now = proxmox::tools::time::epoch_f64();
113 let rrd_resolution = RRDTimeFrameResolution::Month;
114 let rrd_mode = RRDMode::Average;
115
116 let total_res = crate::rrd::extract_cached_data(
117 &rrd_dir,
118 "total",
119 now,
120 rrd_resolution,
121 rrd_mode,
122 );
123
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 }
156 }
157
158 entry["history"] = history.into();
159
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);
166 } else {
167 entry["estimated-full-date"] = Value::from(0);
168 }
169 }
170 }
171 },
172 _ => {},
173 }
174
175 list.push(entry);
176 }
177
178 Ok(list.into())
179 }
180
181 #[api(
182 input: {
183 properties: {
184 since: {
185 type: i64,
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 since: Option<i64>,
204 _param: Value,
205 rpcenv: &mut dyn RpcEnvironment,
206 ) -> Result<Vec<TaskListItem>, Error> {
207
208 let userid: Userid = rpcenv.get_user().unwrap().parse()?;
209 let user_info = CachedUserInfo::new()?;
210 let user_privs = user_info.lookup_privs(&userid, &["system", "tasks"]);
211
212 let list_all = (user_privs & PRIV_SYS_AUDIT) != 0;
213 let since = since.unwrap_or_else(|| 0);
214
215 let list: Vec<TaskListItem> = server::TaskListInfoIterator::new(false)?
216 .take_while(|info| {
217 match info {
218 Ok(info) => info.upid.starttime > since,
219 Err(_) => false
220 }
221 })
222 .filter_map(|info| {
223 match info {
224 Ok(info) => {
225 if list_all || info.upid.userid == userid {
226 Some(Ok(TaskListItem::from(info)))
227 } else {
228 None
229 }
230 }
231 Err(err) => Some(Err(err))
232 }
233 })
234 .collect::<Result<Vec<TaskListItem>, Error>>()?;
235
236 Ok(list.into())
237 }
238
239 const SUBDIRS: SubdirMap = &[
240 ("datastore-usage", &Router::new().get(&API_METHOD_DATASTORE_STATUS)),
241 ("tasks", &Router::new().get(&API_METHOD_LIST_TASKS)),
242 ];
243
244 pub const ROUTER: Router = Router::new()
245 .get(&list_subdirs_api_method!(SUBDIRS))
246 .subdirs(SUBDIRS);