From 3f179aa21acffe4fa2cd321a2c04989bad5b97fb Mon Sep 17 00:00:00 2001 From: Max Baker <> Date: Tue, 29 Apr 2003 02:22:45 +0000 Subject: [PATCH] * Added NOSUCH support to fix bug with Version 1 data collection * Major Documentation (pod) rewrite * Changed debuging to object scope * Added error throwing and checking * clear_cache() --- Info.pm | 1020 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 604 insertions(+), 416 deletions(-) diff --git a/Info.pm b/Info.pm index 942d6176..4590de93 100644 --- a/Info.pm +++ b/Info.pm @@ -4,7 +4,7 @@ # Copyright (c) 2002-3, Regents of the University of California # All rights reserved. # -# See COPYRIGHT below +# See COPYRIGHT at bottom package SNMP::Info; $VERSION = 0.4; @@ -18,11 +18,12 @@ use Math::BigInt; @SNMP::Info::ISA = qw/Exporter/; @SNMP::Info::EXPORT_OK = qw//; -use vars qw/$VERSION %FUNCS %GLOBALS %MIBS %MUNGE $AUTOLOAD $INIT $DEBUG %SPEED_MAP $BIGINT/; +use vars qw/$VERSION %FUNCS %GLOBALS %MIBS %MUNGE $AUTOLOAD $INIT $DEBUG %SPEED_MAP + $NOSUCH $BIGINT/; =head1 NAME -SNMP::Info - Perl5 Interface to Network devices through SNMP. +SNMP::Info - Object Oriented Perl5 Interface to Network devices and MIBs through SNMP. =head1 VERSION @@ -32,40 +33,12 @@ SNMP::Info - Version 0.4 Max Baker (C) -SNMP::Info was created for the Netdisco application at UCSC - -=head1 COPYRIGHT AND LICENCE - -Copyright (c) 2002-3, Regents of the University of California -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of the University of California, Santa Cruz nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +SNMP::Info was created for Netdisco (www.netdisco.org) at UCSC =head1 SYNOPSIS - use SNMP::Info - + use SNMP::Info; + my $info = new SNMP::Info( # Auto Discover more specific Device Class AutoSpecify => 1, @@ -73,275 +46,321 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # The rest is passed to SNMP::Session DestHost => 'router', Community => 'public', - Version => 2 ) - or die; - - $name = $info->name(); - - # Let's see what sub-class it picked for us - print "Device is of type : ", $info->class(), "\n"; - + Version => 2 + ) or die "Can't connect to device.\n" + + $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}; - - my $neighbor_ip = $c_ip->{$iid}; - my $neighbor_port = $c_port->{$iid}; - print "$port: Duplex $duplex\n"; - print " Neighbor : $neighbor_ip \@ $neighbor_port\n"; + # The CDP Table has table entries different than the interface tables. + # So we use c_if to get the map from cdp table to 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"; } -=head1 REQUIREMENTS +=head1 DESCRIPTION + +SNMP::Info gives an object oriented interface to information obtained through SNMP. + +This module is geared towards network devices. Subclasses exist for a number of +network devices and common MIBs. + +The idea behind this module is to give a common interface to data from network devices, +leaving the device-specific hacks behind the scenes in subclasses. + +In the SYNOPSIS example we fetch the name of all the ports on the device and the duplex +setting for that port with two methods -- interfaces() and i_duplex(). + +The information may be coming from any number of MIB files and is very vendor specific. +SNMP::Info provides you a common method for all supported devices. + +Adding support for your own device is easy, and takes little much SNMP knowledge. + +The module is not limited to network devices. Any MIB or device can be given an objected oriented +front-end by making a module that consists of a couple hashes. See EXTENDING SNMP::INFO. + +=head2 Requirements =over =item 1. Net-SNMP -To use this module, you must have Net-SNMP installed on your system. +To use this module, you must have Net-SNMP installed on your system. +More specifically you need the Perl modules that come with it. -Net-SNMP can be found at http://net-snmp.sourceforge.net . Version 5.0.2 or -greater is recommended. +DO NOT INSTALL SNMP:: or Net::SNMP from CPAN! +The SNMP module is matched to an install of net-snmp, and must be installed +from the net-snmp source tree. -The Perl module C is found inside the distribution. Go to the F directory -and install it from there, or run C<./configure --with-perl-modules> . +The Perl module C is found inside the net-snmp distribution. Go to the F directory +of the distribution to install it, or run C<./configure --with-perl-modules> from the top directory +of the net-snmp distribution. + +Net-SNMP can be found at http://net-snmp.sourceforge.net + +Version 5.0.2 or greater is recommended. Various version 4's will work, and 5.0.1 is kinda flaky +on the Perl side. + +Note: Net-SNMP was previously called ucd-snmp. =item 2. MIBS -Each sub-module that you use will also require specific MIBs, -usually obtainable on the net. See the list above for a quick -glance, and the documentation in each sub module for more information. +SNMP::Info operates on textual descriptors found in MIBs. MIBs are text databases that +are freely and easily obtainable on the Net. Make sure that your snmp.conf is updated to point to your MIB directory and that the MIBs are world-readable. -SNMP::Info requires RFC1213-MIB (and whatever supporting MIBs that -are referenced). - -A good starting point are the Version 2 MIBs from Cisco, found at - - ftp://ftp.cisco.com/pub/mibs/v2/v2.tar.gz - -Run C to install them. - Then run C and setup that directory as default. Move F into F when you are done. +=over + +=item Basic MIBs + +A minimum amount of MIBs to have are the Version 2 MIBs from Cisco, found at + +ftp://ftp.cisco.com/pub/mibs/v2/v2.tar.gz + +To install them : + + mkdir -p /usr/local/share/snmp/mibs && cd /usr/local/share/snmp/mibs && tar xvfz /path/to/v2.tar.gz + +=item Version 1 MIBs + +You will also need to install some of the version one MIBs from Cisco : + +ftp://ftp.cisco.com/pub/mibs/v1/v1.tar.gz + +Extract + +=over + +=item BRIDGE-MIB + +=item SNMP-REPEATER-MIB + +=item STAND-ALONE-ETHERNET-SWITCH-MIB (ESSWITCH-MIB) + =back -=head1 DESCRIPTION +by running -SNMP::Info gives an object oriented interface to information obtained through -SNMP. + mkdir -p /usr/local/share/snmp/mibs + cd /usr/local/share/snmp/mibs + tar xvfz /path/to/v1.tar.gz BRIDGE-MIB.my SNMP-REPEATER-MIB.my ESSWITCH-MIB.my -This module is geared towards network devices. Speciality sub-classes -exist for a number of vendors and products (see below). +=item More Specific MIBs + +Some non-cisco subclasses will need MIBs other than the basic one available from Cisco. + +Check below under each subclass for requirements. + +=back + +=back =head2 Design Goals =over -=item 1. Use of MIB variables and values instead of purely numeric OID +=item 1. Use of textual MIB leaf identifier and enumerated values -All values are retrieved via MIB Leaf node names. +=over -This means that SNMP::Info only asks SNMP::Session to look for ``sysName'' instead +=item * All values are retrieved via MIB Leaf node names + +For example SNMP::Info has an entry in its %GLOBALS hash for ``sysName'' instead of 1.3.6.1.2.1.1.5. -It also means that you need to download MIB files for each sub module -that you use. +=item * Data returned is in the enumerated value form. -The other side effect to using MIBs is data results come back as meaningful -text, instead of integers. - -Instead of looking up 1.3.6.1.2.1.2.2.1.3 and getting back C<23> +For Example instead of looking up 1.3.6.1.2.1.2.2.1.3 and getting back C<23> SNMP::Info will ask for C and will get back C. +=back + =item 2. SNMP::Info is easily extended to new devices -You can create a new sub class for a device by filling in Four hashes -%GLOBALS, %MIBS, %FUNCS, and %MUNGE with the names of the SNMP attributes -that are specific to your device. See the bottom of this document -for a sample Sub Class. +You can create a new subclass for a device by providing four hashes : +%GLOBALS, %MIBS, %FUNCS, and %MUNGE. -When you make a new sub class for a device, please be sure to send it +Or you can override any existing methods from a parent class by making a short subroutine. + +See the section EXTENDING SNMP::INFO for more details. + +When you make a new subclass for a device, please be sure to send it back to the developers at snmp@warped.org for inclusion in the next version. =back -=head1 Sub Classes +=head2 Subclasses + +These are the subclasses that implement MIBs and support devices: + +Required MIBs not included in the install instructions above are noted here. + +=head3 MIB Subclasses + +These subclasses implement method to access one or more MIBs. These are not +used directly, but rather inherited from device subclasses. =over =item SNMP::Info::Bridge +BRIDGE-MIB (RFC1286). Inherited by devices with Layer2 service. + =item SNMP::Info::CDP +CISCO-CDP-MIB. Cisco Discovery Protocol (CDP) Support. Inherited by devices serving Layer2 or Layer3. + +=item SNMP::Info::CiscoStats + +Provides common interfaces for memory, cpu, and os statistics for Cisco devices. Provides methods for +information in : OLD-CISCO-CPU-MIB, CISCO-PROCESS-MIB and CISCO-MEMORY-POOL-MIB + +=item SNMP::Info::Entity + +ENTITY-MIB. Used for device info in Cisco and other vendors. + =item SNMP::Info::EtherLike +ETHERLIKE-MIB (RFC1398) - Some Layer3 devices implement this MIB, as well as some Aironet Layer 2 devices (non Cisco). + =item SNMP::Info::MAU -=item SNMP::Info::Layer1 - -=item SNMP::Info::Layer2 - -=item SNMP::Info::Layer3 - -=item SNMP::Info::Layer1::Allied - -=item SNMP::Info::Layer1::Asante - -=item SNMP::Info::Layer2::Bay - -=item SNMP::Info::Layer2::C1900 - -=item SNMP::Info::Layer2::C2900 - -=item SNMP::Info::Layer2::Catalyst - -=item SNMP::Info::Layer2::HP - -=item SNMP::Info::Layer3::Aironet - -=item SNMP::Info::Layer3::Foundry - -=item SNMP::Info::Layer3::C3550 +MAU-MIB (RFC2668). Some Layer2 devices use this for extended Ethernet (Media Access Unit) interface information. =back -=head2 Details +=head3 Device Subclasses + +These subclasses inherit from one or more classes to provide a common interface to data obtainable +from network devices. =over -=item * SNMP::Info::Bridge - BRIDGE-MIB - RFC1286 Support +=item SNMP::Info::Layer1 -Requires BRIDGE-MIB +Generic Layer1 Device subclass. -=item * SNMP::Info::CDP - Cisco Discovery Protocol (CDP) Support. +=over -Provides Layer 2 Topology Information on Cisco and some HP Devices. +=item SNMP::Info::Layer1::Allied -Requires CISCO-CDP-MIB +Subclass for Allied Telesys Repeaters / Hubs. -=item * SNMP::Info::EtherLike +Requires ATI-MIB -Requires ETHERLIKE-MIB - RFC1398 +See SNMP::Info::Layer1::Allied for where to get MIBs required. -=item * SNMP::Info::Layer1 - Generic Layer 1 Device Support +=item SNMP::Info::Layer1::Asante -Requires Standard V1 and V2 MIBs +Subclass for Asante 1012 Hubs. -=item * SNMP::Info::Layer2 - Generic Layer 2 Device Support +Requires ASANTE-HUB1012-MIB -Inherits: - - SNMP::Info::CDP - SNMP::Info::Bridge - -Required MIBs: - - CISCO-PRODUCTS-MIB - Gives model information for Cisco - HP-ICF-OID - Gives model information for HPs - -=item * SNMP::Info::Layer3 - Generic Layer 3 and Layer 2/3 Device Support - -Inherits: - - SNMP::Info::Bridge - For Layer 2/3 devices - SNMP::Info::CDP - SNMP::Info::EtherLike - -Required MIBs: - - CISCO-PRODUCTS-MIB - Gives model information for Cisco - HP-ICF-OID - Gives model information for HPs - ENTITY-MIB - Gives some chassis information - OSPF-MIB - Gives router information - -=item * SNMP::Info::MAU - RFC2668 - Media Access Unit (MAU) MAU-MIB - -=item * SNMP::Info::Layer1::Allied - Allied TeleSys Hub Support - -Requires ATI-MIB - Allied Devices MIB downloadable from -http://www.allied-telesyn.com/allied/support/ - -=item * SNMP::Info::Layer1::Asante - Asante 1012 Hubs - -Requires ASANTE-HUB1012-MIB - Download from http://www.mibdepot.com - -=item * SNMP::Info::Layer2::Bay - Bay Networks BayStack Switch Support - -Required MIBs: - - SYNOPTICS-ROOT-MIB - Gives model information for Bay - S5-ETH-MULTISEG-TOPOLOGY-MIB - Gives Layer 2 topology information for Bay - -Other supporting MIBs needed, see SNMP::Info::Bay for more info - -=item * SNMP::Info::Layer2::C1900 - Cisco 1900 and 1900c Device Support - -Requires STAND-ALONE-ETHERNET-SWITCH-MIB (ESSWITCH-MIB) - -=item * SNMP::Info::Layer2::C2900 - Cisco 2900 Series Device Support. - -Requires CISCO-C2900-MIB - -=item * SNMP::Info::Layer2::Catalyst - Cisco Catalyst WSC Series Switch Support - -Requires MIBs: - - CISCO-STACK-MIB - CISCO-VTP-MIB - -=item * SNMP::Info::Layer2::HP - HP Procurve Switch Support - -Inherits: - - SNMP::Info::MAU - -Required MIBs: - - ENTITY-MIB - RFC1271-MIB - HP-ICF-OID - -=item * SNMP::Info::Layer3::Aironet - Cisco Aironet Wireless Access Points (AP) Support - -Required MIBs: - - AWCVX-MIB - Aironet Specific MIB values - IEEE802dot11-MIB - IEEE 802.11 Specific MIB (currently draft) - - -=item * SNMP::Info::Layer3::C3550 - Cisco Catalyst 3550 Layer2/3 Switch - -=item * SNMP::Info::Layer3::Foundry - Older Foundry Networks Devices Support - -Inherits SNMP::Info::Bridge - -Requires FOUNDRY-SN-ROOT-MIB - Foundry specific values. -See SNMP::Info::Layer3::Foundry for more information. +See SNMP::Info::Layer1::Asante for where to get MIBs required. =back -=head1 METHODS +=item SNMP::Info::Layer2 - These are generic methods from RFC1213. Some subset of these is -probably available for any network device that speaks SNMP. +Generic Layer2 Device subclass. + +=over + +=item SNMP::Info::Layer2::Bay + +Bay Networks BayStack switch Support. Provides translation from Bay Network Topology Table +information to CDP. + +Requires SYNOPTICS-ROOT-MIB and S5-ETH-MULTISEG-TOPOLOGY-MIB + +See SNMP::Info::Bay for where to get MIBs required. + +=item SNMP::Info::Layer2::C1900 + +Subclass for Cisco Catalyst 1900 and 1900c Devices running CatOS. + +=item SNMP::Info::Layer2::C2900 + +Subclass for Cisco Catalyst 2900 devices running IOS. + +=item SNMP::Info::Layer2::Catalyst + +Subclass for Cisco Catalyst switches running CatOS. These switches usually +report a model number that starts with ''wsc''. Note that this class +does not support everything that has the name Catalyst. + +=item SNMP::Info::Layer2::HP + +Subclass for HP Procurve Swithces + +Requires HP-ICF-OID and ENTITY-MIB downloaded from HP. + +See SNMP::Info::Layer2::HP for more info. + +=back + +=item SNMP::Info::Layer3 + +Generic Layer3 and Layer2+3 Device subclass. + +=over + +=item SNMP::Info::Layer3::Aironet + +Subclass for Cisco Aironet wireless access points (AP). + +MIBs for these devices now included in v2.tar.gz available from ftp.cisco.com. + +=item SNMP::Info::Layer3::Foundry + +No longer supported. + +Subclass for older Foundry Network devices. + +Requires FOUNDRY-SN-ROOT-MIB. + +See SNMP::Info::Layer3::Foundry for more info. + +=item SNMP::Info::Layer3::C3550 + +Subclass for Cisco Catalyst 3550 2/3 switches running IOS. + +=back + +=back + +=head1 USAGE =head2 Constructor @@ -365,7 +384,11 @@ SNMP::Info Specific Arguments : *See specify() entry* Debug = Prints Lots of debugging messages Session = SNMP::Session object to use instead of connecting on own. - BigInt = Return Math::BigInt objects for 64 bit counters. + BigInt = Return Math::BigInt objects for 64 bit counters. Sets on a global scope, not object. + RetryNoSuch = When using SNMP Version 1, try reading values even if they come back + as "no such variable in this MIB". Defaults to true, set to false if + so desired. This feature lets you read SNMPv2 data from an SNMP version + 1 connection, and should probably be left on. All other arguments are passed to SNMP::Session. @@ -378,7 +401,7 @@ sub new { my %args = @_; my $new_obj = {}; bless $new_obj,$class; - + $new_obj->{class} = $class; # load references to all the subclass data structures @@ -405,8 +428,10 @@ sub new { delete $args{AutoSpecify}; } if (defined $args{Debug}){ - $DEBUG = $args{Debug}; + $new_obj->debug($args{Debug}); delete $args{Debug}; + } else { + $new_obj->debug($DEBUG); } my $sess = undef; if (defined $args{Session}){ @@ -418,14 +443,15 @@ sub new { delete $args{BigInt}; } + $new_obj->{nosuch} = $args{RetryNoSuch} || $NOSUCH; + # Connects to device unless open session is provided. - $sess = new SNMP::Session( 'UseEnums' => 1, %args ) + $sess = new SNMP::Session( 'UseEnums' => 1, %args , 'RetryNoSuch' => $new_obj->{nosuch}) unless defined $sess; unless (defined $sess){ - $new_obj->{error} = "SNMP::Info::new() Failed to Create Session. ". - $sess->{ErrorStr} || ''; - $DEBUG and carp($new_obj->error()); + my $sess_err = $sess->{ErrorStr} || ''; + $new_obj->error_throw("SNMP::Info::new() Failed to Create Session. $sess_err"); return undef; } @@ -447,23 +473,56 @@ sub new { =head2 Data is Cached -A call to any of these methods will load the data once, and then -return cached versions of that data. +Methods and subroutines requesting data from a device will only load the data once, and then +return cached versions of that data. -Use load_METHOD() to reload from the device +Run $info->load_METHOD() where method is something like 'i_name' to reload data from a +table method. - $data = $cdp->c_ip(); - ... - $cdp->load_c_ip(); - $newdata = $cdp->c_ip(); +Run $info->clear_cache() to clear the cache to allow reload of both globals and table methods. -=head2 Scalar Methods +=head2 Object Scalar Methods + +These are for package related data, not direcly supplied +from SNMP. =over +=item $info->clear_cache() + +Clears the cached data. This includes GLOBALS data and TABLE METHOD data. + +=cut +sub clear_cache { + my $self = shift; + + foreach my $key (keys %$self){ + next unless defined $key; + next unless $key =~ /^_/; + delete $self->{$key}; + } +} + +=item $info->debug(1) + +Returns current debug status, and optionally toggles debugging info for this object. + +=cut +sub debug { + my $self = shift; + my $debug = shift; + + if (defined $debug){ + $self->{debug} = $debug; +# $SNMP::debugging=$debug; + } + + return $self->{debug}; +} + =item $info->device_type() -Returns the SubClass name for this device. C is returned if no more +Returns the Subclass name for this device. C is returned if no more specific class is available. First the device is checked for Layer 3 support and a specific subclass, @@ -475,10 +534,10 @@ SNMP::Info::Layer3 subclasses. If the device still can be connected to via SNMP::Info, then SNMP::Info is returned. -Algorithm for SubClass Detection: +Algorithm for Subclass Detection: Layer3 Support -> SNMP::Info::Layer3 - Aironet -> SNMP::Info::Layer3::Aironet + Aironet (non IOS) -> SNMP::Info::Layer3::Aironet Catalyst 3550 -> SNMP::Info::Layer3::C3550 Foundry -> SNMP::Info::Layer3::Foundry Elsif Layer2 (no Layer3) -> SNMP::Info::Layer2 @@ -554,49 +613,20 @@ sub device_type { return $objtype; } -=item $info->specify() +=item $info->error(no_clear) -Returns an object of a more-specific subclass. +Returns Error message if there is an error, or undef if there is not. - my $info = new SNMP::Info(...); - # Returns more specific object type - $info = $info->specific(); - -Usually this method is called internally from new(AutoSpecify => 1) - -See device_type() entry for how a sub class is chosen. +Reading the error will clear the error unless you set the no_clear flag. =cut -sub specify { - my $self = shift; +sub error { + my $self = shift; + my $no_clear = shift; + my $err = $self->{error}; - my $device_type = $self->device_type(); - unless (defined $device_type) { - $self->{error} = "SNMP::Info::specify() - Could not get info from device"; - $DEBUG and print $self->error(); - return undef; - } - return $self if $device_type eq 'SNMP::Info'; - - # Load Sub Class - # By evaling a string the contents of device_type now becomes a bareword. - eval "require $device_type;"; - if ($@) { - croak "SNMP::Info::specify() Loading $device_type Failed. $@\n"; - } - - my $args = $self->args(); - my $session = $self->session(); - my $sub_obj = $device_type->new(%$args,'Session'=>$session); - - unless (defined $sub_obj) { - $self->{error} = "SNMP::Info::specify() - Could not connect with new class ($device_type)."; - carp($self->error() ); - return $self; - } - - $DEBUG and print "SNMP::Info::specify() - Changed Class to $device_type.\n"; - return $sub_obj; + $self->{error} = undef unless defined $no_clear and $no_clear; + return $err; } =item $info->has_layer(3) @@ -616,6 +646,80 @@ sub has_layer { return substr($layers,8-$check_for, 1); } +=item $info->snmp_comm() + +Returns SNMP Community string used in conncetion + +=cut +sub snmp_comm { + my $self = shift; + return $self->{snmp_comm}; +} + +=item $info->snmp_ver() + +Returns SNMP Version used for this connection + +=cut +sub snmp_ver { + my $self = shift; + return $self->{snmp_ver}; +} + +=item $info->specify() + +Returns an object of a more-specific subclass. + + my $info = new SNMP::Info(...); + # Returns more specific object type + $info = $info->specific(); + +Usually this method is called internally from new(AutoSpecify => 1) + +See device_type() entry for how a subclass is chosen. + +=back + +=cut +sub specify { + my $self = shift; + + my $device_type = $self->device_type(); + unless (defined $device_type) { + $self->error_throw("SNMP::Info::specify() - Could not get info from device"); + return undef; + } + return $self if $device_type eq 'SNMP::Info'; + + # Load Subclass + # By evaling a string the contents of device_type now becomes a bareword. + eval "require $device_type;"; + if ($@) { + croak "SNMP::Info::specify() Loading $device_type Failed. $@\n"; + } + + my $args = $self->args(); + my $session = $self->session(); + my $sub_obj = $device_type->new(%$args,'Session'=>$session); + $sub_obj->debug($self->debug()); + + unless (defined $sub_obj) { + $self->error_throw("SNMP::Info::specify() - Could not connect with new class ($device_type)"); + return $self; + } + + $self->debug() and print "SNMP::Info::specify() - Changed Class to $device_type.\n"; + return $sub_obj; +} + +=head2 Globals (Scalar Methods) + +These are methods to return scalar data from RFC1213. + +Some subset of these is probably available for any network device that speaks SNMP. + +=over + =item $info->uptime() Uptime in hundreths of seconds since device became available. @@ -645,12 +749,17 @@ by the device. Note: This string is 8 digits long. +See $info->has_layer() + (B) =item $info->ports() Number of interfaces available on this device. +Not too useful as the number of SNMP interfaces usually does not +correspond with the number of physical ports + (B) =back @@ -659,8 +768,14 @@ Number of interfaces available on this device. Each of these methods returns a hash_reference to a hash keyed on the interface index in SNMP. - Example : $cdp->c_ip() returns - { '304' => '123.123.231.12' } +Example : $info->interfaces() might return + { '1.12' => 'FastEthernet/0', + '2.15' => 'FastEthernet/1', + '9.99' => 'FastEthernet/2' + } + +The key is what you would see if you were to do an snmpwalk, and in some cases changes between reboots of +the network device. =head3 Interface Information @@ -676,12 +791,9 @@ mapping between the Interface Table Index (iid) and the physical port name. Returns a reference to a hash where key values that exist are interfaces to ignore. -Ignored interfaces are ones that are usually -not Physical ports or Virtual Lans (VLANs) such as the Loopback interface, +Ignored interfaces are ones that are usually not physical ports or Virtual Lans (VLANs) such as the Loopback interface, or the CPU interface. -SNMP::Info and it's subclasses tries to provide data on Physical ports. - =cut sub if_ignore { my %nothing; @@ -690,72 +802,64 @@ sub if_ignore { =item $info->i_index() -Defaults to $info->interfaces() +Default SNMP IID to Interface index. (B) =item $info->i_description() -Returns reference to hash keyed by iid. Values are the Textual Description - of the interface (port). Usually the physical / human-friendly name. +Description of the interface. Usually a little longer single word name that is both +human and machine friendly. Not always. (B) =item $info->i_type() -Returns reference to hash keyed by iid. Values are the port type, such -as Vlan, 10baseT, Ethernet, Serial... +Interface type, such as Vlan, 10baseT, Ethernet, Serial (B) =item $info->i_mtu() -Returns reference to hash keyed by iid. Values are the MTU value for the -port. +INTEGER. Interface MTU value. (B) =item $info->i_speed() -Returns reference to hash keyed by iid. Values are the speed of the link. +Speed of the link, human format. See munge_speed() later in document for details. (B) =item $info->i_mac() -Returns reference to hash keyed by iid. Values are the MAC address of the -interface. Note this is just the MAC of the port, not anything connected to it. +MAC address of the interface. Note this is just the MAC of the port, not anything connected to it. (B) =item $info->i_up() -Returns reference to hash keyed by iid. Values are the Link Status of the -interface. Typical values are 'up' and 'down'. +Link Status of the interface. Typical values are 'up' and 'down'. (B) =item $info->i_up_admin() -Returns reference to hash keyed by iid. Values are the administrative -status of the port. Typical values are 'enabled' and 'disabled'. +Administrative status of the port. Typical values are 'enabled' and 'disabled'. (B) =item $info->i_name() -Returns reference to hash keyed by iid. Values are the Interface Name -field. Supported by a smaller subset of devices, this fields is often +Interface Name field. Supported by a smaller subset of devices, this fields is often human set. (B) =item $info->i_alias() -Returns reference to hash keyed by iid. Values are a differnent version -of the Interface Description or Interface Name. For certain devices this -is a more human friendly form of i_description() . For others it is a human -set field like i_name(). +Interface Name field. For certain devices this is a more human friendly form of i_description(). +For others it is a human set field like i_name(). (B) @@ -862,21 +966,56 @@ Gives broadcast address for IP table entry. =back -=head2 Default %MUNGE +=head2 Setting data via SNMP - ip -> &munge_ip +This section explains how to use SNMP::Info to do SNMP Set operations. - mac -> &munge_mac +=over - i_mac -> &munge_mac +=item $info->set_METHOD($value) - layers -> &munge_dec2bin +Sets the global METHOD to value. Assumes that iid is .0 -=cut +Returns undef if failed, or the return value from SNMP::Session::set() (snmp_errno) -=head1 CREATING SUBCLASSES + $info->set_location("Here!"); -=head2 Data Structures Used in SNMP::Info and SubClasses +=item $info->set_METHOD($value,$iid) + +Table Methods. Set iid of method to value. + +Returns undef if failed, or the return value from SNMP::Session::set() (snmp_errno) + + # Disable a port administratvely + my %if_map = reverse %{$info->interfaces()} + $info->set_i_up_admin('down', $if_map{'FastEthernet0/0') + or die "Couldn't disable the port. ",$info->error(1); + +=back + +NOTE: You must be connected to your device with a ReadWrite community string in order +for set operations to work. + +NOTE: This will only set data listed in %FUNCS and %GLOBALS. For data aquired from +overriden methods (subroutines) specific set_METHOD() subroutines will need to be +added. + +=head2 Quiet Mode + +SNMP::Info will not chirp anything to STDOUT unless there is a serious error (in which case it will probably +die). + +To get lots of debug info, set the Debug flag when calling new() or call $info->debug(1); + +When calling a method check the return value. If the return value is undef then check $info->error() + +Beware, calling $info->error() clears the error. + + my $name = $info->name() or die "Couldn't get sysName!" . $name->error(); + +=head1 EXTENDING SNMP::INFO + +=head2 Data Structures required in new Subclass A class inheriting this class must implement these data structures : @@ -976,10 +1115,16 @@ $info->init() will throw an exception if a MIB does not load. =item %MUNGE -A map between method calls (from %FUNCS or %GLOBALS) and sub routine methods. +A map between method calls (from %FUNCS or %GLOBALS) and subroutine methods. The subroutine called will be passed the data as it gets it from SNMP and it should return that same data in a more human friendly format. +Sample %MUNGE: + + (my_ip => \&munge_ip, + my_mac => \&munge_mac, + my_layers => \&munge_dec2bin + ) =cut %MUNGE = ('ip' => \&munge_ip, @@ -999,10 +1144,12 @@ it should return that same data in a more human friendly format. =back -=head2 Sample Sub Class +=head2 Sample Subclass Let's make a sample Layer 2 Device subclass : +----------------------- snip -------------------------------- + # SNMP::Info::Layer2::Sample package SNMP::Info::Layer2::Sample; @@ -1058,28 +1205,51 @@ Let's make a sample Layer 2 Device subclass : # Take the returned obscure value and return something useful. return 'Fire' if $power =~ /reallyhot/i; return 'Ice' if $power =~ /reallycold/i; - + # Else return $power; } - # Add Copious Documentation here!!! + # Copious Documentation here!!! + =head1 NAME + =head1 AUTHOR + =head1 SYNOPSIS + =head1 DESCRIPTION + =head2 Inherited Classes + =head2 Required MIBs + =head1 GLOBALS + =head2 Overrides + =head1 TABLE METHODS + =head2 Overrides + =cut +----------------------- snip -------------------------------- Be sure and send the debugged version to snmp@warped.org to be included in the next version of SNMP::Info. +=head1 SNMP::INFO INTERNALS + +=head2 Object Namespace + +Internal data is stored with bareword keys. For example $info->{debug} + +SNMP Data is stored or marked cached with keys starting with an underscore. +For example $info->{_name} is the cache for $info->name(). + +Cached Table data is stored in $info->store() and marked cached per above. + =head2 Package Globals -These are variables that get set by methods, or arguments passed to new() -Avoid modifying them directly +These set the default value for an object upon creation. =over =item $DEBUG -Default 0. Sends copious debug info to stdout. Set with Debug argument in new() or with the -debug() method on an object. +Default 0. Sends copious debug info to stdout. This global sets the object's debug status +in new() unless 'Debug' argument passed in new(). Change objects' debug status with +$info->debug(). =cut $DEBUG = 0; @@ -1092,9 +1262,17 @@ string values. See note under Interface Statistics about 64 bit values. =cut $BIGINT = 0; +=item $NOSUCH + +Default 1. Set to false to disable RetryNoSuch option for SNMP::Session. Or see method in new() +to do it on an object scope. + +=cut +$NOSUCH = 1; + =back -=head2 Data Munging Callback Subs +=head2 Data Munging Callback Subroutines =over @@ -1266,21 +1444,6 @@ sub init { croak "The $mib did not load. See README for $self->{class}\n"; } } - -} - -=item $info->debug(1) - -Turns on debugging info for this class and SNMP - -=cut -sub debug { - my $self = shift; - my $debug = shift; - - $DEBUG=$debug; - $SNMP::debugging=$debug; - } =item $info->args() @@ -1293,7 +1456,7 @@ sub args { return $self->{args}; } -=item $info->class() +=item $info->class() Returns the class name of the object. @@ -1303,28 +1466,28 @@ sub class { return $self->{class}; } -=item $info->error(no_new_line) -Returns Error message if error, or undef if not. +=item $info->error_throw(error message) -Pass a 1 as the first argument if you don't want a new line. +Stores the error message for use by $info->error() + +If $info->debug() is true, then the error message is carped too. =cut -sub error { +sub error_throw { my $self = shift; - my $no_nl = shift; - my $err = $self->{error}; - if (defined $no_nl and $no_nl and $err =~ /\n$/) { - $err =~ s/\n$//; - return $err; + my $error = shift; + + return undef unless defined $error; + $self->{error} = $error; + + if ($self->debug()){ + $error .= "\n" unless $error =~ /\n$/; + carp($error); } - if ($err !~ /\n$/) { - $err .= "\n"; - } - return $err; } -=item $info->funcs() +=item $info->funcs() Returns a reference to the %FUNCS hash. @@ -1334,7 +1497,7 @@ sub funcs { return $self->{funcs}; } -=item $info->globals() +=item $info->globals() Returns a reference to the %GLOBALS hash. @@ -1342,10 +1505,9 @@ Returns a reference to the %GLOBALS hash. sub globals { my $self=shift; return $self->{globals}; - } -=item $info->mibs() +=item $info->mibs() Returns a reference to the %MIBS hash. @@ -1355,7 +1517,7 @@ sub mibs { return $self->{mibs}; } -=item $info->munge() +=item $info->munge() Returns a reference ot the %MUNGE hash. @@ -1365,7 +1527,17 @@ sub munge { return $self->{munge}; } -=item $info->session() +=item $info->nosuch() + +Returns NoSuch value set or not in new() + +=cut +sub nosuch { + my $self = shift; + return $self->{nosuch}; +} + +=item $info->session() Gets or Sets the SNMP::Session object. @@ -1376,27 +1548,6 @@ sub session { return $self->{sess}; } - -=item $info->snmp_comm() - -Returns SNMP Community string used in conncetion - -=cut -sub snmp_comm { - my $self = shift; - return $self->{snmp_comm}; -} - -=item $info->snmp_ver() - -Returns SNMP Version used for this connection - -=cut -sub snmp_ver { - my $self = shift; - return $self->{snmp_ver}; -} - =item $info->store() Returns hash store for Table functions. @@ -1409,11 +1560,6 @@ sub store { return $self->{store}; } -=back - -=head3 Functions for SNMP Scalars (%GLOBALS) - -=over =item $info->_global() @@ -1427,7 +1573,7 @@ sub _global{ my $attr = shift; my $sess = $self->session(); return undef unless defined $sess; - + my $globals = $self->globals(); my $oid = $globals->{$attr}; @@ -1437,24 +1583,21 @@ sub _global{ $oid .= ".0"; } - $DEBUG and print "SNMP::Info::_global $attr : $oid\n"; + print "SNMP::Info::_global $attr : $oid\n" if $self->debug(); my $val = $sess->get($oid); if ($sess->{ErrorStr} ){ - $self->{error} = "SNMP::Info::_global($attr) $sess->{ErrorStr}"; - $DEBUG and print $self->error(); + $self->error_throw("SNMP::Info::_global($attr) $sess->{ErrorStr}"); return undef; } if (defined $val and $val eq 'NOSUCHOBJECT'){ - $self->{error} = "SNMP::Info::_global($attr) NOSUCHOBJECT"; - $DEBUG and print $self->error(); + $self->error_throw("SNMP::Info::_global($attr) NOSUCHOBJECT"); return undef; } if (defined $val and $val eq 'NOSUCHINSTANCE'){ - $self->{error} = "SNMP::Info::_global($attr) NOSUCHINSTANCE"; - $DEBUG and print $self->error(); + $self->error_throw("SNMP::Info::_global($attr) NOSUCHINSTANCE"); return undef; } # Get the callback hash for data munging @@ -1487,7 +1630,6 @@ sub _set { # prepend dot if necessary to $iid $iid = ".$iid" unless $iid =~ /^\./; - my $sess = $self->session(); return undef unless defined $sess; @@ -1500,34 +1642,28 @@ sub _set { $oid = $funcs->{$attr} if defined $funcs->{$attr}; unless (defined $oid) { - $self->{error} = "SNMP::Info::_set($attr,$val) - Failed to find $attr in \%GLOBALS or \%FUNCS"; - carp($self->error()); + $self->error_throw("SNMP::Info::_set($attr,$val) - Failed to find $attr in \%GLOBALS or \%FUNCS"); return undef; } $oid .= $iid; - $DEBUG and print "SNMP::Info::_set $attr$iid ($oid) = $val\n"; + $self->debug() and print "SNMP::Info::_set $attr$iid ($oid) = $val\n"; my $rv = $sess->set($oid,$val); if ($sess->{ErrorStr}){ - $self->{error} = "SNMP::Info::_set $attr$iid $sess->{ErrorStr}"; - $DEBUG and print $self->error(); + $self->error_throw("SNMP::Info::_set $attr$iid $sess->{ErrorStr}"); + return undef; } return $rv; } -=back - -=head3 Functions for SNMP Tables (%FUNCS) - -=over - =item $info->load_all() -Loads all possible function data for this class. +Debugging routine. This does not include any overriden method or method implemented +by subroutine. Runs $info->load_METHOD() for each entry in $info->funcs(); @@ -1584,34 +1720,47 @@ sub _load_attr { my $self = shift; my ($attr,$leaf) = @_; - my $sess = $self->session(); - my $store = $self->store(); + my $ver = $self->snmp_ver(); + my $nosuch = $self->nosuch(); + my $sess = $self->session(); + my $store = $self->store(); return undef unless defined $sess; # Get the callback hash for data munging my $munge = $self->munge(); - $DEBUG and print "SNMP::Info::_load_attr $attr : $leaf\n"; + $self->debug() and print "SNMP::Info::_load_attr $attr : $leaf\n"; my $var = new SNMP::Varbind([$leaf]); - while (! $sess->{ErrorNum} ){ + + # So devices speaking SNMP v.1 are not supposed to give out + # data from SNMP2, but most do. Net-SNMP, being very precise + # will tell you that the SNMP OID doesn't exist for the device. + # They have a flag RetryNoSuch that is used for get() operations, + # but not for getnext(). We set this flag normally, and if we're + # using V1, let's try and fetch the data even if we get one of those. + my $errornum = $sess->{ErrorNum}; + if ($ver == 1 and $nosuch and $errornum and $sess->{ErrorStr} =~ /nosuch/i){ + $errornum = 0; + } + while (! $errornum ){ $sess->getnext($var); + $errornum = $sess->{ErrorNum}; last if $var->[0] ne $leaf; my $iid = $var->[1]; my $val = $var->[2]; unless (defined $iid){ - $DEBUG and print "SNMP::Info::_load_attr: $attr not here\n"; + $self->error_throw("SNMP::Info::_load_attr: $attr not here"); next; } - if ($val eq 'NOSUCHOBJECT'){ - $DEBUG and print "SNMP::Info::_load_atr: $attr : NOSUCHOBJECT\n" ; + $self->error_throw("SNMP::Info::_load_atr: $attr : NOSUCHOBJECT"); next; } if ($val eq 'NOSUCHINSTANCE'){ - $DEBUG and print "SNMP::Info::_load_atr: $attr : NOSUCHINSTANCE\n" ; + $self->error_throw("SNMP::Info::_load_atr: $attr : NOSUCHINSTANCE"); next; } @@ -1628,6 +1777,7 @@ sub _load_attr { # mark data as loaded $self->{"_${attr}"}++; + return $store->{$attr}; } =item $info->_show_attr() @@ -1655,18 +1805,28 @@ sub _show_attr { Each entry in either %FUNCS or %GLOBALS is used by AUTOLOAD() to create dynamic methods. -First Autoload sees if the method name is listed in either of the two hashes. +Note that this AUTOLOAD is going to be run for all the classes listed in the @ISA array +in a subclass, so will be called with a variety of package names. We check the %FUNCS and +%GLOBALS of the package that is doing the calling at this given instant. -If the method exists in globals, it runs $info->_global(method). +=over -Next it will check %FUNCS, run $info->_load_attr(method) if needed -and return $info->_show_attr(method). +=item 1. Returns unless method is listed in %FUNCS or %GLOBALS for given class -Override any dynamic method listed in one of these hashes by creating a sub with +=item 2. If the method exists in %GLOBALS it runs $info->_global(method) unless already cached. + +=item 3. Method is in %FUNCS + +=item 4. Run $info->_load_attr(method) if not cached + +=item 5. Return $info->_show_attr(method). + +=back + +Override any dynamic method listed in one of these hashes by creating a subroutine with the same name. -Example : - Override $info->name() by creating `` sub name {}'' in your Module. +For example to override $info->name() create `` sub name {...}'' in your subclass. =cut sub AUTOLOAD { @@ -1694,22 +1854,21 @@ sub AUTOLOAD { unless( defined $funcs{$attr} or defined $globals{$attr} ) { - $self->{error} = "SNMP::Info::AUTOLOAD($attr) Attribute not found in this device class."; - $DEBUG and print($self->error()); + $self->error_throw("SNMP::Info::AUTOLOAD($attr) Attribute not found in this device class."); return; } # Check for load_ ing. if ($sub_name =~ /^load_/){ - $self->_load_attr( $attr,$funcs{$attr} ); - return $self->_show_attr( $attr ) if defined wantarray; + return $self->_load_attr( $attr,$funcs{$attr} ); } + # Check for set_ ing. if ($sub_name =~ /^set_/){ return $self->_set( $attr, @_); } - # First check %GLOBALS and return _scalar(global) + # Next check for entry in %GLOBALS if (defined $globals{$attr} ){ # Return Cached Value if exists return $self->{"_${attr}"} if defined $self->{"_${attr}"}; @@ -1725,5 +1884,34 @@ sub AUTOLOAD { return $self->_show_attr($attr); } - 1; + +=head1 COPYRIGHT AND LICENCE + +Copyright (c) 2002-3, Regents of the University of California +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the University of California, Santa Cruz nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=cut