#!/opt/vdops/bin/perl # This script queries Cisco wireless controllers for cldcClientStatus # and logs the total number of associated clients # V Who When What # --------------------------------------------------------------------------- # 1.0.0 skendric 2011-05-20 First Version # # Author: Stuart Kendrick, sbk {put at sign here} skendric {put dot here} com # # Source: http://www.skendric.com/device # # This software is available under the GNU GENERAL PUBLIC LICENSE, see # http://www.fsf.org/licenses/gpl.html # # # This script takes the following approach: # -Parses the hosts table for a list of targets (or accepts a command- # line list) # -Uses a variety of methods to ask each target for the name # of the image it is running and the name of its hardware type # -Produces a report # # # Requirements: # -The following MIB modules stashed in /opt/vdops/share/snmp/mibs, # or wherever it is that you store MIB modules: # CISCO-PRODUCTS-MIB.my # # -PERL modules: the FHCRC::Netops collection # # # Assumptions: # # # Tested on: # -perl-5.12.2 # -net-snmp-5.6 # # # Instructions: # -Customize the script for your site: find the 'user-configurable # variables' section and modify as appropriate # -Type "count-associated-clients" to see the command-line options # -Try it out # # # # Caveats: # # # Known Bugs: # # # To do: # -Add support for SNMPv3 # # Begin script # Load modules use v5.12.0; use strict; use warnings; use feature 'say'; use feature 'switch'; use Carp qw(carp cluck croak confess); use Data::Dumper; use English qw( -no_match_vars ); use Getopt::Std; use List::MoreUtils qw(any); use FHCRC::Netops::HostTools 1.0.4; use FHCRC::Netops::NetopsTools 2.2.3; use FHCRC::Netops::NetopsData 1.4.0; use FHCRC::Netops::PingTools 1.1.7; use FHCRC::Netops::SNMPTools 1.5.3; use FHCRC::Netops::Utilities 1.4.4; # Declare global variables my %stats; # Hash of hash refs, counting stations by the # values of CLDot11ClientStatus my $total_stations; # Sum of all stations across all targets my @values; # Values of CLDot11ClientStatus # Define global variables $program_name = 'count-associated-clients'; $usage = 'Usage: count-associated-clients -s {yes|no} [-d {integer}] [-r] [-a | -e {expr} | -f {filename} | target1 target2 target3 ...]'; $version = '1.0.0'; # Define user-configureable variables @values = qw/idle aaaPending authenticated associated powersave disassociated tobedeleted probing excluded/; # Grab arguments getopts('ad:e:f:rs:', \%option); @target = @ARGV; # Set mode if ($option{r}) { $mode = 'report' } elsif (-t STDIN) { $mode = 'interactive' } else { $mode = 'batch' } ### Begin Main Program ############################################### { check_args(); # Check arguments read_config(); # Read config file compile_mibs(); # Compile MIB files build_target(); # Populate @target target_check(); # Look for errors in @target basic_info(); # Gather information do_the_work(); # Do the work write_log(); # Record what we found print_report(); # Print report } ##### End Main Program ############################################### ######################################################################## # Do the work: acquire image name ######################################################################## sub do_the_work { # Debug trace trace_location('begin') if $debug; # Notify operator print_it('Acquiring image names...'); # Skip if we aren't serious goto END unless $dome; # Loop through targets TARGET: for my $target (@target) { my $vb; # Acquire cldcClientStatus $vb = snmpWalk( {host => $target, oid => 'cldcClientStatus'} ); # Count clients for my $varbind (@$vb) { my $val = $varbind->{val}; $total_stations++; unless (any {$_ eq $val} @values) { log_it("For $target, invalid value for cldcClientStatus = $val"); } $stats{$target}->{$val}++; } # Entertain operator print $BANG if $mode eq 'interactive'; } # Fill out stats hash for my $target (@target) { for my $value (@values) { $stats{$target}->{$value} //= 0; } } # Make things look pretty say "\n" if $mode eq 'interactive'; # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Tell the operator what I discovered ######################################################################## sub print_report { my $handle; my $total = @target; my $now = get_now(); # Debug trace trace_location('begin') if $debug; # If we are running in test mode, skip this routine unless ($dome) { print_it("Running in test mode, cannot print a meaningful report\n"); return 1; } # Direct output to screen or to file if ($mode eq 'interactive') { $handle = *STDOUT; } else { open $handle, '>', $report_file or die "Cannot open $report_file: $!\n"; } print {$handle} <{idle}, $s->{aaaPending}, $s->{authenticated}, $s->{associated}, $s->{powersave}, $s->{disassociated}, $s->{tobedeleted}, $s->{probing}, $s->{excluded}; } unless ($handle =~ /STDOUT/) { close $handle or warn "Cannot close $report_file: $!\n"; } # Make things look pretty print_it("\n\nEnding $PROGRAM_NAME"); # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Tell the operator what I discovered ######################################################################## sub write_log { my $handle; my $total = @target; my $now = get_date(); # Debug trace trace_location('begin') if $debug; # If we are running in test mode, skip this routine unless ($dome) { print_it("Running in test mode, will not log"); return 1; } # Notify operator say 'Writing log...' if $mode eq 'interactive'; # Write data if (open my $log, '>>', $log_file) { print {$log} "$now\t$total_stations\n"; close $log or warn "Cannot close $log_file: $!"; } else { warn "Cannot open $log_file: $!"; } # Make things look pretty say('') if $mode eq 'interactive'; # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Output help ######################################################################## sub HELP_MESSAGE { print <