diff options
Diffstat (limited to 'torrus/perllib/Torrus/DevDiscover/CiscoGeneric.pm')
-rw-r--r-- | torrus/perllib/Torrus/DevDiscover/CiscoGeneric.pm | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/torrus/perllib/Torrus/DevDiscover/CiscoGeneric.pm b/torrus/perllib/Torrus/DevDiscover/CiscoGeneric.pm new file mode 100644 index 000000000..4262bdd71 --- /dev/null +++ b/torrus/perllib/Torrus/DevDiscover/CiscoGeneric.pm @@ -0,0 +1,743 @@ +# Copyright (C) 2002 Stanislav Sinyagin +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +# $Id: CiscoGeneric.pm,v 1.1 2010-12-27 00:03:48 ivan Exp $ +# Stanislav Sinyagin <ssinyagin@yahoo.com> + +# Common Cisco MIBs, supported by many IOS and CatOS devices + +package Torrus::DevDiscover::CiscoGeneric; + +use strict; +use Torrus::Log; + + +$Torrus::DevDiscover::registry{'CiscoGeneric'} = { + 'sequence' => 500, + 'checkdevtype' => \&checkdevtype, + 'discover' => \&discover, + 'buildConfig' => \&buildConfig + }; + + +our %oiddef = + ( + # CISCO-SMI + 'cisco' => '1.3.6.1.4.1.9', + + # CISCO-ENVMON-MIB + 'ciscoEnvMonTemperatureStatusDescr' => '1.3.6.1.4.1.9.9.13.1.3.1.2', + 'ciscoEnvMonTemperatureStatusValue' => '1.3.6.1.4.1.9.9.13.1.3.1.3', + 'ciscoEnvMonTemperatureThreshold' => '1.3.6.1.4.1.9.9.13.1.3.1.4', + 'ciscoEnvMonTemperatureStatusState' => '1.3.6.1.4.1.9.9.13.1.3.1.6', + 'ciscoEnvMonSupplyState' => '1.3.6.1.4.1.9.9.13.1.5.1.3', + + # CISCO-ENHANCED-MEMPOOL-MIB + 'cempMemPoolName' => '1.3.6.1.4.1.9.9.221.1.1.1.1.3', + + # CISCO-MEMORY-POOL-MIB + 'ciscoMemoryPoolName' => '1.3.6.1.4.1.9.9.48.1.1.1.2', + + # CISCO-PROCESS-MIB + 'cpmCPUTotalTable' => '1.3.6.1.4.1.9.9.109.1.1.1.1', + 'cpmCPUTotalPhysicalIndex' => '1.3.6.1.4.1.9.9.109.1.1.1.1.2', + 'cpmCPUTotal1minRev' => '1.3.6.1.4.1.9.9.109.1.1.1.1.7', + 'cpmCPUTotal1min' => '1.3.6.1.4.1.9.9.109.1.1.1.1.4', + + # OLD-CISCO-CPU-MIB + 'avgBusy1' => '1.3.6.1.4.1.9.2.1.57.0' + ); + +sub checkdevtype +{ + my $dd = shift; + my $devdetails = shift; + + if( not $dd->oidBaseMatch + ( 'cisco', $devdetails->snmpVar( $dd->oiddef('sysObjectID') ) ) ) + { + return 0; + } + + return 1; +} + + +sub discover +{ + my $dd = shift; + my $devdetails = shift; + + my $session = $dd->session(); + my $data = $devdetails->data(); + + if( $devdetails->param('CiscoGeneric::disable-sensors') ne 'yes' ) + { + # Check if temperature sensors are supported + + my $oidTempDescr = $dd->oiddef('ciscoEnvMonTemperatureStatusDescr'); + my $oidTempValue = $dd->oiddef('ciscoEnvMonTemperatureStatusValue'); + my $oidTempThrsh = $dd->oiddef('ciscoEnvMonTemperatureThreshold'); + my $oidTempState = $dd->oiddef('ciscoEnvMonTemperatureStatusState'); + + if( defined $session->get_table( -baseoid => $oidTempValue ) ) + { + $devdetails->setCap('ciscoTemperatureSensors'); + $data->{'ciscoTemperatureSensors'} = {}; + + my $tempDescr = $session->get_table( -baseoid => $oidTempDescr ); + my $tempThrsh = $session->get_table( -baseoid => $oidTempThrsh ); + + # Get the sensor states and ignore those notPresent(5) + + my $tempState = $session->get_table( -baseoid => $oidTempState ); + + my $prefixLen = length( $oidTempDescr ) + 1; + while( my( $oid, $descr ) = each %{$tempDescr} ) + { + # Extract the sensor index from OID + my $sIndex = substr( $oid, $prefixLen ); + + if( $tempState->{$oidTempState.'.'.$sIndex} != 5 ) + { + $data->{'ciscoTemperatureSensors'}{$sIndex}{ + 'description'} = $descr; + $data->{'ciscoTemperatureSensors'}{$sIndex}{ + 'threshold'} = $tempThrsh->{$oidTempThrsh.'.'.$sIndex}; + } + } + } + } + + if( $devdetails->param('CiscoGeneric::disable-psupplies') ne 'yes' ) + { + # Check if power supply status is supported + + my $oidSupply = $dd->oiddef('ciscoEnvMonSupplyState'); + + my $supplyTable = $session->get_table( -baseoid => $oidSupply ); + if( defined( $supplyTable ) ) + { + $devdetails->setCap('ciscoPowerSupplies'); + $data->{'ciscoPowerSupplies'} = []; + + my $prefixLen = length( $oidSupply ) + 1; + while( my( $oid, $val ) = each %{$supplyTable} ) + { + # Extract the supply index from OID + my $sIndex = substr( $oid, $prefixLen ); + + #check if the value is not notPresent(5) + if( $val != 5 ) + { + push( @{$data->{'ciscoPowerSupplies'}}, $sIndex ); + } + } + } + } + + if( $devdetails->param('CiscoGeneric::disable-memory-pools') ne 'yes' ) + { + my $eMemPool = + $session->get_table( -baseoid => + $dd->oiddef('cempMemPoolName') ); + if( defined $eMemPool and scalar( %{$eMemPool} ) > 0 and + $devdetails->isDevType('RFC2737_ENTITY_MIB') ) + { + $devdetails->storeSnmpVars( $eMemPool ); + $devdetails->setCap('cempMemPool'); + $data->{'cempMemPool'} = {}; + + foreach my $INDEX + ( $devdetails-> + getSnmpIndices($dd->oiddef('cempMemPoolName') ) ) + { + # $INDEX is a pair entPhysicalIndex . cempMemPoolIndex + my ( $phyIndex, $poolIndex ) = split('\.', $INDEX); + + my $poolName = $devdetails-> + snmpVar($dd->oiddef('cempMemPoolName') . '.' . $INDEX ); + + $poolName = 'Processor' unless $poolName; + + my $phyDescr = $data->{'entityPhysical'}{$phyIndex}{'descr'}; + my $phyName = $data->{'entityPhysical'}{$phyIndex}{'name'}; + + $phyDescr = 'Processor' unless $phyDescr; + $phyName = ('Chassis #' . + $phyIndex) unless $phyName; + + $data->{'cempMemPool'}{$INDEX} = { + 'phyIndex' => $phyIndex, + 'poolIndex' => $poolIndex, + 'poolName' => $poolName, + 'phyDescr' => $phyDescr, + 'phyName' => $phyName + }; + } + } + else + { + my $MemoryPool = + $session->get_table( -baseoid => + $dd->oiddef('ciscoMemoryPoolName') ); + + if( defined $MemoryPool and scalar( %{$MemoryPool} ) > 0 ) + { + $devdetails->storeSnmpVars( $MemoryPool ); + $devdetails->setCap('ciscoMemoryPool'); + + $data->{'ciscoMemoryPool'} = {}; + + foreach my $memType + ( $devdetails-> + getSnmpIndices($dd->oiddef('ciscoMemoryPoolName')) ) + { + # According to CISCO-MEMORY-POOL-MIB, only types 1 to 5 + # are static, and the rest are dynamic + # (of which none ever seen) + if( $memType <= 5 ) + { + my $name = + $devdetails-> + snmpVar($dd->oiddef('ciscoMemoryPoolName') . + '.' . $memType ); + + $data->{'ciscoMemoryPool'}{$memType} = $name; + } + } + } + } + } + + if( $devdetails->param('CiscoGeneric::disable-cpu-stats') ne 'yes' ) + { + my $ciscoCpuStats = + $session->get_table( -baseoid => $dd->oiddef('cpmCPUTotalTable') ); + + if( defined $ciscoCpuStats ) + { + $devdetails->setCap('ciscoCpuStats'); + $devdetails->storeSnmpVars( $ciscoCpuStats ); + + $data->{'ciscoCpuStats'} = {}; + + # Find multiple CPU entries pointing to the same Phy index + my %phyReferers = (); + foreach my $INDEX + ( $devdetails-> + getSnmpIndices($dd->oiddef('cpmCPUTotalPhysicalIndex') ) ) + { + my $phyIndex = $devdetails-> + snmpVar($dd->oiddef('cpmCPUTotalPhysicalIndex') . + '.' . $INDEX ); + $phyReferers{$phyIndex}++; + } + + foreach my $INDEX + ( $devdetails-> + getSnmpIndices($dd->oiddef('cpmCPUTotalPhysicalIndex') ) ) + { + $data->{'ciscoCpuStats'}{$INDEX} = {}; + + my $phyIndex = $devdetails-> + snmpVar($dd->oiddef('cpmCPUTotalPhysicalIndex') . + '.' . $INDEX ); + + my $phyDescr; + my $phyName; + + if( $phyIndex > 0 and + $devdetails->isDevType('RFC2737_ENTITY_MIB') ) + { + $phyDescr = $data->{'entityPhysical'}{$phyIndex}{'descr'}; + $phyName = $data->{'entityPhysical'}{$phyIndex}{'name'}; + } + + $phyDescr = 'Central Processor' unless $phyDescr; + $phyName = ('Chassis #' . $phyIndex) unless $phyName; + ; + my $cpuNick = $phyName; + $cpuNick =~ s/^\///; + $cpuNick =~ s/\W/_/g; + $cpuNick =~ s/_+/_/g; + + if( $phyReferers{$phyIndex} > 1 ) + { + $phyDescr .= ' (' . $INDEX . ')'; + $cpuNick .= '_' . $INDEX; + } + + $data->{'ciscoCpuStats'}{$INDEX} = { + 'phy-index' => $phyIndex, + 'phy-name' => $phyName, + 'phy-descr' => $phyDescr, + 'phy-referers' => $phyReferers{$phyIndex}, + 'cpu-nick' => $cpuNick }; + + if( $devdetails->hasOID( $dd->oiddef('cpmCPUTotal1minRev') . + '.' . $INDEX ) ) + { + $data->{'ciscoCpuStats'}{$INDEX}{'stats-type'} = 'revised'; + } + } + } + else + { + # Although OLD-CISCO-CPU-MIB is implemented in IOS only, + # it is easier to leave it here in Generic + + if( $dd->checkSnmpOID('avgBusy1') ) + { + $devdetails->setCap('old-ciscoCpuStats'); + push( @{$data->{'templates'}}, 'CiscoGeneric::old-cisco-cpu' ); + } + } + } + + return 1; +} + + +sub buildConfig +{ + my $devdetails = shift; + my $cb = shift; + my $devNode = shift; + + my $data = $devdetails->data(); + + # Temperature Sensors + + if( $devdetails->hasCap('ciscoTemperatureSensors') ) + { + # Create a subtree for the sensors + my $subtreeName = 'Temperature_Sensors'; + + my $fahrenheit = + $devdetails->param('CiscoGeneric::use-fahrenheit') eq 'yes'; + + my $param = { + 'node-display-name' => 'Temperature Sensors', + }; + my $templates = [ 'CiscoGeneric::cisco-temperature-subtree' ]; + + my $filePerSensor = + $devdetails->param('CiscoGeneric::file-per-sensor') eq 'yes'; + + $param->{'data-file'} = '%snmp-host%_sensors' . + ($filePerSensor ? '_%sensor-index%':'') . + ($fahrenheit ? '_fahrenheit':'') . '.rrd'; + + my $subtreeNode = $cb->addSubtree( $devNode, $subtreeName, + $param, $templates ); + + foreach my $sIndex ( sort {$a<=>$b} keys + %{$data->{'ciscoTemperatureSensors'}} ) + { + my $leafName = sprintf( 'sensor_%.2d', $sIndex ); + + my $desc = + $data->{'ciscoTemperatureSensors'}{$sIndex}{'description'}; + my $threshold = + $data->{'ciscoTemperatureSensors'}{$sIndex}{'threshold'}; + + if( $fahrenheit ) + { + $threshold = $threshold * 1.8 + 32; + } + + my $param = { + 'sensor-index' => $sIndex, + 'sensor-description' => $desc, + 'upper-limit' => $threshold + }; + + my $templates = ['CiscoGeneric::cisco-temperature-sensor' . + ($fahrenheit ? '-fahrenheit':'')]; + + my $monitor = $data->{'ciscoTemperatureSensors'}{$sIndex}->{ + 'selectorActions'}{'Monitor'}; + if( defined( $monitor ) ) + { + $param->{'monitor'} = $monitor; + } + + my $tset = $data->{'ciscoTemperatureSensors'}{$sIndex}->{ + 'selectorActions'}{'TokensetMember'}; + if( defined( $tset ) ) + { + $param->{'tokenset-member'} = $tset; + } + + $cb->addLeaf( $subtreeNode, $leafName, $param, $templates ); + } + } + + # Power supplies + + if( $devdetails->hasCap('ciscoPowerSupplies') ) + { + # Create a subtree for the power supplies + my $subtreeName = 'Power_Supplies'; + + my $param = { + 'node-display-name' => 'Power Supplies', + 'comment' => 'Power supplies status', + 'precedence' => -600, + }; + my $templates = []; + + $param->{'data-file'} = '%system-id%_power.rrd'; + + my $monitor = $devdetails->param('CiscoGeneric::power-monitor'); + if( length( $monitor ) > 0 ) + { + $param->{'monitor'} = $monitor; + } + + my $subtreeNode = $cb->addSubtree( $devNode, $subtreeName, + $param, $templates ); + + foreach my $sIndex ( sort {$a<=>$b} @{$data->{'ciscoPowerSupplies'}} ) + { + my $leafName = sprintf( 'power_%.2d', $sIndex ); + + my $param = { + 'power-index' => $sIndex + }; + + my $templates = ['CiscoGeneric::cisco-power-supply']; + + $cb->addLeaf( $subtreeNode, $leafName, $param, $templates ); + } + } + + + # Memory Pools + + if( $devdetails->hasCap('cempMemPool') or + $devdetails->hasCap('ciscoMemoryPool') ) + { + my $subtreeName = 'Memory_Usage'; + + my $param = { + 'node-display-name' => 'Memory Usage', + 'precedence' => '-100', + 'comment' => 'Router memory utilization' + }; + + my $subtreeNode = + $cb->addSubtree( $devNode, $subtreeName, $param, + ['CiscoGeneric::cisco-memusage-subtree']); + + if( $devdetails->hasCap('cempMemPool') ) + { + foreach my $INDEX ( sort { + $data->{'cempMemPool'}{$a}{'phyIndex'} <=> + $data->{'cempMemPool'}{$b}{'phyIndex'} or + $data->{'cempMemPool'}{$a}{'poolIndex'} <=> + $data->{'cempMemPool'}{$b}{'poolIndex'} } + keys %{$data->{'cempMemPool'}} ) + { + my $pool = $data->{'cempMemPool'}{$INDEX}; + + # Chop off the long chassis description, like + # uBR7246VXR chassis, Hw Serial#: XXXXX, Hw Revision: A + my $phyName = $pool->{'phyName'}; + if( $phyName =~ /chassis/ ) + { + $phyName =~ s/,.+//; + } + + my $poolSubtreeName = + $phyName . '_' . $pool->{'poolName'}; + $poolSubtreeName =~ s/^\///; + $poolSubtreeName =~ s/\W/_/g; + $poolSubtreeName =~ s/_+/_/g; + + my $param = {}; + + $param->{'comment'} = + $pool->{'poolName'} . ' memory of '; + if( $pool->{'phyDescr'} eq $pool->{'phyName'} ) + { + $param->{'comment'} .= $phyName; + } + else + { + $param->{'comment'} .= + $pool->{'phyDescr'} . ' in ' . $phyName; + } + + $param->{'mempool-index'} = $INDEX; + $param->{'mempool-phyindex'} = $pool->{'phyIndex'}; + $param->{'mempool-poolindex'} = $pool->{'poolIndex'}; + + $param->{'mempool-name'} = $pool->{'poolName'}; + $param->{'precedence'} = + sprintf("%d", 1000 - + $pool->{'phyIndex'} * 100 - $pool->{'poolIndex'}); + + $cb->addSubtree( $subtreeNode, $poolSubtreeName, $param, + [ 'CiscoGeneric::cisco-enh-mempool' ]); + } + } + else + { + foreach my $memType + ( sort {$a<=>$b} keys %{$data->{'ciscoMemoryPool'}} ) + { + my $poolName = $data->{'ciscoMemoryPool'}{$memType}; + + my $poolSubtreeName = $poolName; + $poolSubtreeName =~ s/^\///; + $poolSubtreeName =~ s/\W/_/g; + $poolSubtreeName =~ s/_+/_/g; + + my $param = { + 'comment' => 'Memory Pool: ' . $poolName, + 'mempool-type' => $memType, + 'mempool-name' => $poolName, + 'precedence' => sprintf("%d", 1000 - $memType) + }; + + $cb->addSubtree( $subtreeNode, $poolSubtreeName, + $param, [ 'CiscoGeneric::cisco-mempool' ]); + } + } + } + + if( $devdetails->hasCap('ciscoCpuStats') ) + { + my $subtreeName = 'CPU_Usage'; + my $param = { + 'node-display-name' => 'CPU Usage', + 'precedence' => '-500', + 'comment' => 'Overall CPU busy percentage' + }; + + my $subtreeNode = + $cb->addSubtree( $devNode, $subtreeName, $param, + ['CiscoGeneric::cisco-cpu-usage-subtree']); + + foreach my $INDEX ( sort {$a<=>$b} keys %{$data->{'ciscoCpuStats'}} ) + { + my $cpu = $data->{'ciscoCpuStats'}{$INDEX}; + + my $param = { + 'comment' => $cpu->{'phy-descr'} . ' in ' . $cpu->{'phy-name'} + }; + + # On newer dual-CPU routers, several (two seen) CPU entries + # refer to the same physical entity. For such entries, + # we map them directly to cpmCPUTotalTable index. + if( $cpu->{'phy-referers'} > 1 ) + { + $param->{'cisco-cpu-indexmap'} = $INDEX; + $param->{'cisco-cpu-ref'} = $INDEX; + } + else + { + $param->{'entity-phy-index'} = $cpu->{'phy-index'}; + $param->{'cisco-cpu-ref'} = '%entity-phy-index%'; + } + + my @templates; + + if( $cpu->{'stats-type'} eq 'revised' ) + { + push( @templates, 'CiscoGeneric::cisco-cpu-revised' ); + } + else + { + push( @templates, 'CiscoGeneric::cisco-cpu' ); + } + + my $cpuNode = $cb->addSubtree( $subtreeNode, $cpu->{'cpu-nick'}, + $param, \@templates ); + + my $tset = $cpu->{'selectorActions'}{'TokensetMember'}; + if( defined( $tset ) ) + { + $cb->addLeaf( $cpuNode, 'CPU_Total_1min', + { 'tokenset-member' => $tset } ); + } + } + } +} + + + +####################################### +# Selectors interface +# + +$Torrus::DevDiscover::selectorsRegistry{'CiscoSensor'} = { + 'getObjects' => \&getSelectorObjects, + 'getObjectName' => \&getSelectorObjectName, + 'checkAttribute' => \&checkSelectorAttribute, + 'applyAction' => \&applySelectorAction, +}; + +$Torrus::DevDiscover::selectorsRegistry{'CiscoCPU'} = { + 'getObjects' => \&getSelectorObjects, + 'getObjectName' => \&getSelectorObjectName, + 'checkAttribute' => \&checkSelectorAttribute, + 'applyAction' => \&applySelectorAction, +}; + +## Objects are interface indexes + +sub getSelectorObjects +{ + my $devdetails = shift; + my $objType = shift; + + my $data = $devdetails->data(); + my @ret; + + if( $objType eq 'CiscoSensor' ) + { + @ret = keys( %{$data->{'ciscoTemperatureSensors'}} ); + } + elsif( $objType eq 'CiscoCPU' ) + { + @ret = keys( %{$data->{'ciscoCpuStats'}} ); + } + + return( sort {$a<=>$b} @ret ); +} + + +sub checkSelectorAttribute +{ + my $devdetails = shift; + my $object = shift; + my $objType = shift; + my $attr = shift; + my $checkval = shift; + + my $data = $devdetails->data(); + + my $value; + my $operator = '=~'; + + if( $objType eq 'CiscoSensor' ) + { + my $sensor = $data->{'ciscoTemperatureSensors'}{$object}; + if( $attr eq 'SensorDescr' ) + { + $value = $sensor->{'description'}; + } + else + { + Error('Unknown CiscoSensor selector attribute: ' . $attr); + $value = ''; + } + } + elsif( $objType eq 'CiscoCPU' ) + { + my $cpu = $data->{'ciscoCpuStats'}{$object}; + if( $attr eq 'CPUName' ) + { + $value = $cpu->{'cpu-nick'}; + } + elsif( $attr eq 'CPUDescr' ) + { + $value = $cpu->{'cpu-descr'}; + } + else + { + Error('Unknown CiscoCPU selector attribute: ' . $attr); + $value = ''; + } + } + + return eval( '$value' . ' ' . $operator . '$checkval' ) ? 1:0; +} + + +sub getSelectorObjectName +{ + my $devdetails = shift; + my $object = shift; + my $objType = shift; + + my $data = $devdetails->data(); + my $name; + + if( $objType eq 'CiscoSensor' ) + { + $name = $data->{'ciscoTemperatureSensors'}{$object}{'description'}; + } + elsif( $objType eq 'CiscoCPU' ) + { + $name = $data->{'ciscoCpuStats'}{$object}{'cpu-nick'}; + } + return $name; +} + + +my %knownSelectorActions = + ( + 'CiscoSensor' => { + 'Monitor' => 1, + 'TokensetMember' => 1 }, + 'CiscoCPU' => { + 'TokensetMember' => 1 } + ); + + +sub applySelectorAction +{ + my $devdetails = shift; + my $object = shift; + my $objType = shift; + my $action = shift; + my $arg = shift; + + my $data = $devdetails->data(); + my $objref; + if( $objType eq 'CiscoSensor' ) + { + $objref = $data->{'ciscoTemperatureSensors'}{$object}; + } + elsif( $objType eq 'CiscoCPU' ) + { + $objref = $data->{'ciscoCpuStats'}{$object}; + } + + if( $knownSelectorActions{$objType}{$action} ) + { + $objref->{'selectorActions'}{$action} = $arg; + } + else + { + Error('Unknown Cisco selector action: ' . $action); + } +} + + + +1; + + +# Local Variables: +# mode: perl +# indent-tabs-mode: nil +# perl-indent-level: 4 +# End: |