1package App::Netdisco::SSHCollector::Platform::ACE; 2 3=head1 NAME 4 5App::Netdisco::SSHCollector::Platform::ACE 6 7=head1 DESCRIPTION 8 9Collect ARP entries from Cisco ACE load balancers. ACEs have multiple 10virtual contexts with individual ARP tables. Contexts are enumerated 11with C<show context>, afterwards the commands C<changeto CONTEXTNAME> and 12C<show arp> must be executed for every context. 13 14The IOS shell does not permit to combine multiple commands in a single 15line, and Net::OpenSSH uses individual connections for individual commands, 16so we need to use Expect to execute the changeto and show commands in 17the same context. 18 19=cut 20 21use strict; 22use warnings; 23 24use Dancer ':script'; 25use Expect; 26use Moo; 27 28=head1 PUBLIC METHODS 29 30=over 4 31 32=item B<arpnip($host, $ssh)> 33 34Retrieve ARP entries from device. C<$host> is the hostname or IP address 35of the device. C<$ssh> is a Net::OpenSSH connection to the device. 36 37Returns a list of hashrefs in the format C<{ mac =E<gt> MACADDR, ip =E<gt> IPADDR }>. 38 39=back 40 41=cut 42 43sub arpnip{ 44 my ($self, $hostlabel, $ssh, $args) = @_; 45 46 debug "$hostlabel $$ arpnip()"; 47 48 my ($pty, $pid) = $ssh->open2pty; 49 unless ($pty) { 50 debug "unable to run remote command [$hostlabel] " . $ssh->error; 51 return (); 52 } 53 my $expect = Expect->init($pty); 54 55 my ($pos, $error, $match, $before, $after); 56 my $prompt = qr/#/; 57 58 ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt); 59 60 $expect->send("terminal length 0\n"); 61 ($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt); 62 63 $expect->send("show context | include Name\n"); 64 ($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt); 65 66 my @ctx; 67 my @arpentries; 68 69 for (split(/\n/, $before)){ 70 if (m/Name: (\S+)/){ 71 push(@ctx, $1); 72 $expect->send("changeto $1\n"); 73 ($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt); 74 $expect->send("show arp\n"); 75 ($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt); 76 for (split(/\n/, $before)){ 77 my ($ip, $mac) = split(/\s+/); 78 if ($ip =~ m/(\d{1,3}\.){3}\d{1,3}/ && $mac =~ m/[0-9a-f.]+/i) { 79 push(@arpentries, { ip => $ip, mac => $mac }); 80 } 81 } 82 83 } 84 } 85 86 $expect->send("exit\n"); 87 $expect->soft_close(); 88 89 return @arpentries; 90} 91 921; 93