]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Tasks.pm
spiceproxy: import PVE::SafeSyslog
[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);
14use PVE::AccessControl;
15
16use base qw(PVE::RESTHandler);
17
18__PACKAGE__->register_method({
19 name => 'node_tasks',
20 path => '',
21 method => 'GET',
00cc94d3
DM
22 permissions => {
23 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).",
24 user => 'all'
25 },
aff192e6
DM
26 description => "Read task list for one node (finished tasks).",
27 proxyto => 'node',
28 parameters => {
29 additionalProperties => 0,
30 properties => {
31 node => get_standard_option('pve-node'),
32 start => {
33 type => 'integer',
34 minimum => 0,
35 optional => 1,
36 },
37 limit => {
38 type => 'integer',
39 minimum => 0,
40 optional => 1,
41 },
42 userfilter => {
43 type => 'string',
44 optional => 1,
45 },
5614bec0
DM
46 vmid => get_standard_option('pve-vmid', {
47 description => "Only list tasks for this VM.",
48 optional => 1
49 }),
aff192e6
DM
50 errors => {
51 type => 'boolean',
52 optional => 1,
53 },
54 },
55 },
56 returns => {
57 type => 'array',
58 items => {
59 type => "object",
60 properties => {
61 upid => { type => 'string' },
62 },
63 },
64 links => [ { rel => 'child', href => "{upid}" } ],
65 },
66 code => sub {
67 my ($param) = @_;
68
69 my $rpcenv = PVE::RPCEnvironment::get();
70 my $user = $rpcenv->get_user();
71
72 my $res = [];
73
74 my $filename = "/var/log/pve/tasks/index";
75
76 my $node = $param->{node};
77 my $start = $param->{start} || 0;
78 my $limit = $param->{limit} || 50;
79 my $userfilter = $param->{userfilter};
80 my $errors = $param->{errors};
81
82 my $count = 0;
83 my $line;
84
e4d554ba 85 my $auditor = $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ], 1);
aff192e6
DM
86
87 my $parse_line = sub {
88 if ($line =~ m/^(\S+)(\s([0-9A-Za-z]{8})(\s(\S.*))?)?$/) {
89 my $upid = $1;
90 my $endtime = $3;
91 my $status = $5;
92 if ((my $task = PVE::Tools::upid_decode($upid, 1))) {
93 return if $userfilter && $task->{user} !~ m/\Q$userfilter\E/i;
92fd515b 94 return if !($auditor || $user eq $task->{user});
aff192e6
DM
95
96 return if $errors && $status && $status eq 'OK';
97
5614bec0
DM
98 return if $param->{vmid} && (!$task->{id} || $task->{id} ne $param->{vmid});
99
aff192e6
DM
100 return if $count++ < $start;
101 return if $limit <= 0;
102
103 $task->{upid} = $upid;
104 $task->{endtime} = hex($endtime) if $endtime;
105 $task->{status} = $status if $status;
106 push @$res, $task;
107 $limit--;
108 }
109 }
110 };
111
112 if (my $bw = File::ReadBackwards->new($filename)) {
113 while (defined ($line = $bw->readline)) {
114 &$parse_line();
115 }
116 $bw->close();
117 }
118 if (my $bw = File::ReadBackwards->new("$filename.1")) {
119 while (defined ($line = $bw->readline)) {
120 &$parse_line();
121 }
122 $bw->close();
123 }
124
e09058af 125 $rpcenv->set_result_attrib('total', $count);
aff192e6
DM
126
127 return $res;
128 }});
129
130__PACKAGE__->register_method({
131 name => 'upid_index',
132 path => '{upid}',
133 method => 'GET',
134 description => '', # index helper
135 permissions => { user => 'all' },
136 parameters => {
137 additionalProperties => 0,
138 properties => {
139 node => get_standard_option('pve-node'),
140 upid => { type => 'string' },
141 }
142 },
143 returns => {
144 type => 'array',
145 items => {
146 type => "object",
147 properties => {},
148 },
149 links => [ { rel => 'child', href => "{name}" } ],
150 },
151 code => sub {
152 my ($param) = @_;
153
154 return [
155 { name => 'log' },
156 { name => 'status' }
157 ];
158 }});
159
160__PACKAGE__->register_method({
161 name => 'stop_task',
162 path => '{upid}',
163 method => 'DELETE',
164 description => 'Stop a task.',
00cc94d3
DM
165 permissions => {
166 description => "The user needs 'Sys.Modify' permissions on '/nodes/<node>' if the task does not belong to him.",
167 user => 'all',
168 },
aff192e6
DM
169 protected => 1,
170 proxyto => 'node',
171 parameters => {
172 additionalProperties => 0,
173 properties => {
174 node => get_standard_option('pve-node'),
175 upid => { type => 'string' },
176 }
177 },
178 returns => { type => 'null' },
179 code => sub {
180 my ($param) = @_;
181
182 my ($task, $filename) = PVE::Tools::upid_decode($param->{upid}, 1);
183 raise_param_exc({ upid => "unable to parse worker upid" }) if !$task;
184 raise_param_exc({ upid => "no such task" }) if ! -f $filename;
185
186 my $rpcenv = PVE::RPCEnvironment::get();
187 my $user = $rpcenv->get_user();
188 my $node = $param->{node};
189
e4d554ba 190 if ($user ne $task->{user}) {
00cc94d3 191 $rpcenv->check($user, "/nodes/$node", [ 'Sys.Modify' ]);
e4d554ba 192 }
aff192e6 193
c5693297 194 PVE::RPCEnvironment::check_worker($param->{upid}, 1);
aff192e6
DM
195
196 return undef;
197 }});
198
199__PACKAGE__->register_method({
200 name => 'read_task_log',
201 path => '{upid}/log',
202 method => 'GET',
00cc94d3
DM
203 permissions => {
204 description => "The user needs 'Sys.Audit' permissions on '/nodes/<node>' if the task does not belong to him.",
205 user => 'all',
206 },
aff192e6
DM
207 protected => 1,
208 description => "Read task log.",
209 proxyto => 'node',
210 parameters => {
211 additionalProperties => 0,
212 properties => {
213 node => get_standard_option('pve-node'),
214 upid => { type => 'string' },
215 start => {
216 type => 'integer',
217 minimum => 0,
218 optional => 1,
219 },
220 limit => {
221 type => 'integer',
222 minimum => 0,
223 optional => 1,
224 },
225 },
226 },
227 returns => {
228 type => 'array',
229 items => {
230 type => "object",
231 properties => {
232 n => {
233 description=> "Line number",
234 type=> 'integer',
235 },
236 t => {
237 description=> "Line text",
238 type => 'string',
239 }
240 }
241 }
242 },
243 code => sub {
244 my ($param) = @_;
245
246 my ($task, $filename) = PVE::Tools::upid_decode($param->{upid}, 1);
247 raise_param_exc({ upid => "unable to parse worker upid" }) if !$task;
248
249 my $lines = [];
250
251 my $rpcenv = PVE::RPCEnvironment::get();
252 my $user = $rpcenv->get_user();
253 my $node = $param->{node};
254
e4d554ba
DM
255 if ($user ne $task->{user}) {
256 $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]);
257 }
aff192e6
DM
258
259 my $fh = IO::File->new($filename, "r");
260 raise_param_exc({ upid => "no such task - unable to open file - $!" }) if !$fh;
261
262 my $start = $param->{start} || 0;
263 my $limit = $param->{limit} || 50;
264 my $count = 0;
265 my $line;
266 while (defined ($line = <$fh>)) {
267 next if $count++ < $start;
268 next if $limit <= 0;
269 chomp $line;
270 push @$lines, { n => $count, t => $line};
271 $limit--;
272 }
273
274 close($fh);
275
276 # HACK: ExtJS store.guaranteeRange() does not like empty array
277 # so we add a line
278 if (!$count) {
279 $count++;
280 push @$lines, { n => $count, t => "no content"};
281 }
282
e09058af 283 $rpcenv->set_result_attrib('total', $count);
aff192e6
DM
284
285 return $lines;
286 }});
287
47a5865a
DM
288
289my $exit_status_cache = {};
290
aff192e6
DM
291__PACKAGE__->register_method({
292 name => 'read_task_status',
293 path => '{upid}/status',
294 method => 'GET',
00cc94d3
DM
295 permissions => {
296 description => "The user needs 'Sys.Audit' permissions on '/nodes/<node>' if the task does not belong to him.",
297 user => 'all',
298 },
aff192e6
DM
299 protected => 1,
300 description => "Read task status.",
301 proxyto => 'node',
302 parameters => {
303 additionalProperties => 0,
304 properties => {
305 node => get_standard_option('pve-node'),
306 upid => { type => 'string' },
307 },
308 },
309 returns => {
310 type => "object",
311 properties => {
312 pid => {
313 type => 'integer'
314 },
315 status => {
316 type => 'string', enum => ['running', 'stopped'],
317 },
318 },
319 },
320 code => sub {
321 my ($param) = @_;
322
323 my ($task, $filename) = PVE::Tools::upid_decode($param->{upid}, 1);
324 raise_param_exc({ upid => "unable to parse worker upid" }) if !$task;
325 raise_param_exc({ upid => "no such task" }) if ! -f $filename;
326
327 my $lines = [];
328
329 my $rpcenv = PVE::RPCEnvironment::get();
330 my $user = $rpcenv->get_user();
331 my $node = $param->{node};
332
e4d554ba
DM
333 if ($user ne $task->{user}) {
334 $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]);
335 }
aff192e6
DM
336
337 my $pstart = PVE::ProcFSTools::read_proc_starttime($task->{pid});
338 $task->{status} = ($pstart && ($pstart == $task->{pstart})) ?
339 'running' : 'stopped';
340
341 $task->{upid} = $param->{upid}; # include upid
342
47a5865a
DM
343 if ($task->{status} eq 'stopped') {
344 if (!defined($exit_status_cache->{$task->{upid}})) {
345 $exit_status_cache->{$task->{upid}} =
346 PVE::Tools::upid_read_status($task->{upid});
347 }
348 $task->{exitstatus} = $exit_status_cache->{$task->{upid}};
349 }
350
aff192e6
DM
351 return $task;
352 }});