File Coverage

blib/lib/Parse/HP/ACU.pm
Criterion Covered Total %
statement 6 133 4.5
branch 0 58 0.0
condition 0 15 0.0
subroutine 2 9 22.2
pod 4 7 57.1
total 12 222 5.4


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2              
3             # Copyright 2010 Jeremy Cole.
4             #
5             # This program is free software; you can redistribute it and/or modify it
6             # under the terms of either: the GNU General Public License as published
7             # by the Free Software Foundation; or the Artistic License.
8             #
9             # See http://dev.perl.org/licenses/ for more information.
10              
11             package Parse::HP::ACU;
12              
13 1     1   20863 use warnings;
  1         2  
  1         25  
14 1     1   5 use strict;
  1         2  
  1         1590  
15              
16             =head1 NAME
17              
18             Parse::HP::ACU - Parse the output of HP's hpacucli utility.
19              
20             =head1 VERSION
21              
22             Version 0.03
23              
24             =cut
25              
26             our $VERSION = '0.03';
27              
28             =head1 SYNOPSIS
29              
30             Parse::HP::ACU parses the output of HP's C utility to allow
31             programmatic access to the RAID configuration information provided by the
32             hpacucli utility on HP ProLiant servers.
33              
34             use Parse::HP::ACU;
35              
36             my $acu = Parse::HP::ACU->new();
37              
38             Run the C tool directly to query the hardware (requires root):
39              
40             my $controllers = $acu->parse_config();
41            
42             Parse a text file created already by running C tool:
43              
44             my $controllers = $acu->parse_config_file("foo.txt");
45              
46             Read from a file descriptor already opened by the program:
47              
48             my $controllers = $acu->parse_config_fh(\*STDIN);
49              
50             =head1 SUBROUTINES/METHODS
51              
52             =head2 new
53              
54             Return an instance of the HP::Parse::ACU class that can be used to parse
55             input in one of several ways.
56              
57             =cut
58              
59             sub new
60             {
61 0     0 1   my ($class, $config, $plugin) = @_;
62 0           my $self = {};
63              
64 0           bless($self, $class);
65 0           return $self;
66             }
67              
68             =head2 parse_config
69              
70             Attempt to run the hpacucli utility, and parse the output. This command
71             actually uses parse_config_fh() after opening a pipe to the relevant command.
72              
73             The command that is actually run is approximately:
74              
75             =over 4
76             hpacucli controller all show config detail
77             =back
78              
79             This command requires root access, and Parse::HP::ACU makes no attempt to
80             use sudo or any other method to gain root access. It is recommended to call
81             your script which uses this module as root.
82              
83             The parse_config_fh() and parse_config_file() will expect output equivalent
84             to that from the above command.
85              
86             =cut
87              
88             sub parse_config
89             {
90 0     0 1   my ($self) = @_;
91              
92 0           my $hpacucli = "/usr/sbin/hpacucli";
93 0           my $argument = "controller all show config detail";
94 0           my $command = sprintf("%s %s|", $hpacucli, $argument);
95              
96 0           my $fh;
97 0 0         if(open $fh, $command)
98             {
99 0           my $c = $self->parse_config_fh($fh);
100 0           close $fh;
101 0           return $c;
102             }
103 0           return undef;
104             }
105              
106             =head2 parse_config_file
107              
108             Open and parse a file containing the output from hpacucli.
109              
110             =cut
111              
112             sub parse_config_file
113             {
114 0     0 1   my ($self, $file) = @_;
115              
116 0           my $fh;
117 0 0         if(open $fh, "<".$file)
118             {
119 0           my $c = $self->parse_config_fh($fh);
120 0           close $fh;
121 0           return $c;
122             }
123 0           return undef;
124              
125             }
126              
127             =head2 parse_config_fh
128              
129             Read from the file handle and parse it, returning a hash-of-hashes.
130              
131             =cut
132              
133             sub parse_config_fh
134             {
135 0     0 1   my ($self, $fh) = @_;
136              
137 0           my $controller = {};
138 0           my $current_controller = 0;
139 0           my $current_array = undef;
140 0           my $current_logical_drive = undef;
141 0           my $current_mirror_group = undef;
142 0           my $current_physical_drive = undef;
143              
144 0           LINE: while(my $line = <$fh>)
145             {
146 0           chomp $line;
147              
148 0 0         next if($line =~ /^$/);
149              
150 0 0         if($line !~ /^[ ]+/)
151             {
152 0           $current_controller = $current_controller + 1;
153 0           $current_array = undef;
154 0           $current_logical_drive = undef;
155 0           $current_mirror_group = undef;
156 0           $current_physical_drive = undef;
157 0           $controller->{$current_controller} = {};
158 0           $controller->{$current_controller}
159             ->{'description'} = $line;
160 0           next;
161             }
162              
163 0 0         next if(!defined($current_controller));
164              
165 0           $line =~ s/^\s+//g;
166 0           $line =~ s/\s+$//g;
167 0           $line =~ s/[ ]+/ /g;
168              
169 0 0         if($line =~ /unassigned/)
170             {
171 0           $current_array = "unassigned";
172 0           $current_logical_drive = undef;
173 0           $current_mirror_group = undef;
174 0           $current_physical_drive = undef;
175 0           $controller->{$current_controller}
176             ->{'unassigned'} = {};
177 0           $controller->{$current_controller}
178             ->{'unassigned'}->{'physical_drive'} = {};
179 0           next;
180             }
181              
182 0 0         if($line =~ /Array: ([A-Z]+)/)
183             {
184 0           $current_array = $1;
185 0           $current_logical_drive = undef;
186 0           $current_mirror_group = undef;
187 0           $current_physical_drive = undef;
188 0           $controller->{$current_controller}
189             ->{'array'}->{$current_array} = {};
190 0           $controller->{$current_controller}
191             ->{'array'}->{$current_array}
192             ->{'logical_drive'} = {};
193 0           $controller->{$current_controller}
194             ->{'array'}->{$current_array}
195             ->{'physical_drive'} = {};
196 0           next;
197             }
198              
199 0 0         if($line =~ /Logical Drive: ([0-9]+)/)
200             {
201 0           $current_logical_drive = $1;
202 0           $current_physical_drive = undef;
203 0           $current_mirror_group = undef;
204 0           $controller->{$current_controller}
205             ->{'array'}->{$current_array}
206             ->{'logical_drive'}->{$current_logical_drive} = {};
207 0           $controller->{$current_controller}
208             ->{'array'}->{$current_array}
209             ->{'logical_drive'}->{$current_logical_drive}
210             ->{'mirror_group'} = {};
211 0           next;
212             }
213              
214 0 0 0       if($line =~ /physicaldrive ([0-9IC:]+)/ and $line !~ /port/)
215             {
216 0           $current_logical_drive = undef;
217 0           $current_physical_drive = $1;
218 0           $current_mirror_group = undef;
219 0 0         if($current_array eq 'unassigned')
220             {
221 0           $controller->{$current_controller}
222             ->{'unassigned'}
223             ->{'physical_drive'}->{$current_physical_drive} = {};
224             } else {
225 0           $controller->{$current_controller}
226             ->{'array'}->{$current_array}
227             ->{'physical_drive'}->{$current_physical_drive} = {};
228             }
229 0           next;
230             }
231              
232 0 0         if($line =~ /Mirror Group ([0-9]+):/)
233             {
234 0           $current_mirror_group = $1;
235 0           $controller->{$current_controller}
236             ->{'array'}->{$current_array}
237             ->{'logical_drive'}->{$current_logical_drive}
238             ->{'mirror_group'}->{$current_mirror_group} = [];
239 0           next;
240             }
241              
242 0 0 0       if(defined($current_array)
      0        
243             and defined($current_logical_drive)
244             and defined($current_mirror_group))
245             {
246 0 0         if($line =~ /physicaldrive ([0-9IC:]+) \(/)
247             {
248 0           my $current_mirror_group_list = $controller->{$current_controller}
249             ->{'array'}->{$current_array}
250             ->{'logical_drive'}->{$current_logical_drive}
251             ->{'mirror_group'}->{$current_mirror_group};
252              
253 0           foreach my $pd (@{$current_mirror_group_list})
  0            
254             {
255 0 0         next LINE if($pd eq $1);
256             }
257 0           push @{$current_mirror_group_list}, $1;
  0            
258             }
259 0           next;
260             }
261              
262 0 0 0       if(defined($current_array)
263             and defined($current_logical_drive))
264             {
265 0 0         if(my ($k, $v) = &K_V($line))
266             {
267 0 0         next unless defined($k);
268 0           $controller->{$current_controller}
269             ->{'array'}->{$current_array}
270             ->{'logical_drive'}->{$current_logical_drive}->{$k} = $v;
271             }
272 0           next;
273             }
274              
275 0 0 0       if(defined($current_array)
276             and defined($current_physical_drive))
277             {
278 0 0         if(my ($k, $v) = &K_V($line))
279             {
280 0 0         next unless defined($k);
281 0 0         if($current_array eq 'unassigned')
282             {
283 0           $controller->{$current_controller}
284             ->{'unassigned'}
285             ->{'physical_drive'}->{$current_physical_drive}->{$k} = $v;
286             } else {
287 0           $controller->{$current_controller}
288             ->{'array'}->{$current_array}
289             ->{'physical_drive'}->{$current_physical_drive}->{$k} = $v;
290             }
291             }
292 0           next;
293             }
294            
295 0 0         if(defined($current_array))
296             {
297 0 0         if(my ($k, $v) = &K_V($line))
298             {
299 0 0         next unless defined($k);
300 0           $controller->{$current_controller}
301             ->{'array'}->{$current_array}->{$k} = $v;
302             }
303 0           next;
304             }
305              
306 0 0         if(my ($k, $v) = &K_V($line))
307             {
308 0 0         next unless defined($k);
309 0           $controller->{$current_controller}->{$k} = $v;
310             }
311 0           next;
312             }
313            
314 0           return $controller;
315             }
316              
317             sub K
318             {
319 0     0 0   my ($k) = @_;
320              
321 0           $k = lc $k;
322 0           $k =~ s/[ \/\-]/_/g;
323 0           $k =~ s/[\(\)]//g;
324              
325 0           return $k;
326             }
327              
328             sub V
329             {
330 0     0 0   my ($k, $v) = @_;
331              
332 0 0         if($k eq 'accelerator_ratio')
333             {
334 0 0         if($v =~ /([0-9]+)% Read \/ ([0-9]+)% Write/)
335             {
336 0           return {'read' => $1, 'write' => $2};
337             }
338             }
339              
340 0           return $v;
341             }
342              
343             sub K_V($)
344             {
345 0     0 0   my ($line) = @_;
346              
347 0 0         if($line =~ /(.+):\s+(.+)/)
348             {
349 0           my $k = &K($1);
350 0           my $v = &V($k, $2);
351 0           return ($k, $v);
352             }
353              
354 0           return (undef, undef);
355             }
356              
357             =head1 AUTHOR
358              
359             Jeremy Cole, C<< >>
360              
361             =head1 BUGS
362              
363             Please report any bugs or feature requests to C, or through
364             the web interface at L. I will be notified, and then you'll
365             automatically be notified of progress on your bug as I make changes.
366              
367             This module has been tested with at least the following RAID controllers:
368              
369             =over 4
370             =item * HP Smart Array P400i (G5 series on-board)
371             =item * HP Smart Array P410i (G6 series on-board)
372             =item * HP Smart Array P410 (add-on with internal connectors)
373             =item * HP Smart Array P411 (add-on with external connectors)
374             =back
375              
376             This module has been tested with at least the following RAID configurations:
377              
378             =over 4
379             =item * 2-disk RAID 1
380             =item * 2-disk RAID 1 + 6-disk RAID 1+0
381             =item * 4-disk RAID 1+0
382             =back
383              
384             Other controllers or configurations may or may not work. Your feedback is
385             appreciated in order to further test and refine the parsing code.
386              
387             =head1 SUPPORT
388              
389             You can find documentation for this module with the perldoc command.
390              
391             perldoc Parse::HP::ACU
392              
393              
394             You can also look for information at:
395              
396             =over 4
397              
398             =item * RT: CPAN's request tracker
399              
400             L
401              
402             =item * AnnoCPAN: Annotated CPAN documentation
403              
404             L
405              
406             =item * CPAN Ratings
407              
408             L
409              
410             =item * Search CPAN
411              
412             L
413              
414             =back
415              
416              
417             =head1 ACKNOWLEDGEMENTS
418              
419              
420             =head1 LICENSE AND COPYRIGHT
421              
422             Copyright 2010 Jeremy Cole.
423              
424             This program is free software; you can redistribute it and/or modify it
425             under the terms of either: the GNU General Public License as published
426             by the Free Software Foundation; or the Artistic License.
427              
428             See http://dev.perl.org/licenses/ for more information.
429              
430              
431             =cut
432              
433             1; # End of Parse::HP::ACU