]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Tasks.pm
API: Tasks: add more fields to return schema
[pve-manager.git] / PVE / API2 / Tasks.pm
CommitLineData
aff192e6
DM
1package PVE::API2::Tasks;
2
3use strict;
4use warnings;
5use POSIX;
6use IO::File;
7use File::ReadBackwards;
8use PVE::Tools;
9use PVE::SafeSyslog;
10use PVE::RESTHandler;
11use PVE::ProcFSTools;
12use PVE::RPCEnvironment;
13use PVE::JSONSchema qw(get_standard_option);
5e44c7e1 14use PVE::Exception qw(raise_param_exc);
aff192e6
DM
15use PVE::AccessControl;
16
17use base qw(PVE::RESTHandler);
18
a901f94a
FG
19my $convert_token_task = sub {
20 my ($task) = @_;
21
22 if (PVE::AccessControl::pve_verify_tokenid($task->{user}, 1)) {
23 ($task->{user}, $task->{tokenid}) = PVE::AccessControl::split_tokenid($task->{user});
24 }
25};
26
8f8073cb
FG
27my $check_task_user = sub {
28 my ($task, $user) = @_;
29
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};
34 } else {
35 return $user eq $task->{user};
36 }
37};
38
aff192e6 39__PACKAGE__->register_method({
20663182
DC
40 name => 'node_tasks',
41 path => '',
aff192e6 42 method => 'GET',
20663182 43 permissions => {
00cc94d3 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).",
20663182 45 user => 'all'
00cc94d3 46 },
aff192e6
DM
47 description => "Read task list for one node (finished tasks).",
48 proxyto => 'node',
49 parameters => {
50 additionalProperties => 0,
51 properties => {
52 node => get_standard_option('pve-node'),
53 start => {
54 type => 'integer',
55 minimum => 0,
7419faa4 56 default => 0,
aff192e6 57 optional => 1,
7419faa4 58 description => "List tasks beginning from this offset.",
aff192e6
DM
59 },
60 limit => {
61 type => 'integer',
62 minimum => 0,
7419faa4 63 default => 50,
aff192e6 64 optional => 1,
7419faa4 65 description => "Only list this amount of tasks.",
aff192e6
DM
66 },
67 userfilter => {
68 type => 'string',
69 optional => 1,
7419faa4 70 description => "Only list tasks from this user.",
aff192e6 71 },
8dacbee3
FG
72 typefilter => {
73 type => 'string',
74 optional => 1,
75 description => 'Only list tasks of this type (e.g., vzstart, vzdump).',
76 },
5614bec0
DM
77 vmid => get_standard_option('pve-vmid', {
78 description => "Only list tasks for this VM.",
7419faa4 79 optional => 1,
5614bec0 80 }),
aff192e6
DM
81 errors => {
82 type => 'boolean',
7419faa4 83 default => 0,
aff192e6
DM
84 optional => 1,
85 },
32388c41
FG
86 source => {
87 type => 'string',
88 enum => ['archive', 'active', 'all'],
89 default => 'archive',
90 optional => 1,
91 description => 'List archived, active or all tasks.',
92 },
aff192e6
DM
93 },
94 },
95 returns => {
96 type => 'array',
97 items => {
98 type => "object",
99 properties => {
7419faa4 100 upid => { type => 'string', title => 'UPID', },
f6faf09c
FG
101 node => { type => 'string', title => 'Node', },
102 pid => { type => 'integer', title => 'PID', },
103 pstart => { type => 'integer', },
104 starttime => { type => 'integer', title => 'Starttime', },
105 type => { type => 'string', title => 'Type', },
106 id => { type => 'string', title => 'ID', },
107 user => { type => 'string', title => 'User', },
7419faa4 108 endtime => { type => 'integer', optional => 1, title => 'Endtime', },
f6faf09c 109 status => { type => 'string', optional => 1, title => 'Status', },
aff192e6
DM
110 },
111 },
112 links => [ { rel => 'child', href => "{upid}" } ],
113 },
114 code => sub {
115 my ($param) = @_;
116
117 my $rpcenv = PVE::RPCEnvironment::get();
118 my $user = $rpcenv->get_user();
119
120 my $res = [];
121
122 my $filename = "/var/log/pve/tasks/index";
123
124 my $node = $param->{node};
f0b87b4c
DC
125 my $start = $param->{start} // 0;
126 my $limit = $param->{limit} // 50;
aff192e6 127 my $userfilter = $param->{userfilter};
8dacbee3 128 my $typefilter = $param->{typefilter};
f0b87b4c 129 my $errors = $param->{errors} // 0;
32388c41 130 my $source = $param->{source} // 'archive';
aff192e6
DM
131
132 my $count = 0;
133 my $line;
134
e4d554ba 135 my $auditor = $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ], 1);
aff192e6 136
32388c41
FG
137 my $filter_task = sub {
138 my $task = shift;
139
140 return 1 if $userfilter && $task->{user} !~ m/\Q$userfilter\E/i;
8f8073cb 141 return 1 if !($auditor || $check_task_user->($task, $user));
32388c41 142
8dacbee3
FG
143 return 1 if $typefilter && $task->{type} ne $typefilter;
144
32388c41
FG
145 return 1 if $errors && $task->{status} && $task->{status} eq 'OK';
146 return 1 if $param->{vmid} && (!$task->{id} || $task->{id} ne $param->{vmid});
147
148 return 1 if $count++ < $start;
149 return 1 if $limit <= 0;
150
151 return 0;
152 };
153
aff192e6
DM
154 my $parse_line = sub {
155 if ($line =~ m/^(\S+)(\s([0-9A-Za-z]{8})(\s(\S.*))?)?$/) {
156 my $upid = $1;
157 my $endtime = $3;
158 my $status = $5;
159 if ((my $task = PVE::Tools::upid_decode($upid, 1))) {
aff192e6
DM
160
161 $task->{upid} = $upid;
162 $task->{endtime} = hex($endtime) if $endtime;
163 $task->{status} = $status if $status;
32388c41 164
a901f94a 165 $convert_token_task->($task);
32388c41
FG
166 if (!$filter_task->($task)) {
167 push @$res, $task;
168 $limit--;
169 }
aff192e6
DM
170 }
171 }
172 };
173
32388c41
FG
174 if ($source eq 'active' || $source eq 'all') {
175 my $recent_tasks = PVE::INotify::read_file('active');
176 for my $task (@$recent_tasks) {
177 next if $task->{saved}; # archived task, already in index(.1)
178 if (!$filter_task->($task)) {
179 $task->{status} = 'RUNNING' if !$task->{status}; # otherwise it would be archived
180 push @$res, $task;
181 $limit--;
182 }
aff192e6 183 }
aff192e6 184 }
32388c41
FG
185
186 if ($source ne 'active') {
187 if (my $bw = File::ReadBackwards->new($filename)) {
188 while (defined ($line = $bw->readline)) {
189 &$parse_line();
190 }
191 $bw->close();
192 }
193 if (my $bw = File::ReadBackwards->new("$filename.1")) {
194 while (defined ($line = $bw->readline)) {
195 &$parse_line();
196 }
197 $bw->close();
aff192e6 198 }
aff192e6
DM
199 }
200
e09058af 201 $rpcenv->set_result_attrib('total', $count);
aff192e6
DM
202
203 return $res;
204 }});
205
206__PACKAGE__->register_method({
20663182
DC
207 name => 'upid_index',
208 path => '{upid}',
aff192e6
DM
209 method => 'GET',
210 description => '', # index helper
211 permissions => { user => 'all' },
212 parameters => {
213 additionalProperties => 0,
214 properties => {
215 node => get_standard_option('pve-node'),
216 upid => { type => 'string' },
217 }
218 },
219 returns => {
220 type => 'array',
221 items => {
222 type => "object",
223 properties => {},
224 },
225 links => [ { rel => 'child', href => "{name}" } ],
226 },
227 code => sub {
228 my ($param) = @_;
229
230 return [
231 { name => 'log' },
232 { name => 'status' }
233 ];
234 }});
235
236__PACKAGE__->register_method({
20663182
DC
237 name => 'stop_task',
238 path => '{upid}',
aff192e6
DM
239 method => 'DELETE',
240 description => 'Stop a task.',
20663182 241 permissions => {
00cc94d3 242 description => "The user needs 'Sys.Modify' permissions on '/nodes/<node>' if the task does not belong to him.",
20663182 243 user => 'all',
00cc94d3 244 },
aff192e6
DM
245 protected => 1,
246 proxyto => 'node',
247 parameters => {
248 additionalProperties => 0,
249 properties => {
250 node => get_standard_option('pve-node'),
251 upid => { type => 'string' },
252 }
253 },
254 returns => { type => 'null' },
255 code => sub {
256 my ($param) = @_;
257
258 my ($task, $filename) = PVE::Tools::upid_decode($param->{upid}, 1);
259 raise_param_exc({ upid => "unable to parse worker upid" }) if !$task;
260 raise_param_exc({ upid => "no such task" }) if ! -f $filename;
261
262 my $rpcenv = PVE::RPCEnvironment::get();
263 my $user = $rpcenv->get_user();
264 my $node = $param->{node};
265
a901f94a
FG
266 $convert_token_task->($task);
267
8f8073cb 268 if (!$check_task_user->($task, $user)) {
00cc94d3 269 $rpcenv->check($user, "/nodes/$node", [ 'Sys.Modify' ]);
e4d554ba 270 }
aff192e6 271
f70fc9ac 272 PVE::RPCEnvironment->check_worker($param->{upid}, 1);
aff192e6
DM
273
274 return undef;
275 }});
276
277__PACKAGE__->register_method({
20663182
DC
278 name => 'read_task_log',
279 path => '{upid}/log',
aff192e6 280 method => 'GET',
20663182 281 permissions => {
00cc94d3
DM
282 description => "The user needs 'Sys.Audit' permissions on '/nodes/<node>' if the task does not belong to him.",
283 user => 'all',
284 },
aff192e6
DM
285 protected => 1,
286 description => "Read task log.",
287 proxyto => 'node',
288 parameters => {
289 additionalProperties => 0,
290 properties => {
291 node => get_standard_option('pve-node'),
292 upid => { type => 'string' },
293 start => {
294 type => 'integer',
295 minimum => 0,
7419faa4 296 default => 0,
aff192e6
DM
297 optional => 1,
298 },
299 limit => {
300 type => 'integer',
301 minimum => 0,
7419faa4 302 default => 50,
aff192e6
DM
303 optional => 1,
304 },
305 },
306 },
307 returns => {
308 type => 'array',
20663182 309 items => {
aff192e6
DM
310 type => "object",
311 properties => {
312 n => {
313 description=> "Line number",
314 type=> 'integer',
315 },
316 t => {
317 description=> "Line text",
318 type => 'string',
319 }
320 }
321 }
322 },
323 code => sub {
324 my ($param) = @_;
325
326 my ($task, $filename) = PVE::Tools::upid_decode($param->{upid}, 1);
327 raise_param_exc({ upid => "unable to parse worker upid" }) if !$task;
328
aff192e6
DM
329 my $rpcenv = PVE::RPCEnvironment::get();
330 my $user = $rpcenv->get_user();
331 my $node = $param->{node};
db354940
DC
332 my $start = $param->{start} // 0;
333 my $limit = $param->{limit} // 50;
aff192e6 334
a901f94a
FG
335 $convert_token_task->($task);
336
8f8073cb 337 if (!$check_task_user->($task, $user)) {
e4d554ba
DM
338 $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]);
339 }
aff192e6 340
db354940 341 my ($count, $lines) = PVE::Tools::dump_logfile($filename, $start, $limit);
aff192e6 342
e09058af 343 $rpcenv->set_result_attrib('total', $count);
db354940 344
aff192e6
DM
345 return $lines;
346 }});
347
47a5865a
DM
348
349my $exit_status_cache = {};
350
aff192e6 351__PACKAGE__->register_method({
20663182
DC
352 name => 'read_task_status',
353 path => '{upid}/status',
aff192e6 354 method => 'GET',
20663182 355 permissions => {
00cc94d3
DM
356 description => "The user needs 'Sys.Audit' permissions on '/nodes/<node>' if the task does not belong to him.",
357 user => 'all',
358 },
aff192e6
DM
359 protected => 1,
360 description => "Read task status.",
361 proxyto => 'node',
362 parameters => {
363 additionalProperties => 0,
364 properties => {
365 node => get_standard_option('pve-node'),
366 upid => { type => 'string' },
367 },
368 },
369 returns => {
370 type => "object",
371 properties => {
20663182 372 pid => {
aff192e6
DM
373 type => 'integer'
374 },
20663182
DC
375 status => {
376 type => 'string', enum => ['running', 'stopped'],
aff192e6 377 },
93a9c349
DC
378 type => {
379 type => 'string',
380 },
381 id => {
382 type => 'string',
383 },
384 user => {
385 type => 'string',
386 },
387 exitstatus => {
388 type => 'string',
389 optional => 1,
390 },
391 upid => {
392 type => 'string',
393 },
394 starttime => {
395 type => 'number',
396 },
397 node => {
398 type => 'string',
399 },
aff192e6
DM
400 },
401 },
402 code => sub {
403 my ($param) = @_;
404
405 my ($task, $filename) = PVE::Tools::upid_decode($param->{upid}, 1);
406 raise_param_exc({ upid => "unable to parse worker upid" }) if !$task;
407 raise_param_exc({ upid => "no such task" }) if ! -f $filename;
408
409 my $lines = [];
410
411 my $rpcenv = PVE::RPCEnvironment::get();
412 my $user = $rpcenv->get_user();
413 my $node = $param->{node};
414
a901f94a
FG
415 $convert_token_task->($task);
416
8f8073cb 417 if (!$check_task_user->($task, $user)) {
e4d554ba
DM
418 $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]);
419 }
aff192e6
DM
420
421 my $pstart = PVE::ProcFSTools::read_proc_starttime($task->{pid});
422 $task->{status} = ($pstart && ($pstart == $task->{pstart})) ?
423 'running' : 'stopped';
424
425 $task->{upid} = $param->{upid}; # include upid
426
47a5865a
DM
427 if ($task->{status} eq 'stopped') {
428 if (!defined($exit_status_cache->{$task->{upid}})) {
20663182 429 $exit_status_cache->{$task->{upid}} =
47a5865a
DM
430 PVE::Tools::upid_read_status($task->{upid});
431 }
432 $task->{exitstatus} = $exit_status_cache->{$task->{upid}};
433 }
434
aff192e6
DM
435 return $task;
436 }});
b0f820ab
DM
437
4381;