744cf207433fd76fa2170a72b3eeff3debb898c6
[dell-switch] / snmp-topology.pl
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
4 use autodie;
5
6 use Data::Dump qw(dump);
7
8 my $dir="/dev/shm/snmpbulkwalk";
9
10 my $stat;
11
12 my @dumps = @ARGV;
13 @dumps = glob("$dir/*") unless @dumps;
14
15 sub macfmt {
16         my $mac = shift;
17         $mac =~ s/^([0-9a-f]):/0$1:/i;
18         while ( $mac =~ s/:([0-9a-f]):/:0$1:/ig ) {};
19         $mac =~ s/:([0-9a-f])$/:0$1/i;
20         return $mac;
21 }
22
23 foreach my $file ( @dumps ) {
24
25         open(my $fh, '<', $file);
26         my $sw = $file; $sw =~ s/^.*\///;
27         while(<$fh>) {
28                 chomp;
29                 if ( m/^SNMPv2-MIB::(sysName|sysDescr)\.0 = STRING: (.+)/ ) {
30                         $stat->{$sw}->{$1} = $2;
31 =for xxx
32                 } elsif ( m/^(IF-MIB)::(ifPhysAddress)\.0 = (\w+): (.+)/ ) {
33                         my ($name,$oid,$type,$value) = ($1,$2,$3,$4);
34                         #$value =~ s/\(\d+\)$// if $type eq 'INTEGER';
35                         $stat->{$sw}->{$name}->{$oid} = $value;
36 =cut
37                 } elsif ( m/^(IF-MIB)::(ifPhysAddress)\[(\d+)\] = (\w+): (.+)/ ) {
38                         my ($name,$oid,$i,$type,$value) = ($1,$2,$3,$4,$5);
39                         #warn "# $sw ",dump($name,$oid,$i,$type,$value),$/;
40                         #$stat->{$sw}->{$name}->{$oid}->[$i] = $value;
41                         $stat->{_mac2sw}->{$value} = $sw;
42                 } elsif ( m/^BRIDGE-MIB::dot1dTpFdbPort\[STRING: ([^\]]+)\] = INTEGER: (\d+)/ ) {
43                         my ( $mac, $port ) = ($1,$2);
44                         push @{ $stat->{_sw_mac_port_vlan}->{$sw}->{$mac}->{$port} }, '';
45                 } elsif ( m/^Q-BRIDGE-MIB::dot1qTpFdbPort\[(\d+)\]\[STRING: ([^\]]+)\] = INTEGER: (\d+)/ ) {
46                         my ( $vlan, $mac, $port ) = ($1,$2,$3);
47                         push @{ $stat->{_sw_mac_port_vlan}->{$sw}->{$mac}->{$port} }, $vlan;
48                 }
49         }
50         #warn "# $sw ",dump( $stat->{$sw} );
51 }
52 #warn "# stat = ",dump($stat);
53
54 open(my $fh, '>', '/dev/shm/mac2sw');
55 foreach my $mac ( keys %{ $stat->{_mac2sw} } ) {
56         print $fh $mac, " ", macfmt( $stat->{_mac2sw}->{$mac} ), "\n";
57 };
58
59 my $s = $stat->{_sw_mac_port_vlan};
60 foreach my $sw ( keys %$s ) {
61         foreach my $mac ( keys %{ $s->{$sw} } ) {
62                 if ( my $mac_name = $stat->{_mac2sw}->{ $mac } ) {
63                         next if $sw eq $mac_name; # mikrotik seems to see itself
64                         foreach my $port ( keys %{ $s->{$sw}->{$mac} } ) {
65                                 #$stat->{_sw_port_sw}->{$sw}->{$port}->{$mac_name} = $s->{$sw}->{$mac}->{$port};
66                                 push @{ $stat->{_sw_port_sw}->{$sw}->{$port} }, $mac_name;
67                         }
68                 }
69         }
70 }
71
72 warn "# _sw_port_sw = ",dump($stat->{_sw_port_sw});
73
74 open(my $dot, '>', '/tmp/snmp-topology.dot');
75 print $dot "digraph topology {\n";
76
77 my $s = $stat->{_sw_port_sw};
78 our $later;
79 my $last_later;
80
81 sub uniq {
82         my @visible = @_;
83         my $u; $u->{$_}++ foreach @visible;
84         @visible = sort keys %$u;
85         return @visible;
86 }
87
88 sub uniq_visible {
89         my @visible = uniq(@_);
90         @visible = grep { ! exists $stat->{_found}->{$_} } @visible;
91         return @visible;
92 }
93
94 sub to_later {
95         my $sw = shift;
96         my $port = shift;
97         my @visible = uniq_visible(@_);
98         warn "# to_later $sw $port visible = ", $#visible + 1, "\n";
99         $later->{$sw}->{$port} = [ @visible ];
100         return @visible;
101 }
102
103 while ( ref $s ) {
104 #warn "## s = ",dump($s);
105 foreach my $sw ( sort keys %$s ) {
106
107         #warn "## $sw s = ",dump($s->{$sw}),$/;
108
109         my @ports = sort { $a <=> $b } uniq( keys %{ $s->{$sw} } );
110
111         foreach my $port ( @ports ) {
112                 warn "## $sw $port => ",join(' ', @{$s->{$sw}->{$port}}),$/;
113         }
114         if ( $#ports == 0 ) {
115                 my $port = $ports[0];
116                 #print "$sw $port TRUNK\n";
117                 push @{$stat->{_trunk}->{$sw}}, $port; # FIXME multiple trunks?
118                 #warn "## _trunk = ",dump( $stat->{_trunk} ).$/;
119
120                 my @visible = uniq_visible( @{ $s->{$sw}->{$port} } );
121                 to_later( $sw, $port, @visible );
122                 next;
123         }
124
125         foreach my $port ( @ports ) {
126                 my @visible = uniq_visible( @{ $s->{$sw}->{$port} } );
127                 warn "### $sw $port visible=",dump(\@visible),$/;
128
129                 if ( $#visible == 0 ) {
130                         warn "++++ $sw $port $visible[0]\n";
131                         #print "$sw $port $visible[0]\n";
132                         print $dot qq{ "$sw" -> "$visible[0]" [ label="$port" ];\n};
133                         $stat->{_found}->{$visible[0]} = "$sw $port";
134                 
135                 } elsif ( @visible ) {
136                         to_later( $sw, $port, @visible );
137                 } else {
138                         warn "#### $sw $port doesn't have anything visible\n";
139                 }
140                         
141         }
142         warn "## _found = ",dump( $stat->{_found} ),$/;
143 }
144
145 warn "NEXT later = ",dump($later),$/;
146 $s = $later;
147
148 my $d = dump($later);
149 if ( $d eq $last_later ) {
150         warn "FIXME later didn't change, last\n";
151         last;
152 }
153 $last_later = $d;
154
155 $later = undef;
156
157 } # while
158
159 print $dot "}\n";
160
161 warn "FINAL _found = ",dump( $stat->{_found} ),$/;
162 warn "FINAL _trunk = ",dump( $stat->{_trunk} ),$/;
163
164 foreach my $sw ( keys %{ $stat->{_found} } ) {
165         printf "%s -> %s %s\n", $stat->{_found}->{$sw}, $sw, uniq(@{ $stat->{_trunk}->{$sw} });
166 }