diff --git a/Netdisco/Changes b/Netdisco/Changes index 708aa33b..2c3d3945 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -4,6 +4,7 @@ * [#123] Allow devices with no LLDP/CDP to be found as Nodes * Accept host names to netdisco-do show + * [#139] Add PaloAlto and IOS-XR support to netdisco-sshcollector [BUG FIXES] diff --git a/Netdisco/Makefile.PL b/Netdisco/Makefile.PL index 1e53763b..5f1aa25b 100644 --- a/Netdisco/Makefile.PL +++ b/Netdisco/Makefile.PL @@ -71,6 +71,7 @@ if ( $^O eq 'linux' ) { recommends 'Graph' => 0; recommends 'GraphViz' => 0; recommends 'Net::OpenSSH' => 0; +recommends 'Expect' => 0; install_share 'share'; diff --git a/Netdisco/bin/netdisco-sshcollector b/Netdisco/bin/netdisco-sshcollector index 38747b1b..033fea04 100644 --- a/Netdisco/bin/netdisco-sshcollector +++ b/Netdisco/bin/netdisco-sshcollector @@ -20,6 +20,10 @@ Currently, ARP tables can be retrieved from the following device classes: =item * L - Cisco IOS +=item * L - Cisco IOS XR + +=item * L - PaloAlto devices + =back The collected arp entries are then directly stored in the netdisco database. @@ -91,6 +95,8 @@ B and B datatypes in PostgreSQL can handle. =item L +=item L + =back =head1 COPYRIGHT AND LICENSE diff --git a/Netdisco/lib/App/Netdisco/SSHCollector/Platform/IOSXR.pm b/Netdisco/lib/App/Netdisco/SSHCollector/Platform/IOSXR.pm new file mode 100644 index 00000000..04404342 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/SSHCollector/Platform/IOSXR.pm @@ -0,0 +1,68 @@ +package App::Netdisco::SSHCollector::Platform::IOSXR; + +# vim: set expandtab tabstop=8 softtabstop=4 shiftwidth=4: + +=head1 NAME + +App::Netdisco::SSHCollector::Platform::IOSXR + +=head1 DESCRIPTION + +Collect ARP entries from Cisco IOS XR devices. + +=cut + +use strict; +use warnings; + +use Dancer ':script'; +use Data::Printer; +use Moo; +use Expect; + +=head1 PUBLIC METHODS + +=over 4 + +=item B + +Retrieve ARP entries from device. C<$host> is the hostname or IP address +of the device. C<$ssh> is a Net::OpenSSH connection to the device. + +Returns an array of hashrefs in the format { mac => MACADDR, ip => IPADDR }. + +=cut + +sub arpnip { + my ($self, $hostlabel, $ssh, @args) = @_; + + debug "$hostlabel $$ arpnip()"; + + my ($pty, $pid) = $ssh->open2pty or die "unable to run remote command"; + my $expect = Expect->init($pty); + + my ($pos, $error, $match, $before, $after); + my $prompt = qr/#/; + + ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt); + + $expect->send("terminal length 0\n"); + ($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt); + + my @arpentries; + + $expect->send("show arp vrf all\n"); + ($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt); + + # 0.0.0.0 00:00:00 0000.0000.0000 Dynamic ARPA GigabitEthernet0/0/0/0 + for (split(/\n/, $before)){ + my ($ip, $age, $mac, $state, $t, $iface) = split(/\s+/); + if ($ip =~ m/(\d{1,3}\.){3}\d{1,3}/ && $mac =~ m/[0-9a-f.]+/i) { + push(@arpentries, { ip => $ip, mac => $mac }); + } + } + + return @arpentries; +} + +1; diff --git a/Netdisco/lib/App/Netdisco/SSHCollector/Platform/PaloAlto.pm b/Netdisco/lib/App/Netdisco/SSHCollector/Platform/PaloAlto.pm new file mode 100644 index 00000000..67ba10dc --- /dev/null +++ b/Netdisco/lib/App/Netdisco/SSHCollector/Platform/PaloAlto.pm @@ -0,0 +1,66 @@ +package App::Netdisco::SSHCollector::Platform::PaloAlto; + +# vim: set expandtab tabstop=8 softtabstop=4 shiftwidth=4: + +=head1 NAME + +App::Netdisco::SSHCollector::Platform::PaloAlto + +=head1 DESCRIPTION + +Collect ARP entries from PaloAlto devices. + +=cut + +use strict; +use warnings; + +use Dancer ':script'; +use Data::Printer; +use Moo; +use Expect; + +=head1 PUBLIC METHODS + +=over 4 + +=item B + +Retrieve ARP entries from device. C<$host> is the hostname or IP address +of the device. C<$ssh> is a Net::OpenSSH connection to the device. + +Returns an array of hashrefs in the format { mac => MACADDR, ip => IPADDR }. + +=cut + +sub arpnip{ + my ($self, $hostlabel, $ssh, @args) = @_; + + debug "$hostlabel $$ arpnip()"; + + my ($pty, $pid) = $ssh->open2pty or die "unable to run remote command"; + my $expect = Expect->init($pty); + my ($pos, $error, $match, $before, $after); + my $prompt = qr/> \r?$/; + + ($pos, $error, $match, $before, $after) = $expect->expect(20, -re, $prompt); + $expect->send("set cli pager off\n"); + ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt); + $expect->send("show arp all\n"); + ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt); + + my @arpentries; + for (split(/\r\n/, $before)){ + next unless $_ =~ m/(\d{1,3}\.){3}\d{1,3}/; + my ($tmp, $ip, $mac) = split(/\s+/); + if ($ip =~ m/(\d{1,3}\.){3}\d{1,3}/ && $mac =~ m/([0-9a-f]{2}:){5}[0-9a-f]{2}/i) { + push(@arpentries, { ip => $ip, mac => $mac }); + } + } + $expect->send("exit\n"); + $expect->soft_close(); + + return @arpentries; +} + +1;