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