Lots of Changes.

- Added auto-specify code in new() and specify()
    - Added BigInt / 64 Bit Counter code
    - Added SNMP::Info::Layer2::Aironet
    - GLOBALs (scalar SNMP data) are now cached
    - Added Interface Statistics methods (i_pkts_*, i_octet_*)
This commit is contained in:
Max Baker
2003-04-17 21:20:26 +00:00
parent 3c3c040471
commit c2bbd03c2f

300
Info.pm
View File

@@ -13,13 +13,12 @@ use strict;
use Exporter; use Exporter;
use SNMP; use SNMP;
use Carp; use Carp;
use Math::BigInt;
@SNMP::Info::ISA = qw/Exporter/; @SNMP::Info::ISA = qw/Exporter/;
@SNMP::Info::EXPORT_OK = qw//; @SNMP::Info::EXPORT_OK = qw//;
use vars qw/$VERSION %FUNCS %GLOBALS %MIBS %MUNGE $AUTOLOAD $INIT $DEBUG %SPEED_MAP/; use vars qw/$VERSION %FUNCS %GLOBALS %MIBS %MUNGE $AUTOLOAD $INIT $DEBUG %SPEED_MAP $BIGINT/;
$DEBUG=0;
=head1 NAME =head1 NAME
@@ -65,27 +64,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=head1 SYNOPSIS =head1 SYNOPSIS
# Connect with generic Info object use SNMP::Info
my $info = new SNMP::Info( DestHost => 'router' , my $info = new SNMP::Info(
Community => 'public' ); # Auto Discover more specific Device Class
AutoSpecify => 1,
Debug => 1,
# The rest is passed to SNMP::Session
DestHost => 'router',
Community => 'public',
Version => 2 )
or die;
$name = $info->name(); $name = $info->name();
# Try and find a more specific subclass of SNMP::Info # Let's see what sub-class it picked for us
my $object_class = $info->device_type(); print "Device is of type : ", $info->class(), "\n";
my $more_specific_device = new $object_class(
'Desthost' => 'mydevice',
'Community' => 'public');
# Find out the Duplex status for the ports # Find out the Duplex status for the ports
my $interfaces = $more_specific_device->interfaces(); my $interfaces = $info->interfaces();
my $i_duplex = $more_specific_device->i_duplex(); my $i_duplex = $info->i_duplex();
# Get CDP Neighbor info # Get CDP Neighbor info
my $c_ip = $more_specific_device->c_ip(); my $c_ip = $info->c_ip();
my $c_port = $more_specific_device->c_port(); my $c_port = $info->c_port();
foreach my $iid (keys %$interfaces){ foreach my $iid (keys %$interfaces){
@@ -349,11 +351,30 @@ probably available for any network device that speaks SNMP.
Creates a new object and connects via SNMP::Session. Creates a new object and connects via SNMP::Session.
Arguments given are passed to SNMP::Session and can be used to overide defaults. my $info = new SNMP::Info( 'Debug' => 1,
'AutoSpecify' => 1,
'BigInt' => 1
'DestHost' => 'myrouter',
'Community' => 'public',
'Version' => 2
) or die;
Arguments :
AutoSpecify = Returns an object of a more specific device class
*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.
All other arguments are passed to SNMP::Session.
See SNMP::Session for a list of other possible arguments.
=cut =cut
sub new { sub new {
my $class = shift; my $proto = shift;
my $class = ref($proto) || $proto;
my %args = @_; my %args = @_;
my $new_obj = {}; my $new_obj = {};
bless $new_obj,$class; bless $new_obj,$class;
@@ -380,18 +401,36 @@ sub new {
$$init_ref=1; $$init_ref=1;
} }
# These session defaults can be overwritten with @_ # SNMP::Info specific args :
my $sess = new SNMP::Session( my $auto_specific = 0;
#'Version' => 2, if (defined $args{AutoSpecify}){
'UseEnums' => 1, $auto_specific = $args{AutoSpecify} || 0;
delete $args{AutoSpecify};
}
if (defined $args{Debug}){
$DEBUG = $args{Debug};
delete $args{Debug};
}
my $sess = undef;
if (defined $args{Session}){
$sess = $args{Session};
delete $args{Session};
}
if (defined $args{BigInt}){
$BIGINT = $args{BigInt};
delete $args{BigInt};
}
# Save Args for later
$new_obj->{_args} = \%args;
# Connects to device unless open session is provided.
$sess = new SNMP::Session( 'UseEnums' => 1,
%args %args
); ) unless defined $sess;
unless (defined $sess){ unless (defined $sess){
# How do i get error messages back from SNMP? $DEBUG and carp("SNMP::Info::new() Failed to Create Session. ", defined $sess->{ErrorStr} ? $sess->{ErrorStr} : '', "\n");
#print $SNMP::ErrorStr;
print "SNMP::Info::new() $sess->{ErrorStr}\n"
if ($DEBUG and $sess->{ErrorStr});
return undef; return undef;
} }
@@ -400,7 +439,8 @@ sub new {
my $store = {}; my $store = {};
$new_obj->{store} = $store; $new_obj->{store} = $store;
return $new_obj; return $auto_specific ?
$new_obj->specify() : $new_obj;
} }
=back =back
@@ -442,6 +482,7 @@ Algorithm for SubClass Detection:
Catalyst 3550 -> SNMP::Info::Layer3::C3550 Catalyst 3550 -> SNMP::Info::Layer3::C3550
Foundry -> SNMP::Info::Layer3::Foundry Foundry -> SNMP::Info::Layer3::Foundry
Elsif Layer2 (no Layer3) -> SNMP::Info::Layer2 Elsif Layer2 (no Layer3) -> SNMP::Info::Layer2
Aironet (Cisco) AP1100 -> SNMP::Info::Layer2::Aironet
Bay Networks -> SNMP::Info::Layer2::Bay Bay Networks -> SNMP::Info::Layer2::Bay
Catalyst 1900 -> SNMP::Info::Layer2::C1900 Catalyst 1900 -> SNMP::Info::Layer2::C1900
Catalyst 2900XL (IOS) -> SNMP::Info::Layer2::C2900 Catalyst 2900XL (IOS) -> SNMP::Info::Layer2::C2900
@@ -500,6 +541,9 @@ sub device_type {
# Bay Switch # Bay Switch
$objtype = 'SNMP::Info::Layer2::Bay' if ($desc =~ /bay/i); $objtype = 'SNMP::Info::Layer2::Bay' if ($desc =~ /bay/i);
# Aironet
$objtype = 'SNMP::Info::Layer2::Aironet' if ($desc =~ /C1100/);
} elsif ($info->has_layer(1)) { } elsif ($info->has_layer(1)) {
$objtype = 'SNMP::Info::Layer1'; $objtype = 'SNMP::Info::Layer1';
# Allied crap-o-hub # Allied crap-o-hub
@@ -510,6 +554,47 @@ sub device_type {
return $objtype; return $objtype;
} }
=item $info->specify()
Returns an object of a more-specific subclass.
my $info = new SNMP::Info(...);
# Returns more specific object type
$info = $info->specific();
See device_type() entry for how a sub class is chosen.
=cut
sub specify {
my $self = shift;
my $device_type = $self->device_type();
unless (defined $device_type) {
carp("SNMP::Info::specify() - Could not get info from device.\n");
return $self;
}
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 ($@) {
print "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) {
carp("SNMP::Info::specify() - Could not connect with new class ($device_type).\n");
return $self;
}
$DEBUG and print "SNMP::Info::specify() - Changed Class to $device_type.\n";
return $sub_obj;
}
=item $info->has_layer(3) =item $info->has_layer(3)
Returns non-zero if the device has the supplied layer in the OSI Model Returns non-zero if the device has the supplied layer in the OSI Model
@@ -573,7 +658,7 @@ Each of these methods returns a hash_reference to a hash keyed on the interface
Example : $cdp->c_ip() returns Example : $cdp->c_ip() returns
{ '304' => '123.123.231.12' } { '304' => '123.123.231.12' }
=head3 Interfaces =head3 Interface Information
=over =over
@@ -672,6 +757,74 @@ set field like i_name().
=back =back
=head3 Interface Statistics
=over
=item $info->i_octet_in(), $info->i_octets_out(),
$info->i_octet_in64(), $info->i_octets_out64()
Bandwidth.
Number of octets sent/received on the interface including framing characters.
64 bit version may not exist on all devices.
NOTE: To manipulate 64 bit counters you need to use Math::BigInt, since the values
are too large for a normal Perl scalar. Set the global $SNMP::Info::BIGINT to 1 , or
pass the BigInt value to new() if you want SNMP::Info to do it for you.
(B<ifInOctets>) (B<ifOutOctets>)
(B<ifHCInOctets>) (B<ifHCOutOctets>)
=item $info->i_errors_in(), $info->i_errors_out()
Number of packets that contained an error prventing delivery. See IF-MIB for more info.
(B<ifInErrors>) (B<ifOutErrors>)
=item $info->i_pkts_ucast_in(), $info->i_pkts_ucast_out(),
$info->i_pkts_ucast_in64(), $info->i_pkts_ucast_out64()
Number of packets not sent to a multicast or broadcast address.
64 bit version may not exist on all devices.
(B<ifInUcastPkts>) (B<ifOutUcastPkts>)
(B<ifHCInUcastPkts>) (B<ifHCOutUcastPkts>)
=item $info->i_pkts_nucast_in(), $info->i_pkts_nucast_out(),
Number of packets sent to a multicast or broadcast address.
These methods are depricated by i_pkts_multi_in() and i_pkts_bcast_in()
according to IF-MIB. Actual device usage may vary.
(B<ifInNUcastPkts>) (B<ifOutNUcastPkts>)
=item $info->i_pkts_multi_in() $info->i_pkts_multi_out(),
$info->i_pkts_multi_in64(), $info->i_pkts_multi_out64()
Number of packets sent to a multicast address.
64 bit version may not exist on all devices.
(B<ifInMulticastPkts>) (B<ifOutMulticastPkts>)
(B<ifHCInMulticastPkts>) (B<ifHCOutMulticastPkts>)
=item $info->i_pkts_bcast_in() $info->i_pkts_bcast_out(),
$info->i_pkts_bcast_in64() $info->i_pkts_bcast_out64()
Number of packets sent to a broadcast address on an interface.
64 bit version may not exist on all devices.
(B<ifInBroadcastPkts>) (B<ifOutBroadcastPkts>)
(B<ifHCInBroadcastPkts>) (B<ifHCOutBroadcastPkts>)
=back
=head3 IP Address Table =head3 IP Address Table
Each entry in this table is an IP address in use on this device. Usually Each entry in this table is an IP address in use on this device. Usually
@@ -774,12 +927,33 @@ These are table entries, such as the IfIndex
'i_up' => 'ifOperStatus', 'i_up' => 'ifOperStatus',
'i_up_admin' => 'ifAdminStatus', 'i_up_admin' => 'ifAdminStatus',
'i_name' => 'ifName', 'i_name' => 'ifName',
'i_alias' => 'ifAlias', 'i_octet_in' => 'ifInOctets',
'i_octet_out' => 'ifOutOctets',
'i_errors_in' => 'ifInErrors',
'i_errors_out' => 'ifOutErrors',
'i_pkts_ucast_in' => 'ifInUcastPkts',
'i_pkts_ucast_out' => 'ifOutUcastPkts',
'i_pkts_nucast_in' => 'ifInNUcastPkts',
'i_pkts_nucast_out' => 'ifOutNUcastPkts',
# IP Address Table # IP Address Table
'ip_index' => 'ipAdEntIfIndex', 'ip_index' => 'ipAdEntIfIndex',
'ip_table' => 'ipAdEntAddr', 'ip_table' => 'ipAdEntAddr',
'ip_netmask' => 'ipAdEntNetMask', 'ip_netmask' => 'ipAdEntNetMask',
'ip_broadcast' => 'ipAdEntBcastAddr', 'ip_broadcast' => 'ipAdEntBcastAddr',
# ifXTable - Extension Table
'i_pkts_multi_in' => 'ifInMulticastPkts',
'i_pkts_multi_out' => 'ifOutMulticastPkts',
'i_pkts_bcast_in' => 'ifInBroadcastPkts',
'i_pkts_bcast_out' => 'ifOutBroadcastPkts',
'i_octet_in64' => 'ifHCInOctets',
'i_octet_out64' => 'ifHCOutOctets',
'i_pkts_ucast_in64' => 'ifHCInUcastPkts',
'i_pkts_ucast_out64' => 'ifHCOutUcastPkts',
'i_pkts_multi_in64' => 'ifHCInMulticastPkts',
'i_pkts_multi_out64' => 'ifHCOutMulticastPkts',
'i_pkts_bcast_in64' => 'ifHCInBroadcastPkts',
'i_pkts_bcast_out64' => 'ifHCOutBroadcastPkts',
'i_alias' => 'ifAlias'
); );
=item %MIBS =item %MIBS
@@ -808,7 +982,15 @@ it should return that same data in a more human friendly format.
'mac' => \&munge_mac, 'mac' => \&munge_mac,
'i_mac' => \&munge_mac, 'i_mac' => \&munge_mac,
'layers' => \&munge_dec2bin, 'layers' => \&munge_dec2bin,
'i_speed'=> \&munge_speed 'i_speed' => \&munge_speed,
'i_octet_in64' => \&munge_counter64,
'i_octet_out64' => \&munge_counter64,
'i_pkts_ucast_in64' => \&munge_counter64,
'i_pkts_ucast_out64' => \&munge_counter64,
'i_pkts_mutli_in64' => \&munge_counter64,
'i_pkts_multi_out64' => \&munge_counter64,
'i_pkts_bcast_in64' => \&munge_counter64,
'i_pkts_bcast_out64' => \&munge_counter64,
); );
=back =back
@@ -883,6 +1065,31 @@ Let's make a sample Layer 2 Device subclass :
Be sure and send the debugged version to snmp@warped.org to be Be sure and send the debugged version to snmp@warped.org to be
included in the next version of SNMP::Info. included in the next version of SNMP::Info.
=head2 Package Globals
These are variables that get set by methods, or arguments passed to new()
Avoid modifying them directly
=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.
=cut
$DEBUG = 0;
=item $BIGINT
Default 0. Set to true to have 64 bit counters return Math::BigInt objects instead of scalar
string values. See note under Interface Statistics about 64 bit values.
=cut
$BIGINT = 0;
=back
=head2 Data Munging Callback Subs =head2 Data Munging Callback Subs
=over =over
@@ -1003,6 +1210,21 @@ sub munge_bits {
return unpack("b*",$bits); return unpack("b*",$bits);
} }
=item munge_counter64
If $BIGINT is set to true, then a Math::BigInt object is returned.
See Math::BigInt for details.
=cut
sub munge_counter64 {
my $counter = shift;
return unless defined $counter;
return $counter unless $BIGINT;
my $bigint = Math::BigInt->new($counter);
return $bigint;
}
=back =back
=head2 Internaly Used Functions =head2 Internaly Used Functions
@@ -1062,6 +1284,16 @@ sub debug {
} }
=item $info->args()
Returns a reference to the argument hash supplied to SNMP::Session
=cut
sub args {
my $self = shift;
return $self->{_args};
}
=item $info->class() =item $info->class()
Returns the class name of the object. Returns the class name of the object.
@@ -1178,6 +1410,9 @@ sub _global{
$val = &$subref($val); $val = &$subref($val);
} }
# Save Cached Value
$self->{"__$attr"} = $val;
return $val; return $val;
} }
@@ -1422,6 +1657,9 @@ sub AUTOLOAD {
# First check %GLOBALS and return _scalar(global) # First check %GLOBALS and return _scalar(global)
if (defined $globals{$attr} ){ if (defined $globals{$attr} ){
# Return Cached Value if exists
return $self->{"__${attr}"} if defined $self->{"__${attr}"};
# Fetch New Value
return $self->_global( $attr ); return $self->_global( $attr );
} }