File Coverage

blib/lib/FusionInventory/Agent/Task/Deploy.pm
Criterion Covered Total %
statement 39 180 21.6
branch 0 62 0.0
condition 0 6 0.0
subroutine 13 17 76.4
pod 3 3 100.0
total 55 268 20.5


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Task::Deploy;
2              
3             # Full protocol documentation available here:
4             # http://forge.fusioninventory.org/projects/fusioninventory-agent/wiki/API-REST-deploy
5              
6 1     1   16503844 use strict;
  1         7  
  1         80  
7 1     1   10 use warnings;
  1         1  
  1         67  
8 1     1   4 use base 'FusionInventory::Agent::Task';
  1         39  
  1         490  
9              
10 1     1   5 use JSON;
  1         1  
  1         14  
11 1     1   549 use LWP;
  1         31909  
  1         8  
12 1     1   29 use URI::Escape;
  1         2  
  1         54  
13              
14 1     1   414 use FusionInventory::Agent::HTTP::Client::Fusion;
  1         3  
  1         34  
15 1     1   29 use FusionInventory::Agent::Storage;
  1         1  
  1         5  
16 1     1   395 use FusionInventory::Agent::Task::Deploy::ActionProcessor;
  1         3  
  1         11  
17 1     1   387 use FusionInventory::Agent::Task::Deploy::CheckProcessor;
  1         2  
  1         11  
18 1     1   362 use FusionInventory::Agent::Task::Deploy::Datastore;
  1         4  
  1         12  
19 1     1   476 use FusionInventory::Agent::Task::Deploy::File;
  1         2  
  1         10  
20 1     1   367 use FusionInventory::Agent::Task::Deploy::Job;
  1         3  
  1         9  
21              
22             our $VERSION = '2.0.4';
23              
24             sub isEnabled {
25 0     0 1   my ($self) = @_;
26              
27 0 0         if (!$self->{target}->isa('FusionInventory::Agent::Target::Server')) {
28 0           $self->{logger}->debug("Deploy task not compatible with local target");
29 0           return;
30             }
31              
32 0           return 1;
33             }
34              
35             sub _validateAnswer {
36 0     0     my ($msgRef, $answer) = @_;
37              
38 0           $$msgRef = "";
39              
40 0 0         if (!defined($answer)) {
41 0           $$msgRef = "No answer from server.";
42 0           return;
43             }
44              
45 0 0         if (ref($answer) ne 'HASH') {
46 0           $$msgRef = "Bad answer from server. Not a hash reference.";
47 0           return;
48             }
49              
50 0 0         if (!defined($answer->{associatedFiles})) {
51 0           $$msgRef = "missing associatedFiles key";
52 0           return;
53             }
54              
55 0 0         if (ref($answer->{associatedFiles}) ne 'HASH') {
56 0           $$msgRef = "associatedFiles should be an hash";
57 0           return;
58             }
59 0           foreach my $k (keys %{$answer->{associatedFiles}}) {
  0            
60 0           foreach (qw/mirrors multiparts name p2p-retention-duration p2p uncompress/) {
61 0 0         if (!defined($answer->{associatedFiles}->{$k}->{$_})) {
62 0           $$msgRef = "Missing key `$_' in associatedFiles";
63 0           return;
64             }
65             }
66             }
67 0           foreach my $job (@{$answer->{jobs}}) {
  0            
68 0           foreach (qw/uuid associatedFiles actions checks/) {
69 0 0         if (!defined($job->{$_})) {
70 0           $$msgRef = "Missing key `$_' in jobs";
71 0           return;
72             }
73              
74 0 0         if (ref($job->{actions}) ne 'ARRAY') {
75 0           $$msgRef = "jobs/actions must be an array";
76 0           return;
77             }
78             }
79             }
80              
81 0           return 1;
82             }
83              
84             sub processRemote {
85 0     0 1   my ($self, $remoteUrl) = @_;
86              
87 0 0         if ( !$remoteUrl ) {
88 0           return;
89             }
90              
91 0           my $datastore = FusionInventory::Agent::Task::Deploy::Datastore->new(
92             path => $self->{target}{storage}{directory}.'/deploy',
93             logger => $self->{logger}
94             );
95 0           $datastore->cleanUp();
96              
97 0           my $jobList = [];
98 0           my $files;
99              
100 0           my $answer = $self->{client}->send(
101             url => $remoteUrl,
102             args => {
103             action => "getJobs",
104             machineid => $self->{deviceid},
105             }
106             );
107 0 0 0       if (ref($answer) eq 'HASH' && !keys %$answer) {
108 0           $self->{logger}->debug("Nothing to do");
109 0           return;
110             }
111              
112 0           my $msg;
113 0 0         if (!_validateAnswer(\$msg, $answer)) {
114 0           $self->{logger}->debug("bad JSON: ".$msg);
115 0           return;
116             }
117              
118 0           foreach my $sha512 ( keys %{ $answer->{associatedFiles} } ) {
  0            
119 0           $files->{$sha512} = FusionInventory::Agent::Task::Deploy::File->new(
120             client => $self->{client},
121             sha512 => $sha512,
122             data => $answer->{associatedFiles}{$sha512},
123             datastore => $datastore,
124             logger => $self->{logger}
125             );
126             }
127              
128 0           foreach ( @{ $answer->{jobs} } ) {
  0            
129 0           my $associatedFiles = [];
130 0 0         if ( $_->{associatedFiles} ) {
131 0           foreach my $uuid ( @{ $_->{associatedFiles} } ) {
  0            
132 0 0         if ( !$files->{$uuid} ) {
133 0           die "unknow file: `" . $uuid
134             . "'. Not found in JSON answer!";
135             }
136 0           push @$associatedFiles, $files->{$uuid};
137             }
138             }
139 0           push @$jobList,
140             FusionInventory::Agent::Task::Deploy::Job->new(
141             data => $_,
142             associatedFiles => $associatedFiles
143             );
144             }
145              
146 0           JOB: foreach my $job (@$jobList) {
147              
148             # RECEIVED
149 0           $self->{client}->send(
150             url => $remoteUrl,
151             args => {
152             action => "setStatus",
153             machineid => $self->{deviceid},
154             part => 'job',
155             uuid => $job->{uuid},
156             currentStep => 'checking',
157             msg => 'starting'
158             }
159             );
160              
161             # CHECKING
162 0 0         if ( ref( $job->{checks} ) eq 'ARRAY' ) {
163 0           foreach my $checknum ( 0 .. @{ $job->{checks} } ) {
  0            
164 0 0         next unless $job->{checks}[$checknum];
165 0           my $checkStatus = FusionInventory::Agent::Task::Deploy::CheckProcessor->process(
166             check => $job->{checks}[$checknum],
167             logger => $self->{logger}
168             );
169 0 0         next if $checkStatus eq "ok";
170 0 0         next if $checkStatus eq "ignore";
171              
172 0           $self->{client}->send(
173             url => $remoteUrl,
174             args => {
175             action => "setStatus",
176             machineid => $self->{deviceid},
177             part => 'job',
178             uuid => $job->{uuid},
179             currentStep => 'checking',
180             status => 'ko',
181             msg => "failure of check #".($checknum+1)." ($checkStatus)",
182             cheknum => $checknum
183             }
184             );
185              
186 0           next JOB;
187             }
188             }
189              
190 0           $self->{client}->send(
191             url => $remoteUrl,
192             args => {
193             action => "setStatus",
194             machineid => $self->{deviceid},
195             part => 'job',
196             uuid => $job->{uuid},
197             currentStep => 'checking',
198             status => 'ok',
199             msg => 'all checks are ok'
200             }
201             );
202              
203              
204             # DOWNLOADING
205              
206 0           $self->{client}->send(
207             url => $remoteUrl,
208             args => {
209             action => "setStatus",
210             machineid => $self->{deviceid},
211             part => 'job',
212             uuid => $job->{uuid},
213             currentStep => 'downloading',
214             msg => 'downloading files'
215             }
216             );
217              
218 0           my $retry = 5;
219 0           my $workdir = $datastore->createWorkDir( $job->{uuid} );
220 0           FETCHFILE: foreach my $file ( @{ $job->{associatedFiles} } ) {
  0            
221              
222             # File exists, no need to download
223 0 0         if ( $file->filePartsExists() ) {
224 0           $self->{client}->send(
225             url => $remoteUrl,
226             args => {
227             action => "setStatus",
228             machineid => $self->{deviceid},
229             part => 'file',
230             uuid => $job->{uuid},
231             sha512 => $file->{sha512},
232             status => 'ok',
233             currentStep=> 'downloading',
234             msg => $file->{name}.' already downloaded'
235             }
236             );
237              
238 0           $workdir->addFile($file);
239 0           next;
240             }
241              
242             # File doesn't exist, lets try or retry a download
243 0           $self->{client}->send(
244             url => $remoteUrl,
245             args => {
246             action => "setStatus",
247             machineid => $self->{deviceid},
248             part => 'file',
249             uuid => $job->{uuid},
250             sha512 => $file->{sha512},
251             currentStep => 'downloading',
252             msg => 'fetching '.$file->{name}
253             }
254             );
255              
256 0           $file->download();
257              
258             # Are all the fileparts here?
259 0           my $downloadIsOK = $file->filePartsExists();
260              
261 0 0         if ( $downloadIsOK ) {
262              
263 0           $self->{client}->send(
264             url => $remoteUrl,
265             args => {
266             action => "setStatus",
267             machineid => $self->{deviceid},
268             part => 'file',
269             uuid => $job->{uuid},
270             sha512 => $file->{sha512},
271             currentStep => 'downloading',
272             status => 'ok',
273             msg => $file->{name}.' downloaded'
274             }
275             );
276              
277 0           $workdir->addFile($file);
278 0           next;
279             }
280              
281             # Retry the download 5 times in a row and then give up
282 0 0         if ( !$downloadIsOK ) {
283              
284 0 0         if ($retry--) { # Retry
285             # OK, retry!
286 0           $self->{client}->send(
287             url => $remoteUrl,
288             args => {
289             action => "setStatus",
290             machineid => $self->{deviceid},
291             part => 'file',
292             uuid => $job->{uuid},
293             sha512 => $file->{sha512},
294             currentStep => 'downloading',
295             msg => 'retrying '.$file->{name}
296             }
297             );
298              
299 0           redo FETCHFILE;
300             } else { # Give up...
301              
302 0           $self->{client}->send(
303             url => $remoteUrl,
304             args => {
305             action => "setStatus",
306             machineid => $self->{deviceid},
307             part => 'file',
308             uuid => $job->{uuid},
309             sha512 => $file->{sha512},
310             currentStep => 'downloading',
311             status => 'ko',
312             msg => $file->{name}.' download failed'
313             }
314             );
315              
316 0           next JOB;
317             }
318             }
319              
320             }
321              
322              
323 0           $self->{client}->send(
324             url => $remoteUrl,
325             args => {
326             action => "setStatus",
327             machineid => $self->{deviceid},
328             part => 'job',
329             uuid => $job->{uuid},
330             currentStep => 'downloading',
331             status => 'ok',
332             msg => 'success'
333             }
334             );
335              
336             # # CHECKING
337             # if (!$job->checkWinkey()) {
338             # $self->setStatus({ machineid => 'DEVICEID', part => 'job', uuid => $job->{uuid}, status => 'ko', msg => 'rejected because of a Windows registry check' });
339             # next JOB;
340             # } elsif (!$job->checkFreespace()) {
341             # $self->setStatus({ machineid => 'DEVICEID', part => 'job', uuid => $job->{uuid}, status => 'ko', msg => 'rejected because of harddrive free space' });
342             # next JOB;
343             # }
344              
345 0 0         if (!$workdir->prepare()) {
346 0           $self->{client}->send(
347             url => $remoteUrl,
348             args => {
349             action => "setStatus",
350             machineid => $self->{deviceid},
351             part => 'job',
352             uuid => $job->{uuid},
353             currentStep => 'prepare',
354             status => 'ko',
355             msg => 'failed to prepare work dir'
356             }
357             );
358 0           next JOB;
359             } else {
360 0           $self->{client}->send(
361             url => $remoteUrl,
362             args => {
363             action => "setStatus",
364             machineid => $self->{deviceid},
365             part => 'job',
366             uuid => $job->{uuid},
367             currentStep => 'prepare',
368             status => 'ok',
369             msg => 'success'
370             }
371             );
372             }
373              
374             # PROCESSING
375             # $self->{client}->send(
376             # url => $remoteUrl,
377             # args => {
378             # action => "setStatus",
379             # machineid => 'DEVICEID',
380             # part => 'job',
381             # uuid => $job->{uuid},
382             # currentStep => 'processing'
383             # }
384             # );
385 0           my $actionProcessor =
386             FusionInventory::Agent::Task::Deploy::ActionProcessor->new(
387             workdir => $workdir
388             );
389 0           my $actionnum = 0;
390 0           ACTION: while ( my $action = $job->getNextToProcess() ) {
391 0           my ($actionName, $params) = %$action;
392 0 0 0       if ( $params && (ref( $params->{checks} ) eq 'ARRAY') ) {
393 0           foreach my $checknum ( 0 .. @{ $params->{checks} } ) {
  0            
394 0 0         next unless $job->{checks}[$checknum];
395 0           my $checkStatus = FusionInventory::Agent::Task::Deploy::CheckProcessor->process(
396             check => $params->{checks}[$checknum],
397             logger => $self->{logger}
398              
399             );
400 0 0         if ( $checkStatus ne 'ok') {
401              
402 0           $self->{client}->send(
403             url => $remoteUrl,
404             args => {
405             action => "setStatus",
406             machineid => $self->{deviceid},
407             part => 'job',
408             uuid => $job->{uuid},
409             currentStep => 'checking',
410             status => $checkStatus,
411             msg => "failure of check #".($checknum+1)." ($checkStatus)",
412             actionnum => $actionnum,
413             cheknum => $checknum
414             }
415             );
416              
417 0           next ACTION;
418             }
419             }
420             }
421              
422              
423 0           my $ret;
424 0           eval { $ret = $actionProcessor->process($actionName, $params, $self->{logger}); };
  0            
425 0 0         $ret->{msg} = [] unless $ret->{msg};
426 0 0         push @{$ret->{msg}}, $@ if $@;
  0            
427 0 0         if ( !$ret->{status} ) {
428 0           $self->{client}->send(
429             url => $remoteUrl,
430             args => {
431             action => "setStatus",
432             machineid => $self->{deviceid},
433             uuid => $job->{uuid},
434             msg => $ret->{msg},
435             actionnum => $actionnum,
436             }
437             );
438              
439 0           $self->{client}->send(
440             url => $remoteUrl,
441             args => {
442             action => "setStatus",
443             machineid => $self->{deviceid},
444             part => 'job',
445             uuid => $job->{uuid},
446             currentStep => 'processing',
447             status => 'ko',
448             actionnum => $actionnum,
449             msg => "action #".($actionnum+1)." processing failure"
450             }
451             );
452              
453 0           next JOB;
454             }
455 0           $self->{client}->send(
456             url => $remoteUrl,
457             args => {
458             action => "setStatus",
459             machineid => $self->{deviceid},
460             part => 'job',
461             uuid => $job->{uuid},
462             currentStep => 'processing',
463             status => 'ok',
464             actionnum => $actionnum,
465             msg => "action #".($actionnum+1)." processing success"
466             }
467             );
468              
469 0           $actionnum++;
470             }
471              
472 0           $self->{client}->send(
473             url => $remoteUrl,
474             args => {
475             action => "setStatus",
476             machineid => $self->{deviceid},
477             part => 'job',
478             uuid => $job->{uuid},
479             status => 'ok',
480             msg => "job successfully completed"
481             }
482             );
483             }
484              
485 0           $datastore->cleanUp();
486 0           1;
487             }
488              
489              
490             sub run {
491 0     0 1   my ($self, %params) = @_;
492              
493             # Turn off localised output for commands
494 0           $ENV{LC_ALL} = 'C'; # Turn off localised output for commands
495 0           $ENV{LANG} = 'C'; # Turn off localised output for commands
496              
497 0           $self->{client} = FusionInventory::Agent::HTTP::Client::Fusion->new(
498             logger => $self->{logger},
499             user => $params{user},
500             password => $params{password},
501             proxy => $params{proxy},
502             ca_cert_file => $params{ca_cert_file},
503             ca_cert_dir => $params{ca_cert_dir},
504             no_ssl_check => $params{no_ssl_check},
505             debug => $self->{debug}
506             );
507              
508 0           my $globalRemoteConfig = $self->{client}->send(
509             url => $self->{target}->{url},
510             args => {
511             action => "getConfig",
512             machineid => $self->{deviceid},
513             task => { Deploy => $VERSION },
514             }
515             );
516              
517 0 0         return unless $globalRemoteConfig->{schedule};
518 0 0         return unless ref( $globalRemoteConfig->{schedule} ) eq 'ARRAY';
519              
520 0           foreach my $job ( @{ $globalRemoteConfig->{schedule} } ) {
  0            
521 0 0         next unless $job->{task} eq "Deploy";
522 0           $self->processRemote($job->{remote});
523             }
524              
525 0           return 1;
526             }
527              
528             __END__