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