migrate from EUMM to Module::Build

This commit is contained in:
Oliver Gorwits
2017-06-28 22:40:15 +01:00
parent 433b87b9df
commit 44b19153f1
132 changed files with 52 additions and 2239 deletions

View File

@@ -1,35 +0,0 @@
#!/usr/bin/perl -i.bak
#
# [Re-]write POD to create cross-links between Required MIBs, GLOBALS
# and TABLE METHODS sections.
# This is a horrible jumble of heuristics, but works with
# all of the existing files. It has only one false positive: HP.pm's
# mention of an SNMP::Info version number.
#
$section = undef;
$waiting = 0;
while (<>) {
if (eof) {
$section = undef;
$waiting = 0;
}
if (/^=head(\d)/) {
$sl = $1;
if ($sl <= $level) {
$section = undef;
}
if (/(TABLE METHODS|GLOBALS|Required MIBs)/) {
$section = $1;
$level = $sl;
if ($section eq 'TABLE METHODS' || $section eq 'GLOBALS') {
$waiting = 1;
}
}
if ($waiting && /imported/i) {
$waiting = 0;
}
} elsif (defined($section) && !$waiting && /^[^=]/ && /SNMP::Info/) {
s,(?:L<)?(SNMP::Info[a-zA-Z0-9:]*)(?:/[^>]+)?(?:>)?,L<$1/"$section">,g;
}
print;
}

View File

@@ -1,338 +0,0 @@
#!/usr/bin/perl -w
# $Id$
$DevMatrix = '../DeviceMatrix.txt';
$DevHTML = 'DeviceMatrix.html';
$DevPNG = 'DeviceMatrix.png';
$Attributes= {};
# Parse Data File
$matrix = parse_data($DevMatrix);
# Graph it for fun
eval "use GraphViz::Data::Structure;";
if ($@) {
print "GraphViz::Data::Structure not installed. $@\n";
} else {
my %graph = ();
foreach my $vendor (sort sort_nocase keys %$matrix){
$graph{$vendor} = {};
foreach my $family (sort sort_nocase keys %{$matrix->{$vendor}->{families}} ){
my @models;
foreach my $mod (keys %{$matrix->{$vendor}->{families}->{$family}->{models}}){
push(@models,split(/\s*,\s*/,$mod));
}
if (scalar @models){
$graph{$vendor}->{$family}=\@models;
} else {
$graph{$vendor}->{$family}=[];
}
}
}
my $now = scalar localtime;
my $gvds = GraphViz::Data::Structure->new(\%graph,Orientation=>'vertical',
Colors=> 'Deep',
graph => {label=>"SNMP::Info and Netdisco Supported Devices \n $now",'fontpath'=>'/usr/local/netdisco','fontname'=>'lucon',concentrate=>'true','overlap'=>'false',spline=>'true',bgcolor=>'wheat'},
node => {fontname=>'lucon'},
);
$gvds->graph()->as_png($DevPNG);
}
print "Creating $DevHTML\n";
open (HTML, "> $DevHTML") or die "Can't open $DevHTML. $!\n";
$old_fh = select(HTML);
&html_head;
print_vendors($matrix);
foreach my $vendor (sort sort_nocase keys %$matrix){
print "<A NAME=\"$vendor\"><SPAN CLASS=\"vendor\"><B>$vendor</B></SPAN></A>\n";
print "<DL>\n";
my $vendor_defaults = $matrix->{$vendor}->{defaults};
print_notes($vendor_defaults,1);
my $families = $matrix->{$vendor}->{families};
foreach my $family (sort sort_nocase keys %$families ) {
print "<DT>$family Family\n";
my $family_defaults = $families->{$family}->{defaults};
print_notes($family_defaults,2);
my $models = $families->{$family}->{models};
foreach my $model (sort sort_nocase keys %$models ){
my $model_defaults = $models->{$model}->{defaults};
print "<DD>$model\n";
print "<DL>\n";
print_notes($model_defaults,3);
print "<DT><DD><TABLE BORDER=1>\n";
print_headers();
print "<TR>\n";
foreach my $a (sort sort_nocase keys %$Attributes) {
my $val;
next if $a eq 'note';
$val = ['-'];
$class = 'none';
if (defined $model_defaults->{$a}) {
$val = $model_defaults->{$a};
$class = 'model';
} elsif (defined $family_defaults->{$a}){
$val = $family_defaults->{$a};
$class = 'family';
} elsif (defined $vendor_defaults->{$a}){
$val = $vendor_defaults->{$a};
$class = 'vendor';
}
print " <TD CLASS='$class'>",join("<BR>\n",@$val),"</TD>\n";
}
print "</TR></TABLE>\n";
print "</DL>\n";
}
}
print "</DL>\n";
}
&html_tail;
select ($old_fh);
close (HTML) or die "Can't write $DevHTML. $!\n";
# Data Structures
# Matrix =
# ( vendor => { families => { family => family_hash },
# defaults => { cmd => [values] },
# }
# )
# Family Hash
# ( models => { model => model_hash },
# defaults => { cmd => [values] }
# )
# Model Hash
# ( defaults => { cmd => [values] } )
sub parse_data {
my $file = shift;
my %ignore = map { $_ => 1 } @_;
my $Matrix;
my @Lines;
open (DM, "< $file") or die "Can't open $file. $!\n";
{
@Lines = <DM>;
}
close (DM);
my ($device,$family,$vendor,$class);
foreach my $line (@Lines){
chomp($line);
# Comments
$line =~ s/#.*//;
# Blank Lines
next if $line =~ /^\s*$/;
# Trim whitespace
$line =~ s/^\s+//;
$line =~ s/\s+$//;
my ($cmd,$value);
if ($line =~ /^([a-z-_]+)\s*:\s*(.*)$/) {
$cmd = $1; $value = $2;
} else {
print "What do i do with this line : $line \n";
next;
}
if (exists $ignore{$cmd}){
print "Ignoring $cmd\n";
}
# Set Class {vendor,family,device}
if ($cmd eq 'device-vendor'){
$vendor = $value;
$family = $model = undef;
$Matrix->{$vendor} = {} unless defined $Matrix->{$vendor};
$class = $Matrix->{$vendor};
$class->{defaults}->{type}='vendor';
next;
}
if ($cmd eq 'device-family'){
$family = $value;
$model = undef;
print "$family has no vendor.\n" unless defined $vendor;
$Matrix->{$vendor}->{families}->{$family} = {}
unless defined $Matrix->{$vendor}->{families}->{$family};
$class = $Matrix->{$vendor}->{families}->{$family};
$class->{defaults}->{type}='family';
next;
}
if ($cmd eq 'device') {
$model = $value;
print "$model has no family.\n" unless defined $family;
print "$model has no vendor.\n" unless defined $vendor;
$Matrix->{$vendor}->{families}->{$family}->{models}->{$model} = {}
unless defined $Matrix->{$vendor}->{families}->{$family}->{models}->{$model};
$class = $Matrix->{$vendor}->{families}->{$family}->{models}->{$model};
$class->{defaults}->{type}='device';
next;
}
# Store attribute
push (@{$class->{defaults}->{$cmd}} , $value);
$Attributes->{$cmd}++;
}
return $Matrix;
}
sub sort_nocase {
return lc($a) cmp lc($b);
}
sub print_notes {
my $defaults = shift;
my $level = shift;
my $notes = $defaults->{note} || [];
foreach my $note (@$notes){
if ($note =~ s/^!//){
$note = '<SPAN CLASS="note">' . $note . '</SPAN>';
}
}
if (scalar @$notes){
print "<DT>\n";
my $print_note = join("\n<LI>",@$notes);
print "<UL TYPE='square'><LI>$print_note</UL>\n";
}
}
sub print_vendors {
my $matrix=shift;
print "<h1>Device Vendors</h1>\n";
foreach my $vendor (sort sort_nocase keys %$matrix){
print "[<A HREF=\"#$vendor\">$vendor</A>]\n";
}
print "<HR>\n";
}
sub html_head {
print <<"end_head";
<HTML>
<HEAD>
<TITLE>SNMP::Info - Device Compatibility Matrix</TITLE>
<STYLE TYPE="text/css" MEDIA="screen">
<!--
BODY { font-family:arial,helvetica,sans-serif; font-size:12pt; }
TD { font-family:arial,helvetica,sans-serif; font-size:10pt; }
TH { font-family:arial,helvetica,sans-serif; font-size:10pt; background:#F0F0F0; }
H1 { font-family:arial,helvetica,sans-serif; font-size:14pt; }
.vendor { font-size:12pt; color:#777777; }
.family { font-size:12pt; color:blue; }
.model { font-size:12pt; color:red; }
.note { color:red; }
//-->
</STYLE>
</HEAD>
<BODY>
<h1>SNMP::Info - Device Compatibility Matrix</h1>
<P>
end_head
}
sub html_tail {
print <<'end_tail';
<HR>
<h1>Color Key</h1>
[<SPAN CLASS="model">Model Attribute</SPAN>]
[<SPAN CLASS="family">Family Attribute</SPAN>]
[<SPAN CLASS="vendor">Vendor Attribute</SPAN>]
<h1>Attribute Key</h1>
A value of <B>-</B> signifies the information is not specified and can not
be assumed working.
<TABLE BORDER=1>
<TR>
<TD>Arpnip</TD>
<TD>Ability to collect ARP tables for MAC to IP translation.</TD>
</TR>
<TR>
<TD>Class</TD>
<TD>SNMP::Info Class the the device currently uses. Devices using more generic
interfaces like <tt>Layer2</tt> or <tt>Layer3</tt> may eventually get their
own subclass.
</TD>
</TR>
<TR>
<TD>Duplex</TD>
<TD>Ability to cull duplex settings from device.<BR>
<UL>
<LI><tt>no</tt> - Can't recover current or admin setting.
<LI><tt>link</tt> - Can get current setting only.
<LI><tt>both</tt> - Can get admin and link setting.
<LI><tt>write</tt> - Can get admin and link setting and perform sets.
</UL>
</TD>
</TR>
<TR>
<TD>Macsuck</TD>
<TD>Ability to get CAM tables for MAC to switch port mapping.<BR>
<UL>
<LI><TT>no</TT> - Have not found an SNMP method to get data yet.
<LI><TT>yes</TT> - Can get through normal SWITCH-MIB method.
<LI><TT>vlan</TT> - Have to re-connect to each VLAN and then fetch with normal
method.
</UL>
</TD>
</TR>
<TR>
<TD>Modules</TD>
<TD>Ability to gather hardware module information.</TD>
</TR>
<TR>
<TD>Portmac</TD>
<TD>Whether the device will list the MAC address of the switch port on each
switch port when doing a Macsuck.
</TD>
</TR>
<TR>
<TD>Topo</TD>
<TD>Ability to get Layer 2 Topology Information from device if the
protocol is enabled. SNMP::Info supports querying Link Layer
Discovery Protocol (LLDP), Cisco Discovery Protocol (CDP),
SynOptics/Bay/Nortel/Avaya Network Management Protocol (SONMP),
Foundry/Brocade Discovery Protocol (FDP), Extreme Discovery
Protocol (EDP), and Alcatel Mapping Adjacency Protocol (AMAP).
</TD>
</TR>
<TR>
<TD>Ver</TD>
<TD>SNMP Protocol Version the device has to use.</TD>
</TR>
<TR>
<TD>Vlan</TD>
<TD>Ability to get VLAN port assignments.<BR>
<UL>
<LI><TT>no</TT> - Have not found an SNMP method to get data yet.
<LI><TT>yes</TT> - Can read information.
<LI><TT>write</TT> - Can read and write (set).
</UL>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>
end_tail
}
sub print_headers {
print "<TR>\n";
foreach my $a (sort sort_nocase keys %$Attributes) {
next if $a eq 'note';
print " <TH>$a</TH>\n";
}
print "</TR>\n";
}

View File

@@ -1,94 +0,0 @@
#!/usr/local/bin/perl -w
# prereq.t - Test file for prerequesites for SNMP::Info
# $Id$
use strict;
use warnings;
use Test::More tests=> 3;
# Check for SNMP Module
my $have_snmp=0;
eval {
require SNMP;
};
if ($@){
print STDERR <<'end_snmp';
Net-SNMP not found. Net-SNMP installs the perl modules SNMP and
SNMP::Session.
Versions 4.2.1 to 5.3 the Perl modules are not distributed on CPAN, you must
install from the distribution.
Install Net-SNMP from http://net-snmp.sourceforge.net and make sure you run
configure with the --with-perl-modules switch!
Note to Redhat Users: Redhat, in its infinite wisdom, does not install the
Perl modules as part of their 8.0 RPMS. Please uninstall them and install the
newest version by hand.
Versions 5.3.1 and higher are once again available from CPAN.
end_snmp
ok(0,'Net-SNMP not installed, or missing Perl modules.');
} else {
$have_snmp=1;
ok(1,'Net-SNMP installed');
}
# Check for version
SKIP: {
skip('SNMP not installed, no further testing',2) unless $have_snmp;
my $VERSION = $SNMP::VERSION;
ok(defined $VERSION ? 1 : 1, "found version for SNMP");
my ($ver_maj,$ver_min,$ver_rev) = split(/\./,$VERSION);
ok ($ver_maj >= 4, 'Net-SNMP ver 4 or higher');
if ($ver_maj == 4 and $ver_min == 2 and $ver_rev == 0){
print STDERR << "end_420";
SNMP module version 4.2.0 found. Please triple check that you have
version 4.2.0 of Net-SNMP installed, and that you did not accidently install
the SNMP module found on CPAN. All newer versions are bundled with
Net-SNMP, and are not available on CPAN. Please find them at
http://net-snmp.sourceforge.net . Make sure you run configure with the
--with-perl-modules switch.
end_420
}
if( $ver_maj == 5 and $ver_min == 0 and $ver_rev == 1 ){
print STDERR << "end_501";
Perl module of Net-SNMP 5.0.1 is buggy. Please upgrade.
end_501
}
if(( $ver_maj == 5 and $ver_min == 3 and $ver_rev == 1 ) or
( $ver_maj == 5 and $ver_min == 2 and $ver_rev == 3 )) {
print STDERR << "end_bulkwalk";
Perl module of Net-SNMP Versions 5.3.1 and 5.2.3 have issues with bulkwalk,
turn off bulkwalk. Please upgrade.
end_bulkwalk
}
}
print STDERR << "end_mibs";
Make sure you download and install the MIBS needed for SNMP::Info.
See Man page or perldoc for SNMP::Info.
end_mibs
# vim:syntax=perl

View File

@@ -1,47 +0,0 @@
#!/usr/bin/perl -w
# $Id$
use File::Glob qw/bsd_glob/;
my @pms = glob_rec("../Info");
$new_version = shift @ARGV || '3.01';
foreach my $p (@pms) {
print "$p\n";
rename($p,"$p.orig");
open (O,"<$p.orig") or die;
open (P,">$p") or die "Can't open $p for write. $!\n";
while (<O>) {
s/^\s*\$VERSION\s+=\s*'[^']+'\s*;/\$VERSION = '$new_version';/;
print P;
}
close O;
close P or die "Can't write $p. $!\n";
unlink("$p.orig");
#last;
}
sub glob_rec {
my $dir = shift;
my @files = bsd_glob("$dir/*");
my @pms;
foreach my $f (@files) {
next if $f eq '\.$';
if (-d $f) {
push @pms, glob_rec($f);
next;
}
push @pms,$f if $f =~ /.pm$/;
}
return @pms;
}

View File

@@ -1,4 +0,0 @@
#!/bin/sh
# Run this from ../
/usr/local/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/$1

View File

@@ -1,346 +0,0 @@
#!/usr/bin/perl
#
# test_class.pl
#
# Copyright (c) 2013 Eric Miller
# 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.
use strict;
use warnings;
use Carp;
use Getopt::Long;
use Pod::Usage;
use SNMP::Info;
my $EMPTY = q{};
# Default Values
my $class = $EMPTY;
my @dump = ();
my $debug = 0;
my $cache = 0;
my $device = '';
my $comm = '';
my $ver = 2;
my $ignore = 0;
my $help = 0;
my $nobulk = 0;
my $mibdirs;
my %dumped;
GetOptions(
'c|class=s' => \$class,
'd|dev=s' => \$device,
's|comm=s' => \$comm,
'v|ver=i' => \$ver,
'i|ignore' => \$ignore,
'p|print=s' => \@dump,
'm|mibdir=s' => \$mibdirs,
'n|nobulk' => \$nobulk,
'x|debug+' => \$debug,
'k|cache' => \$cache,
'h|?|help' => sub { pod2usage(1); },
);
unless ( $device and $comm ) {
pod2usage(2);
}
if ( $ignore && !defined $mibdirs ) {
print "mibdirs must be provided if ignoring snmp.conf \n\n";
pod2usage(1);
}
local $ENV{'SNMPCONFPATH'} = $EMPTY if $ignore;
local $ENV{'MIBDIRS'} = "$mibdirs" if $ignore;
if ( defined $mibdirs ) {
SNMP::addMibDirs($mibdirs);
}
$class = $class ? "SNMP::Info::$class" : 'SNMP::Info';
( my $mod = "$class.pm" )
=~ s{::}{/}xg; # SNMP::Info::Layer3 => SNMP/Info/Layer3.pm
if ( !eval { require $mod; 1; } ) {
croak "Could not load $class. Error Message: $@\n";
}
my $class_ver = $class->VERSION();
print
"Class $class ($class_ver) loaded from SNMP::Info $SNMP::Info::VERSION.\n";
if ( scalar @dump ) { print 'Dumping : ', join( q{,}, @dump ), "\n" }
my %args = ();
if ($nobulk) {
$args{BulkWalk} = 0;
}
my $dev = $class->new(
'AutoSpecify' => 0,
'AutoVerBack' => 0,
'Debug' => $debug,
'Version' => $ver,
'DestHost' => $device,
'Community' => $comm,
%args
) or die "\n";
print "Connected to $device.\n";
print 'Detected Class: ', $dev->device_type(), "\n";
print "Using Class: $class (-c to change)\n";
my $layers = $dev->layers();
my $descr = $dev->description();
if ( !defined $layers || !defined $descr ) {
die
"Are you sure you got the right community string and version?\nCan't fetch layers or description.\n";
}
print "\nFetching base info...\n\n";
my @base_fns = qw/vendor model os os_ver description contact location
layers mac serial/;
foreach my $fn (@base_fns) {
test_global( $dev, $fn );
}
print "\nFetching interface info...\n\n";
my @fns = qw/interfaces i_type i_ignore i_description i_mtu i_speed i_mac i_up
i_up_admin i_name i_duplex i_duplex_admin i_stp_state
i_vlan i_pvid i_lastchange/;
foreach my $fn (@fns) {
test_fn( $dev, $fn );
}
print "\nFetching VLAN info...\n\n";
my @vlan = qw/v_index v_name/;
foreach my $fn (@vlan) {
test_fn( $dev, $fn );
}
print "\nFetching topology info...\n\n";
my @topo = qw/c_if c_ip c_port c_id c_platform/;
foreach my $fn (@topo) {
test_fn( $dev, $fn );
}
print "\nFetching module info...\n\n";
my @modules = qw/e_descr e_type e_parent e_name e_class e_pos e_hwver
e_fwver e_swver e_model e_serial e_fru/;
foreach my $fn (@modules) {
test_fn( $dev, $fn );
}
foreach my $fn (@dump) {
if ( !$dumped{$fn} ) { test_fn( $dev, $fn ) }
}
if ($cache) {
eval {
require Data::Printer;
} && eval {
print "\nDumping cache...\n\n";
Data::Printer::p $dev;
};
}
#--------------------------------
sub test_global {
my $info = shift;
my $method = shift;
my $value = $info->$method();
if ( !defined $value ) {
printf "%-20s Does not exist.\n", $method;
return 0;
}
$value =~ s/[[:cntrl:]]+/ /gx;
if ( length $value > 60 ) {
$value = substr $value, 0, 60;
$value .= '...';
}
printf "%-20s %s \n", $method, $value;
return 1;
}
sub test_fn {
my $info = shift;
my $method = shift;
my $results = $info->$method();
# If accidentally called on a global, pass it along nicely.
if ( defined $results && !ref $results ) {
return test_global( $dev, $method );
}
if ( !defined $results && !scalar keys %{$results} ) {
printf "%-20s Empty Results.\n", $method;
return 0;
}
printf "%-20s %d rows.\n", $method, scalar keys %{$results};
if ( grep {/^$method$/x} @dump ) {
$dumped{$method} = 1;
foreach my $iid ( keys %{$results} ) {
print " $iid : ";
if ( ref( $results->{$iid} ) eq 'ARRAY' ) {
print '[ ', join( ', ', @{ $results->{$iid} } ), ' ]';
}
else {
print $results->{$iid};
}
print "\n";
}
}
return 1;
}
__END__
=head1 NAME
test_class.pl - Test a device against an SNMP::Info class.
=head1 AUTHOR
Eric Miller
=head1 SYNOPSIS
test_class.pl [options]
Options:
-c|class SNMP::Info class to use, Layer2::Catalyst
-d|dev Device
-s|comm SNMP community
-v|ver SNMP version
-p|print Print values
-i|ignore Ignore Net-SNMP configuration file
-m|mibdir Directory containing MIB Files
-n|nobulk Disable bulkwalk
-x|debug Debugging flag
-k|cache Dump cache (requires Data::Printer to be installed)
-h|?|help Brief help message
=head1 OPTIONS
=over 8
=item B<-class>
Specific SNMP::Info class to use. Defaults to SNMP::Info if no specific
class provided.
-class Layer2::Catalyst
=item B<-dev>
Device to test against. No default and a mandatory option.
-dev 1.2.3.4
=item B<-comm>
SNMP community string. No default and a mandatory option.
-comm public
=item B<-ver>
SNMP version. Default 2.
-ver 1
=item B<-print>
Print values of a class method rather than summarizing. May be repeated
multiple times.
-print i_description -print i_type
=item B<-ignore >
Ignore Net-SNMP configuration file snmp.conf. If this used mibdirs must be
provided.
-ignore
=item B<-mibdir>
Directory containing MIB Files. Multiple directories should be separated by a
colon ':'.
-mibdir /usr/local/share/snmp/mibs/rfc:/usr/local/share/snmp/mibs/net-snmp
=item B<-nobulk >
Disable SNMP bulkwalk. Default bulkwalk is on and utilized with version 2.
-nobulk
=item B<-debug>
Turns on SNMP::Info debug.
-debug
=item B<-cache>
Dumps the table and leaf cache at the end of running. Requires that the
L<Data::Printer> module be installed, otherwise does nothing.
-cache
=item B<-help>
Print help message and exits.
=back
=head1 DESCRIPTION
B<test_class.pl> will test a device against a specfied SNMP::Info class.
This allows debugging and testing of live devices to include validating
device support with existing classes.
=cut

View File

@@ -1,207 +0,0 @@
#!/usr/bin/perl
#
# make_snmpdata.pl
#
# Copyright (c) 2012 Eric Miller
# 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.
use strict;
use warnings;
use Carp;
use Getopt::Long;
use Pod::Usage;
use SNMP;
local $| = 1;
my $mibdirs = ['/usr/local/share/snmp/mibs'];
my $comm = 'public';
my $ver = '2c';
my $dev;
my $ignore = 0;
my $help = 0;
GetOptions(
'community=s' => \$comm,
'device=s' => \$dev,
'ignore' => \$ignore,
'mibdir=s' => \$mibdirs,
'version' => \$ver,
'help|?' => sub { pod2usage(2); },
) or pod2usage(2);
unless ( defined $dev && $ver =~ /[1|2c]/ ) {
pod2usage(1);
}
local $ENV{'SNMPCONFPATH'} = '' if $ignore;
local $ENV{'MIBDIRS'} = "$mibdirs" if $ignore;
SNMP::addMibDirs($mibdirs);
# Connect to Device
my $sess = SNMP::Session->new(
'UseEnums' => 1,
'RetryNoSuch' => 1,
'DestHost' => $dev,
'Community' => $comm,
'Version' => $ver,
'UseSprintValue' => 1
);
my $sysdescr = $sess->get('sysDescr.0');
unless ( defined $sysdescr ) {
die "Couldn't connect to $dev via snmp.\n";
}
SNMP::loadModules(@ARGV);
# Create a hash of MIB Modules for which we want results
my %mib_hash = map {$_ => 1} @ARGV;
# Add the common MIB Modules we always want
my @common_mibs = ('SNMPv2-MIB', 'IF-MIB');
foreach my $mib (@common_mibs) {
$mib_hash{$mib} = 1;
}
foreach my $key ( sort( keys %SNMP::MIB ) ) {
my $module = $SNMP::MIB{$key}{moduleID} || '';
# IMPORTS pulls in many modules we don't want to walk
# Only walk those we've specified
next unless (defined $mib_hash{$module});
my $access = $SNMP::MIB{$key}{'access'} || '';
next unless ( $access =~ /Read|Create/x );
my $label = SNMP::translateObj( $key, 0, 1 ) || '';
snmpwalk($label);
}
sub snmpwalk {
return unless defined $sess;
my $label = shift;
my $var = SNMP::Varbind->new( [$label] );
my $e = 0;
my $last_iid = '';
my %seen = ();
while ( !$e ) {
$sess->getnext($var);
$e = $sess->{ErrorNum};
return if $var->[0] ne $label;
my $iid = $var->[1];
my $val = $var->[2];
return unless defined $iid;
# Check to see if we've already seen this IID (looping)
if ( defined $seen{$iid} and $seen{$iid} ) {
warn "Looping on $label iid:$iid. Skipped.\n";
return;
}
else { $seen{$iid}++; }
# why is it looping?
return if $last_iid eq $iid;
$last_iid = $iid;
my $line = "$label.$iid = $val";
print "$line\n";
}
return;
}
__END__
=head1 NAME
make_snmpdata.pl - Tool to get SNMP data for the SNMP::Info testing framework
=head1 AUTHOR
Eric Miller
=head1 SYNOPSIS
make_snmpdata.pl [options] MIB-MODULE-1 MIB-MODULE-2
Options:
-community SNMP Community
-device IP Address to query
-ignore Ignore Net-SNMP configuration file
-mibdir Directory containing MIB Files
-version SNMP version to use
-help Brief help message
=head1 OPTIONS
=over 8
=item B<-community>
SNMP Community, either 1 or 2c. Defaults to version 2c
-community 2c
=item B<-device>
IP Address to query for the SNMP data. No default and a mandatory option.
-device 127.0.0.1
=item B<-ignore >
Ignore Net-SNMP configuration file snmp.conf. If this used mibdirs must be
provided
-ignore
=item B<-mibdir>
Directory containing MIB Files. Mutiple directories should be separated by a
colon ':'. Defaults to /usr/local/share/snmp/mibs.
-mibdir /usr/local/share/snmp/mibs/rfc:/usr/local/share/snmp/mibs/net-snmp
=item B<-version>
SNMP version to use. Only version 1 and 2c are supported. Defaults to 2c
-version 2c
=item B<-help>
Print help message and exits.
=back
=head1 DESCRIPTION
B<make_snmpdata.pl> will gather SNMP data by walking specified MIB files and
output the data to a file which can be used by the SNMP::Info testing
framework.
=cut

View File

@@ -1,454 +0,0 @@
#!/usr/bin/perl
#
# test_class_mocked.pl
#
# Copyright (c) 2012 Eric Miller
# 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.
use strict;
use warnings;
use Carp;
use FindBin;
use lib "$FindBin::Bin/../../..";
use File::Slurp qw(slurp);
use Getopt::Long;
use Pod::Usage;
use SNMP::Info;
use Test::MockObject::Extends;
my $EMPTY = q{};
# Default Values
my $class = $EMPTY;
my @dump = ();
my $debug = 0;
my $mibdirs;
my $ignore = 0;
my $help = 0;
my $file;
my %dumped;
GetOptions(
'c|class=s' => \$class,
'i|ignore' => \$ignore,
'p|print=s' => \@dump,
'x|debug+' => \$debug,
'm|mibdir=s' => \$mibdirs,
'file=s' => \$file,
'h|?|help' => sub { pod2usage(1); },
);
if ( !$file ) {
pod2usage(1);
}
if ( $ignore && !defined $mibdirs ) {
print "mibdirs must be provided if ignoring snmp.conf \n\n";
pod2usage(1);
}
local $ENV{'SNMPCONFPATH'} = $EMPTY if $ignore;
local $ENV{'MIBDIRS'} = "$mibdirs" if $ignore;
if ( defined $mibdirs ) {
SNMP::addMibDirs($mibdirs);
}
$class = $class ? "SNMP::Info::$class" : 'SNMP::Info';
( my $mod = "$class.pm" )
=~ s{::}{/}g; # SNMP::Info::Layer3 => SNMP/Info/Layer3.pm
if ( !eval { require $mod; 1; } ) {
croak "Could not load $class. Error Message: $@\n";
}
my $class_ver = $class->VERSION();
print
"Class $class ($class_ver) loaded from SNMP::Info $SNMP::Info::VERSION.\n";
if ( scalar @dump ) { print 'Dumping : ', join( q{,}, @dump ), "\n" }
my $mocked = create_mock_session();
my $dev = $class->new(
'AutoSpecify' => 0,
'BulkWalk' => 0,
'Debug' => $debug,
'Session' => $mocked,
) or die "\n";
print 'Detected Class: ', $dev->device_type(), "\n";
print "Using Class: $class (-c to change)\n";
my $layers = $dev->layers();
my $descr = $dev->description();
if ( !defined $layers || !defined $descr ) {
die "Are you sure you specified a file created with make_snmpdata.pl ?\n";
}
print "\nFetching base info...\n\n";
my @base_fns = qw/vendor model os os_ver description contact location
layers mac serial/;
foreach my $fn (@base_fns) {
test_global( $dev, $fn );
}
print "\nFetching interface info...\n\n";
my @fns = qw/interfaces i_type i_ignore i_description i_mtu i_speed i_mac i_up
i_up_admin i_name i_duplex i_duplex_admin i_stp_state
i_vlan i_pvid i_lastchange/;
foreach my $fn (@fns) {
test_fn( $dev, $fn );
}
print "\nFetching VLAN info...\n\n";
my @vlan = qw/v_index v_name/;
foreach my $fn (@vlan) {
test_fn( $dev, $fn );
}
print "\nFetching topology info...\n\n";
my @topo = qw/c_if c_ip c_port c_id c_platform/;
foreach my $fn (@topo) {
test_fn( $dev, $fn );
}
print "\nFetching module info...\n\n";
my @modules = qw/e_descr e_type e_parent e_name e_class e_pos e_hwver
e_fwver e_swver e_model e_serial e_fru/;
foreach my $fn (@modules) {
test_fn( $dev, $fn );
}
foreach my $fn (@dump) {
if ( !$dumped{$fn} ) { test_fn( $dev, $fn ) }
}
#--------------------------------
sub load_snmpdata {
my $data_file = shift;
my @lines = slurp($data_file);
my $snmp_data = {};
foreach my $line (@lines) {
next if !$line;
next if ( $line =~ /^#/ );
if ( $line =~ /^(\S+::\w+)[.]?(\S+)*\s=\s(.*)$/ ) {
my ( $leaf, $iid, $val ) = ( $1, $2, $3 );
next if !$leaf;
$iid ||= 0;
$val =~ s/\"//g;
$snmp_data->{$leaf}->{$iid} = $val;
}
}
return $snmp_data;
}
sub create_mock_session {
my $snmp_data = load_snmpdata($file);
my $session = SNMP::Session->new(
UseEnums => 1,
RetryNoSuch => 1,
Data => $snmp_data,
DestHost => '127.0.0.1',
Community => 'public',
Version => 2,
);
my $mock_session = Test::MockObject::Extends->new($session);
mock_get($mock_session);
mock_getnext($mock_session);
return $mock_session;
}
sub mock_get {
my $mock_session = shift;
$mock_session->mock(
'get',
sub {
my $self = shift;
my $vars = shift;
my ( $leaf, $iid, $oid, $oid_name );
my $c_data = $self->{Data};
# From SNMP::Info get will only be passed either an OID or
# SNMP::Varbind with a fully qualified leaf and potentially
# a partial
if ( ref($vars) =~ /SNMP::Varbind/ ) {
( $leaf, $iid ) = @{$vars};
}
else {
$oid = $vars;
$oid_name = SNMP::translateObj( $oid, 0, 1 ) || $EMPTY;
( $leaf, $iid ) = $oid_name =~ /^(\S+::\w+)[.]?(\S+)*$/;
}
$iid ||= 0;
my $new_iid = $iid;
my $val = $EMPTY;
my $data = $c_data->{$leaf} || {};
my $count = scalar keys %{$data} || 0;
if ( $count > 1 ) {
my $found = 0;
foreach my $d_iid ( sort keys %{$data} ) {
if ( $d_iid eq $iid ) {
$val = $data->{$d_iid};
$found = 1;
next;
}
elsif ( $found == 1 ) {
$new_iid = $d_iid;
last;
}
}
if ( $found && ( $new_iid eq $iid ) ) {
$leaf = 'unknown';
}
}
else {
$val = $data->{$iid};
$leaf = 'unknown';
}
if ( ref $vars =~ /SNMP::Varbind/ ) {
$vars->[0] = $leaf;
$vars->[1] = $new_iid;
$vars->[2] = $val;
}
return ( wantarray() ? $vars : $val );
}
);
return;
}
sub mock_getnext {
my $mock_session = shift;
$mock_session->mock(
'getnext',
sub {
my $self = shift;
my $vars = shift;
my ( $leaf, $iid, $oid, $oid_name );
my $c_data = $self->{Data};
# From SNMP::Info getnext will only be passed a SNMP::Varbind
# with a fully qualified leaf and potentially a partial
( $leaf, $iid ) = @{$vars};
unless (defined $iid) {
$iid = -1;
}
my $new_iid = $iid;
my $val = $EMPTY;
my $data = $c_data->{$leaf};
my $count = scalar keys %{$data} || 0;
if ( $count ) {
my $found = 0;
foreach my $d_iid ( sort keys %{$data} ) {
if ( $d_iid gt $iid && !$found ) {
$val = $data->{$d_iid};
$new_iid = $d_iid;
$found = 1;
next;
}
elsif ( $found == 1 ) {
last;
}
}
if ( $found && ( $new_iid eq $iid ) ) {
$leaf = 'unknown';
}
}
else {
$val = $data->{$iid};
$leaf = 'unknown';
}
$vars->[0] = $leaf;
$vars->[1] = $new_iid;
$vars->[2] = $val;
return ( wantarray() ? $vars : $val );
}
);
return;
}
sub test_global {
my $device = shift;
my $method = shift;
my $value = $device->$method();
if ( !defined $value ) {
printf "%-20s Does not exist.\n", $method;
return 0;
}
$value =~ s/[[:cntrl:]]+/ /g;
if ( length $value > 60 ) {
$value = substr $value, 0, 60;
$value .= '...';
}
printf "%-20s %s \n", $method, $value;
return 1;
}
sub test_fn {
my $device = shift;
my $method = shift;
my $results = $device->$method();
# If accidentally called on a global, pass it along nicely.
if ( defined $results && !ref $results ) {
return test_global( $dev, $method );
}
if ( !defined $results && !scalar keys %{$results} ) {
printf "%-20s Empty Results.\n", $method;
return 0;
}
printf "%-20s %d rows.\n", $method, scalar keys %{$results};
if ( grep {/^$method$/} @dump ) {
$dumped{$method} = 1;
foreach my $iid ( keys %{$results} ) {
print " $iid : ";
if ( ref( $results->{$iid} ) eq 'ARRAY' ) {
print '[ ', join( ', ', @{ $results->{$iid} } ), ' ]';
}
else {
print $results->{$iid};
}
print "\n";
}
}
return 1;
}
__END__
=head1 NAME
test_class_mocked.pl - Test a device against an SNMP::Info class using
output from make_snmpdata.pl stored in a text file.
=head1 AUTHOR
Eric Miller
=head1 SYNOPSIS
test_class_mocked.pl [options]
Options:
-class SNMP::Info class to use, Layer2::Catalyst
-file File containing data gathered using make_snmpdata.pl
-print Print values
-debug Debugging flag
-ignore Ignore Net-SNMP configuration file
-mibdir Directory containing MIB Files
-help Brief help message
=head1 OPTIONS
=over 8
=item B<-class>
Specific SNMP::Info class to use. Defaults to SNMP::Info if no specific
class provided.
-class Layer2::Catalyst
=item B<-file>
File containing data gathered using make_snmpdata.pl. No default and a
mandatory option.
-file /data/mydevice.txt
=item B<-print>
Print values of a class method rather than summarizing. May be repeated
multiple times.
-print i_description -print i_type
=item B<-debug>
Turns on SNMP::Info debug.
-debug
=item B<-ignore >
Ignore Net-SNMP configuration file snmp.conf. If this used mibdirs must be
provided.
-ignore
=item B<-mibdir>
Directory containing MIB Files. Multiple directories should be separated by a
colon ':'.
-mibdir /usr/local/share/snmp/mibs/rfc:/usr/local/share/snmp/mibs/net-snmp
=item B<-help>
Print help message and exits.
=back
=head1 DESCRIPTION
B<test_class_mocked.pl> will test a device against an SNMP::Info class using
snmpwalk output from the utility B<make_snmpdata.pl> stored in a text file.
This allows debugging and testing without requiring network access to the
device being tested.
=cut