Top level changes to Info.pm

1 - Simplify AUTOLOAD()
2 - Add dynamically generated methods to symbol table to avoid AUTOLOAD on subsequent calls
3 - Override UNIVERSAL::can() to work with dynamic methods
4 - [3160037] - Support _raw suffix on methods to skip munging
5 - Add default bulkwalk_no()
This commit is contained in:
Eric A. Miller
2012-11-23 22:58:46 -05:00
parent ead15a1cde
commit b6cf462b16

840
Info.pm
View File

@@ -10,6 +10,7 @@
package SNMP::Info; package SNMP::Info;
use warnings;
use strict; use strict;
use Exporter; use Exporter;
use SNMP; use SNMP;
@@ -989,7 +990,7 @@ sub new {
# load references to all the subclass data structures # load references to all the subclass data structures
{ {
no strict 'refs'; ## no critic no strict 'refs'; ## no critic (ProhibitNoStrict ProhibitProlongedStrictureOverride)
$new_obj->{init} = \${ $class . '::INIT' }; $new_obj->{init} = \${ $class . '::INIT' };
$new_obj->{mibs} = \%{ $class . '::MIBS' }; $new_obj->{mibs} = \%{ $class . '::MIBS' };
$new_obj->{globals} = \%{ $class . '::GLOBALS' }; $new_obj->{globals} = \%{ $class . '::GLOBALS' };
@@ -1857,6 +1858,17 @@ sub if_ignore {
return \%nothing; return \%nothing;
} }
=item $info->bulkwalk_no()
Returns 0. Is an overridable method used for turn off bulkwalk for the
device class.
=cut
sub bulkwalk_no {
return 0;
}
=item $info->i_index() =item $info->i_index()
Default SNMP IID to Interface index. Default SNMP IID to Interface index.
@@ -3139,96 +3151,88 @@ sub store {
=item $info->_global() =item $info->_global()
Used internally by AUTOLOAD to load dynamic methods from %GLOBALS. Used internally by AUTOLOAD to create dynamic methods from %GLOBALS.
Example: $info->name() calls autoload which calls $info->_global('name'). Example: $info->name() on the first call dispatches to AUTOLOAD() which
calls $info->_global('name') creating the method name().
=cut =cut
sub _global { sub _global {
my $self = shift; my $method = shift;
my $attr = shift; my $oid = shift;
my $sess = $self->session();
return unless defined $sess;
my $globals = $self->globals(); return sub {
my $self = shift;
my $oid; my $sess = $self->session();
if ( exists $globals->{$attr} ) { return unless defined $sess;
$oid = $globals->{$attr};
unless ( $oid =~ /\.\d+$/ ) { my $load = $method =~ /^load/;
$oid .= ".0"; my $raw = $method =~ /raw$/;
my $attr = $method;
$attr =~ s/^load_//;
$attr =~ s/_raw$//;
# Get the callback hash for data munging
my $munge = $self->munge();
# Return cached data unless loading
# We now store in raw format so munge before returning
# unless expecting raw data
if ( defined $self->{"_$attr"} && !$load ) {
if ( defined $munge->{$attr} && !$raw ) {
my $val = $self->{"_$attr"};
my $subref = $munge->{$attr};
return &$subref($val);
} else{
return $self->{"_$attr"};
}
} }
# Check for fully qualified attr print "SNMP::Info::_global $method : $oid\n" if $self->debug();
if ( $oid =~ /__/ ) { my $val = $sess->get($oid);
$oid =~ s/__/::/;
$oid =~ s/_/-/g;
# Need to translate fully qualified attr to full oid # Mark as gotten. Even if it fails below, we don't want to keep failing.
$oid = &SNMP::translateObj($oid); $self->{"_$attr"} = undef;
unless ( defined $oid ) {
$self->error_throw( if ( $sess->{ErrorStr} ) {
"SNMP::Info::_load_attr: Can't translate $globals->{$attr}. Missing MIB?\n" $self->error_throw(
); "SNMP::Info::_global($method) $sess->{ErrorStr}");
return; return;
}
} }
if ( defined $val and $val eq 'NOSUCHOBJECT' ) {
$self->error_throw("SNMP::Info::_global($method) NOSUCHOBJECT");
return;
}
if ( defined $val and $val eq 'NOSUCHINSTANCE' ) {
$self->error_throw("SNMP::Info::_global($method) NOSUCHINSTANCE");
return;
}
# Save Cached Value
$self->{"_$attr"} = $val;
# Data Munging
if ( defined $munge->{$attr} && !$raw ) {
my $subref = $munge->{$attr};
$val = &$subref($val);
}
return $val;
} }
else {
$oid = $attr;
}
# Tag on .0 unless the leaf ends in .number
unless ( $oid =~ /\.\d+$/ ) {
$oid .= ".0";
}
print "SNMP::Info::_global $attr : $oid\n" if $self->debug();
my $val = $sess->get($oid);
# mark as gotten. Even if it fails below, we don't want to keep failing.
$self->{"_$attr"} = undef;
if ( $sess->{ErrorStr} ) {
$self->error_throw("SNMP::Info::_global($attr) $sess->{ErrorStr}");
return;
}
if ( defined $val and $val eq 'NOSUCHOBJECT' ) {
$self->error_throw("SNMP::Info::_global($attr) NOSUCHOBJECT");
return;
}
if ( defined $val and $val eq 'NOSUCHINSTANCE' ) {
$self->error_throw("SNMP::Info::_global($attr) NOSUCHINSTANCE");
return;
}
# Get the callback hash for data munging
my $munge = $self->munge();
# Data Munging
if ( defined $munge->{$attr} ) {
my $subref = $munge->{$attr};
$val = &$subref($val);
}
# Save Cached Value
$self->{"_$attr"} = $val;
return $val;
} }
=item $info->_set(attr,val,iid,type) =item $info->_set(attr,val,iid,type)
Used internally by AUTOLOAD to run an SNMP set command for dynamic methods Used internally by set_multi() to run an SNMP set command. When run
listed in either %GLOBALS or %FUNCS or a valid mib leaf from a loaded MIB or
the set_multi() method to set multiple variable in one command. When run
clears attr cache. clears attr cache.
Attr is passed as either a scalar for dynamic methods or a reference to an Attr can be passed as either a scalar or a reference to an array or array
array or array of arrays when used with set_multi(). of arrays when used with set_multi().
Example: $info->set_name('dog',3) uses autoload to resolve to Example: $info->set_name('dog',3) uses autoload to resolve to
$info->_set('name','dog',3); $info->_set('name','dog',3);
@@ -3316,6 +3320,50 @@ sub _set {
return $rv; return $rv;
} }
=item $info->_make_setter(val,iid)
Used internally by AUTOLOAD to create dynamic methods from either %GLOBALS,
%FUNCS, or a valid mib leaf from a loaded MIB which runs an SNMP set command.
When run clears the attribute cache.
Example: $info->set_name('dog',3) dispatches to autoload to resolve to
$info->_set('name','dog',3) and _make_setter creates the set_name() method.
=cut
sub _make_setter {
my $method = shift;
my $oid = shift;
return sub {
my $self = shift;
my $val = shift;
my $iid = shift;
$iid = defined $iid ? $iid : '.0';
# prepend dot if necessary to $iid
$iid = ".$iid" unless $iid =~ /^\./;
my $sess = $self->session();
return unless defined $sess;
$oid .= $iid;
$self->debug()
and print "SNMP::Info::_set $method$iid ($oid) = $val\n";
delete $self->{"_$method"};
my $rv = $sess->set( $oid, $val );
if ( $sess->{ErrorStr} ) {
$self->error_throw("SNMP::Info::_set $sess->{ErrorStr}");
return;
}
return $rv;
}
}
=item $info->set_multi(arrayref) =item $info->set_multi(arrayref)
Used to run an SNMP set command on several new values in the one request. Used to run an SNMP set command on several new values in the one request.
@@ -3409,212 +3457,209 @@ sub all {
=item $info->_load_attr() =item $info->_load_attr()
Used internally by AUTOLOAD to fetch data called from methods listed in %FUNCS Used internally by AUTOLOAD to create dynamic methods from %FUNCS
or a MIB Leaf node name. or a MIB Leaf node name which fetches data.
Supports partial table fetches and single instance table fetches. Supports partial table fetches and single instance table fetches.
See L<SNMP::Info/"Partial Table Fetches">. See L<SNMP::Info/"Partial Table Fetches">.
Called from $info->load_METHOD();
=cut =cut
sub _load_attr { sub _load_attr {
my $self = shift; my $method = shift;
my ( $attr, $leaf, $partial ) = @_; my $oid = shift;
my $ver = $self->snmp_ver(); return sub {
my $nosuch = $self->nosuch(); my $self = shift;
my $sess = $self->session(); my $partial = shift;
my $store = $self->store();
my $munge = $self->munge();
return unless defined $sess;
my $varleaf = $leaf; my $sess = $self->session();
return unless defined $sess;
# Check for fully qualified attr my $ver = $self->snmp_ver();
if ( $leaf =~ /__/ ) { my $nosuch = $self->nosuch();
$leaf =~ s/__/::/; my $store = $self->store();
$leaf =~ s/_/-/g; my $munge = $self->munge();
$varleaf = $leaf;
}
# Deal with partial entries. my $load = $method =~ /^load/;
if ( defined $partial ) { my $raw = $method =~ /raw$/;
# If we aren't supplied an OID translate my $attr = $method;
if ( $leaf !~ /^[.\d]*$/ ) { $attr =~ s/^load_//;
$attr =~ s/_raw$//;
# VarBind will not resolve mixed OID and leaf entries like # Return cached data unless loading or partial
# "ipRouteMask.255.255". So we convert to full OID # We now store in raw format so munge before returning
my $oid = &SNMP::translateObj($leaf); # unless expecting raw data
unless ( defined $oid ) { return $self->_show_attr($attr, $raw)
if ( defined $self->{"_${attr}"}
&& !$load
&& !defined $partial );
my $leaf_name = SNMP::translateObj($oid) || '';
my $varleaf = defined $partial ? "$oid.$partial" : "$oid";
$self->debug()
and print "SNMP::Info::_load_attr $method : $oid",
defined $partial ? "($partial)" : '', "\n";
my $var = new SNMP::Varbind( [$varleaf] );
# 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 $localstore = undef;
my $errornum = 0;
my %seen = ();
my $vars = [];
my $bulkwalk_no
= $self->can('bulkwalk_no') ? $self->bulkwalk_no() : 0;
my $bulkwalk_on = defined $self->{BulkWalk} ? $self->{BulkWalk} : 1;
my $can_bulkwalk = $bulkwalk_on && !$bulkwalk_no;
my $repeaters = $self->{BulkRepeaters} || $REPEATERS;
my $bulkwalk = $can_bulkwalk && $ver != 1;
my $loopdetect
= defined $self->{LoopDetect} ? $self->{LoopDetect} : 1;
if ( defined $partial ) {
# Try a GET, in case the partial is a leaf OID.
# Would like to only do this if we know the OID is
# long enough; implementing that would require a
# lot of MIB mucking.
my $try = $sess->get($var);
$errornum = $sess->{ErrorNum};
if ( defined($try) && $errornum == 0 && $try !~ /^NOSUCH/ ) {
$var->[2] = $try;
$vars = [$var];
$bulkwalk = 1; # fake a bulkwalk return
}
# We want to execute the while loop below for the getnext request.
if ( $ver == 1
and $sess->{ErrorNum}
and $sess->{ErrorStr} =~ /nosuch/i )
{
$errornum = 0;
}
}
# Use BULKWALK if we can because its faster
if ( $bulkwalk && @$vars == 0 ) {
($vars) = $sess->bulkwalk( 0, $repeaters, $var );
if ( $sess->{ErrorNum} ) {
$self->error_throw( $self->error_throw(
"SNMP::Info::_load_attr: Can't translate $leaf.$partial. Missing MIB?\n" "SNMP::Info::_load_atrr: BULKWALK " . $sess->{ErrorStr},
); "\n" );
return; return;
} }
$varleaf = "$oid.$partial";
}
else {
$varleaf = "$leaf.$partial";
}
}
$self->debug()
and print "SNMP::Info::_load_attr $attr : $leaf",
defined $partial ? "($partial / $varleaf)" : '', "\n";
my $var = new SNMP::Varbind( [$varleaf] );
# 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 $localstore = undef;
my $errornum = 0;
my %seen = ();
my $vars = [];
my $bulkwalk_no = $self->can('bulkwalk_no') ? $self->bulkwalk_no() : 0;
my $bulkwalk_on = defined $self->{BulkWalk} ? $self->{BulkWalk} : 1;
my $can_bulkwalk = $bulkwalk_on && !$bulkwalk_no;
my $repeaters = $self->{BulkRepeaters} || $REPEATERS;
my $bulkwalk = $can_bulkwalk && $ver != 1;
my $loopdetect = defined $self->{LoopDetect} ? $self->{LoopDetect} : 1;
if ( defined $partial ) {
# Try a GET, in case the partial is a leaf OID.
# Would like to only do this if we know the OID is
# long enough; implementing that would require a
# lot of MIB mucking.
my $try = $sess->get($var);
$errornum = $sess->{ErrorNum};
if ( defined($try) && $errornum == 0 && $try !~ /^NOSUCH/ ) {
$var->[2] = $try;
$vars = [$var];
$bulkwalk = 1; # fake a bulkwalk return
} }
# We want to execute the while loop below for the getnext request. while ( !$errornum ) {
if ( $ver == 1 if ($bulkwalk) {
and $sess->{ErrorNum} $var = shift @$vars or last;
and $sess->{ErrorStr} =~ /nosuch/i )
{
$errornum = 0;
}
}
# Use BULKWALK if we can because its faster
if ( $bulkwalk && @$vars == 0 ) {
($vars) = $sess->bulkwalk( 0, $repeaters, $var );
if ( $sess->{ErrorNum} ) {
$self->error_throw(
"SNMP::Info::_load_atrr: BULKWALK " . $sess->{ErrorStr},
"\n" );
return;
}
}
while ( !$errornum ) {
if ($bulkwalk) {
$var = shift @$vars or last;
}
else {
# GETNEXT instead of BULKWALK
$sess->getnext($var);
$errornum = $sess->{ErrorNum};
}
if ( $self->debug() > 1 ) {
use Data::Dumper;
print "SNMP::Info::_load_attr $attr : leaf = $leaf , var = ",
Dumper($var);
}
# Check if we've left the requested subtree
last if $var->[0] ne $leaf;
my $iid = $var->[1];
my $val = $var->[2];
unless ( defined $iid ) {
$self->error_throw("SNMP::Info::_load_attr: $attr not here");
next;
}
# Check to make sure we are still in partial land
if ( defined $partial
and $iid !~ /^$partial$/
and $iid !~ /^$partial\./ )
{
$self->debug() and print "$iid makes us leave partial land.\n";
last;
}
# Check if last element, V2 devices may report ENDOFMIBVIEW even if
# instance or object doesn't exist.
if ( $val eq 'ENDOFMIBVIEW' ) {
last;
}
# Similarly for SNMPv1 - noSuchName return results in both $iid
# and $val being empty strings.
if ( $val eq '' and $iid eq '' ) {
last;
}
# Another check for SNMPv1 - noSuchName return may results in an $iid
# we've already seen and $val an empty string. If we don't catch
# this here we erronously report a loop below.
if ( defined $seen{$iid} and $seen{$iid} and $val eq '' ) {
last;
}
if ($loopdetect) {
# Check to see if we've already seen this IID (looping)
if ( defined $seen{$iid} and $seen{$iid} ) {
$self->error_throw("Looping on: $attr iid:$iid. ");
last;
} }
else { else {
$seen{$iid}++;
# GETNEXT instead of BULKWALK
$sess->getnext($var);
$errornum = $sess->{ErrorNum};
} }
if ( $self->debug() > 1 ) {
use Data::Dumper;
print "SNMP::Info::_load_attr $method : leaf = $oid , var = ",
Dumper($var);
}
# Check if we've left the requested subtree
last if $var->[0] ne $leaf_name;
my $iid = $var->[1];
my $val = $var->[2];
unless ( defined $iid ) {
$self->error_throw(
"SNMP::Info::_load_attr: $method not here");
next;
}
# Check to make sure we are still in partial land
if ( defined $partial
and $iid !~ /^$partial$/
and $iid !~ /^$partial\./ )
{
$self->debug()
and print "$iid makes us leave partial land.\n";
last;
}
# Check if last element, V2 devices may report ENDOFMIBVIEW even if
# instance or object doesn't exist.
if ( $val eq 'ENDOFMIBVIEW' ) {
last;
}
# Similarly for SNMPv1 - noSuchName return results in both $iid
# and $val being empty strings.
if ( $val eq '' and $iid eq '' ) {
last;
}
# Another check for SNMPv1 - noSuchName return may results in an $iid
# we've already seen and $val an empty string. If we don't catch
# this here we erronously report a loop below.
if ( defined $seen{$iid} and $seen{$iid} and $val eq '' ) {
last;
}
if ($loopdetect) {
# Check to see if we've already seen this IID (looping)
if ( defined $seen{$iid} and $seen{$iid} ) {
$self->error_throw("Looping on: $method iid:$iid. ");
last;
}
else {
$seen{$iid}++;
}
}
if ( $val eq 'NOSUCHOBJECT' ) {
$self->error_throw(
"SNMP::Info::_load_attr: $method : NOSUCHOBJECT");
next;
}
if ( $val eq 'NOSUCHINSTANCE' ) {
$self->error_throw(
"SNMP::Info::_load_attr: $method : NOSUCHINSTANCE");
next;
}
$localstore->{$iid} = $val;
} }
if ( $val eq 'NOSUCHOBJECT' ) { # Cache data if we are not getting partial data:
$self->error_throw( if ( !defined $partial ) {
"SNMP::Info::_load_attr: $attr : NOSUCHOBJECT"); $self->{"_${attr}"}++;
next; $store->{$attr} = $localstore;
}
if ( $val eq 'NOSUCHINSTANCE' ) {
$self->error_throw(
"SNMP::Info::_load_attr: $attr : NOSUCHINSTANCE");
next;
} }
# Data Munging # Data Munging
# Checks for an entry in %munge and runs the subroutine # Checks for an entry in %munge and munges values unless we expect
if ( defined $munge->{$attr} ) { # raw data
if ( defined $munge->{$attr} && !$raw ) {
my $subref = $munge->{$attr}; my $subref = $munge->{$attr};
$val = &$subref($val); my %munged = map { $_ => &$subref( $localstore->{$_} ) } keys %$localstore;
return \%munged;
} }
return $localstore;
$localstore->{$iid} = $val;
} }
# Cache data if we are not getting partial data:
if ( !defined $partial ) {
$self->{"_${attr}"}++;
$store->{$attr} = $localstore;
}
return $localstore;
} }
=item $info->_show_attr() =item $info->_show_attr()
@@ -3631,10 +3676,21 @@ Every time after it will return cached data.
sub _show_attr { sub _show_attr {
my $self = shift; my $self = shift;
my $attr = shift; my $attr = shift;
my $raw = shift;
my $store = $self->store(); my $store = $self->store();
return $store->{$attr}; # Get the callback hash for data munging
my $munge = $self->munge();
if ( defined $munge->{$attr} && !$raw ) {
my $subref = $munge->{$attr};
my %munged = map { $_ => &$subref( $store->{$attr}{$_} ) } keys $store->{$attr};
return \%munged;
}
else {
return $store->{$attr};
}
} }
=item $info->snmp_connect_ip(ip) =item $info->snmp_connect_ip(ip)
@@ -3704,43 +3760,168 @@ sub modify_port_list {
return pack( "B*", join( '', @$portlist ) ); return pack( "B*", join( '', @$portlist ) );
} }
=back =item _validate_autoload_method(method)
=head2 AUTOLOAD Used internally by AUTOLOAD to validate that a dynamic method should be
created. Returns the OID of the MIB leaf node the method will get or set.
Each entry in either %FUNCS, %GLOBALS, or MIB Leaf node names present in
loaded MIBs are used by AUTOLOAD() to create dynamic methods.
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.
=over =over
=item 1. Returns unless method is listed in %FUNCS, %GLOBALS, or is MIB Leaf =item 1. Returns unless method is listed in %FUNCS, %GLOBALS, or is MIB Leaf
node name in a loaded MIB for given class. node name in a loaded MIB for given class.
=item 2. Checks for load_ prefix and if present runs $info->_global(method) =item 2. Translates the MIB Leaf node name to an OID.
for methods which exist in %GLOBALS or are a single instance MIB Leaf node
name, otherwise runs $info->_load_attr(method) for methods which exist in
%FUNCS or are MIB Leaf node name contained within a table. This always
forces reloading and does not use cached data.
=item 3. Check for set_ prefix and if present runs $info->_set(method). =item 3. Checks to see if the method access type is allowed for the resolved
OID. Write access for set_ methods, read access for others.
=item 4. If the method exists in %GLOBALS or is a single instance MIB Leaf
node name it runs $info->_global(method) unless already cached.
=item 5. If the method exists in %FUNCS or is MIB Leaf node name contained
within a table it runs $info->_load_attr(method) if not cached.
=item 6. Otherwise return $info->_show_attr(method).
=back =back
Override any dynamic method listed in one of these hashes by creating a =cut
subroutine with the same name.
sub _validate_autoload_method {
my $self = shift;
my $method = shift;
my $attr = $method;
$attr =~ s/^(load|set|orig)_//;
$attr =~ s/_raw$//;
my $globals = $self->globals();
my $funcs = $self->funcs();
my $leaf_name = $globals->{$attr} || $funcs->{$attr} || $attr;
# Check for fully qualified name
if ( $leaf_name =~ /__/ ) {
$leaf_name =~ s/__/::/;
$leaf_name =~ s/_/-/g;
}
if ($leaf_name && $globals->{$attr}) {
# Tag on .0 unless the leaf ends in a digit
unless ( $leaf_name =~ /\d$/ ) {
$leaf_name .= ".0";
}
}
# Translate MIB leaf node name to OID
my $oid = SNMP::translateObj($leaf_name);
if ( $leaf_name =~ /^[.]?\d[\.\d]+$/ ) {
$oid = $leaf_name;
}
unless ( defined $oid ) {
print
"SNMP::Info::_validate_autoload_method($leaf_name) Unable to resolve method.\n"
if $self->debug();
return;
}
# Validate that we have proper access for the operation
my $access = $SNMP::MIB{$oid}{'access'} || '';
# If we were given a fully qualified OID because we don't have the MIB
# file, it will translate above but we won't be able to check access so
# skip the check and return
if ($access) {
if ( $method =~ /^set/ && $access =~ /Write|Create/ ) {
return $oid;
}
elsif ( $access =~ /Read|Create/ ) {
return $oid;
}
else {
print
"SNMP::Info::_validate_autoload_method($attr : $oid) Not accessable for requested operation.\n"
if $self->debug();
return;
}
}
return $oid;
}
=item $info->can()
Overrides UNIVERSAL::can() so that objects will correctly report thier
capabilities to include dynamic methods generated at runtime via AUTOLOAD.
Calls parent can() first to see if method exists, if not validates that a
method should be created then dispatches to the appropriate internal method
for creation. The newly created method is inserted into the symbol table
returning to AUTOLOAD only for the inital method call.
Returns undef if the method does not exist and can not be created.
=cut
sub can {
my $self = shift;
my $method = shift;
my $super = shift;
# use results of parent can()
my $meth_ref = $self->SUPER::can($method);
# Don't return if passed $super as it means we were called
# from AUTOLOAD for a method that hasn't been generated yet.
return $meth_ref if ( !$super && $meth_ref );
my $oid = $self->_validate_autoload_method($method);
return unless $oid;
# _validate_autoload_method validates, so we need to check for
# set_ , globals, and everything else goes to _load_attr
my $globals = $self->globals();
# We need to resolve globals with a prefix or suffix
my $g_method = $method;
$g_method =~ s/^(load|orig)_//;
$g_method =~ s/_raw$//;
no strict 'refs'; ## no critic (ProhibitNoStrict )
# Check for set_ ing.
if ( $method =~ /^set_/ ) {
return *{$method} = _make_setter( $method, $oid, @_ );
}
elsif ( defined $globals->{$g_method} ) {
return *{$method} = _global( $method, $oid );
}
else {
return *{$method} = _load_attr( $method, $oid, @_ );
}
}
=back
=head2 AUTOLOAD
Each entry in either %FUNCS, %GLOBALS, or MIB Leaf node names present in
loaded MIBs are used by AUTOLOAD() to create dynamic methods. Generated
methods are inserted into the symbol table so that subsequent calls can avoid
AUTOLOAD() and dispatch directly.
=over
=item 1. Returns unless method is listed in %FUNCS, %GLOBALS, or is MIB Leaf
node name in a loaded MIB for given class.
=item 2. If the method exists in %GLOBALS, _global() generates the method.
=item 3. If a set_ prefix is present _make_setter() generates the method.
=item 4. If the method exists in %FUNCS or is MIB Leaf node name in a
loaded MIB, _load_attr() generates the method.
=item 5. A load_ prefix forces reloading of data and does not use cached data.
=item 6. A _raw suffix returns data ignoring any munge routines.
=back
Override any dynamic method listed in %GLOBALS, %FUNCS, or MIB Leaf node
name a by creating a subroutine with the same name.
For example to override $info->name() create `` sub name {...}'' in your For example to override $info->name() create `` sub name {...}'' in your
subclass. subclass.
@@ -3748,20 +3929,11 @@ subclass.
=cut =cut
sub AUTOLOAD { sub AUTOLOAD {
my $self = shift; my $self = shift;
my $sub_name = $AUTOLOAD; my ($super, $sub_name) = $AUTOLOAD =~ /(SUPER)?::(\w+)$/;
return if $sub_name =~ /DESTROY$/; return if $sub_name =~ /DESTROY$/;
# package is the first part
( my $package = $sub_name ) =~ s/[^:]*$//;
# Sub name is the last part
$sub_name =~ s/.*://;
# Enable calls to SUPER class to find autoloaded methods
$package =~ s/SUPER::$//;
# Typos in function calls in SNMP::Info subclasses turn into # Typos in function calls in SNMP::Info subclasses turn into
# AUTOLOAD requests for non-methods. While this is deprecated, # AUTOLOAD requests for non-methods. While this is deprecated,
# we'll still get called, so report a less confusing error. # we'll still get called, so report a less confusing error.
@@ -3775,97 +3947,9 @@ sub AUTOLOAD {
); );
} }
my $attr = $sub_name; return unless my $meth_ref = $self->can($sub_name, $super, @_);
$attr =~ s/^(load|set)_//; return $self->$meth_ref;
$attr =~ s/^orig_//;
# Let's use the %GLOBALS and %FUNCS from the class that
# inherited us.
my ( %funcs, %globals );
{
no strict 'refs'; ## no critic
%funcs = %{ $package . 'FUNCS' };
%globals = %{ $package . 'GLOBALS' };
}
# Check if we were called with a MIB leaf node name
my $trans = SNMP::translateObj($attr);
my $mib_leaf = 0;
my $table_leaf = 0;
if ( defined($trans) ) {
my $mib = $SNMP::MIB{$trans};
# We're not a leaf if we don't have access attribute
# Don't bother if not-accessable
my $access = $$mib{'access'};
$mib_leaf = 1 if ( defined $access && $access !~ /NoAccess/ );
if ( $self->debug() and !$mib_leaf ) {
print "SNMP::Info::AUTOLOAD($attr) Leaf not accessable.\n";
}
# If we're a leaf check to see if we are in a table
if ($mib_leaf) {
my $indexes = $$mib{'parent'}{'indexes'};
$table_leaf = 1
if ( defined $indexes && scalar( @{$indexes} ) > 0 );
}
}
unless ( defined $funcs{$attr}
or defined $globals{$attr}
or defined $mib_leaf )
{
$self->error_throw(
"SNMP::Info::AUTOLOAD($attr) Attribute not found in this device class."
);
return;
}
# Check for load_ ing.
if ( $sub_name =~ /^load_/ ) {
if ( defined $globals{$attr} ) {
return $self->_global($attr);
}
if ( defined $funcs{$attr} ) {
return $self->_load_attr( $attr, $funcs{$attr}, @_ );
}
if ( $mib_leaf and !$table_leaf ) {
return $self->_global($attr);
}
if ($table_leaf) {
return $self->_load_attr( $attr, $attr, @_ );
}
}
# Check for set_ ing.
if ( $sub_name =~ /^set_/ ) {
return $self->_set( $attr, @_ );
}
# Next check for entry in %GLOBALS
if ( defined $globals{$attr} or ( $mib_leaf and !$table_leaf ) ) {
# Return Cached Value if exists
return $self->{"_${attr}"} if exists $self->{"_${attr}"};
# Fetch New Value
return $self->_global($attr);
}
# Otherwise we must be listed in %FUNCS
# Load data if it both not cached and we are not requesting partial info.
if ( defined $funcs{$attr} ) {
return $self->_load_attr( $attr, $funcs{$attr}, @_ )
unless ( defined $self->{"_${attr}"} and !defined $_[0] );
}
if ($table_leaf) {
return $self->_load_attr( $attr, $attr, @_ )
unless ( defined $self->{"_${attr}"} and !defined $_[0] );
}
return $self->_show_attr($attr);
} }
1; 1;