]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/node/tasks.rs
remove unused import
[proxmox-backup.git] / src / api2 / node / tasks.rs
CommitLineData
cad540e9
WB
1use std::fs::File;
2use std::io::{BufRead, BufReader};
063ca5be 3
f7d4e4b5 4use anyhow::{Error};
063ca5be
DM
5use serde_json::{json, Value};
6
e7cb4dc5 7use proxmox::api::{api, Router, RpcEnvironment, Permission};
cad540e9 8use proxmox::api::router::SubdirMap;
9ea4bce4 9use proxmox::{identity, list_subdirs_api_method, sortable};
552c2259
DM
10
11use crate::tools;
4ebf0eab 12use crate::api2::types::*;
5a12c0e2 13use crate::server::{self, UPID};
83b6a7cf 14use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
720af9f6
DM
15use crate::config::cached_user_info::CachedUserInfo;
16
5a12c0e2 17
83b6a7cf
DM
18#[api(
19 input: {
20 properties: {
21 node: {
22 schema: NODE_SCHEMA,
23 },
24 upid: {
25 schema: UPID_SCHEMA,
26 },
27 },
28 },
29 returns: {
30 description: "Task status nformation.",
31 properties: {
32 node: {
33 schema: NODE_SCHEMA,
34 },
35 upid: {
36 schema: UPID_SCHEMA,
37 },
38 pid: {
39 type: i64,
40 description: "The Unix PID.",
41 },
42 pstart: {
43 type: u64,
44 description: "The Unix process start time from `/proc/pid/stat`",
45 },
46 starttime: {
47 type: i64,
48 description: "The task start time (Epoch)",
49 },
50 "type": {
51 type: String,
52 description: "Worker type (arbitrary ASCII string)",
53 },
54 id: {
55 type: String,
56 optional: true,
57 description: "Worker ID (arbitrary ASCII string)",
58 },
59 user: {
60 type: String,
61 description: "The user who started the task.",
62 },
63 status: {
64 type: String,
65 description: "'running' or 'stopped'",
66 },
67 exitstatus: {
68 type: String,
69 optional: true,
70 description: "'OK', 'Error: <msg>', or 'unkwown'.",
71 },
72 },
73 },
74 access: {
720af9f6
DM
75 description: "Users can access there own tasks, or need Sys.Audit on /system/tasks.",
76 permission: &Permission::Anybody,
83b6a7cf
DM
77 },
78)]
79/// Get task status.
5751e495 80async fn get_task_status(
5a12c0e2 81 param: Value,
720af9f6 82 rpcenv: &mut dyn RpcEnvironment,
5a12c0e2
DM
83) -> Result<Value, Error> {
84
85 let upid = extract_upid(&param)?;
86
e7cb4dc5 87 let userid: Userid = rpcenv.get_user().unwrap().parse()?;
720af9f6 88
e7cb4dc5 89 if userid != upid.userid {
720af9f6 90 let user_info = CachedUserInfo::new()?;
e7cb4dc5 91 user_info.check_privs(&userid, &["system", "tasks"], PRIV_SYS_AUDIT, false)?;
720af9f6
DM
92 }
93
c360bd73
DM
94 let mut result = json!({
95 "upid": param["upid"],
96 "node": upid.node,
97 "pid": upid.pid,
98 "pstart": upid.pstart,
99 "starttime": upid.starttime,
100 "type": upid.worker_type,
101 "id": upid.worker_id,
e7cb4dc5 102 "user": upid.userid,
c360bd73
DM
103 });
104
5751e495 105 if crate::server::worker_is_active(&upid).await? {
c360bd73 106 result["status"] = Value::from("running");
5a12c0e2 107 } else {
c360bd73
DM
108 let exitstatus = crate::server::upid_read_status(&upid).unwrap_or(String::from("unknown"));
109 result["status"] = Value::from("stopped");
110 result["exitstatus"] = Value::from(exitstatus);
5a12c0e2
DM
111 };
112
113 Ok(result)
114}
115
116fn extract_upid(param: &Value) -> Result<UPID, Error> {
117
118 let upid_str = tools::required_string_param(&param, "upid")?;
119
8560fe3e 120 upid_str.parse::<UPID>()
5a12c0e2
DM
121}
122
83b6a7cf
DM
123#[api(
124 input: {
125 properties: {
126 node: {
127 schema: NODE_SCHEMA,
128 },
129 upid: {
130 schema: UPID_SCHEMA,
131 },
132 "test-status": {
133 type: bool,
134 optional: true,
135 description: "Test task status, and set result attribute \"active\" accordingly.",
136 },
137 start: {
138 type: u64,
139 optional: true,
140 description: "Start at this line.",
141 default: 0,
142 },
143 limit: {
144 type: u64,
145 optional: true,
146 description: "Only list this amount of lines.",
147 default: 50,
148 },
149 },
150 },
151 access: {
720af9f6
DM
152 description: "Users can access there own tasks, or need Sys.Audit on /system/tasks.",
153 permission: &Permission::Anybody,
83b6a7cf
DM
154 },
155)]
156/// Read task log.
5751e495 157async fn read_task_log(
5a12c0e2 158 param: Value,
e8d1da6a 159 mut rpcenv: &mut dyn RpcEnvironment,
5a12c0e2
DM
160) -> Result<Value, Error> {
161
162 let upid = extract_upid(&param)?;
6b508dd5 163
e7cb4dc5 164 let userid: Userid = rpcenv.get_user().unwrap().parse()?;
720af9f6 165
e7cb4dc5 166 if userid != upid.userid {
720af9f6 167 let user_info = CachedUserInfo::new()?;
e7cb4dc5 168 user_info.check_privs(&userid, &["system", "tasks"], PRIV_SYS_AUDIT, false)?;
720af9f6
DM
169 }
170
6b508dd5
DM
171 let test_status = param["test-status"].as_bool().unwrap_or(false);
172
5a12c0e2
DM
173 let start = param["start"].as_u64().unwrap_or(0);
174 let mut limit = param["limit"].as_u64().unwrap_or(50);
6b508dd5 175
5a12c0e2
DM
176 let mut count: u64 = 0;
177
178 let path = upid.log_path();
179
180 let file = File::open(path)?;
181
182 let mut lines: Vec<Value> = vec![];
183
184 for line in BufReader::new(file).lines() {
185 match line {
186 Ok(line) => {
187 count += 1;
188 if count < start { continue };
11377a47 189 if limit == 0 { continue };
5a12c0e2
DM
190
191 lines.push(json!({ "n": count, "t": line }));
192
193 limit -= 1;
194 }
195 Err(err) => {
196 log::error!("reading task log failed: {}", err);
197 break;
198 }
199 }
200 }
201
e8d1da6a 202 rpcenv["total"] = Value::from(count);
d8d40dd0 203
6b508dd5 204 if test_status {
5751e495 205 let active = crate::server::worker_is_active(&upid).await?;
e8d1da6a 206 rpcenv["active"] = Value::from(active);
6b508dd5
DM
207 }
208
5a12c0e2
DM
209 Ok(json!(lines))
210}
063ca5be 211
83b6a7cf
DM
212#[api(
213 protected: true,
214 input: {
215 properties: {
216 node: {
217 schema: NODE_SCHEMA,
218 },
219 upid: {
220 schema: UPID_SCHEMA,
221 },
222 },
223 },
224 access: {
720af9f6
DM
225 description: "Users can stop there own tasks, or need Sys.Modify on /system/tasks.",
226 permission: &Permission::Anybody,
83b6a7cf
DM
227 },
228)]
229/// Try to stop a task.
a665dea1
DM
230fn stop_task(
231 param: Value,
720af9f6 232 rpcenv: &mut dyn RpcEnvironment,
a665dea1
DM
233) -> Result<Value, Error> {
234
235 let upid = extract_upid(&param)?;
236
e7cb4dc5 237 let userid: Userid = rpcenv.get_user().unwrap().parse()?;
720af9f6 238
e7cb4dc5 239 if userid != upid.userid {
720af9f6 240 let user_info = CachedUserInfo::new()?;
e7cb4dc5 241 user_info.check_privs(&userid, &["system", "tasks"], PRIV_SYS_MODIFY, false)?;
720af9f6
DM
242 }
243
5751e495 244 server::abort_worker_async(upid);
a665dea1
DM
245
246 Ok(Value::Null)
247}
248
2c4b303c
DM
249#[api(
250 input: {
251 properties: {
252 node: {
253 schema: NODE_SCHEMA
254 },
255 start: {
256 type: u64,
257 description: "List tasks beginning from this offset.",
258 default: 0,
259 optional: true,
260 },
261 limit: {
262 type: u64,
263 description: "Only list this amount of tasks.",
264 default: 50,
265 optional: true,
266 },
267 store: {
268 schema: DATASTORE_SCHEMA,
269 optional: true,
270 },
271 running: {
272 type: bool,
273 description: "Only list running tasks.",
274 optional: true,
ca9dfe5f 275 default: false,
2c4b303c
DM
276 },
277 errors: {
278 type: bool,
279 description: "Only list erroneous tasks.",
280 optional:true,
ca9dfe5f 281 default: false,
2c4b303c
DM
282 },
283 userfilter: {
e7cb4dc5 284 optional: true,
2c4b303c
DM
285 type: String,
286 description: "Only list tasks from this user.",
287 },
288 },
289 },
290 returns: {
291 description: "A list of tasks.",
292 type: Array,
99384f79 293 items: { type: TaskListItem },
2c4b303c 294 },
83b6a7cf 295 access: {
720af9f6
DM
296 description: "Users can only see there own tasks, unless the have Sys.Audit on /system/tasks.",
297 permission: &Permission::Anybody,
83b6a7cf 298 },
2c4b303c
DM
299)]
300/// List tasks.
8528fce8 301pub fn list_tasks(
ca9dfe5f
DM
302 start: u64,
303 limit: u64,
304 errors: bool,
305 running: bool,
063ca5be 306 param: Value,
e8d1da6a 307 mut rpcenv: &mut dyn RpcEnvironment,
99384f79 308) -> Result<Vec<TaskListItem>, Error> {
063ca5be 309
e7cb4dc5 310 let userid: Userid = rpcenv.get_user().unwrap().parse()?;
720af9f6 311 let user_info = CachedUserInfo::new()?;
e7cb4dc5 312 let user_privs = user_info.lookup_privs(&userid, &["system", "tasks"]);
720af9f6
DM
313
314 let list_all = (user_privs & PRIV_SYS_AUDIT) != 0;
315
567d3e00
DM
316 let store = param["store"].as_str();
317
d2a2e02b
DM
318 let userfilter = param["userfilter"].as_str();
319
063ca5be
DM
320 let list = server::read_task_list()?;
321
322 let mut result = vec![];
323
324 let mut count = 0;
325
df528ee6 326 for info in list {
e7cb4dc5 327 if !list_all && info.upid.userid != userid { continue; }
720af9f6 328
063ca5be 329
e7cb4dc5
WB
330 if let Some(userid) = userfilter {
331 if !info.upid.userid.as_str().contains(userid) { continue; }
d2a2e02b
DM
332 }
333
567d3e00
DM
334 if let Some(store) = store {
335 // Note: useful to select all tasks spawned by proxmox-backup-client
336 let worker_id = match &info.upid.worker_id {
337 Some(w) => w,
338 None => continue, // skip
339 };
340
503995c7
DM
341 if info.upid.worker_type == "backup" || info.upid.worker_type == "restore" ||
342 info.upid.worker_type == "prune"
343 {
567d3e00
DM
344 let prefix = format!("{}_", store);
345 if !worker_id.starts_with(&prefix) { continue; }
503995c7 346 } else if info.upid.worker_type == "garbage_collection" {
567d3e00
DM
347 if worker_id != store { continue; }
348 } else {
349 continue; // skip
350 }
351 }
352
063ca5be 353 if let Some(ref state) = info.state {
6d046129 354 if running { continue; }
063ca5be
DM
355 if errors && state.1 == "OK" {
356 continue;
357 }
063ca5be
DM
358 }
359
3c3bee2e 360 if (count as u64) < start {
063ca5be
DM
361 count += 1;
362 continue;
363 } else {
364 count += 1;
365 }
366
df528ee6 367 if (result.len() as u64) < limit { result.push(info.into()); };
063ca5be
DM
368 }
369
e8d1da6a 370 rpcenv["total"] = Value::from(count);
063ca5be 371
0196b9bf 372 Ok(result)
063ca5be
DM
373}
374
552c2259 375#[sortable]
83b6a7cf 376const UPID_API_SUBDIRS: SubdirMap = &sorted!([
255f378a
DM
377 (
378 "log", &Router::new()
83b6a7cf 379 .get(&API_METHOD_READ_TASK_LOG)
255f378a
DM
380 ),
381 (
382 "status", &Router::new()
83b6a7cf 383 .get(&API_METHOD_GET_TASK_STATUS)
255f378a 384 )
83b6a7cf 385]);
255f378a
DM
386
387pub const UPID_API_ROUTER: Router = Router::new()
388 .get(&list_subdirs_api_method!(UPID_API_SUBDIRS))
83b6a7cf 389 .delete(&API_METHOD_STOP_TASK)
255f378a
DM
390 .subdirs(&UPID_API_SUBDIRS);
391
392pub const ROUTER: Router = Router::new()
2c4b303c 393 .get(&API_METHOD_LIST_TASKS)
255f378a 394 .match_all("upid", &UPID_API_ROUTER);