29th August 2006
YAPC::Europe::2006
Speeding
up SNMP queries
by
Jan Henning Thorsen
Table of Contents
1.Summary 3
2.Introduction 3
2.1.Preface 3
2.2.Requirements 3
2.3.Pro / cons about the module 4
3.A bit about the module “SNMP” 4
3.1.Description from the SNMP POD 4
3.2.SNMP + SNMP::Effective 4
4.The interface of SNMP::Effective 5
4.1.SNMP::Effective->new( ... ); 5
4.2.$snmp_effective->add( ... ); 5
4.3.$snmp_effective->execute; 5
5.The inner workings of SNMP::Effective 5
5.1.Introduction 5
5.2.SNMP/Effective.pm methods 6
5.3.SNMP/Effective/Dispatch.pm 6
5.4.SNMP/Effective/Var.pm 7
6.SNMP::Effective's callback method 7
7.Debugging 8
8.References 9
1.Summary
“Speeding up SNMP queries” tells you about a new module called SNMP::Effective. The module is written with one main goal: It has to be fast.
This paper does not dig into how the SNMP protocol works. It contains information about the module's interface and also some of the inner workings. It covers what the SNMP module offers, and how the two modules interact.
2.Introduction
2.1.Preface
SNMP is a protocol for querying network components. It features great flexibility and is quite simple to understand how to use it. I needed a Perl module that could query many hosts in a short period of time.
I searched CPAN for a module that could do this and found SNMP::Multi. It looked promising, but it didn't exactly fit my needs, so I decided to make my own module instead.
The module is written from scratch by me, but some parts are inspired by SNMP::Multi.
2.2.Requirements
The module had to meet these requirements:
It had to be fast. The components that should limit the speed were hardware and network topology.
It had to be able to get as much data as required, from one or many hosts.
Flexible interface. It should be able to add many OIDs to many hosts, or specify unique host / OID pairs.
It should support SNMP -get, -set and some sort of -walk / -getnext method, which can traverse a MIB-subtree.
It should have a callback method, defined by the user, which handles the returned data.
Few dependencies
...And of course the normal requirements like stability, understandable code and documentation.
2.3.Pro / cons about the module
It's really fast. It looks like it's the hardware that is slowing the module down.
The interface is pretty flexible, but could maybe be even more flexible.
Stability is good. It has been tested for quite a long time, without any known problems.
Except for common Perl modules, it only depends on the SNMP module.
The module supports get, set and walk. The
walk()method is emulated. More about this in the “5.3 SNMP/Effective/Dispatch.pm” section.Documentation is lacking. There is no POD included with the module.
3.A bit about the module “SNMP”
3.1.Description from the SNMP POD
The basic operations of the SNMP protocol are provided
by this module through an object oriented interface for modularity
and ease of use. The primary class is SNMP::Session which
encapsulates the persistent aspects of a connection between the
management application and the managed agent. Internally the class is
implemented as a blessed hash reference. This class supplies get,
getnext, set,
fget, and fgetnext
method calls. The methods take a variety of input argument formats
and support both synchronous and asynchronous operation through a
polymorphic API.
3.2.SNMP + SNMP::Effective
SNMP::Effective creates a SNMP-object, using the
SNMP::Session->new() constructor. It
passes on arguments defined by the user or default arguments defined
in either SNMP::Session or SNMP::Effective.
Later it use the SNMP::Session object to call
get(), set()
or getnext() asynchronously. Calling
them asynchronously allows SNMP::Effective to do multiple things at
the same time, which then again saves time and increases the
efficiency. After SNMP is done with the request, it calls a callback
method inside SNMP::Effective, which then stores the retrieved data.
SNMP::MainLoop is a
subroutine that starts the asynchronous calls. If you specify
MasterTimeout in SNMP::Effective, it
will call SNMP::MainLoop with a timeout
and a callback. The callback method calls SNMP::finish,
which stops SNMP::MainLoop.
SNMP::Effective will call SNMP::finish
when it has no more hosts to query. It must do so, since SNMP::finish
is the only thing that can stop SNMP::MainLoop
from looping forever.
4.The interface of SNMP::Effective
4.1.SNMP::Effective->new( ... );
This method returns a SNMP::Effective object
(refered to as $snmp_effective later in
this document) . Arguments to the constructor can be:
MaxSessions => int # maximum number of simultainious SNMP session MasterTimeout => int # maximum number of seconds before killing execute
All other arguments are passed on to
SNMP::Effective::Var::new,
which from a users views is the same as calling $snmp_effective->add(
[args] ).
4.2.$snmp_effective->add( ... );
Arguments to this method can be:
DestHost => [] # an array-ref that contains a list of hosts, in this format:
# xxx.xxx.xxx.xxx or a hostname
Arg => {} # arguments passed on to SNMP::Session
Callback => sub {} # the callback method whick handles the received data
get => [] # an array-ref that contains a list of OIDs to get
walk => [] # an array-ref that contains a list of OIDs “trees” to get
set => [] # an array-ref that contains a list of OIDs to set
This can be called with many different combinations, such as:
DestHost / any other argument: This will make changes pr. DestHost. You can then change Arg, Callback, or add OIDs on host-basis.
get / walk / set: The OID list submitted to add() will be added to all DestHosts, if it's no DestHost is spesified.
Arg / Callback: This can be used to alter all hosts SNMP-arguments or callback method.
4.3.$snmp_effective->execute;
This method starts setting / getting data. It
will run as long as necessarily, or until MasterTimeout
is reached. Every time the some data is set / retrieved, it will
call the callback-method, defined pr. host.
5.The inner workings of SNMP::Effective
5.1.Introduction
The module consists of three separate files / perl modules. It's kind of “unorthodox” set up, since SNMP::Effective inherit from SNMP::Effective::Dispatch and SNMP::Effective::Var, instead of the other way around – like it's normally done. (Example: CD::Burner::Wui inherit from CD::Burner...) Comments are welcome, but It's done this way, because the programmer likes to separate blocks of code into different files.
5.2.SNMP/Effective.pm methods
new( %args ) is the object constructor. It doesn't do much else than what's covered at “4.1 SNMP::Effective->new( ... );“. The one thing to add is that it returns a blessed hash.
execute() starts retrieving / setting data. It initializes
SNMP::Session, by calling$snmp_effective->dispatch(). It then starts get/setting by callingSNMP::MainLoop(), either with a timeout and a callback method as arguments, or just letting it loop untildispatch()is done with it's work._timeout() is the the callback method, which
SNMP::MainLoop()calls ifMasterTimeoutis set. It will stop all asynchronous calls and then also stop$snmp_effective->dispatch()._create_session() creates new SNMP sessions. The sessions are created with either default SNMP args, default SNMP::Effective args or host-specific args, provided by the
$snmp_effective->add()method._check_errno() checks any problems, unless _create_session fails to create a new SNMP session. It returns a list containing wither SNMP::Effective can retry creating the session and the error message.
warning() is the internal warn() method that takes a message and a warning level as input. If the warning level is higher
error() returns the last error message.
matchOid() takes two numeric OIDs as arguments. The second OID is matched against the first OID:
return $first =~ /^\.?$second\.?(.*)/ ? $1 : undef;This subroutine is not an object method.makeNumericOid() and makeNameOid() translates an OID into either numeric (dotted) or named (leaf) OID
5.3.SNMP/Effective/Dispatch.pm
dispatch()is the method that does the work. This method creates the SNMP sessions and calls the SNMP methods asynchronous. It shifts off hosts from an array, so you cannot get back the list of hosts afterdispatch()is started. This is also how it knows if it's done collecting data or not:SNMP::finishis called when there's no hosts left in the array and no active SNMP sessions._set(),_get()and_walk()are callback methods for the SNMP methods. The methods store the data returned from the host and then calls$snmp_effective->_end()._end()calls the callback methods, defined by the user and then callsdispatch()which then creates more SNMP sessions. It passes on$hostand$errorto the callback method. Look at “6 SNMP::Effective's callback method“ for more information about these “scalars”. After calling the callback method it calls the$snmp_effective->dispatch()method again, which then gets / sets more data.Notes about the
walk()method: There have been experienced some problems, when using thewalk()method provided from the SNMP module. Because of this SNMP::Effective provides has it's ownwalk()method which use the SNMP module'sgetnext()method. There is no difference from the users point of view, but it seems to be more stable, when walking through a three with many OIDs.
5.4.SNMP/Effective/Var.pm
This part of the module is kind of hard to explain, since it contains tied and overloaded variables, but here is a short summary of the classes:
SNMP::Effective::VarList is a tied array which holds the OID list.
SNMP::Effective::Host use overload for various uses but also has some methods:
“$host”returns the hostname.${$host}returns the SNMP::Session object.@{$host}returns the OID list of the host.$host->datareturns the data retrieved from SNMP.$host->Argreturns the arguments specified by the user.SNMP::Effective::HostList is a tied array, which holds a list of all the hosts. It is also overload:
@{$hostlist}returns a list of all the hosts added by the user.SNMP::Effective::Var is a class that connects ::VarList, ::Host and ::HostList together. This is the class where
$snmp_effective->add()is located.
6.SNMP::Effective's callback method
When SNMP is done collecting data from a host, it
calls a callback method, provided by the Callback
=> sub{}
argument. Here is an example on a callback method:
my my_callback {
my $host = shift;
my $error = shift;
if($error) {
warn “$host failed with this error: $error”;
return;
}
my $data = $host->data;
PRINT_DATA:
for my $oid (%$data) {
print “$host returned oid “$oid” with this data:\n”;
print join “\n\t”, map {
"$_ => $data->{$oid}{$_}”;
} keys %{ $data->{$oid} };
print "\n";
}
}
Example output:
10.2.10.3 returned oid “1.3.6.1.2.1.1.1” with this data: 1 => C12-MM4-CS, Hardware V4 <<VENDOR: BigBand; BOOTR: >> (ser#0600012893, part#90000046000, rev#A2, opt#c02), v6.0.1(14) , Release6.0_Integration Built 2006_03_29_153010
7.Debugging
By setting $SNMP::Effective::DEBUG
you will get a debugging information printed to STDERR. There are no
strict debugging levels (yet), but setting it to “100”
will output a lot of information.
8.References
SNMP module: http://search.cpan.org/~gsm/SNMP-4.2.0/SNMP.pm