#!/opt/vdops/bin/perl # This script queries Cisco devices for ENTITY-MIB items, producing a # CSV report # V Who When What # --------------------------------------------------------------------------- # 1.2.0 skendric 2011-02-21 Upgrade to Netops 1.4.0 # 1.1.0 skendric 2011-01-16 Add summary report # 1.0.0 skendric 2011-01-14 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 List::MoreUtils qw(any); use Text::Trim qw(trim); 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 %inventory_of; # Hash of hash refs, keyed by device name, storing # entPhysicalModelName and entPhysicalDescr along # with a count of each part my %total_of; # Count and entPhysicalDescr of parts, keyed by # entPhysicalModelName # Define global variables $program_name = 'inv-hardware'; $usage = 'Usage: inv-hardware -s {yes|no} [-d {integer}] [-r] [-a | -e {expr} | -f {filename} | target1 target2 target3 ...]'; $version = '1.1.0'; # 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 do_the_work(); # Do it print_csv(); # Write CSV output print_report(); # Print report } ##### End Main Program ################################################# ######################################################################## # 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 (%count_of, %descr_of, %name_of, $vb); say "Processing $target" if $debug > 1; # Grab entPhysicalModelName $vb = snmpWalk( {host => $target, oid => 'entPhysicalModelName'} ); # Extract interesting parts say " Extracting entPhysicalModelName" if $debug > 2; VARBIND: for my $varbind (@$vb) { my (@array, $iid, $name); $iid = $varbind->{iid}; $name = $varbind->{val}; next VARBIND unless defined $name;; $name =~ s/,//g; $name = trim $name; next VARBIND if $name eq $EMPTY_STR; @array = split '', $name; $name = 'Mangled' if any {ord $_ > 127} @array; # Non-printing characters $count_of{$name}++; $name_of{$iid} = $name; say " For name $name, iid = $iid" if $debug > 2; } # Grab entPhysicalDescr say " Extracting entPhysicalDescr" if $debug > 2; for my $iid (sort keys %name_of) { my ($name, $descr); $name = $name_of{$iid}; $descr = snmpGet( {host => $target, oid => "entPhysicalDescr.$iid"} ); $descr //= $QUERY; $descr =~ s/,//g; $descr = trim $descr; $descr =~ s/\( /(/; $descr =~ s/ \)/)/; say " For $name / $iid, descr = $descr" if $debug > 2; $descr_of{$name} = $descr; } # Store the results into global variables for my $name (keys %descr_of) { $inventory_of{$target}->{$name} = { Descr => $descr_of{$name}, Count => $count_of{$name}, }; $total_of{$name}->{Descr} = $descr_of{$name}; $total_of{$name}->{Count} += $count_of{$name}; } # 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 if (defined $csv_file) { open $handle, '>', $csv_file or warn "Cannot open $csv_file: $!\n"; } # Write CSV file if (defined fileno $handle) { say "Writing CSV File $csv_file" if $debug; print {$handle} "target, name, descr, count\n"; for my $target (@target) { my ($hash_ref, $name, $descr, $count); $hash_ref = $inventory_of{$target}; for my $name (keys %$hash_ref) { my ($count, $descr); $count = $hash_ref->{$name}->{'Count'}; $descr = $hash_ref->{$name}->{'Descr'}; print {$handle} "$target, $name, $descr, $count\n"; } } close $handle or warn "Cannot close $csv_file: $!\n"; } # 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(); # 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} <{Descr}, $total_of{$name}->{Count}; } 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; } ######################################################################## # Output help ######################################################################## sub HELP_MESSAGE { print <