File Coverage

blib/lib/FusionInventory/Agent/Task/Inventory/Linux/Storages/Megacli.pm
Criterion Covered Total %
statement 57 99 57.5
branch 26 44 59.0
condition 0 6 0.0
subroutine 6 8 75.0
pod 0 2 0.0
total 89 159 55.9


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