]> mj.ucw.cz Git - jablonka.git/blob - show-switch
show-switch: the first attempt
[jablonka.git] / show-switch
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5 use Net::SNMP ();
6 use Net::Netmask;
7 use Data::Dumper;
8 use Getopt::Long;
9
10 sub usage {
11         die "Usage: $0 [--debug] [--mac] <ip-addr>\n";
12 }
13
14 my $debug = 0;
15 my $mac = 0;
16 GetOptions(
17         'debug' => \$debug,
18         'mac' => \$mac,
19 ) or usage;
20
21 @ARGV == 1 or usage;
22 my ($switch_ip) = @ARGV;
23 my $community = 'public';
24
25 my $is_tty = -t STDOUT;
26 sub attr {
27         my ($a) = @_;
28         return $is_tty ? `tput $a` : "";
29 }
30 my $t_red = attr("setaf 1");
31 my $t_green = attr("setaf 2");
32 my $t_yellow = attr("setaf 3");
33 my $t_norm = attr("sgr0");
34
35 my ($snmp, $err) = Net::SNMP->session(
36         -hostname => $switch_ip,
37         -version => '2c',
38         -community => $community,
39 );
40 $snmp or die "Cannot establish session: $err\n";
41 $snmp->translate(0);
42
43 sub my_get_table {
44         my ($cols) = @_;
45         my $tab = {};
46         for my $c (keys %$cols) {
47                 my $depth = split /\./, $cols->{$c};
48                 my $t = $snmp->get_table(-baseoid => $cols->{$c}) or next;
49                 for my $k (keys %$t) {
50                         my @k = split /\./, $k;
51                         my $kk = join('.', @k[$depth..$#k]);
52                         $tab->{$kk}->{$c} = $t->{$k};
53                 }
54         }
55         return $tab;
56 }
57
58 sub format_uptime {
59         my ($t) = @_;
60         my $d = "";
61         $t = int($t/100);
62         if ($t >= 86400) {
63                 $d = int($t/86400) . " days, ";
64                 $t %= 86400;
65         }
66         return $d . sprintf "%02d:%02d:%02d", int($t/3600), int(($t%3600)/60), $t%60;
67 }
68
69 my $OID_basic = '1.3.6.1.2.1.1';
70
71 my $basics = my_get_table({
72         'desc' => "$OID_basic.1",
73         'uptime' => "$OID_basic.3",
74         'contact' => "$OID_basic.4",
75         'name' => "$OID_basic.5",
76         'location' => "$OID_basic.6",
77 });
78 print Dumper($basics) if $debug;
79 my $bas = $basics->{0} or die "Cannot find basic info";
80
81 print "### Basics ###\n\n";
82 print "Device:   ", $bas->{desc}, "\n";
83 print "Uptime:   ", format_uptime($bas->{uptime}), "\n";
84 print "Contact:  ", $bas->{contact}, "\n";
85 print "Name:     ", $bas->{name}, "\n";
86 print "Location: ", $bas->{location}, "\n";
87
88 my $OID_ifTable = '1.3.6.1.2.1.2.2';
89 my $OID_ifTablev2 = '1.3.6.1.2.1.31.1.1';
90
91 my $if_table = my_get_table({
92         'desc' => "$OID_ifTable.1.2",
93         'name' => "$OID_ifTablev2.1.1",
94         'alias' => "$OID_ifTablev2.1.18",
95         'speed' => "$OID_ifTable.1.5",
96         'hispeed' => "$OID_ifTablev2.1.15",
97         'admin' => "$OID_ifTable.1.7",
98         'oper' => "$OID_ifTable.1.8",
99 });
100 print Dumper($if_table) if $debug;
101 my @ifaces = sort { $a <=> $b } keys %$if_table;
102
103 my $OID_ipTable = '1.3.6.1.2.1.4.20';
104 my $ip_table = my_get_table({
105         'iface' => "$OID_ipTable.1.2",
106         'mask' => "$OID_ipTable.1.3",
107 });
108 print Dumper($ip_table) if $debug;
109
110 # XXX: IPv6 not supported yet
111 for my $ipa (keys %$ip_table) {
112         my $ip = $ip_table->{$ipa};
113         my $if = $if_table->{$ip->{iface}} or die "IP table refers to unknown iface";
114         my $nm = Net::Netmask->new2($ipa, $ip->{mask}) or die "Cannot parse IP prefix";
115         push @{$if->{ip_addrs}}, $ipa . '/' . $nm->bits;
116 }
117 print Dumper($if_table) if $debug;
118
119 if ($mac) {
120         my $OID_1qTpFdbTable = '1.3.6.1.2.1.17.7.1.2.2';
121         my $OID_1qPort = "$OID_1qTpFdbTable.1.2";
122         my $OID_1qStatus = "$OID_1qTpFdbTable.1.3";
123         my $vlan_fdb_table = my_get_table({
124                 'port' => $OID_1qPort,
125                 'status' => $OID_1qStatus,
126         });
127         print Dumper($vlan_fdb_table) if $debug;
128
129         for my $m (keys %$vlan_fdb_table) {
130                 my $fdb = $vlan_fdb_table->{$m};
131                 my $port = $if_table->{$fdb->{port}} or die "Forwarding DB refers to unknown iface";
132                 my @m = split /\./, $m;
133                 my $vlan = shift @m;
134                 my $mac = join(':', map { sprintf('%02x', $_) } @m);
135                 push @{$port->{macs}}, $mac;
136         }
137 }
138
139 my $OID_VlanStaticTable = '1.3.6.1.2.1.17.7.1.4.3';
140 my $vlan_table = my_get_table({
141         'name' => "$OID_VlanStaticTable.1.1",
142         'egress-ports' => "$OID_VlanStaticTable.1.2",
143         'untagged-ports' => "$OID_VlanStaticTable.1.4",
144         'row-status' => "$OID_VlanStaticTable.1.5",
145 });
146 for my $vlan (values %$vlan_table) {
147         for my $k ('egress-ports', 'untagged-ports') {
148                 $vlan->{$k} = [ split //, unpack("B*", $vlan->{$k} // "") ];
149         }
150 }
151 print Dumper($vlan_table) if $debug;
152 my @vlans = sort { $a <=> $b } grep { $vlan_table->{$_}->{'row-status'} == 1 } keys %$vlan_table;
153
154 print "\n### VLANs ###\n\n";
155 if (@vlans) {
156         for my $vid (@vlans) {
157                 printf "%-4d  %s\n", $vid, $vlan_table->{$vid}->{'name'} // '-';
158         }
159 } else {
160         print "No VLAN support.\n";
161 }
162
163 print "\n### Ports ###\n\n";
164 # XXX: We assume that 802.1d switch ports IDs are equal to interface IDs
165 for my $port (@ifaces) {
166         my $if = $if_table->{$port};
167         my $state;
168         my $scolor = $t_norm;
169         if ($if->{'admin'} != 1) {
170                 $state = 'OFF';
171         } elsif ($if->{'oper'} != 1) {
172                 $state = 'DOWN';
173                 $scolor = $t_red;
174         } else {
175                 $state = 'UP';
176                 $scolor = $t_green;
177         }
178         my $speed = $if->{hispeed} || int($if->{speed} / 1000000);
179         if (!$speed) {
180                 $speed = "";
181         } elsif ($speed < 1000) {
182                 $speed = "${speed}M";
183         } else {
184                 $speed = int($speed/1000);
185                 $speed = "${speed}G";
186         }
187         printf "%-4d %-15.15s  %s%-4s %-5s %s%-20s%s", $port, $if->{name}, $scolor, $state, $speed, $t_yellow, $if->{alias}, $t_norm;
188
189         if ($mac) {
190                 my $macs = $if->{macs};
191                 my $show_mac = "";
192                 my $more_macs = " ";
193                 if ($macs && @$macs) {
194                         $show_mac = $macs->[0];
195                         $more_macs = "${t_yellow}+${t_norm}" if @$macs > 1;
196                 }
197                 printf "%-17s%s ", $show_mac, $more_macs;
198         }
199
200         for my $vid (@vlans) {
201                 my $vlan = $vlan_table->{$vid};
202                 if ($vlan->{'egress-ports'}->[$port]) {
203                         if ($vlan->{'untagged-ports'}->[$port]) {
204                                 if ($t_green ne "") {
205                                         print " ${t_green}${vid}${t_norm}";
206                                 } else {
207                                         print " ${vid}U";
208                                 }
209                         } else {
210                                 print " $vid";
211                         }
212                 }
213         }
214
215         print "\n";
216
217         if ($if->{ip_addrs}) {
218                 print "${t_yellow}     IP: ", join(" ", @{$if->{ip_addrs}}), "${t_norm}\n";
219         }
220 }