view release on metacpan or search on metacpan
[NEW FEATURES]
* Finally we have a discover/refresh/arpnip/macsuck daemon jobs :)
* Also... a Scheduler which removes need for crontab installation
* The netdisco-do script can queue any one-off job
* Select MAC Address display format on Node and Device Port search
* Helper script to import the Netdisco 1.x Topology file to the database
* Support for pseudo devices (useful for dummy device links)
* Manual Topology editing via the web
* Job Queue view and delete page
* Empty device table prompts initial discover on homepage
* Support for App::NetdiscoX::Web::Plugin namespace
* Plugins can add columns to Device Ports display
* Observium Sparklines port column plugin
* Plugins can have CSS and Javascript loaded within <head>
[ENHANCEMENTS]
* SNMP connection now uses cached info if available
* Better handling of MIBs home location in deploy script
* Port filter in device port display is now highlighted green
[ENHANCEMENTS]
* Device Port filter by vlan includes both tagging and untagged (closes #37)
* Change colour and name of Port Control toggle option in Device Port View
* Incorporate latest DB schema changes from upstream
* Add some doc clarifcations
[BUG FIXES]
* No longer prompt for demo/demo credentials (closes #38) (J. Binks)
* Display IP in Vlan search if device has no DNS (closes #36) (J. Binks)
2.004000 - 2013-01-14
[BUG FIXES]
* Very naughty typo in netmap code, which I forgot to merge a fix for.
* Typo in installation docs (S. Gupta)
2.002000 - 2013-01-06
bin/netdisco-deploy view on Meta::CPAN
say 'https://github.com/netdisco/netdisco/wiki/Release-Notes';
say 'There you will find required and incompatible changes';
say 'which are not covered by this script.';
say '';
say 'You will be asked to confirm all changes to your system.';
say '';
print color 'reset';
my $term = Term::ReadLine->new('netdisco');
my $bool = $term->ask_yn(
prompt => 'So, is all of the above in place?', default => 'n',
);
exit(0) unless $bool;
say '';
$bool = $term->ask_yn(
prompt => 'Would you like to deploy the database schema?', default => 'n',
);
deploy_db() if $bool;
say '';
$bool = $term->ask_yn(
prompt => 'Download and update vendor MAC prefixes (OUI data)?', default => 'n',
);
deploy_oui() if $bool;
say '';
my $default_mibhome = dir($home, 'netdisco-mibs');
if (setting('mibhome') and setting('mibhome') ne $default_mibhome) {
my $mibhome = $term->get_reply(
print_me => "MIB home options:",
prompt => "Download and update MIB files to...?",
choices => [setting('mibhome'), $default_mibhome, 'Skip this.'],
default => 'Skip this.',
);
deploy_mibs($mibhome) if $mibhome and $mibhome ne 'Skip this.';
}
else {
$bool = $term->ask_yn(
prompt => "Download and update MIB files?", default => 'n',
);
deploy_mibs($default_mibhome) if $bool;
}
sub deploy_db {
system('netdisco-db-deploy') == 0 or die "\n";
print color 'bold blue';
say 'DB schema update complete.';
print color 'reset';
bin/netdisco-deploy view on Meta::CPAN
# set initial dancer web session cookie key
schema('netdisco')->resultset('Session')->find_or_create(
{id => 'dancer_session_cookie_key', a_session => \'md5(random()::text)'},
{key => 'primary'},
);
}
sub get_userpass {
my $upterm = shift;
my $name = $upterm->get_reply(prompt => 'Username: ');
my $pass = $upterm->get_reply(prompt => 'Password: ');
unless ($name and $pass) {
say 'username and password cannot be empty, please try again.';
($name, $pass) = get_userpass($upterm);
}
return ($name, $pass);
}
sub deploy_oui {
bin/netdisco-sshcollector view on Meta::CPAN
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
lib/App/Netdisco/SSHCollector/Platform/ACE.pm view on Meta::CPAN
debug "$hostlabel $$ arpnip()";
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 = 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);
$expect->send("show context | include Name\n");
($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
my @ctx;
my @arpentries;
for (split(/\n/, $before)){
if (m/Name: (\S+)/){
push(@ctx, $1);
$expect->send("changeto $1\n");
($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
$expect->send("show arp\n");
($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
for (split(/\n/, $before)){
my ($ip, $mac) = split(/\s+/);
if ($ip =~ m/(\d{1,3}\.){3}\d{1,3}/ && $mac =~ m/[0-9a-f.]+/i) {
push(@arpentries, { ip => $ip, mac => $mac });
}
}
}
}
lib/App/Netdisco/SSHCollector/Platform/ASA.pm view on Meta::CPAN
debug "$hostlabel $$ arpnip()";
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;
if ($args->{enable_password}) {
$prompt = qr/>/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send("enable\n");
$prompt = qr/Password:/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send( $args->{enable_password} ."\n" );
}
$prompt = qr/#\s*$/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send("terminal pager 2147483647\n");
($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
$expect->send("show names\n");
($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
my @names = split(m/\n/, $before);
$expect->send("show arp\n");
($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
my @lines = split(m/\n/, $before);
my @arpentries = ();
# ifname 192.0.2.1 0011.2233.4455 123
my $linereg = qr/[A-z0-9\-\.]+\s([A-z0-9\-\.]+)\s
([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})/x;
foreach my $line (@lines) {
if ($line =~ $linereg) {
lib/App/Netdisco/SSHCollector/Platform/ASA.pm view on Meta::CPAN
}
}
if ($ip =~ m/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) {
push @arpentries, { mac => $mac, ip => $ip };
}
}
}
# start ipv6
$expect->send("show ipv6 neighbor\n");
($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
@lines = split(m/\n/, $before);
# IPv6 age MAC state ifname
$linereg = qr/([0-9a-fA-F\:]+)\s+[0-9]+\s
([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})/x;
foreach my $line (@lines) {
if ($line =~ $linereg) {
my ($ip, $mac) = ($1, $2);
lib/App/Netdisco/SSHCollector/Platform/ASAContext.pm view on Meta::CPAN
debug "$hostlabel $$ arpnip()";
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;
if ($args->{enable_password}) {
$prompt = qr/>/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send("enable\n");
$prompt = qr/Password:/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send( $args->{enable_password} ."\n" );
}
$prompt = qr/#\s*$/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send("terminal pager 2147483647\n");
($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
$expect->send("changeto system\n");
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send("show run | include ^context\n");
($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
my @contexts = split(m/\n/, $before);
my @arpentries = ();
for (my $i = 1; $i < scalar @contexts; $i++) {
$contexts[$i] =~ s/context //;
debug "$hostlabel/$contexts[$i]";
$expect->send("changeto context $contexts[$i]\n");
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send("show names\n");
($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
my @names = split(m/\n/, $before);
$expect->send("terminal pager 2147483647\n");
($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
$expect->send("show arp\n");
($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
my @lines = split(m/\n/, $before);
# ifname 192.0.2.1 0011.2233.4455 123
my $linereg = qr/[A-z0-9\-\.]+\s([A-z0-9\-\.]+)\s
([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})/x;
foreach my $line(@lines) {
if ($line =~ $linereg) {
my ($ip, $mac) = ($1, $2);
lib/App/Netdisco/SSHCollector/Platform/ASAContext.pm view on Meta::CPAN
}
}
if ($ip =~ m/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) {
push @arpentries, { mac => $mac, ip => $ip };
}
}
}
# start ipv6
$expect->send("show ipv6 neighbor\n");
($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
@lines = split(m/\n/, $before);
# IPv6 age MAC state ifname
$linereg = qr/([0-9a-fA-F\:]+)\s+[0-9]+\s
([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})/x;
foreach my $line (@lines) {
if ($line =~ $linereg) {
my ($ip, $mac) = ($1, $2);
lib/App/Netdisco/SSHCollector/Platform/ArubaCX.pm view on Meta::CPAN
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 = qr/#/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send("no page\n");
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send("show arp all-vrfs\n");
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
my @lines= split(m/\n/,$before);
$expect->send("exit\n");
$expect->soft_close();
my @arpentries = ();
# Example output from 'show arp':
#
# IPv4 Address MAC Port Physical Port State
lib/App/Netdisco/SSHCollector/Platform/ArubaCont.pm view on Meta::CPAN
debug "$hostlabel arpnip() - Starting ARP collection for Aruba Controller";
# Open pseudo-terminal
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/#/; # Adjust to match the Aruba controller prompt
# Log into the controller and disable paging
$expect->expect(10, -re, $prompt);
$expect->send("no paging\n");
$expect->expect(10, -re, $prompt);
# Send 'show arp' command
$expect->send("show arp\n");
my ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
# Parse the ARP output
my @data = split "\n", $before;
my @arpentries;
# Example regex matching for controller ARP output
foreach my $line (@data) {
if ($line =~ /(\d+\.\d+\.\d+\.\d+)\s+([\da-f:]+)\s+(vlan\d+)/) {
push @arpentries, { ip => $1, mac => $2, port => $3 };
debug "$hostlabel - Parsed ARP entry: IP=$1, MAC=$2, Port=$3";
lib/App/Netdisco/SSHCollector/Platform/CPVSX.pm view on Meta::CPAN
debug "$hostlabel $$ arpnip()";
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);
# TODO: check CP os/version via "cpstat os" and VSX status via "show vsx"
# $expect->send("show vsx\n");
# ($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
# debug "$hostlabel $$ show vsx: $before";
# Enumerate virtual systems
# Virtual systems list
# VS ID VS NAME
# 0 0
# 1 BACKUP-VSX_xxxxxx_Context
# ...
$expect->send("show virtual-system all\n");
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
my @vsxentries = ();
my @lines = split(m/\n/, $before);
my $linereg = qr/(\d+)\s+([A-Za-z0-9_-]+)/;
foreach my $line (@lines) {
if ($line =~ $linereg) {
my ($vsid, $vsname) = ($1, $2);
push @vsxentries, { vsid => $vsid, vsname=> $vsname };
debug "$hostlabel $$ $vsid, $vsname";
lib/App/Netdisco/SSHCollector/Platform/CPVSX.pm view on Meta::CPAN
}
# TODO:
# Expert mode should be used only for pre-R77.30 versions
# For R77.30 and later we can use:
# set virtual-system $vsid
# show arp dynamic all
$expect->send("expert\n");
$prompt = qr/Enter expert password:/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send( $args->{expert_password} ."\n" );
$prompt = qr/#/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
foreach (@vsxentries) {
my $vsid = $_->{vsid};
debug "$hostlabel $$ arpnip VSID: $vsid";
$expect->send("vsenv $vsid\n");
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send("arp -n | tail -n +2\n");
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
@lines = split(m/\n/, $before);
# 192.168.1.1 ether 00:b6:aa:f5:bb:6e C eth1
$linereg = qr/([0-9\.]+)\s+ether\s+([a-fA-F0-9:]+)/;
foreach my $line (@lines) {
if ($line =~ $linereg) {
my ($ip, $mac) = ($1, $2);
push @arpentries, { mac => $mac, ip => $ip };
debug "$hostlabel $$ arpnip VSID: $vsid IP: $ip MAC: $mac";
}
}
}
$expect->send("exit\n");
$prompt = qr/>/;
($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
$expect->send("exit\n");
$expect->soft_close();
return @arpentries;
}
1;
lib/App/Netdisco/SSHCollector/Platform/FTD.pm view on Meta::CPAN
debug "$hostlabel $$ arpnip()";
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;
if ($args->{enable_password}) {
$prompt = qr/>/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send("enable\n");
$prompt = qr/Password:/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
$expect->send( $args->{enable_password} ."\n" );
}
$prompt = qr/>\s*$/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
# $expect->send("terminal pager 2147483647\n");
# ($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
# $expect->send("show names\n");
# ($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
# my @names = split(m/\n/, $before);
$expect->send("show arp\n");
($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
my @lines = split(m/\n/, $before);
my @arpentries = ();
# ifname 192.0.2.1 0011.2233.4455 123
my $linereg = qr/[A-z0-9\-\.]+\s([A-z0-9\-\.]+)\s
([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})/x;
foreach my $line (@lines) {
if ($line =~ $linereg) {
lib/App/Netdisco/SSHCollector/Platform/FTD.pm view on Meta::CPAN
# }
}
if ($ip =~ m/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) {
push @arpentries, { mac => $mac, ip => $ip };
}
}
}
# start ipv6
$expect->send("show ipv6 neighbor\n");
($pos, $error, $match, $before, $after) = $expect->expect(60, -re, $prompt);
@lines = split(m/\n/, $before);
# IPv6 age MAC state ifname
$linereg = qr/([0-9a-fA-F\:]+)\s+[0-9]+\s
([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})/x;
foreach my $line (@lines) {
if ($line =~ $linereg) {
my ($ip, $mac) = ($1, $2);
lib/App/Netdisco/SSHCollector/Platform/FortiOS.pm view on Meta::CPAN
of the device. C<$ssh> is a Net::OpenSSH connection to the device.
If a post-login banner needs to be accepted, please set C<$banner> to true.
Returns a list of hashrefs in the format C<< { mac => MACADDR, ip => IPADDR } >>.
=back
=cut
my $prompt = qr/ [\$#] +$/;
my $more_pattern = qr/--More--/;
my $timeout = 10;
sub get_paginated_output {
my ($command, $expect) = @_;
my $more_flag = 0;
my @lines = undef;
my @alllines = undef;
$expect->send($command."\n");
while (1) {
my ($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt, -re, $more_pattern);
if ($match) {
if ($match =~ $more_pattern) {
$more_flag = 1;
@lines = split(/\R/, $before);
push(@alllines, grep {$_ =~ /\S/} @lines);
debug("skipping through --More-- pagination");
$expect->send(" ");
} elsif ($match =~ $prompt) {
$more_flag = 0;
@lines = split(/\R/, $before);
push(@alllines, grep {$_ =~ /\S/} @lines);
foreach my $line (@alllines) {
debug("output collected: $line") if $line;
}
last;
}
}
}
return @alllines;
}
sub arpnip_context {
my ($expect, $prompt, $timeout, $arpentries) = @_;
# IPv4 ARP
##########
my @data = get_paginated_output("get system arp", $expect);
# fortigate # get system arp
# Address Age(min) Hardware Addr Interface
# 2.6.0.5 0 00:40:46:f9:63:0f PLAY-0400
# 1.2.9.7 2 00:30:59:bc:f6:94 DEAD-3550
lib/App/Netdisco/SSHCollector/Platform/FortiOS.pm view on Meta::CPAN
$expect->raw_pty(1);
my ($pos, $error, $match, $before, $after);
if ($args->{banner}) {
my $banner = qr/^\(Press 'a' to accept\):/;
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $banner);
$expect->send("a");
}
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
my @data = get_paginated_output("get system status", $expect);
my $multi_vdom = 0;
foreach (@data) {
if ($_ && /^Virtual domain configuration: (multiple|split-task)$/) {
$multi_vdom = 1;
last;
}
}
my $arpentries = [];
if ($multi_vdom) {
$expect->send("config global\n");
$expect->expect($timeout, -re, $prompt);
my @data = get_paginated_output("get system vdom-property", $expect);
my $vdoms = [];
foreach (@data) {
push(@$vdoms, $1) if $_ && (/^==\s*\[\s*(\S+)\s*\]$/);
}
$expect->send("end\n");
$expect->expect($timeout, -re, $prompt);
foreach (@$vdoms) {
$expect->send("config vdom\n");
$expect->expect($timeout, -re, $prompt);
$expect->send("edit $_\n");
debug ("switched to config vdom; edit $_");
$expect->expect($timeout, -re, $prompt);
arpnip_context($expect, $prompt, $timeout, $arpentries);
$expect->send("end\n");
$expect->expect($timeout, -re, $prompt);
}
} else {
arpnip_context($expect, $prompt, $timeout, $arpentries);
}
$expect->send("exit\n");
$expect->soft_close();
return @$arpentries;
}
1;
lib/App/Netdisco/SSHCollector/Platform/FreeBSD.pm view on Meta::CPAN
debug "$hostlabel $$ arpnip()";
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 = qr/\$/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
my $command = ($args->{arp_command} || 'arp');
$expect->send("$command -n -a\n");
($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
my @arpentries = ();
my @lines = split(m/\n/, $before);
# ? (192.0.2.1) at fe:ed:de:ad:be:ef on igb0_vlan2 expires in 658 seconds [vlan]
my $linereg = qr/\s+\((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\)\s+at\s+([a-fA-F0-9:]{17})\s+on/;
foreach my $line (@lines) {
if ($line =~ $linereg) {
my ($ip, $mac) = ($1, $2);
lib/App/Netdisco/SSHCollector/Platform/GAIAEmbedded.pm view on Meta::CPAN
debug "$hostlabel $$ arpnip()";
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 = qr/>/;
($pos, $error, $match, $before, $after) = $expect->expect(30, -re, $prompt);
my $command = ($args->{arp_command} || 'arp');
$expect->send("$command -n \n");
($pos, $error, $match, $before, $after) = $expect->expect(30, -re, $prompt);
my @arpentries = ();
my @lines = split(m/\n/, $before);
# ? (192.168.17.178) at 5C:F9:DD:71:1F:08 [ether] on LAN1
# https://github.com/netdisco/netdisco/issues/365
my $linereg = qr/([0-9.]+)\s+ether\s+([a-fA-F0-9:]+).+/;
foreach my $line (@lines) {
if ($line =~ $linereg) {
my ($ip, $mac) = ($1, $2);
lib/App/Netdisco/SSHCollector/Platform/IOSXE.pm view on Meta::CPAN
my ($pty, $pid) = $ssh->open2pty;
unless ($pty) {
warn "unable to run remote command [$hostlabel] " . $ssh->error;
return;
}
my $exp = Expect->init($pty);
$exp->raw_pty(1);
my $prompt = qr/[>#]\s*$/; # IOS-XE exec/enable prompt
my $timeout = ($args && $args->{timeout}) ? $args->{timeout} : 30;
my ($pos, $err, $match, $before, $after) = $exp->expect($timeout, -re => $prompt);
unless (defined $pos) {
info "$hostlabel $$ $purpose() unable to reach CLI prompt";
$exp->hard_close();
return;
}
# Disable paging so Expect sees complete command output
$exp->send("terminal length 0\n");
($pos, $err, $match, $before, $after) = $exp->expect($timeout, -re => $prompt);
debug "$hostlabel $$ $purpose() cli session ready";
return ($exp, $prompt, $timeout);
}
sub _parse_vrfs {
my ($text) = @_;
my @vrfs;
# "show vrf" includes a Platform iVRF section on some builds; stop before that.
for my $line (split /\r?\n/, ($text // '')) {
last if $line =~ /^\s*Platform iVRF Name/i;
next if $line =~ /^\s*$/;
lib/App/Netdisco/SSHCollector/Platform/IOSXE.pm view on Meta::CPAN
++$added;
}
return (\@entries, $added);
}
sub arpnip {
my ($self, $hostlabel, $ssh, $args) = @_;
debug "$hostlabel $$ arpnip()";
my ($exp, $prompt, $timeout) = _open_expect($hostlabel, $ssh, $args, 'arpnip');
return () unless $exp;
my @arpentries;
my %seen;
# Stats are only for one end-of-run summary line
my %stats = ( global => 0, vrf => {} );
my ($pos, $err, $match, $before, $after);
debug "$hostlabel $$ arpnip() collecting ARP from [global]";
$exp->send("show ip arp\n");
($pos, $err, $match, $before, $after) = $exp->expect($timeout, -re => $prompt);
my ($g_entries, $g_added) = _parse_arp($before, \%seen);
push @arpentries, @$g_entries;
$stats{global} += $g_added;
# VRF enumeration; failures here should not block global ARP.
$exp->send("show vrf\n");
($pos, $err, $match, $before, $after) = $exp->expect($timeout, -re => $prompt);
my @vrfs = _parse_vrfs($before);
debug "$hostlabel $$ arpnip() detected " . scalar(@vrfs) . " VRFs";
for my $vrf (@vrfs) {
debug "$hostlabel $$ arpnip() collecting ARP from [vrf:$vrf]";
$exp->send("show ip arp vrf $vrf\n");
($pos, $err, $match, $before, $after) = $exp->expect($timeout, -re => $prompt);
my ($v_entries, $v_added) = _parse_arp($before, \%seen);
push @arpentries, @$v_entries;
$stats{vrf}{$vrf} = ($stats{vrf}{$vrf} || 0) + $v_added;
}
$exp->send("exit\n");
$exp->hard_close();
my $total = scalar(@arpentries);
lib/App/Netdisco/SSHCollector/Platform/IOSXE.pm view on Meta::CPAN
}
debug "$hostlabel $$ arpnip() summary: total=$total (" . join(', ', @parts) . ")";
return @arpentries;
}
sub macsuck {
my ($self, $hostlabel, $ssh, $args) = @_;
debug "$hostlabel $$ macsuck()";
my ($exp, $prompt, $timeout) = _open_expect($hostlabel, $ssh, $args, 'macsuck');
return unless $exp;
my ($pos, $err, $match, $before, $after);
$exp->send("show mac address-table\n");
($pos, $err, $match, $before, $after) = $exp->expect($timeout, -re => $prompt);
my @lines = split /\r?\n/, ($before // '');
$exp->send("exit\n");
$exp->hard_close();
my $macentries = {};
# Tolerant line match: grab VLAN + MAC + final port token
# Handles variants with extra columns (age/secure/ntfy/etc).
my $re_line = qr{
lib/App/Netdisco/SSHCollector/Platform/IOSXR.pm view on Meta::CPAN
debug "$hostlabel $$ arpnip()";
my ($pty, $pid) = $ssh->open2pty;
unless ($pty) {
warn "unable to run remote command [$hostlabel] " . $ssh->error;
return ();
}
my $expect = Expect->init($pty);
my ($pos, $error, $match, $before, $after);
my $prompt = qr/# +$/;
my $timeout = 10;
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
$expect->send("terminal length 0\n");
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
$expect->send("show arp vrf all\n");
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
my @arpentries = ();
my @data = split(m/\n/, $before);
foreach (@data) {
my ($ip, $age, $mac, $state, $t, $iface) = split(/\s+/);
if ($ip =~ m/(\d{1,3}\.){3}\d{1,3}/
&& $mac =~ m/([0-9a-f]{4}\.){2}[0-9a-f]{4}/i) {
push(@arpentries, { ip => $ip, mac => $mac });
lib/App/Netdisco/SSHCollector/Platform/Linux.pm view on Meta::CPAN
debug "$hostlabel $$ arpnip()";
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 = qr/\$/;
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
my $command = ($args->{arp_command} || 'arp');
$expect->send("$command -n | tail -n +2\n");
($pos, $error, $match, $before, $after) = $expect->expect(5, -re, $prompt);
my @arpentries = ();
my @lines = split(m/\n/, $before);
# 192.168.1.1 ether 00:b6:aa:f5:bb:6e C eth1
my $linereg = qr/([0-9\.]+)\s+ether\s+([a-fA-F0-9:]+)/;
foreach my $line (@lines) {
if ($line =~ $linereg) {
my ($ip, $mac) = ($1, $2);
lib/App/Netdisco/SSHCollector/Platform/NXOS.pm view on Meta::CPAN
return ();
}
#$Expect::Debug = 1;
#$Expect::Exp_Internal = 1;
my $expect = Expect->init($pty);
$expect->raw_pty(1);
my ($pos, $error, $match, $before, $after);
my $prompt = qr/# +$/;
my $timeout = 10;
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
# we filter on the : in Age as the output header of the command may contain prompt chars, e.g.
# Flags: # - Adjacencies Throttled for Glean
$expect->send("show ip arp vrf all | inc ^[1-9] | no-more\n");
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
my @arpentries;
my @data = split(/\R/, $before);
#IP ARP Table for all contexts
#Total number of entries: 5
#Address Age MAC Address Interface
#192.168.228.1 00:00:43 0000.abcd.1111 mgmt0
#192.168.228.9 00:05:24 cccc.7777.1b1b mgmt0
foreach (@data) {
my ($ip, $age, $mac, $iface) = split(/\s+/);
if ($ip && $ip =~ m/(\d{1,3}\.){3}\d{1,3}/
&& $mac =~ m/([0-9a-f]{4}\.){2}[0-9a-f]{4}/i) {
push(@arpentries, { ip => $ip, mac => $mac });
}
}
$expect->send("show ipv6 neighbor vrf all | exclude Flags: | no-more\n");
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
my @data6 = split(/\R/, $before);
#IPv6 neighbors use this two-line format
#IPv6 Adjacency Table for all VRFs
#Total number of entries: 65
#Address Age MAC Address Pref Source Interface
#bff:a90:c405:120::3
# 00:01:46 5c71.0d42.df3f 50 icmpv6 Vlan376
#bff:a90:c405:120::52
lib/App/Netdisco/SSHCollector/Platform/NXOS.pm view on Meta::CPAN
return ();
}
#$Expect::Debug = 1;
#$Expect::Exp_Internal = 1;
my $expect = Expect->init($pty);
$expect->raw_pty(1);
my ($pos, $error, $match, $before, $after);
my $prompt = qr/# +$/;
my $timeout = 10;
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
# IP Route Table for VRF "xyz"
#'*' denotes best ucast next-hop
#'**' denotes best mcast next-hop
#'[x/y]' denotes [preference/metric]
#'%<string>' in via output denotes VRF <string>
#
#0.0.0.0/0, ubest/mbest: 1/0 time
# *via 10.255.254.17, [1/0], 7w1d, static
#10.1.1.0/24, ubest/mbest: 1/0 time, attached
# *via 10.1.1.2, Vlan1234, [0/0], 1y13w, direct
#10.1.1.1/32, ubest/mbest: 1/0 time, attached
# *via 10.1.1.1, Vlan1234, [0/0], 1y13w, hsrp
# include only lines with "attached" and exclude /32 subnets
$expect->send("show ip route vrf all | inc attached | exc /32 | no-more \n");
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
my @subnets;
my @data = split(/\R/, $before);
foreach (@data) {
# subnet cidr is the first part before the comma and space
my ($cidr, $rest) = split(/,\s+/);
if ($cidr && $cidr =~ m/(\d{1,3}\.){3}\d{1,3}\/\d{1,2}/) {
push(@subnets, $cidr);
}
}
lib/App/Netdisco/SSHCollector/Platform/OS10.pm view on Meta::CPAN
return ();
}
# $Expect::Debug = 1;
# $Expect::Exp_Internal = 1;
my $expect = Expect->init($pty);
$expect->raw_pty(1);
my ($pos, $error, $match, $before, $after);
my $prompt = qr/# +$/;
my $timeout = 60;
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
# Get all VRFs, skip header line
$expect->send("show ip vrf | except VRF-Name | no-more \n");
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
my @vrfs = split(/\R/, $before);
# Regex for VRF name
my $vrf_re = qr/^([a-z\-_0-9\.]+)\s+.+$/i;
# IP ARP match
my $iparp_re = qr/^((\d{1,3}\.){3}\d{1,3})\s*(([0-9a-f]{2}[:-]){5}[0-9a-f]{2})\s+.+$/i;
# Will hold results
my @arpentries;
lib/App/Netdisco/SSHCollector/Platform/OS10.pm view on Meta::CPAN
my $vrf_name ;
# Get the VRF name
if ($vrf_line && $vrf_line =~ m/$vrf_re/) {
$vrf_name = $1;
} else {
next ;
}
# Get IP ARP entries for this VRF
my $vrf_cmd = sprintf("show ip arp vrf %s | no-more \n", $vrf_name) ;
$expect->send($vrf_cmd);
($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt);
my @iparps = split(/\R/, $before);
foreach my $iparp_line (@iparps) {
if($iparp_line && $iparp_line =~ m/$iparp_re/) {
push(@arpentries, { ip => $1, mac => $3 });
}
}
}
return @arpentries;
lib/App/Netdisco/SSHCollector/Platform/PaloAlto.pm view on Meta::CPAN
debug "$hostlabel $$ arpnip()";
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 = qr/> \r?$/;
($pos, $error, $match, $before, $after) = $expect->expect(20, -re, $prompt);
$expect->send("set cli scripting-mode on\n");
# The PAN cli echos stuff back at us, causing us to see the prompt 3 extra times.
# Fortunately, the previous command disables this, so we only deal with it once.
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
($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("show neighbor interface all\n");
($pos, $error, $match, $before, $after) = $expect->expect(10, -re, $prompt);
for (split(/\r\n/, $before)){
next unless $_ =~ m/([0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}/;
my ($tmp, $ip, $mac) = split(/\s+/);
if ($ip =~ m/([0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}/ && $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();
lib/App/Netdisco/SSHCollector/Platform/VOSS.pm view on Meta::CPAN
}
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/) {
share/contrib/raddb/dictionary.cisco view on Meta::CPAN
VENDOR Cisco 9
#
# Attributes Sent ftom Radius Server to the Voice Gateway
#
ATTRIBUTE h323-ivr-in 1 avpair Cisco
ATTRIBUTE h323-credit-amount 101 avpair Cisco
ATTRIBUTE h323-credit-time 102 avpair Cisco
ATTRIBUTE h323-return-code 103 avpair Cisco
ATTRIBUTE h323-prompt-id 104 avpair Cisco
ATTRIBUTE h323-time-and-day 105 avpair Cisco
ATTRIBUTE h323-redirect-number 106 avpair Cisco
ATTRIBUTE h323-preferred-lang 107 avpair Cisco
ATTRIBUTE h323-redirect-ip-address 108 avpair Cisco
ATTRIBUTE h323-billing-model 109 avpair Cisco
ATTRIBUTE h323-currency 110 avpair Cisco
#
# Extra voice attributes sent by the Cisco, if you configure
# "radius-server vsa accounting" (requires IOS11.2+ for non-VoIP
share/contrib/raddb/dictionary.quintum view on Meta::CPAN
VENDOR Quintum 6618
#
# Attributes Sent ftom Radius Server to the Voice Gateway
#
ATTRIBUTE h323-ivr-in 1 avpair Quintum
ATTRIBUTE h323-credit-amount 101 avpair Quintum
ATTRIBUTE h323-credit-time 102 avpair Quintum
ATTRIBUTE h323-return-code 103 avpair Quintum
ATTRIBUTE h323-prompt-id 104 avpair Quintum
ATTRIBUTE h323-time-and-day 105 avpair Quintum
ATTRIBUTE h323-redirect-number 106 avpair Quintum
ATTRIBUTE h323-preferred-lang 107 avpair Quintum
ATTRIBUTE h323-redirect-ip-address 108 avpair Quintum
ATTRIBUTE h323-billing-model 109 avpair Quintum
ATTRIBUTE h323-currency 110 avpair Quintum
#
# Extra voice attributes sent by the Cisco, if you configure
# "radius-server vsa accounting" (requires IOS11.2+ for non-VoIP
share/contrib/raddb/dictionary.usr view on Meta::CPAN
VALUE USR-Connect-Term-Reason v42BadSetup 28
VALUE USR-Connect-Term-Reason v42InvalidCodeWord 29
VALUE USR-Connect-Term-Reason v42StringToLong 30
VALUE USR-Connect-Term-Reason v42InvalidCommand 31
VALUE USR-Connect-Term-Reason none 32
VALUE USR-Connect-Term-Reason v32Cleardown 33
VALUE USR-Connect-Term-Reason dialSecurity 34
VALUE USR-Connect-Term-Reason remoteAccessDenied 35
VALUE USR-Connect-Term-Reason loopLoss 36
VALUE USR-Connect-Term-Reason ds0Teardown 37
VALUE USR-Connect-Term-Reason promptNotEnabled 38
VALUE USR-Connect-Term-Reason noPromptingInSync 39
VALUE USR-Connect-Term-Reason nonArqMode 40
VALUE USR-Connect-Term-Reason modeIncompatible 41
VALUE USR-Connect-Term-Reason noPromptInNonARQ 42
VALUE USR-Connect-Term-Reason dialBackLink 43
VALUE USR-Connect-Term-Reason linkAbort 44
VALUE USR-Connect-Term-Reason autopassFailed 45
VALUE USR-Connect-Term-Reason pbGenericError 46
VALUE USR-Connect-Term-Reason pbLinkErrTxPreAck 47
VALUE USR-Connect-Term-Reason pbLinkErrTxTardyACK 48
share/contrib/raddb/dictionary.usr view on Meta::CPAN
VALUE USR-Failure-to-Connect-Reason v42BadSetup 28
VALUE USR-Failure-to-Connect-Reason v42InvalidCodeWord 29
VALUE USR-Failure-to-Connect-Reason v42StringToLong 30
VALUE USR-Failure-to-Connect-Reason v42InvalidCommand 31
VALUE USR-Failure-to-Connect-Reason none 32
VALUE USR-Failure-to-Connect-Reason v32Cleardown 33
VALUE USR-Failure-to-Connect-Reason dialSecurity 34
VALUE USR-Failure-to-Connect-Reason remoteAccessDenied 35
VALUE USR-Failure-to-Connect-Reason loopLoss 36
VALUE USR-Failure-to-Connect-Reason ds0Teardown 37
VALUE USR-Failure-to-Connect-Reason promptNotEnabled 38
VALUE USR-Failure-to-Connect-Reason noPromptingInSync 39
VALUE USR-Failure-to-Connect-Reason nonArqMode 40
VALUE USR-Failure-to-Connect-Reason modeIncompatible 41
VALUE USR-Failure-to-Connect-Reason noPromptInNonARQ 42
VALUE USR-Failure-to-Connect-Reason dialBackLink 43
VALUE USR-Failure-to-Connect-Reason linkAbort 44
VALUE USR-Failure-to-Connect-Reason autopassFailed 45
VALUE USR-Failure-to-Connect-Reason pbGenericError 46
VALUE USR-Failure-to-Connect-Reason pbLinkErrTxPreAck 47
VALUE USR-Failure-to-Connect-Reason pbLinkErrTxTardyACK 48