Section 11.12. SNMP: The Object-Oriented Way


11.12. SNMP: The Object-Oriented Way

The SNMP::Info Perl package was developed at the University of California, Santa Cruz. The official web site for this package is http://snmp-info.sourceforge.net.

SNMP::Info is based on the Net-SNMP Perl module. It allows you to obtain various information from a device without having to know any OIDs, MIBs, etc., and it does so with object orientation (OO). How does it do this? It supports a well-developed list of MIBs, and it can discover the type of device you are trying to query. If it knows about the device, you can use predefined methods to get interface information and other things. Here's an example script that gathers information about interfaces on a switch:

     #!/usr/bin/perl     use SNMP::Info;     my $info = new SNMP::Info(                                 # Auto Discover more specific Device Class                                 AutoSpecify => 1,                                 Debug       => 0,                                 # The rest is passed to SNMP::Session                                 DestHost    => '192.168.0.148',                                 Community   => 'public',                                 Version     => 2                               ) or die "Can't connect to device.\n";      my $err = $info->error( );      die "SNMP Community or Version probably wrong connecting to device. $err\n"     if defined $err;      $name  = $info->name( );      $class = $info->class( );      print "SNMP::Info is using this device class : $class\n";      # Find out the Duplex status for the ports      my $interfaces = $info->interfaces( );      my $i_duplex   = $info->i_duplex( );      # Get CDP Neighbor info      my $c_if       = $info->c_if( );      my $c_ip       = $info->c_ip( );      my $c_port     = $info->c_port( );      # Print out data per port      foreach my $iid (keys %$interfaces){         my $duplex = $i_duplex->{$iid};         # Print out physical port name, not snmp iid         my $port  = $interfaces->{$iid};         # The CDP Table has table entries different from the interface tables.         # So we use c_if to get the map from the cdp table to the interface table.         my %c_map = reverse %$c_if;         my $c_key = $c_map{$iid};         my $neighbor_ip   = $c_ip->{$c_key};         my $neighbor_port = $c_port->{$c_key};         print "$port: $duplex duplex";         print " connected to $neighbor_ip / $neighbor_port\n" if defined $remote_ip;         print "\n";      } 

And here's the output:

     $ ./getinterfaceinfo.pl     SNMP::Info is using this device class : SNMP::Info::Layer2::C2900     FastEthernet0/5: half duplex     FastEthernet0/10: half duplex     FastEthernet0/2: full duplex     FastEthernet0/6: half duplex     FastEthernet0/8: half duplex     FastEthernet0/1: half duplex     FastEthernet0/11: half duplex     Null0:  duplex     VLAN2:  duplex     FastEthernet0/7: half duplex     FastEthernet0/3: half duplex     VLAN1:  duplex     FastEthernet0/9: half duplex     FastEthernet0/12: half duplex     FastEthernet0/4: half duplex     $ 

Let's talk a little bit about the script. First, we create an SNMP::Info object, with such typical parameters as the host we are querying, community string, and SNMP version. The constructor for SNMP::Info also has an AutoSpecify parameter, which instructs the session we create to try and discover the device class. This code will get the specific device class:

     $class = $info->class( ); 

The following code will get all the interfaces from the remote host:

     my $interfaces = $info->interfaces( ); 

It doesn't get any easier than that. Let's look at what each OO method does, according to the SNMP::Info documentation:


$info->interfaces( )

Returns a reference to the map between ifIndex in the interface subtree and the physical port. On the 2900 devices, i_name isn't reliable, so we override it to just the description. Next, all dots are changed to forward slashes so that the physical port name is the same as the broadcasted Cisco Discovery Protocol (CDP) port name (e.g., Ethernet0.1 becomes Ethernet0/1).


$info->i_duplex( )

Returns a reference to a map of ifIndex IDs to the current link duplex.


$info->c_if( )

The CDP is a Layer 2 protocol that supplies topology information to other devices that also speak CDP (mostly switches and routers). CDP is implemented in Cisco and some HP devices. c_if( ) returns the CDP interface mapping to the SNMP Interface Table.


$info->c_ip( )

Returns a remote CDP IP address.


$info->c_port( )

Returns a remote CDP port ID.

The rest of the program is just a foreach loop that runs through all the data structures. Now let's look at another script:

     #!/usr/bin/perl     use SNMP::Info;     my $bridge = new SNMP::Info (                                  AutoSpecify => 1,                                  Debug       => 0,                                  DestHost    => '192.168.0.148',                                  Community   => 'public',                                  Version     => 2                                  );      my $class = $bridge->class( );      print " Using device sub class : $class\n";      # Grab Forwarding Tables      my $interfaces = $bridge->interfaces( );      my $fw_mac     = $bridge->fw_mac( );      my $fw_port    = $bridge->fw_port( );      my $bp_index   = $bridge->bp_index( );      foreach my $fw_index (keys %$fw_mac){         my $mac   = $fw_mac->{$fw_index};         my $bp_id = $fw_port->{$fw_index};         my $iid   = $bp_index->{$bp_id};         my $port  = $interfaces->{$iid};         print "Port:$port forwarding to $mac\n";      } 

And its output:

     $ ./bridge.pl      Using device sub class : SNMP::Info::Layer2::C2900      Port:FastEthernet0/12 forwarding to 00:04:9a:da:5e:4c      Port:FastEthernet0/10 forwarding to 00:04:9a:da:5e:4a      Port:FastEthernet0/2 forwarding to 00:0f:1f:d3:c6:3a      Port:FastEthernet0/2 forwarding to 00:11:43:04:a5:25      Port:FastEthernet0/11 forwarding to 00:04:9a:da:5e:4b      Port:FastEthernet0/2 forwarding to 00:0f:20:41:b8:ed      Port:FastEthernet0/2 forwarding to 00:11:43:04:c2:e6      Port:FastEthernet0/2 forwarding to 00:0b:db:d2:d6:10      Port:FastEthernet0/2 forwarding to 00:0f:1f:df:ef:f9     $ 

This script prints port-forwarding information for a bridge. Let's look at some of the methods used in this script:


$bridge->fw_mac( )

Returns a reference to a hash of forwarding table MAC addresses as defined in the BRIDGE-MIB object dot1dTpFdbAddress.


$bridge->fw_port( )

Returns a reference to a hash of learned bridge ports. See dot1dTpFdbPort in the BRIDGE-MIB for more detail.


$bridge->bp_index( )

Returns a reference to a hash of bridge port table entries mapped back to ifIndex entries.

Of course, once we've run these methods, we just use a foreach loop to access the data structures and print the forwarding details.

11.12.1. Extending SNMP::Info

SNMP::Info is great for talking to network devices. But what if you want to, say, talk to Unix systems and obtain more information than what is provided by RFC 1213 and other similar MIBs? Well, luckily the author of SNMP::Info has made it quite easy to extend.

We decided to create a module called HostResources, which makes use of some of the Host Resources objects. Here's the entire Perl module:

     package SNMP::Info::HostResources;     $VERSION = 1.0;     use strict;     use Exporter;     use SNMP::Info;     @SNMP::Info::HostResources::ISA = qw/SNMP::Info Exporter/;     @SNMP::Info::HostResources::EXPORT_OK = qw//;     use vars qw/$VERSION %FUNCS %GLOBALS %MIBS %MUNGE $AUTOLOAD $INIT $DEBUG/;     %MIBS    = (%SNMP::Info::MIBS,                 'HOST-RESOURCES-MIB'  => 'host',        );     %GLOBALS = (%SNMP::Info::GLOBALS,                 'hr_users' => 'hrSystemNumUsers',                 'hr_processes' => 'hrSystemProcesses',                 'hr_date' => 'hrSystemDate',                 );     %FUNCS   = (%SNMP::Info::FUNCS,                 # HostResources MIB objects                 'hr_sindex'  => 'hrStorageIndex',                 'hr_sdescr'  => 'hrStorageDescr',                 'hr_sused'  => 'hrStorageUsed',                );     %MUNGE   = (%SNMP::Info::MUNGE,                 'hr_date' => \&munge_hrdate,                );     sub munge_hrdate {        my($oct) = @_;        #        # hrSystemDate has a syntax of DateAndTime, which is defined in SNMPv2-TC as        #        #DateAndTime ::= TEXTUAL-CONVENTION        # DISPLAY-HINT "2d-1d-1d,1d:1d:1d.1d,1a1d:1d"        # STATUS       current        # DESCRIPTION        #         "A date-time specification.        #        #            field  octets  contents                  range        #            -----  ------  --------                  -----        #              1      1-2   year*                     0..65536        #              2       3    month                     1..12        #              3       4    day                       1..31        #              4       5    hour                      0..23        #              5       6    minutes                   0..59        #              6       7    seconds                   0..60        #                           (use 60 for leap-second)        #              7       8    deci-seconds              0..9        #              8       9    direction from UTC        '+' / '-'        #              9      10    hours from UTC*           0..13        #             10      11    minutes from UTC          0..59        #        #            * Notes:        #            - the value of year is in network-byte order        #            - daylight savings time in New Zealand is +13        #        #            For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be        #            displayed as:        #        #                             1992-5-26,13:30:15.0,-4:0        #        #            Note that if only local time is known, then timezone        #            information (fields 8-10) is not present."        #    SYNTAX       OCTET STRING (SIZE (8 | 11))        #        my ($year1, $year2, $month, $day, $hour, $min, $secs, $decisecs,              $direction, $hoursFromUTC, $minFromUTC) = split(/ /, sprintf("%d     %d %d %d %d %d %d %d %d %d %d",unpack('C*',$oct)));        my $value = 0;        $direction = chr($direction);        $value = $value * 256 + $year1;        $value = $value * 256 + $year2;        my $year = $value;        return           "$year-$month-$day,$hour:$min:$secs:$decisecs,$direction$hoursFromUTC:     $minFromUTC";     }     1; # don't forget this line 

Let's take a look at this module. The first line (package SNMP::Info::HostResources;) names the module and the package we are going to be a part ofin this case, SNMP::Info. The %MIBS hash configures SNMP::Info with a list of various MIBs you will use in the module. Here we loaded HOST-RESOURCES-MIB and gave it the name of the top-level OID in that MIB, host. %SNMP::Info::MIBS loads global SNMP::Info MIBs.

The %GLOBALS hash lists scalar OIDs that we plan to usei.e., OIDs that are not part of a column in a table. %SNMP::Info::GLOBALS loads SNMP::Info globals. These globals include RFC 1213 objects sysUptime, sysDescr, etc. hrSystemNumUsers is:

     hrSystemNumUsers OBJECT-TYPE         SYNTAX     Gauge32         MAX-ACCESS read-only         STATUS     current         DESCRIPTION             "The number of user sessions for which this host is             storing state information.  A session is a collection             of processes requiring a single act of user             authentication and possibly subject to collective job             control."         ::= { hrSystem 5 } 

hrSystemProcesses is:

     hrSystemProcesses OBJECT-TYPE         SYNTAX     Gauge32         MAX-ACCESS read-only         STATUS     current         DESCRIPTION             "The number of process contexts currently loaded or             running on this system."         ::= { hrSystem 6 } 

hrSystemDate is:

     hrSystemDate OBJECT-TYPE         SYNTAX     DateAndTime         MAX-ACCESS read-write         STATUS     current         DESCRIPTION             "The host's notion of the local date and time of day."         ::= { hrSystem 2 } 

The %FUNCS hash lists columnar OIDs to query. In this example, we query two items from the hrStorageTable. hrStorageTable is defined as:

     hrStorageTable OBJECT-TYPE         SYNTAX     SEQUENCE OF HrStorageEntry         MAX-ACCESS not-accessible         STATUS     current         DESCRIPTION             "The (conceptual) table of logical storage areas on             the host.             An entry shall be placed in the storage table for each             logical area of storage that is allocated and has             fixed resource limits.  The amount of storage             represented in an entity is the amount actually usable             by the requesting entity, and excludes loss due to             formatting or file system reference information.             These entries are associated with logical storage             areas, as might be seen by an application, rather than             physical storage entities which are typically seen by             an operating system.  Storage such as tapes and             floppies without file systems on them are typically             not allocated in chunks by the operating system to             requesting applications, and therefore shouldn't             appear in this table.  Examples of valid storage for             this table include disk partitions, file systems, ram             (for some architectures this is further segmented into             regular memory, extended memory, and so on), backing             store for virtual memory ('swap space').             This table is intended to be a useful diagnostic for             'out of memory' and 'out of buffers' types of             failures.  In addition, it can be a useful performance             monitoring tool for tracking memory, disk, or buffer             usage."         ::= { hrStorage 3 } 

hrStorageIndex is:

     hrStorageIndex OBJECT-TYPE         SYNTAX     Integer32 (1..2147483647)         MAX-ACCESS read-only         STATUS     current         DESCRIPTION             "A unique value for each logical storage area             contained by the host."         ::= { hrStorageEntry 1 } 

hrStorageDescr is:

     hrStorageDescr OBJECT-TYPE         SYNTAX     DisplayString         MAX-ACCESS read-only         STATUS     current         DESCRIPTION             "A description of the type and instance of the storage             described by this entry."         ::= { hrStorageEntry 3 } 

hrStorageUsed is:

     hrStorageUsed OBJECT-TYPE         SYNTAX     Integer32 (0..2147483647)         MAX-ACCESS read-only         STATUS     current         DESCRIPTION             "The amount of the storage represented by this entry             that is allocated, in units of             hrStorageAllocationUnits."         ::= { hrStorageEntry 6 } 

We need hrStorageIndex as a way to access hrStorageDescr and hrStorageUsed, which will be indexed based on hrStorageIndex.

%MUNGE lists methods, in your module, that will be called based on values in the %GLOBALS or %FUNCS hashes. For example, in the initialization for %MUNGE, we have:

     'hr_date' => \&munge_hrdate, 

'hr_date' is also defined in %GLOBALS. Once SNMP::Info performs the actual SNMP operation on the OID for this identifier, it will call munge_hrdate and pass it the value that was retrieved.

The basic reason for having this %MUNGE data structure is to allow for routines to be defined that can perform extended processing. Take the hrSystemDate OID. Its SYNTAX is DateAndTime, which is a textual convention defined in the SNMPv2-TC MIB. The actual definition for DateAndTime is provided in the subroutine in the module, but basically we are handed a raw OctetString and we must decode the string and format the date and time as specified by this textual convention for DateAndTime. Note that the subroutine returns the converted value. This is an important step, so don't forget it.

Once your module is done, you need to modify some SNMP::Info files. First, copy your new module over to where SNMP::Info is installed:

     $ cp HostResources.pm /usr/local/share/perl/5.8.4/SNMP/Info/ 

Now edit /usr/local/share/perl/5.8.4/SNMP/Info.pm and find the following line in the device_type( ) method:

     return undef unless (defined $layers and length($layers)); 

now comment it out:

     #return undef unless (defined $layers and length($layers)); 

The reason for this is that some host-based agents may not have sysServices set, which is what this is checking for. In the same method, find the lines that look like this:

         # These devices don't claim to have Layer1-3 but we like em anyway.         } else {             $objtype = 'SNMP::Info::Layer2::ZyXEL_DSLAM' if ($desc =~ /8-port     .DSL Module\(Annex .\)/i);         } 

And make it look like this:

         # These devices don't claim to have Layer1-3 but we like em anyway.         } elsif($desc =~ /linux|unix|windows/i){            $objtype = 'SNMP::Info::HostResources';         } else {             $objtype = 'SNMP::Info::Layer2::ZyXEL_DSLAM' if ($desc =~ /8-port     .DSL Module\(Annex .\)/i);         } 

This allows SNMP::Info to create and return a proper HostResources object. Here is an example script which uses this new module:

     #!/usr/bin/perl     use SNMP::Info::HostResources;     my $host = new SNMP::Info (                                  AutoSpecify => 1,                                  Debug       => 0,                                  DestHost    => '127.0.0.1',                                  Community   => 'public',                                  Version     => 2                                  );     my $class = $host->class( );     print "Using device sub class : $class\n\n";     my $users = $host->hr_users( );     my $processes = $host->hr_processes( );     my $date = $host->hr_date( );     print "(System date: $date) There are $users users running $processes processes     \n\n";     my $storage_index = $host->hr_sindex( );     my $storage_descr = $host->hr_sdescr( );     my $used = $host->hr_sused( );     foreach my $index (keys %$storage_index){        print "$storage_descr->{$index} is using $used->{$index}\n";     } 

Note that $host->hr_users( ) and $host->hr_processes( ) are called out of %GLOBALS, and $host->hr_sindex( ) and $host->hr_sdescr( ) are called out of %FUNCS. And here is a sample run:

     $ ./host.pl     Using device sub class : SNMP::Info::HostResources     (System date: 2005-5-17,13:12:15:0,-4:0)  There are 5 users running 85 processes     /home is using 839925     / is using 702477     Memory Buffers is using 156044     Swap Space is using 0     /proc/bus/usb is using 0     Real Memory is using 909092     /sys is using 0     $ 

SNMP::Info is a well-thought-out API. It is perfect for people who may not wish to think about the gory details of OIDs, MIBS, etc. The downside is that if you are going to extend SNMP::Info, you need to know about these details. However, you may be in a situation where you want to allow others to utilize SNMP to write scripts but aren't interested in spending a week teaching people SNMP. You can instead hand someone this module with minimal instruction and they can become productive quite quickly.




Essential SNMP
Essential SNMP, Second Edition
ISBN: 0596008406
EAN: 2147483647
Year: 2003
Pages: 165

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net