1 package PVE
::API2
::Tasks
;
7 use File
::ReadBackwards
;
12 use PVE
::RPCEnvironment
;
13 use PVE
::JSONSchema
qw(get_standard_option);
14 use PVE
::Exception
qw(raise_param_exc);
15 use PVE
::AccessControl
;
17 use base
qw(PVE::RESTHandler);
19 my $convert_token_task = sub {
22 if (PVE
::AccessControl
::pve_verify_tokenid
($task->{user
}, 1)) {
23 ($task->{user
}, $task->{tokenid
}) = PVE
::AccessControl
::split_tokenid
($task->{user
});
27 my $check_task_user = sub {
28 my ($task, $user) = @_;
30 if ($task->{tokenid
}) {
31 my $fulltoken = PVE
::AccessControl
::join_tokenid
($task->{user
}, $task->{tokenid
});
32 # token only sees token tasks, user sees user + token tasks
33 return $user eq $fulltoken || $user eq $task->{user
};
35 return $user eq $task->{user
};
39 __PACKAGE__-
>register_method({
44 description
=> "List task associated with the current user, or all task the user has 'Sys.Audit' permissions on /nodes/<node> (the <node> the task runs on).",
47 description
=> "Read task list for one node (finished tasks).",
50 additionalProperties
=> 0,
52 node
=> get_standard_option
('pve-node'),
58 description
=> "List tasks beginning from this offset.",
65 description
=> "Only list this amount of tasks.",
70 description
=> "Only list tasks from this user.",
75 description
=> 'Only list tasks of this type (e.g., vzstart, vzdump).',
77 vmid
=> get_standard_option
('pve-vmid', {
78 description
=> "Only list tasks for this VM.",
88 enum
=> ['archive', 'active', 'all'],
91 description
=> 'List archived, active or all tasks.',
95 description
=> "Only list tasks since this UNIX epoch.",
100 description
=> "Only list tasks until this UNIX epoch.",
105 format
=> 'pve-task-status-type-list',
107 description
=> 'List of Task States that should be returned.',
116 upid
=> { type
=> 'string', title
=> 'UPID', },
117 node
=> { type
=> 'string', title
=> 'Node', },
118 pid
=> { type
=> 'integer', title
=> 'PID', },
119 pstart
=> { type
=> 'integer', },
120 starttime
=> { type
=> 'integer', title
=> 'Starttime', },
121 type
=> { type
=> 'string', title
=> 'Type', },
122 id
=> { type
=> 'string', title
=> 'ID', },
123 user
=> { type
=> 'string', title
=> 'User', },
124 endtime
=> { type
=> 'integer', optional
=> 1, title
=> 'Endtime', },
125 status
=> { type
=> 'string', optional
=> 1, title
=> 'Status', },
128 links
=> [ { rel
=> 'child', href
=> "{upid}" } ],
133 my $rpcenv = PVE
::RPCEnvironment
::get
();
134 my $user = $rpcenv->get_user();
138 my $filename = "/var/log/pve/tasks/index";
140 my $node = $param->{node
};
141 my $start = $param->{start
} // 0;
142 my $limit = $param->{limit
} // 50;
143 my $userfilter = $param->{userfilter
};
144 my $typefilter = $param->{typefilter
};
145 my $errors = $param->{errors
} // 0;
146 my $source = $param->{source
} // 'archive';
147 my $since = $param->{since
};
148 my $until = $param->{until};
156 if (defined($param->{statusfilter
}) && !$errors) {
163 for my $filter (PVE
::Tools
::split_list
($param->{statusfilter
})) {
164 $statusfilter->{lc($filter)} = 1 ;
167 $statusfilter->{ok
} = 0;
173 my $auditor = $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ], 1);
175 my $filter_task = sub {
178 return 1 if $userfilter && $task->{user
} !~ m/\Q$userfilter\E/i;
179 return 1 if !($auditor || $check_task_user->($task, $user));
181 return 1 if $typefilter && $task->{type
} ne $typefilter;
183 return 1 if $param->{vmid
} && (!$task->{id
} || $task->{id
} ne $param->{vmid
});
185 return 1 if defined($since) && $task->{starttime
} < $since;
186 return 1 if defined($until) && $task->{starttime
} > $until;
188 my $type = PVE
::Tools
::upid_normalize_status_type
($task->{status
});
189 return 1 if !$statusfilter->{$type};
191 return 1 if $count++ < $start;
192 return 1 if $limit <= 0;
197 my $parse_line = sub {
198 if ($line =~ m/^(\S+)(\s([0-9A-Za-z]{8})(\s(\S.*))?)?$/) {
202 if ((my $task = PVE
::Tools
::upid_decode
($upid, 1))) {
204 $task->{upid
} = $upid;
205 $task->{endtime
} = hex($endtime) if $endtime;
206 $task->{status
} = $status if $status;
208 $convert_token_task->($task);
209 if (!$filter_task->($task)) {
217 if ($source eq 'active' || $source eq 'all') {
218 my $recent_tasks = PVE
::INotify
::read_file
('active');
219 for my $task (@$recent_tasks) {
220 next if $task->{saved
}; # archived task, already in index(.1)
221 if (!$filter_task->($task)) {
222 $task->{status
} = 'RUNNING' if !$task->{status
}; # otherwise it would be archived
229 if ($source ne 'active') {
230 if (my $bw = File
::ReadBackwards-
>new($filename)) {
231 while (defined ($line = $bw->readline)) {
236 if (my $bw = File
::ReadBackwards-
>new("$filename.1")) {
237 while (defined ($line = $bw->readline)) {
244 $rpcenv->set_result_attrib('total', $count);
249 __PACKAGE__-
>register_method({
250 name
=> 'upid_index',
253 description
=> '', # index helper
254 permissions
=> { user
=> 'all' },
256 additionalProperties
=> 0,
258 node
=> get_standard_option
('pve-node'),
259 upid
=> { type
=> 'string' },
268 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
279 __PACKAGE__-
>register_method({
283 description
=> 'Stop a task.',
285 description
=> "The user needs 'Sys.Modify' permissions on '/nodes/<node>' if the task does not belong to him.",
291 additionalProperties
=> 0,
293 node
=> get_standard_option
('pve-node'),
294 upid
=> { type
=> 'string' },
297 returns
=> { type
=> 'null' },
301 my ($task, $filename) = PVE
::Tools
::upid_decode
($param->{upid
}, 1);
302 raise_param_exc
({ upid
=> "unable to parse worker upid" }) if !$task;
303 raise_param_exc
({ upid
=> "no such task" }) if ! -f
$filename;
305 my $rpcenv = PVE
::RPCEnvironment
::get
();
306 my $user = $rpcenv->get_user();
307 my $node = $param->{node
};
309 $convert_token_task->($task);
311 if (!$check_task_user->($task, $user)) {
312 $rpcenv->check($user, "/nodes/$node", [ 'Sys.Modify' ]);
315 PVE
::RPCEnvironment-
>check_worker($param->{upid
}, 1);
320 __PACKAGE__-
>register_method({
321 name
=> 'read_task_log',
322 path
=> '{upid}/log',
325 description
=> "The user needs 'Sys.Audit' permissions on '/nodes/<node>' if the task does not belong to him.",
329 description
=> "Read task log.",
332 additionalProperties
=> 0,
334 node
=> get_standard_option
('pve-node'),
335 upid
=> { type
=> 'string' },
356 description
=> "Line number",
360 description
=> "Line text",
369 my ($task, $filename) = PVE
::Tools
::upid_decode
($param->{upid
}, 1);
370 raise_param_exc
({ upid
=> "unable to parse worker upid" }) if !$task;
372 my $rpcenv = PVE
::RPCEnvironment
::get
();
373 my $user = $rpcenv->get_user();
374 my $node = $param->{node
};
375 my $start = $param->{start
} // 0;
376 my $limit = $param->{limit
} // 50;
378 $convert_token_task->($task);
380 if (!$check_task_user->($task, $user)) {
381 $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]);
384 my ($count, $lines) = PVE
::Tools
::dump_logfile
($filename, $start, $limit);
386 $rpcenv->set_result_attrib('total', $count);
392 my $exit_status_cache = {};
394 __PACKAGE__-
>register_method({
395 name
=> 'read_task_status',
396 path
=> '{upid}/status',
399 description
=> "The user needs 'Sys.Audit' permissions on '/nodes/<node>' if the task does not belong to him.",
403 description
=> "Read task status.",
406 additionalProperties
=> 0,
408 node
=> get_standard_option
('pve-node'),
409 upid
=> { type
=> 'string' },
419 type
=> 'string', enum
=> ['running', 'stopped'],
448 my ($task, $filename) = PVE
::Tools
::upid_decode
($param->{upid
}, 1);
449 raise_param_exc
({ upid
=> "unable to parse worker upid" }) if !$task;
450 raise_param_exc
({ upid
=> "no such task" }) if ! -f
$filename;
454 my $rpcenv = PVE
::RPCEnvironment
::get
();
455 my $user = $rpcenv->get_user();
456 my $node = $param->{node
};
458 $convert_token_task->($task);
460 if (!$check_task_user->($task, $user)) {
461 $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]);
464 my $pstart = PVE
::ProcFSTools
::read_proc_starttime
($task->{pid
});
465 $task->{status
} = ($pstart && ($pstart == $task->{pstart
})) ?
466 'running' : 'stopped';
468 $task->{upid
} = $param->{upid
}; # include upid
470 if ($task->{status
} eq 'stopped') {
471 if (!defined($exit_status_cache->{$task->{upid
}})) {
472 $exit_status_cache->{$task->{upid
}} =
473 PVE
::Tools
::upid_read_status
($task->{upid
});
475 $task->{exitstatus
} = $exit_status_cache->{$task->{upid
}};