Files
netdisco/bin/netdisco-sshcollector
Oliver Gorwits d44a8f56ea Integrate netdisco-sshcollector into Worker::Plugin architecture (#489) (#535)
* 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:

commit 3fe8f383a7
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Mar 11 17:07:42 2019 +0000

    add debug lines and tested

commit 3249739e42
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Mar 11 16:54:11 2019 +0000

    change config name to get_credentials

commit e78558397a
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Mar 11 16:51:11 2019 +0000

    separate out generic device auth to DeviceAuth module

commit 249f05165f
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed Mar 6 18:43:31 2019 +0000

    release 2.040007

commit e3af64df77
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed Mar 6 18:42:47 2019 +0000

    #521-redux fix wifi date search

commit 48857ae300
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Mar 4 12:03:31 2019 +0000

    release 2.040006

commit e09dab5362
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Mar 4 11:39:12 2019 +0000

    #527 update List::MoreUtils version requirement

commit 6e7de3fff3
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Mar 4 09:59:41 2019 +0000

    release 2.040005

commit 0c98318a45
Author: Oliver Gorwits <oliver@spike.local>
Date:   Mon Mar 4 09:57:18 2019 +0000

    #526 fix discover syntax bug

commit e9efc45182
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun Mar 3 14:56:48 2019 +0000

    release 2.040004

commit 6cdfd80d10
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun Mar 3 14:34:00 2019 +0000

    allow undiscovered neighbors report to use discover_{waps,phones} setting

commit ac381e0802
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun Mar 3 14:13:20 2019 +0000

    #506 was a red herring

commit b83e614c85
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun Mar 3 13:00:36 2019 +0000

    make discover_{phones,waps} work with LLDP capabilities as well

commit 189d234b55
Author: 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

commit 9c956466f3
Author: 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

commit 09d29954d2
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun Mar 3 12:26:50 2019 +0000

    #512 fix regression in phone/wap discovery exclusion

commit 2bae91f1b6
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun Mar 3 12:01:34 2019 +0000

    rename match_devicetype() to match_to_setting()

commit 57cb6ddb70
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun Mar 3 09:19:39 2019 +0000

    fix for over-eager fix to #506

commit ef560fb59a
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Mar 2 22:41:40 2019 +0000

    #506 relax device renumber so it works for an alias

commit 7a8bcb094e
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Mar 2 22:23:39 2019 +0000

    #521 Search Node Date Range not working

commit a643820a62
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Mar 2 21:54:27 2019 +0000

    #428 Port-Channels not showing in netmap

commit 5ba5bcd295
Merge: e7aacddb a1f95028
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Mar 2 20:04:11 2019 +0000

    Merge branch 'master' of github.com:netdisco/netdisco

commit e7aacddbc6
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Mar 2 20:01:05 2019 +0000

    #498 Map with VLAN filter omits unconnected devices

commit a1f95028ca
Author: 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

commit ce1b847cea
Author: 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

commit 78e30a7926
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Mar 2 16:28:15 2019 +0000

    #500 filtering in device/ports on native vlan duplicates entries

commit 9952f0c6c7
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Mar 2 15:02:12 2019 +0000

    #499 netdisco-do renumber reports wrong ip (inphobia)

commit ca3fd8f466
Author: 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

commit 1265bc8470
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Mar 2 14:52:21 2019 +0000

    #520 catch slave ports defined without a master

commit d4c7579c10
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Mar 2 14:47:49 2019 +0000

    #522 TypeAhead.pm can reference empty data (inphobia)

commit 77decc23b7
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Mar 2 14:45:37 2019 +0000

    #514 inconsistent results in ip inventory (inphobia)

commit 3f211650b8
Author: 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
2019-03-12 18:50:24 +00:00

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