#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Carp; use Parse::CSV; use SNMP; use SNMP::Query::AsynchMulti; #--------------------------------------------------------- my $csv_file = shift || die "Please specify a CSV file with SNMP host info!"; my @reqired_fields = qw(HOSTIP COMMUNITY SNMPVER SNMPPORT); my @hosts = read_hosts_csv($csv_file, @reqired_fields); # This object encapsulates the desired queries to run. my $query = SNMP::Query::AsynchMulti->new(); foreach my $host (@hosts) { my $sess = SNMP::Session->new( DestHost => $host->{HOSTIP}, Community => $host->{COMMUNITY}, Version => $host->{SNMPVER}, RemotePort => $host->{SNMPPORT}, #Timeout => $host->{SNMP_TIMEOUT}, #Retries => $host->{SNMP_RETRIES}, ); my @varbinds = qw( ifDescr ifInOctets ifOutOctets ifAlias ifType ifName ifInErrors ifOutErrors ifSpeed ifAdminStatus ifOperStatus ); my $varlist = SNMP::VarList->new( map { [$_] } @varbinds ); $query->add({ Session => $sess, VarBinds => $varlist, QueryType => 'getbulk', # See POD for explanation... MaxRepeaters => 20, # Additional options depend on NonRepeaters => 0, # the QueryType... }); } $query->shuffle(); # Randomize order of queries...(not yet implemented) # Run all the added queries with up to X # asynchronous operations in-flight at any time. foreach my $iter (1..10) { sleep 30 unless $iter == 1; my $results = $query->execute({ InFlight => 30 }); print Dumper $results; } exit; #--------------------------------------------------------- # Read in the CSV file. sub read_hosts_csv { my $file = shift; my @required_fields = @_; # Parse entries from a CSV file into hashes hash my $csv_parser = Parse::CSV->new( file => $file, fields => 'auto', # Use the first line as column headers, # which become the hash keys. ); my @node_cfg; # Return a reference to this my $line_num = 0; while ( my $line = $csv_parser->fetch() ) { $line_num++; my $error_flag = 0; foreach my $field (@required_fields) { if ( ! exists $line->{$field} ) { $error_flag = 1; carp "Missing field [$field] on line [$line_num] in CSV file [$file]"; } } croak "Terminating due to errors on line [$line_num] in CSV file [$file]" if $error_flag; push @node_cfg, $line; } if ( $csv_parser->errstr() ) { croak "Fatal error parsing [$file]: " . $csv_parser->errstr(); } return @node_cfg; } 1; __END__ #### NODE_NAME,NODE_TYPE,COMMUNITY,HOSTIP,SNMPPORT,SNMPVER camel.mystuff.com,CPE-3810,foobar,10.1.15.62,161,2 somename.mystuff.com,CPE-2420,wheebaz,10.2.7.45,161,2 buhziuhuh.mystuff.com,CPE-2420,nipnop,10.2.7.40,161,2 salma-hayek.mystuff.com,CPE-2420,hummahumma,10.1.14.16,161,2 zippy.mystuff.com,CPE-2420,woo!woo,10.2.6.41,161,2 napoleon.mystuff.com,CPE-2420,vote4pedro,10.2.7.35,161,2 wjul.mystuff.com,CPE-3810,hoosurdaddy,10.1.15.72,161,2 telephone.mystuff.com,CPE-3660,yipyipyip,10.1.15.42,161,2 brrrrrring.mystuff.com,CPE-3660,uuuuuh-huh,10.1.15.73,161,2 #### #!/usr/bin/perl package SNMP::Query::AsynchMulti; use strict; use warnings; use Carp; use SNMP; sub new { my $class = shift; my $self = bless {}, $class; $self->{query_stack} = []; $self->{results} = []; $self->{max_in_flight} = 10; $self->{current_in_flight} = 0; $self->{total_query_count} = 0; return $self; } sub add { my $self = shift; my $params = shift; my $query_stack = $self->{query_stack}; my $sess = $params->{Session} || croak "Session parameter must be specified"; my $varbinds = $params->{VarBinds} || croak "VarBinds parameter must be specified"; my $query_type = $params->{QueryType} || 'getbulk'; if ($query_type eq 'getbulk') { my $non_repeaters = $params->{NonRepeaters} || 0; my $max_repeaters = $params->{MaxRepeaters} || 10; my $query = $self->_make_getbulk_query($sess, $varbinds, $non_repeaters, $max_repeaters); push @$query_stack, $query; } else { croak "Attempt to add using unknown query type: $query_type\n"; } return 1; } sub _make_getbulk_query { my $self = shift; my ($sess,$varbinds,$non_repeaters,$max_repeaters) = @_; return sub { my $callback = sub { my $bulkwalk_results = shift; push @{$self->{results}}, "Query Complete", $bulkwalk_results; $self->{current_in_flight}--; warn "Finished Query\n"; if (scalar @{$self->{query_stack}}) { my $next_query = pop @{$self->{query_stack}}; return $next_query->(); } return SNMP::finish() if $self->{current_in_flight} <= 0; }; $self->{current_in_flight}++; $self->{total_query_count}++; warn "Started query $self->{total_query_count}\n"; return $sess->bulkwalk($non_repeaters, $max_repeaters, $varbinds, [$callback]); }; } sub shuffle {} # TODO implement shuffle with List::Util or something sub execute { my $self = shift; my $params = shift; @{$self->{results}} = (); $self->{current_in_flight} = 0; my $max_in_flight = $params->{InFlight} || $self->{max_in_flight}; my $query_stack_ref = $self->{query_stack}; my @query_stack_copy = @{$self->{query_stack}}; while (scalar @$query_stack_ref) { my $query = pop @$query_stack_ref; $query->(); last if $self->{current_in_flight} >= $max_in_flight; } SNMP::MainLoop(); #use Data::Dumper; warn Dumper $self->{query_stack}, \@query_stack_copy; exit; $self->{query_stack} = \@query_stack_copy; return \@{$self->{results}}; } 1; __END__