File Coverage

blib/lib/Cisco/Version.pm
Criterion Covered Total %
statement 9 214 4.2
branch 0 116 0.0
condition 0 21 0.0
subroutine 3 27 11.1
pod 5 6 83.3
total 17 384 4.4


line stmt bran cond sub pod time code
1             package Cisco::Version;
2              
3             ## ----------------------------------------------------------------------------------------------
4             ## Cisco::Version
5             ##
6             ## Cisco "Show Version" parser.
7             ## Try to parse some useful info from the "show version" output like memory, software, flash, etc.
8             ##
9             ## $Id: Version.pm 76 2007-07-23 21:05:02Z mwallraf $
10             ## $Author: mwallraf $
11             ## $Date: 2007-07-23 23:05:02 +0200 (Mon, 23 Jul 2007) $
12             ##
13             ## This program is free software; you can redistribute it and/or
14             ## modify it under the same terms as Perl itself.
15             ## ----------------------------------------------------------------------------------------------
16              
17 1     1   22321 use warnings;
  1         3  
  1         34  
18 1     1   6 use strict;
  1         1  
  1         33  
19 1     1   6 use Carp;
  1         5  
  1         4112  
20             require 5.002;
21             #use Data::Dumper;
22              
23             our $VERSION = '0.02';
24             my $AUTOLOAD;
25              
26             my $DEBUG = 1; # 0 = OFF, 1 = ERROR, 2 = WARN, 3 = INFO, 4 = DEBUG
27              
28              
29             my %CMD = (
30             'bootstrap' => 'bootstrap',
31             'sw_type' => 'sw-type',
32             'sw_featureset' => 'sw-featureset',
33             'sw_version' => 'sw-version',
34             'bootldr_type' => 'bootldr-type',
35             'bootldr_version' => 'bootldr-version',
36             'bootldr_featureset' => 'bootldr-featureset',
37             'hostname' => 'hostname',
38             'uptime' => 'uptime',
39             'reload_reason' => 'reload-reason',
40             'reload_time' => 'reload-time',
41             'image_file' => 'image-file',
42             'chassis_type' => 'chassis-type',
43             'memory' => 'memory',
44             'confreg' => 'confreg',
45             'pwdrecovery' => 'pwdrecovery',
46             'flash_filesystems_sizes' => 'flash_filesystems_sizes',
47             'flash_largest_size' => 'flash_largest_size',
48             );
49              
50              
51              
52             sub new() {
53 0     0 1   my ($this, $show_version) = @_;
54 0   0       my $class = ref($this) || $this;
55 0           my $self = {};
56            
57 0           $self->{'show_version'} = $show_version; # full output of "show version"
58            
59 0           $self->{'parsed'} = {}; # this will contain hash of parsed parameters
60 0           $self->{'not_found'} = ''; # this value is returned if a parameter was not found in show version
61              
62             ## these are the possible values we can expect for now
63             ##
64             # $self->{'parsed'}->{'bootstrap'};
65             # $self->{'parsed'}->{'sw-type'};
66             # $self->{'parsed'}->{'sw-featureset'};
67             # $self->{'parsed'}->{'sw-version'};
68             # $self->{'parsed'}->{'bootldr-type'};
69             # $self->{'parsed'}->{'bootldr-version'};
70             # $self->{'parsed'}->{'bootldr-featureset'};
71             # $self->{'parsed'}->{'hostname'};
72             # $self->{'parsed'}->{'uptime'};
73             # $self->{'parsed'}->{'reload-reason'};
74             # $self->{'parsed'}->{'reload-time'};
75             # $self->{'parsed'}->{'image-file'};
76             # $self->{'parsed'}->{'chassis-type'};
77             # $self->{'parsed'}->{'memory'};
78             # $self->{'parsed'}->{'confreg'};
79             # $self->{'parsed'}->{'pwdrecovery'};
80             # $self->{'parsed'}->{'flash_filesystems_sizes'} = [];
81             # $self->{'parsed'}->{'flash_largest_size'};
82            
83 0           bless($self, $class);
84 0           return($self);
85             }
86              
87              
88              
89             # This routine parses "show version"
90             sub parse {
91 0     0 1   my ($self, $sv) = @_;
92              
93 0 0         if (!$sv) {
94 0 0         ($self->{'show_version'})?($sv = $self->{'show_version'}):(croak('Forgot to load "show config" output?'));
95             }
96              
97 0           my @lines = split( /[\n\r]/, $sv);
98              
99 0           my ($line);
100              
101 0           foreach $line (@lines) {
102 0 0         next unless ($line);
103 0           &_debug("new line found", $line);
104 0 0         if ($line =~ /^(?:Cisco|IOS).*Version ([^ ,]+)/) { $self->_process_software_version($line); };
  0            
105 0 0         if ($line =~ /^ROM: /) { $self->_process_rom($line); };
  0            
106 0 0         if ($line =~ /^BOOTLDR: /) { $self->_process_bootloader($line); };
  0            
107 0 0         if ($line =~ /uptime/i) { $self->_process_uptime($line); };
  0            
108 0 0         if ($line =~ /System returned to ROM by /) { $self->_process_reload_reason($line); };
  0            
109 0 0         if ($line =~ /restarted/) { $self->_process_reload_time($line); };
  0            
110 0 0         if ($line =~ /System image file is/) { $self->_process_image_file($line); };
  0            
111 0 0         if ($line =~ /^cisco.*[0-9]+K.*memory.$/i) { $self->_process_memory($line); };
  0            
112 0 0         if ($line =~ /of physical memory \(DRAM\)$/) { $self->_process_additional_memory($line); };
  0            
113 0 0         if ($line =~ /^Configuration register is/i) { $self->_process_configuration_register($line); };
  0            
114 0 0         if ($line =~ /password-recovery mechanism/) { $self->_process_password_recovery($line); };
  0            
115 0 0         if ($line =~ /^[0-9]+K .*(?:PCMCIA |[fF]lash[^-]|ATA )/) { $self->_process_flash($line); };
  0            
116             }
117              
118             }
119              
120              
121              
122             sub AUTOLOAD() {
123 0     0     my ($self,@args) = @_;
124 0           my $cmd = $Cisco::Version::AUTOLOAD;
125 0           my $parm;
126            
127 0           $cmd =~ s/.*:://;
128 0           $parm = $cmd;
129 0           $parm =~ s/get_//;
130            
131 0 0 0       if ( ($cmd =~ /^get_/) && (defined($CMD{"$parm"})) ) {
132 0           return $self->get_parameter($parm);
133             }
134             else {
135 0           croak("function $cmd does not exist");
136             }
137             }
138              
139              
140              
141              
142             sub get_parameter() {
143 0     0 0   my ($self, $parm) = @_;
144            
145 0 0         if (defined($self->{'parsed'}->{$CMD{"$parm"}})) {
146 0           return $self->{'parsed'}->{$CMD{"$parm"}};
147             }
148             else {
149 0           return $self->{'not_found'};
150             }
151             }
152              
153              
154             ##
155             ## returns a reference to the 'parsed' hash,
156             ## this contains all the elements that were found in 'show version'
157             ##
158             sub get_summary() {
159 0     0 1   my ($self) = shift;
160            
161 0           return $self->{'parsed'};
162             }
163              
164              
165             sub get_not_found_value() {
166 0     0 1   my ($self) = shift;
167            
168 0           return $self->{'not_found'};
169             }
170              
171             sub set_not_found_value() {
172 0     0 1   my ($self, $value) = @_;
173            
174 0 0         $self->{'not_found'} = $value if (defined($value));
175             }
176              
177              
178             ## look for bootstrap version
179             sub _process_rom() {
180 0     0     my ($self, $line) = @_;
181 0           my $version;
182            
183 0           &_debug("parsing bootstrap", $line);
184            
185 0 0 0       if ( ($line !~ /(?:bootstrap|ROM: [0-9]+\.[0-9]+)/i) || ($line =~ /bootstrap program/i) ) {
186 0           &_info("IGNORE - $line");
187             }
188            
189             else {
190 0           $line =~ /(?:version ([^ ,]+)|ROM: ([0-9].*))/i;
191 0   0       $version = $1 || $2;
192 0 0         if ($version) {
193 0           $self->{'parsed'}->{'bootstrap'} = $version;
194            
195 0           &_debug("result = $version");
196             }
197             else {
198 0           &_warn("bootstrap version not found", $line);
199             }
200             }
201             }
202              
203              
204             sub _process_software_version() {
205 0     0     my ($self, $line) = @_;
206 0           my ($sw_version, $sw_type, $sw_featureset);
207              
208 0           &_debug("parsing software version", $line);
209            
210 0 0         if ($line =~ /^(?:Cisco IOS Software|IOS \(tm\))[, ]+(.*) Software \((.*)\).*Version ([^ ,]+)/) {
211 0           $sw_type = $1;
212 0           $sw_featureset = $2;
213 0           $sw_version = $3;
214            
215 0 0         ($sw_type)?($self->{'parsed'}->{'sw-type'} = $sw_type):(&_warn("software type not found", $line));
216 0 0         ($sw_featureset)?($self->{'parsed'}->{'sw-featureset'} = $sw_featureset):(&_warn("software featureset not found", $line));
217 0 0         ($sw_version)?($self->{'parsed'}->{'sw-version'} = $sw_version):(&_warn("software version not found", $line));
218            
219 0           &_debug("result = $sw_type");
220 0           &_debug("result = $sw_featureset");
221 0           &_debug("result = $sw_version");
222             }
223             else {
224 0           &_error("software version, type or featureset cannot be parsed", $line);
225             }
226             }
227              
228              
229              
230             sub _process_bootloader() {
231 0     0     my ($self, $line) = @_;
232 0           my ($bl_version, $bl_type, $bl_featureset);
233              
234 0           &_debug("parsing bootloader", $line);
235              
236 0 0         if ($line =~ /^BOOTLDR: (.*) (?:Software|Boot Loader) \((.*)\).*Version ([^ ,]+)/) {
237 0           $bl_type = $1;
238 0           $bl_featureset = $2;
239 0           $bl_version = $3;
240            
241 0 0         ($bl_type)?($self->{'parsed'}->{'bootldr-type'} = $bl_type):(&_warn("bootloader type not found", $line));
242 0 0         ($bl_featureset)?($self->{'parsed'}->{'bootldr-featureset'} = $bl_featureset):(&_warn("bootloader featureset not found", $line));
243 0 0         ($bl_version)?($self->{'parsed'}->{'bootldr-version'} = $bl_version):(&_warn("bootloader version not found", $line));
244              
245 0           &_debug("result = $bl_type");
246 0           &_debug("result = $bl_featureset");
247 0           &_debug("result = $bl_version");
248             }
249             else {
250 0           &_error("bootloader version, type or featureset cannot be parsed", $line);
251             }
252             }
253              
254              
255              
256             sub _process_uptime() {
257 0     0     my ($self, $line) = @_;
258 0           my ($host, $uptime);
259              
260 0           &_debug("parsing uptime", $line);
261            
262 0 0         if ($line =~ /^ *(?:(.*) uptime is|Switch Uptime|Uptime for this control processor is)[^0-9]+(.*minutes*)/) {
263 0 0 0       if ($1 && $2) {
264 0           $host = $1;
265 0           $self->{'parsed'}->{'hostname'} = $host;
266             }
267 0           $uptime = $2;
268 0 0         ($uptime)?($self->{'parsed'}->{'uptime'} = $uptime):(&_warn("uptime was not found", $line));
269              
270 0           &_debug("result = $uptime");
271             }
272             else {
273 0           &_error("uptime cannot be parsed", $line);
274             }
275             }
276              
277              
278              
279             sub _process_reload_reason() {
280 0     0     my ($self, $line) = @_;
281 0           my $reason;
282              
283 0           &_debug("parsing reload reason", $line);
284            
285 0 0         if ($line =~ /System returned to ROM by (.*)/) {
286 0           $reason = $1;
287 0 0         ($reason)?($self->{'parsed'}->{'reload-reason'} = $reason):(&_warn("reload reason was not found", $line));
288              
289 0           &_debug("result = $reason");
290             }
291             else {
292 0           &_error("reload reason cannot be parsed", $line);
293             }
294             }
295              
296              
297             sub _process_reload_time() {
298 0     0     my ($self, $line) = @_;
299 0           my $time;
300              
301 0           &_debug("parsing reload time", $line);
302            
303 0 0         if ($line =~ /restarted.* at (.*)/) {
304 0           $time = $1;
305 0 0         ($time)?($self->{'parsed'}->{'reload-time'} = $time):(&_warn("reload time was not found", $line));
306              
307 0           &_debug("result = $time");
308             }
309             else {
310 0           &_error("reload time cannot be parsed", $line);
311             }
312             }
313              
314              
315             sub _process_image_file() {
316 0     0     my ($self, $line) = @_;
317 0           my $image;
318              
319 0           &_debug("parsing image file info", $line);
320            
321 0 0         if ($line =~ /System image file is \"(.*)\"/) {
322 0           $image = $1;
323 0 0         ($image)?($self->{'parsed'}->{'image-file'} = $image):(&_warn("image file was not found", $line));
324              
325 0           &_debug("result = $image");
326             }
327             else {
328 0           &_error("system image file cannot be parsed", $line);
329             }
330             }
331              
332              
333             ##
334             ## tries to calculate the memory
335             ## This is no exact science so be careful ...
336             ## Here's how we do it by default to get memory in MB : (main memory + shared IO memory) / 1024
337             ## But there are a few exceptions.
338             ##
339             sub _process_memory() {
340 0     0     my ($self, $line) = @_;
341 0           my ($memory, $chassis);
342 0           my ($main_mem, $io_mem);
343              
344 0           &_debug("parsing memory", $line);
345            
346 0 0         if ($line =~ /cisco ([^ ]+).*with (?:([0-9]+)K |([0-9]+)K\/([0-9]+)K).*memory.*/i) {
347 0           $chassis = $1;
348            
349 0 0 0       if ($3 && $4) {
    0          
350 0           $main_mem = $3;
351 0           $io_mem = $4;
352            
353             ### some exceptions
354            
355             # ex. for WS-C3550
356 0 0         if ($chassis =~ /^WS-C35/) {
357 0           $memory = $main_mem;
358             }
359            
360             ### default calculation
361             else {
362 0           $memory = $main_mem + $io_mem;
363             }
364            
365             }
366             elsif ($2) {
367 0           $memory = $2;
368             }
369             # save memory in megabytes (try to round to decimal number)
370 0           $memory = int(($memory / 1024) + .5);
371              
372 0 0         ($chassis)?($self->{'parsed'}->{'chassis-type'} = $chassis):(&_warn("chassis type was not found", $line));
373 0 0         ($memory)?($self->{'parsed'}->{'memory'} = $memory):(&_warn("memory was not found", $line));
374              
375 0           &_debug("result = $chassis");
376 0           &_debug("result = $memory");
377             }
378             else {
379 0           &_error("memory or chassis type cannot be parsed", $line);
380             }
381             }
382              
383              
384              
385             ##
386             ## some smaller routers have extra line with 'additional' DRAM
387             ## this should be added to the RAM we already found
388             ##
389             sub _process_additional_memory() {
390 0     0     my ($self, $line) = @_;
391 0           my ($memory);
392            
393 0 0         if ($line =~ /([0-9]+)M .* of physical memory \(DRAM\)$/) {
394 0           $memory = int($1 + .5);
395              
396 0 0         ($memory)?($self->{'parsed'}->{'memory'} = $self->{'parsed'}->{'memory'} + $memory):(&_warn("additional DRAM was not found", $line));
397             }
398             else {
399 0           &_error("unable to parse additional DRAM", $line);
400             }
401             }
402              
403              
404              
405              
406             sub _process_configuration_register() {
407 0     0     my ($self, $line) = @_;
408 0           my ($confreg);
409              
410 0           &_debug("parsing configuration register", $line);
411            
412 0 0         if ($line =~ /^Configuration register is (.*)/) {
413 0           $confreg = $1;
414            
415 0 0         ($confreg)?($self->{'parsed'}->{'confreg'} = $confreg):(&_warn("configuration register was not found", $line));
416              
417 0           &_debug("result = $confreg");
418             }
419             else {
420 0           &_error("unable to parse configuration register", $line);
421             }
422             }
423              
424              
425             sub _process_password_recovery() {
426 0     0     my ($self, $line) = @_;
427 0           my ($recovery);
428              
429 0           &_debug("parsing password recovery mechanism", $line);
430            
431 0 0         if ($line =~ /password-recovery mechanism is ([a-zA-Z]+)/) {
432 0           $recovery = $1;
433              
434 0 0         ($recovery)?($self->{'parsed'}->{'pwdrecovery'} = $recovery):(&_warn("password recovery mechanism was not found", $line));
435              
436 0           &_debug("result = $recovery");
437             }
438             else {
439 0           &_error("unable to parse password recovery mechanism", $line);
440             }
441             }
442              
443              
444              
445             ##
446             ## Flash info is also difficult to parse as a chassis may have multiple
447             ## filesystems. Also not all chassis types report flash info.
448             ## Usually we're only interested in largest filesystem only so this is what
449             ## we try to parse :
450             ##
451             ## List of all flash filesystem sizes is kept as flash_filesystems_sizes
452             ## Largest flash filesystem is reported as flash_largest_size
453             ##
454             sub _process_flash() {
455 0     0     my ($self, $line) = @_;
456 0           my ($flash);
457            
458 0           &_debug("parsing flash info", $line);
459            
460 0 0         if ($line =~ /^([0-9]+)K .*(?:PCMCIA |[fF]lash[^-]|ATA )/) {
461 0           $flash = int(($1 / 1024) + .5);
462            
463 0 0         if ($flash) {
464 0 0         if (!defined($self->{'parsed'}->{'flash_filesystems_sizes'})) {
465 0           $self->{'parsed'}->{'flash_filesystems_sizes'} = [];
466             }
467 0           push (@{$self->{'parsed'}->{'flash_filesystems_sizes'}}, $flash);
  0            
468              
469 0 0 0       if (!defined($self->{'parsed'}->{'flash_largest_size'}) || ($self->{'parsed'}->{'flash_largest_size'} < $flash)) {
470 0           $self->{'parsed'}->{'flash_largest_size'} = $flash;
471             }
472              
473 0           &_debug("result = $flash");
474             }
475             else {
476 0           &_warn("flash was not found", $line);
477             }
478             }
479             else {
480 0           &_error("unable to parse flash", $line);
481             }
482             }
483              
484              
485              
486             ##
487             ## carp a log message, regardless of $DEBUG value
488             ##
489             sub _log() {
490 0     0     my ($msg, $line) = @_;
491            
492 0 0         if ($line) {
493 0           $msg = $msg . " [$line]";
494             }
495            
496 0           &carp($msg);
497             }
498              
499              
500             ##
501             ## carp a log message, only if $DEBUG >= 1
502             ##
503             sub _error() {
504 0     0     my ($msg, $line) = @_;
505            
506 0 0         if ($DEBUG >= 1) {
507 0           &_log("ERROR: ".$msg, $line);
508             }
509             }
510              
511              
512             ##
513             ## carp a log message, only if $DEBUG >= 2
514             ##
515             sub _warn() {
516 0     0     my ($msg, $line) = @_;
517            
518 0 0         if ($DEBUG >= 2) {
519 0           &_log("WARN: ".$msg, $line);
520             }
521             }
522              
523              
524             ##
525             ## carp a log message, only if $DEBUG >= 3
526             ##
527             sub _info() {
528 0     0     my ($msg, $line) = @_;
529            
530 0 0         if ($DEBUG >= 3) {
531 0           &_log("INFO: ".$msg, $line);
532             }
533             }
534              
535              
536             ##
537             ## carp a log message, only if $DEBUG >= 3
538             ##
539             sub _debug() {
540 0     0     my ($msg, $line) = @_;
541            
542 0 0         if ($DEBUG >= 4) {
543 0           &_log("DEBUG: ".$msg, $line);
544             }
545             }
546              
547             1; # End of Cisco::Version
548              
549              
550             __END__