File Coverage

blib/lib/FusionInventory/Agent/Task/Deploy.pm
Criterion Covered Total %
statement 66 180 36.6
branch 12 62 19.3
condition 0 6 0.0
subroutine 14 17 82.3
pod 3 3 100.0
total 95 268 35.4


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 2     2   23106560 use strict;
  2         6  
  2         89  
7 2     2   12 use warnings;
  2         7  
  2         122  
8 2     2   11 use base 'FusionInventory::Agent::Task';
  2         59  
  2         1212  
9              
10 2     2   12 use JSON;
  2         4  
  2         48  
11 2     2   1939 use LWP;
  2         97598  
  2         25  
12 2     2   70 use URI::Escape;
  2         4  
  2         159  
13              
14 2     2   1323 use FusionInventory::Agent::HTTP::Client::Fusion;
  2         8  
  2         28  
15 2     2   66 use FusionInventory::Agent::Storage;
  2         4  
  2         24  
16 2     2   1328 use FusionInventory::Agent::Task::Deploy::ActionProcessor;
  2         7  
  2         22  
17 2     2   1265 use FusionInventory::Agent::Task::Deploy::CheckProcessor;
  2         5  
  2         26  
18 2     2   1265 use FusionInventory::Agent::Task::Deploy::Datastore;
  2         7  
  2         25  
19 2     2   1446 use FusionInventory::Agent::Task::Deploy::File;
  2         7  
  2         26  
20 2     2   1185 use FusionInventory::Agent::Task::Deploy::Job;
  2         6  
  2         24  
21              
22             our $VERSION = '2.0.4';
23              
24             sub isEnabled {
25 0     0 1 0 my ($self) = @_;
26              
27 0 0       0 if (!$self->{target}->isa('FusionInventory::Agent::Target::Server')) {
28 0         0 $self->{logger}->debug("Deploy task not compatible with local target");
29 0         0 return;
30             }
31              
32 0         0 return 1;
33             }
34              
35             sub _validateAnswer {
36 7     7   6428 my ($msgRef, $answer) = @_;
37              
38 7         15 $$msgRef = "";
39              
40 7 100       21 if (!defined($answer)) {
41 1         2 $$msgRef = "No answer from server.";
42 1         4 return;
43             }
44              
45 6 100       18 if (ref($answer) ne 'HASH') {
46 1         2 $$msgRef = "Bad answer from server. Not a hash reference.";
47 1         4 return;
48             }
49              
50 5 100       15 if (!defined($answer->{associatedFiles})) {
51 1         2 $$msgRef = "missing associatedFiles key";
52 1         4 return;
53             }
54              
55 4 50       12 if (ref($answer->{associatedFiles}) ne 'HASH') {
56 0         0 $$msgRef = "associatedFiles should be an hash";
57 0         0 return;
58             }
59 4         6 foreach my $k (keys %{$answer->{associatedFiles}}) {
  4         14  
60 2         6 foreach (qw/mirrors multiparts name p2p-retention-duration p2p uncompress/) {
61 6 100       17 if (!defined($answer->{associatedFiles}->{$k}->{$_})) {
62 2         7 $$msgRef = "Missing key `$_' in associatedFiles";
63 2         6 return;
64             }
65             }
66             }
67 2         5 foreach my $job (@{$answer->{jobs}}) {
  2         6  
68 2         5 foreach (qw/uuid associatedFiles actions checks/) {
69 2 100       7 if (!defined($job->{$_})) {
70 1         4 $$msgRef = "Missing key `$_' in jobs";
71 1         5 return;
72             }
73              
74 1 50       5 if (ref($job->{actions}) ne 'ARRAY') {
75 1         2 $$msgRef = "jobs/actions must be an array";
76 1         3 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             my $datastore = FusionInventory::Agent::Task::Deploy::Datastore->new(
92             path => $self->{target}{storage}{directory}.'/deploy',
93             logger => $self->{logger}
94 0           );
95 0           $datastore->cleanUp();
96              
97 0           my $jobList = [];
98 0           my $files;
99              
100             my $answer = $self->{client}->send(
101             url => $remoteUrl,
102             args => {
103             action => "getJobs",
104             machineid => $self->{deviceid},
105             }
106 0           );
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             $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 0           );
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             $self->{client}->send(
150             url => $remoteUrl,
151             args => {
152             action => "setStatus",
153             machineid => $self->{deviceid},
154             part => 'job',
155             uuid => $job->{uuid},
156 0           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             my $checkStatus = FusionInventory::Agent::Task::Deploy::CheckProcessor->process(
166             check => $job->{checks}[$checknum],
167             logger => $self->{logger}
168 0           );
169 0 0         next if $checkStatus eq "ok";
170 0 0         next if $checkStatus eq "ignore";
171              
172             $self->{client}->send(
173             url => $remoteUrl,
174             args => {
175             action => "setStatus",
176             machineid => $self->{deviceid},
177             part => 'job',
178             uuid => $job->{uuid},
179 0           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             $self->{client}->send(
191             url => $remoteUrl,
192             args => {
193             action => "setStatus",
194             machineid => $self->{deviceid},
195             part => 'job',
196             uuid => $job->{uuid},
197 0           currentStep => 'checking',
198             status => 'ok',
199             msg => 'all checks are ok'
200             }
201             );
202              
203              
204             # DOWNLOADING
205              
206             $self->{client}->send(
207             url => $remoteUrl,
208             args => {
209             action => "setStatus",
210             machineid => $self->{deviceid},
211             part => 'job',
212             uuid => $job->{uuid},
213 0           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             $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 0           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             $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 0           );
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             $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 0           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             $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 0           );
298              
299 0           redo FETCHFILE;
300             } else { # Give up...
301              
302             $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 0           msg => $file->{name}.' download failed'
313             }
314             );
315              
316 0           next JOB;
317             }
318             }
319              
320             }
321              
322              
323             $self->{client}->send(
324             url => $remoteUrl,
325             args => {
326             action => "setStatus",
327             machineid => $self->{deviceid},
328             part => 'job',
329             uuid => $job->{uuid},
330 0           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             $self->{client}->send(
347             url => $remoteUrl,
348             args => {
349             action => "setStatus",
350             machineid => $self->{deviceid},
351             part => 'job',
352             uuid => $job->{uuid},
353 0           currentStep => 'prepare',
354             status => 'ko',
355             msg => 'failed to prepare work dir'
356             }
357             );
358 0           next JOB;
359             } else {
360             $self->{client}->send(
361             url => $remoteUrl,
362             args => {
363             action => "setStatus",
364             machineid => $self->{deviceid},
365             part => 'job',
366             uuid => $job->{uuid},
367 0           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             my $checkStatus = FusionInventory::Agent::Task::Deploy::CheckProcessor->process(
396             check => $params->{checks}[$checknum],
397             logger => $self->{logger}
398              
399 0           );
400 0 0         if ( $checkStatus ne 'ok') {
401              
402             $self->{client}->send(
403             url => $remoteUrl,
404             args => {
405             action => "setStatus",
406             machineid => $self->{deviceid},
407             part => 'job',
408             uuid => $job->{uuid},
409 0           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             $self->{client}->send(
429             url => $remoteUrl,
430             args => {
431             action => "setStatus",
432             machineid => $self->{deviceid},
433             uuid => $job->{uuid},
434             msg => $ret->{msg},
435 0           actionnum => $actionnum,
436             }
437             );
438              
439             $self->{client}->send(
440             url => $remoteUrl,
441             args => {
442             action => "setStatus",
443             machineid => $self->{deviceid},
444             part => 'job',
445             uuid => $job->{uuid},
446 0           currentStep => 'processing',
447             status => 'ko',
448             actionnum => $actionnum,
449             msg => "action #".($actionnum+1)." processing failure"
450             }
451             );
452              
453 0           next JOB;
454             }
455             $self->{client}->send(
456             url => $remoteUrl,
457             args => {
458             action => "setStatus",
459             machineid => $self->{deviceid},
460             part => 'job',
461             uuid => $job->{uuid},
462 0           currentStep => 'processing',
463             status => 'ok',
464             actionnum => $actionnum,
465             msg => "action #".($actionnum+1)." processing success"
466             }
467             );
468              
469 0           $actionnum++;
470             }
471              
472             $self->{client}->send(
473             url => $remoteUrl,
474             args => {
475             action => "setStatus",
476             machineid => $self->{deviceid},
477             part => 'job',
478             uuid => $job->{uuid},
479 0           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             $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 0           );
507              
508             my $globalRemoteConfig = $self->{client}->send(
509             url => $self->{target}->{url},
510             args => {
511             action => "getConfig",
512             machineid => $self->{deviceid},
513 0           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__