From 3d231ec135d83f6a2a8424d518aebc813c865145 Mon Sep 17 00:00:00 2001 From: antonc42 Date: Thu, 23 Jan 2020 09:06:06 -0600 Subject: [PATCH] new SSHCollector platform for Extreme VSP (#695) This module works for Extreme (formerly Avaya) VSP switches running the VOSS operating system. When using multiple VRFs, the default Arpnip does not work. The way to query the ARP tables of different VRFs is to append "::X" to the end of the SNMP community string, where "X" is the VRF ID number. Since Netdisco doesn't support querying with multiple community strings at this time, this module uses SSH to query the ARP tables of all the VRFs by default or select VRFs using the "vrfs" key in "device_auth" (see documentation in the module for an example). --- .../Netdisco/SSHCollector/Platform/VOSS.pm | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 lib/App/Netdisco/SSHCollector/Platform/VOSS.pm diff --git a/lib/App/Netdisco/SSHCollector/Platform/VOSS.pm b/lib/App/Netdisco/SSHCollector/Platform/VOSS.pm new file mode 100644 index 00000000..54e46878 --- /dev/null +++ b/lib/App/Netdisco/SSHCollector/Platform/VOSS.pm @@ -0,0 +1,134 @@ +package App::Netdisco::SSHCollector::Platform::VOSS; + +=head1 NAME + +App::Netdisco::SSHCollector::Platform::VOSS + +=head1 DESCRIPTION + +Collect ARP entries from Extreme VSP devices running the VOSS operating system. + +This is useful if running multiple VRFs as the built-in SNMP ARP collection will only fetch from the default GlobalRouter VRF. + +By default this module gets ARP entries from all VRFs (0-512). To specify only certain VRFs in the config: + + device_auth: + - tag: sshvsp + driver: cli + platform: VOSS + only: + - 10.1.1.1 + - 192.168.0.1 + username: oliver + password: letmein + vrfs: 1,5,100 + +The VRFs can be specified in any format that the "show ip arp vrfids" command will take. For example: + + 1,2,3,4,5,10 + 1-5,10 + 1-100 + 99 + +=cut + +use strict; +use warnings; + +use Dancer ':script'; +use Expect; +use Moo; + +=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 a list of hashrefs in the format C<{ mac =E MACADDR, ip =E IPADDR }>. + +=back + +=cut + +sub arpnip { + my ($self, $hostlabel, $ssh, $args) = @_; + + debug "$hostlabel $$ arpnip()"; + + # default to entire range of VRFs + my $vrflist = "0-512"; + # if specified in config, only get ARP from certain VRFs + if ($args->{vrfs}) { + if ($args->{vrfs} =~ m/^[0-9,\-]+$/) { + $vrflist = $args->{vrfs}; + } + } + + my ($pty, $pid) = $ssh->open2pty; + unless ($pty) { + debug "unable to run remote command [$hostlabel] " . $ssh->error; + return (); + } + my $expect = Expect->init($pty); + + my ($pos, $error, $match, $before, $after); + my $prompt; + + $prompt = qr/>/; + ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt); + + $expect->send("terminal more disable\n"); + ($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt); + + if ($before =~ m/% Invalid input detected/) { + debug "invalid command [$hostlabel]"; + return (); + } + + $expect->send("show ip arp vrfids $vrflist\n"); + ($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt); + my @lines = split(m/\n/, $before); + + if ($before =~ m/% Invalid input detected/) { + debug "invalid command [$hostlabel]"; + return (); + } + + if ($before =~ m/Error : ([^\n]+)/) { + my $errormsg = $1; + if ($errormsg =~ m/Invalid VRF ID/ || $errormsg =~ m/vrfId should be/) { + debug "incorrect VRF specified [$hostlabel] : $vrflist : $errormsg"; + return (); + } + else { + debug "general error fetching ARP [$hostlabel] : $errormsg"; + return (); + } + } + + my @arpentries; + + my $ipregex = '(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'; + my $macregex = '([[:xdigit:]]{2}:){5}[[:xdigit:]]{2}'; + + # IP Address MAC Address VLAN Port Type TTL Tunnel + # 172.16.20.15 0024.b269.867d 100 1/1 DYNAMIC 999 device-name + foreach my $line (@lines) { + next unless $line =~ m/^\s*$ipregex\s+$macregex/; + my @fields = split m/\s+/, $line; + + debug "[$hostlabel] arpnip - mac $fields[1] ip $fields[0]"; + push @arpentries, { mac => $fields[1], ip => $fields[0] }; + } + + $expect->send("exit\n"); + $expect->soft_close(); + + return @arpentries; +} + +1;