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