use Data::Dump qw(dump);
-my $dir="/dev/shm/snmpbulkwalk";
+my $dir="/dev/shm/snmp-topology";
my $stat;
foreach my $file ( @dumps ) {
+ my $lines = 0;
+ my $ignored = 0;
+
open(my $fh, '<', $file);
my $sw = $file; $sw =~ s/^.*\///;
while(<$fh>) {
chomp;
+ $lines++;
if ( m/^SNMPv2-MIB::(sysName|sysDescr)\.0 = STRING: (.+)/ ) {
$stat->{$sw}->{$1} = $2;
=for xxx
#warn "# $sw ",dump($name,$oid,$i,$type,$value),$/;
#$stat->{$sw}->{$name}->{$oid}->[$i] = $value;
$stat->{_mac2sw}->{$value} = $sw;
+ $stat->{_sw_mac_count}->{$sw}++;
} elsif ( m/^BRIDGE-MIB::dot1dTpFdbPort\[STRING: ([^\]]+)\] = INTEGER: (\d+)/ ) {
my ( $mac, $port ) = ($1,$2);
push @{ $stat->{_sw_mac_port_vlan}->{$sw}->{$mac}->{$port} }, '';
+ #$stat->{_sw_port_vlan_count}->{$sw}->{$port}->{''}++;
+ $stat->{_sw_port_mac_count}->{$sw}->{$port}++;
} elsif ( m/^Q-BRIDGE-MIB::dot1qTpFdbPort\[(\d+)\]\[STRING: ([^\]]+)\] = INTEGER: (\d+)/ ) {
my ( $vlan, $mac, $port ) = ($1,$2,$3);
push @{ $stat->{_sw_mac_port_vlan}->{$sw}->{$mac}->{$port} }, $vlan;
+ #$stat->{_sw_port_vlan_count}->{$sw}->{$port}->{$vlan}++;
+ $stat->{_sw_port_mac_count}->{$sw}->{$port}++;
+ } else {
+ $ignored++;
}
}
#warn "# $sw ",dump( $stat->{$sw} );
+ warn "## $file $lines / $ignored ignored";
}
-#warn "# stat = ",dump($stat);
+warn "# stat = ",dump($stat);
+#warn "# _sw_port_vlan_count = ",dump($stat->{_sw_port_vlan_count});
+warn "# _sw_port_mac_count = ",dump($stat->{_sw_port_mac_count});
open(my $fh, '>', '/dev/shm/mac2sw');
+open(my $fh2, '>', '/dev/shm/mac2sw.snmp');
foreach my $mac ( keys %{ $stat->{_mac2sw} } ) {
print $fh macfmt($mac), " ", $stat->{_mac2sw}->{$mac}, "\n";
+ print $fh $mac, " ", $stat->{_mac2sw}->{$mac}, "\n";
};
+# XXX inject additional mac in filter to include wap devices
+my $mac_include = '/dev/shm/mac.wap';
+if ( -e $mac_include ) {
+ my $count = 0;
+ open(my $fh, '<', $mac_include);
+ while(<$fh>) {
+ chomp;
+ my ($mac,$host) = split(/\s+/,$_,2);
+ $mac =~ s/^0//; $mac =~ s/:0/:/g; # mungle mac to snmp format without leading zeros
+ $stat->{_mac2sw}->{$mac} = $host;
+ $count++;
+ }
+# warn "# $mac_include added to _mac2sw = ",dump($stat->{_mac2sw}),$/;
+ warn "# $mac_include added to _mac2sw $count hosts\n";
+} else {
+ warn "MISSING $mac_include\n";
+}
+
my $s = $stat->{_sw_mac_port_vlan};
foreach my $sw ( keys %$s ) {
foreach my $mac ( keys %{ $s->{$sw} } ) {
foreach my $port ( keys %{ $s->{$sw}->{$mac} } ) {
#$stat->{_sw_port_sw}->{$sw}->{$port}->{$mac_name} = $s->{$sw}->{$mac}->{$port};
push @{ $stat->{_sw_port_sw}->{$sw}->{$port} }, $mac_name;
+ push @{ $stat->{_sw_sw_port}->{$sw}->{$mac_name} }, $port;
}
}
}
}
warn "# _sw_port_sw = ",dump($stat->{_sw_port_sw});
+warn "# _sw_sw_port = ",dump($stat->{_sw_sw_port});
my $s = $stat->{_sw_port_sw};
our $later;
my $last_later;
+our @single_sw_port_visible;
+sub single_sw_port_visible {
+ @single_sw_port_visible = ();
+ my $s = {};
+ foreach my $sw ( keys %$later ) {
+ if ( exists $stat->{_found}->{$sw} ) {
+ my @d = delete $later->{$sw};
+ warn "REMOVED $sw from later it's _found! later was = ",dump( \@d );
+ next;
+ }
+ my @ports = sort keys %{ $later->{$sw} };
+ foreach my $port ( @ports ) {
+ my @visible = uniq_visible( @{ $later->{$sw}->{$port} } );
+ if ( $#visible < 0 ) {
+ warn "REMOVED $sw $port from later it's empty";
+ delete $later->{$sw}->{$port};
+ next;
+ }
+ $s->{$sw}->{$port} = [ @visible ];
+ push @single_sw_port_visible, [ $sw, $port, $visible[0] ] if $#visible == 0; # single
+ }
+ }
+ my $d_s = dump($s);
+ my $d_l = dump($later);
+ if ( $d_s ne $d_l ) {
+ $later = $s;
+ warn "# single_sw_port_visible = ",dump( \@single_sw_port_visible );
+ warn "# reduced later = ",dump( $later );
+ }
+}
+
sub uniq {
my @visible = @_;
my $u; $u->{$_}++ foreach @visible;
my $sw = shift;
my $port = shift;
my @visible = uniq_visible(@_);
- warn "# to_later $sw $port visible = ", $#visible + 1, "\n";
+ warn "# to_later $sw $port visible = ",dump( \@visible ),"\n";
$later->{$sw}->{$port} = [ @visible ];
return @visible;
}
foreach my $port ( @ports ) {
warn "## $sw $port => ",join(' ', @{$s->{$sw}->{$port}}),$/;
}
+
if ( $#ports == 0 ) {
my $port = $ports[0];
- #print "$sw $port TRUNK\n";
+ print "$sw $port TRUNK\n";
push @{$stat->{_trunk}->{$sw}}, $port; # FIXME multiple trunks?
#warn "## _trunk = ",dump( $stat->{_trunk} ).$/;
warn "++++ $sw $port $visible[0]\n";
#print "$sw $port $visible[0]\n";
$stat->{_found}->{$visible[0]} = "$sw $port";
-
- } elsif ( @visible ) {
+ single_sw_port_visible();
+
+ } elsif ( $#visible > 0 ) {
to_later( $sw, $port, @visible );
} else {
- warn "#### $sw $port doesn't have anything visible\n";
+ warn "#### $sw $port doesn't have anything visible, reseting visibility\n";
+ to_later( $sw, $port, @{ $stat->{_sw_port_sw}->{$sw}->{$port} } );
}
}
}
warn "NEXT later = ",dump($later),$/;
-$s = $later;
+
+single_sw_port_visible();
my $d = dump($later);
if ( $d eq $last_later ) {
- warn "FIXME later didn't change, last\n";
- last;
+ warn "FIXME later didn't change single_sw_port_visible = ",dump( \@single_sw_port_visible ),$/;
+
+ my $did_patch = 0;
+
+ while ( @single_sw_port_visible ) {
+ my $single = shift @single_sw_port_visible;
+ my ( $sw, $port, $visible ) = @$single;
+ warn "XXX $sw | $port | $visible\n";
+
+ foreach my $port ( keys %{ $later->{$visible} } ) {
+ # check back in original full map to see if it was visible
+ my @visible = @{ $stat->{_sw_port_sw}->{$visible}->{$port} };
+ if ( scalar grep(/$sw/,@visible) ) {
+ warn "PATCH $visible $port -> $sw ONLY";
+ $stat->{_found}->{$sw} = "$visible $port";
+
+ my @d = delete $later->{$visible}->{$port};
+ warn "DELETED $visible $port ",dump(@d);
+ $did_patch++;
+
+ single_sw_port_visible();
+ } else {
+ warn "FATAL $visible $port NO $sw IN ",dump( \@visible );
+ }
+ }
+
+ if ( ! $did_patch ) {
+ # OK, we have link from trunk probably, which port was originally visible on?
+
+ foreach my $port ( keys %{ $stat->{_sw_port_sw}->{$visible} } ) {
+ my @visible = grep /$sw/, @{ $stat->{_sw_port_sw}->{$visible}->{$port} };
+ if ( scalar @visible ) {
+ warn "PATCH-2 $visible $port -> $sw\n";
+ $stat->{_found}->{$sw} = "$visible $port";
+
+ my @d = delete $later->{$visible}->{$port};
+ warn "DELETED $visible $port ",dump(@d);
+ $did_patch++;
+
+ single_sw_port_visible();
+ } else {
+ warn "FATAL $visible $port _sw_port_sw doesn't have $sw";
+ }
+ }
+ }
+
+ }
+
+ warn "## applied $did_patch patches to unblock\n";
+
+ last if $d eq $last_later;
+
}
$last_later = $d;
warn "FINAL _found = ",dump( $stat->{_found} ),$/;
warn "FINAL _trunk = ",dump( $stat->{_trunk} ),$/;
+warn "FINAL later = ",dump( $later ),$/;
my $node;
my @edges;
-my $ports = $ENV{PORTS} || 0; # FIXME
+my $ports = $ENV{PORTS} || 1; # FIXME
open(my $dot, '>', '/tmp/snmp-topology.dot');
my $shape = $ports ? 'record' : 'ellipse';
-my $rankdir = $ports ? 'TB' : 'LR';
+my $rankdir = 'LR'; #$ports ? 'TB' : 'LR';
print $dot <<"__DOT__";
digraph topology {
graph [ rankdir = $rankdir ]
my @to_port = uniq(@{ $stat->{_trunk}->{$to_sw} });
my $to_port = $to_port[0];
warn "ERROR: $to_sw has ",dump(\@to_port), " ports instead of just one!" if $#to_port > 0;
+ no warnings;
printf "%s %s -> %s %s\n", $from_sw, $from_port, $to_sw, $to_port;
push @edges, [ $from_sw, $to_sw, $from_port, $to_port ];
push @{ $node->{$from_sw} }, [ $from_port, $to_sw ];
if ( $ports ) {
foreach my $n ( keys %$node ) {
+ no warnings;
my @port_sw =
- sort { $a->[0] <=> $b->[1] }
+ sort { $a->[0] <=> $b->[0] }
@{ $node->{$n} };
-warn "XXX $n ",dump( \@port_sw );
- print $dot qq!"$n" [ label="*$n*|! . join('|', map { sprintf "<%d>%2d %s", $_->[0], $_->[0], $_->[1] } @port_sw ) . qq!" ];\n!;
+#warn "XXX $n ",dump( \@port_sw );
+ print $dot qq!"$n" [ label="!.uc($n).'|' . join('|', map { sprintf "<%d>%2d %s", $_->[0], $_->[0], $_->[1] } @port_sw ) . qq!" ];\n!;
}
}
if (! $ports) {
print $dot sprintf qq{ "%s" -> "%s" [ taillabel="%s" ; headlabel="%s" ]\n}, @$e;
} else {
+ no warnings;
print $dot sprintf qq{ "%s":%d -> "%s":%d\n}, $e->[0], $e->[2], $e->[1], $e->[3];
}
}