# # Dump ethernet port detail (duplex, autoneg settings, etc) from # 3Com SuperStack II switches # # $Id: EtherDetail.pm,v 1.11 2000/08/18 21:40:57 trockij Exp $ # # Copyright (C) 1998 Jim Trocki # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package A3Com::EtherDetail; use strict; use vars qw($VERSION); use A3Com::Base; use Expect; $VERSION = "0.05"; sub _convert_etherdetail; sub new { my $proto = shift; my $class = ref($proto) || $proto; my %vars = @_; my $self = {}; $self->{"PORTS"} = {}; $self->{"ERROR"} = ""; $self->{"SWITCH"} = ""; $self->{"READ_METHOD"} = \&telnet_read; $self->{"LOGIN"} = "read"; $self->{"PASSWORD"} = ""; $self->{"LOG"} = 0; $self->{"CACHE"} = 1; $self->{"GLOBALCACHE"} = 1; $self->{"CACHETIME"} = $A3Com::Base::DEFAULT_CACHETIME; $self->{"FILE"} = ""; $self->{"GLOBALCACHEDIR"} = "/usr/local/share/a3com"; if (defined $ENV{"HOME"}) { $self->{CACHEDIR} = $ENV{"HOME"} . "/.a3com"; } else { $self->{"CACHEDIR"} = ".a3com"; } my %c = &A3Com::Base::_read_conf ($self, $ENV{"A3COM_CONF"}); for (keys %c) { $self->{$_} = $c{$_}; } if (defined $ENV{"A3COM_GLOBALCACHEDIR"}) { $self->{"GLOBALCACHEDIR"} = $ENV{"A3COM_GLOBALCACHEDIR"}; } if (defined $ENV{"A3COM_CACHEDIR"}) { $self->{"CACHEDIR"} = $ENV{"A3COM_CACHEDIR"}; } $self->{"_LOADED"} = 0; for my $k (keys %vars) { $self->{$k} = $vars{$k} if ($vars{$k} ne ""); } bless ($self, $class); return $self; } sub cachedir { my $self = shift; if (@_) { $self->{"CACHEDIR"} = shift } return $self->{"CACHEDIR"}; } sub globalcachedir { my $self = shift; if (@_) { $self->{"GLOBALCACHEDIR"} = shift } return $self->{"GLOBALCACHEDIR"}; } sub cachetime { my $self = shift; if (@_) { $self->{"CACHETIME"} = shift } return $self->{"CACHETIME"}; } sub switch { my $self = shift; if (@_) { $self->{"SWITCH"} = shift } return $self->{"SWITCH"}; } sub error { my $self = shift; if (@_) { $self->{"ERROR"} = shift } return $self->{"ERROR"}; } sub log { my $self = shift; if (@_) { $self->{"LOG"} = shift } return $self->{"LOG"}; } sub cache { my $self = shift; if (@_) { $self->{"CACHE"} = shift } return $self->{"CACHE"}; } sub globalcache { my $self = shift; if (@_) { $self->{"GLOBALCACHE"} = shift } return $self->{"GLOBALCACHE"}; } sub port_val { my $self = shift; my ($portnum, $var) = @_; $self->{"ERROR"} = ""; return undef if (!defined $self->_load_cache ($self)); if (!defined $self->{"PORTS"}->{$portnum}->{$var}) { $self->{"ERROR"} = "value $portnum/$var not found"; return undef; } return $self->{"PORTS"}->{$portnum}->{$var}; } sub ports { my $self = shift; $self->{"ERROR"} = ""; return undef if (!defined $self->_load_cache ($self)); return sort {$a <=> $b} keys %{$self->{"PORTS"}}; } # # write ether detail # sub file_write { my $self = shift; my $file = shift; my ($port, $key); $self->{"ERROR"} = ""; if (!defined $file) { my $cd; if ($self->{"GLOBALCACHE"}) { $cd = "GLOBALCACHEDIR"; } else { $cd = "CACHEDIR"; } if (! -d $self->{$cd}) { $self->{"ERROR"} = "$cd is not a directory"; return undef; } $file = "$self->{$cd}/$self->{SWITCH}.ed"; } if (!open (O, ">$file")) { $self->{"ERROR"} = "$!"; return undef; } print O "#\n# ethernet detail table for $self->{SWITCH} at " . time . "\n#\n"; foreach $port (sort {$a <=> $b} keys %{$self->{"PORTS"}}) { foreach $key (sort keys %{$self->{"PORTS"}->{$port}}) { print O "$port $key $self->{PORTS}->{$port}->{$key}\n"; } } close (O); } # # read bridge table # sub file_read { my $self = shift; my $file = shift; my ($port, $key, $val); $self->{"ERROR"} = ""; if (!defined $file) { if (! -d $self->{"CACHEDIR"}) { $self->{"ERROR"} = "CACHEDIR is not a directory"; return undef; } $file = "$self->{CACHEDIR}/$self->{SWITCH}.ed"; } if (!open (I, $file)) { $self->{"ERROR"} = "$!"; return undef; } $self->{"PORTS"} = {}; while () { chomp; next if (/^\s*#/ || /^\s*$/); ($port, $key, $val) = split (/\s+/, $_, 3); $self->{"PORTS"}->{$port}->{$key} = $val; } close (I); } sub telnet_read { my $self = shift; my ($ADDRS, $s, @n); $self->{"ERROR"} = ""; $s = Expect->spawn ("telnet $self->{SWITCH}"); if (!$s) { $self->{"ERROR"} = "could not telnet to $self->{SWITCH}"; return undef; } $s->log_stdout($self->{"LOG"}); # # log in # if (!$s->expect (15, ("Select access level (read, write, administer): "))) { $self->{"ERROR"} = "did not get login prompt in time"; return undef; } $s->clear_accum; print $s "$self->{LOGIN}\r"; if (!$s->expect (8, ("Password:"))) { $self->{"ERROR"} = "did not get password prompt"; return undef; } $s->clear_accum; print $s "$self->{PASSWORD}\r"; @n = $s->expect (8, "Incorrect password", "Select menu option: "); if ($n[0] == 1) { $s->hard_close(); $self->{"ERROR"} = "incorrect password for $self->{SWITCH}"; return undef; } elsif (!defined $n[0] && $n[1] eq "1:TIMEOUT") { $s->hard_close(); $self->{"ERROR"} = "timeout logging in to $self->{SWITCH}, it says {$n[3]}"; return undef; } $s->clear_accum; print $s "ether detail all\r"; @n = $s->expect (30, "Select menu option:"); $ADDRS = $n[3]; if (!defined ($n[0])) { $s->hard_close(); $self->{"ERROR"} = "error ($n[1]) during ethernet data gather for $self->{SWITCH}\n$n[3]"; return undef; } $s->clear_accum; # # log out politely # print $s "logout\r"; if ($s->expect (8, "Exiting") != 1) { $s->hard_close(); $self->{"ERROR"} = "did not get logout confirmation"; return undef; } $s->hard_close(); _convert_etherdetail $self, $ADDRS; } sub _convert_etherdetail { my $self = shift; my $ADDRS = shift; my ($l); my ($port, @fields, @vals); $self->{"PORTS"} = {}; while ($ADDRS =~ /([^\n\r]+\n\r)/sg) { $l = $1; $l =~ s/[\r\n]*$//; next if ($l =~ /^\s*$/); if ($l =~ /^port/) { ($port, @fields) = split (/\s+/, $l); for (@fields) { s/^\s*//; s/\s*$//; } next; } elsif ($l =~ /^(\d+)/) { $port = $1; $l =~ s{(\([^)]+\))} {my $t = $1; $t =~ s/\s+//g; $t}e; my $junk; ($junk, @vals) = split (/\s+/, $l, 1 + @fields); for (my $i=0; $i<@fields; $i++) { $self->{"PORTS"}->{$port}->{$fields[$i]} = $vals[$i]; } next; } } } sub _load_cache { my $self = shift; if (!$self->{"_LOADED"}) { if ($self->{"GLOBALCACHE"}) { return undef if (!defined (A3Com::Base::_load_global ($self, ".ed"))); } else { return undef if (!defined (A3Com::Base::_load ($self, ".ed"))); } $self->{"_LOADED"} = 1; } }