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