2 use std
::io
::{BufRead, BufReader}
;
5 use serde_json
::{json, Value}
;
7 use proxmox
::api
::{api, Router, RpcEnvironment, Permission}
;
8 use proxmox
::api
::router
::SubdirMap
;
9 use proxmox
::{identity, list_subdirs_api_method, sortable}
;
12 use crate::api2
::types
::*;
13 use crate::server
::{self, UPID}
;
14 use crate::config
::acl
::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}
;
28 description
: "Task status nformation.",
38 description
: "The Unix PID.",
42 description
: "The Unix process start time from `/proc/pid/stat`",
46 description
: "The task start time (Epoch)",
50 description
: "Worker type (arbitrary ASCII string)",
55 description
: "Worker ID (arbitrary ASCII string)",
59 description
: "The user who started the task.",
63 description
: "'running' or 'stopped'",
68 description
: "'OK', 'Error: <msg>', or 'unkwown'.",
73 permission
: &Permission
::Privilege(&["system", "tasks"], PRIV_SYS_AUDIT
, false),
79 ) -> Result
<Value
, Error
> {
81 let upid
= extract_upid(¶m
)?
;
83 let mut result
= json
!({
84 "upid": param
["upid"],
87 "pstart": upid
.pstart
,
88 "starttime": upid
.starttime
,
89 "type": upid
.worker_type
,
91 "user": upid
.username
,
94 if crate::server
::worker_is_active(&upid
) {
95 result
["status"] = Value
::from("running");
97 let exitstatus
= crate::server
::upid_read_status(&upid
).unwrap_or(String
::from("unknown"));
98 result
["status"] = Value
::from("stopped");
99 result
["exitstatus"] = Value
::from(exitstatus
);
105 fn extract_upid(param
: &Value
) -> Result
<UPID
, Error
> {
107 let upid_str
= tools
::required_string_param(¶m
, "upid")?
;
109 upid_str
.parse
::<UPID
>()
124 description
: "Test task status, and set result attribute \"active\" accordingly.",
129 description
: "Start at this line.",
135 description
: "Only list this amount of lines.",
141 permission
: &Permission
::Privilege(&["system", "tasks"], PRIV_SYS_AUDIT
, false),
147 rpcenv
: &mut dyn RpcEnvironment
,
148 ) -> Result
<Value
, Error
> {
150 let upid
= extract_upid(¶m
)?
;
152 let test_status
= param
["test-status"].as_bool().unwrap_or(false);
154 let start
= param
["start"].as_u64().unwrap_or(0);
155 let mut limit
= param
["limit"].as_u64().unwrap_or(50);
157 let mut count
: u64 = 0;
159 let path
= upid
.log_path();
161 let file
= File
::open(path
)?
;
163 let mut lines
: Vec
<Value
> = vec
![];
165 for line
in BufReader
::new(file
).lines() {
169 if count
< start { continue }
;
170 if limit
== 0 { continue }
;
172 lines
.push(json
!({ "n": count, "t": line }
));
177 log
::error
!("reading task log failed: {}", err
);
183 rpcenv
.set_result_attrib("total", Value
::from(count
));
186 let active
= crate::server
::worker_is_active(&upid
);
187 rpcenv
.set_result_attrib("active", Value
::from(active
));
206 permission
: &Permission
::Privilege(&["system", "tasks"], PRIV_SYS_MODIFY
, false),
209 /// Try to stop a task.
212 ) -> Result
<Value
, Error
> {
214 let upid
= extract_upid(¶m
)?
;
216 if crate::server
::worker_is_active(&upid
) {
217 server
::abort_worker_async(upid
);
231 description
: "List tasks beginning from this offset.",
237 description
: "Only list this amount of tasks.",
242 schema
: DATASTORE_SCHEMA
,
247 description
: "Only list running tasks.",
252 description
: "Only list erroneous tasks.",
258 description
: "Only list tasks from this user.",
263 description
: "A list of tasks.",
265 items
: { type: TaskListItem }
,
268 permission
: &Permission
::Privilege(&[], PRIV_SYS_AUDIT
, false),
274 rpcenv
: &mut dyn RpcEnvironment
,
275 ) -> Result
<Vec
<TaskListItem
>, Error
> {
277 let start
= param
["start"].as_u64().unwrap_or(0);
278 let limit
= param
["limit"].as_u64().unwrap_or(50);
279 let errors
= param
["errors"].as_bool().unwrap_or(false);
280 let running
= param
["running"].as_bool().unwrap_or(false);
282 let store
= param
["store"].as_str();
284 let userfilter
= param
["userfilter"].as_str();
286 let list
= server
::read_task_list()?
;
288 let mut result
= vec
![];
292 for info
in list
.iter() {
293 let mut entry
= TaskListItem
{
294 upid
: info
.upid_str
.clone(),
295 node
: "localhost".to_string(),
296 pid
: info
.upid
.pid
as i64,
297 pstart
: info
.upid
.pstart
,
298 starttime
: info
.upid
.starttime
,
299 worker_type
: info
.upid
.worker_type
.clone(),
300 worker_id
: info
.upid
.worker_id
.clone(),
301 user
: info
.upid
.username
.clone(),
306 if let Some(username
) = userfilter
{
307 if !info
.upid
.username
.contains(username
) { continue; }
310 if let Some(store
) = store
{
311 // Note: useful to select all tasks spawned by proxmox-backup-client
312 let worker_id
= match &info
.upid
.worker_id
{
314 None
=> continue, // skip
317 if info
.upid
.worker_type
== "backup" || info
.upid
.worker_type
== "restore" ||
318 info
.upid
.worker_type
== "prune"
320 let prefix
= format
!("{}_", store
);
321 if !worker_id
.starts_with(&prefix
) { continue; }
322 } else if info
.upid
.worker_type
== "garbage_collection" {
323 if worker_id
!= store { continue; }
329 if let Some(ref state
) = info
.state
{
330 if running { continue; }
331 if errors
&& state
.1 == "OK" {
335 entry
.endtime
= Some(state
.0);
336 entry
.status
= Some(state
.1.clone());
339 if (count
as u64) < start
{
346 if (result
.len() as u64) < limit { result.push(entry); }
;
349 rpcenv
.set_result_attrib("total", Value
::from(count
));
355 const UPID_API_SUBDIRS
: SubdirMap
= &sorted
!([
357 "log", &Router
::new()
358 .get(&API_METHOD_READ_TASK_LOG
)
361 "status", &Router
::new()
362 .get(&API_METHOD_GET_TASK_STATUS
)
366 pub const UPID_API_ROUTER
: Router
= Router
::new()
367 .get(&list_subdirs_api_method
!(UPID_API_SUBDIRS
))
368 .delete(&API_METHOD_STOP_TASK
)
369 .subdirs(&UPID_API_SUBDIRS
);
371 pub const ROUTER
: Router
= Router
::new()
372 .get(&API_METHOD_LIST_TASKS
)
373 .match_all("upid", &UPID_API_ROUTER
);