From f101aa884646f958f52e73e68e2753fae7ea0ef9 Mon Sep 17 00:00:00 2001 From: Christian Ramseyer Date: Fri, 3 Nov 2023 21:50:14 +0100 Subject: [PATCH] FortiOS.pm improvements * can handle --More-- pagination * suppress some uninitialized variable warnings --- .../Netdisco/SSHCollector/Platform/FortiOS.pm | 79 ++++++++++++------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/lib/App/Netdisco/SSHCollector/Platform/FortiOS.pm b/lib/App/Netdisco/SSHCollector/Platform/FortiOS.pm index be6ba020..edb3806e 100644 --- a/lib/App/Netdisco/SSHCollector/Platform/FortiOS.pm +++ b/lib/App/Netdisco/SSHCollector/Platform/FortiOS.pm @@ -35,16 +35,47 @@ Returns a list of hashrefs in the format C<< { mac => MACADDR, ip => IPADDR } >> =cut +our $prompt = qr/ [\$#] +$/; +our $more_pattern = qr/--More--/; +our $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 ########## - $expect->send("get system arp\n"); - my ($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt); - - my @data = split(/\R/, $before); + my @data = get_paginated_output("get system arp", $expect); # fortigate # get system arp # Address Age(min) Hardware Addr Interface @@ -52,7 +83,7 @@ sub arpnip_context { # 1.2.9.7 2 00:30:59:bc:f6:94 DEAD-3550 foreach (@data) { - if (/^($RE{net}{IPv4})\s*\d+\s*($RE{net}{MAC})\s*\S+$/) { + if ($_ && /^($RE{net}{IPv4})\s*\d+\s*($RE{net}{MAC})\s*\S+$/) { debug "\tfound IPv4: $1 => MAC: $2"; push(@$arpentries, { ip => $1, mac => $2 }); } @@ -61,10 +92,7 @@ sub arpnip_context { # IPv6 ND ########## - $expect->send("diagnose ipv6 neighbor-cache list\n"); - ($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt); - - @data = split(/\R/, $before); + @data = get_paginated_output("diagnose ipv6 neighbor-cache list", $expect); # fortigate # diagnose ipv6 neighbor-cache list # ifindex=403 ifname=WORK-4016 fe80::abcd:1234:dead:f00d ab:cd:ef:01:23:45 state=00000004 use=42733 confirm=42733 update=41100 ref=3 @@ -72,8 +100,10 @@ sub arpnip_context { # ifindex=28 ifname=root :: 00:00:00:00:00:00 state=00000040 use=589688110 confirm=589694110 update=589688110 ref=1 # ifindex=48 ifname=FUN-4024 2001:42:1234:fe80:1234:1234:1234:1234 b0:c1:e2:f3:a4:b5 state=00000008 use=12 confirm=2 update=12 ref=2 + # might fail with: Unknown action 0 - this is a permission issue of the logged in user + foreach (@data) { - if (/^ifindex=\d+\s+ifname=\S+\s+($RE{net}{IPv6}{-sep => ':'}{-style => 'HeX'})\s+($RE{net}{MAC}).*$/) { + if ($_ && /^ifindex=\d+\s+ifname=\S+\s+($RE{net}{IPv6}{-sep => ':'}{-style => 'HeX'})\s+($RE{net}{MAC}).*$/) { debug "\tfound IPv6: $1 => MAC: $2"; push(@$arpentries, { ip => $1, mac => $2 }); } @@ -91,15 +121,13 @@ sub arpnip { return (); } - #$Expect::Debug = 1; - #$Expect::Exp_Internal = 1; + $Expect::Debug = 0; + $Expect::Exp_Internal = 0; my $expect = Expect->init($pty); $expect->raw_pty(1); my ($pos, $error, $match, $before, $after); - my $prompt = qr/ [\$#] +$/; - my $timeout = 10; if ($args->{banner}) { my $banner = qr/^\(Press 'a' to accept\):/; @@ -109,37 +137,30 @@ sub arpnip { } ($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt); - # Check if we are in a VDOM context - $expect->send("get system status\n"); - ($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt); - - my @data = split(/\R/, $before); + my @data = get_paginated_output("get system status", $expect); my $multi_vdom = 0; foreach (@data) { - if (/^Virtual domain configuration: multiple$/) { + if ($_ && /^Virtual domain configuration: multiple$/) { $multi_vdom = 1; last; } } my $arpentries = []; if ($multi_vdom) { - # Get list of all VDOM $expect->send("config global\n"); $expect->expect($timeout, -re, $prompt); - $expect->send("get system vdom-property\n"); - ($pos, $error, $match, $before, $after) = $expect->expect($timeout, -re, $prompt); - $expect->send("end\n"); - $expect->expect($timeout, -re, $prompt); - @data = split(/\R/, $before); + + my @data = get_paginated_output("get system vdom-property", $expect); my $vdoms = []; foreach (@data) { - push(@$vdoms, $1) if (/^==\s*\[\s*(\S+)\s*\]$/); + push(@$vdoms, $1) if $_ && (/^==\s*\[\s*(\S+)\s*\]$/); } foreach (@$vdoms) { $expect->send("config vdom\n"); - $expect->expect($timeout, -re, $prompt); - $expect->send("edit $_\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");