http://qs1969.pair.com?node_id=98175
Category: Networking Code
Author/Contact Info fingers
Description: Usage: switchcheck.pl options ip { ip ip ... }
Designed to get an overview or collect statisics on switchports usage using SNMP.
-b option allows you to specify community name on the command line to allow being called in a cron job -l logs basic port usage stats to a dbm file
-r is used to read files created by -l
-v gives more detailed stats on ports that are up
-c generates a csv file, can be used in conjunction with -v
Some options are mutually exclusive. The later option takes precedence.
Tested
with:
Perl 5.6.1
RedHat 7.1
against:
Cisco 6509,2924XL,3508XL,3512XL,3524XL,3548XL switches
I realize it may be a little sloppy and amateur, I definately would appreciate any constructive criticism anyone has about my code.
#!/usr/bin/perl -w
# switchcheck.pl
use strict;
use Net::SNMP;
my($error,$session,$seed_oid,$oid_root,$csvname,$community,$hostname,$
+seed_ip,$serial,$option,$logdir);
my(@unique,@hostname);
my($ifMIB) = "1.3.6.1.2.1.31.1.1.1";
my(%option)=(
    verbose => 0,
    csv => 0,
    log => 0,
    batch => 0,
    help => 0,
    hostname => 0,
    read => 0
    );
die "\nusage: $0 option ip\n Use $0 -h for list of options\n" unless (
+ @ARGV >= 1 ); 
$logdir="/var/log/sc";


#Begin processing command line arguments
#***************************************************************
while(@ARGV){
    $option = $ARGV[0];
    if($option eq "-v"){
        $option{'verbose'} = 1;
        $option{'log'} = 0;
        $option{'read'} = 0;
    
    }
    elsif($option eq "-l"){
        $option{'log'} = 1;
        $option{'verbose'} = 0;
        $option{'read'} = 0;
    }
    elsif($option eq "-r"){
        $option{'read'} = 1;
        $option{'verbose'} = 0;
        $option{'log'} = 0;
    }
    elsif($option eq "-b"){
        $option{'batch'} = 1;
        $community = $ARGV[1];
        shift @ARGV;
    }
    elsif($option eq "-h"){
        $option{'help'} = 1;
        die "\n\n usage: $0 options ip ... \n\n
        -h        This help message
        -l        Logging
        -v        Verbose
        -c filename     Send output to filename.csv
        -b community    Batch, community is the snmp community name
        -r ip ...    Display results from log files for specified IPs\
+n\n\n";
    
    }
    elsif($option eq "-c"){
        $option{'csv'} = 1;
        $option{'read'} = 0;
        $option{'log'} = 0;
        $csvname = ("$ARGV[1]".".csv");
        shift @ARGV;
    }
    else{
        die "usage: $0 ip\n" unless ($option =~ m{\d+\.\d+\.\d+\.\d+})
+;
        push @hostname , $option;
        $option{'hostname'} = 1;
    }
    shift @ARGV;
}
die "You need to give at least one IP address as an option\n" unless($
+option{'hostname'});    

#End argument processing
#*************************************************************

#Prompt for SNMP community string
unless($option{'batch'}){
    print "community: ";
    chomp($community = <STDIN>);
}
$oid_root = "1.3.6.1.2.1.2.2.1";
$seed_oid = ("$oid_root".".1");

unless($option{'csv'}){
foreach(@hostname){
    undef @unique;
    $hostname=$_;
    #Open SNMP session
    ($session,$error) = Net::SNMP->session(Hostname => $hostname, Comm
+unity => $community);
    die "$error\n" unless($session);
    get_oids($seed_oid); #Get the SNMP info for this target
        
        
        if($option{'verbose'}){
            print "Port  Speed            Errors/Data In              
+         Errors/Data Out                  Uptime \n";
            print "---------------------------------------------------
+---------------------------------------------------------------------
+-----\n";        
            
            SNMP_Grocery_List();
        }

        elsif($option{'log'}){
            SNMP_Logging();
        }
        elsif($option{'read'}){
            Read_Log();
        }
        else{
            SNMP_Light();
        }
    $session->close;
    
    }
}
    if($option{'csv'}){
        open(CSV,">$csvname")|| die "Can't open $csvname\n";
        
        if($option{'verbose'}){
            print CSV "Name,Speed,Errors_In,Data_in,Errors_out,Data_ou
+t,Uptime,Switch_IP\n";
            foreach(@hostname){
                undef @unique;
                $hostname=$_;
                #Open SNMP session
                ($session,$error) = Net::SNMP->session(Hostname => $ho
+stname, Community => $community);
                die "$error\n" unless($session);
                get_oids($seed_oid); #Get the SNMP info for this targe
+t
                Make_CSV_Verbose();
            }
            $session->close;
            
        }
        else{
        print CSV "Switch_IP,Name,Total_Ports,Up,Down,Admin_Down\n";
            foreach(@hostname){
                undef @unique;
                $hostname=$_;
                #Open SNMP session
                ($session,$error) = Net::SNMP->session(Hostname => $ho
+stname, Community => $community);
                die "$error\n" unless($session);
                get_oids($seed_oid); #Get the SNMP info for this targe
+t
                Make_CSV();
                $session->close;
            }
        }
        close(CSV) || die "Can't close $csvname\n";    
    }
    


#Subs below here
#*********************************************************************
+******    

#This sub is what walks through and enumerates an oid tree
#*********************************************************************
+******    
    sub get_oids{
        my($starting_oid , $new_oid , $unique_oid , $result , $crap);
        $starting_oid = $_[0];
        $new_oid = $starting_oid ;
        
        while(Net::SNMP::oid_context_match($starting_oid,$new_oid)){
            $result = $session->get_next_request(($new_oid));
            return unless (defined $result);
            ($new_oid , $crap) = %$result;
            if (Net::SNMP::oid_context_match($starting_oid,$new_oid)){
                $unique_oid = $new_oid;
                $unique_oid =~ s/$starting_oid//g;
                push @unique , $unique_oid ;
                get_oids($new_oid);
            
            }
        
        }

#This is the sub for -v option
#*********************************************************************
+******    
    
    sub SNMP_Grocery_List{        
        my($unique , $oper , $admin , $name , $uptime , $speed , $data
+in , $dataout , $errorsin , $errorsout);
        my(%tally);
        %tally=(
            total => 0,
            up => 0,
            down => 0,
            admin => 0
            );
        foreach(@unique){
            $unique = $_ ;
            $tally{'total'}++;
            $oper = (Get_SNMP_Info("$oid_root".".8"."$unique"));
            if($oper == 1){    
                $tally{'up'}++;
                $name = (Get_SNMP_Info("$ifMIB".".1"."$unique"));
                $uptime = (Get_SNMP_Info("$oid_root".".9"."$unique"));
                $speed = (Get_SNMP_Info("$ifMIB".".15"."$unique"));
                $datain = (Get_SNMP_Info("$oid_root".".10"."$unique"))
+;
                $dataout = (Get_SNMP_Info("$oid_root".".16"."$unique")
+);
                $errorsin = (Get_SNMP_Info("$oid_root".".14"."$unique"
+));
                $errorsout = (Get_SNMP_Info("$oid_root".".20"."$unique
+"));
                
                $~="GROCERY";
                write;
            }
            else {
                $tally{'down'}++;
                $admin = (Get_SNMP_Info("$oid_root".".7"."$unique"));
                if($admin == 2){
                    $tally{'admin'}++;
                }
            }
        }
        print "\nSummary: $hostname \n Total Ports: $tally{'total'} \n
+ Ports Up: $tally{'up'} \n Ports Down: $tally{'down'} \n Admin Down $
+tally{'admin'} \n";
            
format GROCERY =
@<<<<<<<<<< @<<<<<< @>>>>>>>>>>>>>>>>>/@<<<<<<<<<<<<<<<<<   @>>>>>>>>>
+>>>>>>>>/@<<<<<<<<<<<<<<<<<        @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+<<<<<<<
$name,$speed,$errorsin,$datain,$errorsout,$dataout,$uptime
.
            
    }
# Sub for -l option
#*********************************************************************
+******    
    sub SNMP_Logging{
        my($unique , $oper , $dbm);
        my(%log);
        $dbm=("$logdir"."sc"."$hostname");
        dbmopen(%log, $dbm, 0644);
        foreach(@unique){
            $unique = $_ ;
            $oper = (Get_SNMP_Info("$oid_root".".8"."$unique"));
            if($oper == 1){    
                $log{$unique}++;
            }
        }
        dbmclose(%log);
    }

#Sub for -r option
#*********************************************************************
+******    

    sub Read_Log{
        my($dbm,$name,$uptime,$unique,$counter);
        my(%log);
        $dbm=("$logdir"."sc"."$hostname");
        dbmopen(%log, $dbm, 0644);
        print "\n\n Switch Check Log for $hostname\n";
        print "---------------------------------------------------\n";
        while (($unique, $counter) = each(%log)) {
            $name = (Get_SNMP_Info("$ifMIB".".1"."$unique"));
            $uptime = (Get_SNMP_Info("$oid_root".".9"."$unique"));
            $~="READ_LOG";
            write;
        }
        dbmclose(%log);
format READ_LOG =
@<<<<<<<<<<<<<< @<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+<<<<<<<
$name,$counter,$uptime
.


}
                
        

#sub for default operation
#*********************************************************************
+******    
    sub SNMP_Light{        
        my($unique , $oper , $admin);
        my(%tally);
        %tally=(
            total => 0,
            up => 0,
            down => 0,
            admin => 0
            );
        foreach(@unique){
            $unique = $_ ;
            $tally{'total'}++;
            $oper = (Get_SNMP_Info("$oid_root".".8"."$unique"));
            if($oper == 1){    
                $tally{'up'}++;
            }
            else {
                $tally{'down'}++;
                $admin = (Get_SNMP_Info("$oid_root".".7"."$unique"));
                if($admin == 2){
                    $tally{'admin'}++;
                }
            }
        }
        print "\nSummary: $hostname \n Total Ports: $tally{'total'} \n
+ Ports Up: $tally{'up'} \n Ports Down: $tally{'down'} \n Admin Down $
+tally{'admin'} \n";
            
    }

#Sub that acually gets the value for a oid
#*********************************************************************
+******    

        sub Get_SNMP_Info{ #This sub gets the value of an oid
        
            my($crap , $value , $result);
            my($oid) = $_[0];
            $result = $session->get_request("$oid");
            return unless (defined $result);
            ($crap , $value) = %$result;
            return $value;

        }


#sub for -c and -v
#*********************************************************************
+******    
        
        sub Make_CSV_Verbose{
        my($unique , $oper , $admin , $name , $uptime , $speed , $data
+in , $dataout , $errorsin , $errorsout);
        foreach(@unique){
            $unique = $_ ;
            $oper = (Get_SNMP_Info("$oid_root".".8"."$unique"));
            $name = (Get_SNMP_Info("$ifMIB".".1"."$unique"));
            $uptime = (Get_SNMP_Info("$oid_root".".9"."$unique"));
            $speed = (Get_SNMP_Info("$ifMIB".".15"."$unique"));
            $datain = (Get_SNMP_Info("$oid_root".".10"."$unique"));
            $dataout = (Get_SNMP_Info("$oid_root".".16"."$unique"));
            $errorsin = (Get_SNMP_Info("$oid_root".".14"."$unique"));
            $errorsout = (Get_SNMP_Info("$oid_root".".20"."$unique"));
            print CSV "$name,$speed,$errorsin,$datain,$errorsout,$data
+out,$uptime,$hostname\n";
                
            
        }
            
    }
#sub for -c            
#*********************************************************************
+******    
        sub Make_CSV{
        my($unique, $sysname , $oper , $admin);
        my(%tally);
        %tally=(
            total => 0,
            up => 0,
            down => 0,
            admin => 0
            );
            $sysname = (Get_SNMP_Info("1.3.6.1.2.1.1.5.0"));
        foreach(@unique){
            $unique = $_ ;
            $tally{'total'}++;
            $oper = (Get_SNMP_Info("$oid_root".".8"."$unique"));
            if($oper == 1){    
                $tally{'up'}++;
            }
            else {
                $tally{'down'}++;
                $admin = (Get_SNMP_Info("$oid_root".".7"."$unique"));
                if($admin == 2){
                    $tally{'admin'}++;
                }
            }
        }
        print CSV "$hostname,$sysname,$tally{'total'},$tally{'up'},$ta
+lly{'down'},$tally{'admin'}\n";
            
    }


#*********************************************************************
+******    



}

=head1 Name

 switchcheck.pl

=head1 Summary

 Usage: switchcheck.pl options ip { ip ip ... }
 
 Designed to get an overview or collect statisics on switchports usage
+ using SNMP.

 -b option allows you to specify community name on the command line to
+ allow being called in a cron job

 -l logs basic port usage stats to a dbm file

 -r is used to read files created by -l

 -v gives more detailed stats on ports that are up

 -c generates a csv file, can be used in conjunction with -v

 Some options are mutually exclusive. The later option takes precedenc
+e.
 

=head1 Tested

 with:
   Perl 5.6.1
   RedHat 7.1
 against:
   Cisco 6509,2924XL,3508XL,3512XL,3524XL,3548XL switches

=head1 Author

 fingers

=head1 Credits
 Thanks to ybic for answering dumb perl questions and appreciating poo
+rly written code

=head1 Caveats

 When looking at the port counts it is important to keep in mind that 
+vlans and other "interfaces" show up in this count. 
 Look at the output from -v for each type of switch you are using to g
+et an idea how many non-port interfaces each switch has.

=head1 Todos

  Add in -i option to add a polling interval, to spread out the bandwi
+dth impacts of running against remote equipment.

  Accept multiple RO strings for use in enviroments where switches may
+ have different community names.

  Add in VLAN and MAC information to the -v reports. So far I haven't 
+been able to identify the oids to use for these.
  
  Add in support to log to a real database instead of dbm.

=head1 Updates

 2001-07-19 Post to perlmonks


 =cut