diff options
Diffstat (limited to 'torrus/perllib/Torrus/DevDiscover.pm')
-rw-r--r-- | torrus/perllib/Torrus/DevDiscover.pm | 1106 |
1 files changed, 1106 insertions, 0 deletions
diff --git a/torrus/perllib/Torrus/DevDiscover.pm b/torrus/perllib/Torrus/DevDiscover.pm new file mode 100644 index 000000000..b6ee8eef8 --- /dev/null +++ b/torrus/perllib/Torrus/DevDiscover.pm @@ -0,0 +1,1106 @@ +# Copyright (C) 2002-2010 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: DevDiscover.pm,v 1.1 2010-12-27 00:03:43 ivan Exp $ +# Stanislav Sinyagin <ssinyagin@yahoo.com> + +# Core SNMP device discovery module + +package Torrus::DevDiscover::DevDetails; + +package Torrus::DevDiscover; + +use strict; +use POSIX qw(strftime); +use Net::SNMP qw(:snmp :asn1); +use Digest::MD5 qw(md5); + +use Torrus::Log; + +BEGIN +{ + foreach my $mod ( @Torrus::DevDiscover::loadModules ) + { + eval( 'require ' . $mod ); + die( $@ ) if $@; + } +} + +# Custom overlays for templates +# overlayName -> +# 'Module::templateName' -> { 'name' => 'templateName', +# 'source' => 'filename.xml' } +our %templateOverlays; + +our @requiredParams = + ( + 'snmp-port', + 'snmp-version', + 'snmp-timeout', + 'snmp-retries', + 'data-dir', + 'snmp-host' + ); + +our %defaultParams; + +$defaultParams{'rrd-hwpredict'} = 'no'; +$defaultParams{'domain-name'} = ''; +$defaultParams{'host-subtree'} = ''; +$defaultParams{'snmp-check-sysuptime'} = 'yes'; +$defaultParams{'show-recursive'} = 'yes'; +$defaultParams{'snmp-ipversion'} = '4'; +$defaultParams{'snmp-transport'} = 'udp'; + +our @copyParams = + ( 'collector-period', + 'collector-timeoffset', + 'collector-dispersed-timeoffset', + 'collector-timeoffset-min', + 'collector-timeoffset-max', + 'collector-timeoffset-step', + 'comment', + 'domain-name', + 'monitor-period', + 'monitor-timeoffset', + 'nodeid-device', + 'show-recursive', + 'snmp-host', + 'snmp-port', + 'snmp-localaddr', + 'snmp-localport', + 'snmp-ipversion', + 'snmp-transport', + 'snmp-community', + 'snmp-version', + 'snmp-username', + 'snmp-authkey', + 'snmp-authpassword', + 'snmp-authprotocol', + 'snmp-privkey', + 'snmp-privpassword', + 'snmp-privprotocol', + 'snmp-timeout', + 'snmp-retries', + 'snmp-oids-per-pdu', + 'snmp-check-sysuptime', + 'snmp-max-msg-size', + 'system-id' ); + + +%Torrus::DevDiscover::oiddef = + ( + 'system' => '1.3.6.1.2.1.1', + 'sysDescr' => '1.3.6.1.2.1.1.1.0', + 'sysObjectID' => '1.3.6.1.2.1.1.2.0', + 'sysUpTime' => '1.3.6.1.2.1.1.3.0', + 'sysContact' => '1.3.6.1.2.1.1.4.0', + 'sysName' => '1.3.6.1.2.1.1.5.0', + 'sysLocation' => '1.3.6.1.2.1.1.6.0' + ); + +my @systemOIDs = ('sysDescr', 'sysObjectID', 'sysUpTime', 'sysContact', + 'sysName', 'sysLocation'); + +sub new +{ + my $self = {}; + my $class = shift; + my %options = @_; + bless $self, $class; + + $self->{'oiddef'} = {}; + $self->{'oidrev'} = {}; + + # Combine all %MODULE::oiddef hashes into one + foreach my $module ( 'Torrus::DevDiscover', + @Torrus::DevDiscover::loadModules ) + { + while( my($name, $oid) = each %{eval('\%'.$module.'::oiddef')} ) + { + die( $@ ) if $@; + $self->{'oiddef'}->{$name} = $oid; + $self->{'oidrev'}->{$oid} = $name; + } + } + + $self->{'datadirs'} = {}; + $self->{'globalData'} = {}; + + return $self; +} + + + +sub globalData +{ + my $self = shift; + return $self->{'globalData'}; +} + + +sub discover +{ + my $self = shift; + my @paramhashes = @_; + + my $devdetails = new Torrus::DevDiscover::DevDetails(); + + foreach my $params ( \%defaultParams, @paramhashes ) + { + $devdetails->setParams( $params ); + } + + foreach my $param ( @requiredParams ) + { + if( not defined( $devdetails->param( $param ) ) ) + { + Error('Required parameter not defined: ' . $param); + return 0; + } + } + + my %snmpargs; + my $community; + + my $version = $devdetails->param( 'snmp-version' ); + $snmpargs{'-version'} = $version; + + foreach my $arg ( qw(-port -localaddr -localport -timeout -retries) ) + { + if( defined( $devdetails->param( 'snmp' . $arg ) ) ) + { + $snmpargs{$arg} = $devdetails->param( 'snmp' . $arg ); + } + } + + $snmpargs{'-domain'} = $devdetails->param('snmp-transport') . '/ipv' . + $devdetails->param('snmp-ipversion'); + + if( $version eq '1' or $version eq '2c' ) + { + $community = $devdetails->param( 'snmp-community' ); + if( not defined( $community ) ) + { + Error('Required parameter not defined: snmp-community'); + return 0; + } + $snmpargs{'-community'} = $community; + + # set maxMsgSize to a maximum value for better compatibility + + my $maxmsgsize = $devdetails->param('snmp-max-msg-size'); + if( defined( $maxmsgsize ) ) + { + $devdetails->setParam('snmp-max-msg-size', $maxmsgsize); + $snmpargs{'-maxmsgsize'} = $maxmsgsize; + } + } + elsif( $version eq '3' ) + { + foreach my $arg ( qw(-username -authkey -authpassword -authprotocol + -privkey -privpassword -privprotocol) ) + { + if( defined $devdetails->param( 'snmp' . $arg ) ) + { + $snmpargs{$arg} = $devdetails->param( 'snmp' . $arg ); + } + } + $community = $snmpargs{'-username'}; + if( not defined( $community ) ) + { + Error('Required parameter not defined: snmp-user'); + return 0; + } + } + else + { + Error('Illegal value for snmp-version parameter: ' . $version); + return 0; + } + + my $hostname = $devdetails->param('snmp-host'); + my $domain = $devdetails->param('domain-name'); + + if( $domain and index($hostname, '.') < 0 and index($hostname, ':') < 0 ) + { + $hostname .= '.' . $domain; + } + $snmpargs{'-hostname'} = $hostname; + + my $port = $snmpargs{'-port'}; + Debug('Discovering host: ' . $hostname . ':' . $port . ':' . $community); + + my ($session, $error) = + Net::SNMP->session( %snmpargs, + -nonblocking => 0, + -translate => ['-all', 0, '-octetstring', 1] ); + if( not defined($session) ) + { + Error('Cannot create SNMP session: ' . $error); + return undef; + } + + my @oids = (); + foreach my $var ( @systemOIDs ) + { + push( @oids, $self->oiddef( $var ) ); + } + + # This is the only checking if the remote agent is alive + + my $result = $session->get_request( -varbindlist => \@oids ); + if( defined $result ) + { + $devdetails->storeSnmpVars( $result ); + } + else + { + # When the remote agent is reacheable, but system objecs are + # not implemented, we get a positive error_status + if( $session->error_status() == 0 ) + { + Error("Unable to communicate with SNMP agent on " . $hostname . + ':' . $port . ':' . $community . " - " . $session->error()); + return undef; + } + } + + my $data = $devdetails->data(); + $data->{'param'} = {}; + + $data->{'templates'} = []; + my $customTmpl = $devdetails->param('custom-host-templates'); + if( length( $customTmpl ) > 0 ) + { + push( @{$data->{'templates'}}, split( /\s*,\s*/, $customTmpl ) ); + } + + # Build host-level legend + my %legendValues = + ( + 10 => { + 'name' => 'Location', + 'value' => $devdetails->snmpVar($self->oiddef('sysLocation')) + }, + 20 => { + 'name' => 'Contact', + 'value' => $devdetails->snmpVar($self->oiddef('sysContact')) + }, + 30 => { + 'name' => 'System ID', + 'value' => $devdetails->param('system-id') + }, + 50 => { + 'name' => 'Description', + 'value' => $devdetails->snmpVar($self->oiddef('sysDescr')) + } + ); + + if( defined( $devdetails->snmpVar($self->oiddef('sysUpTime')) ) ) + { + $legendValues{40}{'name'} = 'Uptime'; + $legendValues{40}{'value'} = + sprintf("%d days since %s", + $devdetails->snmpVar($self->oiddef('sysUpTime')) / + (100*3600*24), + strftime($Torrus::DevDiscover::timeFormat, + localtime(time()))); + } + + my $legend = ''; + foreach my $key ( sort keys %legendValues ) + { + my $text = $legendValues{$key}{'value'}; + if( length( $text ) > 0 ) + { + $text = $devdetails->screenSpecialChars( $text ); + $legend .= $legendValues{$key}{'name'} . ':' . $text . ';'; + } + } + + if( $devdetails->param('suppress-legend') ne 'yes' ) + { + $data->{'param'}{'legend'} = $legend; + } + + # some parameters need just one-to-one copying + + my @hostCopyParams = + split('\s*,\s*', $devdetails->param('host-copy-params')); + + foreach my $param ( @copyParams, @hostCopyParams ) + { + my $val = $devdetails->param( $param ); + if( length( $val ) > 0 ) + { + $data->{'param'}{$param} = $val; + } + } + + # If snmp-host is ipv6 address, system-id needs to be adapted to + # remove colons + + if( not defined( $data->{'param'}{'system-id'} ) and + index($data->{'param'}{'snmp-host'}, ':') >= 0 ) + { + my $systemid = $data->{'param'}{'snmp-host'}; + $systemid =~ s/:/_/g; + $data->{'param'}{'system-id'} = $systemid; + } + + if( not defined( $devdetails->snmpVar($self->oiddef('sysUpTime')) ) ) + { + Debug('Agent does not support sysUpTime'); + $data->{'param'}{'snmp-check-sysuptime'} = 'no'; + } + + $data->{'param'}{'data-dir'} = + $self->genDataDir( $devdetails->param('data-dir'), $hostname ); + + # Register the directory for listDataDirs() + $self->{'datadirs'}{$devdetails->param('data-dir')} = 1; + + $self->{'session'} = $session; + + # some discovery modules need to be disabled on per-device basis + + my %onlyDevtypes; + my $useOnlyDevtypes = 0; + foreach my $devtype ( split('\s*,\s*', + $devdetails->param('only-devtypes') ) ) + { + $onlyDevtypes{$devtype} = 1; + $useOnlyDevtypes = 1; + } + + my %disabledDevtypes; + foreach my $devtype ( split('\s*,\s*', + $devdetails->param('disable-devtypes') ) ) + { + $disabledDevtypes{$devtype} = 1; + } + + # 'checkdevtype' procedures for each known device type return true + # when it's their device. They also research the device capabilities. + my $reg = \%Torrus::DevDiscover::registry; + foreach my $devtype + ( sort {$reg->{$a}{'sequence'} <=> $reg->{$b}{'sequence'}} + keys %{$reg} ) + { + if( ( not $useOnlyDevtypes or $onlyDevtypes{$devtype} ) and + not $disabledDevtypes{$devtype} and + &{$reg->{$devtype}{'checkdevtype'}}($self, $devdetails) ) + { + $devdetails->setDevType( $devtype ); + Debug('Found device type: ' . $devtype); + } + } + + my @devtypes = sort { + $reg->{$a}{'sequence'} <=> $reg->{$b}{'sequence'} + } $devdetails->getDevTypes(); + $data->{'param'}{'devdiscover-devtypes'} = join(',', @devtypes); + + $data->{'param'}{'devdiscover-nodetype'} = '::device'; + + # Do the detailed discovery and prepare data + my $ok = 1; + foreach my $devtype ( @devtypes ) + { + $ok = &{$reg->{$devtype}{'discover'}}($self, $devdetails) ? $ok:0; + } + + delete $self->{'session'}; + $session->close(); + + $devdetails->applySelectors(); + + my $subtree = $devdetails->param('host-subtree'); + if( not defined( $self->{'devdetails'}{$subtree} ) ) + { + $self->{'devdetails'}{$subtree} = []; + } + push( @{$self->{'devdetails'}{$subtree}}, $devdetails ); + + my $define_tokensets = $devdetails->param('define-tokensets'); + if( defined( $define_tokensets ) and length( $define_tokensets ) > 0 ) + { + foreach my $pair ( split(/\s*;\s*/, $define_tokensets ) ) + { + my( $tset, $description ) = split( /\s*:\s*/, $pair ); + if( $tset !~ /^[a-z][a-z0-9-_]*$/ ) + { + Error('Invalid name for tokenset: ' . $tset); + $ok = 0; + } + elsif( length( $description ) == 0 ) + { + Error('Missing description for tokenset: ' . $tset); + $ok = 0; + } + else + { + $self->{'define-tokensets'}{$tset} = $description; + } + } + } + return $ok; +} + + +sub buildConfig +{ + my $self = shift; + my $cb = shift; + + my $reg = \%Torrus::DevDiscover::registry; + + foreach my $subtree ( sort keys %{$self->{'devdetails'}} ) + { + # Chop the first and last slashes + my $path = $subtree; + $path =~ s/^\///; + $path =~ s/\/$//; + + # generate subtree path XML + my $subtreeNode = undef; + foreach my $subtreeName ( split( '/', $path ) ) + { + $subtreeNode = $cb->addSubtree( $subtreeNode, $subtreeName ); + } + + foreach my $devdetails + ( sort {$a->param('snmp-host') cmp $b->param('snmp-host')} + @{$self->{'devdetails'}{$subtree}} ) + { + + my $data = $devdetails->data(); + + my @registryOverlays = (); + if( defined( $devdetails->param('template-registry-overlays' ) ) ) + { + my @overlayNames = + split(/\s*,\s*/, + $devdetails->param('template-registry-overlays' )); + foreach my $overlayName ( @overlayNames ) + { + if( defined( $templateOverlays{$overlayName}) ) + { + push( @registryOverlays, + $templateOverlays{$overlayName} ); + } + else + { + Error('Cannot find the template overlay named ' . + $overlayName); + } + } + } + + # we should call this anyway, in order to flush the overlays + # set by previous host + $cb->setRegistryOverlays( @registryOverlays ); + + if( $devdetails->param('disable-snmpcollector' ) eq 'yes' ) + { + push( @{$data->{'templates'}}, '::viewonly-defaults' ); + } + else + { + push( @{$data->{'templates'}}, '::snmp-defaults' ); + } + + if( $devdetails->param('rrd-hwpredict' ) eq 'yes' ) + { + push( @{$data->{'templates'}}, '::holt-winters-defaults' ); + } + + + my $devNodeName = $devdetails->param('symbolic-name'); + if( length( $devNodeName ) == 0 ) + { + $devNodeName = $devdetails->param('system-id'); + if( length( $devNodeName ) == 0 ) + { + $devNodeName = $devdetails->param('snmp-host'); + } + } + + my $devNode = $cb->addSubtree( $subtreeNode, $devNodeName, + $data->{'param'}, + $data->{'templates'} ); + + my $aliases = $devdetails->param('host-aliases'); + if( length( $aliases ) > 0 ) + { + foreach my $alias ( split( '\s*,\s*', $aliases ) ) + { + $cb->addAlias( $devNode, $alias ); + } + } + + my $includeFiles = $devdetails->param('include-files'); + if( length( $includeFiles ) > 0 ) + { + foreach my $file ( split( '\s*,\s*', $includeFiles ) ) + { + $cb->addFileInclusion( $file ); + } + } + + + # Let the device type-specific modules add children + # to the subtree + foreach my $devtype + ( sort {$reg->{$a}{'sequence'} <=> $reg->{$b}{'sequence'}} + $devdetails->getDevTypes() ) + { + &{$reg->{$devtype}{'buildConfig'}} + ( $devdetails, $cb, $devNode, $self->{'globalData'} ); + } + + $cb->{'statistics'}{'hosts'}++; + } + } + + foreach my $devtype + ( sort {$reg->{$a}{'sequence'} <=> $reg->{$b}{'sequence'}} + keys %{$reg} ) + { + if( defined( $reg->{$devtype}{'buildGlobalConfig'} ) ) + { + &{$reg->{$devtype}{'buildGlobalConfig'}}($cb, + $self->{'globalData'}); + } + } + + if( defined( $self->{'define-tokensets'} ) ) + { + my $tsetsNode = $cb->startTokensets(); + foreach my $tset ( sort keys %{$self->{'define-tokensets'}} ) + { + $cb->addTokenset( $tsetsNode, $tset, { + 'comment' => $self->{'define-tokensets'}{$tset} } ); + } + } +} + + + +sub session +{ + my $self = shift; + return $self->{'session'}; +} + +sub oiddef +{ + my $self = shift; + my $var = shift; + + my $ret = $self->{'oiddef'}->{$var}; + if( not $ret ) + { + Error('Undefined OID definition: ' . $var); + } + return $ret; +} + + +sub oidref +{ + my $self = shift; + my $oid = shift; + return $self->{'oidref'}->{$oid}; +} + + +sub genDataDir +{ + my $self = shift; + my $basedir = shift; + my $hostname = shift; + + if( $Torrus::DevDiscover::hashDataDirEnabled ) + { + return $basedir . '/' . + sprintf( $Torrus::DevDiscover::hashDataDirFormat, + unpack('N', md5($hostname)) % + $Torrus::DevDiscover::hashDataDirBucketSize ); + } + else + { + return $basedir; + } +} + + +sub listDataDirs +{ + my $self = shift; + + my @basedirs = keys %{$self->{'datadirs'}}; + my @ret = @basedirs; + + if( $Torrus::DevDiscover::hashDataDirEnabled ) + { + foreach my $basedir ( @basedirs ) + { + for( my $i = 0; + $i < $Torrus::DevDiscover::hashDataDirBucketSize; + $i++ ) + { + push( @ret, $basedir . '/' . + sprintf( $Torrus::DevDiscover::hashDataDirFormat, $i ) ); + } + } + } + return @ret; +} + +## +# Check if SNMP table is present, without retrieving the whole table + +sub checkSnmpTable +{ + my $self = shift; + my $oidname = shift; + + my $session = $self->session(); + my $oid = $self->oiddef( $oidname ); + + my $result = $session->get_next_request( -varbindlist => [ $oid ] ); + if( defined( $result ) ) + { + # check if the returned oid shares the base of the query + my $firstOid = (keys %{$result})[0]; + if( Net::SNMP::oid_base_match( $oid, $firstOid ) and + length( $result->{$firstOid} ) > 0 ) + { + return 1; + } + } + return 0; +} + + +## +# Check if given OID is present + +sub checkSnmpOID +{ + my $self = shift; + my $oidname = shift; + + my $session = $self->session(); + my $oid = $self->oiddef( $oidname ); + + my $result = $session->get_request( -varbindlist => [ $oid ] ); + if( $session->error_status() == 0 and + defined($result) and + defined($result->{$oid}) and + length($result->{$oid}) > 0 ) + { + return 1; + } + return 0; +} + + +## +# retrieve the given OIDs by names and return hash with values + +sub retrieveSnmpOIDs +{ + my $self = shift; + my @oidnames = @_; + + my $session = $self->session(); + my $oids = []; + foreach my $oidname ( @oidnames ) + { + push( @{$oids}, $self->oiddef( $oidname ) ); + } + + my $result = $session->get_request( -varbindlist => $oids ); + if( $session->error_status() == 0 and defined( $result ) ) + { + my $ret = {}; + foreach my $oidname ( @oidnames ) + { + $ret->{$oidname} = $result->{$self->oiddef( $oidname )}; + } + return $ret; + } + return undef; +} + +## +# Simple wrapper for Net::SNMP::oid_base_match + +sub oidBaseMatch +{ + my $self = shift; + my $base_oid = shift; + my $oid = shift; + + if( $base_oid =~ /^\D/ ) + { + $base_oid = $self->oiddef( $base_oid ); + } + return Net::SNMP::oid_base_match( $base_oid, $oid ); +} + +## +# some discovery modules need to adjust max-msg-size + +sub setMaxMsgSize +{ + my $self = shift; + my $devdetails = shift; + my $msgsize = shift; + my $opt = shift; + + $opt = {} unless defined($opt); + + if( (not $opt->{'only_v1_and_v2'}) or $self->session()->version() != 3 ) + { + $self->session()->max_msg_size($msgsize); + $devdetails->data()->{'param'}{'snmp-max-msg-size'} = $msgsize; + } +} + + + + +########################################################################### +#### Torrus::DevDiscover::DevDetails: the information container for a device +#### + +package Torrus::DevDiscover::DevDetails; + +use strict; +use Torrus::RPN; +use Torrus::Log; + +sub new +{ + my $self = {}; + my $class = shift; + bless $self, $class; + + $self->{'params'} = {}; + $self->{'snmpvars'} = {}; # SNMP results stored here + $self->{'devtype'} = {}; # Device types + $self->{'caps'} = {}; # Device capabilities + $self->{'data'} = {}; # Discovery data + + return $self; +} + + +sub setParams +{ + my $self = shift; + my $params = shift; + + while( my ($param, $value) = each %{$params} ) + { + $self->{'params'}->{$param} = $value; + } +} + + +sub setParam +{ + my $self = shift; + my $param = shift; + my $value = shift; + + $self->{'params'}->{$param} = $value; +} + + +sub param +{ + my $self = shift; + my $name = shift; + return $self->{'params'}->{$name}; +} + + +## +# store the query results for later use + +sub storeSnmpVars +{ + my $self = shift; + my $vars = shift; + + while( my( $oid, $value ) = each %{$vars} ) + { + if( $oid !~ /^\d[0-9.]+\d$/o ) + { + Error("Invalid OID syntax: '$oid'"); + } + else + { + $self->{'snmpvars'}{$oid} = $value; + + while( length( $oid ) > 0 ) + { + $oid =~ s/\d+$//o; + $oid =~ s/\.$//o; + if( not exists( $self->{'snmpvars'}{$oid} ) ) + { + $self->{'snmpvars'}{$oid} = undef; + } + } + } + } + + # Clean the cache of sorted OIDs + $self->{'sortedoids'} = undef; +} + +## +# check if the stored query results have such OID prefix + +sub hasOID +{ + my $self = shift; + my $oid = shift; + + my $found = 0; + if( exists( $self->{'snmpvars'}{$oid} ) ) + { + $found = 1; + } + return $found; +} + +## +# get the value of stored SNMP variable + +sub snmpVar +{ + my $self = shift; + my $oid = shift; + return $self->{'snmpvars'}{$oid}; +} + +## +# get the list of table indices for the specified prefix + +sub getSnmpIndices +{ + my $self = shift; + my $prefix = shift; + + # Remember the sorted OIDs, as sorting is quite expensive for large + # arrays. + + if( not defined( $self->{'sortedoids'} ) ) + { + $self->{'sortedoids'} = []; + push( @{$self->{'sortedoids'}}, + Net::SNMP::oid_lex_sort( keys %{$self->{'snmpvars'}} ) ); + } + + my @ret; + my $prefixLen = length( $prefix ) + 1; + my $matched = 0; + + foreach my $oid ( @{$self->{'sortedoids'}} ) + { + if( defined($self->{'snmpvars'}{$oid} ) ) + { + if( Net::SNMP::oid_base_match( $prefix, $oid ) ) + { + # Extract the index from OID + my $index = substr( $oid, $prefixLen ); + push( @ret, $index ); + $matched = 1; + } + elsif( $matched ) + { + last; + } + } + } + return @ret; +} + + +## +# device type is the registered discovery module name + +sub setDevType +{ + my $self = shift; + my $type = shift; + $self->{'devtype'}{$type} = 1; +} + +sub isDevType +{ + my $self = shift; + my $type = shift; + return $self->{'devtype'}{$type}; +} + +sub getDevTypes +{ + my $self = shift; + return keys %{$self->{'devtype'}}; +} + +## +# device capabilities. Each discovery module may define its own set of +# capabilities and use them for information exchange between checkdevtype(), +# discover(), and buildConfig() of its own and dependant modules + +sub setCap +{ + my $self = shift; + my $cap = shift; + Debug('Device capability: ' . $cap); + $self->{'caps'}{$cap} = 1; +} + +sub hasCap +{ + my $self = shift; + my $cap = shift; + return $self->{'caps'}{$cap}; +} + +sub clearCap +{ + my $self = shift; + my $cap = shift; + Debug('Clearing device capability: ' . $cap); + if( exists( $self->{'caps'}{$cap} ) ) + { + delete $self->{'caps'}{$cap}; + } +} + + + +sub data +{ + my $self = shift; + return $self->{'data'}; +} + + +sub screenSpecialChars +{ + my $self = shift; + my $txt = shift; + + $txt =~ s/:/{COLON}/gm; + $txt =~ s/;/{SEMICOL}/gm; + $txt =~ s/%/{PERCENT}/gm; + + return $txt; +} + + +sub applySelectors +{ + my $self = shift; + + my $selList = $self->param('selectors'); + return if not defined( $selList ); + + my $reg = \%Torrus::DevDiscover::selectorsRegistry; + + foreach my $sel ( split('\s*,\s*', $selList) ) + { + my $type = $self->param( $sel . '-selector-type' ); + if( not defined( $type ) ) + { + Error('Parameter ' . $sel . '-selector-type must be defined ' . + 'for ' . $self->param('snmp-host')); + } + elsif( not exists( $reg->{$type} ) ) + { + Error('Unknown selector type: ' . $type . + ' for ' . $self->param('snmp-host')); + } + else + { + Debug('Initializing selector: ' . $sel); + + my $treg = $reg->{$type}; + my @objects = &{$treg->{'getObjects'}}( $self, $type ); + + foreach my $object ( @objects ) + { + Debug('Checking object: ' . + &{$treg->{'getObjectName'}}( $self, $object, $type )); + + my $expr = $self->param( $sel . '-selector-expr' ); + $expr = '1' if length( $expr ) == 0; + + my $callback = sub + { + my $attr = shift; + my $checkval = $self->param( $sel . '-' . $attr ); + + Debug('Checking attribute: ' . $attr . + ' and value: ' . $checkval); + my $ret = &{$treg->{'checkAttribute'}}( $self, + $object, $type, + $attr, $checkval ); + Debug(sprintf('Returned value: %d', $ret)); + return $ret; + }; + + my $rpn = new Torrus::RPN; + my $result = $rpn->run( $expr, $callback ); + Debug('Selector result: ' . $result); + if( $result ) + { + my $actions = $self->param( $sel . '-selector-actions' ); + foreach my $action ( split('\s*,\s*', $actions) ) + { + my $arg = + $self->param( $sel . '-' . $action . '-arg' ); + $arg = 1 if not defined( $arg ); + + Debug('Applying action: ' . $action . + ' with argument: ' . $arg); + &{$treg->{'applyAction'}}( $self, $object, $type, + $action, $arg ); + } + } + } + } + } +} + +1; + + +# Local Variables: +# mode: perl +# indent-tabs-mode: nil +# perl-indent-level: 4 +# End: |