#!/opt/vdops/bin/perl # This script queries network devices for chassis serial numbers # and produces a report # V Who When What # --------------------------------------------------------------------------- # 1.4.0 skendric 2011-02-21 Upgrade to Netops 1.4.0 # 1.3.0 skendric 2010-06-25 Support stacked Cat3750 # 1.2.1 skendric 2010-06-17 Use entPhysicalSerialNum.10 for Nexi # 1.2.0 skendric 2010-01-10 Upgrade to perl 5.10.1 # 1.1.1 skendric 2008-01-08 Support Cisco Communications Manager # 1.1.0 skendric 2008-12-13 Update names & buildings # 1.0.9 skendric 2008-01-02 Fix default Cisco path # 1.0.8 skendric 2007-12-16 Add default Cisco path # 1.0.7 skendric 2007-10-21 Add Cisco ASA support # 1.0.6 skendric 2007-03-21 Stylistic mods # 1.0.5 skendric 2006-12-05 Add support for more Cisco gear # 1.0.4 skendric 2006-11-29 Replace Object Values with OIDs # 1.0.3 skendric 2006-07-14 Replace custom chassis string with sysObjectID # 1.0.2 skendric 2006-07-14 Add support for more hardware # 1.0.1 skendric 2006-03-21 Add 'owner' # 1.0.0 skendric 2006-03-07 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) # -Queries a bunch of enterprise-specific variables # -Produces a report # # # Requirements: # -The target(s) must be pingable # # -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 WI::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 # -Try it out # # # Caveats: # # # Known Bugs: # # # To do: # -Add support for SNMPv3 # # # Begin script # Load modules 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 WI::Netops::CiscoTools 1.4.3; use WI::Netops::HostTools 1.0.4; use WI::Netops::NetopsTools 2.2.3; use WI::Netops::NetopsData 1.4.0; use WI::Netops::PingTools 1.1.7; use WI::Netops::SNMPTools 1.5.3; use WI::Netops::Utilities 1.4.4; # Declare global variables my $csv_file; # CSV-formatted output my %chassis_model; # Hash of array refs, keyed by target my %chassis_serial_number; # Hash of array refs, keyed by target my %owner; # WI or SCCA, keyed by target # Define global variables $program_name = 'chassis-serial-num'; $usage = 'Usage: chassis-serial-num -s {yes|no} [-d {integer}] [-r] [-a | -e {expr} | -f {filename} | target1 target2 target3 ...]'; $version = '1.4.0'; # Report stuff $csv_file = '/home/netops/rpts/chassis-serial-num.csv'; # 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 Netops config file compile_mibs(); # Compile MIB files build_target(); # Populate @target target_check(); # Look for errors in @target basic_info(); # Gather information assign_owner(); # Assign the owning institution do_the_work(); # Do it print_report(); # Print report print_csv(); # Write CSV output } ##### End Main Program ################################################# ######################################################################## # Populate the %owner hash, using naming convetion. This routine # contains locally relevant information ######################################################################## sub assign_owner { # Debug trace trace_location('begin') if $debug; # Loop through targets, using name to guess owner for my $target (@target) { given ($target) { when (/hutch/) { $owner{$target} = 'WI' } when (/^border|^cm|^e|^g|^lg|colo|scca/) { $owner{$target} = 'SCCA' } when ('mmz-b-rtr') { $owner{$target} = 'SCCA' } when ('gigapop-b-rtr') { $owner{$target} = 'SCCA' } default { $owner{$target} = 'WI' } } } # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Query variables ######################################################################## sub do_the_work { # Debug trace trace_location('begin') if $debug; # Notify operator print_it('Querying targets...'); unless ($dome) { sleep $short; return 1; } # Loop through the list of targets for my $target (@target) { my (@chassis, @serial, $serial); # Acquire serial number given ($box{$target}) { my (%arg, $vb); # Brocade when (/silk/i) { $serial = snmpGet( {host => $target, oid => 'entPhysicalSerialNum.1'} ); } # Nokia when (/IP/i) { $serial = snmpGet( {host=>$target, oid=>'ipsoChassisSerialNumber.0'} ); } # catalyst4948 when (/cat49/) { $serial = snmpGet( {host=>$target, oid=>'entPhysicalSerialNum.1000'} ); } # catalyst37xxStack when (/cat356|cat37|nme/i) { # Acquire serial numbers $vb = snmpWalk( {host => $target, oid => 'moduleSerialNumberString'} ); for my $varbind (@$vb) { push @serial, $varbind->{val}; } # Acquire chassis models $vb = snmpWalk( {host => $target, oid => 'moduleModel'} ); for my $varbind (@$vb) { push @chassis, $varbind->{val}; } } # cisco7206 when (/cisco72/) { $serial = snmpGet( {host => $target, oid => 'entPhysicalDescr.1'} ); ($serial) = ($serial =~ /Serial#: (\d+),/) if defined $serial; } # Cisco WAP when (/air/i) { $serial = snmpGet( host => $target, oid => 'ceAssetSerialNumber.1' ); } # CatOS when (/cat/) { if ($os_flavor{$target} eq 'CatOS') { %arg = ( host => $target, oid => 'chassisSerialNumberString.0' ); $serial = snmpGet(\%arg); } else { say 'Getting entPhysicalSerialNum.1' if $debug > 3; %arg = ( host => $target, oid => 'entPhysicalSerialNum.1' ); $serial = snmpGet(\%arg); } } # Nexi when (/N5k/) { $serial = snmpGet({host => $target, oid => 'entPhysicalSerialNum.10'}); } # Typical routers when (/cisco/) { say 'Getting OLD-CISCO-CHASSIS-MIB::chassisId.0' if $debug > 3; $serial = snmpGet( {host => $target, oid => 'chassisId.0'} ); ($serial) = ($serial =~ /^(\w+)/) if defined $serial; } # Cisco Communications Manager boxes when (/MCS/) { $serial = snmpGet( {host => $target, oid => 'cpqSiSysSerialNum.0'} ); } # Modern Cisco boxes default { $serial = snmpGet( {host => $target, oid => 'entPhysicalSerialNum.1'} ); } } # Store the results into global variables if (@chassis > 0) { $chassis_model{$target} = \@chassis; $chassis_serial_number{$target} = \@serial; } else { $serial //= 'X'; ($serial) = ($serial =~ /^(\w+)/); $chassis_model{$target} = [ $box{$target} ]; $chassis_serial_number{$target} = [ $serial ]; } # Entertain the operator print $BANG if $mode eq 'interactive'; } # End 'Loop through list of targets' # Make things look pretty say('') if $mode eq 'interactive'; # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Produce a CSV-formatted report ######################################################################## sub print_csv { my $handle; # 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; } # Debug trace trace_location('begin') if $debug; # Direct output to file open $handle, '>', $csv_file or die "Cannot open $csv_file: $!\n"; for my $target (@target) { for (my $i = 0; $i < @{$chassis_model{$target}}; $i++) { my ($chassis, $serial); $chassis = ${$chassis_model{$target}}[$i]; $serial = ${$chassis_serial_number{$target}}[$i]; print {$handle} "$target, $manufacturer{$target}, $chassis, $serial, $owner{$target}\n"; } } close $handle or warn "Cannot close $csv_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 print_report { my $handle; my $j; # Iterates across modules my $total = @target; my $now = get_now(); # 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; } # Debug trace trace_location('begin') if $debug; # Direct output to screen or to file if ($mode eq 'report' or $mode eq 'batch') { open $handle, ">", $report_file or die "Cannot open $report_file: $!\n"; } else { $handle = *STDOUT; } print {$handle} <