* update changes and SNMP::Info dep * Integrate netdisco-sshcollector into Worker::Plugin architecture (#489) * Initial integration of sshcollector into Worker::Plugin architecture * add NodesBySSH.pm * update Build.PL and config.yml to integrate the new module * Further integration of sshcollector into Worker::Plugin architecture * added App::Netdisco::Transport::CLI loosely based on ::SNMP counterpart * switched to the more prevalent two-space tabs style * removed various TBD items, some new ones * Further steps to integration of sshcollector into Worker::Plugin architecture * cleaned up code * added various error handling * warning for bin/netdisco-sshcollector deprecation * device_auth allows passing master_opts to Net::OpenSSH * netdisco-do -D also toggles Net::OpenSSH debug * Merged NodesBySSH.pm into Nodes.pm * see https://github.com/netdisco/netdisco/pull/489#pullrequestreview-205603516 * Further integration of sshcollector into Worker::Plugin architecture * add snmp_arpnip_also option to sshcollector device_auth * cleanup code * Remove big TBD: comment from CLI.pm as doc is updated now * add transport/cli.pm to manifest * revert some changes to allow simpler merging * silent exit legacy script unless explicitly requested * move ssh code into Transport, part one * rewrite the CLI transport to provide an API * merge in og-get_external_credentials Squashed commit of the following: commit3fe8f383a7Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Mar 11 17:07:42 2019 +0000 add debug lines and tested commit3249739e42Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Mar 11 16:54:11 2019 +0000 change config name to get_credentials commite78558397aAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon Mar 11 16:51:11 2019 +0000 separate out generic device auth to DeviceAuth module commit249f05165fAuthor: Oliver Gorwits <oliver@cpan.org> Date: Wed Mar 6 18:43:31 2019 +0000 release 2.040007 commite3af64df77Author: Oliver Gorwits <oliver@cpan.org> Date: Wed Mar 6 18:42:47 2019 +0000 #521-redux fix wifi date search commit48857ae300Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Mar 4 12:03:31 2019 +0000 release 2.040006 commite09dab5362Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Mar 4 11:39:12 2019 +0000 #527 update List::MoreUtils version requirement commit6e7de3fff3Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Mar 4 09:59:41 2019 +0000 release 2.040005 commit0c98318a45Author: Oliver Gorwits <oliver@spike.local> Date: Mon Mar 4 09:57:18 2019 +0000 #526 fix discover syntax bug commite9efc45182Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 3 14:56:48 2019 +0000 release 2.040004 commit6cdfd80d10Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 3 14:34:00 2019 +0000 allow undiscovered neighbors report to use discover_{waps,phones} setting commitac381e0802Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 3 14:13:20 2019 +0000 #506 was a red herring commitb83e614c85Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 3 13:00:36 2019 +0000 make discover_{phones,waps} work with LLDP capabilities as well commit189d234b55Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 3 12:47:38 2019 +0000 check discover_no_type and friends earlier on in neighbors list build commit9c956466f3Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 3 12:32:07 2019 +0000 also update default config for new discover_phones and discover_waps settings commit09d29954d2Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 3 12:26:50 2019 +0000 #512 fix regression in phone/wap discovery exclusion commit2bae91f1b6Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 3 12:01:34 2019 +0000 rename match_devicetype() to match_to_setting() commit57cb6ddb70Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 3 09:19:39 2019 +0000 fix for over-eager fix to #506 commitef560fb59aAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 22:41:40 2019 +0000 #506 relax device renumber so it works for an alias commit7a8bcb094eAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 22:23:39 2019 +0000 #521 Search Node Date Range not working commita643820a62Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 21:54:27 2019 +0000 #428 Port-Channels not showing in netmap commit5ba5bcd295Merge:e7aacddba1f95028Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 20:04:11 2019 +0000 Merge branch 'master' of github.com:netdisco/netdisco commite7aacddbc6Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 20:01:05 2019 +0000 #498 Map with VLAN filter omits unconnected devices commita1f95028caAuthor: nick n <39005454+inphobia@users.noreply.github.com> Date: Sat Mar 2 19:54:22 2019 +0100 catch up with changes noticed that rc-sshcollector-core received updates to changes, add them here as well. didn't mention #499 & #522 commitce1b847ceaAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 18:47:44 2019 +0000 fix bug showing no nodes when only one matches in netmap commit78e30a7926Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 16:28:15 2019 +0000 #500 filtering in device/ports on native vlan duplicates entries commit9952f0c6c7Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 15:02:12 2019 +0000 #499 netdisco-do renumber reports wrong ip (inphobia) commitca3fd8f466Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 15:00:18 2019 +0000 #505 device renumber should update device port properties and device skips commit1265bc8470Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 14:52:21 2019 +0000 #520 catch slave ports defined without a master commitd4c7579c10Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 14:47:49 2019 +0000 #522 TypeAhead.pm can reference empty data (inphobia) commit77decc23b7Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Mar 2 14:45:37 2019 +0000 #514 inconsistent results in ip inventory (inphobia) commit3f211650b8Author: nick n <39005454+inphobia@users.noreply.github.com> Date: Fri Mar 1 12:34:42 2019 +0100 last pieces for db schema upgrade last piece of #510 * import legacy sshcollector config * add default use_legacy_sshcollector config * remove unneeded deps * various fixes and now tested * enable sshcollector platform tests
338 lines
8.7 KiB
Perl
Executable File
338 lines
8.7 KiB
Perl
Executable File
#!/usr/bin/env perl
|
|
|
|
use warnings;
|
|
use strict;
|
|
|
|
our $home;
|
|
|
|
BEGIN {
|
|
use FindBin;
|
|
FindBin::again();
|
|
|
|
$home = ($ENV{NETDISCO_HOME} || $ENV{HOME});
|
|
|
|
# try to find a localenv if one isn't already in place.
|
|
if (!exists $ENV{PERL_LOCAL_LIB_ROOT}) {
|
|
use File::Spec;
|
|
my $localenv = File::Spec->catfile($FindBin::RealBin, 'localenv');
|
|
exec($localenv, $0, @ARGV) if -f $localenv;
|
|
$localenv = File::Spec->catfile($home, 'perl5', 'bin', 'localenv');
|
|
exec($localenv, $0, @ARGV) if -f $localenv;
|
|
|
|
die "Sorry, can't find libs required for App::Netdisco.\n"
|
|
if !exists $ENV{PERLBREW_PERL};
|
|
}
|
|
}
|
|
|
|
BEGIN {
|
|
use Path::Class;
|
|
|
|
# stuff useful locations into @INC and $PATH
|
|
unshift @INC,
|
|
dir($FindBin::RealBin)->parent->subdir('lib')->stringify,
|
|
dir($FindBin::RealBin, 'lib')->stringify;
|
|
|
|
unshift @INC,
|
|
split m/:/, ($ENV{NETDISCO_INC} || '');
|
|
|
|
use Config;
|
|
$ENV{PATH} = $FindBin::RealBin . $Config{path_sep} . $ENV{PATH};
|
|
}
|
|
|
|
use App::Netdisco;
|
|
use App::Netdisco::Util::Node qw/check_mac store_arp/;
|
|
use App::Netdisco::Util::FastResolver 'hostnames_resolve_async';
|
|
use Dancer ':script';
|
|
|
|
use Data::Printer;
|
|
use Module::Load ();
|
|
use Net::OpenSSH;
|
|
use MCE::Loop Sereal => 1;
|
|
use Pod::Usage 'pod2usage';
|
|
|
|
use Getopt::Long;
|
|
Getopt::Long::Configure ("bundling");
|
|
|
|
my ($debug, $sqltrace, $device, $opensshdebug, $workers) = (undef, 0, undef, undef, "auto");
|
|
my $result = GetOptions(
|
|
'debug|D' => \$debug,
|
|
'sqltrace|Q' => \$sqltrace,
|
|
'device|d=s' => \$device,
|
|
'opensshdebug|O' => \$opensshdebug,
|
|
'workers|w=i' => \$workers,
|
|
) or pod2usage(
|
|
-msg => 'error: bad options',
|
|
-verbose => 0,
|
|
-exitval => 1,
|
|
);
|
|
|
|
my $CONFIG = config();
|
|
$CONFIG->{logger} = 'console';
|
|
$CONFIG->{log} = ($debug ? 'debug' : 'info');
|
|
$ENV{DBIC_TRACE} ||= $sqltrace;
|
|
|
|
# reconfigure logging to force console output
|
|
Dancer::Logger->init('console', $CONFIG);
|
|
|
|
# silent exit unless explicitly requested
|
|
exit(0) unless setting('use_legacy_sshcollector');
|
|
|
|
if ($opensshdebug){
|
|
$Net::OpenSSH::debug = ~0;
|
|
}
|
|
|
|
MCE::Loop::init { chunk_size => 1, max_workers => $workers };
|
|
my %stats;
|
|
$stats{entry} = 0;
|
|
|
|
exit main();
|
|
|
|
sub main {
|
|
my @input = @{ setting('sshcollector') };
|
|
|
|
if ($device){
|
|
@input = grep{ ($_->{hostname} && $_->{hostname} eq $device)
|
|
|| ($_->{ip} && $_->{ip} eq $device) } @input;
|
|
}
|
|
|
|
#one-line Fisher-Yates from https://www.perlmonks.org/index.pl?node=Array%20One-Liners
|
|
my ($i,$j) = (0);
|
|
@input[-$i,$j] = @input[$j,-$i] while $j = rand(@input - $i), ++$i < @input;
|
|
|
|
my @mce_result = mce_loop {
|
|
my ($mce, $chunk_ref, $chunk_id) = @_;
|
|
my $host = $chunk_ref->[0];
|
|
|
|
my $hostlabel = (!defined $host->{hostname} or $host->{hostname} eq "-")
|
|
? $host->{ip} : $host->{hostname};
|
|
|
|
if ($hostlabel) {
|
|
my $ssh = Net::OpenSSH->new(
|
|
$hostlabel,
|
|
user => $host->{user},
|
|
password => $host->{password},
|
|
timeout => 30,
|
|
async => 0,
|
|
default_stderr_file => '/dev/null',
|
|
master_opts => [
|
|
-o => "StrictHostKeyChecking=no",
|
|
-o => "BatchMode=no"
|
|
],
|
|
);
|
|
|
|
|
|
if ($ssh->error){
|
|
warning "WARNING: Couldn't connect to <$hostlabel> - " . $ssh->error;
|
|
}else{
|
|
MCE->gather( process($hostlabel, $ssh, $host) );
|
|
}
|
|
}
|
|
} \@input;
|
|
|
|
return 0 unless scalar @mce_result;
|
|
|
|
foreach my $host (@mce_result) {
|
|
$stats{host}++;
|
|
info sprintf ' [%s] arpnip - retrieved %s entries',
|
|
$host->[0], scalar @{$host->[1]};
|
|
store_arpentries($host->[1]);
|
|
}
|
|
|
|
info sprintf 'arpnip - processed %s ARP Cache entries from %s devices',
|
|
$stats{entry}, $stats{host};
|
|
return 0;
|
|
}
|
|
|
|
sub process {
|
|
my ($hostlabel, $ssh, $args) = @_;
|
|
|
|
my $class = "App::Netdisco::SSHCollector::Platform::".$args->{platform};
|
|
Module::Load::load $class;
|
|
|
|
my $device = $class->new();
|
|
my $arpentries = [ $device->arpnip($hostlabel, $ssh, $args) ];
|
|
|
|
# debug p $arpentries;
|
|
if (not scalar @$arpentries) {
|
|
warning "WARNING: no entries received from <$hostlabel>";
|
|
}
|
|
hostnames_resolve_async($arpentries);
|
|
return [$hostlabel, $arpentries];
|
|
}
|
|
|
|
sub store_arpentries {
|
|
my ($arpentries) = @_;
|
|
|
|
foreach my $arpentry ( @$arpentries ) {
|
|
# skip broadcast/vrrp/hsrp and other wierdos
|
|
next unless check_mac( $arpentry->{mac} );
|
|
|
|
debug sprintf ' arpnip - stored entry: %s / %s',
|
|
$arpentry->{mac}, $arpentry->{ip};
|
|
store_arp({
|
|
node => $arpentry->{mac},
|
|
ip => $arpentry->{ip},
|
|
dns => $arpentry->{dns},
|
|
});
|
|
|
|
$stats{entry}++;
|
|
}
|
|
}
|
|
|
|
=head1 NAME
|
|
|
|
netdisco-sshcollector - Collect ARP data for Netdisco from devices without
|
|
full SNMP support
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
# install dependencies:
|
|
~/bin/localenv cpanm --notest Net::OpenSSH Expect
|
|
|
|
# run manually, or add to cron:
|
|
~/bin/netdisco-sshcollector [-DQO] [-w <max_workers>]
|
|
|
|
# limit run to a single device defined in the config
|
|
~/bin/netdisco-sshcollector [-DQO] [-w <max_workers>] -d <device>
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Collects ARP data for Netdisco from devices without full SNMP support.
|
|
Currently, ARP tables can be retrieved from the following device classes:
|
|
|
|
=over 4
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::GAIAEmbedded> - Check Point GAIA Embedded
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::CPVSX> - Check Point VSX
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::ACE> - Cisco ACE
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::ASA> - Cisco ASA
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::IOS> - Cisco IOS
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::IOSXR> - Cisco IOS XR
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::NXOS> - Cisco NXOS
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::BigIP> - F5 Networks BigIP
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::FreeBSD> - FreeBSD
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::Linux> - Linux
|
|
|
|
=item * L<App::Netdisco::SSHCollector::Platform::PaloAlto> - Palo Alto
|
|
|
|
=back
|
|
|
|
The collected arp entries are then directly stored in the netdisco database.
|
|
|
|
=head1 CONFIGURATION
|
|
|
|
The following should go into your Netdisco configuration file,
|
|
F<~/environments/deployment.yml>.
|
|
|
|
=over 4
|
|
|
|
=item C<sshcollector>
|
|
|
|
Data is collected from the machines specified in this setting. The format is a
|
|
list of dictionaries. The keys C<ip>, C<user>, C<password>, and C<platform>
|
|
are required. Optionally the C<hostname> key can be used instead of the
|
|
C<ip>. For example:
|
|
|
|
sshcollector:
|
|
- ip: '192.0.2.1'
|
|
user: oliver
|
|
password: letmein
|
|
platform: IOS
|
|
- hostname: 'core-router.example.com'
|
|
user: oliver
|
|
password:
|
|
platform: IOS
|
|
|
|
Platform is the final part of the classname to be instantiated to query the
|
|
host, e.g. platform B<ACE> will be queried using
|
|
C<App::Netdisco::SSHCollector::Platform::ACE>.
|
|
|
|
If the password is blank, public key authentication will be attempted with the
|
|
default key for the netdisco user. Password protected keys are currently not
|
|
supported.
|
|
|
|
=back
|
|
|
|
=head1 ADDING DEVICES
|
|
|
|
Additional device classes can be easily integrated just by adding and
|
|
additonal class to the C<App::Netdisco::SSHCollector::Platform> namespace.
|
|
This class must implement an C<arpnip($hostname, $ssh)> method which returns
|
|
an array of hashrefs in the format
|
|
|
|
@result = ({ ip => IPADDR, mac => MACADDR }, ...)
|
|
|
|
The parameter C<$ssh> is an active C<Net::OpenSSH> connection to the host.
|
|
Depending on the target system, it can be queried using simple methods like
|
|
|
|
my @data = $ssh->capture("show whatever")
|
|
|
|
or automated via Expect - this is mostly useful for non-Linux appliances which
|
|
don't support command execution via ssh:
|
|
|
|
my ($pty, $pid) = $ssh->open2pty;
|
|
unless ($pty) {
|
|
debug "unable to run remote command [$hostlabel] " . $ssh->error;
|
|
return ();
|
|
}
|
|
my $expect = Expect->init($pty);
|
|
my $prompt = qr/#/;
|
|
my ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
|
|
$expect->send("terminal length 0\n");
|
|
# etc...
|
|
|
|
The returned IP and MAC addresses should be in a format that the respective
|
|
B<inetaddr> and B<macaddr> datatypes in PostgreSQL can handle.
|
|
|
|
=head1 COMMAND LINE OPTIONS
|
|
|
|
=over 4
|
|
|
|
=item C<-D>
|
|
|
|
Netdisco debug log level.
|
|
|
|
=item C<-Q>
|
|
|
|
L<DBIx::Class> trace enabled.
|
|
|
|
=item C<-O>
|
|
|
|
L<Net::OpenSSH> trace enabled.
|
|
|
|
=item C<-w>
|
|
|
|
Set maximum parallel workers for L<MCE::Loop>. The default is B<auto>.
|
|
|
|
=item C<-d device>
|
|
|
|
Only run for a single device. Takes an IP or hostname, must exactly match the
|
|
value in the config file.
|
|
|
|
=back
|
|
|
|
=head1 DEPENDENCIES
|
|
|
|
=over 4
|
|
|
|
=item L<App::Netdisco>
|
|
|
|
=item L<Net::OpenSSH>
|
|
|
|
=item L<Expect>
|
|
|
|
=item L<http://www.openssh.com/>
|
|
|
|
=back
|
|
|
|
=cut
|