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