| 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; |