File Coverage

blib/lib/FusionInventory/Agent/Task/Inventory/Linux/Storages/Megacli.pm
Criterion Covered Total %
statement 9 99 9.0
branch 0 44 0.0
condition 0 6 0.0
subroutine 3 8 37.5
pod 0 2 0.0
total 12 159 7.5


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Task::Inventory::Linux::Storages::Megacli;
2              
3 1     1   112999590 use strict;
  1         6  
  1         92  
4 1     1   10 use warnings;
  1         10  
  1         68  
5              
6 1     1   453 use FusionInventory::Agent::Tools;
  1         4  
  1         1794  
7              
8             sub isEnabled {
9 0     0 0   return canRun('megacli');
10             }
11              
12             # The module gets a disk data from `megacli -PDlist` and `megacli -ShowSummary`.
13             # `PDlist` provides s/n and model in a single 'Inquiry Data' string, and
14             # `ShowSummary` helps to "separate the wheat from the chaff". (Wish there was
15             # an easier way).
16             sub doInventory {
17 0     0 0   my (%params) = @_;
18              
19 0           my $inventory = $params{inventory};
20              
21 0           my $count = getFirstMatch(
22             command => "megacli -adpCount",
23             pattern => qr/Controller Count: (\d+)/
24             );
25 0 0         return unless $count;
26              
27 0           my (%adapter, %summary, %pdlist, $storage);
28 0           for (my $adp = 0; $adp < $count; $adp++) {
29 0           $adapter{$adp} = _getAdpEnclosure( adp => $adp );
30 0           $summary{$adp} = _getSummary( adp => $adp );
31 0           $pdlist{$adp} = _getPDlist( adp => $adp );
32             }
33              
34 0           while (my ($adp_id, $adp) = each %adapter) {
35 0           while (my ($pd_id, $pd) = each %{$pdlist{$adp_id}}) {
  0            
36 0           my ($firmware, $size, $model, $vendor);
37              
38             # Raw Size: 232.885 GB [0x1d1c5970 Sectors]
39 0           ($size) = ($pd->{'Raw Size'} =~ /^(.+) \[/);
40 0           $size = getCanonicalSize($size);
41 0           $firmware = $pd->{'Device Firmware Level'};
42              
43 0           $storage = {
44             TYPE => 'disk',
45             FIRMWARE => $firmware,
46             DESCRIPTION => $pd->{'PD Type'},
47             DISKSIZE => $size,
48             };
49              
50             # Lookup the disk info in 'ShowSummary'
51 0           while (my ($sum_id, $sum) = each %{$summary{$adp_id}}) {
  0            
52             next unless
53 0 0 0       $adp->{$sum->{encl_id}} == $pd->{'Enclosure Device ID'} &&
      0        
54             $sum->{encl_pos} == $pd->{'Enclosure position'} &&
55             $sum->{slot} == $pd->{'Slot Number'};
56              
57             # 'HUC101212CSS' <-- note it is incomplete
58 0           $model = $sum->{'Product Id'};
59              
60             # 'HGST HUC101212CSS600 U5E0KZGLG2HE'
61 0           my $serial = $pd->{'Inquiry Data'};
62 0           $serial =~ s/$firmware//; # remove firmware part
63              
64 0 0         if ($sum->{'Vendor Id'} ne 'ATA') {
65 0           $vendor = $sum->{'Vendor Id'};
66 0           $serial =~ s/$vendor//; # remove vendor part
67             }
68              
69 0           $serial =~ s/$model[^ ]*//; # remove model part
70 0           $serial =~ s/\s//g; # remove remaining spaces
71 0           $storage->{SERIALNUMBER} = $serial;
72              
73             # Restore complete model name:
74             # HUC101212CSS --> HUC101212CSS600
75 0 0         if ($pd->{'Inquiry Data'} =~ /($sum->{'Product Id'}(?:[^ ]*))/) {
76 0           $model = $1;
77 0           $model =~ s/^\s+//;
78 0           $model =~ s/\s+$//;
79             }
80              
81 0           last;
82             }
83              
84             # When Product ID ($model) looks like 'INTEL SSDSC2CW24'
85 0 0         if ($model =~ /^(\S+)\s+(\S+)$/) {
86 0           $vendor = $1; # 'INTEL'
87 0           $model = $2; # 'SSDSC2CW24'
88             }
89              
90 0           $storage->{NAME} = $model;
91 0           $storage->{MODEL} = $model;
92 0 0         $storage->{MANUFACTURER} = defined $vendor ?
93             getCanonicalManufacturer($vendor) :
94             getCanonicalManufacturer($model);
95              
96 0           $inventory->addEntry(
97             section => 'STORAGES',
98             entry => $storage,
99             );
100             }
101             }
102             }
103              
104             sub _getAdpEnclosure {
105 0     0     my (%params) = @_;
106              
107 0 0         my $command = $params{adp} ? "megacli -EncInfo -a$params{adp}" : undef;
108              
109 0           my $handle = getFileHandle(
110             command => $command,
111             %params
112             );
113 0 0         return unless $handle;
114              
115 0           my %enclosure;
116             my $encl_id;
117 0           while (my $line = <$handle>) {
118 0           chomp $line;
119              
120 0 0         if ($line =~ /Enclosure (\d+):/) {
121 0           $encl_id = $1;
122             }
123              
124 0 0         if ($line =~ /Device ID\s+:\s+(\d+)/) {
125 0           $enclosure{$encl_id} = $1;
126             }
127             }
128 0           close $handle;
129              
130 0           return \%enclosure;
131             }
132              
133             sub _getSummary {
134 0     0     my (%params) = @_;
135              
136 0 0         my $command = $params{adp} ? "megacli -ShowSummary -a$params{adp}" : undef;
137              
138 0           my $handle = getFileHandle(
139             command => $command,
140             %params
141             );
142 0 0         return unless $handle;
143              
144             # fast forward to relevant section
145 0           while (my $line = <$handle>) {
146 0 0         last if $line =~ /^\s+PD\s+$/;
147             }
148              
149 0           my %drive;
150 0           my $n = -1;
151 0           while (my $line = <$handle>) {
152             # end of relevant section
153 0 0         last if $line =~ /^Storage$/;
154 0           chomp $line;
155              
156 0 0         $n++ if $line =~ /Connector\s*:/;
157              
158 0 0         if ($line =~ /Connector\s*:\s*(\d+)(?:)?: Slot (\d+)/) {
    0          
159 0           $drive{$n} = {
160             encl_id => $1,
161             encl_pos => $2,
162             slot => $3,
163             };
164 0           $drive{$n}->{'encl_id'} += 0; # drop leading zeroes
165             } elsif ($line =~ /^\s*(.+[^ ])\s*:\s*(.+[^ ])/) {
166 0           $drive{$n}->{$1} = $2;
167             }
168             }
169 0           close $handle;
170              
171             #delete non-disks
172 0           foreach my $i (keys %drive) {
173 0 0         delete $drive{$i} unless defined $drive{$i}->{'slot'};
174             }
175              
176 0           return \%drive;
177             }
178              
179             sub _getPDlist {
180 0     0     my (%params) = @_;
181              
182 0 0         my $command = $params{adp} ? "megacli -PDlist -a$params{adp}" : undef;
183              
184 0           my $handle = getFileHandle(
185             command => $command,
186             %params
187             );
188 0 0         return unless $handle;
189              
190 0           my %pdlist;
191 0           my $n = 0;
192 0           while (my $line = <$handle>) {
193 0           chomp $line;
194 0 0         next unless $line =~ /^([^:]+)\s*:\s*(.+[^ ])/;
195 0           my $key = $1;
196 0           my $val = $2;
197 0 0         $n++ if $key =~ /Enclosure Device ID/;
198 0           $pdlist{$n}->{$key} = $val;
199             }
200 0           close $handle;
201              
202 0           return \%pdlist;
203             }
204              
205             1;