1 # Copyright (C) 2002 Stanislav Sinyagin
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
17 # $Id: CiscoGeneric.pm,v 1.1 2010-12-27 00:03:48 ivan Exp $
18 # Stanislav Sinyagin <ssinyagin@yahoo.com>
20 # Common Cisco MIBs, supported by many IOS and CatOS devices
22 package Torrus::DevDiscover::CiscoGeneric;
28 $Torrus::DevDiscover::registry{'CiscoGeneric'} = {
30 'checkdevtype' => \&checkdevtype,
31 'discover' => \&discover,
32 'buildConfig' => \&buildConfig
39 'cisco' => '1.3.6.1.4.1.9',
42 'ciscoEnvMonTemperatureStatusDescr' => '1.3.6.1.4.1.9.9.13.1.3.1.2',
43 'ciscoEnvMonTemperatureStatusValue' => '1.3.6.1.4.1.9.9.13.1.3.1.3',
44 'ciscoEnvMonTemperatureThreshold' => '1.3.6.1.4.1.9.9.13.1.3.1.4',
45 'ciscoEnvMonTemperatureStatusState' => '1.3.6.1.4.1.9.9.13.1.3.1.6',
46 'ciscoEnvMonSupplyState' => '1.3.6.1.4.1.9.9.13.1.5.1.3',
48 # CISCO-ENHANCED-MEMPOOL-MIB
49 'cempMemPoolName' => '1.3.6.1.4.1.9.9.221.1.1.1.1.3',
51 # CISCO-MEMORY-POOL-MIB
52 'ciscoMemoryPoolName' => '1.3.6.1.4.1.9.9.48.1.1.1.2',
55 'cpmCPUTotalTable' => '1.3.6.1.4.1.9.9.109.1.1.1.1',
56 'cpmCPUTotalPhysicalIndex' => '1.3.6.1.4.1.9.9.109.1.1.1.1.2',
57 'cpmCPUTotal1minRev' => '1.3.6.1.4.1.9.9.109.1.1.1.1.7',
58 'cpmCPUTotal1min' => '1.3.6.1.4.1.9.9.109.1.1.1.1.4',
61 'avgBusy1' => '1.3.6.1.4.1.9.2.1.57.0'
67 my $devdetails = shift;
69 if( not $dd->oidBaseMatch
70 ( 'cisco', $devdetails->snmpVar( $dd->oiddef('sysObjectID') ) ) )
82 my $devdetails = shift;
84 my $session = $dd->session();
85 my $data = $devdetails->data();
87 if( $devdetails->param('CiscoGeneric::disable-sensors') ne 'yes' )
89 # Check if temperature sensors are supported
91 my $oidTempDescr = $dd->oiddef('ciscoEnvMonTemperatureStatusDescr');
92 my $oidTempValue = $dd->oiddef('ciscoEnvMonTemperatureStatusValue');
93 my $oidTempThrsh = $dd->oiddef('ciscoEnvMonTemperatureThreshold');
94 my $oidTempState = $dd->oiddef('ciscoEnvMonTemperatureStatusState');
96 if( defined $session->get_table( -baseoid => $oidTempValue ) )
98 $devdetails->setCap('ciscoTemperatureSensors');
99 $data->{'ciscoTemperatureSensors'} = {};
101 my $tempDescr = $session->get_table( -baseoid => $oidTempDescr );
102 my $tempThrsh = $session->get_table( -baseoid => $oidTempThrsh );
104 # Get the sensor states and ignore those notPresent(5)
106 my $tempState = $session->get_table( -baseoid => $oidTempState );
108 my $prefixLen = length( $oidTempDescr ) + 1;
109 while( my( $oid, $descr ) = each %{$tempDescr} )
111 # Extract the sensor index from OID
112 my $sIndex = substr( $oid, $prefixLen );
114 if( $tempState->{$oidTempState.'.'.$sIndex} != 5 )
116 $data->{'ciscoTemperatureSensors'}{$sIndex}{
117 'description'} = $descr;
118 $data->{'ciscoTemperatureSensors'}{$sIndex}{
119 'threshold'} = $tempThrsh->{$oidTempThrsh.'.'.$sIndex};
125 if( $devdetails->param('CiscoGeneric::disable-psupplies') ne 'yes' )
127 # Check if power supply status is supported
129 my $oidSupply = $dd->oiddef('ciscoEnvMonSupplyState');
131 my $supplyTable = $session->get_table( -baseoid => $oidSupply );
132 if( defined( $supplyTable ) )
134 $devdetails->setCap('ciscoPowerSupplies');
135 $data->{'ciscoPowerSupplies'} = [];
137 my $prefixLen = length( $oidSupply ) + 1;
138 while( my( $oid, $val ) = each %{$supplyTable} )
140 # Extract the supply index from OID
141 my $sIndex = substr( $oid, $prefixLen );
143 #check if the value is not notPresent(5)
146 push( @{$data->{'ciscoPowerSupplies'}}, $sIndex );
152 if( $devdetails->param('CiscoGeneric::disable-memory-pools') ne 'yes' )
155 $session->get_table( -baseoid =>
156 $dd->oiddef('cempMemPoolName') );
157 if( defined $eMemPool and scalar( %{$eMemPool} ) > 0 and
158 $devdetails->isDevType('RFC2737_ENTITY_MIB') )
160 $devdetails->storeSnmpVars( $eMemPool );
161 $devdetails->setCap('cempMemPool');
162 $data->{'cempMemPool'} = {};
166 getSnmpIndices($dd->oiddef('cempMemPoolName') ) )
168 # $INDEX is a pair entPhysicalIndex . cempMemPoolIndex
169 my ( $phyIndex, $poolIndex ) = split('\.', $INDEX);
171 my $poolName = $devdetails->
172 snmpVar($dd->oiddef('cempMemPoolName') . '.' . $INDEX );
174 $poolName = 'Processor' unless $poolName;
176 my $phyDescr = $data->{'entityPhysical'}{$phyIndex}{'descr'};
177 my $phyName = $data->{'entityPhysical'}{$phyIndex}{'name'};
179 $phyDescr = 'Processor' unless $phyDescr;
180 $phyName = ('Chassis #' .
181 $phyIndex) unless $phyName;
183 $data->{'cempMemPool'}{$INDEX} = {
184 'phyIndex' => $phyIndex,
185 'poolIndex' => $poolIndex,
186 'poolName' => $poolName,
187 'phyDescr' => $phyDescr,
188 'phyName' => $phyName
195 $session->get_table( -baseoid =>
196 $dd->oiddef('ciscoMemoryPoolName') );
198 if( defined $MemoryPool and scalar( %{$MemoryPool} ) > 0 )
200 $devdetails->storeSnmpVars( $MemoryPool );
201 $devdetails->setCap('ciscoMemoryPool');
203 $data->{'ciscoMemoryPool'} = {};
207 getSnmpIndices($dd->oiddef('ciscoMemoryPoolName')) )
209 # According to CISCO-MEMORY-POOL-MIB, only types 1 to 5
210 # are static, and the rest are dynamic
211 # (of which none ever seen)
216 snmpVar($dd->oiddef('ciscoMemoryPoolName') .
219 $data->{'ciscoMemoryPool'}{$memType} = $name;
226 if( $devdetails->param('CiscoGeneric::disable-cpu-stats') ne 'yes' )
229 $session->get_table( -baseoid => $dd->oiddef('cpmCPUTotalTable') );
231 if( defined $ciscoCpuStats )
233 $devdetails->setCap('ciscoCpuStats');
234 $devdetails->storeSnmpVars( $ciscoCpuStats );
236 $data->{'ciscoCpuStats'} = {};
238 # Find multiple CPU entries pointing to the same Phy index
239 my %phyReferers = ();
242 getSnmpIndices($dd->oiddef('cpmCPUTotalPhysicalIndex') ) )
244 my $phyIndex = $devdetails->
245 snmpVar($dd->oiddef('cpmCPUTotalPhysicalIndex') .
247 $phyReferers{$phyIndex}++;
252 getSnmpIndices($dd->oiddef('cpmCPUTotalPhysicalIndex') ) )
254 $data->{'ciscoCpuStats'}{$INDEX} = {};
256 my $phyIndex = $devdetails->
257 snmpVar($dd->oiddef('cpmCPUTotalPhysicalIndex') .
263 if( $phyIndex > 0 and
264 $devdetails->isDevType('RFC2737_ENTITY_MIB') )
266 $phyDescr = $data->{'entityPhysical'}{$phyIndex}{'descr'};
267 $phyName = $data->{'entityPhysical'}{$phyIndex}{'name'};
270 $phyDescr = 'Central Processor' unless $phyDescr;
271 $phyName = ('Chassis #' . $phyIndex) unless $phyName;
273 my $cpuNick = $phyName;
275 $cpuNick =~ s/\W/_/g;
276 $cpuNick =~ s/_+/_/g;
278 if( $phyReferers{$phyIndex} > 1 )
280 $phyDescr .= ' (' . $INDEX . ')';
281 $cpuNick .= '_' . $INDEX;
284 $data->{'ciscoCpuStats'}{$INDEX} = {
285 'phy-index' => $phyIndex,
286 'phy-name' => $phyName,
287 'phy-descr' => $phyDescr,
288 'phy-referers' => $phyReferers{$phyIndex},
289 'cpu-nick' => $cpuNick };
291 if( $devdetails->hasOID( $dd->oiddef('cpmCPUTotal1minRev') .
294 $data->{'ciscoCpuStats'}{$INDEX}{'stats-type'} = 'revised';
300 # Although OLD-CISCO-CPU-MIB is implemented in IOS only,
301 # it is easier to leave it here in Generic
303 if( $dd->checkSnmpOID('avgBusy1') )
305 $devdetails->setCap('old-ciscoCpuStats');
306 push( @{$data->{'templates'}}, 'CiscoGeneric::old-cisco-cpu' );
317 my $devdetails = shift;
321 my $data = $devdetails->data();
323 # Temperature Sensors
325 if( $devdetails->hasCap('ciscoTemperatureSensors') )
327 # Create a subtree for the sensors
328 my $subtreeName = 'Temperature_Sensors';
331 $devdetails->param('CiscoGeneric::use-fahrenheit') eq 'yes';
334 'node-display-name' => 'Temperature Sensors',
336 my $templates = [ 'CiscoGeneric::cisco-temperature-subtree' ];
339 $devdetails->param('CiscoGeneric::file-per-sensor') eq 'yes';
341 $param->{'data-file'} = '%snmp-host%_sensors' .
342 ($filePerSensor ? '_%sensor-index%':'') .
343 ($fahrenheit ? '_fahrenheit':'') . '.rrd';
345 my $subtreeNode = $cb->addSubtree( $devNode, $subtreeName,
346 $param, $templates );
348 foreach my $sIndex ( sort {$a<=>$b} keys
349 %{$data->{'ciscoTemperatureSensors'}} )
351 my $leafName = sprintf( 'sensor_%.2d', $sIndex );
354 $data->{'ciscoTemperatureSensors'}{$sIndex}{'description'};
356 $data->{'ciscoTemperatureSensors'}{$sIndex}{'threshold'};
360 $threshold = $threshold * 1.8 + 32;
364 'sensor-index' => $sIndex,
365 'sensor-description' => $desc,
366 'upper-limit' => $threshold
369 my $templates = ['CiscoGeneric::cisco-temperature-sensor' .
370 ($fahrenheit ? '-fahrenheit':'')];
372 my $monitor = $data->{'ciscoTemperatureSensors'}{$sIndex}->{
373 'selectorActions'}{'Monitor'};
374 if( defined( $monitor ) )
376 $param->{'monitor'} = $monitor;
379 my $tset = $data->{'ciscoTemperatureSensors'}{$sIndex}->{
380 'selectorActions'}{'TokensetMember'};
381 if( defined( $tset ) )
383 $param->{'tokenset-member'} = $tset;
386 $cb->addLeaf( $subtreeNode, $leafName, $param, $templates );
392 if( $devdetails->hasCap('ciscoPowerSupplies') )
394 # Create a subtree for the power supplies
395 my $subtreeName = 'Power_Supplies';
398 'node-display-name' => 'Power Supplies',
399 'comment' => 'Power supplies status',
400 'precedence' => -600,
404 $param->{'data-file'} = '%system-id%_power.rrd';
406 my $monitor = $devdetails->param('CiscoGeneric::power-monitor');
407 if( length( $monitor ) > 0 )
409 $param->{'monitor'} = $monitor;
412 my $subtreeNode = $cb->addSubtree( $devNode, $subtreeName,
413 $param, $templates );
415 foreach my $sIndex ( sort {$a<=>$b} @{$data->{'ciscoPowerSupplies'}} )
417 my $leafName = sprintf( 'power_%.2d', $sIndex );
420 'power-index' => $sIndex
423 my $templates = ['CiscoGeneric::cisco-power-supply'];
425 $cb->addLeaf( $subtreeNode, $leafName, $param, $templates );
432 if( $devdetails->hasCap('cempMemPool') or
433 $devdetails->hasCap('ciscoMemoryPool') )
435 my $subtreeName = 'Memory_Usage';
438 'node-display-name' => 'Memory Usage',
439 'precedence' => '-100',
440 'comment' => 'Router memory utilization'
444 $cb->addSubtree( $devNode, $subtreeName, $param,
445 ['CiscoGeneric::cisco-memusage-subtree']);
447 if( $devdetails->hasCap('cempMemPool') )
449 foreach my $INDEX ( sort {
450 $data->{'cempMemPool'}{$a}{'phyIndex'} <=>
451 $data->{'cempMemPool'}{$b}{'phyIndex'} or
452 $data->{'cempMemPool'}{$a}{'poolIndex'} <=>
453 $data->{'cempMemPool'}{$b}{'poolIndex'} }
454 keys %{$data->{'cempMemPool'}} )
456 my $pool = $data->{'cempMemPool'}{$INDEX};
458 # Chop off the long chassis description, like
459 # uBR7246VXR chassis, Hw Serial#: XXXXX, Hw Revision: A
460 my $phyName = $pool->{'phyName'};
461 if( $phyName =~ /chassis/ )
466 my $poolSubtreeName =
467 $phyName . '_' . $pool->{'poolName'};
468 $poolSubtreeName =~ s/^\///;
469 $poolSubtreeName =~ s/\W/_/g;
470 $poolSubtreeName =~ s/_+/_/g;
474 $param->{'comment'} =
475 $pool->{'poolName'} . ' memory of ';
476 if( $pool->{'phyDescr'} eq $pool->{'phyName'} )
478 $param->{'comment'} .= $phyName;
482 $param->{'comment'} .=
483 $pool->{'phyDescr'} . ' in ' . $phyName;
486 $param->{'mempool-index'} = $INDEX;
487 $param->{'mempool-phyindex'} = $pool->{'phyIndex'};
488 $param->{'mempool-poolindex'} = $pool->{'poolIndex'};
490 $param->{'mempool-name'} = $pool->{'poolName'};
491 $param->{'precedence'} =
493 $pool->{'phyIndex'} * 100 - $pool->{'poolIndex'});
495 $cb->addSubtree( $subtreeNode, $poolSubtreeName, $param,
496 [ 'CiscoGeneric::cisco-enh-mempool' ]);
502 ( sort {$a<=>$b} keys %{$data->{'ciscoMemoryPool'}} )
504 my $poolName = $data->{'ciscoMemoryPool'}{$memType};
506 my $poolSubtreeName = $poolName;
507 $poolSubtreeName =~ s/^\///;
508 $poolSubtreeName =~ s/\W/_/g;
509 $poolSubtreeName =~ s/_+/_/g;
512 'comment' => 'Memory Pool: ' . $poolName,
513 'mempool-type' => $memType,
514 'mempool-name' => $poolName,
515 'precedence' => sprintf("%d", 1000 - $memType)
518 $cb->addSubtree( $subtreeNode, $poolSubtreeName,
519 $param, [ 'CiscoGeneric::cisco-mempool' ]);
524 if( $devdetails->hasCap('ciscoCpuStats') )
526 my $subtreeName = 'CPU_Usage';
528 'node-display-name' => 'CPU Usage',
529 'precedence' => '-500',
530 'comment' => 'Overall CPU busy percentage'
534 $cb->addSubtree( $devNode, $subtreeName, $param,
535 ['CiscoGeneric::cisco-cpu-usage-subtree']);
537 foreach my $INDEX ( sort {$a<=>$b} keys %{$data->{'ciscoCpuStats'}} )
539 my $cpu = $data->{'ciscoCpuStats'}{$INDEX};
542 'comment' => $cpu->{'phy-descr'} . ' in ' . $cpu->{'phy-name'}
545 # On newer dual-CPU routers, several (two seen) CPU entries
546 # refer to the same physical entity. For such entries,
547 # we map them directly to cpmCPUTotalTable index.
548 if( $cpu->{'phy-referers'} > 1 )
550 $param->{'cisco-cpu-indexmap'} = $INDEX;
551 $param->{'cisco-cpu-ref'} = $INDEX;
555 $param->{'entity-phy-index'} = $cpu->{'phy-index'};
556 $param->{'cisco-cpu-ref'} = '%entity-phy-index%';
561 if( $cpu->{'stats-type'} eq 'revised' )
563 push( @templates, 'CiscoGeneric::cisco-cpu-revised' );
567 push( @templates, 'CiscoGeneric::cisco-cpu' );
570 my $cpuNode = $cb->addSubtree( $subtreeNode, $cpu->{'cpu-nick'},
571 $param, \@templates );
573 my $tset = $cpu->{'selectorActions'}{'TokensetMember'};
574 if( defined( $tset ) )
576 $cb->addLeaf( $cpuNode, 'CPU_Total_1min',
577 { 'tokenset-member' => $tset } );
585 #######################################
586 # Selectors interface
589 $Torrus::DevDiscover::selectorsRegistry{'CiscoSensor'} = {
590 'getObjects' => \&getSelectorObjects,
591 'getObjectName' => \&getSelectorObjectName,
592 'checkAttribute' => \&checkSelectorAttribute,
593 'applyAction' => \&applySelectorAction,
596 $Torrus::DevDiscover::selectorsRegistry{'CiscoCPU'} = {
597 'getObjects' => \&getSelectorObjects,
598 'getObjectName' => \&getSelectorObjectName,
599 'checkAttribute' => \&checkSelectorAttribute,
600 'applyAction' => \&applySelectorAction,
603 ## Objects are interface indexes
605 sub getSelectorObjects
607 my $devdetails = shift;
610 my $data = $devdetails->data();
613 if( $objType eq 'CiscoSensor' )
615 @ret = keys( %{$data->{'ciscoTemperatureSensors'}} );
617 elsif( $objType eq 'CiscoCPU' )
619 @ret = keys( %{$data->{'ciscoCpuStats'}} );
622 return( sort {$a<=>$b} @ret );
626 sub checkSelectorAttribute
628 my $devdetails = shift;
632 my $checkval = shift;
634 my $data = $devdetails->data();
639 if( $objType eq 'CiscoSensor' )
641 my $sensor = $data->{'ciscoTemperatureSensors'}{$object};
642 if( $attr eq 'SensorDescr' )
644 $value = $sensor->{'description'};
648 Error('Unknown CiscoSensor selector attribute: ' . $attr);
652 elsif( $objType eq 'CiscoCPU' )
654 my $cpu = $data->{'ciscoCpuStats'}{$object};
655 if( $attr eq 'CPUName' )
657 $value = $cpu->{'cpu-nick'};
659 elsif( $attr eq 'CPUDescr' )
661 $value = $cpu->{'cpu-descr'};
665 Error('Unknown CiscoCPU selector attribute: ' . $attr);
670 return eval( '$value' . ' ' . $operator . '$checkval' ) ? 1:0;
674 sub getSelectorObjectName
676 my $devdetails = shift;
680 my $data = $devdetails->data();
683 if( $objType eq 'CiscoSensor' )
685 $name = $data->{'ciscoTemperatureSensors'}{$object}{'description'};
687 elsif( $objType eq 'CiscoCPU' )
689 $name = $data->{'ciscoCpuStats'}{$object}{'cpu-nick'};
695 my %knownSelectorActions =
699 'TokensetMember' => 1 },
701 'TokensetMember' => 1 }
705 sub applySelectorAction
707 my $devdetails = shift;
713 my $data = $devdetails->data();
715 if( $objType eq 'CiscoSensor' )
717 $objref = $data->{'ciscoTemperatureSensors'}{$object};
719 elsif( $objType eq 'CiscoCPU' )
721 $objref = $data->{'ciscoCpuStats'}{$object};
724 if( $knownSelectorActions{$objType}{$action} )
726 $objref->{'selectorActions'}{$action} = $arg;
730 Error('Unknown Cisco selector action: ' . $action);
741 # indent-tabs-mode: nil
742 # perl-indent-level: 4