File Coverage

blib/lib/FusionInventory/Agent/Task/Inventory.pm
Criterion Covered Total %
statement 33 182 18.1
branch 0 74 0.0
condition 0 12 0.0
subroutine 11 18 61.1
pod 2 2 100.0
total 46 288 15.9


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Task::Inventory;
2              
3 3     3   21170354 use strict;
  3         9  
  3         143  
4 3     3   49 use warnings;
  3         6  
  3         222  
5 3     3   29 use base 'FusionInventory::Agent::Task';
  3         106  
  3         2207  
6              
7 3     3   17 use Config;
  3         6  
  3         136  
8 3     3   16 use English qw(-no_match_vars);
  3         6  
  3         19  
9 3     3   1457 use UNIVERSAL::require;
  3         6  
  3         20  
10              
11 3     3   71 use FusionInventory::Agent::Tools;
  3         6  
  3         537  
12 3     3   1895 use FusionInventory::Agent::Inventory;
  3         9  
  3         49  
13 3     3   1993 use FusionInventory::Agent::XML::Query::Inventory;
  3         9  
  3         33  
14              
15             our $VERSION = '1.0';
16              
17             sub isEnabled {
18 0     0 1   my ($self, $response) = @_;
19              
20             # always enabled for local target
21             return 1 unless
22 0 0         $self->{target}->isa('FusionInventory::Agent::Target::Server');
23              
24 0           my $content = $response->getContent();
25 0 0 0       if (!$content || !$content->{RESPONSE} || $content->{RESPONSE} ne 'SEND') {
      0        
26 0 0         if ($self->{config}->{force}) {
27 0           $self->{logger}->debug("Inventory task execution not requested, but execution forced");
28             } else {
29 0           $self->{logger}->debug("Inventory task execution not requested");
30 0           return;
31             }
32             }
33              
34 0           $self->{registry} = [ $response->getOptionsInfoByName('REGISTRY') ];
35 0           return 1;
36             }
37              
38             sub run {
39 0     0 1   my ($self, %params) = @_;
40              
41 0 0         if ( $REAL_USER_ID != 0 ) {
42             $self->{logger}->warning(
43 0           "You should execute this task as super-user"
44             );
45             }
46              
47 0           $self->{modules} = {};
48              
49             my $inventory = FusionInventory::Agent::Inventory->new(
50             statedir => $self->{target}->getStorage()->getDirectory(),
51             logger => $self->{logger},
52 0           tag => $self->{config}->{'tag'}
53             );
54              
55 0 0         if (not $ENV{PATH}) {
56             # set a minimal PATH if none is set (#1129, #1747)
57             $ENV{PATH} =
58 0           '/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin';
59             $self->{logger}->debug(
60 0           "PATH is not set, using $ENV{PATH} as default"
61             );
62             }
63              
64 0           my %disabled = map { $_ => 1 } @{$self->{config}->{'no-category'}};
  0            
  0            
65              
66 0           $self->_initModulesList(\%disabled);
67 0           $self->_feedInventory($inventory, \%disabled);
68              
69 0 0         if ($self->{target}->isa('FusionInventory::Agent::Target::Local')) {
    0          
70 0           my $path = $self->{target}->getPath();
71 0           my $format = $self->{target}->{format};
72 0           my ($file, $handle);
73              
74             SWITCH: {
75 0 0         if ($path eq '-') {
  0            
76 0           $handle = \*STDOUT;
77 0           last SWITCH;
78             }
79              
80 0 0         if (-d $path) {
81             $file =
82             $path . "/" . $self->{deviceid} .
83 0 0         ($format eq 'xml' ? '.ocs' : '.html');
84 0           last SWITCH;
85             }
86              
87 0           $file = $path;
88             }
89              
90 0 0         if ($file) {
91 0 0         if (Win32::Unicode::File->require()) {
92 0           $handle = Win32::Unicode::File->new('w', $file);
93             } else {
94 0           open($handle, '>', $file);
95             }
96 0 0         $self->{logger}->error("Can't write to $file: $ERRNO")
97             unless $handle;
98             }
99              
100 0           binmode $handle, ':encoding(UTF-8)';
101              
102 0           $self->_printInventory(
103             inventory => $inventory,
104             handle => $handle,
105             format => $format
106             );
107              
108 0 0         if ($file) {
109 0           $self->{logger}->info("Inventory saved in $file");
110 0           close $handle;
111             }
112              
113             } elsif ($self->{target}->isa('FusionInventory::Agent::Target::Server')) {
114             my $client = FusionInventory::Agent::HTTP::Client::OCS->new(
115             logger => $self->{logger},
116             user => $params{user},
117             password => $params{password},
118             proxy => $params{proxy},
119             ca_cert_file => $params{ca_cert_file},
120             ca_cert_dir => $params{ca_cert_dir},
121             no_ssl_check => $params{no_ssl_check},
122 0           );
123              
124             my $message = FusionInventory::Agent::XML::Query::Inventory->new(
125             deviceid => $self->{deviceid},
126 0           content => $inventory->getContent()
127             );
128              
129             my $response = $client->send(
130 0           url => $self->{target}->getUrl(),
131             message => $message
132             );
133              
134 0 0         return unless $response;
135 0           $inventory->saveLastState();
136              
137             }
138              
139             }
140              
141             sub _initModulesList {
142 0     0     my ($self, $disabled) = @_;
143              
144 0           my $logger = $self->{logger};
145 0           my $config = $self->{config};
146              
147 0           my @modules = __PACKAGE__->getModules('');
148 0 0         die "no inventory module found" if !@modules;
149              
150             # first pass: compute all relevant modules
151 0           foreach my $module (sort @modules) {
152             # compute parent module:
153 0           my @components = split('::', $module);
154 0 0         my $parent = @components > 5 ?
155             join('::', @components[0 .. $#components -1]) : '';
156              
157             # skip if parent is not allowed
158 0 0 0       if ($parent && !$self->{modules}->{$parent}->{enabled}) {
159 0           $logger->debug2(" $module disabled: implicit dependency $parent not enabled");
160 0           $self->{modules}->{$module}->{enabled} = 0;
161 0           next;
162             }
163              
164 0           $module->require();
165 0 0         if ($EVAL_ERROR) {
166 0           $logger->debug("module $module disabled: failure to load ($EVAL_ERROR)");
167 0           $self->{modules}->{$module}->{enabled} = 0;
168 0           next;
169             }
170              
171             my $enabled = runFunction(
172             module => $module,
173             function => "isEnabled",
174             logger => $logger,
175             timeout => $config->{'backend-collect-timeout'},
176             params => {
177             no_category => $disabled,
178             datadir => $self->{datadir},
179             logger => $self->{logger},
180             registry => $self->{registry},
181             scan_homedirs => $self->{config}->{'scan-homedirs'},
182 0           scan_profiles => $self->{config}->{'scan-profiles'},
183             }
184             );
185 0 0         if (!$enabled) {
186 0           $logger->debug2("module $module disabled");
187 0           $self->{modules}->{$module}->{enabled} = 0;
188 0           next;
189             }
190              
191 0           $self->{modules}->{$module}->{enabled} = 1;
192 0           $self->{modules}->{$module}->{done} = 0;
193 0           $self->{modules}->{$module}->{used} = 0;
194              
195 3     3   3067 no strict 'refs'; ## no critic (ProhibitNoStrict)
  3         6  
  3         256  
196             $self->{modules}->{$module}->{runAfter} = [
197             $parent ? $parent : (),
198 0 0         ${$module . '::runAfter'} ? @${$module . '::runAfter'} : ()
  0 0          
  0            
199             ];
200             }
201              
202             # second pass: disable fallback modules
203 0           foreach my $module (@modules) {
204             ## no critic (ProhibitProlongedStrictureOverride)
205 3     3   19 no strict 'refs'; ## no critic (ProhibitNoStrict)
  3         5  
  3         3210  
206              
207             # skip modules already disabled
208 0 0         next unless $self->{modules}->{$module}->{enabled};
209             # skip non-fallback modules
210 0 0         next unless ${$module . '::runMeIfTheseChecksFailed'};
  0            
211              
212 0           my $failed;
213              
214 0           foreach my $other_module (@${$module . '::runMeIfTheseChecksFailed'}) {
  0            
215 0 0         if ($self->{modules}->{$other_module}->{enabled}) {
216 0           $failed = $other_module;
217 0           last;
218             }
219             }
220              
221 0 0         if ($failed) {
222 0           $self->{modules}->{$module}->{enabled} = 0;
223 0           $logger->debug("module $module disabled because of $failed");
224             }
225             }
226             }
227              
228             sub _runModule {
229 0     0     my ($self, $module, $inventory, $disabled) = @_;
230              
231 0           my $logger = $self->{logger};
232              
233 0 0         return if $self->{modules}->{$module}->{done};
234              
235 0           $self->{modules}->{$module}->{used} = 1; # lock the module
236              
237             # ensure all needed modules have been executed first
238 0           foreach my $other_module (@{$self->{modules}->{$module}->{runAfter}}) {
  0            
239             die "module $other_module, needed before $module, not found"
240 0 0         if !$self->{modules}->{$other_module};
241              
242             die "module $other_module, needed before $module, not enabled"
243 0 0         if !$self->{modules}->{$other_module}->{enabled};
244              
245             die "circular dependency between $module and $other_module"
246 0 0         if $self->{modules}->{$other_module}->{used};
247              
248 0           $self->_runModule($other_module, $inventory, $disabled);
249             }
250              
251 0           $logger->debug("Running $module");
252              
253             runFunction(
254             module => $module,
255             function => "doInventory",
256             logger => $logger,
257             timeout => $self->{config}->{'backend-collect-timeout'},
258             params => {
259             datadir => $self->{datadir},
260             inventory => $inventory,
261             no_category => $disabled,
262             logger => $self->{logger},
263             registry => $self->{registry},
264             scan_homedirs => $self->{config}->{'scan-homedirs'},
265 0           scan_profiles => $self->{config}->{'scan-profiles'},
266             }
267             );
268 0           $self->{modules}->{$module}->{done} = 1;
269 0           $self->{modules}->{$module}->{used} = 0; # unlock the module
270             }
271              
272             sub _feedInventory {
273 0     0     my ($self, $inventory, $disabled) = @_;
274              
275 0           my $begin = time();
276             my @modules =
277 0           grep { $self->{modules}->{$_}->{enabled} }
278 0           keys %{$self->{modules}};
  0            
279              
280 0           foreach my $module (sort @modules) {
281 0           $self->_runModule($module, $inventory, $disabled);
282             }
283              
284 0 0         if (-d $self->{confdir} . '/softwares') {
285             $self->{logger}->info(
286 0           "using custom scripts for adding softwares to inventory is " .
287             "deprecated, use --additional-content option insted"
288             );
289             }
290              
291 0 0 0       if ($self->{config}->{'additional-content'} && -f $self->{config}->{'additional-content'}) {
292 0           $self->_injectContent($self->{config}->{'additional-content'}, $inventory)
293             }
294              
295             # Execution time
296 0           $inventory->setHardware({ETIME => time() - $begin});
297              
298 0           $inventory->computeLegacyValues();
299 0           $inventory->computeChecksum();
300             }
301              
302             sub _injectContent {
303 0     0     my ($self, $file, $inventory) = @_;
304              
305 0 0         return unless -f $file;
306              
307             $self->{logger}->debug(
308 0           "importing $file file content to the inventory"
309             );
310              
311 0           my $content;
312             SWITCH: {
313 0 0         if ($file =~ /\.xml$/) {
  0            
314 0           eval {
315 0           my $tree = XML::TreePP->new()->parsefile($file);
316 0           $content = $tree->{REQUEST}->{CONTENT};
317             };
318 0           last SWITCH;
319             }
320 0           die "unknown file type $file";
321             }
322              
323 0 0         if (!$content) {
324 0           $self->{logger}->error("no suitable content found");
325 0           return;
326             }
327              
328 0           $inventory->mergeContent($content);
329             }
330              
331             sub _printInventory {
332 0     0     my ($self, %params) = @_;
333              
334             SWITCH: {
335 0 0         if ($params{format} eq 'xml') {
  0            
336              
337 0           my $tpp = XML::TreePP->new(
338             indent => 2,
339             utf8_flag => 1,
340             output_encoding => 'UTF-8'
341             );
342 0           print {$params{handle}} $tpp->write({
343             REQUEST => {
344             CONTENT => $params{inventory}->{content},
345             DEVICEID => $self->{deviceid},
346 0           QUERY => "INVENTORY",
347             }
348             });
349              
350 0           last SWITCH;
351             }
352              
353 0 0         if ($params{format} eq 'html') {
354 0           Text::Template->require();
355 0           my $template = Text::Template->new(
356             TYPE => 'FILE', SOURCE => "$self->{datadir}/html/inventory.tpl"
357             );
358              
359             my $hash = {
360             version => $FusionInventory::Agent::VERSION,
361             deviceid => $params{inventory}->{deviceid},
362             data => $params{inventory}->{content},
363             fields => $params{inventory}->{fields},
364 0           };
365              
366 0           print {$params{handle}} $template->fill_in(HASH => $hash);
  0            
367              
368 0           last SWITCH;
369             }
370              
371 0           die "unknown format $params{format}";
372             }
373             }
374              
375             1;
376             __END__